alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Commons Digester example source code file (Main.java)

This example Commons Digester source code file (Main.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Commons Digester tags/keywords

bookfactory, bookfactory, catalog, catalog, digester, digester, main, string, string

The Commons Digester Main.java source code

/*
 * 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 org.apache.commons.digester.Digester;
import org.apache.commons.digester.AbstractObjectCreationFactory;

/**
 * A simple program to demonstrate some of the functionality of the
 * Commons Digester module.
 * <p>
 * This code will parse the provided "example.xml" file to build a tree
 * of java objects, then cause those objects to print out their values
 * to demonstrate that the input file has been processed correctly. The
 * input file represents a catalog of items in a library.
 * <p>
 * As with all code, there are many ways of achieving the same goal;
 * the solution here is only one possible implementation.
* <p> 
 * Very verbose comments are included here, as this class is intended
 * as a tutorial; if you look closely at method "addRules", you will
 * see that the amount of code required to use the Digester is actually
 * quite low.
 * <p>
 * Usage: java Main example.xml
 */
public class Main {
    
    /**
     * Main method : entry point for running this example program.
     * <p>
     * Usage: java CatalogDigester example.xml
     */
    public static void main(String[] args) {
        if (args.length != 1) {
            usage();
            System.exit(-1);
        }
        
        String filename = args[0];
        
        // Create a Digester instance
        Digester d = new Digester();
        
        // Add rules to the digester that will be triggered while
        // parsing occurs.
        addRules(d);
        
        // Process the input file.
        try {
            java.io.Reader reader = getInputData(filename);
            d.parse(reader);
        }
        catch(java.io.IOException ioe) {
            System.out.println("Error reading input file:" + ioe.getMessage());
            System.exit(-1);
        }
        catch(org.xml.sax.SAXException se) {
            System.out.println("Error parsing input file:" + se.getMessage());
            System.exit(-1);
        }

        // Get the first object created by the digester's rules
        // (the "root" object). Note that this is exactly the same object
        // returned by the Digester.parse method; either approach works.
        Catalog catalog = (Catalog) d.getRoot();
        
        // Print out all the contents of the catalog, as loaded from
        // the input file.
        catalog.print();
    }
    
    private static void addRules(Digester d) {

        //--------------------------------------------------

        // when we encounter the root "catalog" tag, create an
        // instance of the Catalog class. 
        //
        // Note that this approach is different from the approach taken in 
        // the AddressBook example, where an initial "root" object was 
        // explicitly created and pushed onto the digester stack before 
        // parsing started instead
        //
        // Either approach is fine.
        
        d.addObjectCreate("catalog", Catalog.class);
        
        //--------------------------------------------------

        // when we encounter a book tag, we want to create a Book
        // instance. However the Book class doesn't have a default
        // constructor (one with no arguments), so we can't use
        // the ObjectCreateRule. Instead, we use the FactoryCreateRule.
        
        BookFactory factory = new BookFactory();
        d.addFactoryCreate("catalog/book", factory);
        
        // and add the book to the parent catalog object (which is
        // the next-to-top object on the digester object stack).
        d.addSetNext("catalog/book", "addItem");
        
        // we want each subtag of book to map the text contents of
        // the tag into a bean property with the same name as the tag.
        // eg <title>foo --> setTitle("foo")
        d.addSetNestedProperties("catalog/book");
        
        
        //-----------------------------------------------
        
        // We are using the "AudioVisual" class to represent both
        // dvds and videos, so when the "dvd" tag is encountered,
        // create an AudioVisual object.
        
        d.addObjectCreate("catalog/dvd", AudioVisual.class);
        
        // add this dvd to the parent catalog object
        
        d.addSetNext("catalog/dvd", "addItem");
        
        // We want to map every xml attribute onto a corresponding
        // property-setter method on the Dvd class instance. However
        // this doesn't work with the xml attribute "year-made", because
        // of the internal hyphen. We could use explicit CallMethodRule
        // rules instead, or use a version of the SetPropertiesRule that
        // allows us to override any troublesome mappings...
        //
        // If there was more than one troublesome mapping, we could
        // use the method variant that takes arrays of xml-attribute-names
        // and bean-property-names to override multiple mappings.
        //
        // For any attributes not explicitly mapped here, the default
        // processing is applied, so xml attribute "category" --> setCategory.
        
        d.addSetProperties("catalog/dvd", "year-made", "yearMade");
        
        // We also need to tell this AudioVisual object that it is actually
        // a dvd; we can use the ObjectParamRule to pass a string to any
        // method. This usage is a little artificial - normally in this
        // situation there would be separate Dvd and Video classes.
        // Note also that equivalent behaviour could be implemented by
        // using factory objects to create & initialise the AudioVisual
        // objects with their type rather than using ObjectCreateRule.
        
        d.addCallMethod("catalog/dvd", "setType", 1);
        d.addObjectParam("catalog/dvd", 0, "dvd"); // pass literal "dvd" string
        
        // Each tag of form "<attr id="foo" value="bar"/> needs to map
        // to a call to setFoo("bar").
        //
        // This is an alternative to the syntax used for books above (see
        // method addSetNestedProperties), where the name of the subtag 
        // indicated which property to set. Using this syntax in the xml has 
        // advantages and disadvantages both for the user and the application 
        // developer. It is commonly used with the FactoryCreateRule variant 
        // which allows the target class to be created to be specified in an 
        // xml attribute; this feature of FactoryCreateRule is not demonstrated
        // in this example, but see the Apache Tomcat configuration files for 
        // an example of this usage.
        //
        // Note that despite the name similarity, there is no link
        // between SetPropertyRule and SetPropertiesRule.
        
        d.addSetProperty("catalog/dvd/attr", "id", "value");
        
        //-----------------------------------------------
        
        // and here we repeat the dvd rules, but for the video tag.
        d.addObjectCreate("catalog/video", AudioVisual.class);
        d.addSetNext("catalog/video", "addItem");
        d.addSetProperties("catalog/video", "year-made", "yearMade");
        d.addCallMethod("catalog/video", "setType", 1);
        d.addObjectParam("catalog/video", 0, "video");
        d.addSetProperty("catalog/video/attr", "id", "value");
    }

    /*
     * Reads the specified file into memory, and returns a StringReader
     * object which reads from that in-memory buffer.
     * <p>
     * This method exists just to demonstrate that the input to the
     * digester doesn't need to be from a file; for example, xml could
     * be read from a database or generated dynamically; any old buffer
     * in memory can be processed by the digester.
     * <p>
     * Clearly, if the data is always coming from a file, then calling
     * the Digester.parse method that takes a File object would be
     * more sensible (see AddressBook example).
     */
    private static java.io.Reader getInputData(String filename) 
    throws java.io.IOException {
        java.io.File srcfile = new java.io.File(filename);
        
        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(1000);
        byte[] buf = new byte[100];
        java.io.FileInputStream fis = new java.io.FileInputStream(srcfile);
        for(;;) {
            int nread = fis.read(buf);
            if (nread == -1) {
                break;
            }
            baos.write(buf, 0, nread);
        }
        fis.close();
        
        return new java.io.StringReader( baos.toString() );
        
    }
    
    private static void usage() {
        System.out.println("Usage: java Main example.xml");
    }
}

Other Commons Digester examples (source code examples)

Here is a short list of links related to this Commons Digester Main.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.