blob: 7b4d8080d947a0d8d1b6354c65a52ce54a59bb95 [file] [log] [blame]
package org.apache.commons.digester3.xmlrules;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses 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.
*/
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;
import org.apache.commons.digester3.Rule;
import org.apache.commons.digester3.binder.RulesBinder;
import org.apache.commons.digester3.binder.RulesModule;
import org.xml.sax.Attributes;
/**
* A rule for including one rules XML file within another. Included files behave as if they are 'macro-expanded' within
* the includer. This means that the values of the pattern stack are prefixed to every pattern in the included rules.
* <p>
* This rule will detect 'circular' includes, which would result in infinite recursion. It throws a
* CircularIncludeException when a cycle is detected, which will terminate the parse.
*/
final class IncludeRule
extends Rule
{
private static final String CLASSPATH_URL_PREFIX = "classpath:";
private final WithMemoryRulesBinder memoryRulesBinder;
private final RulesBinder targetRulesBinder;
public IncludeRule( final WithMemoryRulesBinder memoryRulesBinder, final RulesBinder targetRulesBinder )
{
this.memoryRulesBinder = memoryRulesBinder;
this.targetRulesBinder = targetRulesBinder;
}
/**
* {@inheritDoc}
*/
@Override
public void begin( final String namespace, final String name, final Attributes attributes )
throws Exception
{
// The path attribute gives the URI to another digester rules xml file
final String fileName = attributes.getValue( "url" );
if ( fileName != null && !fileName.isEmpty() )
{
final URL xmlRulesResource;
if ( fileName.startsWith( CLASSPATH_URL_PREFIX ) )
{
String path = fileName.substring( CLASSPATH_URL_PREFIX.length() );
if ( '/' == path.charAt( 0 ) )
{
path = path.substring( 1 );
}
xmlRulesResource = this.targetRulesBinder.getContextClassLoader().getResource( path );
if ( xmlRulesResource == null )
{
targetRulesBinder.addError( "Resource '%s' not found, please make sure it is in the classpath",
path );
return;
}
}
else
{
try
{
xmlRulesResource = new URL( fileName );
}
catch ( final MalformedURLException e )
{
targetRulesBinder.addError( "An error occurred while inculing file from '%s': %s", fileName,
e.getMessage() );
return;
}
}
final Set<String> includedFiles = memoryRulesBinder.getIncludedFiles();
final String xmlRulesResourceString = xmlRulesResource.toString();
if ( includedFiles.add( xmlRulesResourceString ) )
{
try
{
install( new FromXmlRulesModule()
{
@Override
protected void loadRules()
{
loadXMLRules( xmlRulesResource );
}
} );
}
finally
{
includedFiles.remove( xmlRulesResourceString );
}
}
else
{
targetRulesBinder.addError( "Circular file inclusion detected for XML rules: %s", xmlRulesResource );
}
}
// The class attribute gives the name of a class that implements
// the DigesterRulesSource interface
final String className = attributes.getValue( "class" );
if ( className != null && !className.isEmpty() )
{
try
{
final Class<?> cls = Class.forName( className );
if ( !RulesModule.class.isAssignableFrom( cls ) )
{
targetRulesBinder.addError( "Class '%s' if not a '%s' implementation", className,
RulesModule.class.getName() );
return;
}
final RulesModule rulesSource = (RulesModule) cls.newInstance();
install( rulesSource );
}
catch ( final Exception e )
{
targetRulesBinder.addError( "Impossible to include programmatic rules from class '%s': %s", className,
e.getMessage() );
}
}
}
private void install( final RulesModule rulesModule )
{
// that's an hack, shall not be taken in consideration!!! :)
rulesModule.configure( new PrefixedRulesBinder( targetRulesBinder,
memoryRulesBinder.getPatternStack().toString() ) );
}
}