View Javadoc

1   package org.apache.commons.digester3.examples.api.catalog;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   * 
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */ 
19  
20  import org.apache.commons.digester3.Digester;
21  
22  /**
23   * A simple program to demonstrate some of the functionality of the
24   * Commons Digester module.
25   * <p>
26   * This code will parse the provided "example.xml" file to build a tree
27   * of java objects, then cause those objects to print out their values
28   * to demonstrate that the input file has been processed correctly. The
29   * input file represents a catalog of items in a library.
30   * <p>
31   * As with all code, there are many ways of achieving the same goal;
32   * the solution here is only one possible implementation.
33  * <p> 
34   * Very verbose comments are included here, as this class is intended
35   * as a tutorial; if you look closely at method "addRules", you will
36   * see that the amount of code required to use the Digester is actually
37   * quite low.
38   * <p>
39   * Usage: java Main example.xml
40   */
41  public class Main
42  {
43  
44      /**
45       * Main method : entry point for running this example program.
46       * <p>
47       * Usage: java CatalogDigester example.xml
48       */
49      public static void main( String[] args )
50      {
51          if ( args.length != 1 )
52          {
53              usage();
54              System.exit( -1 );
55          }
56  
57          String filename = args[0];
58  
59          // Create a Digester instance
60          Digester d = new Digester();
61  
62          // Add rules to the digester that will be triggered while
63          // parsing occurs.
64          addRules( d );
65  
66          // Process the input file.
67          try
68          {
69              java.io.Reader reader = getInputData( filename );
70              d.parse( reader );
71          }
72          catch ( java.io.IOException ioe )
73          {
74              System.out.println( "Error reading input file:" + ioe.getMessage() );
75              System.exit( -1 );
76          }
77          catch ( org.xml.sax.SAXException se )
78          {
79              System.out.println( "Error parsing input file:" + se.getMessage() );
80              System.exit( -1 );
81          }
82  
83          // Get the first object created by the digester's rules
84          // (the "root" object). Note that this is exactly the same object
85          // returned by the Digester.parse method; either approach works.
86          Catalog catalog = (Catalog) d.getRoot();
87  
88          // Print out all the contents of the catalog, as loaded from
89          // the input file.
90          catalog.print();
91      }
92  
93      private static void addRules( Digester d )
94      {
95  
96          // --------------------------------------------------
97  
98          // when we encounter the root "catalog" tag, create an
99          // instance of the Catalog class.
100         //
101         // Note that this approach is different from the approach taken in
102         // the AddressBook example, where an initial "root" object was
103         // explicitly created and pushed onto the digester stack before
104         // parsing started instead
105         //
106         // Either approach is fine.
107 
108         d.addObjectCreate( "catalog", Catalog.class );
109 
110         // --------------------------------------------------
111 
112         // when we encounter a book tag, we want to create a Book
113         // instance. However the Book class doesn't have a default
114         // constructor (one with no arguments), so we can't use
115         // the ObjectCreateRule. Instead, we use the FactoryCreateRule.
116 
117         BookFactory factory = new BookFactory();
118         d.addFactoryCreate( "catalog/book", factory );
119 
120         // and add the book to the parent catalog object (which is
121         // the next-to-top object on the digester object stack).
122         d.addSetNext( "catalog/book", "addItem" );
123 
124         // we want each subtag of book to map the text contents of
125         // the tag into a bean property with the same name as the tag.
126         // eg <title>foo</title> --> setTitle("foo")
127         d.addSetNestedProperties( "catalog/book" );
128 
129         // -----------------------------------------------
130 
131         // We are using the "AudioVisual" class to represent both
132         // dvds and videos, so when the "dvd" tag is encountered,
133         // create an AudioVisual object.
134 
135         d.addObjectCreate( "catalog/dvd", AudioVisual.class );
136 
137         // add this dvd to the parent catalog object
138 
139         d.addSetNext( "catalog/dvd", "addItem" );
140 
141         // We want to map every xml attribute onto a corresponding
142         // property-setter method on the Dvd class instance. However
143         // this doesn't work with the xml attribute "year-made", because
144         // of the internal hyphen. We could use explicit CallMethodRule
145         // rules instead, or use a version of the SetPropertiesRule that
146         // allows us to override any troublesome mappings...
147         //
148         // If there was more than one troublesome mapping, we could
149         // use the method variant that takes arrays of xml-attribute-names
150         // and bean-property-names to override multiple mappings.
151         //
152         // For any attributes not explicitly mapped here, the default
153         // processing is applied, so xml attribute "category" --> setCategory.
154 
155         d.addSetProperties( "catalog/dvd", "year-made", "yearMade" );
156 
157         // We also need to tell this AudioVisual object that it is actually
158         // a dvd; we can use the ObjectParamRule to pass a string to any
159         // method. This usage is a little artificial - normally in this
160         // situation there would be separate Dvd and Video classes.
161         // Note also that equivalent behaviour could be implemented by
162         // using factory objects to create & initialise the AudioVisual
163         // objects with their type rather than using ObjectCreateRule.
164 
165         d.addCallMethod( "catalog/dvd", "setType", 1 );
166         d.addObjectParam( "catalog/dvd", 0, "dvd" ); // pass literal "dvd" string
167 
168         // Each tag of form "<attr id="foo" value="bar"/> needs to map
169         // to a call to setFoo("bar").
170         //
171         // This is an alternative to the syntax used for books above (see
172         // method addSetNestedProperties), where the name of the subtag
173         // indicated which property to set. Using this syntax in the xml has
174         // advantages and disadvantages both for the user and the application
175         // developer. It is commonly used with the FactoryCreateRule variant
176         // which allows the target class to be created to be specified in an
177         // xml attribute; this feature of FactoryCreateRule is not demonstrated
178         // in this example, but see the Apache Tomcat configuration files for
179         // an example of this usage.
180         //
181         // Note that despite the name similarity, there is no link
182         // between SetPropertyRule and SetPropertiesRule.
183 
184         d.addSetProperty( "catalog/dvd/attr", "id", "value" );
185 
186         // -----------------------------------------------
187 
188         // and here we repeat the dvd rules, but for the video tag.
189         d.addObjectCreate( "catalog/video", AudioVisual.class );
190         d.addSetNext( "catalog/video", "addItem" );
191         d.addSetProperties( "catalog/video", "year-made", "yearMade" );
192         d.addCallMethod( "catalog/video", "setType", 1 );
193         d.addObjectParam( "catalog/video", 0, "video" );
194         d.addSetProperty( "catalog/video/attr", "id", "value" );
195     }
196 
197     /*
198      * Reads the specified file into memory, and returns a StringReader object which reads from that in-memory buffer.
199      * <p> This method exists just to demonstrate that the input to the digester doesn't need to be from a file; for
200      * example, xml could be read from a database or generated dynamically; any old buffer in memory can be processed by
201      * the digester. <p> Clearly, if the data is always coming from a file, then calling the Digester.parse method that
202      * takes a File object would be more sensible (see AddressBook example).
203      */
204     private static java.io.Reader getInputData( String filename )
205         throws java.io.IOException
206     {
207         java.io.File srcfile = new java.io.File( filename );
208 
209         java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream( 1000 );
210         byte[] buf = new byte[100];
211         java.io.FileInputStream fis = new java.io.FileInputStream( srcfile );
212         for ( ;; )
213         {
214             int nread = fis.read( buf );
215             if ( nread == -1 )
216             {
217                 break;
218             }
219             baos.write( buf, 0, nread );
220         }
221         fis.close();
222 
223         return new java.io.StringReader( baos.toString() );
224 
225     }
226 
227     private static void usage()
228     {
229         System.out.println( "Usage: java Main example.xml" );
230     }
231 
232 }