FeaturesPluginsDocs & SupportCommunityPartners

NetBeans: schema2beans library - User Documentation


Version: 1.3.2
Last Updated: 2004/2/18

Author: Cliff Draper, Sun Microsystems/Forte Tools

Abstract:

The schema2beans library (schema2beans.jar file) allows you to generate a set of java bean classes from a DTD or XML Schema file. This set of beans can be used to represent an XML file, as a graph of java beans. You can add, change and remove elements of the graph, merge and compare graphs and also get events on any change that happens in the graph. Afterwards, you can write back the graph as an XML document.

The package of this library is org.netbeans.modules.schema2beans and is composed of a schema2beans runtime (schema2beans.jar) and schema2beans generator (schema2beansdev.jar).

Contents:

The schema2beans library allows you to:

Generation

To generate a set of beans (which uses the schema2beans runtime) from a DTD or XML Schema file, use the GenBeans class:
java org.netbeans.modules.schema2beansdev.GenBeans -f myDtdFile.dtd

If you run this from the command line, I highly recommend creating an alias similar to the following csh alias:

alias GenBeans java org.netbeans.modules.schema2beansdev.GenBeans

All generated beans will inherit from a schema2beans class called BaseBean.
If you run the generator without any argument, you get the following help:

usage: 

  GenBeans -f filename [-d docRoot] [-t] [-p package] [-r rootDir]
        [-sp number] [-mdd filename] [-noe] [-ts] [-veto] [-version]
      [-st] [-throw] [-dtd|-xmlschema] [-javabeans] [-validate]
      [-propertyevents] [-attrprop] [-delegator] [-commoninterface]
      [-premium] [-compile] [-defaultsAccessable] [-auto]
      [-useinterfaces] [-geninterfaces] [-keepelementpositions]
      [-dumpbeantree filename] [-removeUnreferencedNodes]
      [-genDotGraph filename] [-comments] [-checkUpToDate]
[-writeBeanGraph filename] [-readBeanGraph filename]*

