![JAR search and dependency download from the Maven repository](/logo.png)
io.github.perplexhub.rsql.RSQLJPAPredicateConverter Maven / Gradle / Ivy
The newest version!
package io.github.perplexhub.rsql;
import static io.github.perplexhub.rsql.RSQLOperators.*;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import io.github.perplexhub.rsql.jsonb.JsonbSupport;
import jakarta.persistence.criteria.*;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.Attribute.PersistentAttributeType;
import jakarta.persistence.metamodel.IdentifiableType;
import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.SingularAttribute;
import cz.jirutka.rsql.parser.ast.AndNode;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import cz.jirutka.rsql.parser.ast.ComparisonOperator;
import cz.jirutka.rsql.parser.ast.OrNode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@SuppressWarnings({ "rawtypes", "unchecked" })
public class RSQLJPAPredicateConverter extends RSQLVisitorBase {
private final CriteriaBuilder builder;
private final Map cachedJoins = new HashMap<>();
private final @Getter Map propertyPathMapper;
private final @Getter Map> customPredicates;
private final @Getter Map joinHints;
private final Collection procedureWhiteList;
private final Collection procedureBlackList;
private final boolean strictEquality;
private final Character likeEscapeCharacter;
public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper) {
this(builder, propertyPathMapper, null, null);
}
public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper, List> customPredicates) {
this(builder, propertyPathMapper, customPredicates, null);
}
public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper, List> customPredicates, Map joinHints) {
this(builder, propertyPathMapper, customPredicates, joinHints, null, null, false, null);
}
public RSQLJPAPredicateConverter(CriteriaBuilder builder, Map propertyPathMapper, List> customPredicates, Map joinHints, Collection procedureWhiteList, Collection procedureBlackList) {
this(builder, propertyPathMapper, customPredicates, joinHints, procedureWhiteList, procedureBlackList, false, null);
}
public RSQLJPAPredicateConverter(CriteriaBuilder builder,
Map propertyPathMapper,
List> customPredicates,
Map joinHints,
Collection proceduresWhiteList,
Collection proceduresBlackList,
boolean strictEquality,
Character likeEscapeCharacter) {
this.builder = builder;
this.propertyPathMapper = propertyPathMapper != null ? propertyPathMapper : Collections.emptyMap();
this.customPredicates = customPredicates != null ? customPredicates.stream().collect(Collectors.toMap(RSQLCustomPredicate::getOperator, Function.identity(), (a, b) -> a)) : Collections.emptyMap();
this.joinHints = joinHints != null ? joinHints : Collections.emptyMap();
this.procedureWhiteList = proceduresWhiteList != null ? proceduresWhiteList : Collections.emptyList();
this.procedureBlackList = proceduresBlackList != null ? proceduresBlackList : Collections.emptyList();
this.strictEquality = strictEquality;
this.likeEscapeCharacter = likeEscapeCharacter;
}
RSQLJPAContext findPropertyPath(String propertyPath, Path startRoot) {
return findPropertyPathInternal(propertyPath, startRoot, true);
}
private RSQLJPAContext findPropertyPathInternal(String propertyPath, Path startRoot, boolean firstTry) {
Class type = startRoot.getJavaType();
ManagedType> classMetadata = getManagedType(type);
ManagedType> previousClassMetadata = null;
Path> root = startRoot;
Attribute, ?> attribute = null;
String resolvedPropertyPath = firstTry? mapPropertyPath(propertyPath) : propertyPath;
String[] properties = mapPropertyPath(resolvedPropertyPath).split("\\.");
for (int i = 0, propertiesLength = properties.length; i < propertiesLength; i++) {
String property = properties[i];
String mappedProperty = mapProperty(property, classMetadata.getJavaType());
if (!mappedProperty.equals(property)) {
RSQLJPAContext context = findPropertyPathInternal(mappedProperty, root, firstTry);
root = context.getPath();
attribute = context.getAttribute();
classMetadata = context.getManagedType();
} else {
if (!hasPropertyName(mappedProperty, classMetadata)) {
Optional mayBeJSonPath = PathUtils
.findMappingOnBeginning(propertyPath, propertyPathMapper);
//firstTry check to avoid stack overflow on cyclic mapping
if(firstTry && mayBeJSonPath.isPresent()) {
//Try with path mapping that matches just the beginning of the expression if json
return findPropertyPathInternal(mayBeJSonPath.get(), startRoot, false);
}
throw new UnknownPropertyException(mappedProperty, classMetadata.getJavaType());
}
if (isAssociationType(mappedProperty, classMetadata) && !property.equals(resolvedPropertyPath)) {
boolean isOneToAssociationType = isOneToOneAssociationType(mappedProperty, classMetadata) || isOneToManyAssociationType(mappedProperty, classMetadata);
Class> associationType = findPropertyType(mappedProperty, classMetadata);
type = associationType;
String previousClass = classMetadata.getJavaType().getName();
previousClassMetadata = classMetadata;
classMetadata = getManagedType(associationType);
String keyJoin = getKeyJoin(root, mappedProperty);
if (isOneToAssociationType) {
if (joinHints.containsKey(keyJoin)) {
log.debug("Create a join between [{}] and [{}] using key [{}] with supplied hints", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = join(keyJoin, root, mappedProperty, joinHints.get(keyJoin));
} else {
log.debug("Create a join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = join(keyJoin, root, mappedProperty, JoinType.LEFT);
}
} else {
String lookAheadProperty = i < propertiesLength - 1 ? properties[i + 1] : null;
boolean lookAheadPropertyIsId = false;
if (!isManyToManyAssociationType(mappedProperty, previousClassMetadata) && classMetadata instanceof IdentifiableType && lookAheadProperty != null) {
final IdentifiableType identifiableType = (IdentifiableType) classMetadata;
final SingularAttribute id = identifiableType.getId(identifiableType.getIdType().getJavaType());
if (identifiableType.hasSingleIdAttribute() && id.isId() && id.getName().equals(lookAheadProperty)) {
lookAheadPropertyIsId = true;
}
}
if (lookAheadPropertyIsId || lookAheadProperty == null) {
log.debug("Create property path for type [{}] property [{}]", classMetadata.getJavaType().getName(), mappedProperty);
root = root.get(mappedProperty);
} else {
log.debug("Create a join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = join(keyJoin, root, mappedProperty, joinHints.get(keyJoin));
}
}
} else if (isElementCollectionType(mappedProperty, classMetadata)) {
String previousClass = classMetadata.getJavaType().getName();
attribute = classMetadata.getAttribute(property);
classMetadata = getManagedElementCollectionType(mappedProperty, classMetadata);
String keyJoin = getKeyJoin(root, mappedProperty);
log.debug("Create a element collection join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = join(keyJoin, root, mappedProperty);
} else if (JsonbSupport.isJsonType(mappedProperty, classMetadata)) {
root = root.get(mappedProperty);
attribute = classMetadata.getAttribute(mappedProperty);
break;
} else {
log.debug("Create property path for type [{}] property [{}]", classMetadata.getJavaType().getName(), mappedProperty);
root = root.get(mappedProperty);
if (isEmbeddedType(mappedProperty, classMetadata)) {
Class> embeddedType = findPropertyType(mappedProperty, classMetadata);
type = embeddedType;
classMetadata = getManagedType(embeddedType);
} else {
attribute = classMetadata.getAttribute(property);
}
}
}
}
if (attribute != null) {
accessControl(type, attribute.getName());
}
return RSQLJPAContext.of(root, attribute, classMetadata);
}
private String getKeyJoin(Path> root, String mappedProperty) {
return root.getJavaType().getSimpleName().concat(".").concat(mappedProperty);
}
protected Path> join(String keyJoin, Path> root, String mappedProperty) {
return join(keyJoin, root, mappedProperty, null);
}
protected Path> join(String keyJoin, Path> root, String mappedProperty, JoinType joinType) {
log.debug("join(keyJoin:{},root:{},mappedProperty:{},joinType:{})", keyJoin, root, mappedProperty, joinType);
if (cachedJoins.containsKey(keyJoin)) {
root = cachedJoins.get(keyJoin);
} else {
root = JoinUtils.getOrCreateJoin((From) root, mappedProperty, joinType);
cachedJoins.put(keyJoin, root);
}
return root;
}
@Override
public Predicate visit(ComparisonNode node, From root) {
log.debug("visit(node:{},root:{})", node, root);
ComparisonOperator op = node.getOperator();
if (customPredicates.containsKey(op)) {
RSQLCustomPredicate> customPredicate = customPredicates.get(op);
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy