/**
* 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 java.util.*;
/**
* @author Santhosh Kumar T
*/
public class Paths extends ArrayList<Path>{
public final Path owner;
public final int depth;
public Paths(Path owner){
this.owner = owner;
if(owner!=null){
owner.children = this;
depth = owner.depth+1;
}else
depth = 1;
}
public boolean add(Path path){
if(owner==null)
path.branch = size();
else
path.branch = owner.branch;
path.parent = owner;
path.depth = depth;
return super.add(path);
}
public List<Path> leafs(){
List<Path> list = new ArrayList<Path>();
leafs(list);
return list;
}
private void leafs(List<Path> list){
for(Path path: this){
if(path.children==null)
list.add(path);
else
path.children.leafs(list);
}
}
@SuppressWarnings({"SimplifiableIfStatement"})
private static boolean clashes(Path p1, Path p2){
if(p1.matcher()==null && p2.matcher()==null)
throw new IllegalStateException("Ambiguous Routes: "+p1+" AND "+p2);
if(p1.matcher()!=null && p2.matcher()!=null){
if(p1.fallback() || p2.fallback())
return false;
else
return p1.clashesWith(p2);
}
return false;
}
public static Paths travel(Node fromNode, boolean digIntoRule){
Paths rootPaths = new Paths(null);
List<Path> list = new ArrayList<Path>();
while(true){
if(list.size()==0){
rootPaths.populate(fromNode, digIntoRule);
list.addAll(rootPaths);
}else{
List<Path> newList = new ArrayList<Path>();
for(Path path: list){
if(path.matcher()!=null){
Paths paths = new Paths(path);
paths.populate((Node)path.get(path.size()-1), true);
newList.addAll(paths);
}
}
list = newList;
}
TreeSet<Integer> clashingIndexes = new TreeSet<Integer>();
for(int ibranch=0; ibranch<rootPaths.size()-1; ibranch++){
for(int jbranch=ibranch+1; jbranch<rootPaths.size(); jbranch++){
int i = 0;
for(Path ipath: list){
if(ipath.branch==ibranch){
int j = 0;
for(Path jpath: list){
if(jpath.branch==jbranch){
if(clashes(ipath, jpath)){
if(ipath.hasLoop() && jpath.hasLoop())
throw new IllegalStateException("Infinite lookAhead needed: "+ipath+" and "+jpath);
clashingIndexes.add(i);
clashingIndexes.add(j);
}
}
j++;
}
}
i++;
}
}
}
if(clashingIndexes.size()==0)
return rootPaths;
List<Path> clashingPaths = new ArrayList<Path>(clashingIndexes.size());
for(int id: clashingIndexes)
clashingPaths.add(list.get(id));
list = clashingPaths;
}
}
private void populate(Node fromNode, boolean digIntoRule){
populate(fromNode, new ArrayDeque<Object>(), digIntoRule);
}
private void populate(Node fromNode, Deque<Object> stack, boolean digIntoRule){
if(stack.contains(fromNode))
throw new IllegalStateException("infinite loop detected");
stack.push(fromNode);
if(fromNode.outgoing.size()>0){
if(fromNode.outgoing.size()>1)
digIntoRule = true;
for(Edge edge: fromNode.outgoing){
stack.push(edge);
if(edge.matcher!=null){
stack.push(edge.target);
add(new Path(stack));
stack.pop();
}else if(edge.ruleTarget!=null){
if(!digIntoRule){
if(new Routes(edge.ruleTarget.rule, edge.ruleTarget.node(), true).routeStartingWithEOF!=null) // the rule can match nothing
digIntoRule = true;
}
if(!digIntoRule){
stack.push(edge.ruleTarget.node());
add(new Path(stack));
stack.pop();
} else{
populate(edge.ruleTarget.node(), stack, digIntoRule);
}
}else
populate(edge.target, stack, digIntoRule);
stack.pop();
}
}else{
Path temp = new Path(stack);
temp.parent = this.owner;
Node target = temp.nodeAfterPop();
if(target==null)
add(new Path(stack));
else
populate(target, stack, digIntoRule);
}
stack.pop();
}
}