View Javadoc
1   /*
2    * $Id$
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  package org.apache.struts.tiles.xmlDefinition;
23  
24  import java.io.BufferedInputStream;
25  import java.io.FileInputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.net.URL;
29  
30  import org.apache.commons.digester.Digester;
31  import org.xml.sax.SAXException;
32  
33  /**
34   * Parse an XML definitions file.
35   */
36  public class XmlParser
37  {
38  
39      /** Associated digester. */
40    protected Digester digester;
41      /**
42       * Should we use a validating XML parser to read the configuration file.
43       * Default is <code>false</code>.
44       */
45      protected boolean validating = false;
46      /**
47       * The set of public identifiers, and corresponding resource names for
48       * the versions of the configuration file DTDs we know about.  There
49       * <strong>MUST</strong> be an even number of Strings in this list!
50       */
51      protected String registrations[] = {
52          "-//Apache Software Foundation//DTD Tiles Configuration 1.1//EN",
53          "/org/apache/struts/resources/tiles-config_1_1.dtd",
54          "-//Apache Software Foundation//DTD Tiles Configuration 1.3//EN",
55          "/org/apache/struts/resources/tiles-config_1_3.dtd",
56          "-//Apache Software Foundation//DTD Tiles Configuration 1.4//EN",
57          "/org/apache/struts/resources/tiles-config_1_4.dtd"
58      };
59  
60       /**
61        * Constructor.
62        * Creates a digester parser and initializes syntax rules.
63        */
64    public XmlParser()
65    {
66      digester = new Digester();
67      digester.setValidating(validating);
68      digester.setNamespaceAware(true);
69      digester.setUseContextClassLoader(true);
70      // Register our local copy of the DTDs that we can find
71    for (int i = 0; i < registrations.length; i += 2) {
72        URL url = this.getClass().getResource(registrations[i+1]);
73        if (url != null)
74            {
75            digester.register(registrations[i], url.toString());
76            }
77    }
78      // Init syntax rules
79    initDigester( digester );
80    }
81  
82      /**
83       * Set digester validating flag.
84       */
85    public void setValidating( boolean validating )
86      {
87      digester.setValidating( validating);
88      }
89  
90  
91     /**
92      * Init digester for components syntax.
93      * This is an old set of rules, left for backward compatibility.
94      * @param digester Digester instance to use.
95      */
96    private void initDigesterForComponentsDefinitionsSyntax( Digester digester )
97    {
98       // Common constants
99    String PACKAGE_NAME = "org.apache.struts.tiles.xmlDefinition";
100   String DEFINITION_TAG = "component-definitions/definition";
101   String definitionHandlerClass = PACKAGE_NAME + ".XmlDefinition";
102 
103   String PUT_TAG  = DEFINITION_TAG + "/put";
104   String putAttributeHandlerClass = PACKAGE_NAME + ".XmlAttribute";
105 
106   String LIST_TAG = DEFINITION_TAG + "/putList";
107   String listHandlerClass     = PACKAGE_NAME + ".XmlListAttribute";
108 
109   String ADD_LIST_ELE_TAG = LIST_TAG + "/add";
110 
111     // syntax rules
112     digester.addObjectCreate(  DEFINITION_TAG, definitionHandlerClass );
113     digester.addSetProperties( DEFINITION_TAG);
114     digester.addSetNext(       DEFINITION_TAG, "putDefinition", definitionHandlerClass);
115     // put / putAttribute rules
116     digester.addObjectCreate(  PUT_TAG, putAttributeHandlerClass);
117     digester.addSetNext(       PUT_TAG, "addAttribute", putAttributeHandlerClass);
118     digester.addSetProperties( PUT_TAG);
119     digester.addCallMethod(    PUT_TAG, "setBody", 0);
120     // list rules
121     digester.addObjectCreate(  LIST_TAG, listHandlerClass);
122     digester.addSetProperties( LIST_TAG);
123     digester.addSetNext(       LIST_TAG, "addAttribute", putAttributeHandlerClass);
124     // list elements rules
125     // We use Attribute class to avoid rewriting a new class.
126     // Name part can't be used in listElement attribute.
127     digester.addObjectCreate(  ADD_LIST_ELE_TAG, putAttributeHandlerClass);
128     digester.addSetNext(       ADD_LIST_ELE_TAG, "add", putAttributeHandlerClass);
129     digester.addSetProperties( ADD_LIST_ELE_TAG);
130     digester.addCallMethod(    ADD_LIST_ELE_TAG, "setBody", 0);
131   }
132 
133    /**
134     * Init digester for Tiles syntax.
135     * Same as components, but with first element = tiles-definitions
136     * @param digester Digester instance to use.
137     */
138   private void initDigesterForTilesDefinitionsSyntax( Digester digester )
139   {
140      // Common constants
141   String PACKAGE_NAME = "org.apache.struts.tiles.xmlDefinition";
142   String DEFINITION_TAG = "tiles-definitions/definition";
143   String definitionHandlerClass = PACKAGE_NAME + ".XmlDefinition";
144 
145   String PUT_TAG  = DEFINITION_TAG + "/put";
146   String putAttributeHandlerClass = PACKAGE_NAME + ".XmlAttribute";
147 
148   //String LIST_TAG = DEFINITION_TAG + "/putList";
149     // List tag value
150   String LIST_TAG = "putList";
151   String DEF_LIST_TAG = DEFINITION_TAG + "/" + LIST_TAG;
152   String listHandlerClass     = PACKAGE_NAME + ".XmlListAttribute";
153     // Tag value for adding an element in a list
154   String ADD_LIST_ELE_TAG = "*/" + LIST_TAG + "/add";
155 
156     // syntax rules
157     digester.addObjectCreate(  DEFINITION_TAG, definitionHandlerClass );
158     digester.addSetProperties( DEFINITION_TAG);
159     digester.addSetNext(       DEFINITION_TAG, "putDefinition", definitionHandlerClass);
160     // put / putAttribute rules
161     // Rules for a same pattern are called in order, but rule.end() are called
162     // in reverse order.
163     // SetNext and CallMethod use rule.end() method. So, placing SetNext in
164     // first position ensure it will be called last (sic).
165     digester.addObjectCreate(  PUT_TAG, putAttributeHandlerClass);
166     digester.addSetNext(       PUT_TAG, "addAttribute", putAttributeHandlerClass);
167     digester.addSetProperties( PUT_TAG);
168     digester.addCallMethod(    PUT_TAG, "setBody", 0);
169     // Definition level list rules
170     // This is rules for lists nested in a definition
171     digester.addObjectCreate(  DEF_LIST_TAG, listHandlerClass);
172     digester.addSetProperties( DEF_LIST_TAG);
173     digester.addSetNext(       DEF_LIST_TAG, "addAttribute", putAttributeHandlerClass);
174     // list elements rules
175     // We use Attribute class to avoid rewriting a new class.
176     // Name part can't be used in listElement attribute.
177     digester.addObjectCreate(  ADD_LIST_ELE_TAG, putAttributeHandlerClass);
178     digester.addSetNext(       ADD_LIST_ELE_TAG, "add", putAttributeHandlerClass);
179     digester.addSetProperties( ADD_LIST_ELE_TAG);
180     digester.addCallMethod(    ADD_LIST_ELE_TAG, "setBody", 0);
181 
182     // nested list elements rules
183     // Create a list handler, and add it to parent list
184   String NESTED_LIST = "*/" + LIST_TAG + "/" + LIST_TAG;
185     digester.addObjectCreate(  NESTED_LIST, listHandlerClass);
186     digester.addSetProperties( NESTED_LIST);
187     digester.addSetNext(       NESTED_LIST, "add", putAttributeHandlerClass);
188 
189     // item elements rules
190     // We use Attribute class to avoid rewriting a new class.
191     // Name part can't be used in listElement attribute.
192   //String ADD_WILDCARD = LIST_TAG + "/addItem";
193   // non String ADD_WILDCARD = LIST_TAG + "/addx*";
194   String ADD_WILDCARD = "*/item";
195   String menuItemDefaultClass = "org.apache.struts.tiles.beans.SimpleMenuItem";
196     digester.addObjectCreate(  ADD_WILDCARD, menuItemDefaultClass, "classtype");
197     digester.addSetNext(       ADD_WILDCARD, "add", "java.lang.Object");
198     digester.addSetProperties( ADD_WILDCARD);
199 
200     // bean elements rules
201   String BEAN_TAG = "*/bean";
202   String beanDefaultClass = "org.apache.struts.tiles.beans.SimpleMenuItem";
203     digester.addObjectCreate(  BEAN_TAG, beanDefaultClass, "classtype");
204     digester.addSetNext(       BEAN_TAG, "add", "java.lang.Object");
205     digester.addSetProperties( BEAN_TAG);
206 
207     // Set properties to surrounding element
208   digester.addSetProperty(BEAN_TAG+ "/set-property", "property", "value");
209   }
210 
211    /**
212     * Init digester in order to parse instances definition file syntax.
213     * Instances is an old name for "definition". This method is left for
214     * backwards compatibility.
215     * @param digester Digester instance to use.
216     */
217   private void initDigesterForInstancesSyntax( Digester digester )
218   {
219         // Build a digester to process our configuration resource
220   String PACKAGE_NAME = "org.apache.struts.tiles.xmlDefinition";
221   String INSTANCE_TAG = "component-instances/instance";
222   String instanceHandlerClass = PACKAGE_NAME + ".XmlDefinition";
223 
224   String PUT_TAG = INSTANCE_TAG + "/put";
225   String PUTATTRIBUTE_TAG = INSTANCE_TAG + "/putAttribute";
226   String putAttributeHandlerClass = PACKAGE_NAME + ".XmlAttribute";
227 
228   String LIST_TAG     = INSTANCE_TAG + "/putList";
229   String listHandlerClass     = PACKAGE_NAME + ".XmlListAttribute";
230 
231   String ADD_LIST_ELE_TAG = LIST_TAG + "/add";
232 
233     // component instance rules
234     digester.addObjectCreate(  INSTANCE_TAG, instanceHandlerClass );
235     digester.addSetProperties( INSTANCE_TAG);
236     digester.addSetNext(       INSTANCE_TAG, "putDefinition", instanceHandlerClass);
237     // put / putAttribute rules
238     digester.addObjectCreate(  PUTATTRIBUTE_TAG, putAttributeHandlerClass);
239     digester.addSetProperties( PUTATTRIBUTE_TAG);
240     digester.addSetNext(       PUTATTRIBUTE_TAG, "addAttribute", putAttributeHandlerClass);
241     // put / putAttribute rules
242     digester.addObjectCreate(  PUT_TAG, putAttributeHandlerClass);
243     digester.addSetProperties( PUT_TAG);
244     digester.addSetNext(       PUT_TAG, "addAttribute", putAttributeHandlerClass);
245     // list rules
246     digester.addObjectCreate(  LIST_TAG, listHandlerClass);
247     digester.addSetProperties( LIST_TAG);
248     digester.addSetNext(       LIST_TAG, "addAttribute", putAttributeHandlerClass);
249     // list elements rules
250     // We use Attribute class to avoid rewriting a new class.
251     // Name part can't be used in listElement attribute.
252     digester.addObjectCreate(  ADD_LIST_ELE_TAG, putAttributeHandlerClass);
253     digester.addSetProperties( ADD_LIST_ELE_TAG);
254     digester.addSetNext(       ADD_LIST_ELE_TAG, "add", putAttributeHandlerClass);
255   }
256 
257    /**
258     * Init digester.
259     * @param digester Digester instance to use.
260     */
261   protected void initDigester( Digester digester )
262   {
263   initDigesterForTilesDefinitionsSyntax( digester );
264   initDigesterForComponentsDefinitionsSyntax( digester );
265   initDigesterForInstancesSyntax( digester );
266   }
267 
268   /**
269    * Parse input reader and add encountered definitions to definitions set.
270    * @param in Input stream.
271    * @param definitions Xml Definitions set to which encountered definition are added.
272    * @throws IOException On errors during file parsing.
273    * @throws SAXException On errors parsing XML.
274    */
275   public void parse( InputStream in, XmlDefinitionsSet definitions ) throws IOException, SAXException
276   {
277     try
278     {
279       // set first object in stack
280     //digester.clear();
281     digester.push(definitions);
282       // parse
283       digester.parse(in);
284       in.close();
285       }
286   catch (SAXException e)
287     {
288       //throw new ServletException( "Error while parsing " + mappingConfig, e);
289     throw e;
290       }
291 
292   }
293 
294     /**
295      * Main method to check file syntax.
296      */
297   public static void main(String[] args)
298   {
299   //String filename = "E:/programs/jakarta-tomcat/webapps/wtiles-struts/WEB-INF/tiles-examples-defs.xml";
300   String filename = "E:/programs/jakarta-tomcat-4.0.3/webapps/wtiles-struts/WEB-INF/tiles-examples-defs.xml";
301   //String filename = "E:/programs/jakarta-tomcat/webapps/wtiles-struts/WEB-INF/tilesDefinitions.xml";
302   //String filename = "E:/programs/jakarta-tomcat/webapps/wtiles-channel/WEB-INF/componentDefinitions.xml";
303   //String filename2 = "E:/programs/jakarta-tomcat/webapps/wtiles-tutorial/WEB-INF/componentDefinitions.xml";
304 
305 
306     if( args.length > 1 )
307       {
308       filename = args[1];
309       } // end if
310 
311   System.out.println( "Read file '" + filename  +"'" );
312 
313   InputStream input = null;
314   // InputStream input2 = null;
315     // Open file
316     try
317       {
318         input = new BufferedInputStream(
319                              new FileInputStream( filename) );
320     //    input2 = new BufferedInputStream(
321           //                   new FileInputStream( filename2) );
322       }
323      catch( IOException ex )
324       {
325       System.out.println( "can't open file '" + filename + "' : " + ex.getMessage() );
326       }
327     // Check file syntax
328     try
329       {
330         XmlParser parser = new XmlParser();
331       parser.setValidating(true);
332       XmlDefinitionsSet definitions = new XmlDefinitionsSet();
333         System.out.println( "  Parse file" );
334       parser.parse( input, definitions);
335       //  System.out.println( "  Check file 2" );
336       //parser.parse( input2, definitions);
337         System.out.println( "  done." );
338       System.out.println( "  Result : " + definitions.toString() );
339       }
340      catch( Exception ex )
341       {
342       System.out.println( "Error during parsing '" + filename + "' : " + ex.getMessage() );
343       ex.printStackTrace();
344       }
345   }
346 
347 }