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

ai.vespa.schemals.tree.Node Maven / Gradle / Ivy

There is a newer version: 8.441.21
Show newest version
package ai.vespa.schemals.tree;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;

import org.eclipse.lsp4j.Range;

import ai.vespa.schemals.index.Symbol;
import ai.vespa.schemals.index.Symbol.SymbolType;

public abstract class Node implements Iterable {

    public enum LanguageType {
        SCHEMA,
        INDEXING,
        RANK_EXPRESSION,
        YQLPlus,
        GROUPING,
        CUSTOM
    }

    // This array has to be in order, without overlapping elements
    private List children = new ArrayList<>();
    private Node parent;

    protected LanguageType language; // Language specifies which parser the node comes from
    protected Range range;
    protected boolean isDirty;
    protected Symbol symbol;

    protected Node(LanguageType language, Range range, boolean isDirty) {
        this.language = language;
        this.range = range;
        this.isDirty = isDirty;
    }

    public LanguageType getLanguageType() { return language; }

    public Range getRange() {
        return range;
    }

    public boolean getIsDirty() {
        return isDirty;
    }

    public int size() {
        return children.size();
    }

    public Node get(int i) {
        return children.get(i);
    }

    public void setParent(Node parent) {
        this.parent = parent;
    }

    public void addChild(Node child) {
        child.setParent(this);
        this.children.add(child);
    }

    public void addChildren(List children) {
        for (Node child : children) {
            addChild(child);
        }
    }

    public void clearChildren() {
        for (Node child : children) {
            child.setParent(null);
        }

        children.clear();
    }

    public Node removeChild(int i) {
        Node child = children.remove(i);
        child.setParent(null);

        return child;
    }

    public Node getParent(int levels) {
        if (levels == 0) {
            return this;
        }

        if (parent == null) {
            return null;
        }

        return parent.getParent(levels - 1);
    }

    public Node getParent() {
        return getParent(1);
    }

    public void insertChildAfter(int index, Node child) {
        this.children.add(index+1, child);
        child.setParent(this);
    }

    /**
     * Returns the previous SchemaNode of the sibilings of the node.
     * If there is no previous node, it moves upwards to the parent and tries to get the previous node.
     *
     * @return the previous SchemaNode, or null if there is no previous node
     */
    public Node getPrevious() {
        if (parent == null)return null;

        int parentIndex = parent.indexOf(this);

        if (parentIndex == -1)return null; // invalid setup

        if (parentIndex == 0)return parent;
        return parent.get(parentIndex - 1);
    }

    /**
     * Returns the next SchemaNode of the sibilings of the node.
     * If there is no next node, it returns the next node of the parent node.
     *
     * @return the next SchemaNode or null if there is no next node
     */
    public Node getNext() {
        if (parent == null) return null;

        int parentIndex = parent.indexOf(this);

        if (parentIndex == -1) return null;
        
        if (parentIndex == parent.size() - 1) return parent.getNext();

        return parent.get(parentIndex + 1);
    }

    /**
     * Returns the sibling node at the specified relative index.
     * A sibling node is a node that shares the same parent node.
     *
     * @param relativeIndex the relative index of the sibling node
     * @return the sibling node at the specified relative index, or null if the sibling node does not exist
     */
    public Node getSibling(int relativeIndex) {
        if (parent == null)return null;

        int parentIndex = parent.indexOf(this);

        if (parentIndex == -1) return null; // invalid setup

        int siblingIndex = parentIndex + relativeIndex;
        if (siblingIndex < 0 || siblingIndex >= parent.size()) return null;
        
        return parent.get(siblingIndex);
    }

    /**
     * Returns the previous sibling of this schema node.
     * A sibling node is a node that shares the same parent node. 
     *
     * @return the previous sibling of this schema node, or null if there is no previous sibling
     */
    public Node getPreviousSibling() {
        return getSibling(-1);
    }

    /**
     * Returns the next sibling of this schema node.
     * A sibling node is a node that shares the same parent node.
     *
     * @return the next sibling of this schema node, or null if there is no previous sibling
     */
    public Node getNextSibling() {
        return getSibling(1);
    }

    public int indexOf(Node child) {
        return this.children.indexOf(child);
    }

    public boolean isLeaf() {
        return children.size() == 0;
    }

    public boolean hasSymbol() {
        return this.symbol != null;
    }

    public Symbol getSymbol() {
        if (!hasSymbol()) throw new IllegalArgumentException("get Symbol called on node without a symbol!");
        return this.symbol;
    }

    public void removeSymbol() {
        this.symbol = null;
    }

    public Symbol setSymbol(SymbolType type, String fileURI, Symbol scope, String shortIdentifier) {
        if (this.hasSymbol()) {
            throw new IllegalArgumentException("Cannot set symbol for node: " + this.toString() + ". Already has symbol.");
        }
        this.symbol = new Symbol(this, type, fileURI, scope, shortIdentifier);

        return symbol;
    }

    public Symbol setSymbol(SymbolType type, String fileURI) {
        if (this.hasSymbol()) {
            throw new IllegalArgumentException("Cannot set symbol for node: " + this.toString() + ". Already has symbol.");
        }
        this.symbol = new Symbol(this, type, fileURI);

        return symbol;
    }

    public Symbol setSymbol(SymbolType type, String fileURI, Symbol scope) {
        if (this.hasSymbol()) {
            throw new IllegalArgumentException("Cannot set symbol for node: " + this.toString() + ". Already has symbol.");
        }
        this.symbol = new Symbol(this, type, fileURI, scope);

        return symbol;
    }

    public Symbol setSymbol(SymbolType type, String fileURI, Optional scope) {
        if (scope.isPresent()) {
            setSymbol(type, fileURI, scope.get());
        } else {
            setSymbol(type, fileURI);
        }

        return symbol;
    }

    public abstract int getBeginOffset();

    public abstract String getText();
    public abstract Class getASTClass();

    public boolean isASTInstance(Class cls) {
        return getASTClass() == cls;
    }

    public boolean isSchemaNode() { return false; }
    public SchemaNode getSchemaNode() {
        throw new UnsupportedOperationException("Cannot get a SchemaNode from a non SchemaNode.");
    }

    public boolean isYQLNode() { return false; }
    public YQLNode getYQLNode() {
        throw new UnsupportedOperationException("Cannot get a YQLNode from a non YQLNode.");
    }

    @Override
	public Iterator iterator() {
        return new Iterator() {
            int currentIndex = 0;

			@Override
			public boolean hasNext() {
                return currentIndex < children.size();
			}

			@Override
			public Node next() {
                return children.get(currentIndex++);
			}
        };
	}
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy