View Javadoc

1   package org.apache.commons.digester3.xmlrules;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.net.MalformedURLException;
23  import java.net.URL;
24  import java.util.Set;
25  
26  import org.apache.commons.digester3.Rule;
27  import org.apache.commons.digester3.binder.RulesBinder;
28  import org.apache.commons.digester3.binder.RulesModule;
29  import org.xml.sax.Attributes;
30  
31  /**
32   * A rule for including one rules XML file within another. Included files behave as if they are 'macro-expanded' within
33   * the includer. This means that the values of the pattern stack are prefixed to every pattern in the included rules.
34   * <p>
35   * This rule will detect 'circular' includes, which would result in infinite recursion. It throws a
36   * CircularIncludeException when a cycle is detected, which will terminate the parse.
37   */
38  final class IncludeRule
39      extends Rule
40  {
41  
42      private static final String CLASSPATH_URL_PREFIX = "classpath:";
43  
44      private final WithMemoryRulesBinder memoryRulesBinder;
45  
46      private final RulesBinder targetRulesBinder;
47  
48      public IncludeRule( final WithMemoryRulesBinder memoryRulesBinder, final RulesBinder targetRulesBinder )
49      {
50          this.memoryRulesBinder = memoryRulesBinder;
51          this.targetRulesBinder = targetRulesBinder;
52      }
53  
54      /**
55       * {@inheritDoc}
56       */
57      @Override
58      public void begin( String namespace, String name, Attributes attributes )
59          throws Exception
60      {
61          // The path attribute gives the URI to another digester rules xml file
62          String fileName = attributes.getValue( "url" );
63          if ( fileName != null && fileName.length() > 0 )
64          {
65              final URL xmlRulesResource;
66  
67              if ( fileName.startsWith( CLASSPATH_URL_PREFIX ) )
68              {
69                  String path = fileName.substring( CLASSPATH_URL_PREFIX.length() );
70                  if ( '/' == path.charAt( 0 ) )
71                  {
72                      path = path.substring( 1 );
73                  }
74                  xmlRulesResource = this.targetRulesBinder.getContextClassLoader().getResource( path );
75                  if ( xmlRulesResource == null )
76                  {
77                      targetRulesBinder.addError( "Resource '%s' not found, please make sure it is in the classpath",
78                                                  path );
79                      return;
80                  }
81              }
82              else
83              {
84                  try
85                  {
86                      xmlRulesResource = new URL( fileName );
87                  }
88                  catch ( MalformedURLException e )
89                  {
90                      targetRulesBinder.addError( "An error occurred while inculing file from '%s': %s", fileName,
91                                                  e.getMessage() );
92                      return;
93                  }
94              }
95  
96              Set<String> includedFiles = memoryRulesBinder.getIncludedFiles();
97              String xmlRulesResourceString = xmlRulesResource.toString();
98              if ( includedFiles.add( xmlRulesResourceString ) )
99              {
100                 try
101                 {
102                     install( new FromXmlRulesModule()
103                     {
104 
105                         @Override
106                         protected void loadRules()
107                         {
108                             loadXMLRules( xmlRulesResource );
109                         }
110 
111                     } );
112                 }
113                 finally
114                 {
115                     includedFiles.remove( xmlRulesResourceString );
116                 }
117             }
118             else
119             {
120                 targetRulesBinder.addError( "Circular file inclusion detected for XML rules: %s", xmlRulesResource );
121             }
122         }
123 
124         // The class attribute gives the name of a class that implements
125         // the DigesterRulesSource interface
126         String className = attributes.getValue( "class" );
127         if ( className != null && className.length() > 0 )
128         {
129             try
130             {
131                 Class<?> cls = Class.forName( className );
132                 if ( !RulesModule.class.isAssignableFrom( cls ) )
133                 {
134                     targetRulesBinder.addError( "Class '%s' if not a '%s' implementation", className,
135                                                 RulesModule.class.getName() );
136                     return;
137                 }
138 
139                 RulesModule rulesSource = (RulesModule) cls.newInstance();
140 
141                 install( rulesSource );
142             }
143             catch ( Exception e )
144             {
145                 targetRulesBinder.addError( "Impossible to include programmatic rules from class '%s': %s", className,
146                                             e.getMessage() );
147             }
148         }
149     }
150 
151     private void install( RulesModule rulesModule )
152     {
153         // that's an hack, shall not be taken in consideration!!! :)
154         rulesModule.configure( new PrefixedRulesBinder( targetRulesBinder,
155                                                         memoryRulesBinder.getPatternStack().toString() ) );
156     }
157 
158 }