All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jetbrains.exodus.query.NodeBase Maven / Gradle / Ivy

There is a newer version: 2.0.1
Show newest version
/**
 * Copyright 2010 - 2017 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.query.metadata.ModelMetaData;
import jetbrains.exodus.util.StringInterner;

import java.util.*;

@SuppressWarnings("HardcodedLineSeparator")
public abstract class NodeBase {
    protected static final List NO_CHILDREN = Collections.emptyList();
    static final String TREE_LEVEL_INDENT = "  ";

    private NodeBase parent;

    protected NodeBase() {
    }

    public NodeBase getParent() {
        return parent;
    }

    public void setParent(NodeBase parent) {
        if (this.parent != null) {
            this.parent = null;
        }
        this.parent = parent;
    }

    public NodeBase replaceChild(NodeBase child, NodeBase newChild) {
        throw new RuntimeException(getClass() + ": can't replace child.");
    }

    public abstract Iterable instantiate(String entityType, QueryEngine queryEngine, ModelMetaData metaData);

    public abstract NodeBase getClone();

    public Collection getChildren() {
        return NO_CHILDREN;
    }

    public void optimize(Sorts sorts, OptimizationPlan rules) {
        boolean applied = true;
        while (applied) {
            applied = false;
            for (final NodeBase child : getChildren()) {
                if (!rules.applyOnEnter) {
                    child.optimize(sorts, rules);
                }
                for (OptimizationRule rule : rules.rules) {
                    if (child.replaceIfMatches(rule)) {
                        applied = true;
                        break;
                    }
                }
                if (applied) {
                    break;
                }
                if (rules.applyOnEnter) {
                    child.optimize(sorts, rules);
                }
            }
        }
    }

    public void cleanSorts(Sorts sorts) {
        for (NodeBase child : getChildren()) {
            child.cleanSorts(sorts);
        }
    }

    @SuppressWarnings({"EqualsWhichDoesntCheckParameterClass"})
    @Override
    public boolean equals(Object obj) {
        NodeBase node = (NodeBase) obj;
        Iterator iterator = node.getChildren().iterator();
        for (NodeBase child1 : getChildren()) {
            NodeBase child2 = iterator.next();
            if (!(child1.equals(child2))) {
                return false;
            }
        }
        return true;
    }

    public void checkWildcard(Object obj) {
        if (obj instanceof Wildcard || obj instanceof ConversionWildcard) {
            throw new RuntimeException("Can't compare wildcard with " + obj.getClass() + '.');
        }
    }

    boolean replaceIfMatches(OptimizationRule rule) {
        NodeBase.MatchContext ctx = new NodeBase.MatchContext();
        if (!(match(rule.getSource(), ctx))) {
            return false;
        }
        parent.replaceChild(this, substituteMatches(rule.getDest(), ctx));
        return true;
    }

    protected boolean match(NodeBase node, NodeBase.MatchContext ctx) {
        if (node instanceof Wildcard) {
            final NodeBase nodeInst = ctx.getNode((Wildcard) node);
            if (nodeInst == null) {
                ctx.putNode((Wildcard) node, this);
                return true;
            }
            return equals(nodeInst);
        }
        if (node instanceof ConversionWildcard) {
            ConversionWildcard customWildcard = (ConversionWildcard) node;
            if (!(getClass().equals(customWildcard.getClazz()))) {
                return false;
            }
            if (!customWildcard.isOk(this)) {
                return false;
            }
            NodeBase leafInst = ctx.getLeave((ConversionWildcard) node);
            if (leafInst == null) {
                ctx.putLeave(customWildcard, this);
                return true;
            }
            return equals(leafInst);
        }
        return getClass().equals(node.getClass()) && matchChildren(node, ctx);
    }

    protected boolean polymorphic() {
        return false;
    }

    protected boolean matchChildren(NodeBase node, NodeBase.MatchContext ctx) {
        return true;
    }

    public String toString() {
        return toString("");
    }

    boolean toString(StringBuilder result, NodeBase subtree, String presentation) {
        return toString(result, "", subtree, presentation);
    }

    protected String toString(String prefix) {
        final StringBuilder result = new StringBuilder(prefix).append(getClass().getSimpleName());
        for (NodeBase child : getChildren()) {
            result.append('\n').append(child.toString(TREE_LEVEL_INDENT + prefix));
        }
        return result.toString();
    }

    private boolean toString(StringBuilder result, String prefix, NodeBase subtree, String presentation) {
        if (equals(subtree)) {
            result.append((prefix + presentation).replace("\n", '\n' + prefix));
            return true;
        }
        result.append(prefix);
        result.append(getClass().getSimpleName());
        boolean used = false;
        for (NodeBase child : getChildren()) {
            result.append('\n');
            StringBuilder childResult = new StringBuilder();
            boolean presentationUsed = !used && child.toString(childResult, TREE_LEVEL_INDENT + prefix, subtree, presentation);
            if (presentationUsed) {
                subtree = null;
                result.append(childResult);
            } else {
                result.append(child.toString(TREE_LEVEL_INDENT + prefix));
            }
            used |= presentationUsed;
        }
        return used;
    }

    String getHandle() {
        return StringInterner.intern(getHandle(new StringBuilder(32)), 100);
    }

    public StringBuilder getHandle(StringBuilder sb) {
        return sb.append(getSimpleName());
    }

    public abstract String getSimpleName();

    public int size() {
        int r = 1;
        for (NodeBase child : getChildren()) {
            r += child.size();
        }
        return r;
    }

    public Iterable getDescendants() {
        return new Iterable() {
            @Override
            public Iterator iterator() {
                final List stack = new LinkedList<>();
                stack.add(NodeBase.this); // push root
                return new Iterator() {
                    @Override
                    public boolean hasNext() {
                        return !stack.isEmpty();
                    }

                    @Override
                    public NodeBase next() {
                        if (stack.isEmpty()) {
                            throw new NoSuchElementException();
                        } else {
                            final NodeBase result = stack.remove(0); // pop
                            stack.addAll(0, result.getChildren()); // push all children (first will be on top)
                            return result;
                        }
                    }

                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }

    public static NodeBase getUnderRoot(NodeBase root) {
        while (root instanceof Root) {
            root = ((UnaryNode) root).getChild();
        }
        return root;
    }

    private static NodeBase substituteMatches(NodeBase pattern, NodeBase.MatchContext ctx) {
        Root root = new Root(pattern.getClone());
        for (NodeBase node : root.getDescendants()) {
            if (node instanceof Wildcard) {
                node.parent.replaceChild(node, ctx.getNode((Wildcard) node));
            } else if (node instanceof ConversionWildcard) {
                ConversionWildcard customWildcard = (ConversionWildcard) node;
                node.parent.replaceChild(node, customWildcard.convert(ctx.getLeave((ConversionWildcard) node)));
            }
        }
        return root.getChild();
    }

    static class MatchContext {
        private Map nodes;
        private Map leaves;

        private MatchContext() {
        }

        private NodeBase getNode(Wildcard wildcard) {
            return nodes == null ?
                    null :
                    nodes.get(wildcard);
        }

        private void putNode(Wildcard wildcard, NodeBase node) {
            if (nodes == null) {
                nodes = new HashMap<>();
            }
            nodes.put(wildcard, node);
        }

        private NodeBase getLeave(ConversionWildcard wildcard) {
            return leaves == null ?
                    null :
                    leaves.get(wildcard);
        }

        private void putLeave(ConversionWildcard wildcard, NodeBase node) {
            if (leaves == null) {
                leaves = new HashMap<>();
            }
            leaves.put(wildcard, node);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy