001    package com.sptci.echo2;
002    
003    import java.io.File;
004    import java.io.FileOutputStream;
005    import java.io.IOException;
006    
007    import java.util.List;
008    import java.util.Map;
009    
010    import org.jdom.Attribute;
011    import org.jdom.DocType;
012    import org.jdom.Document;
013    import org.jdom.Element;
014    import org.jdom.JDOMException;
015    import org.jdom.input.SAXBuilder;
016    import org.jdom.output.Format;
017    import org.jdom.output.XMLOutputter;
018    import org.jdom.xpath.XPath;
019    
020    /**
021     * A JDO meta-data generator.  This is a generic generator that does
022     * not apply any JDO vendor specific extensions.  Sub-classes may be
023     * developed that apply vendor specific extensions.
024     *
025     * <p>Copyright 2006 Sans Pareil Technologies, Inc.</p>
026     * @author Rakesh Vidyadharan 2006-02-08
027     * @version $Id: JDOMetaDataGenerator.java,v 1.4 2006/02/15 17:22:56 rakesh Exp $
028     */
029    public class JDOMetaDataGenerator
030    {
031      /**
032       * The name of the class for which the meta-data is to be generated.
033       */
034      protected String name;
035    
036      /**
037       * The name of the package to which the class belongs.
038       */
039      protected String packageName;
040    
041      /**
042       * The fields available in the class for which meta-data is to be
043       * generated.
044       */
045      protected Map<String, String> fields;
046    
047      /**
048       * Default constructor.  No special actions required.
049       */
050      protected JDOMetaDataGenerator() {}
051    
052      /**
053       * Create a new instance of the class using the specified values.
054       *
055       * @param name The {@link #name} value to set.
056       * @param packageName The {@link #packageName} value to set.
057       * @param fields The {@link #fields} value to set.
058       */
059      public JDOMetaDataGenerator ( String name, String packageName,
060          Map<String, String> fields )
061      {
062        super();
063        this.name = name;
064        this.packageName = packageName;
065        this.fields = fields;
066      }
067    
068      /**
069       * Generate the JDO meta-data file for a JDO JavaBean.
070       *
071       * <p>Uses JDOM and Jaxen for parsing an existing meta-data file
072       * and for creating or updating the file.</p>
073       *
074       * @see #updateFile
075       * @see #generateFile
076       * @throws JDOMException If errors are encountered while fetching
077       *   the element.
078       * @throws IOException If errors are encountered while writing to
079       *   the meta-data file.
080       */
081      public void generate() throws JDOMException, IOException
082      {
083        File file = new File( "package.jdo" );
084    
085        if ( file.exists() )
086        {
087          updateFile( file );
088        }
089        else
090        {
091          generateFile( file );
092        }
093      }
094    
095      /**
096       * Generate a new meta-data file with the meta-data for the {@link
097       * #name} JDO object.
098       *
099       * @see #writeFile
100       * @param file The file to generate
101       * @throws JDOMException If errors are encountered while fetching
102       *   the element.
103       * @throws IOException If errors are encountered while writing to
104       *   the meta-data file.
105       */
106      protected void generateFile( File file )
107        throws JDOMException, IOException
108      {
109        DocType docType = new DocType( 
110            "jdo", 
111            "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 1.0//EN",
112            "http://java.sun.com/dtd/jdo_1_0.dtd" );
113    
114        Document document = new Document( new Element( "jdo" ), docType );
115        document.getRootElement().addContent( generateMetaData() );
116        document.getRootElement().addContent( generateEchoPackage() );
117        writeFile ( file, document );
118      }
119    
120      /**
121       * Generate the meta-data elements and attributes for the JDO object
122       * represented by {@link #name}.
123       *
124       * @see #generateSourcePackage
125       * @see #generateSource
126       * @return Element The element that represents the JDO object.
127       */
128      protected Element generateMetaData()
129      {
130        Element element = generateSourcePackage();
131        element.addContent( generateSource() );
132    
133        return element;
134      }
135    
136      /**
137       * Generate the package ({@link #packageName}) element for the class 
138       * represented by {@link #name}.
139       *
140       * @return Element The element representing the package.
141       */
142      protected Element generateSourcePackage()
143      {
144        Element element = new Element( "package" );
145        element.setAttribute( "name", packageName );
146    
147        return element;
148      }
149    
150      /**
151       * Generate the element for the class represented by {@link #name}.
152       *
153       * @return Element The element representing the class.
154       */
155      protected Element generateSource()
156      {
157        Element element = new Element( "class" );
158        element.setAttribute( "name", name );
159    
160        Element hash = new Element( "field" );
161        hash.setAttribute( "name", "hash" );
162        element.addContent( hash );
163    
164        for ( Map.Entry<String, String> entry : fields.entrySet() )
165        {
166          Element field = new Element( "field" );
167          field.setAttribute( "name", entry.getKey() );
168          element.addContent( field );
169    
170          if ( entry.getValue().contains( "List<ListItem>" ) )
171          {
172            Element child = new Element( "collection" );
173            child.setAttribute( "element-type", "com.sptci.echo2.ListItem" );
174            field.addContent( child );
175          }
176          if ( entry.getValue().contains( "Map<String, Boolean>" ) )
177          {
178            Element child = new Element( "map" );
179            child.setAttribute( "key-type", "java.lang.String" );
180            child.setAttribute( "value-type", "java.lang.Boolean" );
181            field.addContent( child );
182          }
183        }
184    
185        return element;
186      }
187    
188      /**
189       * Generate the meta-data elements and attributes for the
190       * <code>com.sptci.echo2</code> package.
191       *
192       * @see #generateListItem
193       * @return Element The element that represents the meta-data for
194       *   the package.
195       */
196      protected Element generateEchoPackage()
197      {
198        Element element = new Element( "package" );
199        element.setAttribute( "name", "com.sptci.echo2" );
200        element.addContent( generateListItem() );
201    
202        return element;
203      }
204    
205      /**
206       * Write the specified XML document to the specified file.
207       *
208       * @param file The file to write to.
209       * @param document The document to write.
210       * @throws JDOMException If errors are encountered while fetching
211       *   the element.
212       * @throws IOException If errors are encountered while writing to
213       *   the meta-data file.
214       */
215      protected void writeFile( File file, Document document )
216        throws JDOMException, IOException
217      {
218        FileOutputStream out = new FileOutputStream( file );
219        Format format = Format.getPrettyFormat();
220        format.setLineSeparator( System.getProperty( "line.separator" ) );
221        format.setIndent( "  " );
222    
223        XMLOutputter outputter = new XMLOutputter( format ); 
224        outputter.output( document, out );
225        out.flush();
226        out.close();
227      }
228    
229      /**
230       * Add the meta-data for the {@link #name} JDO object to the
231       * existing meta-data file.
232       *
233       * @see #fetchSourcePackage
234       * @see #generateMetaData
235       * @see #fetchSource
236       * @see #generateSource
237       * @see #fetchEchoPackage
238       * @see #fetchListItem
239       * @see #generateEchoPackage
240       * @see #generateListItem
241       * @see #writeFile
242       * @param file The file to update.
243       * @throws JDOMException If errors are encountered while fetching
244       *   the element.
245       * @throws IOException If errors are encountered while writing to
246       *   the meta-data file.
247       */
248      protected void updateFile( File file )
249        throws JDOMException, IOException
250      {
251        SAXBuilder builder = new SAXBuilder();
252        Document document = builder.build( file );
253    
254        Element sourcePackage = fetchSourcePackage( document );
255        if ( sourcePackage == null )
256        {
257          document.getRootElement().addContent( generateMetaData() );
258        }
259        else if ( fetchSource( document ) == null )
260        {
261          sourcePackage.addContent( generateSource() );
262        }
263        
264        Element echo = fetchEchoPackage( document );
265        if ( echo == null )
266        {
267          document.getRootElement().addContent( generateEchoPackage() );
268        }
269        else
270        {
271          if ( fetchListItem( document ) == null )
272          {
273            echo.addContent( generateListItem() );
274          }
275        }
276    
277        writeFile( file, document );
278      }
279    
280      /**
281       * Fetch the element (if it exists) for the package definition for
282       * {@link #packageName}.
283       *
284       * @param document The document from which to fetch the element.
285       * @return Element The appropriate element.
286       * @throws JDOMException If errors are encountered while fetching
287       *   the element.
288       */
289      protected Element fetchSourcePackage( Document document )
290        throws JDOMException
291      {
292        XPath xpath = XPath.newInstance( 
293            "/jdo/package[@name = '" + packageName + "']" );
294        return ( (Element) xpath.selectSingleNode( document ) );
295      }
296    
297      /**
298       * Fetch the element (if it exists) that represents the class
299       * representing {@link #name}.
300       *
301       * @param document The document from which to fetch the element.
302       * @return Element The appropriate element.
303       * @throws JDOMException If errors are encountered while fetching
304       *   the element.
305       */
306      protected Element fetchSource( Document document )
307        throws JDOMException
308      {
309        XPath xpath = XPath.newInstance( 
310            "/jdo/package[@name = '" + packageName + "']/class[@name = '" + name + "']" );
311        return ( (Element) xpath.selectSingleNode( document ) );
312      }
313    
314      /**
315       * Fetch the element (if it exists) for the package definition for
316       * <code>com.sptci.echo2</code>.
317       *
318       * @param document The document from which to fetch the element.
319       * @return Element The appropriate element.
320       * @throws JDOMException If errors are encountered while fetching
321       *   the element.
322       */
323      protected Element fetchEchoPackage( Document document )
324        throws JDOMException
325      {
326        XPath xpath = XPath.newInstance( 
327            "/jdo/package[@name = 'com.sptci.echo2']" );
328        return ( (Element) xpath.selectSingleNode( document ) );
329      }
330    
331      /**
332       * Fetch the element (if it exists) for the class definition for
333       * <code>com.sptci.echo2.ListItem</code>.
334       *
335       * @param document The document from which to fetch the element.
336       * @return Element The appropriate element.
337       * @throws JDOMException If errors are encountered while fetching
338       *   the element.
339       */
340      protected Element fetchListItem( Document document )
341        throws JDOMException
342      {
343        XPath xpath = XPath.newInstance( 
344            "/jdo/package[@name = 'com.sptci.echo2']/class[@name = 'ListItem']" );
345        return ( (Element) xpath.selectSingleNode( document ) );
346      }
347    
348      /**
349       * Generate the meta-data for the {@link ListItem} class.
350       *
351       * @return Element The element that represents the meta-data for
352       *   the class.
353       */
354      protected Element generateListItem()
355      {
356        Element element = new Element( "class" );
357        element.setAttribute( "name", "ListItem" );
358    
359        Element field1 = new Element( "field" );
360        field1.setAttribute( "name", "value" );
361        field1.setAttribute( "null-value", "exception" );
362        element.addContent( field1 );
363    
364        Element field2 = new Element( "field" );
365        field2.setAttribute( "name", "selected" );
366        field2.setAttribute( "null-value", "exception" );
367        element.addContent( field2 );
368    
369        return element;
370      }
371    }