/*
* Copyright (c) 2010-2016 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.prism.marshaller;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import javax.xml.namespace.QName;
import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.prism.xnode.*;
import com.evolveum.prism.xml.ns._public.query_3.SearchFilterType;
import com.evolveum.prism.xml.ns._public.types_3.ItemPathType;
import com.evolveum.prism.xml.ns._public.types_3.ObjectReferenceType;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.prism.query.OrgFilter.Scope;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.QNameUtil;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Note that expressions are not serialized yet.
*/
public class QueryConvertor {
private static final Trace LOGGER = TraceManager.getTrace(QueryConvertor.class);
public static final String NS_QUERY = "http://prism.evolveum.com/xml/ns/public/query-3";
// TODO removed unused constants eventually
public static final QName FILTER_ELEMENT_NAME = new QName(NS_QUERY, "filter");
public static QName KEY_FILTER = new QName(NS_QUERY, "filter");
public static QName KEY_PAGING = new QName(NS_QUERY, "paging");
public static QName KEY_CONDITION = new QName(NS_QUERY, "condition");
// please keep the order of filter clause symbols synchronized with query-3.xsd
private static final QName CLAUSE_ALL = new QName(NS_QUERY, "all");
private static final QName CLAUSE_NONE = new QName(NS_QUERY, "none");
private static final QName CLAUSE_UNDEFINED = new QName(NS_QUERY, "undefined");
private static final QName CLAUSE_EQUAL = new QName(NS_QUERY, "equal");
private static final QName CLAUSE_GREATER = new QName(NS_QUERY, "greater");
private static final QName CLAUSE_GREATER_OR_EQUAL = new QName(NS_QUERY, "greaterOrEqual");
private static final QName CLAUSE_LESS = new QName(NS_QUERY, "less");
private static final QName CLAUSE_LESS_OR_EQUAL = new QName(NS_QUERY, "lessOrEqual");
private static final QName CLAUSE_SUBSTRING = new QName(NS_QUERY, "substring");
private static final QName CLAUSE_REF = new QName(NS_QUERY, "ref");
private static final QName CLAUSE_ORG = new QName(NS_QUERY, "org");
private static final QName CLAUSE_IN_OID = new QName(NS_QUERY, "inOid");
private static final QName CLAUSE_FULL_TEXT = new QName(NS_QUERY, "fullText");
private static final QName CLAUSE_AND = new QName(NS_QUERY, "and");
private static final QName CLAUSE_OR = new QName(NS_QUERY, "or");
private static final QName CLAUSE_NOT = new QName(NS_QUERY, "not");
private static final QName CLAUSE_TYPE = new QName(NS_QUERY, "type");
private static final QName CLAUSE_EXISTS = new QName(NS_QUERY, "exists");
// common elements
private static final QName ELEMENT_PATH = new QName(NS_QUERY, "path");
private static final QName ELEMENT_MATCHING = new QName(NS_QUERY, "matching");
private static final QName ELEMENT_VALUE = new QName(NS_QUERY, "value");
private static final QName ELEMENT_RIGHT_HAND_SIDE_PATH = new QName(NS_QUERY, "rightHandSidePath");
// substring
private static final QName ELEMENT_ANCHOR_START = new QName(NS_QUERY, "anchorStart");
private static final QName ELEMENT_ANCHOR_END = new QName(NS_QUERY, "anchorEnd");
// org
private static final QName ELEMENT_ORG_REF = new QName(NS_QUERY, "orgRef");
private static final QName ELEMENT_SCOPE = new QName(NS_QUERY, "scope");
private static final QName ELEMENT_IS_ROOT = new QName(NS_QUERY, "isRoot");
// inOid
private static final QName ELEMENT_OID = new QName(NS_QUERY, "oid");
private static final QName ELEMENT_CONSIDER_OWNER = new QName(NS_QUERY, "considerOwner");
// type and exists
public static final QName ELEMENT_TYPE = new QName(NS_QUERY, "type");
private static final QName ELEMENT_FILTER = new QName(NS_QUERY, "filter");
/**
* Used by XNodeProcessor and similar code that does not have complete schema for the filter
*/
public static ObjectFilter parseFilter(XNode xnode, PrismContext prismContext) throws SchemaException {
Validate.notNull(prismContext);
MapXNode xmap = toMap(xnode);
return parseFilterInternal(xmap, null, false, prismContext);
}
public static <O extends Containerable> ObjectFilter parseFilter(MapXNode xmap, PrismContainerDefinition<O> objDef) throws SchemaException {
Validate.notNull(objDef);
if (xmap == null) {
return null;
}
return parseFilterInternal(xmap, objDef, false, objDef.getPrismContext());
}
public static <O extends Objectable> ObjectFilter parseFilter(SearchFilterType filter, Class<O> clazz, PrismContext prismContext) throws SchemaException {
PrismObjectDefinition<O> objDef = prismContext.getSchemaRegistry().findObjectDefinitionByCompileTimeClass(clazz);
if (objDef == null) {
throw new SchemaException("Cannot find obj definition for "+clazz);
}
return parseFilter(filter, objDef);
}
public static ObjectFilter parseFilter(SearchFilterType filter, PrismObjectDefinition objDef) throws SchemaException {
Validate.notNull(objDef);
return parseFilter(filter.getFilterClauseXNode(), objDef);
}
private static <C extends Containerable> ObjectFilter parseFilterInternal(
@NotNull MapXNode filterXMap,
@Nullable PrismContainerDefinition<C> pcd,
boolean preliminaryParsingOnly,
@NotNull PrismContext prismContext) throws SchemaException {
Validate.notNull(prismContext);
Entry<QName, XNode> clauseEntry = filterXMap.getSingleEntryThatDoesNotMatch(SearchFilterType.F_DESCRIPTION);
QName clauseQName = clauseEntry.getKey();
XNode clauseContent = clauseEntry.getValue();
return parseFilterInternal(clauseContent, clauseQName, pcd, preliminaryParsingOnly, prismContext);
}
private static <C extends Containerable> ObjectFilter parseFilterInternal(XNode clauseContent, QName clauseQName,
PrismContainerDefinition<C> pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
// trivial filters
if (QNameUtil.match(clauseQName, CLAUSE_ALL)) {
return new AllFilter();
} else if (QNameUtil.match(clauseQName, CLAUSE_NONE)) {
return new NoneFilter();
} else if (QNameUtil.match(clauseQName, CLAUSE_UNDEFINED)) {
return new UndefinedFilter();
}
// primitive filters
MapXNode clauseXMap = toMap(clauseContent);
if (QNameUtil.match(clauseQName, CLAUSE_EQUAL)
|| QNameUtil.match(clauseQName, CLAUSE_GREATER)
|| QNameUtil.match(clauseQName, CLAUSE_GREATER_OR_EQUAL)
|| QNameUtil.match(clauseQName, CLAUSE_LESS)
|| QNameUtil.match(clauseQName, CLAUSE_LESS_OR_EQUAL)) {
return parseComparisonFilter(clauseQName, clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_SUBSTRING)) {
return parseSubstringFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_REF)) {
return parseRefFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_ORG)) {
return parseOrgFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_IN_OID)) {
return parseInOidFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_FULL_TEXT)) {
return parseFullTextFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
}
// logical filters
if (QNameUtil.match(clauseQName, CLAUSE_AND)) {
return parseAndFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_OR)) {
return parseOrFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_NOT)) {
return parseNotFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
}
// other complex filters
if (QNameUtil.match(clauseQName, CLAUSE_TYPE)) {
return parseTypeFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
} else if (QNameUtil.match(clauseQName, CLAUSE_EXISTS)) {
return parseExistsFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
}
throw new UnsupportedOperationException("Unsupported query filter " + clauseQName);
}
private static <C extends Containerable> AndFilter parseAndFilter(MapXNode clauseXMap, PrismContainerDefinition<C> pcd,
boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
List<ObjectFilter> subfilters = parseLogicalFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
if (preliminaryParsingOnly) {
return null;
} else {
return AndFilter.createAnd(subfilters);
}
}
private static <C extends Containerable> List<ObjectFilter> parseLogicalFilter(MapXNode clauseXMap,
PrismContainerDefinition<C> pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
List<ObjectFilter> subfilters = new ArrayList<ObjectFilter>();
for (Entry<QName, XNode> entry : clauseXMap.entrySet()) {
if (entry.getValue() instanceof ListXNode){
Iterator<XNode> subNodes = ((ListXNode) entry.getValue()).iterator();
while (subNodes.hasNext()){
ObjectFilter subFilter = parseFilterInternal(subNodes.next(), entry.getKey(), pcd, preliminaryParsingOnly, prismContext);
if (!preliminaryParsingOnly) {
subfilters.add(subFilter);
}
}
} else{
ObjectFilter subfilter = parseFilterInternal(entry.getValue(), entry.getKey(), pcd, preliminaryParsingOnly, prismContext);
if (!preliminaryParsingOnly) {
subfilters.add(subfilter);
}
}
}
return subfilters;
}
private static <C extends Containerable> OrFilter parseOrFilter(MapXNode clauseXMap, PrismContainerDefinition<C> pcd,
boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
List<ObjectFilter> subfilters = parseLogicalFilter(clauseXMap, pcd, preliminaryParsingOnly, prismContext);
if (preliminaryParsingOnly) {
return null;
} else {
return OrFilter.createOr(subfilters);
}
}
private static <C extends Containerable> NotFilter parseNotFilter(MapXNode clauseXMap, PrismContainerDefinition<C> pcd,
boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
Entry<QName, XNode> entry = singleSubEntry(clauseXMap, "not");
ObjectFilter subfilter = parseFilterInternal(entry.getValue(), entry.getKey(), pcd, preliminaryParsingOnly, prismContext);
if (preliminaryParsingOnly) {
return null;
} else {
return NotFilter.createNot(subfilter);
}
}
private static <T,C extends Containerable> ObjectFilter parseComparisonFilter(QName clauseQName, MapXNode clauseXMap,
PrismContainerDefinition<C> pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
boolean isEq = QNameUtil.match(clauseQName, CLAUSE_EQUAL);
boolean isGt = QNameUtil.match(clauseQName, CLAUSE_GREATER);
boolean isGtEq = QNameUtil.match(clauseQName, CLAUSE_GREATER_OR_EQUAL);
boolean isLt = QNameUtil.match(clauseQName, CLAUSE_LESS);
boolean isLtEq = QNameUtil.match(clauseQName, CLAUSE_LESS_OR_EQUAL);
ItemPath itemPath = getPath(clauseXMap);
if (itemPath == null || itemPath.isEmpty()){
throw new SchemaException("Could not convert query, because query does not contain item path.");
}
QName itemName = ItemPath.getName(itemPath.last());
QName matchingRule = getMatchingRule(clauseXMap);
XNode valueXnode = clauseXMap.get(ELEMENT_VALUE);
ItemPath rightSidePath = getPath(clauseXMap, ELEMENT_RIGHT_HAND_SIDE_PATH);
ItemDefinition itemDefinition = locateItemDefinition(valueXnode, itemPath, pcd, prismContext);
if (itemDefinition != null) {
itemName = itemDefinition.getName();
}
if (valueXnode != null) {
if (preliminaryParsingOnly) {
return null;
} else {
RootXNode valueRoot = new RootXNode(ELEMENT_VALUE, valueXnode);
Item item = parseItem(valueRoot, itemName, itemDefinition, prismContext);
if (!isEq && item.getValues().size() != 1) {
throw new SchemaException("Expected exactly one value, got " + item.getValues().size() + " instead");
}
if (isEq) {
List<PrismPropertyValue<T>> values = item.getValues();
PrismValue.clearParent(values);
return EqualFilter.createEqual(itemPath,
(PrismPropertyDefinition<T>)itemDefinition, matchingRule, prismContext, values);
}
PrismPropertyValue<T> propertyValue = (PrismPropertyValue<T>) item.getValue(0);
propertyValue.clearParent();
if (isGt || isGtEq) {
return GreaterFilter.createGreater(itemPath, (PrismPropertyDefinition<T>) itemDefinition, isGtEq, prismContext, propertyValue);
} else {
return LessFilter.createLess(itemPath, (PrismPropertyDefinition<T>) itemDefinition, prismContext,
propertyValue, isLtEq);
}
}
} else if (rightSidePath != null) {
if (preliminaryParsingOnly) {
return null;
} else {
ItemDefinition rightSideDefinition = pcd != null ? pcd.findItemDefinition(rightSidePath) : null;
if (isEq) {
return EqualFilter.createEqual(itemPath, (PrismPropertyDefinition) itemDefinition, matchingRule, rightSidePath, rightSideDefinition);
} else if (isGt || isGtEq) {
return GreaterFilter.createGreater(itemPath, (PrismPropertyDefinition) itemDefinition, rightSidePath,
rightSideDefinition, isGtEq);
} else {
return LessFilter.createLess(itemPath, (PrismPropertyDefinition) itemDefinition, rightSidePath,
rightSideDefinition, isLtEq);
}
}
} else {
Entry<QName,XNode> expressionEntry = clauseXMap.getSingleEntryThatDoesNotMatch(
ELEMENT_VALUE, ELEMENT_MATCHING, ELEMENT_PATH);
if (expressionEntry != null) {
if (preliminaryParsingOnly) {
return null;
} else {
RootXNode expressionRoot = clauseXMap.getEntryAsRoot(expressionEntry.getKey());
PrismPropertyValue expressionPropertyValue = prismContext.parserFor(expressionRoot).parseItemValue();
ExpressionWrapper expressionWrapper = new ExpressionWrapper();
expressionWrapper.setExpression(expressionPropertyValue.getValue());
if (isEq) {
return EqualFilter.createEqual(itemPath, (PrismPropertyDefinition<T>) itemDefinition, matchingRule, expressionWrapper);
} else if (isGt || isGtEq) {
return GreaterFilter.createGreater(itemPath, (PrismPropertyDefinition<T>) itemDefinition,
expressionWrapper, isGtEq);
} else {
return LessFilter.createLess(itemPath, (PrismPropertyDefinition<T>) itemDefinition, expressionWrapper,
isLtEq);
}
}
} else {
if (!isEq) {
throw new SchemaException("Comparison filter (greater, less) requires at least one value expression.");
}
if (preliminaryParsingOnly) {
return null;
} else {
return EqualFilter.createEqual(itemPath, (PrismPropertyDefinition<T>) itemDefinition, matchingRule);
}
}
}
}
private static InOidFilter parseInOidFilter(MapXNode clauseXMap, PrismContainerDefinition pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException{
boolean considerOwner = Boolean.TRUE.equals(clauseXMap.getParsedPrimitiveValue(ELEMENT_CONSIDER_OWNER, DOMUtil.XSD_BOOLEAN));
XNode valueXnode = clauseXMap.get(ELEMENT_VALUE);
if (valueXnode != null) {
List<String> oids = getStringValues(valueXnode);
return InOidFilter.createInOid(considerOwner, oids);
} else {
ExpressionWrapper expression = parseExpression(clauseXMap, prismContext);
if (expression != null) {
return InOidFilter.createInOid(considerOwner, expression);
} else {
throw new SchemaException("InOid filter with no values nor expression");
}
}
}
private static FullTextFilter parseFullTextFilter(MapXNode clauseXMap, PrismContainerDefinition pcd,
boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException{
XNode valueXnode = clauseXMap.get(ELEMENT_VALUE);
if (valueXnode != null) {
List<String> values = getStringValues(valueXnode);
return FullTextFilter.createFullText(values);
} else {
ExpressionWrapper expression = parseExpression(clauseXMap, prismContext);
if (expression != null) {
return FullTextFilter.createFullText(expression);
} else {
throw new SchemaException("FullText filter with no values nor expression");
}
}
}
@NotNull
private static List<String> getStringValues(
XNode valueXnode) throws SchemaException {
List<String> values = new ArrayList<>();
if (valueXnode instanceof ListXNode) {
for (XNode subnode : (ListXNode) valueXnode) {
if (subnode instanceof PrimitiveXNode) {
values.add(((PrimitiveXNode<String>) subnode).getParsedValue(DOMUtil.XSD_STRING, String.class));
} else {
throw new SchemaException("The value was expected to be present as primitive XNode, instead it is: " + subnode);
}
}
} else if (valueXnode instanceof PrimitiveXNode) {
values.add(((PrimitiveXNode<String>) valueXnode).getParsedValue(DOMUtil.XSD_STRING, String.class));
} else {
throw new SchemaException("The value was expected to be present as primitive or list XNode, instead it is: " + valueXnode);
}
return values;
}
private static TypeFilter parseTypeFilter(MapXNode clauseXMap, PrismContainerDefinition pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException{
QName type = clauseXMap.getParsedPrimitiveValue(ELEMENT_TYPE, DOMUtil.XSD_QNAME);
XNode subXFilter = clauseXMap.get(ELEMENT_FILTER);
PrismObjectDefinition def = prismContext.getSchemaRegistry().findObjectDefinitionByType(type);
ObjectFilter subFilter = null;
if (subXFilter != null && subXFilter instanceof MapXNode) {
subFilter = parseFilterInternal((MapXNode) subXFilter, def, preliminaryParsingOnly, prismContext);
}
if (preliminaryParsingOnly) {
return null;
} else {
return new TypeFilter(type, subFilter);
}
}
private static ExistsFilter parseExistsFilter(MapXNode clauseXMap, PrismContainerDefinition pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException{
ItemPath path = getPath(clauseXMap);
XNode subXFilter = clauseXMap.get(ELEMENT_FILTER);
ObjectFilter subFilter = null;
PrismContainerDefinition subPcd = pcd != null ? pcd.findContainerDefinition(path) : null;
if (subXFilter != null && subXFilter instanceof MapXNode) {
subFilter = parseFilterInternal((MapXNode) subXFilter, subPcd, preliminaryParsingOnly, prismContext);
}
if (preliminaryParsingOnly) {
return null;
} else {
return ExistsFilter.createExists(path, pcd, subFilter);
}
}
private static <C extends Containerable> RefFilter parseRefFilter(MapXNode clauseXMap, PrismContainerDefinition<C> pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException{
ItemPath itemPath = getPath(clauseXMap);
if (itemPath == null || itemPath.isEmpty()){
throw new SchemaException("Cannot convert query, because query does not contain property path.");
}
QName itemName = ItemPath.getName(itemPath.last());
ItemDefinition itemDefinition = null;
if (pcd != null) {
itemDefinition = pcd.findItemDefinition(itemPath);
if (itemDefinition == null && !preliminaryParsingOnly) {
throw new SchemaException("No definition for item "+itemPath+" in "+pcd);
}
}
XNode valueXnode = clauseXMap.get(ELEMENT_VALUE);
if (valueXnode != null) {
if (preliminaryParsingOnly) {
return null;
}
RootXNode valueRoot = new RootXNode(ELEMENT_VALUE, valueXnode);
Item<?,?> item = prismContext.parserFor(valueRoot)
.name(itemName)
.definition(itemDefinition)
.context(ParsingContext.allowMissingRefTypes())
.parseItem();
if (!(item instanceof PrismReference)) {
throw new IllegalStateException("Expected PrismReference, got " + item);
}
PrismReference ref = (PrismReference)item;
if (item.getValues().size() < 1) {
throw new IllegalStateException("No values to search specified for item " + itemName);
}
return RefFilter.createReferenceEqual(itemPath, (PrismReferenceDefinition) itemDefinition, PrismValue.cloneCollection(ref.getValues()));
} else {
ExpressionWrapper expressionWrapper = parseExpression(clauseXMap, prismContext);
if (expressionWrapper != null) {
if (preliminaryParsingOnly) {
return null;
} else {
return RefFilter.createReferenceEqual(itemPath,
(PrismReferenceDefinition) itemDefinition, expressionWrapper);
}
} else {
if (preliminaryParsingOnly) {
return null;
} else {
return RefFilter.createReferenceEqual(itemPath,
(PrismReferenceDefinition) itemDefinition, (ExpressionWrapper) null);
}
}
}
}
private static ExpressionWrapper parseExpression(MapXNode xmap, PrismContext prismContext) throws SchemaException {
Entry<QName, XNode> expressionEntry = xmap.getSingleEntryThatDoesNotMatch(
ELEMENT_VALUE, ELEMENT_MATCHING, ELEMENT_PATH);
if (expressionEntry != null) {
RootXNode expressionRoot = new RootXNode(expressionEntry);
PrismPropertyValue expressionPropertyValue = prismContext.parserFor(expressionRoot).parseItemValue();
ExpressionWrapper expressionWrapper = new ExpressionWrapper();
expressionWrapper.setExpression(expressionPropertyValue.getValue());
return expressionWrapper;
}
return null;
}
private static <C extends Containerable> SubstringFilter parseSubstringFilter(MapXNode clauseXMap, PrismContainerDefinition<C> pcd, boolean preliminaryParsingOnly, PrismContext prismContext)
throws SchemaException {
ItemPath itemPath = getPath(clauseXMap);
if (itemPath == null || itemPath.isEmpty()){
throw new SchemaException("Could not convert query, because query does not contain item path.");
}
QName itemName = ItemPath.getName(itemPath.last());
QName matchingRule = getMatchingRule(clauseXMap);
XNode valueXnode = clauseXMap.get(ELEMENT_VALUE);
ItemDefinition itemDefinition = locateItemDefinition(valueXnode, itemPath, pcd, prismContext);
Item item = parseItem(new RootXNode(ELEMENT_VALUE, valueXnode), itemName, itemDefinition, prismContext);
Boolean anchorStart = clauseXMap.getParsedPrimitiveValue(ELEMENT_ANCHOR_START, DOMUtil.XSD_BOOLEAN);
if (anchorStart == null) {
anchorStart = false;
}
Boolean anchorEnd = clauseXMap.getParsedPrimitiveValue(ELEMENT_ANCHOR_END, DOMUtil.XSD_BOOLEAN);
if (anchorEnd == null) {
anchorEnd = false;
}
if (preliminaryParsingOnly) {
return null;
} else {
List values = item.getValues();
Object realValue;
if (values == null || values.isEmpty()) {
realValue = null; // TODO throw an exception?
} else if (values.size() > 1) {
throw new IllegalArgumentException("Expected at most 1 value, got " + values);
} else {
realValue = ((PrismPropertyValue) values.get(0)).getValue();
}
return SubstringFilter.createSubstring(itemPath, (PrismPropertyDefinition) itemDefinition,
prismContext, matchingRule, realValue, anchorStart, anchorEnd);
}
}
private static <C extends Containerable> OrgFilter parseOrgFilter(MapXNode clauseXMap, PrismContainerDefinition<C> pcd, boolean preliminaryParsingOnly, PrismContext prismContext) throws SchemaException {
if (Boolean.TRUE.equals(clauseXMap.getParsedPrimitiveValue(ELEMENT_IS_ROOT, DOMUtil.XSD_BOOLEAN))) {
// TODO check if other content is present
if (preliminaryParsingOnly) {
return null;
} else {
return OrgFilter.createRootOrg();
}
}
XNode xorgrefnode = clauseXMap.get(ELEMENT_ORG_REF);
if (xorgrefnode == null) {
throw new SchemaException("No organization reference defined in the search query.");
}
MapXNode xorgrefmap = toMap(xorgrefnode);
String orgOid = xorgrefmap.getParsedPrimitiveValue(ELEMENT_OID, DOMUtil.XSD_STRING);
if (orgOid == null || StringUtils.isBlank(orgOid)) {
throw new SchemaException("No oid attribute defined in the organization reference element.");
}
String scopeString = xorgrefmap.getParsedPrimitiveValue(ELEMENT_SCOPE, DOMUtil.XSD_STRING); // original (in my opinion incorrect) place
if (scopeString == null) {
scopeString = clauseXMap.getParsedPrimitiveValue(ELEMENT_SCOPE, DOMUtil.XSD_STRING); // here it is placed by the serializer
}
Scope scope = scopeString != null ? Scope.valueOf(scopeString) : null;
if (preliminaryParsingOnly) {
return null;
} else {
return OrgFilter.createOrg(orgOid, scope);
}
}
private static Entry<QName, XNode> singleSubEntry(MapXNode xmap, String filterName) throws SchemaException {
if (xmap == null || xmap.isEmpty()) {
return null;
}
return xmap.getSingleSubEntry("search filter "+filterName);
}
private static MapXNode toMap(XNode xnode) throws SchemaException {
if (!(xnode instanceof MapXNode)) {
throw new SchemaException("Cannot parse filter from "+xnode);
}
return (MapXNode)xnode;
}
private static PrimitiveXNode toPrimitive(XNode xnode, XNode context) throws SchemaException {
if (!(xnode instanceof PrimitiveXNode)) {
throw new SchemaException("Cannot parse filter from "+context+ ": This should be a primitive: "+xnode);
}
return (PrimitiveXNode)xnode;
}
private static ItemPath getPath(MapXNode clauseXMap) throws SchemaException {
return getPath(clauseXMap, ELEMENT_PATH);
}
private static ItemPath getPath(MapXNode clauseXMap, QName key) throws SchemaException {
XNode xnode = clauseXMap.get(key);
if (xnode == null) {
return null;
}
if (!(xnode instanceof PrimitiveXNode<?>)) {
throw new SchemaException("Expected that field "+key+" will be primitive, but it is "+xnode.getDesc());
}
ItemPathType itemPathType = clauseXMap.getParsedPrimitiveValue(key, ItemPathType.COMPLEX_TYPE);
return itemPathType != null ? itemPathType.getItemPath() : null;
}
private static QName getMatchingRule(MapXNode xmap) throws SchemaException{
String matchingRuleString = xmap.getParsedPrimitiveValue(ELEMENT_MATCHING, DOMUtil.XSD_STRING);
if (StringUtils.isNotBlank(matchingRuleString)){
if (QNameUtil.isUri(matchingRuleString)) {
return QNameUtil.uriToQName(matchingRuleString);
} else {
return new QName(PrismConstants.NS_MATCHING_RULE, matchingRuleString);
}
} else {
return null;
}
}
private static Item parseItem(RootXNode root, QName itemName, ItemDefinition itemDefinition, PrismContext prismContext) throws SchemaException{
Item<?,?> item;
if (prismContext == null) {
item = (Item) PrismProperty.createRaw(root.getSubnode(), itemName, null);
} else {
item = prismContext.parserFor(root)
.name(itemName)
.definition(itemDefinition)
.context(ParsingContext.allowMissingRefTypes())
.parseItem();
}
if (item.getValues().size() < 1 ) {
throw new IllegalStateException("No values to search specified for item " + itemName);
}
return item;
}
private static <C extends Containerable> ItemDefinition locateItemDefinition(XNode valueXnode, ItemPath itemPath, PrismContainerDefinition<C> pcd, PrismContext prismContext) throws SchemaException{
ItemDefinition itemDefinition = null;
if (pcd != null) {
itemDefinition = pcd.findItemDefinition(itemPath);
if (itemDefinition == null) {
ItemPath rest = itemPath.tail();
QName first = ItemPath.getName(itemPath.first());
itemDefinition = ((PrismContextImpl) prismContext).getPrismUnmarshaller().locateItemDefinition(pcd, first, valueXnode);
if (rest.isEmpty()) {
return itemDefinition;
} else{
if (itemDefinition != null && itemDefinition instanceof PrismContainerDefinition){
return locateItemDefinition(valueXnode, rest, (PrismContainerDefinition) itemDefinition, prismContext);
}
}
}
}
return itemDefinition;
}
public static SearchFilterType createSearchFilterType(ObjectFilter filter, PrismContext prismContext) throws SchemaException {
MapXNode xnode = serializeFilter(filter, prismContext);
return SearchFilterType.createFromXNode(xnode, prismContext);
}
public static MapXNode serializeFilter(ObjectFilter filter, PrismContext prismContext) throws SchemaException{
return serializeFilter(filter, prismContext.xnodeSerializer());
}
private static MapXNode serializeFilter(ObjectFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException{
// null or primitive filters
if (filter == null) {
return null;
} else if (filter instanceof AllFilter) {
return serializeAllFilter();
} else if (filter instanceof NoneFilter) {
return serializeNoneFilter();
} else if (filter instanceof UndefinedFilter) {
return serializeUndefinedFilter();
} else if (filter instanceof EqualFilter
|| filter instanceof GreaterFilter
|| filter instanceof LessFilter) {
return serializeComparisonFilter((PropertyValueFilter) filter, xnodeSerializer);
} else if (filter instanceof SubstringFilter) {
return serializeSubstringFilter((SubstringFilter) filter, xnodeSerializer);
} else if (filter instanceof RefFilter) {
return serializeRefFilter((RefFilter) filter, xnodeSerializer);
} else if (filter instanceof OrgFilter) {
return serializeOrgFilter((OrgFilter) filter, xnodeSerializer);
} else if (filter instanceof InOidFilter) {
return serializeInOidFilter((InOidFilter) filter, xnodeSerializer);
} else if (filter instanceof FullTextFilter) {
return serializeFullTextFilter((FullTextFilter) filter, xnodeSerializer);
}
// complex filters
if (filter instanceof AndFilter) {
return serializeAndFilter((AndFilter) filter, xnodeSerializer);
} else if (filter instanceof OrFilter) {
return serializeOrFilter((OrFilter) filter, xnodeSerializer);
} else if (filter instanceof NotFilter) {
return serializeNotFilter((NotFilter) filter, xnodeSerializer);
}
if (filter instanceof TypeFilter) {
return serializeTypeFilter((TypeFilter) filter, xnodeSerializer);
} else if (filter instanceof ExistsFilter) {
return serializeExistsFilter((ExistsFilter) filter, xnodeSerializer);
}
throw new UnsupportedOperationException("Unsupported filter type: " + filter);
}
private static MapXNode serializeAndFilter(AndFilter filter, PrismSerializer<RootXNode> xnodeSerilizer) throws SchemaException{
return createFilter(CLAUSE_AND, serializeNaryLogicalSubfilters(filter.getConditions(), xnodeSerilizer));
}
private static MapXNode serializeOrFilter(OrFilter filter, PrismSerializer<RootXNode> xnodeSerilizer) throws SchemaException{
MapXNode map = createFilter(CLAUSE_OR, serializeNaryLogicalSubfilters(filter.getConditions(), xnodeSerilizer));
return map;
}
private static MapXNode serializeNaryLogicalSubfilters(List<ObjectFilter> objectFilters, PrismSerializer<RootXNode> xnodeSerilizer) throws SchemaException{
MapXNode filters = new MapXNode();
for (ObjectFilter of : objectFilters) {
MapXNode subFilter = serializeFilter(of, xnodeSerilizer);
filters.merge(subFilter);
}
return filters;
}
private static MapXNode serializeNotFilter(NotFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException{
return createFilter(CLAUSE_NOT, serializeFilter(filter.getFilter(), xnodeSerializer));
}
@NotNull
private static MapXNode createFilter(QName clauseNot, MapXNode value) {
MapXNode map = new MapXNode();
map.put(clauseNot, value);
return map;
}
private static MapXNode serializeInOidFilter(InOidFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException {
MapXNode clauseMap = new MapXNode();
if (filter.getOids() != null && !filter.getOids().isEmpty()) {
ListXNode valuesNode = new ListXNode();
for (String oid : filter.getOids()) {
XNode val = createPrimitiveXNode(oid, DOMUtil.XSD_STRING);
valuesNode.add(val);
}
clauseMap.put(ELEMENT_VALUE, valuesNode);
} else if (filter.getExpression() != null) {
// TODO serialize expression
} else {
throw new SchemaException("InOid filter with no values nor expression");
}
if (filter.isConsiderOwner()) {
clauseMap.put(ELEMENT_CONSIDER_OWNER, new PrimitiveXNode<>(true));
}
return createFilter(CLAUSE_IN_OID, clauseMap);
}
private static MapXNode serializeFullTextFilter(FullTextFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException {
MapXNode clauseMap = new MapXNode();
if (filter.getValues() != null && !filter.getValues().isEmpty()) {
ListXNode valuesNode = new ListXNode();
for (String value : filter.getValues()) {
XNode val = createPrimitiveXNode(value, DOMUtil.XSD_STRING);
valuesNode.add(val);
}
clauseMap.put(ELEMENT_VALUE, valuesNode);
} else if (filter.getExpression() != null) {
// TODO serialize expression
} else {
throw new SchemaException("FullText filter with no values nor expression");
}
return createFilter(CLAUSE_FULL_TEXT, clauseMap);
}
private static <T> MapXNode serializeComparisonFilter(PropertyValueFilter<T> filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException{
MapXNode map = new MapXNode();
QName clause;
if (filter instanceof EqualFilter) {
clause = CLAUSE_EQUAL;
} else if (filter instanceof GreaterFilter) {
clause = ((GreaterFilter) filter).isEquals() ? CLAUSE_GREATER_OR_EQUAL : CLAUSE_GREATER;
} else if (filter instanceof LessFilter) {
clause = ((LessFilter) filter).isEquals() ? CLAUSE_LESS_OR_EQUAL : CLAUSE_LESS;
} else {
throw new IllegalStateException();
}
map.put(clause, serializeValueFilter(filter, xnodeSerializer));
return map;
}
private static <V extends PrismValue, D extends ItemDefinition> MapXNode serializeValueFilter(ValueFilter<V,D> filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException {
MapXNode map = new MapXNode();
serializeMatchingRule(filter, map);
serializePath(map, filter.getFullPath(), filter);
List<V> values = filter.getValues();
if (values != null) {
ListXNode valuesNode = new ListXNode();
for (V val : values) {
if (val.getParent() == null) {
val.setParent(filter);
}
XNode valNode = xnodeSerializer.definition(filter.getDefinition()).serialize(val).getSubnode();
if (filter instanceof RefFilter) { // TODO shouldn't we do this in all cases?
valNode.setExplicitTypeDeclaration(true);
if (valNode.getTypeQName() == null) {
valNode.setTypeQName(ObjectReferenceType.COMPLEX_TYPE);
}
}
valuesNode.add(valNode);
}
map.put(ELEMENT_VALUE, valuesNode);
}
if (filter.getRightHandSidePath() != null) {
map.put(ELEMENT_RIGHT_HAND_SIDE_PATH, createPrimitiveXNode(filter.getRightHandSidePath().asItemPathType(), ItemPathType.COMPLEX_TYPE));
}
ExpressionWrapper xexpression = filter.getExpression();
if (xexpression != null) {
//map.merge(xexpression);
//TODO serialize expression
}
return map;
}
private static MapXNode serializeRefFilter(RefFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException {
MapXNode map = createFilter(CLAUSE_REF, serializeValueFilter(filter, xnodeSerializer));
return map;
}
private static <T> MapXNode serializeSubstringFilter(SubstringFilter<T> filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException{
MapXNode map = new MapXNode();
MapXNode content = serializeValueFilter(filter, xnodeSerializer);
if (filter.isAnchorStart()) {
content.put(ELEMENT_ANCHOR_START, new PrimitiveXNode<>(true));
}
if (filter.isAnchorEnd()) {
content.put(ELEMENT_ANCHOR_END, new PrimitiveXNode<>(true));
}
map.put(CLAUSE_SUBSTRING, content);
return map;
}
private static MapXNode serializeTypeFilter(TypeFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException{
MapXNode content = new MapXNode();
content.put(ELEMENT_TYPE, createPrimitiveXNode(filter.getType(), DOMUtil.XSD_QNAME));
if (filter.getFilter() != null){
content.put(ELEMENT_FILTER, serializeFilter(filter.getFilter(), xnodeSerializer));
}
return createFilter(CLAUSE_TYPE, content);
}
private static MapXNode serializeExistsFilter(ExistsFilter filter, PrismSerializer<RootXNode> xnodeSerializer) throws SchemaException{
MapXNode content = new MapXNode();
serializePath(content, filter.getFullPath(), filter);
if (filter.getFilter() != null){
content.put(ELEMENT_FILTER, serializeFilter(filter.getFilter(), xnodeSerializer));
}
return createFilter(CLAUSE_EXISTS, content);
}
private static MapXNode serializeOrgFilter(OrgFilter filter, PrismSerializer<RootXNode> xnodeSerializer) {
MapXNode map = new MapXNode();
if (filter.getOrgRef() != null) {
MapXNode orgRefMap = new MapXNode();
orgRefMap.put(ELEMENT_OID, createPrimitiveXNode(filter.getOrgRef().getOid(), DOMUtil.XSD_STRING));
map.put(ELEMENT_ORG_REF, orgRefMap);
}
if (filter.getScope() != null) {
map.put(ELEMENT_SCOPE, createPrimitiveXNode(filter.getScope().name(), DOMUtil.XSD_STRING));
}
if (filter.isRoot()) {
map.put(ELEMENT_IS_ROOT, createPrimitiveXNode(Boolean.TRUE, DOMUtil.XSD_BOOLEAN));
}
return createFilter(CLAUSE_ORG, map);
}
private static MapXNode serializeAllFilter() {
return createFilter(CLAUSE_ALL, new MapXNode());
}
private static MapXNode serializeNoneFilter() {
return createFilter(CLAUSE_NONE, new MapXNode());
}
private static MapXNode serializeUndefinedFilter() {
return createFilter(CLAUSE_UNDEFINED, new MapXNode());
}
private static void serializeMatchingRule(ValueFilter filter, MapXNode map) {
if (filter.getMatchingRule() != null){
PrimitiveXNode<String> matchingNode = createPrimitiveXNode(filter.getMatchingRule().getLocalPart(), DOMUtil.XSD_STRING);
map.put(ELEMENT_MATCHING, matchingNode);
}
}
private static void serializePath(MapXNode map, ItemPath path, ObjectFilter filter) {
if (path == null) {
throw new IllegalStateException("Cannot serialize filter " + filter + " because it does not contain path");
}
map.put(ELEMENT_PATH, createPrimitiveXNode(path.asItemPathType(), ItemPathType.COMPLEX_TYPE));
}
private static <T> XNode serializePropertyValue(PrismPropertyValue<T> value, PrismPropertyDefinition<T> definition, BeanMarshaller beanConverter) throws SchemaException {
QName typeQName = definition.getTypeName();
T realValue = value.getValue();
if (beanConverter.canProcess(typeQName)) {
return beanConverter.marshall(realValue);
} else {
// primitive value
return createPrimitiveXNode(realValue, typeQName);
}
}
private static <T> PrimitiveXNode<T> createPrimitiveXNode(T val, QName type) {
PrimitiveXNode<T> xprim = new PrimitiveXNode<>();
xprim.setValue(val, type);
return xprim;
}
// TODO what with this? [med]
public static void revive (ObjectFilter filter, final PrismContext prismContext) throws SchemaException {
// Visitor visitor = new Visitor() {
// @Override
// public void visit(ObjectFilter filter) {
// if (filter instanceof PropertyValueFilter<?>) {
// try {
// parseExpression((PropertyValueFilter<?>)filter, prismContext);
// } catch (SchemaException e) {
// throw new TunnelException(e);
// }
// }
// }
// };
// try {
// filter.accept(visitor);
// } catch (TunnelException te) {
// SchemaException e = (SchemaException) te.getCause();
// throw e;
// }
}
/**
* Tries to parse as much from filter as possible, without knowing the definition of object(s) to which the
* filter will be applied. It is used mainly to parse path specifications, in order to avoid namespace loss
* when serializing raw (unparsed) paths and QNames - see MID-1969.
*
* @param xfilter
* @param prismContext
*/
public static void parseFilterPreliminarily(MapXNode xfilter, PrismContext prismContext) throws SchemaException {
parseFilterInternal(xfilter, null, true, prismContext);
}
// public PrismPropertyValue parsePrismPropertyFromGlobalXNodeValue(Entry<QName, XNode> entry, ParsingContext pc) throws SchemaException {
// Validate.notNull(entry);
//
// QName globalElementName = entry.getKey();
// if (globalElementName == null) {
// throw new SchemaException("No global element name to look for");
// }
// ItemDefinition itemDefinition = getSchemaRegistry().resolveGlobalItemDefinition(globalElementName);
// if (itemDefinition == null) {
// throw new SchemaException("No definition for item " + globalElementName);
// }
//
// if (itemDefinition instanceof PrismPropertyDefinition) {
// PrismProperty prismProperty = parsePrismProperty(entry.getValue(), globalElementName, (PrismPropertyDefinition) itemDefinition, pc);
// if (prismProperty.size() > 1) {
// throw new SchemaException("Retrieved more than one value from globally defined element " + globalElementName);
// } else if (prismProperty.size() == 0) {
// return null;
// } else {
// return (PrismPropertyValue) prismProperty.getValues().get(0);
// }
// } else {
// throw new IllegalArgumentException("Parsing global elements with definitions other than PrismPropertyDefinition is not supported yet: element = " + globalElementName + " definition kind = " + itemDefinition.getClass().getSimpleName());
// }
// }
}