package org.infinispan.objectfilter.impl.syntax.parser;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.tree.Tree;
import org.infinispan.objectfilter.impl.logging.Log;
import org.infinispan.objectfilter.impl.ql.JoinType;
import org.infinispan.objectfilter.impl.ql.PropertyPath;
import org.infinispan.objectfilter.impl.ql.QueryResolverDelegate;
import org.jboss.logging.Logger;
/**
* @author anistor@redhat.com
* @since 7.0
*/
final class QueryResolverDelegateImpl<TypeMetadata> implements QueryResolverDelegate<TypeDescriptor<TypeMetadata>> {
private static final Log log = Logger.getMessageLogger(Log.class, QueryResolverDelegateImpl.class.getName());
private final Map<String, String> aliasToEntityType = new HashMap<>();
private final Map<String, PropertyPath<TypeDescriptor<TypeMetadata>>> aliasToPropertyPath = new HashMap<>();
private final ObjectPropertyHelper<TypeMetadata> propertyHelper;
private String targetType;
private TypeMetadata entityMetadata;
private String alias;
private enum Phase {
SELECT,
FROM
}
private Phase phase;
QueryResolverDelegateImpl(ObjectPropertyHelper<TypeMetadata> propertyHelper) {
this.propertyHelper = propertyHelper;
}
@Override
public void registerPersisterSpace(String entityName, Tree aliasTree) {
String aliasText = aliasTree.getText();
String prevAlias = aliasToEntityType.put(aliasText, entityName);
if (prevAlias != null && !prevAlias.equalsIgnoreCase(entityName)) {
throw new UnsupportedOperationException("Alias reuse currently not supported: aliasText " + aliasText + " already assigned to type " + prevAlias);
}
if (targetType != null) {
throw new IllegalStateException("Can't target multiple types: " + targetType + " already selected before " + entityName);
}
targetType = entityName;
entityMetadata = propertyHelper.getEntityMetadata(entityName);
if (entityMetadata == null) {
throw log.getUnknownEntity(entityName);
}
}
@Override
public void registerJoinAlias(Tree alias, PropertyPath<TypeDescriptor<TypeMetadata>> path) {
if (!path.isEmpty() && !aliasToPropertyPath.containsKey(alias.getText())) {
aliasToPropertyPath.put(alias.getText(), path);
}
}
@Override
public boolean isUnqualifiedPropertyReference() {
return true;
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizeUnqualifiedPropertyReference(Tree propertyNameTree) {
String property = propertyNameTree.getText();
if (aliasToEntityType.containsKey(property)) {
return normalizeQualifiedRoot(propertyNameTree);
}
EntityTypeDescriptor<TypeMetadata> type = new EntityTypeDescriptor<>(targetType, entityMetadata);
return normalizeProperty(type, Collections.emptyList(), property);
}
@Override
public boolean isPersisterReferenceAlias() {
return aliasToEntityType.containsKey(alias);
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizeUnqualifiedRoot(Tree aliasTree) {
String alias = aliasTree.getText();
if (aliasToEntityType.containsKey(alias)) {
return normalizeQualifiedRoot(aliasTree);
}
PropertyPath<TypeDescriptor<TypeMetadata>> propertyPath = aliasToPropertyPath.get(alias);
if (propertyPath == null) {
throw log.getUnknownAliasException(alias);
}
List<String> resolvedAlias = resolveAlias(propertyPath);
TypeDescriptor<TypeMetadata> sourceType = propertyPath.getFirst().getTypeDescriptor();
EmbeddedEntityTypeDescriptor<TypeMetadata> type = new EmbeddedEntityTypeDescriptor<>(sourceType.getTypeName(), sourceType.getTypeMetadata(), resolvedAlias);
return new PropertyPath.PropertyReference<>(alias, type, true);
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizeQualifiedRoot(Tree root) {
String alias = root.getText();
String entityNameForAlias = aliasToEntityType.get(alias);
if (entityNameForAlias == null) {
PropertyPath<TypeDescriptor<TypeMetadata>> propertyPath = aliasToPropertyPath.get(alias);
if (propertyPath == null) {
throw log.getUnknownAliasException(alias);
}
return new PropertyPath.PropertyReference<>(propertyPath.asStringPathWithoutAlias(), null, false);
}
TypeMetadata entityMetadata = propertyHelper.getEntityMetadata(entityNameForAlias);
if (entityMetadata == null) {
throw log.getUnknownEntity(entityNameForAlias);
}
return new PropertyPath.PropertyReference<>(alias, new EntityTypeDescriptor<>(entityNameForAlias, entityMetadata), true);
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizePropertyPathIntermediary(PropertyPath<TypeDescriptor<TypeMetadata>> path, Tree propertyNameTree) {
String propertyName = propertyNameTree.getText();
TypeDescriptor<TypeMetadata> sourceType = path.getLast().getTypeDescriptor();
if (!propertyHelper.hasProperty(sourceType.getTypeMetadata(), sourceType.makePath(propertyName))) {
throw log.getNoSuchPropertyException(sourceType.getTypeName(), propertyName);
}
List<String> newPath = resolveAlias(path);
newPath.add(propertyName);
EmbeddedEntityTypeDescriptor<TypeMetadata> type = new EmbeddedEntityTypeDescriptor<>(sourceType.getTypeName(), sourceType.getTypeMetadata(), newPath);
return new PropertyPath.PropertyReference<>(propertyName, type, false);
}
private List<String> resolveAlias(PropertyPath<TypeDescriptor<TypeMetadata>> path) {
if (path.isAlias()) {
String alias = path.getFirst().getPropertyName();
if (aliasToEntityType.containsKey(alias)) {
// Alias for entity
return path.getNodeNamesWithoutAlias();
} else if (aliasToPropertyPath.containsKey(alias)) {
// Alias for embedded
PropertyPath<TypeDescriptor<TypeMetadata>> propertyPath = aliasToPropertyPath.get(alias);
List<String> resolvedAlias = resolveAlias(propertyPath);
resolvedAlias.addAll(path.getNodeNamesWithoutAlias());
return resolvedAlias;
} else {
// Alias not found
throw log.getUnknownAliasException(alias);
}
}
// It does not start with an alias
return path.getNodeNamesWithoutAlias();
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizeIntermediateIndexOperation(PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> propertyReference, Tree collectionProperty, Tree selector) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public void normalizeTerminalIndexOperation(PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> propertyReference, Tree collectionProperty, Tree selector) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizeUnqualifiedPropertyReferenceSource(Tree identifier) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizePropertyPathTerminus(PropertyPath<TypeDescriptor<TypeMetadata>> path, Tree propertyNameTree) {
// receives the property name on a specific entity reference _source_
return normalizeProperty(path.getLast().getTypeDescriptor(), path.getNodeNamesWithoutAlias(), propertyNameTree.getText());
}
private PropertyPath.PropertyReference<TypeDescriptor<TypeMetadata>> normalizeProperty(TypeDescriptor<TypeMetadata> type, List<String> path, String propertyName) {
String[] propertyPath = type.makePath(propertyName);
if (!propertyHelper.hasProperty(type.getTypeMetadata(), propertyPath)) {
throw log.getNoSuchPropertyException(type.getTypeName(), propertyName);
}
TypeDescriptor<TypeMetadata> propType = null;
if (propertyHelper.hasEmbeddedProperty(type.getTypeMetadata(), propertyPath)) {
List<String> newPath = new LinkedList<>(path);
newPath.add(propertyName);
propType = new EmbeddedEntityTypeDescriptor<>(type.getTypeName(), type.getTypeMetadata(), newPath);
}
return new PropertyPath.PropertyReference<>(propertyName, propType, false);
}
@Override
public void activateFromStrategy(JoinType joinType, Tree associationFetchTree, Tree propertyFetchTree, Tree aliasTree) {
phase = Phase.FROM;
alias = aliasTree.getText();
}
@Override
public void activateSelectStrategy() {
phase = Phase.SELECT;
}
@Override
public void deactivateStrategy() {
phase = null;
alias = null;
}
@Override
public void propertyPathCompleted(PropertyPath<TypeDescriptor<TypeMetadata>> path) {
if (phase == Phase.SELECT) {
TypeDescriptor<TypeMetadata> type = path.getLast().getTypeDescriptor();
if (type instanceof EmbeddedEntityTypeDescriptor<?>) {
throw log.getProjectionOfCompleteEmbeddedEntitiesNotSupportedException(type.getTypeName(), path.asStringPathWithoutAlias());
}
}
}
}