/* * Copyright 2015-2016 Red Hat, Inc. and/or its affiliates * and other contributors as indicated by the @author tags. * * 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 org.hawkular.inventory.impl.tinkerpop; import static org.hawkular.inventory.impl.tinkerpop.HawkularTraversal.hwk__; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.tinkerpop.gremlin.process.traversal.Step; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal; import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeOtherVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.EdgeVertexStep; import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep; import org.hawkular.inventory.api.FilterFragment; import org.hawkular.inventory.api.Query; import org.hawkular.inventory.api.QueryFragment; import org.hawkular.inventory.api.filters.Contained; import org.hawkular.inventory.api.filters.Defined; import org.hawkular.inventory.api.filters.Filter; import org.hawkular.inventory.api.filters.Incorporated; import org.hawkular.inventory.api.filters.Marker; import org.hawkular.inventory.api.filters.RecurseFilter; import org.hawkular.inventory.api.filters.Related; import org.hawkular.inventory.api.filters.RelationWith; import org.hawkular.inventory.api.filters.SwitchElementType; import org.hawkular.inventory.api.filters.With; import org.hawkular.inventory.base.spi.Discriminator; import org.hawkular.inventory.base.spi.NoopFilter; /** * A filter applicator applies a filter to a Gremlin query. * * @author Lukas Krejci * @author Jirka Kremser * @see FilterVisitor * @since 0.0.1 */ abstract class FilterApplicator<T extends Filter> { private static Map<Class<? extends Filter>, Class<? extends FilterApplicator<?>>> applicators; static { applicators = new HashMap<>(); applicators.put(Related.class, RelatedApplicator.class); applicators.put(Contained.class, RelatedApplicator.class); applicators.put(Defined.class, RelatedApplicator.class); applicators.put(Incorporated.class, RelatedApplicator.class); applicators.put(With.Ids.class, WithIdsApplicator.class); applicators.put(With.Types.class, WithTypesApplicator.class); applicators.put(With.PropertyValues.class, WithPropertyValuesApplicator.class); applicators.put(RelationWith.Ids.class, RelationWithIdsApplicator.class); applicators.put(RelationWith.PropertyValues.class, RelationWithPropertiesApplicator.class); applicators.put(RelationWith.SourceOfType.class, RelationWithSourcesOfTypesApplicator.class); applicators.put(RelationWith.TargetOfType.class, RelationWithTargetsOfTypesApplicator.class); applicators.put(RelationWith.SourceOrTargetOfType.class, RelationWithSourcesOrTargetsOfTypesApplicator.class); applicators.put(SwitchElementType.class, SwitchElementTypeApplicator.class); applicators.put(NoopFilter.class, NoopApplicator.class); applicators.put(With.CanonicalPaths.class, CanonicalPathApplicator.class); applicators.put(With.RelativePaths.class, RelativePathApplicator.class); applicators.put(Marker.class, MarkerApplicator.class); applicators.put(With.DataAt.class, DataAtApplicator.class); applicators.put(With.DataValued.class, DataValuedApplicator.class); applicators.put(With.DataOfTypes.class, DataOfTypesApplicator.class); applicators.put(RecurseFilter.class, RecurseApplicator.class); applicators.put(With.SameIdentityHash.class, SameIdentityHashApplicator.class); applicators.put(With.Names.class, NamesApplicator.class); } protected final T filter; protected final FilterVisitor visitor = new FilterVisitor(); private FilterApplicator(T f) { this.filter = f; } public static FilterApplicator of(Filter filter) { if (filter == null) { throw new IllegalArgumentException("filter == null"); } Class<? extends Filter> filterClazz = filter.getClass(); Class<? extends FilterApplicator<?>> applicatorClazz = applicators.get(filterClazz); if (applicatorClazz == null) { throw new IllegalArgumentException("Unsupported filter type " + filterClazz); } Constructor<? extends FilterApplicator<?>> constructor = null; try { constructor = applicatorClazz.getDeclaredConstructor(filterClazz); } catch (NoSuchMethodException e) { try { // Contained, Defined, Owned constructor = applicatorClazz.getDeclaredConstructor(filterClazz.getSuperclass()); } catch (NoSuchMethodException e1) { throw new IllegalArgumentException("Unable to create an instance of " + applicatorClazz); } } try { constructor.setAccessible(true); return constructor.newInstance(filter); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new IllegalArgumentException("Unable to create an instance of " + applicatorClazz); } } /** * Applies all the filters from the applicator tree to the provided Gremlin query. * * @param filterTree the tree of filters to apply to the query * @param q the query to update with filters from the tree * @param <S> type of the source of the query * @param <E> type of the output of the query */ public static <S, E> void applyAll(Discriminator discriminator, Query filterTree, HawkularTraversal<S, E> q, boolean inEdges) { if (filterTree == null) { return; } QueryTranslationState state = new QueryTranslationState(); state.setInEdges(inEdges); applyAll(discriminator, filterTree, q, false, state); } /** * A private impl of the {@code applyAll()} method that tracks the current type of the filter being applied. * The type of the filter is either a path ({@code isFilter == false}) which potentially progresses the query to * next positions in the inventory traversal or a filter ({@code isFilter == true}) which merely trims down the * number of the elements at the current "tail" of the traversal by applying filters to them. * * @param <S> the start element type of the pipeline * @param <E> the end element type of the pipeline * @param discriminator * @param query the query * @param pipeline the Gremlin pipeline that the query gets translated to * @param isFilter whether we are currently processing filters as filters or path elements @return true if after applying the filters, we're the filtering state or false if we are in path-progression * state. */ @SuppressWarnings("unchecked") private static <S, E> boolean applyAll(Discriminator discriminator, Query query, HawkularTraversal<S, E> pipeline, boolean isFilter, QueryTranslationState state) { QueryTranslationState origState = state.clone(); HawkularTraversal<E, E> workingPipeline = hwk__(); for (QueryFragment qf : query.getFragments()) { boolean thisIsFilter = qf instanceof FilterFragment; if (thisIsFilter != isFilter) { isFilter = thisIsFilter; if (thisIsFilter) { //add the path progressions we had finishPipeline(workingPipeline, state, origState); GraphTraversal.Admin<S, E> admin = pipeline.asAdmin(); workingPipeline.asAdmin().getSteps().forEach(admin::addStep); } else { if (needsRememberingPosition(workingPipeline)) { finishPipeline(workingPipeline, state, origState); pipeline.where(workingPipeline); } else { //add the path progressions we had //finishPipeline(workingPipeline, state, origState); GraphTraversal.Admin<S, E> admin = pipeline.asAdmin(); workingPipeline.asAdmin().getSteps().forEach(admin::addStep); } } workingPipeline = hwk__(); } FilterApplicator.of(qf.getFilter()).applyTo(discriminator, workingPipeline, state); } finishPipeline(workingPipeline, state, origState); boolean remember = isFilter && needsRememberingPosition(workingPipeline); if (remember) { pipeline.where(workingPipeline); } else { GraphTraversal.Admin<S, E> admin = pipeline.asAdmin(); workingPipeline.asAdmin().getSteps().forEach(admin::addStep); } if (query.getSubTrees().isEmpty()) { return remember; } if (query.getSubTrees().size() == 1) { return applyAll(discriminator, query.getSubTrees().get(0), pipeline, isFilter, state); } else { List<GraphTraversal<E, ?>> branches = new ArrayList<>(); Iterator<Query> it = query.getSubTrees().iterator(); // apply the first branch - in here, we know there are at least 2 actually HawkularTraversal<E, ?> branch = hwk__(); // the branch is a brand new pipeline, so it doesn't make sense for it to inherit // our current filter state. applyAll(discriminator, it.next(), branch, false, state.clone()); branches.add(branch); while (it.hasNext()) { branch = hwk__(); applyAll(discriminator, it.next(), branch, false, state.clone()); branches.add(branch); } pipeline.union(branches.toArray(new GraphTraversal[branches.size()])); finishPipeline(pipeline, state, origState); return isFilter; } } static <S, E> void finishPipeline(GraphTraversal<S, E> pipeline, QueryTranslationState state, QueryTranslationState originalState) { if (state.isExplicitChange()) { return; } if (originalState.isInEdges() != state.isInEdges()) { if (originalState.isInEdges()) { switch (originalState.getComingFrom()) { case IN: pipeline.outE(); break; case OUT: pipeline.inE(); break; case BOTH: pipeline.bothE(); } } else { switch (state.getComingFrom()) { case IN: pipeline.outV(); break; case OUT: pipeline.inV(); break; case BOTH: pipeline.bothV(); } } } //we've moved back to the state as it was originally. reflect that. state.setInEdges(originalState.isInEdges()); state.setComingFrom(originalState.getComingFrom()); } private static boolean needsRememberingPosition(GraphTraversal<?, ?> pipeline) { for (Step<?, ?> p : pipeline.asAdmin().getSteps()) { if (p instanceof VertexStep || p instanceof EdgeVertexStep || p instanceof EdgeOtherVertexStep) { return true; } } return false; } /** * To be implemented by inheritors, this applies the filter this applicator holds to the provided query taking into * the account the type of the filter. * @param discriminator the discriminator to apply to the query * @param query the query to update with filter * @param state the query traversal translation state */ public abstract void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state); public Filter filter() { return filter; } @Override public String toString() { return "FilterApplicator[filter=" + filter + "]"; } private static final class RelatedApplicator extends FilterApplicator<Related> { private RelatedApplicator(Related filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class WithIdsApplicator extends FilterApplicator<With.Ids> { private WithIdsApplicator(With.Ids filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class WithTypesApplicator extends FilterApplicator<With.Types> { private WithTypesApplicator(With.Types filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class RelationWithIdsApplicator extends FilterApplicator<RelationWith.Ids> { private RelationWithIdsApplicator(RelationWith.Ids filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class RelationWithPropertiesApplicator extends FilterApplicator<RelationWith.PropertyValues> { private RelationWithPropertiesApplicator(RelationWith.PropertyValues filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class RelationWithSourcesOfTypesApplicator extends FilterApplicator<RelationWith.SourceOfType> { private RelationWithSourcesOfTypesApplicator(RelationWith.SourceOfType filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class RelationWithTargetsOfTypesApplicator extends FilterApplicator<RelationWith.TargetOfType> { private RelationWithTargetsOfTypesApplicator(RelationWith.TargetOfType filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class RelationWithSourcesOrTargetsOfTypesApplicator extends FilterApplicator<RelationWith.SourceOrTargetOfType> { private RelationWithSourcesOrTargetsOfTypesApplicator(RelationWith.SourceOrTargetOfType filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class SwitchElementTypeApplicator extends FilterApplicator<SwitchElementType> { private SwitchElementTypeApplicator(SwitchElementType filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class NoopApplicator extends FilterApplicator<NoopFilter> { private NoopApplicator(NoopFilter filter) { super(filter); } public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(query, filter, state); } } private static final class WithPropertyValuesApplicator extends FilterApplicator<With.PropertyValues> { private WithPropertyValuesApplicator(With.PropertyValues f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class CanonicalPathApplicator extends FilterApplicator<With.CanonicalPaths> { private CanonicalPathApplicator(With.CanonicalPaths f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class RelativePathApplicator extends FilterApplicator<With.RelativePaths> { private RelativePathApplicator(With.RelativePaths f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class MarkerApplicator extends FilterApplicator<Marker> { private MarkerApplicator(Marker f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class DataAtApplicator extends FilterApplicator<With.DataAt> { private DataAtApplicator(With.DataAt f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class DataValuedApplicator extends FilterApplicator<With.DataValued> { private DataValuedApplicator(With.DataValued f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(query, filter, state); } } private static final class DataOfTypesApplicator extends FilterApplicator<With.DataOfTypes> { private DataOfTypesApplicator(With.DataOfTypes f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(query, filter, state); } } private static final class RecurseApplicator extends FilterApplicator<RecurseFilter> { private RecurseApplicator(RecurseFilter f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class SameIdentityHashApplicator extends FilterApplicator<With.SameIdentityHash> { private SameIdentityHashApplicator(With.SameIdentityHash f) { super(f); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } private static final class NamesApplicator extends FilterApplicator<With.Names> { private NamesApplicator(With.Names names) { super(names); } @Override public void applyTo(Discriminator discriminator, HawkularTraversal<?, ?> query, QueryTranslationState state) { visitor.visit(discriminator, query, filter, state); } } }