001    package com.sptci.echo2;
002    
003    import java.io.FileNotFoundException;
004    import java.io.IOException;
005    
006    import java.util.Date;
007    import java.util.Formatter;
008    import java.util.List;
009    import java.util.Map;
010    
011    import java.text.SimpleDateFormat;
012    
013    import org.jdom.JDOMException;
014    
015    import org.rakeshv.utils.StringUtilities;
016    
017    /**
018     * A JDO JavaBean generator.  In addition to generating the source file
019     * as implemented in {@link ModelGenerator}, generates an additional
020     * <code>Factory</code> class to implement interactions with the JDO
021     * datastore, and a default JDO metadata file.
022     *
023     * <p>The following shows the invocation semantics for this class:</p>
024     * <pre>
025     *  java -classpath &lt;path to classes&gt;:sptecho.jar:&lt;path to Echo2_App.jar&gt;:&lt;path to jdom&gt;:&lt;path to jaxen&gt; \
026     *    com.sptci.echo2.JDOModelGenerator \
027     *    &lt;fully qualified name of UI class&gt;
028     * </pre>
029     *
030     * <p>Copyright 2006 Sans Pareil Technologies, Inc.</p>
031     * @author Rakesh Vidyadharan 2006-02-08
032     * @version $Id: JDOModelGenerator.java,v 1.3 2006/02/15 17:23:32 rakesh Exp $
033     * @see JDOMetaDataGenerator
034     */
035    public class JDOModelGenerator extends ModelGenerator
036    {
037      /**
038       * Delegates to the super-class constructor.
039       *
040       * @param name The fully qualified name of the class.  Example:
041       *   <code>com.sptci.demo.InputForm</code>.
042       * @throws ClassNotFoundException If the class corresponding to the
043       *   <code>name</code> cannot be found in the classpath.
044       */
045      public JDOModelGenerator( String name ) throws ClassNotFoundException
046      {
047        super( name );
048      }
049    
050      /**
051       * Over-ridden to generate the factory class source and metadata file 
052       * in addition to the JavaBean source.
053       *
054       * @see #addInstanceCallbacks
055       * @see #generateFactory
056       * @see #generateMetaData
057       * @throws Exception If errors are encountered while generating
058       *   the source file.
059       */
060      @Override
061      public void generate() throws Exception
062      {
063        addInstanceCallbacks();
064        generateFactory();
065        generateMetaData();
066      }
067    
068      /**
069       * Add <code>InstanceCallbacks</code> support to the JDO JavaBean.
070       * Write the modified source code back to the source file.
071       *
072       * @see ModelGenerator#generate
073       * @throws Exception If errors are encountered while writing to
074       *   the file.
075       */
076      protected void addInstanceCallbacks() throws Exception
077      {
078        super.generate();
079        StringBuilder builder = new StringBuilder(
080            StringUtilities.fromFile( name + ".java" ) );
081    
082        StringBuilder callback = new StringBuilder( 32 );
083        Formatter formatter = new Formatter( callback );
084        formatter.format( "import javax.jdo.InstanceCallbacks;%n" );
085    
086        int index = builder.indexOf( "import" );
087        builder.insert( index, callback.toString() );
088    
089        String impl = "implements ";
090        index = builder.indexOf( impl ) + impl.length();
091        builder.insert( index, "InstanceCallbacks, " );
092        StringUtilities.toFile( builder.toString(), name + ".java" );
093      }
094    
095      /**
096       * Generate the methods for the JavaBean.
097       *
098       * @see ModelGenerator#generateMethods
099       * @return String The source code for the methods to be included in
100       *  the JavaBean.
101       */
102      @Override
103      protected String generateMethods()
104      {
105        StringBuilder builder = new StringBuilder( super.generateMethods() );
106        Formatter formatter = new Formatter( builder );
107    
108        formatter.format( "%n  /**%n" );
109        formatter.format( "   * Called after the values are loaded from the%n" );
110        formatter.format( "   * data store into this instance.%n" );
111        formatter.format( "   * InstanceCallbacks method implementation.  No actions required%n" );
112        formatter.format( "   * by default.%n" );
113        formatter.format( "   */%n" );
114        formatter.format( "  public void jdoPostLoad() {}%n%n" );
115    
116        formatter.format( "  /**%n" );
117        formatter.format( "   * Called before the values are stored from this instance%n" );
118        formatter.format( "   * to the data store.%n" );
119        formatter.format( "   * InstanceCallbacks method implementation.%n" );
120        formatter.format( "   * Pre-calculate {@link #hash} using {@link #hashCode}.%n" );
121        formatter.format( "   */%n" );
122        formatter.format( "  public void jdoPreStore()%n" );
123        formatter.format( "  {%n" );
124        formatter.format( "    hash = hashCode();%n" );
125        formatter.format( "  }%n%n" );
126    
127        formatter.format( "  /**%n" );
128        formatter.format( "   * Called before the values in the instance are cleared.%n" );
129        formatter.format( "   * InstanceCallbacks method implementation.  No actions required%n" );
130        formatter.format( "   * by default.%n" );
131        formatter.format( "   */%n" );
132        formatter.format( "  public void jdoPreClear() {}%n%n" );
133    
134        formatter.format( "  /**%n" );
135        formatter.format( "   * Called before the instance is deleted.%n" );
136        formatter.format( "   * InstanceCallbacks method implementation.  No actions required%n" );
137        formatter.format( "   * by default.%n" );
138        formatter.format( "   */%n" );
139        formatter.format( "  public void jdoPreDelete() {}%n" );
140    
141        return builder.toString();
142      }
143    
144      /**
145       * Generate a <code>Factory</code> class that handles interactions
146       * between the JDO JavaBean objects and the JDO datastore.
147       *
148       * @see #generateFactoryInitialisers
149       * @see #generateSaveMethod
150       * @see #generateFactoryMethods
151       * @throws IOException If errors are encountered while writing the
152       *   generated source file.
153       */
154      protected void generateFactory() throws IOException
155      {
156        StringBuilder builder = new StringBuilder( 8192 );
157        Formatter formatter = new Formatter( builder );
158        String factoryName = name + "Factory";
159    
160        formatter.format( "package %s;%n%n", source.getPackage().getName() );
161        formatter.format( "import java.io.Serializable;%n" );
162        formatter.format( "import java.util.ArrayList;%n" );
163        formatter.format( "import java.util.Collection;%n" );
164        formatter.format( "import java.util.logging.Logger;%n" );
165    
166        formatter.format( "%n" );
167        formatter.format( "import javax.jdo.JDOHelper;%n" );
168        formatter.format( "import javax.jdo.PersistenceManager;%n" );
169        formatter.format( "import javax.jdo.Query;%n" );
170    
171        formatter.format( "%n" );
172        formatter.format( "import com.sptci.jdo.PersistenceManagerFactory;%n" );
173        formatter.format( "%n" );
174    
175        formatter.format( "/**%n" );
176        formatter.format( " * A factory class for abstracting interactions between the%n" );
177        formatter.format( 
178            " * {@link %s} JDO JavaBean and the JDO data store.%n",
179            name );
180        formatter.format( " *%n" );
181        formatter.format( " * @author %s %s%n", getClass().getName(),
182            ( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ssZ" ) ).format( new Date() ) );
183        formatter.format( " * @version $Id: JDOModelGenerator.java,v 1.3 2006/02/15 17:23:32 rakesh Exp $%n" );
184        formatter.format( " */%n" );
185        formatter.format( "public class %s%n", factoryName );
186        formatter.format( "  implements Serializable%n" );
187        formatter.format( "{%n" );
188    
189        builder.append( generateFactoryInitialisers() );
190        builder.append( generateSaveMethod() );
191        builder.append( generateDeleteMethod() );
192        builder.append( generateFactoryMethods() );
193    
194        formatter.format( "}%n" );
195    
196    
197        StringUtilities.toFile( builder.toString(), factoryName + ".java" );
198      }
199    
200      /**
201       * Generate the initialiser methods for the factory class.
202       *
203       * @return String The source code for the initialisers.
204       */
205      protected String generateFactoryInitialisers()
206      {
207        StringBuilder builder = new StringBuilder( 8192 );
208        Formatter formatter = new Formatter( builder );
209        String factoryName = name + "Factory";
210    
211        formatter.format( "  /**%n" );
212        formatter.format( "   * The singleton instance of the class.%n" );
213        formatter.format( "   */%n" );
214        formatter.format( "  private static final %s singleton =%n",
215            factoryName );
216        formatter.format( "      new %s();%n%n", factoryName );
217    
218        formatter.format( "  /**%n" );
219        formatter.format( "   * The logger used to log errors/warnings to.%n" );
220        formatter.format( "   */%n" );
221        formatter.format( "  private static final Logger logger =%n" );
222        formatter.format( "      Logger.getLogger( \"%s.%s\" );%n%n",
223            source.getPackage().getName(), factoryName );
224    
225        formatter.format( "  /**%n" );
226        formatter.format( "   * Default constructor.  Cannot be instantiated.%n" );
227        formatter.format( "   */%n" );
228        formatter.format( "  protected %s() {}%n%n", factoryName );
229    
230        formatter.format( "  /**%n" );
231        formatter.format( "   * Return the singleton instance of the class.%n" );
232        formatter.format( "   *%n" );
233        formatter.format( "   * @return %s The {@link #singleton} instance.%n",
234            factoryName );
235        formatter.format( "   */%n" );
236        formatter.format( "  public static final %s getInstance()%n",
237            factoryName );
238        formatter.format( "  {%n" );
239        formatter.format( "    return singleton;%n" );
240        formatter.format( "  }%n%n" );
241    
242        return builder.toString();
243      }
244    
245      /**
246       * Generate the <code>save</code> method that will be used to save
247       * a JDO JavaBean to the JDO data store.
248       *
249       * @return String The source code for the save method.
250       */
251      protected String generateSaveMethod()
252      {
253        StringBuilder builder = new StringBuilder( 1024 );
254        Formatter formatter = new Formatter( builder );
255    
256        String fieldName = name.substring( 0, 1 ).toLowerCase() +
257          name.substring( 1 );
258        formatter.format( "  /**%n" );
259        formatter.format( 
260            "   * Save the specified instance of {@link %s}%n",
261            name );
262        formatter.format( "   * to the JDO data store.%n" );
263        formatter.format( "   *%n" );
264        formatter.format( "   * @param %s The instance to persist to%n",
265            fieldName );
266        formatter.format( "   *   the data store.%n" );
267        formatter.format( "   */%n" );
268        formatter.format( "  public void save( %s %s )%n", name, fieldName );
269        formatter.format( "  {%n" );
270        formatter.format( "    PersistenceManager persistenceManager = null;%n" );
271        formatter.format( "    boolean active = false;%n%n" );
272    
273        formatter.format( "    try%n" );
274        formatter.format( "    {%n" );
275        formatter.format( "      persistenceManager =%n" );
276        formatter.format( "        JDOHelper.getPersistenceManager( %s );%n", fieldName );
277        formatter.format( "      if ( persistenceManager == null )%n" );
278        formatter.format( "      {%n" );
279        formatter.format( "        persistenceManager =%n" );
280        formatter.format( "          PersistenceManagerFactory.getPersistenceManager();%n" );
281        formatter.format( "      }%n" );
282        formatter.format( "      active = persistenceManager.currentTransaction().isActive();%n%n" );
283    
284        formatter.format( "      if ( ! active )%n" );
285        formatter.format( "      {%n" );
286        formatter.format( "        persistenceManager.currentTransaction().begin();%n" );
287        formatter.format( "      }%n%n" );
288    
289        formatter.format( "      if ( ! JDOHelper.isPersistent( %s ) )%n",
290            fieldName );
291        formatter.format( "      {%n" );
292        formatter.format( "        persistenceManager.makePersistent( %s );%n", 
293            fieldName );
294        formatter.format( "      }%n%n" );
295    
296        formatter.format( "      if ( ! active )%n" );
297        formatter.format( "      {%n" );
298        formatter.format( "        persistenceManager.currentTransaction().commit();%n" );
299        formatter.format( "      }%n" );
300    
301        formatter.format( "    }%n" );
302        formatter.format( "    finally%n" );
303        formatter.format( "    {%n" );
304        formatter.format( "      if ( ! active && persistenceManager.currentTransaction().isActive() )%n" );
305        formatter.format( "      {%n" );
306        formatter.format( "        persistenceManager.currentTransaction().rollback();%n" );
307        formatter.format( "        logger.severe( \"Unknown problem.  Rollling back transaction.\" );%n" );
308        formatter.format( "      }%n" );
309        formatter.format( "    }%n" );
310    
311        formatter.format( "  }%n%n" );
312    
313        return builder.toString();
314      }
315    
316      /**
317       * Generate the <code>delete</code> method that will be used to delete
318       * a JDO JavaBean to the JDO data store.
319       *
320       * @return String The source code for the save method.
321       */
322      protected String generateDeleteMethod()
323      {
324        StringBuilder builder = new StringBuilder( 1024 );
325        Formatter formatter = new Formatter( builder );
326    
327        String fieldName = name.substring( 0, 1 ).toLowerCase() +
328          name.substring( 1 );
329        formatter.format( "  /**%n" );
330        formatter.format( 
331            "   * Delete the specified instance of {@link %s}%n",
332            name );
333        formatter.format( "   * from the JDO data store.%n" );
334        formatter.format( "   *%n" );
335        formatter.format( "   * @param %s The instance to delete from%n",
336            fieldName );
337        formatter.format( "   *   the data store.%n" );
338        formatter.format( "   */%n" );
339        formatter.format( "  public void delete( %s %s )%n", name, fieldName );
340        formatter.format( "  {%n" );
341        formatter.format( "    PersistenceManager persistenceManager = null;%n" );
342        formatter.format( "    boolean active = false;%n%n" );
343    
344        formatter.format( "    try%n" );
345        formatter.format( "    {%n" );
346        formatter.format( "      persistenceManager =%n" );
347        formatter.format( "        JDOHelper.getPersistenceManager( %s );%n", fieldName );
348        formatter.format( "      active = persistenceManager.currentTransaction().isActive();%n%n" );
349    
350        formatter.format( "      if ( ! active )%n" );
351        formatter.format( "      {%n" );
352        formatter.format( "        persistenceManager.currentTransaction().begin();%n" );
353        formatter.format( "      }%n%n" );
354    
355        formatter.format( "      if ( JDOHelper.isPersistent( %s ) )%n",
356            fieldName );
357        formatter.format( "      {%n" );
358        formatter.format( "        persistenceManager.deletePersistent( %s );%n", 
359            fieldName );
360        formatter.format( "      }%n%n" );
361    
362        formatter.format( "      if ( ! active )%n" );
363        formatter.format( "      {%n" );
364        formatter.format( "        persistenceManager.currentTransaction().commit();%n" );
365        formatter.format( "      }%n" );
366    
367        formatter.format( "    }%n" );
368        formatter.format( "    finally%n" );
369        formatter.format( "    {%n" );
370        formatter.format( "      if ( ! active && persistenceManager.currentTransaction().isActive() )%n" );
371        formatter.format( "      {%n" );
372        formatter.format( "        persistenceManager.currentTransaction().rollback();%n" );
373        formatter.format( "        logger.severe( \"Unknown problem.  Rollling back transaction.\" );%n" );
374        formatter.format( "      }%n" );
375        formatter.format( "    }%n" );
376    
377        formatter.format( "  }%n%n" );
378    
379        return builder.toString();
380      }
381    
382      /**
383       * Generate additional factory methods for fetching the JDO
384       * JavaBean instances from the JDO data store.
385       *
386       * @return String The source code for the factory methods.
387       */
388      protected String generateFactoryMethods()
389      {
390        StringBuilder builder = new StringBuilder( 2046 );
391        Formatter formatter = new Formatter( builder );
392    
393        formatter.format( "  /**%n" );
394        formatter.format( "   * Return all the instance of {@link %s}%n", 
395            name );
396        formatter.format( "   * from the JDO data store.%n" );
397        formatter.format( "   *%n" );
398        formatter.format( "   * @return Collection An <code>ArrayList</code> of all the instances.%n" );
399        formatter.format( "   */%n" );
400        formatter.format( "  @SuppressWarnings(value={\"unchecked\"})%n" );
401        formatter.format( "  public final Collection<%s> fetchAll()%n",
402            name );
403        formatter.format( "  {%n" );
404    
405        formatter.format( "    Collection<%s> collection = new ArrayList<%s>();%n",
406            name, name );
407        formatter.format( "    PersistenceManager persistenceManager =%n" );
408        formatter.format( "      PersistenceManagerFactory.getPersistenceManager();%n" );
409        formatter.format( "    Query query = null;%n" );
410        formatter.format( "    try%n" );
411        formatter.format( "    {%n" );
412        formatter.format( 
413            "      query = persistenceManager.newQuery( %s.class );%n",
414            name );
415        formatter.format( 
416            "      collection.addAll( (Collection<%s>) query.execute() );%n",
417            name );
418        formatter.format( "    }%n" );
419        formatter.format( "    finally%n" );
420        formatter.format( "    {%n" );
421        formatter.format( "      query.closeAll();%n" );
422        formatter.format( "    }%n" );
423        formatter.format( "%n    return collection;%n%n" );
424        formatter.format( "  }%n%n" );
425    
426        return builder.toString();
427      }
428    
429      /**
430       * Generate the JDO meta-data file for the JDO JavaBean.
431       *
432       * <p>Uses JDOM and Jaxen for parsing an existing meta-data file
433       * and for creating or updating the file.</p>
434       *
435       * @see JDOMetaDataGenerator#generate
436       * @throws JDOMException If errors are encountered while fetching
437       *   the element.
438       * @throws IOException If errors are encountered while writing to
439       *   the meta-data file.
440       */
441      protected void generateMetaData()
442        throws JDOMException, IOException
443      {
444        JDOMetaDataGenerator generator = new JDOMetaDataGenerator( name,
445            source.getPackage().getName(), fields );
446        generator.generate();
447      }
448    
449      /**
450       * Main method.
451       */
452      public static void main( String[] args )
453      {
454        if ( args.length != 1 )
455        {
456          System.err.println( 
457              "Usage: java -classpath <classpath> com.sptci.echo2.JDOModelGenerator <UI class>" );
458          System.exit( 2 );
459        }
460        else
461        {
462          try
463          {
464            JDOModelGenerator generator = new JDOModelGenerator( args[0] );
465            generator.generate();
466          }
467          catch ( Throwable t )
468          {
469            t.printStackTrace();
470            System.exit( 1 );
471          }
472        }
473    
474        System.exit( 0 );
475      }
476    }