/**
* 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.xml.sax.dog;
import jlibs.core.lang.ImpossibleException;
import jlibs.xml.sax.SAXProperties;
import jlibs.xml.sax.SAXUtil;
import jlibs.xml.sax.dog.expr.Expression;
import jlibs.xml.sax.dog.expr.Literal;
import jlibs.xml.sax.dog.expr.func.FunctionCall;
import jlibs.xml.sax.dog.expr.nodset.LocationExpression;
import jlibs.xml.sax.dog.expr.nodset.PathExpression;
import jlibs.xml.sax.dog.path.Constraint;
import jlibs.xml.sax.dog.path.LocationPath;
import jlibs.xml.sax.dog.path.PositionalPredicate;
import jlibs.xml.sax.dog.path.Step;
import jlibs.xml.sax.dog.sniff.Event;
import jlibs.xml.sax.dog.sniff.SAXHandler;
import jlibs.xml.sax.dog.sniff.XPathParser;
import jlibs.xml.stream.STAXXMLReader;
import org.jaxen.saxpath.SAXPathException;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.XPathException;
import javax.xml.xpath.XPathFunctionResolver;
import javax.xml.xpath.XPathVariableResolver;
import java.util.ArrayList;
import java.util.List;
/**
* @author Santhosh Kumar T
*/
public final class XMLDog{
public final NamespaceContext nsContext;
public final XPathVariableResolver variableResolver;
public final XPathFunctionResolver functionResolver;
private final XPathParser parser;
public XMLDog(NamespaceContext nsContext){
this(nsContext, null, null);
}
public XMLDog(NamespaceContext nsContext, XPathVariableResolver variableResolver, XPathFunctionResolver functionResolver){
this.nsContext = nsContext;
this.variableResolver = variableResolver;
this.functionResolver = functionResolver;
parser = new XPathParser(nsContext, variableResolver, functionResolver);
}
public boolean isAllowDefaultPrefixMapping(){
return parser.isAllowDefaultPrefixMapping();
}
public void setAllowDefaultPrefixMapping(boolean allow){
parser.setAllowDefaultPrefixMapping(allow);
}
private final List<Expression> expressions = new ArrayList<Expression>();
private final List<Expression> docExpressions = new ArrayList<Expression>();
private final List<Expression> globalExpressions = new ArrayList<Expression>();
public Expression addXPath(String xpath) throws SAXPathException{
Expression compiledExpr = parser.parse(xpath, true);
compiledExpr.setXPath(xpath);
addXPath(compiledExpr);
return compiledExpr;
}
public Expression addForEach(String forEach, String xpath) throws SAXPathException{
Expression forEachExpr = parser.parse(forEach, true);
LocationPath union = new LocationPath(Scope.LOCAL, 0);
if(forEachExpr instanceof LocationExpression)
union.addToContext(((LocationExpression)forEachExpr).locationPath);
else
union.addToContext(((PathExpression)forEachExpr).union);
Expression relativeExpr = parser.parse(xpath, false);
PathExpression compiledExpr = new PathExpression(union, relativeExpr, true);
compiledExpr.setXPath("#for-each "+forEach+" #eval "+xpath);
addXPath(compiledExpr);
return compiledExpr;
}
@SuppressWarnings({"unchecked"})
private void addXPath(Expression compiledExpr) throws SAXPathException{
expressions.add(compiledExpr);
switch(compiledExpr.scope()){
case Scope.DOCUMENT:
searchDocExpressions(compiledExpr, compiledExpr);
break;
case Scope.GLOBAL:
assert compiledExpr instanceof Literal;
globalExpressions.add(compiledExpr);
break;
default:
throw new ImpossibleException("scope of "+compiledExpr.getXPath()+" can't be"+compiledExpr.scope());
}
}
private void searchDocExpressions(Expression userExpr, Expression expr){
if(expr.scope()==Scope.DOCUMENT){
expr.id = docExpressions.size();
docExpressions.add(expr);
if(expr!=userExpr)
expr.storeResult = true;
}
if(expr instanceof LocationExpression){
for(Step step: ((LocationExpression)expr).locationPath.steps){
Expression predicate = step.predicateSet.getPredicate();
if(predicate!=null){
if(predicate.scope()!=Scope.GLOBAL)
searchDocExpressions(userExpr, predicate);
for(PositionalPredicate positionPredicate=step.predicateSet.headPositionalPredicate; positionPredicate!=null; positionPredicate=positionPredicate.next){
if(positionPredicate.predicate.scope()!=Scope.GLOBAL)
searchDocExpressions(userExpr, positionPredicate.predicate);
}
}
}
}else if(expr instanceof FunctionCall){
FunctionCall functionCall = (FunctionCall)expr;
for(Expression member: functionCall.members){
if(member.scope()!=Scope.GLOBAL)
searchDocExpressions(userExpr, member);
}
}else if(expr instanceof PathExpression){
PathExpression pathExpr = (PathExpression)expr;
if(pathExpr.union.predicateSet.getPredicate()!=null)
searchDocExpressions(userExpr, pathExpr.union.predicateSet.getPredicate());
for(Expression context: pathExpr.contexts)
searchDocExpressions(userExpr, context);
searchDocExpressions(userExpr, pathExpr.relativeExpression);
}
}
public Iterable<Expression> getXPaths(){
return expressions;
}
public int getDocumentXPathsCount(){
return docExpressions.size();
}
public Event createEvent(){
return new Event(nsContext, globalExpressions, docExpressions, Constraint.ID_START+parser.constraints.size(), parser.langInterested);
}
/*-------------------------------------------------[ Sniff ]---------------------------------------------------*/
public void sniff(Event event, InputSource source, boolean useSTAX) throws XPathException{
XMLReader reader;
try{
if(useSTAX)
reader = new STAXXMLReader();
else
reader = SAXUtil.newSAXFactory(true, false, false).newSAXParser().getXMLReader();
sniff(event, source, reader);
}catch(Exception ex){
throw new XPathException(ex);
}
}
public void sniff(Event event, InputSource source, XMLReader reader) throws XPathException{
try{
SAXHandler handler = event.getSAXHandler();
reader.setContentHandler(handler);
reader.setProperty(SAXProperties.LEXICAL_HANDLER, handler);
}catch(Exception ex){
throw new XPathException(ex);
}
try{
reader.parse(source);
}catch(Exception ex){
if(ex!=Event.STOP_PARSING)
throw new XPathException(ex);
}
}
public XPathResults sniff(InputSource source, boolean useSTAX) throws XPathException{
Event event = createEvent();
XPathResults results = new XPathResults(event);
event.setListener(results);
sniff(event, source, useSTAX);
return results;
}
public void sniff(Event event, InputSource source) throws XPathException{
sniff(event, source, false);
}
public XPathResults sniff(InputSource source) throws XPathException{
Event event = createEvent();
XPathResults results = new XPathResults(event);
event.setListener(results);
sniff(event, source);
return results;
}
}