Java tutorial
/** * Copyright 2010 - 2015 JetBrains s.r.o. * * 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 jetbrains.exodus.query; import jetbrains.exodus.entitystore.Entity; import jetbrains.exodus.entitystore.EntityIterable; import jetbrains.exodus.entitystore.Explainer; import jetbrains.exodus.entitystore.PersistentEntityStoreImpl; import jetbrains.exodus.entitystore.iterate.EntityIterableBase; import jetbrains.exodus.entitystore.metadata.EntityMetaData; import jetbrains.exodus.entitystore.metadata.ModelMetaData; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @SuppressWarnings({ "HardcodedLineSeparator", "AssignmentToMethodParameter", "ConstructorWithTooManyParameters" }) public class TreeKeepingEntityIterable extends StaticTypedEntityIterable { private static final Log log = LogFactory.getLog(TreeKeepingEntityIterable.class); private static final boolean unionSubtypesResults = Boolean .getBoolean("jetbrains.exodus.query.unionSubtypesResults"); private final Iterable<Entity> instance; private final NodeBase sourceTree; private NodeBase optimizedTree; private Sorts sorts; private Object origin; private String strippedStacktrace; String annotatedTree; private final boolean isExplainOn; public TreeKeepingEntityIterable(@Nullable final Iterable<Entity> entityIterable, @NotNull String entityType, @NotNull final NodeBase queryTree, @Nullable String leftChildPresentation, @Nullable String rightChildPresentation, @NotNull QueryEngine queryEngine) { super(queryEngine); final Explainer explainer = queryEngine.getPersistentStore().getExplainer(); isExplainOn = explainer.isExplainOn(); origin = explainer.genOrigin(); if (isExplainOn) { strippedStacktrace = Explainer.stripStackTrace(new Throwable()); } // get entityType from iterable if (entityIterable instanceof StaticTypedEntityIterable) { final String entityIterableType = ((StaticTypedEntityIterable) entityIterable).getEntityType(); if (!(entityType.equals(entityIterableType)) && Utils.isTypeOf(entityIterableType, entityType, queryEngine.getModelMetaData())) { entityType = entityIterableType; } } // if (isExplainOn) { if (queryTree instanceof BinaryOperator && (leftChildPresentation != null || rightChildPresentation != null)) { BinaryOperator binaryOperator = (BinaryOperator) queryTree; if (leftChildPresentation == null) { leftChildPresentation = binaryOperator.getLeft().toString(); } if (rightChildPresentation == null) { rightChildPresentation = binaryOperator.getRight().toString(); } annotatedTree = "at " + strippedStacktrace + '\n' + binaryOperator.getClass().getSimpleName() + ('\n' + leftChildPresentation + '\n' + rightChildPresentation).replace("\n", '\n' + NodeBase.TREE_LEVEL_INDENT); } else { annotatedTree = "at " + strippedStacktrace + '\n' + queryTree; } } if (entityIterable instanceof TreeKeepingEntityIterable) { final TreeKeepingEntityIterable instanceTreeIt = (TreeKeepingEntityIterable) entityIterable; final NodeBase instanceTree = instanceTreeIt.sourceTree; if (queryTree instanceof Sort && ((UnaryNode) queryTree).getChild().equals(NodeFactory.all())) { sourceTree = queryTree.getClone(); sourceTree.replaceChild(((UnaryNode) sourceTree).getChild(), instanceTree.getClone()); if (isExplainOn) { annotatedTree = "at " + strippedStacktrace + '\n' + sourceTree.getClass().getSimpleName() + ("\n" + (instanceTreeIt.annotatedTree != null ? instanceTreeIt.annotatedTree : instanceTree)).replace("\n", '\n' + NodeBase.TREE_LEVEL_INDENT); } } else { sourceTree = instanceTree instanceof GetAll ? queryTree : new And(instanceTree.getClone(), queryTree); if (isExplainOn && !(instanceTree instanceof GetAll)) { annotatedTree = "at " + strippedStacktrace + "\nAnd" + ("\n" + (instanceTreeIt.annotatedTree != null ? instanceTreeIt.annotatedTree : instanceTree) + '\n' + annotatedTree).replace("\n", '\n' + NodeBase.TREE_LEVEL_INDENT); } } instance = instanceTreeIt.instance; } else { instance = entityIterable; sourceTree = queryTree; } this.entityType = entityType; optimizedTree = null; } public TreeKeepingEntityIterable(@Nullable final Iterable<Entity> entityIterable, @NotNull String entityType, @NotNull final NodeBase queryTree, @NotNull QueryEngine queryEngine) { this(entityIterable, entityType, queryTree, null, null, queryEngine); } @SuppressWarnings("UnusedDeclaration") @Override public Iterable<Entity> instantiate() { optimize(); Iterable<Entity> result; if (instance == null) { result = instantiateForWholeHierarchy(); } else if (optimizedTree instanceof GetAll) { result = instance; } else { // clone TreeKeepingEntityIterable tkei = new TreeKeepingEntityIterable(null, entityType, optimizedTree.getClone(), queryEngine); tkei.optimizedTree = optimizedTree; result = queryEngine.toEntityIterable(queryEngine.intersect(instance, tkei)); } if (sorts != null) { result = sorts.apply(entityType, result, queryEngine); } if (result == null) { result = instantiateForWholeHierarchy(); } if (isExplainOn) { Iterable<Entity> explained = result; while (explained instanceof SortEngine.InMemorySortIterable) { explained = ((SortEngine.InMemorySortIterable) explained).source; } if (explained instanceof EntityIterable) { EntityIterable entityIterable = ((EntityIterable) explained).getSource(); if (entityIterable instanceof EntityIterableBase && entityIterable != EntityIterableBase.EMPTY) { EntityIterableBase entityIterableBase = ((EntityIterableBase) entityIterable); final PersistentEntityStoreImpl store = queryEngine.getPersistentStore(); Explainer explainer = store.getExplainer(); if (!Explainer.isExplainForcedForThread()) { explainer.start(origin); } entityIterableBase.setOrigin(origin); explainer.explain(origin, Explainer.INITIAL_TREE, annotatedTree); explainer.explain(origin, Explainer.OPTIMIZED_TREE, optimizedTree); if (!Explainer.isExplainForcedForThread()) { for (Entity entity : result) { explainer.explain(origin, Explainer.ITERABLE_ADVANCES); } explainer.log(origin); } } } } return result; } private Iterable<Entity> instantiateForWholeHierarchy() { return instantiateForWholeHierarchy(entityType, optimizedTree); } private Iterable<Entity> instantiateForWholeHierarchy(final String entityType, final NodeBase ast) { final ModelMetaData mmd = queryEngine.getModelMetaData(); final EntityMetaData emd = mmd == null ? null : mmd.getEntityMetaData(entityType); Iterable<Entity> result = ast.instantiate(entityType, queryEngine, mmd); if (!(emd == null || ast.polymorphic())) { for (String subType : emd.getSubTypes()) { if (unionSubtypesResults) { // union returns sorted by id results provided its operands are sorted by id result = queryEngine.unionAdjusted(result, instantiateForWholeHierarchy(subType, ast)); } else { result = queryEngine.concatAdjusted(result, instantiateForWholeHierarchy(subType, ast)); } } } return queryEngine.adjustEntityIterable(result); } public void optimize() { if (optimizedTree == null) { OptimizedTreesCache.OptimizedTreeAndSorts optimized = OptimizedTreesCache.get() .findOptimized(sourceTree); if (optimized != null) { optimizedTree = optimized.getOptimizedTree(); sorts = optimized.getSorts(); } else { final long start = System.currentTimeMillis(); sorts = new Sorts(); final Root root = new Root(sourceTree.getClone()); for (OptimizationPlan rules : OptimizationPlan.PLANS) { root.optimize(sorts, rules); } root.cleanSorts(sorts); optimizedTree = root.getChild(); if (sorts.canBeCached()) { OptimizedTreesCache.get().cacheOptimized(sourceTree, optimizedTree, sorts); } final long delta = System.currentTimeMillis() - start; if (delta > 1) { if (log.isDebugEnabled()) { log.debug("Optimize tree in [" + delta + " ms]"); } log.trace("---------------------------------------------------"); log.trace("Source tree: "); log.trace(sourceTree); log.trace("---------------------------------------------------"); } log.trace("Optimized tree: "); log.trace(optimizedTree); log.trace("---------------------------------------------------"); } } } public Iterable<Entity> getInstance() { return instance; } @Override public String getEntityType() { return entityType; } public NodeBase getTree() { return sourceTree; } public NodeBase getOptimizedTree() { return optimizedTree; } public Sorts getSorts() { return sorts; } }