Skip to content

Commit

Permalink
Preliminary support for case management related queries.
Browse files Browse the repository at this point in the history
  • Loading branch information
mederly committed Sep 21, 2017
1 parent 679d35f commit 4c90010
Show file tree
Hide file tree
Showing 11 changed files with 427 additions and 37 deletions.
Expand Up @@ -19,6 +19,7 @@
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.util.DebugDumpable;
import com.evolveum.midpoint.util.DebugUtil;
import org.jetbrains.annotations.NotNull;

import javax.xml.namespace.QName;
import java.io.Serializable;
Expand All @@ -31,7 +32,7 @@ public class ObjectPaging implements DebugDumpable, Serializable {

private Integer offset;
private Integer maxSize;
private List<ObjectOrdering> ordering = new ArrayList<>();
@NotNull private final List<ObjectOrdering> ordering = new ArrayList<>();
private String cookie;

protected ObjectPaging() {
Expand Down Expand Up @@ -108,11 +109,11 @@ public List<ObjectOrdering> getOrderingInstructions() {
}

public boolean hasOrdering() {
return ordering != null && !ordering.isEmpty(); // first is just for sure
return !ordering.isEmpty();
}

public void setOrdering(ItemPath orderBy, OrderDirection direction) {
this.ordering = new ArrayList<>();
this.ordering.clear();
addOrderingInstruction(orderBy, direction);
}

Expand All @@ -124,12 +125,19 @@ public void addOrderingInstruction(QName orderBy, OrderDirection direction) {
addOrderingInstruction(new ItemPath(orderBy), direction);
}

@SuppressWarnings("NullableProblems")
public void setOrdering(ObjectOrdering... orderings) {
this.ordering = new ArrayList<>(Arrays.asList(orderings));
this.ordering.clear();
if (orderings != null) {
this.ordering.addAll(Arrays.asList(orderings));
}
}

public void setOrdering(Collection<ObjectOrdering> orderings) {
this.ordering = orderings != null ? new ArrayList<>(orderings) : new ArrayList<>();
this.ordering.clear();
if (orderings != null) {
this.ordering.addAll(orderings);
}
}

public Integer getOffset() {
Expand Down Expand Up @@ -187,11 +195,8 @@ public ObjectPaging clone() {
protected void copyTo(ObjectPaging clone) {
clone.offset = this.offset;
clone.maxSize = this.maxSize;
if (this.ordering != null) {
clone.ordering = new ArrayList<>(this.ordering);
} else {
clone.ordering = null;
}
clone.ordering.clear();
clone.ordering.addAll(this.ordering);
clone.cookie = this.cookie;
}

Expand Down Expand Up @@ -231,10 +236,6 @@ public String debugDump(int indent) {
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PAGING: ");
if (this == null){
sb.append("null");
return sb.toString();
}
if (getOffset() != null){
sb.append("O: ");
sb.append(getOffset());
Expand All @@ -258,6 +259,7 @@ public String toString() {
return sb.toString();
}

@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
@Override
public boolean equals(Object o) {
return equals(o, true);
Expand All @@ -275,20 +277,15 @@ public boolean equals(Object o, boolean exact) {
return false;
if (maxSize != null ? !maxSize.equals(that.maxSize) : that.maxSize != null)
return false;
if ((ordering != null && that.ordering == null) || (ordering == null && that.ordering != null)) {
if (ordering.size() != that.ordering.size()) {
return false;
}
if (ordering != null) {
if (ordering.size() != that.ordering.size()) {
for (int i = 0; i < ordering.size(); i++) {
ObjectOrdering oo1 = this.ordering.get(i);
ObjectOrdering oo2 = that.ordering.get(i);
if (!oo1.equals(oo2, exact)) {
return false;
}
for (int i = 0; i < ordering.size(); i++) {
ObjectOrdering oo1 = this.ordering.get(i);
ObjectOrdering oo2 = that.ordering.get(i);
if (!oo1.equals(oo2, exact)) {
return false;
}
}
}
return cookie != null ? cookie.equals(that.cookie) : that.cookie == null;
}
Expand All @@ -297,7 +294,7 @@ public boolean equals(Object o, boolean exact) {
public int hashCode() {
int result = offset != null ? offset.hashCode() : 0;
result = 31 * result + (maxSize != null ? maxSize.hashCode() : 0);
result = 31 * result + (ordering != null ? ordering.hashCode() : 0);
result = 31 * result + ordering.hashCode();
result = 31 * result + (cookie != null ? cookie.hashCode() : 0);
return result;
}
Expand Down
Expand Up @@ -97,7 +97,7 @@ public enum ObjectTypes {
SERVICE(ServiceType.COMPLEX_TYPE, SchemaConstantsGenerated.C_SERVICE, ServiceType.class, ObjectManager.MODEL,
"services"),

CASE(CaseType.COMPLEX_TYPE, SchemaConstantsGenerated.C_CASE, CaseType.class, ObjectManager.MODEL,
CASE(CaseType.COMPLEX_TYPE, SchemaConstantsGenerated.C_CASE, CaseType.class, ObjectManager.EMULATED,
"cases"),

// this should be at end, because otherwise it presents itself as entry for all subtypes of ObjectType
Expand All @@ -118,7 +118,7 @@ public ObjectTypes superType() {
}

public enum ObjectManager {
PROVISIONING, TASK_MANAGER, MODEL, WORKFLOW, REPOSITORY
PROVISIONING, TASK_MANAGER, MODEL, WORKFLOW, REPOSITORY, EMULATED
}

private QName type;
Expand Down
Expand Up @@ -57,6 +57,19 @@
<xsd:sequence>
<!-- TODO payload -->

<xsd:element name="objectRef" type="tns:ObjectReferenceType" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Object that the case is associated with. It might be e.g. resource, if the case
deals with creation of an account on that resource.
EXPERIMENTAL. May change in future.
</xsd:documentation>
<xsd:appinfo>
<a:since>3.7</a:since>
<a:experimental>true</a:experimental>
</xsd:appinfo>
</xsd:annotation>
</xsd:element>
<xsd:element name="state" type="xsd:anyURI" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Expand Down
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2010-2017 Evolveum
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 com.evolveum.midpoint.model.impl.controller;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.match.MatchingRuleRegistry;
import com.evolveum.midpoint.prism.query.ObjectFilter;
import com.evolveum.midpoint.prism.query.ObjectOrdering;
import com.evolveum.midpoint.prism.query.ObjectPaging;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.prism.util.CloneUtil;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SearchResultList;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CaseWorkItemType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

/**
* @author mederly
*/
@Component
public class EmulatedSearchProvider {

@Autowired private MatchingRuleRegistry matchingRuleRegistry;
@Autowired
@Qualifier("cacheRepositoryService")
private transient RepositoryService cacheRepositoryService;

public <T extends ObjectType> SearchResultList<PrismObject<T>> searchObjects(Class<T> type, ObjectQuery query,
Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult result) throws SchemaException {

SearchResultList<PrismObject<T>> allObjects = cacheRepositoryService.searchObjects(type, null, options, result);
if (query == null) {
return allObjects;
}

List<PrismObject<T>> filtered = doFiltering(query, allObjects, (o) -> o.getValue());
List<PrismObject<T>> paged = doPaging(query, filtered, (o) -> o.getValue());
return new SearchResultList<>(paged);
}

public <T extends Containerable> SearchResultList<T> searchContainers(Class<T> type, ObjectQuery query,
Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult result) throws SchemaException {
if (!CaseWorkItemType.class.equals(type)) {
throw new UnsupportedOperationException("searchContainers is supported only for CaseWorkItemType, not for " + type);
}

List<CaseWorkItemType> allValues = new ArrayList<>();
SearchResultList<PrismObject<CaseType>> cases = cacheRepositoryService
.searchObjects(CaseType.class, null, null, result);
for (PrismObject<CaseType> aCase : cases) {
allValues.addAll(CloneUtil.cloneCollectionMembers(aCase.asObjectable().getWorkItem()));
}
if (query == null) {
//noinspection unchecked
return (SearchResultList<T>) new SearchResultList<>(allValues);
}

List<CaseWorkItemType> filtered = doFiltering(query, allValues, (v) -> v.asPrismContainerValue());
List<CaseWorkItemType> paged = doPaging(query, filtered, (v) -> v.asPrismContainerValue());
//noinspection unchecked
return (SearchResultList<T>) new SearchResultList<>(paged);
}

private <T> List<T> doFiltering(ObjectQuery query, List<T> allObjects, Function<T, PrismContainerValue> pcvExtractor)
throws SchemaException {
ObjectFilter filter = query.getFilter();
if (filter == null) {
return allObjects;
}
List<T> filtered = new ArrayList<>();
for (T object : allObjects) {
if (filter.match(pcvExtractor.apply(object), matchingRuleRegistry)) {
filtered.add(object);
}
}
return filtered;
}


private <T> List<T> doPaging(ObjectQuery query, List<T> allObjects, Function<T, PrismContainerValue> pcvExtractor)
throws SchemaException {
ObjectPaging paging = query.getPaging();
if (paging == null) {
return allObjects;
}
if (!paging.getOrderingInstructions().isEmpty()) {
allObjects.sort((o1, o2) -> {
PrismContainerValue pcv1 = pcvExtractor.apply(o1);
PrismContainerValue pcv2 = pcvExtractor.apply(o2);
for (ObjectOrdering ordering : paging.getOrderingInstructions()) {
Comparable<Object> c1 = getComparable(pcv1.find(ordering.getOrderBy()));
Comparable<Object> c2 = getComparable(pcv2.find(ordering.getOrderBy()));
int result = c1 == null
? (c2 == null ? 0 : 1)
: (c2 == null ? -1 : c1.compareTo(c2));
if (result != 0) {
return result;
}
}
return 0;
});
}
int start = paging.getOffset() != null ? paging.getOffset() : 0;
int end = paging.getMaxSize() != null ? Math.min(start + paging.getMaxSize(), allObjects.size()) : allObjects.size();
if (start == 0 && end == allObjects.size()) {
return allObjects;
} else {
return allObjects.subList(start, end);
}
}

@SuppressWarnings("unchecked")
private Comparable<Object> getComparable(Object o) {
if (o instanceof Item) {
Item item = (Item) o;
if (item.getValues().size() > 1) {
throw new IllegalStateException("Couldn't compare multivalued items");
} else if (item.getValues().size() == 1) {
o = item.getValues().get(0);
} else {
return null;
}
}
if (o instanceof PrismValue) {
o = ((PrismValue) o).getRealValue();
}
if (o instanceof Comparable) {
return (Comparable<Object>) o;
}
return null;
}

}

0 comments on commit 4c90010

Please sign in to comment.