/**
* 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.core.lang.ImpossibleException;
import java.util.*;
/**
* @author Santhosh Kumar T
*/
public class Routes{
public final Rule rule;
public final Node fromNode;
public final Paths paths;
public final int maxLookAhead;
public final List<Path> determinateRoutes;
public final Path indeterminateRoute;
public final Path routeStartingWithEOF;
@SuppressWarnings({"unchecked"})
public Routes(Rule rule, Node fromNode){
this(rule, fromNode, false);
}
@SuppressWarnings({"unchecked"})
public Routes(Rule rule, Node fromNode, boolean digIntoRule){
this.rule = rule;
this.fromNode = fromNode;
this.paths = Paths.travel(fromNode, digIntoRule);
List<Path> routes = paths.leafs();
// split paths into branches and find maxLookAhead required
int maxLookAhead = 0;
List<Path> branches[] = new List[paths.size()];
for(Path route: routes){
int branch = route.branch;
if(branches[branch]==null)
branches[branch] = new ArrayList<Path>();
branches[branch].add(route);
maxLookAhead = Math.max(maxLookAhead, route.depth);
}
this.maxLookAhead = maxLookAhead;
// find branch with multiple paths
int branchWithMultiplePaths = -1;
for(int branch=0; branch<branches.length; branch++){
if(branches[branch].size()>1){
if(branchWithMultiplePaths==-1)
branchWithMultiplePaths = branch;
else{
if(branches[branch].size()>branches[branchWithMultiplePaths].size())
branchWithMultiplePaths = branch;
}
}
}
if(branchWithMultiplePaths==-1)
indeterminateRoute = null;
else
indeterminateRoute = branches[branchWithMultiplePaths].get(0);
determinateRoutes = new ArrayList<Path>();
for(int branch=0; branch<branches.length; branch++){
if(branch!=branchWithMultiplePaths)
determinateRoutes.addAll(branches[branch]);
}
Collections.sort(determinateRoutes, new Comparator<Path>(){
@Override
public int compare(Path route1, Path route2){
int diff = route1.depth - route2.depth;
if(diff==0){
if(route1.fallback())
return +1;
else if(route2.fallback())
return -1;
else
return 0;
}else
return diff;
}
});
Path routeStartingWithEOF = null;
for(Path route: determinateRoutes){
if(route.parent==null && route.matcher()==null){
if(routeStartingWithEOF!=null)
throw new ImpossibleException("found more that one route starting with <EOF>");
routeStartingWithEOF = route;
}
}
if(routeStartingWithEOF!=null)
determinateRoutes.remove(routeStartingWithEOF);
this.routeStartingWithEOF = routeStartingWithEOF;
}
public Integer[] lookAheads(){
TreeSet<Integer> set = new TreeSet<Integer>();
for(Path route: determinateRoutes){
set.add(route.depth);
}
return set.toArray(new Integer[set.size()]);
}
public List<Path> determinateRoutes(int lookAhead){
List<Path> routes = new ArrayList<Path>();
for(Path route: determinateRoutes){
if(route.depth==lookAhead)
routes.add(route);
}
return routes;
}
public String toString(){
StringBuilder buff = new StringBuilder();
for(Path route: determinateRoutes)
add(buff, route);
if(indeterminateRoute !=null)
add(buff, indeterminateRoute.route()[0]);
if(routeStartingWithEOF!=null)
add(buff, routeStartingWithEOF);
return buff.toString();
}
private void add(StringBuilder buff, Path route){
if(buff.length()>0)
buff.append(" OR ");
buff.append(route);
}
public boolean isEOF(){
if(maxLookAhead>1)
return false;
if(determinateRoutes.size()>0)
return false;
if(indeterminateRoute!=null)
return false;
for(Object obj: routeStartingWithEOF){
if(obj instanceof Node){
Node node = (Node)obj;
if(node.action!=null)
return false;
}else if(obj instanceof Edge){
Edge edge = (Edge)obj;
if(edge.ruleTarget!=null)
return false;
else if(edge.matcher!=null)
return false;
}
}
return true;
}
}