/*
* 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.rendering.xslt.validate;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import org.novelang.common.Location;
import org.novelang.logger.Logger;
import org.novelang.logger.LoggerFactory;
/**
* Given an XPath expression, this class checks that all expanded names with a given prefix
* have known element names.
* <p>
* In XPath specification, an
* <a href="http://www.w3.org/TR/xpath#dt-expanded-name">expanded name</a>
* is a pair "consisting of a local part" (XML node name) "and a possibly null namespace URI".
* <p>
* This is useful to ensure an XSLT keeps synchronized with the tokens of a Novelang grammar.
*
* @author Laurent Caillette
*/
public class ExpandedNameVerifier {
private static final Logger LOGGER = LoggerFactory.getLogger( ExpandedNameVerifier.class ) ;
private final Set< String > nodeNames ;
public ExpandedNameVerifier( final Set< String > nodeNames ) {
this.nodeNames = ImmutableSet.copyOf( nodeNames ) ;
}
private static final String XPATH_PATTERN_FRAGMENT = ":([0-9a-zA-Z\\-_]+)" ;
private String xmlPrefix = null ;
private Pattern xpathPattern = null ;
public void setXmlPrefix( final String prefix ) {
Preconditions.checkNotNull( prefix ) ;
Preconditions.checkState( xpathPattern == null ) ;
xmlPrefix = prefix ;
xpathPattern = Pattern.compile( prefix + XPATH_PATTERN_FRAGMENT ) ;
LOGGER.debug( "Crafted regex: ", xpathPattern.toString() ) ;
}
public void unsetXmlPrefix() {
Preconditions.checkNotNull( xpathPattern ) ;
xpathPattern = null ;
xmlPrefix = null ;
}
public String getXmlPrefix() {
return xmlPrefix ;
}
private final List< BadExpandedName > badExpandedNames = Lists.newLinkedList() ;
public void verify( final Location location, final String xpath ) {
if ( null != xpathPattern ) {
final Matcher matcher = xpathPattern.matcher( xpath ) ;
while( matcher.find() && matcher.groupCount() == 1 ) {
final String elementName = matcher.group( 1 ) ;
if( ! nodeNames.contains( elementName ) ) {
badExpandedNames.add( new BadExpandedName( location, xpath, xmlPrefix, elementName ) ) ;
}
}
}
}
public Iterable< BadExpandedName > getBadExpandedNames() {
return ImmutableList.copyOf( badExpandedNames ) ;
}
public void checkNoBadExpandedNames() throws BadExpandedNamesException {
if( ! badExpandedNames.isEmpty() ) {
throw new BadExpandedNamesException( getBadExpandedNames() ) ;
}
}
}