[Bug #645] Refactor ResourcesSearchModel

Now it allows to search only on machines or workers. This will make
easier to fix bugs #645 and #646

FEA: ItEr60S04ValidacionEProbasFuncionaisItEr59S04
This commit is contained in:
Óscar González Fernández 2010-09-19 23:07:05 +02:00
parent 873d3b6311
commit 32beb5734e
5 changed files with 298 additions and 150 deletions

View file

@ -26,6 +26,8 @@ import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.I18nHelper;
import org.navalplanner.web.planner.allocation.INewAllocationsAdder;
import org.navalplanner.web.resources.search.IResourceSearchModel;
import org.navalplanner.web.resources.search.IResourceSearchModel.IResourcesQuery;
import org.navalplanner.web.resources.search.NewAllocationSelectorController;
import org.zkoss.zul.api.Radio;
import org.zkoss.zul.api.Radiogroup;
@ -47,6 +49,12 @@ public class NewAllocationSelector extends AllocationSelector {
.getSelectedCriterions()), controller
.getSelectedWorkers());
}
@Override
public IResourcesQuery<?> doQueryOn(
IResourceSearchModel resourceSearchModel) {
return resourceSearchModel.searchWorkers();
}
},
GENERIC_MACHINES(_("generic machines allocation")) {
@Override
@ -57,6 +65,12 @@ public class NewAllocationSelector extends AllocationSelector {
.getSelectedCriterions()), controller
.getSelectedWorkers());
}
@Override
public IResourcesQuery<?> doQueryOn(
IResourceSearchModel resourceSearchModel) {
return resourceSearchModel.searchMachines();
}
},
SPECIFIC(_("specific allocation")) {
@Override
@ -64,6 +78,12 @@ public class NewAllocationSelector extends AllocationSelector {
INewAllocationsAdder allocationsAdder) {
allocationsAdder.addSpecific(controller.getSelectedWorkers());
}
@Override
public IResourcesQuery<?> doQueryOn(
IResourceSearchModel resourceSearchModel) {
return resourceSearchModel.searchBoth();
}
};
@ -106,6 +126,9 @@ public class NewAllocationSelector extends AllocationSelector {
public abstract void addTo(
NewAllocationSelectorController newAllocationSelectorController,
INewAllocationsAdder allocationsAdder);
public abstract IResourcesQuery<?> doQueryOn(
IResourceSearchModel resourceSearchModel);
}
public NewAllocationSelectorController getController() {

View file

@ -20,13 +20,17 @@
package org.navalplanner.web.resources.search;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.business.resources.entities.Machine;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.business.resources.entities.ResourceEnum;
import org.navalplanner.business.resources.entities.Worker;
/**
* Conversation for worker search
@ -35,15 +39,37 @@ import org.navalplanner.business.resources.entities.Resource;
*/
public interface IResourceSearchModel {
/**
* Returns all resources filtering by name, criteria and limitingResource
*
* @param name
* @param criterions
* @param limitingResource
* @return
*/
List<Resource> findResources(String name, List<Criterion> criteria, boolean limitingResource);
public interface IResourcesQuery<T extends Resource> {
IResourcesQuery<T> byName(String name);
IResourcesQuery<T> byCriteria(Collection<? extends Criterion> criteria);
IResourcesQuery<T> byLimiting(boolean limiting);
List<T> execute();
/**
* <p>
* Gets all {@link Criterion} and groups then by {@link CriterionType}
* with the condition that the {@link CriterionType#getResource()} is of
* a type compatible for this query.
* </p>
* For example if this query has been created by
* {@link IResourceSearchModel#searchWorkers()} only the criteria with
* criterion type such its resource is {@link ResourceEnum.WORKER}
* @return HashMap<CriterionType, Set<Criterion>>
*/
Map<CriterionType, Set<Criterion>> getCriteria();
}
public IResourcesQuery<Worker> searchWorkers();
public IResourcesQuery<Machine> searchMachines();
public IResourcesQuery<?> searchBy(ResourceEnum resourceType);
public IResourcesQuery<Resource> searchBoth();
/**
* Returns all resources
@ -51,24 +77,4 @@ public interface IResourceSearchModel {
*/
List<Resource> getAllResources();
/**
* Gets all {@link Criterion} and groups then by {@link CriterionType}
* @return HashMap<CriterionType, Set<Criterion>>
*/
Map<CriterionType, Set<Criterion>> getCriterions();
/**
* Returns all limiting resources
*
* @return
*/
List<Resource> getAllLimitingResources();
/**
* Returns all non-limiting resources
*
* @return
*/
List<Resource> getAllNonLimitingResources();
}

View file

@ -26,10 +26,12 @@ import java.util.List;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.business.resources.entities.ResourceEnum;
import org.navalplanner.web.common.components.bandboxsearch.BandboxMultipleSearch;
import org.navalplanner.web.common.components.finders.FilterPair;
import org.navalplanner.web.common.components.finders.ResourceAllocationFilterEnum;
import org.navalplanner.web.planner.allocation.INewAllocationsAdder;
import org.navalplanner.web.resources.search.IResourceSearchModel.IResourcesQuery;
import org.zkoss.zk.ui.Component;
/**
@ -57,9 +59,22 @@ public class NewAllocationSelectorComboController extends
* Does the actual search for workers
* @param criterions
*/
private List<Resource> searchResources(List<Criterion> criterions) {
return resourceSearchModel.findResources("", criterions,
limitingResource);
private List<? extends Resource> searchResources(List<Criterion> criterions) {
return query(inferType(criterions)).byCriteria(criterions)
.byLimiting(limitingResource).execute();
}
private static ResourceEnum inferType(List<Criterion> criterions) {
if (criterions.isEmpty()) {
// FIXME resolve the ambiguity. One option is asking the user
return ResourceEnum.WORKER;
}
Criterion first = criterions.iterator().next();
return first.getType().getResource();
}
private IResourcesQuery<?> query(ResourceEnum resourceEnum) {
return resourceSearchModel.searchBy(resourceEnum);
}
/**
@ -109,7 +124,7 @@ public class NewAllocationSelectorComboController extends
if (!getSelectedItems().isEmpty()) {
if (isGeneric()) {
List<Criterion> criteria = getSelectedCriterions();
List<Resource> resources = searchResources(criteria);
List<? extends Resource> resources = searchResources(criteria);
allocationsAdder.addGeneric(new HashSet<Criterion>(criteria),
resources);
} else {

View file

@ -34,8 +34,10 @@ import org.apache.commons.lang.StringUtils;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.web.common.Util;
import org.navalplanner.web.common.components.NewAllocationSelector.AllocationType;
import org.navalplanner.web.planner.allocation.INewAllocationsAdder;
import org.navalplanner.web.resources.search.IResourceSearchModel.IResourcesQuery;
import org.zkoss.lang.Objects;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
@ -94,6 +96,7 @@ public class NewAllocationSelectorController extends
* Initializes ZUL components
*/
private void initController() {
doInitialSelection();
// Add event listener onSelect to criterionsTree widget
if (criterionsTree != null) {
criterionsTree.addEventListener("onSelect", new EventListener() {
@ -111,8 +114,7 @@ public class NewAllocationSelectorController extends
criterionsTree.setTreeitemRenderer(criterionRenderer);
listBoxResources.setItemRenderer(getListitemRenderer());
// Show all workers
refreshListBoxResources(getAllResources());
refreshListBoxResources();
allocationTypeSelector.addEventListener(Events.ON_CHECK,
new EventListener() {
@ -141,13 +143,14 @@ public class NewAllocationSelectorController extends
}
}
});
doInitialSelection();
}
private List<Resource> getAllResources() {
return (limitingResource) ? resourceSearchModel
.getAllLimitingResources() : resourceSearchModel
.getAllNonLimitingResources();
private List<? extends Resource> getAllResources() {
return query().byLimiting(limitingResource).execute();
}
private IResourcesQuery<?> query() {
return currentAllocationType.doQueryOn(resourceSearchModel);
}
private void doInitialSelection() {
@ -156,10 +159,11 @@ public class NewAllocationSelectorController extends
onType(currentAllocationType);
}
private void onType(AllocationType type) {
currentAllocationType = type;
listBoxResources.setDisabled(isGenericType());
Util.reloadBindings(criterionsTree);
refreshListBoxResources();
}
private static final EnumSet<AllocationType> genericTypes = EnumSet.of(
@ -202,8 +206,10 @@ public class NewAllocationSelectorController extends
* @param criterions
*/
private void searchResources(String name, List<Criterion> criterions) {
final List<Resource> resources = resourceSearchModel.findResources(
name, criterions, limitingResource);
final List<? extends Resource> resources = query().byName(name)
.byCriteria(criterions)
.byLimiting(limitingResource)
.execute();
refreshListBoxResources(resources);
}
@ -240,13 +246,17 @@ public class NewAllocationSelectorController extends
}
public void clearAll() {
refreshListBoxResources(getAllResources());
refreshListBoxResources();
criterionsTree.setModel(getCriterions());
clearSelection(listBoxResources);
clearSelection(criterionsTree);
doInitialSelection();
}
private void refreshListBoxResources() {
refreshListBoxResources(getAllResources());
}
public List<Resource> getSelectedWorkers() {
if (isGenericType()) {
return allResourcesShown();
@ -312,8 +322,7 @@ public class NewAllocationSelectorController extends
* @return
*/
public TreeModel getCriterions() {
Map<CriterionType, Set<Criterion>> criterions = resourceSearchModel
.getCriterions();
Map<CriterionType, Set<Criterion>> criterions = query().getCriteria();
List<CriterionTreeNode> rootList = new ArrayList<CriterionTreeNode>();
for (Entry<CriterionType, Set<Criterion>> entry : criterions.entrySet()) {

View file

@ -20,23 +20,35 @@
package org.navalplanner.web.resources.search;
import static org.hibernate.criterion.Restrictions.eq;
import static org.hibernate.criterion.Restrictions.ilike;
import static org.hibernate.criterion.Restrictions.in;
import static org.hibernate.criterion.Restrictions.like;
import static org.hibernate.criterion.Restrictions.or;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.navalplanner.business.common.IAdHocTransactionService;
import org.navalplanner.business.common.IOnTransaction;
import org.navalplanner.business.resources.daos.ICriterionDAO;
import org.navalplanner.business.resources.daos.IMachineDAO;
import org.navalplanner.business.resources.daos.IResourceDAO;
import org.navalplanner.business.resources.daos.IWorkerDAO;
import org.navalplanner.business.resources.entities.Criterion;
import org.navalplanner.business.resources.entities.CriterionType;
import org.navalplanner.business.resources.entities.Machine;
import org.navalplanner.business.resources.entities.Resource;
import org.navalplanner.business.resources.entities.ResourceEnum;
import org.navalplanner.business.resources.entities.Worker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
@ -51,11 +63,171 @@ import org.springframework.transaction.annotation.Transactional;
*/
public class ResourceSearchModel implements IResourceSearchModel {
@Autowired
private IWorkerDAO workerDAO;
private static final Log LOG = LogFactory.getLog(ResourceSearchModel.class);
@Autowired
private IMachineDAO machineDAO;
private IAdHocTransactionService adHocTransactionService;
@Autowired
private SessionFactory sessionFactory;
public IResourcesQuery<Machine> searchMachines() {
return new Query<Machine>(Machine.class);
}
@Override
public IResourcesQuery<Worker> searchWorkers() {
return new Query<Worker>(Worker.class);
}
class Query<T extends Resource> implements IResourcesQuery<T> {
private final Class<T> klass;
private String name = null;
private List<Criterion> criteria = null;
private boolean limiting = false;
public Query(Class<T> klass) {
this.klass = klass;
}
@Override
public IResourcesQuery<T> byName(String name) {
this.name = name;
return this;
}
@Override
public IResourcesQuery<T> byCriteria(
Collection<? extends Criterion> criteria) {
Validate.noNullElements(criteria);
this.criteria = new ArrayList<Criterion>(criteria);
return this;
}
@Override
public IResourcesQuery<T> byLimiting(boolean limiting) {
this.limiting = limiting;
return this;
}
@Override
public List<T> execute() {
return adHocTransactionService
.runOnReadOnlyTransaction(new IOnTransaction<List<T>>() {
@Override
@SuppressWarnings("unchecked")
public List<T> execute() {
Session session = sessionFactory
.getCurrentSession();
return buildCriteria(session).list();
}
});
}
private Criteria buildCriteria(Session session) {
Criteria result = session.createCriteria(klass);
result.add(eq("limitingResource", limiting));
addQueryByName(result);
addQueryByCriteria(result);
result.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
return result;
}
private void addQueryByCriteria(Criteria criteria) {
if (this.criteria == null || this.criteria.isEmpty()) {
return;
}
criteria.createCriteria("criterionSatisfactions")
.add(in("criterion", this.criteria));
}
private void addQueryByName(Criteria criteria) {
if (name == null) {
return;
}
final String nameWithWildcards = "%" + name + "%";
if (klass.equals(Worker.class)) {
criteria.add(or(
or(ilike("firstName", nameWithWildcards),
ilike("surname", nameWithWildcards)),
like("nif", nameWithWildcards)));
} else if (klass.equals(Machine.class)) {
criteria.add(or(ilike("name", nameWithWildcards),
ilike("code", nameWithWildcards)));
} else {
LOG.warn("can't handle " + klass);
}
}
@Override
public Map<CriterionType, Set<Criterion>> getCriteria() {
return adHocTransactionService
.runOnReadOnlyTransaction(getCriterionsTree(klass));
}
}
@Override
public IResourcesQuery<?> searchBy(ResourceEnum resourceType) {
Validate.notNull(resourceType);
switch (resourceType) {
case MACHINE:
return searchMachines();
case WORKER:
return searchWorkers();
default:
throw new RuntimeException("can't handle " + resourceType);
}
}
@Override
public IResourcesQuery<Resource> searchBoth() {
final IResourcesQuery<Worker> searchWorkers = searchWorkers();
final IResourcesQuery<Machine> searchMachines = searchMachines();
return new IResourcesQuery<Resource>() {
@Override
public IResourcesQuery<Resource> byName(String name) {
searchWorkers.byName(name);
searchMachines.byName(name);
return this;
}
@Override
public IResourcesQuery<Resource> byCriteria(
Collection<? extends Criterion> criteria) {
searchWorkers.byCriteria(criteria);
searchMachines.byCriteria(criteria);
return this;
}
@Override
public IResourcesQuery<Resource> byLimiting(boolean limiting) {
searchWorkers.byLimiting(limiting);
searchMachines.byLimiting(limiting);
return this;
}
@Override
public List<Resource> execute() {
List<Resource> result = new ArrayList<Resource>();
List<Worker> workers = searchWorkers.execute();
result.addAll(workers);
List<Machine> machines = searchMachines.execute();
result.addAll(machines);
return result;
}
@Override
public Map<CriterionType, Set<Criterion>> getCriteria() {
return adHocTransactionService
.runOnReadOnlyTransaction(getCriterionsTree(Resource.class));
}
};
}
@Autowired
private IResourceDAO resourceDAO;
@ -63,108 +235,31 @@ public class ResourceSearchModel implements IResourceSearchModel {
@Autowired
private ICriterionDAO criterionDAO;
@Override
@Transactional(readOnly = true)
public Map<CriterionType, Set<Criterion>> getCriterions() {
HashMap<CriterionType, Set<Criterion>> result = new HashMap<CriterionType, Set<Criterion>>();
List<Criterion> criterions = criterionDAO.getAllSorted();
for (Criterion criterion : criterions) {
CriterionType key = criterion.getType();
Set<Criterion> values = (!result.containsKey(key)) ? new LinkedHashSet<Criterion>()
: (Set<Criterion>) result.get(key);
values.add(criterion);
result.put(key, values);
}
return result;
}
@Override
@Transactional(readOnly = true)
public List<Resource> findResources(String name, List<Criterion> criteria, boolean limitingResource) {
return findByNameAndCriterions(name, criteria, limitingResource);
}
private List<Resource> findByNameAndCriterions(String name,
List<Criterion> criteria, boolean limitingResource) {
final boolean emptyName = StringUtils.isEmpty(name);
if (criteria.isEmpty() && emptyName) {
return getAllResources(limitingResource);
}
Set<Resource> resourcesMatchingCriteria = null;
if (!criteria.isEmpty()) {
resourcesMatchingCriteria = satisfatyingCriteriaAndLimiting(
criteria, limitingResource);
if (resourcesMatchingCriteria.isEmpty()) {
return new ArrayList<Resource>();
}
}
if (emptyName) {
return new ArrayList<Resource>(resourcesMatchingCriteria);
}
Set<Resource> result = intersect(findWorkers(name,
limitingResource), resourcesMatchingCriteria);
result.addAll(intersect(findMachines(name, limitingResource),
resourcesMatchingCriteria));
return new ArrayList<Resource>(result);
}
private Set<Resource> satisfatyingCriteriaAndLimiting(
List<Criterion> criteria, boolean limitingResource) {
Set<Resource> result = new HashSet<Resource>();
for (Resource each : resourceDAO
.findSatisfyingAllCriterionsAtSomePoint(criteria)) {
if (each.isLimitingResource() == limitingResource) {
result.add(each);
}
}
return result;
}
private List<Worker> findWorkers(String name, boolean limitingResource) {
return workerDAO.findByNameSubpartOrNifCaseInsensitive(name,
limitingResource);
}
private List<Machine> findMachines(String name, boolean limitingResource) {
return machineDAO.findByNameOrCode(name, limitingResource);
}
private List<Resource> getAllResources(boolean limitingResource) {
return (limitingResource) ? resourceDAO.getAllLimitingResources()
: resourceDAO.getAllNonLimitingResources();
}
private static Set<Resource> intersect(List<? extends Resource> all,
Set<Resource> filteringBy) {
if (filteringBy == null) {
return new HashSet<Resource>(all);
}
Set<Resource> result = new HashSet<Resource>(filteringBy);
result.retainAll(all);
return result;
}
@Override
@Transactional(readOnly = true)
public List<Resource> getAllResources() {
return resourceDAO.getResources();
}
@Override
@Transactional(readOnly = true)
public List<Resource> getAllLimitingResources() {
return resourceDAO.getAllLimitingResources();
private IOnTransaction<Map<CriterionType, Set<Criterion>>> getCriterionsTree(
final Class<? extends Resource> klassTheCriterionTypeMustBeRelatedWith) {
return new IOnTransaction<Map<CriterionType, Set<Criterion>>>() {
@Override
public Map<CriterionType, Set<Criterion>> execute() {
Map<CriterionType, Set<Criterion>> result = new HashMap<CriterionType, Set<Criterion>>();
for (Criterion criterion : criterionDAO.getAll()) {
CriterionType key = criterion.getType();
if (klassTheCriterionTypeMustBeRelatedWith
.isAssignableFrom(key.getResource().asClass())) {
if (!result.containsKey(key)) {
result.put(key, new LinkedHashSet<Criterion>());
}
result.get(key).add(criterion);
}
}
return result;
}
};
}
@Override
@Transactional(readOnly = true)
public List<Resource> getAllNonLimitingResources() {
return resourceDAO.getAllNonLimitingResources();
}
}
}