/** * * Copyright * 2009-2015 Jayway Products AB * 2016-2017 Föreningen Sambruk * * Licensed under AGPL, Version 3.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.gnu.org/licenses/agpl.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package se.streamsource.streamflow.web.domain.entity.gtd; import static org.qi4j.api.query.QueryExpressions.and; import static org.qi4j.api.query.QueryExpressions.contains; import static org.qi4j.api.query.QueryExpressions.eq; import static org.qi4j.api.query.QueryExpressions.ge; import static org.qi4j.api.query.QueryExpressions.le; import static org.qi4j.api.query.QueryExpressions.or; import static org.qi4j.api.query.QueryExpressions.templateFor; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.joda.time.DateMidnight; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.qi4j.api.injection.scope.Structure; import org.qi4j.api.mixin.Mixins; import org.qi4j.api.query.QueryBuilder; import org.qi4j.api.query.grammar.BooleanExpression; import org.qi4j.api.structure.Module; import org.qi4j.api.unitofwork.NoSuchEntityException; import se.streamsource.streamflow.api.workspace.cases.CaseStates; import se.streamsource.streamflow.web.domain.entity.user.UserEntity; import se.streamsource.streamflow.web.domain.interaction.gtd.Assignable; import se.streamsource.streamflow.web.domain.interaction.gtd.DueOn; import se.streamsource.streamflow.web.domain.interaction.gtd.Ownable; import se.streamsource.streamflow.web.domain.interaction.gtd.Status; import se.streamsource.streamflow.web.domain.structure.casetype.CaseType; import se.streamsource.streamflow.web.domain.structure.casetype.TypedCase; import se.streamsource.streamflow.web.domain.structure.caze.Case; import se.streamsource.streamflow.web.domain.structure.created.CreatedOn; import se.streamsource.streamflow.web.domain.structure.label.Label; import se.streamsource.streamflow.web.domain.structure.label.Labelable; import se.streamsource.streamflow.web.domain.structure.project.Project; import se.streamsource.streamflow.web.domain.structure.user.User; /** * This interface with mixin provides general application of a filter for queries. */ @Mixins(AbstractCaseQueriesFilter.Mixin.class) public interface AbstractCaseQueriesFilter { QueryBuilder<Case> applyFilter(QueryBuilder<Case> builder, String filter); abstract class Mixin implements AbstractCaseQueriesFilter { @Structure Module module; public QueryBuilder<Case> applyFilter(QueryBuilder<Case> builder, String filter) { List<SubQuery> searches = extractSubQueries(filter); for (int i = 0; i < searches.size(); i++) { SubQuery search = searches.get(i); if (search.hasName("status")) { int count = 0; BooleanExpression expression = null; for (String status : Arrays.asList(search.getValue().split(","))) { if (count == 0) { expression = eq(templateFor(Status.Data.class).status(), CaseStates.valueOf(status)); } else { expression = or(expression, eq(templateFor(Status.Data.class).status(), CaseStates.valueOf(status))); } count++; } builder = builder.where(expression); } else if (search.hasName("label")) { int count = 0; BooleanExpression expression = null; for (String label : search.getValue().split(",")) { Label labelEntity = null; try { labelEntity = module.unitOfWorkFactory().currentUnitOfWork().get(Label.class, label); } catch (NoSuchEntityException e) { // do nothing } if (labelEntity == null) continue; if (count == 0) { expression = contains(templateFor(Labelable.Data.class).labels(), labelEntity); } else { expression = or(expression, contains(templateFor(Labelable.Data.class).labels(), labelEntity)); } count++; } if (expression != null) builder = builder.where(expression); } else if (search.hasName("caseType")) { int count = 0; BooleanExpression expression = null; for (String caseType : search.getValue().split(",")) { CaseType caseTypeEntity = null; try { caseTypeEntity = module.unitOfWorkFactory().currentUnitOfWork().get(CaseType.class, caseType); } catch (NoSuchEntityException e) { // do nothing } if (caseTypeEntity == null) continue; if (count == 0) { expression = eq(templateFor(TypedCase.Data.class).caseType(), caseTypeEntity); } else { expression = or(expression, eq(templateFor(TypedCase.Data.class).caseType(), caseTypeEntity)); } count++; } if (expression != null) builder = builder.where(expression); } else if (search.hasName("project")) { int count = 0; BooleanExpression expression = null; for (String project : search.getValue().split(",")) { Project projectEntity = null; try { projectEntity = module.unitOfWorkFactory().currentUnitOfWork().get(Project.class, project); } catch (NoSuchEntityException e) { // do nothing } if (projectEntity == null) continue; if (count == 0) { expression = eq(templateFor(Ownable.Data.class).owner(), projectEntity); } else { expression = or(expression, eq(templateFor(Ownable.Data.class).owner(), projectEntity)); } count++; } if (expression != null) builder = builder.where(expression); } else if (search.hasName("createdBy")) { int count = 0; BooleanExpression expression = null; for (String user : search.getValue().split(",")) { User userEntity = null; try { userEntity = module.unitOfWorkFactory().currentUnitOfWork().get(User.class, user); } catch (NoSuchEntityException e) { // do nothing } if (userEntity == null) continue; if (count == 0) { expression = eq(templateFor(CreatedOn.class).createdBy(), userEntity); } else { expression = or(expression, eq(templateFor(CreatedOn.class).createdBy(), userEntity)); } count++; } if (expression != null) builder = builder.where(expression); } else if (search.hasName("createdOn") || search.hasName("dueOn")) { String value = search.getValue(); DateMidnight lowerDate = null; DateMidnight upperDate = null; DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyyMMdd"); if (value.indexOf("-") == -1) { lowerDate = new DateMidnight(formatter.parseDateTime(value)); } else { lowerDate = new DateMidnight(formatter.parseDateTime(value.substring(0, value.indexOf("-")))); upperDate = new DateMidnight(formatter.parseDateTime(value.substring(value.indexOf("-") + 1))); } if (search.hasName("createdOn")) { if (upperDate == null) { builder = builder.where(and( ge(templateFor(CreatedOn.class).createdOn(), lowerDate.toDate()), le(templateFor(CreatedOn.class).createdOn(), lowerDate.plusDays(1).toDate()))); } else { builder = builder.where(and( ge(templateFor(CreatedOn.class).createdOn(), lowerDate.toDate()), le(templateFor(CreatedOn.class).createdOn(), upperDate.plusDays(1).toDate()))); } } else { if (upperDate == null) { builder = builder.where(and( ge(templateFor(DueOn.Data.class).dueOn(), lowerDate.toDate()), le(templateFor(DueOn.Data.class).dueOn(), lowerDate.plusDays(1).toDate()))); } else { builder = builder.where(and( ge(templateFor(DueOn.Data.class).dueOn(), lowerDate.toDate()), le(templateFor(DueOn.Data.class).dueOn(), upperDate.plusDays(1).toDate()))); } } } else if (search.hasName("assignedTo")) { int count = 0; BooleanExpression expression = null; for (String user : search.getValue().split(",")) { UserEntity userEntity = null; try { userEntity = module.unitOfWorkFactory().currentUnitOfWork().get(UserEntity.class, user); } catch (NoSuchEntityException e) { // do nothing } if (userEntity == null) continue; if (count == 0) { expression = eq(templateFor(Assignable.Data.class).assignedTo(), userEntity); } else { expression = or(expression, eq(templateFor(Assignable.Data.class).assignedTo(), userEntity)); } count++; } if (expression != null) builder = builder.where(expression); } } return builder; } protected List<SubQuery> extractSubQueries(String query) { List<SubQuery> subQueries = new ArrayList<SubQuery>(); // TODO: Extract regular expression to resource file. String regExp = "((\\w+)\\:)?((\\\"([^\\\"]*)\\\")|([^\\s]+))"; // old "(?:\\w+\\:)?(?:\\\"[^\\\"]*?\\\")|(?:[^\\s]+)"; Pattern p; try { p = Pattern.compile(regExp); } catch (PatternSyntaxException e) { return subQueries; } Matcher m = p.matcher(query); while (m.find()) { String value = m.group(5); if (value == null) value = m.group(3); subQueries.add(new SubQuery(m.group(2), value)); } if (subQueries.isEmpty()) { if (query.length() > 0) subQueries.add(new SubQuery(null, query)); } return subQueries; } class SubQuery { String name; String value; public SubQuery(String name, String value) { this.name = name; this.value = value; } public String getName() { return name; } public String getValue() { return value; } public boolean hasName(String... names) { return name == null ? false : Arrays.asList(names).contains(name); } } } }