where:
    -f    file name of the DTD
    -d    DTD root element name (for example webapp or ejb-jar)
    -p    package name
    -r    base root directory (root of the package path)
    -sp    set the indentation to use 'number' spaces instead of
        the default tab (\t) value
    -mdd    provides extra information that the dtd cannot provide.
        If the file doesn't exist, a skeleton file is created and
        no bean generation happens.
    -noe    do not throw the NoSuchElement exception when a scalar property
        has no value, return a default '0' value instead.
    -ts    the toString() of the bean returns the full content
        of the bean sub-tree instead of its simple name.
    -veto    generate vetoable properties (only for non-bean properties).
    -st    standalone mode - do not generate NetBeans dependencies
    -throw    generate code that prefers to pass exceptions
        through instead of converting them to RuntimeException (recommended).
    -dtd    DTD input mode (default)
    -xmlschema    XML Schema input mode
    -javabeans    Generate pure JavaBeans that do not need
        any runtime library support (no BaseBean).
    -validate    Generate a validate method for doing validation.
    -propertyevents    Generate methods for dealing with property events (always on for BaseBean type).
    -attrprop    Attributes become like any other property
    -delegator    Generate a delegator class for every bean generated.
    -commoninterface    Generate a common interface between all beans.
    -premium    The "Premium" Package.  Turn on what ought to be the default switches (but can't be the default due to backwards compatibility).
    -compile    Compile all generated classes using javac.
    -defaultsAccessable    Generate methods to be able to get at default values.
    -useInterfaces    Getters and setters signatures would any defined interface on the bean.
    -genInterfaces    For every bean generated, generate an interfaces for it's accessors.
    -keepElementPositions    Keep track of the positions of elements (no BaseBean support).
    -dumpBeanTree filename    Write out the bean tree to filename.
    -removeUnreferencedNodes    Do not generate unreferenced nodes from the bean graph.
-writeBeanGraph Write out a beangraph XML file. Useful for connecting separate bean graphs.
-readBeanGraph Read in and use the results of another bean graph.
    -genDotGraph    Generate a .dot style file for use with GraphViz.
-comments Process and keep comments (always on for BaseBean type).
-doctype Process and keep Document Types (always on for BaseBean type).
-checkUpToDate Only do generation if the source files are newer than the to be generated files.
    -t [parse|gen|all]    tracing.
    -finder Add a finder method. Format: "on {start} find {selector} by {key}". Example: "on /ejb-jar/enterprise-beans find session by ejb-name".

The bean classes are generated in the directory rootDir/packagePath, where packagePath is built using the package name specified. If the package name is not specified, the doc root element value is used as the default package name.  Use the empty string to get no (default) package.
examples: java GenBeans -f ejb.dtd
        java GenBeans -f webapp.dtd -d webapp -p myproject.webapp -r /myPath/src
        java GenBeans -f webapp.xsd -xmlschema -r /myPath/src -premium

Most of the parameters are optional. Only the file name is mandatory.
With only the file name specified, the generator uses the current directory, and uses the schema docroot value as the package name.


BaseBean style beans

This style of beans generation requires the use of the schema2beans.jar library at runtime.  All generated beans inherit from org.netbeans.modules.schema2beans.BaseBean.  BaseBean is pretty generic and consequently slower and eats more memory than the alternative.  This is the default generation style.

Generation of runtimeless beans

To generate a set of beans that do not depend on the schema2beans runtime, use the GenBeans class.

GenBeans -f myFile.xsd -javabeans

I've been calling them "pure java beans" since the generated beans are the sort of beans that you would write if you wrote them by hand.  They tend to be faster and take up less memory than the ones that require the runtime, since the runtime needs to be more general.  However, not all runtime features are available on these bean that are available on the BaseBean beans; for instance, merge is only available on BaseBean.

GenBeans options
The only mandatory option is -f, to specify the name of the DTD or XML Schema file. The generator figures out by itself what is the root of the DTD or XML Schema graph and uses it as the package name. The current directory is used as the default root directory, where the generated files are created.

You might want to specify the exact name to use for the package. To do that, uses the -p option.

GenBeans -f myDtdFile.dtd -p org.myProject.myPackage
This creates the bean classes under the directory ./org/myProject/myPackage (Since the root directory is not specified, the current directory is used).

To create the package/classes under a specific directory (and not the current directory), uses the -r option. It specifies the root directory.

GenBeans -f myDtdFile.dtd -p org.myProject.myPackage -r /mySrc
This creates the bean classes under the directory /mySrc/org/myProject/myPackage.

It might happen that the dtd file contains more than one possible doc-root element (any element that has no parent). For example, the J2EE web-app DTD has two possible doc-root elements: webapp and url . When the generator doesn't know which element is the doc-root, it asks the user to choose one:

GenBeans -f web-app_1_2.dtd
Building the DTD object graph.
DTD Object graph built.
The following elements could be the root of the document:
1. web-app
2. url
Enter the element that should be used as the root:
Then, the user enters either 1 or 2, to specify the root of the document:
Enter the element that should be used as the root: 1
Using web-app as the root of the document.
created directory .\webapp
Generating class ...


If you already know the doc-root element, you might specify it using the -d option.

GenBeans -f web-app_1_2.dtd -d web-app
Building the DTD object graph.
DTD Object graph built.
The following elements could be the root of the document:
1. web-app <= parameter value
2. url
Using web-app as the root of the document.
Generating class ....
By default, the generator uses one Tab for the indentation. If you want spaces instead of one Tab, uses the -sp option.
GenBeans -f myDtdFile.dtd -p org.myProject.myPackage -sp 3
Generates the classes with an indentation of 3 spaces.

Sometimes, you would like to specify extra information that the dtd cannot describe. For example, a property might have only a set of well defined values that the generated class could know and takes advantage of. Any extra information that cannot be defined in the dtd file is specified in the extra information file, using the -mdd option.

When a property has no element set (typically an element tag in the XML file is not specified), the getter method returns null. When the property is of scalar type, such as int, the getter method cannot return null. The default behavior is for the getter method to throw a NoSuchElementException exception, when there is no value for the property. If you do not want an exception to be thrown, use the -noe option. The getter method will return a default zero value instead. Note that the isNull() method on the BaseBean class allows to check if a property is null, before trying to get its value.

The default toString() method is implemented in the BaseBean class, and simply returns the name of the bean. With the -ts option, the generator adds a toString() method to the generated beans, calling the public method dumpBeanNode(). This method returns the content of the whole bean sub-tree, as a String. Therefore the user has the choice between two toString() versions: the default implementation returning the name of the bean, and a verbose version returning the whole content of the bean (all its sub-tree content).

The -veto option allows the use of VetoableChangeSupport.
The -st option generates beans that do not need NetBeans (recommended).
The -throw option will generate code that prefers to pass exceptions through instead of converint them to RuntimeException (recommended).
The -xmlschema option tells GenBeans to parse the schema file as an XML Schema file.
The -javabeans option turns on generation of java beans that do not need a runtime (see Generation of runtimeless beans, above) (recommended).
The -validate option makes it generate a validate method in every bean which will test how well that bean conforms to the schema.  If it does not conform, then an exception is thrown.
The -propertyevents option makes it generate PropertyChangeEvents.  This is on by default for BaseBean beans.
The -attrProp option makes it generate attributes into normal java bean properties; that is, the attribute will have a getter and setter (recommended).
The -delegator option makes it generate an additional class for every bean where the bean is a delegate of that class.  You can set the superclass to whatever you want it to be (see mdd file).
The -commoninterface option makes it generate a CommonBean.java interface which is the intersection of all beans' methods.  This is very useful when you want to refer to any particular bean in your graph.
The -premium option is there so that you don't have to type in 10 arguments to get all of the recomended features.  This is the "Premium" Package of arguments.  All arguments that are deemed good and most people should use them will be turned on here.  All of the premium arguments ought to be the default ones, but due to backwards compatibility can not be the default ones.  In the future, additional arguments may be added to the Premium Package.  This means that compatibility is not guarenteed in the generated beans between version x of schema2bean's -premium and version y's (recommended).
The -defaultsAccessable option will generate extra methods for the properties that have defaults and are not indexed.  The method name is fetchDefault{propertyName}.
The -useInterfaces option will have the getters and setters of bean properties use the first interface that that bean implements.
The -genInterfaces option will generate for every bean an interface that has all of the getter and setter methods on it.  Some developers may prefer to work with the beans through just these interfaces.  One might use this option with the -useInterfaces option.
The -keepElementPositions option will have the bean keep track of relative element positions.  This can be important if you have a schema that allows 2 or more elements to be listed interspersed with each other and their order is significant.  E.g., (menu-item | separator)*.  Additional methods for getting at elements by position are added:

  • Object fetchChildByPosition(int position)
  • int fetchChildCount()

Currently, turning on this option will slow things down, since every add to an indexed property is preceded by a search to figure out where to put the value.  Note, this is not available for BaseBean.
The -dumpBeanTree filename option will write the bean tree to filename.  This tree can be helpful in understanding the structure of the schema.  Note that this tree is automatically included in comments in the root element's binding class.
The -genDotGraph filename option will generate a .dot file for use with GraphViz.  This helps to visualize the structure of the data.
The -removeUnreferencedNodes option will go through and remove any types defined in the schema that are not referencable from the root.  This will cut down on classes that get generated but not actually used.
The -comments option will give access to comments in the generated java beans (always on for the BaseBean type).  Without this option on, comments from source XML are ignored.
The -checkUpToDate option will make java bean generation occur only if the source files are newer than the files to be generated.  Note that a fair amount of the schema is processed before it knows the exact file name of all potentially generated files.
The -writeBeanGraph option will make it write out all types found in the schema.  See here.
The -readBeanGraph option will read 1 or more bean graphs and use them to override the types from the schema.  See here. 

Generated classes
If you look at the generated classes, you'll see that only some elements of the schema have a corresponding bean class. Basically, if you draw a tree of the schema elements (see -dumpBeanTree or -genDotGraph if you really want to), each node of the tree has a bean class and each leaf of the tree is a simple typed property (String, Boolean, ...). Any bean might be a property of another bean, following the schema.

The element of the schema which has no parent is, by default, choosen as the root. The GenBeans command line prints out during generation time which element has been choosen to be the root. If you look at the bean class generated for this root element, you'll see in the header comments the tree of the bean classes, expressed as a tree of beans. This  might be useful to get an idea of what has bean generated.

A generated bean for BaseBean is composed of the following parts:
 

  • package name

  • This is the name specified by the -p option or the name of the doc-root element if the -p option was not specified.
  • import section
    • import org.w3c.dom.*;
      import org.netbeans.modules.schema2beans.*;
      import java.beans.*;
      import java.util.*;
      import java.io.*;
      import com.sun.xml.tree.*;
  • class name

  • The name of the dtd element, removing any - character and having any first letter upper-cased. Any bean extends the class BaseBean, and can therefore takes advantage of its methods. See the BaseBean section for more information.
     
  • declaration

  • The names of all the properties of the bean expressed as constants.
     
  • constructor

  • There is only one default constructor.
     
  • accessor methods

  • See the section about the accessors.
     
  • event methods

  • See the section about the event.
     
  • graph creation methods

  • See the section about the graph creation.
     
  • utility methods

  • The toString() method returns either the subtree content (option -ts of the generator, or the simple name of the bean).
    The addComparator() and removeComparator() static methods provide a way to customize the comparison of graphs .

Extra information file

The -mdd option of the generator is used to specify extra information about the bean classes. Some information that we might want to specify to generate the bean classes cannot be expresed in the dtd (for example the list of valid values that a property can get, or the user-comparator class to use to merge and compare two graphs). The -mdd option allows to specify the name of the file containing such extra information.

The extra information file is an XML file that intends to complement what cannot be expressed in the dtd. Following is the dtd of this extra information file:
 

<!ELEMENT metaDD (meta-element*, implements?, extends?,  import*, vetoable?, throw-exceptions?, schemaLocation?, finder*)>
<!ELEMENT meta-element (dtd-name, namespace?, bean-name?, bean-class?, wrapper-class?, default-value*, known-value*, meta-property*, comparator-class*, implements?, extends?, import*, user-code?, vetoable?, skip-generation?, delegator-name?, delegator-extends?, bean-interface-extends?>
<!ELEMENT meta-property (bean-name, default-value*, known-value*, key?, vetoable?)>

<!ELEMENT dtd-name (#PCDATA)>
<!ELEMENT namespace (#PCDATA)>
<!ELEMENT bean-name (#PCDATA)>
<!ELEMENT default-value (#PCDATA)>
<!ELEMENT key EMPTY>
<!ELEMENT known-value (#PCDATA)>
<!ELEMENT bean-class (#PCDATA)>
<!ELEMENT wrapper-class (#PCDATA)>
<!ELEMENT comparator-class (#PCDATA)>
<!ELEMENT extends (#PCDATA)>
<!ELEMENT implements (#PCDATA)>
<!ELEMENT user-code (#PCDATA)>
<!ELEMENT vetoable EMPTY>
<!ELEMENT throw-exceptions EMPTY>
<!-- Automatically set the schemaLocation -->
<!ELEMENT schemaLocation (#PCDATA)>

<!ELEMENT finder (#PCDATA)>

<!ELEMENT bean-interface-extends (#PCDATA)>


The extra information file should contain a meta-element tag for each dtd element that we want to provide more information about.  For an XML Schema, dtd-name corresponds to the types of elements and not the element names.

If the generator doesn't find the file specified by the -mdd option, it generates a default extra information file, based on the dtd.

GenBeans -f myDtdFile.dtd -mdd myDtdFile.mdd
Building the DTD object graph.
DTD Object graph built.
Using myElement as the root of the document.
The mdd file myDtdFile.mdd doesn't exist. Should we create it (y/n) ?y
Writing metaDD XML file
If the generator finds the file, it uses it. Therefore, if we run the same command again:
GenBeans -f myDtdFile.dtd -mdd myDtdFile.mdd
Building the DTD object graph.
DTD Object graph built.
Using myElement as the root of the document.
Using the mdd information from myDtdFile.mdd
Generating class .....


The idea is to edit this file, adding any extra information that we want in the generated bean classes, and run GenBeans using these information.  Be careful if you have an old mdd file an upgrade the schema file.  The meanings of some types may have changed or gone away.
 

User defined comparators
The comparator-class element specifies the comparator class to use when two graphs are compared and merged. Several comparators can be specified, see the merge section for more details.
 
Extending a bean class
The bean-class element specifies the class that the schema2beans library should instantiate for this bean, when it builds the bean graph from an XML file. If this element is not specified, the class name is simply the name of the bean. If you plan to extend the bean class, you can specify the extended bean class name.

For example, if the Book bean element has a bean property named Chapter, the schema2beans generates Book.java and Chapter.java. Therefore, the two bean classes are Book.class and Chapter.class. If you want to extend the Chapter bean, you can do the following:
 

//    Define your own Chapter class
public class MyChapter extends Chapter
{ ... }

//    Use the new Chapter class as the default Chapter class to instanciate
<metaDD>
    <meta-element>
        <dtd-name>chapter</dtd-name>
        <bean-name>Chapter</bean-name>
        <bean-class>MyChapter</bean-class>
    </meta-element>
</metaDD>

Note that you have to generate the beans to make your new extended class part of the generated beans. If you simply extend a bean class without regenerating th bean classes, you will be able to create them by yourself (new MyChapter()), but, when reading an XML stream,  the schema2beans runtime will create the default beans and not your new class. So, you do need to regenerate the beans to make sure that the new information you added to the extra information file is part of the generated beans. This remark is valid for any information you add to this extra information file: you need to regenerate the beans to make these information part of the beans.

Another approach to doing this is to create a class (which inherits from BaseBean if you're using that generation style) and use the extends property in the mdd file.
 
Default value
The default-value element is used to specify the default value of a property. Only nonbean properties can use this feature. You might specify more than one value. If the property is an indexed property, all the default values will be used to build the default content of the indexed property. If the property is a single property value, only the first default value is used. More generally, any property which is not a bean property (not a node of the graph of beans), should be able to use this element to specify a default value.

  <meta-element>
    <bean-name>Book</bean-name>
    <dtd-name>book</dtd-name>
    <meta-property>
      <bean-name>Title</bean-name>
      <default-value>no title</default value>
    </meta-property>
    <meta-property>
      <bean-name>Author</bean-name>
      <default-value>Smith</default value>
      <default-value>Barnes</default value>
    </meta-property>
  </meta-element>

Let's assume that the generated Book bean has some default values. Doing the following,

Book b = new Book();
creates a book bean with the default values specified by the default-value element. If you specified default values at generation time and don't want to use them when you instanciate the bean, use the following constructor (only applies to BaseBean style):
Book b = new Book(Common.NO_DEFAULT_VALUE);
When the schema2beans runtime instanciates a graph from an XML file, it doesn't use the default values. Only the content of the XML file is part of the graph (only applies to BaseBean style).

The default value element might be specified in the property meta-element declaration (this default value is then used any time this property is used), and/or in the meta-property declaration of a property within an element declaration (this declaration takes precedence over the meta-element declaration).

Well-known values
The known-value element is used to specify know values that a property might get (only applies to BaseBean style). The idea is to specify these values at generation time, and get the list of these values at runtime, through a BaseBean method call.

Like the default-value tag, the known-value tag might be specified in the meta-element declaration and/or in the meta-property declaration. The latter taking precedence. To get the list of well-know values at runtime, use the following method call from the BaseBean class:

public Object[] knownValues(String name)
This returns the list of values specified by the known-value elements, for the property named name.
These known values are added as an enumeration restriction to the type.  So, if validation is turned on, the value of that property must be one of the enumeration values or the validate call will fail.
Wrapper
The wrapper-class element is used to tell the schema2beans generator and runtime which class should be used to represent a single or indexed property in the graph of beans. A wrapper class can be used only for #PCDATA dtd-elements (which defaults to the java.lang.String class). If the property is a bean, it is possible to change the class that gets used in the graph. Also, see extending a bean class.

For example, if there is the following declaration in the dtd,

    <!ELEMENT date #PCDATA>

The following accessors are generated:

    public String getDate();
    public void setDate(String value);

while the default generated Mdd contains:

  <meta-element>
    <bean-name>Date</bean-name>
    <dtd-name>date</dtd-name>
    <wrapper-class>String<wrapper-class>
    ...
  </meta-element>

Assuming you created your own MyDate class to handle this element, you can use it instead of the default String class. To do that, change the Mdd file:

  <meta-element>
    <bean-name>Date</bean-name>
    <dtd-name>date</dtd-name>
    <wrapper-class>MyPackage.MyDate<wrapper-class>
    ...
  </meta-element>

And regenerate the bean classes using this modified mdd file. You'll get the following accessors:

    public MyPackage.MyDate getDate();
    public void setDate(MyPackage.MyDate value);

It is also possible to generate a scalar data type instead of a class type. Simply specify the scalar type you want to generate.  For example, we could decide that the date element of the above example should be represented as an integer:

  <meta-element>
    <bean-name>Date</bean-name>
    <dtd-name>date</dtd-name>
    <wrapper-class>int<wrapper-class>
    ...
  </meta-element>

After regeneration, we would get:

    public int getDate();
    public void setDate(int value);

The wrapper-class can only by used for properties. You cannot use a wrapper for an attribute. The attributes are always of type String.

The wrapper class represents, in the bean graph, a String element of the DOM document: when the DOM document is read, the string element is converted into a wrapper object; when a wrapper object is written in the DOM document the wrapper object is converted into a String. There are two ways this can happen, and you have to make sure that the wrapper class you provide support one of these two ways.

1. Wrapper interface
public interface Wrapper
{
    public String  getWrapperValue();
    public void  setWrapperValue(String value);
}
If your wrapper class implements this interface (part of the schema2beans package), the schema2beans runtime uses it to set and get the object value.
 
2. String constructor
If your wrapper object doesn't implement the Wrapper interface, the schema2beans runtime looks for a String constructor. If there is one, this is used to instanciate the object with the proper value. The toString() method is used to get a String value of the object.

    public class_name(String value)
    public String toString()

If none of these two mechanisms is available, the schema2beans cannot use the specified wrapper class and throws a runtime exception.
 

Customizing the class definition
If you want a generated bean to extend a specific class, and/or to implement a specific interface, you might specify the class name and interface name(s) using the extends and implements element names.

You can define the values for all the generated beans (if specified in the metaDD element declaration) or for some specific elements (specified in the appropriate meta-element declaration). Any meta-element declaration takes precedence over the metaDD element declaration. If you specify another base class for a generated bean, this new base class must derived from the BaseBean class.

<metaDD>
    <extends>MyBaseBean</extends>
    <meta-element>
        <dtd-name>chapter</dtd-name>
        <bean-name>Chapter</bean-name>
        <implements>MyInterface1, MyInterface2</implements>
        <bean-class>MyChapter</bean-class>
    </meta-element>
</metaDD>
Comparison  keys
If a meta-property is marked with the key tag, the property is used by the default comparison algorithm when comparing beans.

The key tag is an empty tag ( <key/>) used to mark a meta-property element as a key . If there is no meta-property defined as a key for a meta-element (this is the default scenario), then all meta-property of the meta-element are considered as keys. However, as soon as one meta-property is marked as a key, the other meta-properties (not marked as keys) are no more considered keys.

If the property is not a key, the property is not used when comparing beans. This tag is a simple way to specify the significant elements that should be part of the beans comparison, without implementing your own Comparator bean.

For example, you might consider that the description property of a bean is not relevant when comparing beans. You might declare keys all properties but the description property.

The same comparison algorithm and keys are used to compare and merge bean graphs, therefore, specifying keys for comparison purpose will also impact how two graphs are merged. If you want to use specific keys for comparison, but not use these keys to merge bean graphs, you might want to use the method BeanComparator.enableComparatorsKey() to enable/disable the mdd keys usage (see the Merge section for more details).
 

User code
The user-code element is used to specify java code that is directly inserted into the generated beans. The generator simply copy the value of the user-code element and paste the content into the generated bean. The user-code element can be used within any meta-element property which is a bean. User code part of a final meta-property element is ignored and not inserted into the generated beans.

<metaDD>
    <meta-element>
        <dtd-name>chapter</dtd-name>
        <bean-name>Chapter</bean-name>
        <bean-class>MyChapter</bean-class>
        <user-code>
        public String myOwnToString() {
            return "Method code specified from the mdd file";
        }
        </user-code>
    </meta-element>
</metaDD>
 

Vetoable properties
The vetoable element is used to specify which property should support the vetoable event. By default, the schema2beans doesn't generate any vetoable information for the properties. If the generator is run with the -veto option, it generates vetoable information for any property which is not a bean (any property that corresponds to a #PCDATA element in the DTD file).

If you wish to generate vetoable information for only some specific properties, you should use the mdd file and its vetoable element. As soon as you specify one <vetoable/> element in the mdd file, the -veto option is ignored.

You might specify the </vetoable> element in three different places: in the metaDD, meta-element or meta-property .

The metaDD element applies for the whole graph. If you specify the <vetoable/> element here, any non-bean property of the graph will be generated with vetoable information. This is the equivalent of the -veto option.

<metaDD>
    <vetoable/>
    <meta-element>
        <dtd-name>name</dtd-name>
        <bean-name>Name</bean-name>
    </meta-element>
    <meta-element>
    ...
    </meta-element>
</metaDD>

The meta-element information corresponds to either a bean-node in the graph or a final property. If you specify the <vetoable/> element and this is a bean-node element, the information will be ignored (no vetoable information is generated for bean properties). However, if the element is a final property (which corresponds to a #PCDATA element in the DTD), vetoable information will be generated every time this property is used in the graph. For example, if you have a meta-element 'name' used in different places of your graph, specifying the <vetoable/> element here would make sure that any generated 'name' setter methods would have vetoable information.

<metaDD>
    <meta-element>
        <dtd-name>name</dtd-name>
        <bean-name>Name</bean-name>
        <vetoable/>
    </meta-element>
</metaDD>

The meta-property information corresponds to a final property. Specify the <vetoable/> information here if you wish to generate vetoable information for this specific property.

When the schema2beans generates vetoable information for a property, it flags the property as possibly using vetaoble events, generates the ' throws PropertyVetoException' clause for the setter methods of this property (set, add and remove methods) and also makes sure that the appropriate add/remove listener methods are generated. It is up to the user to create listeners and register them.
   

throw-exceptions properties
The throw-exceptions element tells the software to generate methods that will throw exceptions.  When this property is not in the mdd file, no exceptions are declared to be thrown, but RuntimeExceptions are still possible.  It is recommended that you turn throw-exceptions on; this way, you can better see any exceptions coming at you.
Using -wrtieBeanGraph and -readBeanGraph
-readBeanGraph allows the user to override the types in the schema.  For instance, normally xsd:integer is mapped to java.math.BigInteger, but if you wanted to make it an int instead, create the following XML:
<?xml version='1.0' encoding='UTF-8' ?>
<bean-graph>
<schema-type-mapping>
        <schema-type-namespace>http://www.w3.org/2001/XMLSchema</schema-type-namespace>
        <schema-type-name>integer</schema-type-name>
        <java-type>int</java-type>
    </schema-type-mapping>
</bean-graph>

 The -writeBeanGraph option will write out all types found in the schema in the format shown above.  This includes the name of the schema element along with it's namespace, and the java type that was used for it.  That file can then be used as input to another GenBeans -readBeanGraph.  This can be very useful if you want to share types from the first class generation.

For example, say you've got an XML Schemas a.xsd, b.xsd, and c.xsd.  Both c.xsd & b.xsd imports a.xsd.  So, rather than generate 3 completely separate groups of classes, you could do:
GenBeans -f a.xsd -premium -writeBeanGraph a_beangraph.xml
GenBeans -f b.xsd -premium -readBeanGraph a_beangraph.xml
GenBeans -f c.xsd -premium -readBeanGraph a_beangraph.xml

This will save space and help if you want to use common objects between graphs.  Note that if you don't use the same generation options in the shared java beans, you might get compile errors (for example, if you told b.xsd to do validation, it would assume that the classes from a.xsd have a validate method too).

Creating the java bean graph

If we were looking for analogy, creating the graph could be compared to deserializing the XML file into a graph of beans, and writting, as serializing the graph of  beans into an XML file. This section describes the different ways to create the graph of beans.

There are two ways to create the bean graph for BaseBean: dynamically using the DDFactory class or using the createGraph() static method on the root of the beans.

createGraph() method
Let's say, for the following examples, that you generated a set of beans and that the class Book is the root bean.
From an InputStream
You might create the graph using an input stream object:
try
{
    FileInputStream in = new FileInputStream("mybook.xml");
    Book book = Book.createGraph(in);
    System.out.println(book.dumpBeanNode());
    in.close();
}
catch(IOException ...
By default, the createGraph() method doesn't invoke the validating parser. Therefore, the parsed XML document is not validated before the graph is built. If you want to make sure that the XML document is valid before the graph is built, use the second form of the createGraph() method:
try
{
    FileInputStream in = new FileInputStream("mybook.xml");
    //    Use the validating parser
    Book book = Book.createGraph(in, true);
    System.out.println(book.dumpBeanNode());
    in.close();
}
catch(IOException ...
From a DOM node
This current implementation uses the Sun Java project X (XML technology services), with its XMLDocument class. Note that the createGraph(doc) call simply needs a DOM node interface to build the graph (a standard DOM interface). However, the doc object parameter has to be in fact an XMLDocument class object (which implements the DOM Node interface) in order to create new elements. Following is a complete example for building a graph:
try
{
    FileInputStream in = new FileInputStream("mybook.xml");
    XmlDocument doc = XmlDocument.createXmlDocument(in, false);
    Book book = Book.createGraph(doc);
    System.out.println(book.dumpBeanNode());
    in.close();
}
catch(IOException ...
From scratch
To create an empty brand new graph:
//    Create a brand new graph from scratch
Book book = Book.createGraph();
System.out.println(book.toString());
or. you can simply new the bean root:
//    Create also a brand new graph from scratch
Book book = new Book();
System.out.println(book.toString());
Cloning
You can also create a graph from another one, cloning it:
Book book = Book.createGraph(in);
//    Create a new graph using clone()
Book book2 = (Book)book.clone();
DDFactory
The other way to create the bean graph is using the DDFactory. This approach consists in registering the doc-root name and bean class in the DDFactory registry, then asking the DDFactory to build the graph from an input stream, giving the docroot you are intersted in.

This method is more generic and dynamic, since you can register several name/classe pairs in the DDFactory registry, and ask for a graph, only specifying its logical name and the input stream. The DDFactory.create() method returns a BaseBean object, base class for any generated bean.

DDFactory.register("book", "book.Book");
BaseBean book = DDFactory.create(in, "book");
System.out.println(book.toString());

Creating bean graph (no runtime style)

The root class has several read methods on it that are all static and return a filled in root class:
  1. read(java.io.InputStream in)
  2. read(org.xml.sax.InputSource in, boolean validate, org.xml.sax.EntityResolver er, org.xml.sax.ErrorHandler eh)
  3. readNoEntityResolver(java.io.InputStream in)
The first read method just call the second read method with validate turned off and no EntityResolvers or ErrorHanlders.  The thrid read method lets you read in the XML without looking up DTDs (which might be done across a slow network).

Also, every bean has a readNode(org.w3c.dom.Node) method that will read into the bean the data from that DOM Node.

Editing the bean graph

Properties
You can edit any property of the beans, using the method provided in the generated beans.
  • To set a property, use the setter methods.
void setPropertyName(PropertyType value)    // for nonindexed
void setPropertyName(int index, PropertyType value)     // for indexed
void setPropertyName(PropertyType value[])    // for indexed
Setting a property to null removes the property. If this is an indexed property, the number of elements of the indexed property remains unchanged and a getter call on the element returns null. Note that the property type might be a scalar type, for example int or float (see Wrapper classes).
  • To get a property value, call the getter methods:
PropertyType getPropertyName()
boolean isPropertyName()
PropertyType getPropertyName(int index)      // for indexed
PropertyType[] getPropertyName()      // for indexed
If the property type is of type boolean, the getter method generated is isPropertyName() instead of getPropertyName()
  • To remove/add a property for an indexed property, use the remove/add method:
int removePropertyName(PropertyType value)
int addPropertyName(PropertyType value)
The remove() method removes an element from an indexed property. The elements following the removed element will shift to the left, decrementing their index value by 1.

The add() method adds an element at the end of the indexed property array. Both methods return the index of the removed/added element.

  • To get the number of elements in an indexed property, use the size method:


  • int sizePropertyName()
For BaseBean sytle, if you want to copy an element of the graph to insert it into any other graph (even the same graph), you have to clone it before. Otherwise, you'll get an exception.
 
Attributes
I highly recommend using the option to turn on generation of getters and setters for attributes which will make things easier to use.
You can set or get an attribute value using the generic attribute accessor methods of the BaseBean class (any generated bean derives from this class).
//    Attribute setter method for single and indexed properties
void setAttributeValue(String propName, String name, String value)
void setAttributeValue(String propName, int index, String name, String value)

//    Attribute setter method on the current bean
void setAttributeValue(String name, String value)
 

//    Attribute getter method for single and indexed properties
String getAttributeValue(String propName, String name)
String getAttributeValue(String propName, int index, String name)

//    Attribute getter method on the current bean
String getAttributeValue(String name)


For example, let's define the following DTD:

<!ELEMENT book (title, chapter+)>
<!ELEMENT title (#PCDATA)>
<!ELEMENT chapter (summary?, paragraph*)>
<!ELEMENT summary (#PCDATA)>
<!ELEMENT paragraph (#PCDATA)>

<!ATTLIST book instock (yes | no) "yes">
<!ATTLIST title lang CDATA #FIXED "en">
<!ATTLIST chapter length CDATA #IMPLIED>

We have defined the following graph:
Book
    <instock>
    Title - String
        <lang>
    Chapter[1,n]
        <length>
        Summary? - String
        Paragraph[0,n] - String
Where, instock is a enumerated attribute on single type property (here, on the root of the bean graph), lang a fixed attribute on a single type property, and length an optional attribute on an indexed property.

The following code reads the attributes:

Book book = Book.createGraph(some input stream for the XML file...);
String inStock = book.getAttributeValue("Instock");
String inStock3 = book.getAttributeValue("INSTOCK"); // This throws an exception (attribute not known)
book.setAttributeValue("Instock", "no");        //    Set the attribute value to no
book.setAttributeValue("Instock", "maybe");    //    This throws an exception (not enumerated value)

String lang = book.getAttributeValue("Title", "Lang");
book.setAttributeValue("Title", "Lang", "fr"); // This throws en exception (fixed value)

if (book.sizeChapter() > 0)
{
    String length = book.getAttributeValue("Chapter", 0, "Length"); // One way to get the attribute
    Chapter c = book.getChapter(0);
    String length2 = c.getAttributeValue("Length"); // another way to get the attribute
    c.setAttributeValue("Length", "200");
    String length3 = book.getAttributeValue("Chapter", 0, "Length"); // returns "200"
}


To remove an attribute, set the attribute with the value null.

When an attribute is added, changed or removed, a PropertyChangeEvent is fired (if any PropertyChangeListener has been defined). The event name contains the name of the property and the name of the attribute. To extract the property and attribute names from the fired event, use the event utility methods . Note that the VetoableChangeListener support is only for properties, not for attributes. A VetoableChangeListener will never get a PropertyChangeEvent when an attribute is changed.

When specifying the name of the property or attribute, you can use either the dtd name or the bean modified name of the property or attribute.

It is possible to dynamically get the list of all the attributes (set and non-set) of a property, using the following methods:

String[] getAttributeNames(String propName)
String[] getAttributeNames()
Following the above example, we would have:
String[] a = book.getAttributeNames(); // returns a[0] = "Instock"
a = book.getAttributes("Title"); // returns a[0] = "Lang"
a = book.getAttributes("Chapter"); // returns a[0] = "Length"
Chapter c = book.getChapter(0);
c.getAttributes(); // returns a[o] = "Length"
Transient attributes
A transient attribute is an attribute that has been added to the bean structure dynamically, on the fly, when building the bean graph from the XML document. A transient attribute is an attribute that is not defined in the DTD file but used in the XML file. Such attributes are marked TRANSIENT and are always CDATA and IMPLIED.
Besides the fact that they are marked transients, they are considered exactly like the other attributes, created from the DTD file. Both are part of the list of BaseAttribute returned by the BaseBean.listAttributes() or BaseProperty.getAttributes() methods. Also, both might generate events if their values change.
 
Introspection on BaseBean style
You can dynamically get the list of the properties and attributes of any bean, using the following methods:
BaseProperty[] listProperties()
BaseProperty getProperty()
BaseProperty getProperty(String propName)

BaseProperty[] listChoiceProperties(String propName)
Iterator listChoiceProperties()

BaseAttribute[] listAttributes()
 

BaseProperty
BaseProperty is an interface providing the following methods:
String           getName();              // the bean generated name
String           getDtdName();           // the dtd name as it appears in the DTD file
Class            getPropertyClass();     // the class of the property (for example java.lang.String)
boolean          isIndexed();            // return true if this is an indexed property
boolean          isBean();               // return true if the property is a bean (node in the graph)
int              size();                 // number of elts, if this is an indexed property
String[]         getAttributeNames();    // the attributes of the property, if any
BaseAttribute[]  getAttributes();        // the attribute list of this property, if any
String           getFullName(int index); // the full path name of one element of the indexed the property
String           getFullName();          // the full path name of the property
int              getInstanceType();      // the type of instance as declared in the DTD
boolean          isChoiceProperty();     // true if this prop is among a set of choice properties
BaseProperty[]   getChoiceProperties();  // return the whole set of choice properties (if applicable)
boolean          hasName(String name);   // true if parameter name matches the dtd or bean name
The method getInstanceType() can return one of the following values:
BaseProperty.INSTANCE_OPTIONAL_ELT        // DTD option ?
BaseProperty.INSTANCE_MANDATORY_ELT       // no option specified in the DTD
BaseProperty.INSTANCE_OPTIONAL_ARRAY      // DTD option *
BaseProperty.INSTANCE_MANDATORY_ARRAY     // DTD option +
If the property is a choice property, the following methods can be used:
boolean isChoiceProperty();
BaseProperty[] getChoiceProperties()
For example, if the DTD has the following declaration:

<!ELEMENT flavor ((vanilla | berry), extra-sugar?)>

we would get:

    BaseProperty bp = ...(BaseProperty)flavor prop...
    bp.isChoiceProperty("vanilla"); // true
    bp.isChoiceProperty("berry"); // true
    bp.isChoiceProperty("extra-sugar"); // false

Also,

    BaseProperty bp = (BaseProperty)flavor...
    //    The array has two BaseProperty elements: vanilla & berry
    BaseProperty[] bps = bp.getChoiceProperties();

Similar methods are defined on the BaseBean class:

    //    True if the property name is a choice property
    //    Use listChoiceProperty(propName) to get the other properties
    boolean isChoiceProperty(String propName);

    //    Might return null if not a choice property
    BaseProperty[] listChoiceProperties(String propName);

    //    Return an Iterator on a list of BaseProperty arrays
    //    This returns the list of all the choice properties of the current bean property
    Iterator listChoiceProperties();

For example:

    //    Print the list of all the choice properties of the flavor bean property
    Flavor f = myIceCream.getFlavor();
    Iterator it = f.listChoiceProperties();
    while (it.hasNext())
    {
        BaseProperty[] bps = (BaseProperty[])it.next();
        for (int i=0; i<bps.length; i++)
            System.out.println(bps[i].getDtdName);
    }
 

BaseAttribute
You have two ways to get the list of the attributes of a property. The first one is to call BaseBean.listAttributes(),  the second one is to call  BaseProperty.getAttributes(). Both method are identicals. They return the list of all the attributes associated to a property, as an array of BaseAttribute interfaces. The returned array might be empty but cannot be null.
BaseAttribute[] BaseBean.listAttributes();
BaseAttribute[] BaseBean.listAttributes(String propName);
BaseAttribute[] BaseProperty.getAttributes();
The BaseAttribute interface provides the following methods to get information on an attribute:
public String   getName();            // the bean generated name
public String   getDtdName();         // the attribute DTD name
public boolean  hasName(String name); // true if the name param match the bean or DTD name
public String[] getValues();          // list of possible values for enum attribute
public String   getDefaultValue();    // default value used when the attribute is created
public boolean  isEnum();             // same as getType == BaseAttribute.TYPE_ENUM
public boolean  isFixed();            // same as getOption == BaseAttribute.OPTION_FIXED
public int      getOption();          // see values below
public int      getType();            // see values below
public boolean  isTransient();        // true if the attribute is transient


The method getOption() can return:

BaseAttribute.OPTION_REQUIRED
BaseAttribute.OPTION_IMPLIED
BaseAttribute.OPTION_FIXED
The method getType() can return:
BaseAttribute.TYPE_CDATA
BaseAttribute.TYPE_ENUM
BaseAttribute.TYPE_NMTOKEN
BaseAttribute.TYPE_ID
BaseAttribute.TYPE_IDREF
BaseAttribute.TYPE_IDREFS
BaseAttribute.TYPE_ENTITY
BaseAttribute.TYPE_ENTITIES
BaseAttribute.TYPE_NOTATION


The listProperties, listAttributes, getAttributes methods, BaseProperty and BaseAttribute  interfaces provide enough information to parse any bean graph dynamically, without prior knowledge of its structure. (See the treeParser() method in the examples section). 

Introspection (runtimeless style)

 Of course, java.beans.Introspector can be used just like any other java bean.  Here are some additional methods:

  1. public void changePropertyByName(String name, Object value)
  2. public Object fetchPropertyByName(String name)
  3. public {CommonBean}[] childBeans(boolean recrusive)  // return all child beans that are set
  4. public void childBeans(boolean recursive, java.util.List beans)  // place into the beans list all child beans that are set.

Writing the graph as an XML document (BaseBean style)

A write() call on any bean will write the entire bean graph as an XML document on the output stream specified.
book.write(System.out);
Note that there is no method called "read". Instead, the graph is built through the createGraph() method call.

Writing the graph as an XML document (runtimeless style)

The root class has a few write methods on it:
  1. write(java.io.OutputStream out)
  2. write(java.io.OutputStream out, String encoding)
  3. write(java.io.Writer out, String encoding)
If the first 2 write methods are called then an encoding converter is used to write out the text (if encoding is null, then UTF-8) is used.  The third method trusts that the encoding style used is same as what is passed in.
Also note that every bean class has a writeNode method that can be used to write out that bean and all of it's children.

Handling events

Listeners
You can define a PropertyChangeListener for property events on any property of the bean graph (single or indexed property, bean or final type property). You can listen for a specific property or for a set of properties. For example, listening on the root triggers an event each time any element of the tree has changed. Note that an event is triggered if the property content has change, or if any of its attribute has changed (if there is any).

To listen for a specific property, use the addPropertyChangeListener() method call. Use either the generic one to listen on any event or use the method with the property name parameter to listen for a specific property.

 void addPropertyChangeListener(PropertyChangeListener l)
 void addPropertyChangeListener(String n, PropertyChangeListener l)
To remove a listener, use the remove methods:
void removePropertyChangeListener(PropertyChangeListener l)
void removePropertyChangeListener(String n, PropertyChangeListener l)


You can define a VetoableChangeListener for property events on any final-non-bean property (String, scalar, Wrappers, in other words #PCDATA in the DTD)  of the bean graph (single or indexed property). You can listen for a specific property or for a set of properties. For example, listening on the root of the graph fires an event on the VetoableChangeListener whenever a final property is changed in the graph. No event is sent when an attribute is changed.

Note that VetoableChangeListener are called before any change takes place, and PropertyChangeListener are called after the changes took place.

To listen for a specific property, use the addPropertyChangeListener() method call. Use either the generic one to listen on any event or use the method with the property name parameter to listen for a specific property. The property you wish to listen to must have been generated to support the vetoable events (either using the GenBeans -veto option or the mdd <vetoable/> element. See the mdd vetoable section for details).

void addVetoableChangeListener(VetoableChangeListener l)
void addVetoableChangeListener(String n, VetoableChangeListener l)
To remove a listener, use the remove methods:
void removeVetoableChangeListener(VetoableChangeListener l)
void removeVetoableChangeListener(String n, VetoableChangeListener l)
So, if can do something like:
myListener = new MyVetoableChangeListener();
myGraphRoot = GraphBeanRoot.createGraph(someXMLInputStream);

//    Any change on a vetoable property anywhere in the graph