diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java index a496ea4f7..6fc42fccd 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/NewAllocationSelector.java @@ -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() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java index ec06ed6e2..f74103a13 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/IResourceSearchModel.java @@ -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 findResources(String name, List criteria, boolean limitingResource); + public interface IResourcesQuery { + + IResourcesQuery byName(String name); + + IResourcesQuery byCriteria(Collection criteria); + + IResourcesQuery byLimiting(boolean limiting); + + List execute(); + + /** + *

+ * 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. + *

+ * 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> + */ + Map> getCriteria(); + } + + public IResourcesQuery searchWorkers(); + + public IResourcesQuery searchMachines(); + + public IResourcesQuery searchBy(ResourceEnum resourceType); + + public IResourcesQuery searchBoth(); /** * Returns all resources @@ -51,24 +77,4 @@ public interface IResourceSearchModel { */ List getAllResources(); - /** - * Gets all {@link Criterion} and groups then by {@link CriterionType} - * @return HashMap> - */ - Map> getCriterions(); - - /** - * Returns all limiting resources - * - * @return - */ - List getAllLimitingResources(); - - /** - * Returns all non-limiting resources - * - * @return - */ - List getAllNonLimitingResources(); - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorComboController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorComboController.java index 9fcdc2e55..4fe44d0c9 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorComboController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorComboController.java @@ -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 searchResources(List criterions) { - return resourceSearchModel.findResources("", criterions, - limitingResource); + private List searchResources(List criterions) { + return query(inferType(criterions)).byCriteria(criterions) + .byLimiting(limitingResource).execute(); + } + + private static ResourceEnum inferType(List 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 criteria = getSelectedCriterions(); - List resources = searchResources(criteria); + List resources = searchResources(criteria); allocationsAdder.addGeneric(new HashSet(criteria), resources); } else { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java index 1b74c58b8..61bdebb63 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/NewAllocationSelectorController.java @@ -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 getAllResources() { - return (limitingResource) ? resourceSearchModel - .getAllLimitingResources() : resourceSearchModel - .getAllNonLimitingResources(); + private List 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 genericTypes = EnumSet.of( @@ -202,8 +206,10 @@ public class NewAllocationSelectorController extends * @param criterions */ private void searchResources(String name, List criterions) { - final List resources = resourceSearchModel.findResources( - name, criterions, limitingResource); + final List 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 getSelectedWorkers() { if (isGenericType()) { return allResourcesShown(); @@ -312,8 +322,7 @@ public class NewAllocationSelectorController extends * @return */ public TreeModel getCriterions() { - Map> criterions = resourceSearchModel - .getCriterions(); + Map> criterions = query().getCriteria(); List rootList = new ArrayList(); for (Entry> entry : criterions.entrySet()) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java index 39e4b6d3f..028729fe0 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resources/search/ResourceSearchModel.java @@ -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 searchMachines() { + return new Query(Machine.class); + } + + @Override + public IResourcesQuery searchWorkers() { + return new Query(Worker.class); + } + + class Query implements IResourcesQuery { + + private final Class klass; + + private String name = null; + + private List criteria = null; + + private boolean limiting = false; + + public Query(Class klass) { + this.klass = klass; + } + + @Override + public IResourcesQuery byName(String name) { + this.name = name; + return this; + } + + @Override + public IResourcesQuery byCriteria( + Collection criteria) { + Validate.noNullElements(criteria); + this.criteria = new ArrayList(criteria); + return this; + } + + @Override + public IResourcesQuery byLimiting(boolean limiting) { + this.limiting = limiting; + return this; + } + + @Override + public List execute() { + return adHocTransactionService + .runOnReadOnlyTransaction(new IOnTransaction>() { + @Override + @SuppressWarnings("unchecked") + public List 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> 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 searchBoth() { + final IResourcesQuery searchWorkers = searchWorkers(); + final IResourcesQuery searchMachines = searchMachines(); + return new IResourcesQuery() { + + @Override + public IResourcesQuery byName(String name) { + searchWorkers.byName(name); + searchMachines.byName(name); + return this; + } + + @Override + public IResourcesQuery byCriteria( + Collection criteria) { + searchWorkers.byCriteria(criteria); + searchMachines.byCriteria(criteria); + return this; + } + + @Override + public IResourcesQuery byLimiting(boolean limiting) { + searchWorkers.byLimiting(limiting); + searchMachines.byLimiting(limiting); + return this; + } + + @Override + public List execute() { + List result = new ArrayList(); + List workers = searchWorkers.execute(); + result.addAll(workers); + List machines = searchMachines.execute(); + result.addAll(machines); + return result; + } + + @Override + public Map> 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> getCriterions() { - HashMap> result = new HashMap>(); - - List criterions = criterionDAO.getAllSorted(); - for (Criterion criterion : criterions) { - - CriterionType key = criterion.getType(); - Set values = (!result.containsKey(key)) ? new LinkedHashSet() - : (Set) result.get(key); - values.add(criterion); - result.put(key, values); - } - return result; - } - - - @Override - @Transactional(readOnly = true) - public List findResources(String name, List criteria, boolean limitingResource) { - return findByNameAndCriterions(name, criteria, limitingResource); - } - - private List findByNameAndCriterions(String name, - List criteria, boolean limitingResource) { - - final boolean emptyName = StringUtils.isEmpty(name); - if (criteria.isEmpty() && emptyName) { - return getAllResources(limitingResource); - } - - Set resourcesMatchingCriteria = null; - if (!criteria.isEmpty()) { - resourcesMatchingCriteria = satisfatyingCriteriaAndLimiting( - criteria, limitingResource); - if (resourcesMatchingCriteria.isEmpty()) { - return new ArrayList(); - } - } - if (emptyName) { - return new ArrayList(resourcesMatchingCriteria); - } - Set result = intersect(findWorkers(name, - limitingResource), resourcesMatchingCriteria); - result.addAll(intersect(findMachines(name, limitingResource), - resourcesMatchingCriteria)); - return new ArrayList(result); - } - - private Set satisfatyingCriteriaAndLimiting( - List criteria, boolean limitingResource) { - Set result = new HashSet(); - for (Resource each : resourceDAO - .findSatisfyingAllCriterionsAtSomePoint(criteria)) { - if (each.isLimitingResource() == limitingResource) { - result.add(each); - } - } - return result; - } - - private List findWorkers(String name, boolean limitingResource) { - return workerDAO.findByNameSubpartOrNifCaseInsensitive(name, - limitingResource); - } - - private List findMachines(String name, boolean limitingResource) { - return machineDAO.findByNameOrCode(name, limitingResource); - } - - private List getAllResources(boolean limitingResource) { - return (limitingResource) ? resourceDAO.getAllLimitingResources() - : resourceDAO.getAllNonLimitingResources(); - } - - private static Set intersect(List all, - Set filteringBy) { - if (filteringBy == null) { - return new HashSet(all); - } - Set result = new HashSet(filteringBy); - result.retainAll(all); - return result; - } - @Override @Transactional(readOnly = true) public List getAllResources() { return resourceDAO.getResources(); } - @Override - @Transactional(readOnly = true) - public List getAllLimitingResources() { - return resourceDAO.getAllLimitingResources(); + private IOnTransaction>> getCriterionsTree( + final Class klassTheCriterionTypeMustBeRelatedWith) { + return new IOnTransaction>>() { + @Override + public Map> execute() { + Map> result = new HashMap>(); + for (Criterion criterion : criterionDAO.getAll()) { + CriterionType key = criterion.getType(); + if (klassTheCriterionTypeMustBeRelatedWith + .isAssignableFrom(key.getResource().asClass())) { + if (!result.containsKey(key)) { + result.put(key, new LinkedHashSet()); + } + result.get(key).add(criterion); + } + } + return result; + } + }; } - @Override - @Transactional(readOnly = true) - public List getAllNonLimitingResources() { - return resourceDAO.getAllNonLimitingResources(); - } - -} +} \ No newline at end of file