/*
* Copyright (C) 2011 Laurent Caillette
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.parser.xpath;
import java.util.regex.Pattern;
import com.google.common.collect.ImmutableList;
import org.jaxen.BaseXPath;
import org.jaxen.JaxenException;
import org.jaxen.XPath;
import org.novelang.common.SyntacticTree;
import org.novelang.common.tree.Treepath;
/**
* Type-safe XPath implementation.
* There is some cheating with namespaces: because it will always be "n:" its made
* optional and pre-processed by a regex for removal before passing it to the
* {@link SyntacticTreeNavigator} which doesn't support it.
*
* @author Laurent Caillette
*/
public class SyntacticTreeXpath {
private final XPath xpath ;
private static final class UntypedXpath extends BaseXPath {
public UntypedXpath( final String xpathExpression ) throws JaxenException {
super( xpathExpression, SyntacticTreeNavigator.getInstance() ) ;
}
}
/*package*/ static XPath createUntypedXPath( final String xpathExpression )
throws JaxenException
{
return new UntypedXpath( xpathExpression ) ;
}
public SyntacticTreeXpath( final String xpathExpression ) throws JaxenException {
xpath = createUntypedXPath( removeNamespaces( xpathExpression ) ) ;
}
@SuppressWarnings( { "unchecked" } )
public ImmutableList< Treepath< SyntacticTree > > selectNodes( final SyntacticTree tree )
throws JaxenException
{
return ImmutableList.< Treepath< SyntacticTree > >copyOf(
xpath.selectNodes( Treepath.create( tree ) ) ) ;
}
/**
* We're lucky: axis selector like {@code child} don't end by a "n" so we shouldn't get
* confused by them.
* The regex below selects "n:" prefix if there is no preceding letter and if there
* is a letter right after it.
*/
private static final Pattern NAMESPACE_REMOVER =
Pattern.compile( "((?<![0-9a-zA-Z_\\-])n:(?=\\w))" ) ;
private static String removeNamespaces( final String xpathExpression ) {
return NAMESPACE_REMOVER.matcher( xpathExpression ).replaceAll( "" );
}
}