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

jlibs.nblr.rules.Path Maven / Gradle / Ivy

/**
 * Copyright 2015 Santhosh Kumar Tekuri
 *
 * The JLibs authors license this file to you 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 jlibs.nblr.rules;

import jlibs.nblr.matchers.Matcher;

import java.util.*;

/**
 * @author Santhosh Kumar T
 */
public class Path extends ArrayList{
    public Path(Deque stack){
        super(stack);
        Collections.reverse(this);
    }

    public Edge matcherEdge(){
        for(int i=size()-1; i>=0; i--){
            Object obj = get(i);
            if(obj instanceof Edge){
                Edge edge = (Edge)obj;
                if(edge.matcher!=null)
                    return edge;
            }
        }
        return null;
    }

    @SuppressWarnings({"SimplifiableConditionalExpression"})
    public boolean fallback(){
        for(Object obj: this){
            if(obj instanceof Edge){
                Edge edge = (Edge)obj;
                if((edge.matcher!=null || edge.ruleTarget!=null) && edge.fallback)
                    return true;
            }
        }
        return false;
    }

    public Matcher matcher(){
        Edge matcherEdge = matcherEdge();
        return matcherEdge!=null ? matcherEdge.matcher : null;
    }

    public boolean clashesWith(Path that){
        if(this.depth!=that.depth)
            throw new IllegalArgumentException("depths are not same: "+this.depth+"!="+that.depth);
        if(depth>1){
            if(!this.parent.clashesWith(that.parent))
                return false;
        }
        return this.matcher().clashesWith(that.matcher());
    }

    public Paths children;
    public Path parent;
    public int depth;
    public int branch;

    public boolean hasLoop(){
        Node lastNode = (Node)get(size()-1);
        if(subList(0, size()-1).contains(lastNode))
            return true;
        Path path = parent;
        while(path!=null){
            if(path.contains(lastNode))
                return true;
            path = path.parent;
        }
        return false;
    }

    public Path[] route(){
        List route = new LinkedList();
        Path p = this;
        while(p!=null){
            route.add(0, p);
            p = p.parent;
        }
        return route.toArray(new Path[route.size()]);
    }

    public Node nodeAfterPop(){
        int rulesPopped = 0;
        Node target = null;

        Path p = this;
        while(p!=null && target==null){
            boolean wasNode = false;
            for(int i=p.size()-1; i>=0; i--){
                Object obj = p.get(i);
                if(obj instanceof Node){
                    if(wasNode)
                        rulesPopped++;
                    wasNode = true;
                }else if(obj instanceof Edge){
                    wasNode = false;
                    Edge edge = (Edge)obj;
                    if(edge.ruleTarget!=null){
                        if(rulesPopped==0){
                            target = edge.target;
                            break;
                        }else
                            rulesPopped--;
                    }
                }
            }
            p = p.parent;
        }

        return target;
    }

    public Node destination(){
        return (Node)get(size()-1);
    }

    public void travelWithoutMatching(){
        while(true){
            Node node = destination();
            if(node.junction() || (node.name!=null && !node.name.equals(Node.DYNAMIC_STRING_MATCH)))
                return;
            switch(node.outgoing.size()){
                case 1:
                    Edge edge = node.outgoing.get(0);
                    if(edge.matcher==null && edge.ruleTarget==null){
                        add(edge);
                        add(edge.target);
                        break;
                    }else if(edge.ruleTarget!=null){ // travel only one rule target
                        add(edge);
                        add(edge.ruleTarget.node());
                        return;
                    }else
                        return;
                default:
                    return;
            }
        }
    }

    @Override
    public String toString(){
        String str;
        Matcher matcher = matcher();
        if(matcher==null)
            str = "";
        else if(matcher.name!=null)
            str = '<'+matcher.name+'>';
        else
            str = matcher.toString();
        return parent!=null ? parent+str : str;
    }
}