blob: a822bb1aad469e58a5db25b601b4f66bf24e77bd [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.syncope.core.persistence.api.search;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.ext.search.ConditionType;
import org.apache.cxf.jaxrs.ext.search.SearchBean;
import org.apache.cxf.jaxrs.ext.search.SearchCondition;
import org.apache.cxf.jaxrs.ext.search.SearchUtils;
import org.apache.cxf.jaxrs.ext.search.visitor.AbstractSearchConditionVisitor;
import org.apache.syncope.common.lib.search.SpecialAttr;
import org.apache.syncope.common.lib.search.SyncopeFiqlParser;
import org.apache.syncope.common.lib.search.SyncopeFiqlSearchCondition;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
import org.identityconnectors.framework.common.objects.filter.Filter;
import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
public class FilterVisitor extends AbstractSearchConditionVisitor<SearchBean, Filter> {
private Filter filter;
private final Set<String> attrs = new HashSet<>();
public FilterVisitor() {
super(null);
}
private Filter visitPrimitive(final SearchCondition<SearchBean> sc) {
String name = getRealPropertyName(sc.getStatement().getProperty());
Optional<SpecialAttr> specialAttrName = SpecialAttr.fromString(name);
String value = SearchUtils.toSqlWildcardString(
URLDecoder.decode(sc.getStatement().getValue().toString(), StandardCharsets.UTF_8), false).
replaceAll("\\\\_", "_");
Optional<SpecialAttr> specialAttrValue = SpecialAttr.fromString(value);
ConditionType ct = sc.getConditionType();
if (sc instanceof SyncopeFiqlSearchCondition && sc.getConditionType() == ConditionType.CUSTOM) {
SyncopeFiqlSearchCondition<SearchBean> sfsc = (SyncopeFiqlSearchCondition<SearchBean>) sc;
switch (sfsc.getOperator()) {
case SyncopeFiqlParser.IEQ:
ct = ConditionType.EQUALS;
break;
case SyncopeFiqlParser.NIEQ:
ct = ConditionType.NOT_EQUALS;
break;
default:
throw new IllegalArgumentException(
String.format("Condition type %s is not supported", sfsc.getOperator()));
}
}
Attribute attr = AttributeBuilder.build(name, value);
attrs.add(name);
Filter leaf;
switch (ct) {
case EQUALS:
case NOT_EQUALS:
if (specialAttrName.isEmpty()) {
if (specialAttrValue.isPresent() && specialAttrValue.get() == SpecialAttr.NULL) {
Filter empty = FilterBuilder.startsWith(AttributeBuilder.build(name, StringUtils.EMPTY));
if (ct == ConditionType.NOT_EQUALS) {
leaf = empty;
} else {
leaf = FilterBuilder.not(empty);
attrs.remove(name);
}
} else {
if (value.indexOf('%') == -1) {
leaf = sc.getConditionType() == ConditionType.CUSTOM
? FilterBuilder.equalsIgnoreCase(attr)
: FilterBuilder.equalTo(attr);
} else if (sc.getConditionType() != ConditionType.CUSTOM && value.startsWith("%")) {
leaf = FilterBuilder.endsWith(
AttributeBuilder.build(name, value.substring(1)));
} else if (sc.getConditionType() != ConditionType.CUSTOM && value.endsWith("%")) {
leaf = FilterBuilder.startsWith(
AttributeBuilder.build(name, value.substring(0, value.length() - 1)));
} else {
throw new IllegalArgumentException(
String.format("Unsupported search value %s", value));
}
if (ct == ConditionType.NOT_EQUALS) {
leaf = FilterBuilder.not(leaf);
}
}
} else {
throw new IllegalArgumentException(
String.format("Special attr name %s is not supported", specialAttrName));
}
break;
case GREATER_OR_EQUALS:
leaf = FilterBuilder.greaterThanOrEqualTo(attr);
break;
case GREATER_THAN:
leaf = FilterBuilder.greaterThan(attr);
break;
case LESS_OR_EQUALS:
leaf = FilterBuilder.lessThanOrEqualTo(attr);
break;
case LESS_THAN:
leaf = FilterBuilder.lessThan(attr);
break;
default:
throw new IllegalArgumentException(String.format("Condition type %s is not supported", ct.name()));
}
return leaf;
}
private Filter visitCompount(final SearchCondition<SearchBean> sc) {
List<Filter> searchConds = new ArrayList<>();
sc.getSearchConditions().forEach(searchCond -> {
searchConds.add(searchCond.getStatement() == null
? visitCompount(searchCond)
: visitPrimitive(searchCond));
});
Filter compound;
switch (sc.getConditionType()) {
case AND:
compound = FilterBuilder.and(searchConds);
break;
case OR:
compound = FilterBuilder.or(searchConds);
break;
default:
throw new IllegalArgumentException(
String.format("Condition type %s is not supported", sc.getConditionType().name()));
}
return compound;
}
@Override
public void visit(final SearchCondition<SearchBean> sc) {
filter = sc.getStatement() == null
? visitCompount(sc)
: visitPrimitive(sc);
}
@Override
public Filter getQuery() {
return filter;
}
public Set<String> getAttrs() {
return attrs;
}
}