Project Report: fawkez

Packagesummary org.jcoderz.phoenix.cmpgen2

org.jcoderz.phoenix.cmpgen2.CmpGenerator

LineHitsNoteSource
1  /*
2   * $Id: CmpGenerator.java 1011 2008-06-16 17:57:36Z amandel $
3   *
4   * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5   *
6   * Redistribution and use in source and binary forms, with or without
7   * modification, are permitted provided that the following conditions are
8   * met:
9   *
10   *    * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *    * Redistributions in binary form must reproduce the above
13   *      copyright notice, this list of conditions and the following
14   *      disclaimer in the documentation and/or other materials
15   *      provided with the distribution.
16   *    * Neither the name of the jCoderZ.org Project nor the names of
17   *      its contributors may be used to endorse or promote products
18   *      derived from this software without specific prior written
19   *      permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33   package org.jcoderz.phoenix.cmpgen2;
34  
35  import java.io.File;
36  import java.io.FileInputStream;
37  import java.io.FileWriter;
38  import java.io.IOException;
39  import java.io.StringReader;
40  import java.io.StringWriter;
41  import java.util.Iterator;
42  import java.util.List;
43  import java.util.Properties;
44  import java.util.Set;
45  import java.util.StringTokenizer;
46  import java.util.TreeSet;
47  import java.util.logging.Level;
48  import java.util.logging.Logger;
49  
50  import org.apache.velocity.Template;
51  import org.apache.velocity.VelocityContext;
52  import org.apache.velocity.app.Velocity;
53  import org.apache.velocity.runtime.RuntimeConstants;
54  import org.jcoderz.commons.util.IoUtil;
55  import org.jcoderz.phoenix.sqlparser.ColumnSpec;
56  import org.jcoderz.phoenix.sqlparser.CreateTableStatement;
57  import org.jcoderz.phoenix.sqlparser.SqlParser;
58  import org.jcoderz.phoenix.sqlparser.SqlScanner;
59  import org.jcoderz.phoenix.sqlparser.SqlStatement;
60  
61  
62  /**
63   * This is a velocity based version of the CMP generator.
64   * @author Albrecht Messner
65   */
66  public class CmpGenerator
67  {
68     /** The full qualified name of this class. */
690    private static final String CLASSNAME = CmpGenerator.class.getName();
70     /** The logger to use. */
710    private static final Logger logger = Logger.getLogger(CLASSNAME);
72  
73     private static final String ARRAY_MAGIC = "[]";
740    private static final int ARRAY_MAGIC_LENGTH = ARRAY_MAGIC.length();
75  
76     private final String mOutputBaseDirectory;
77     private final String mPackagePrefix;
78     private final String mDataSource;
79     private File mOutputDirectory;
80     private final String mTemplateDir;
81     private final boolean mOverwrite;
82  
83     /**
84      * Construct CMP bean generator.
85      *
86      * @param outputDir the output directory
87      * @param pkgPrefix the package prefix
88      * @param dataSource the jndi name of the data source
89      */
90     public CmpGenerator (String outputDir, String pkgPrefix, String dataSource,
91 (1)(2)         String templateDirboolean overwrite)
920    {
930       mOutputBaseDirectory = outputDir;
940       mPackagePrefix = pkgPrefix;
950       mDataSource = dataSource;
960       mTemplateDir = templateDir;
970       mOverwrite = overwrite;
980    }
99  
100     private static void usage (String errorText)
101     {
1020       if (errorText != null)
103        {
1040          System.err.println(errorText);
105        }
1060       System.err.println("Usage: CmpGenerator ");
1070       System.err.println(
108              " -i <inputfile> Input SQL file");
1090       System.err.println(" -d <outputdir> Base output directory");
1100       System.err.println(
111              " Package subdirs will be created here");
1120       System.err.println(
113              " -p <package> Package of generated classes");
1140       System.err.println(" Defaults to 'org.jcoderz'");
1150       System.err.println(
116              " -ds <datasource> JNDI lookup name of bean's datasource");
1170       System.err.println(
118              " -t <templatedir> Directory where bean "
119              + "templates can be found");
1200       System.err.println(" -o Overwrite existing files");
1210(3)      System.exit(1);
1220    }
123  
124     /**
125      * Main method.
126      * @param args command line args
127      */
128     public static void main (String[] args)
129 (4)(5)         throws Exception
130     {
1310       String outputDir = ".";
1320       String pkgPrefix = "org.jcoderz";
1330       String dataSource = "jdbc/default";
1340       String inputFile = null;
1350       String templateDir = null;
136  
1370       boolean overwrite = false;
138  
1390       String currentArg = null;
140  
141        try
142        {
1430          for (int i = 0; i < args.length; i++)
144           {
1450             currentArg = args[i];
1460(6)(7)            if (currentArg.equals("-d"))
147              {
1480                outputDir = args[++i];
149              }
1500(8)(9)            else if (currentArg.equals("-i"))
151              {
1520                inputFile = args[++i];
153              }
1540(10)(11)            else if (currentArg.equals("-p"))
155              {
1560                pkgPrefix = args[++i];
157              }
1580(12)(13)            else if (currentArg.equals("-ds"))
159              {
1600                dataSource = args[++i];
161              }
1620(14)(15)            else if (currentArg.equals("-t"))
163              {
1640                templateDir = args[++i];
165              }
1660(16)(17)            else if (currentArg.equals("-o"))
167              {
1680                overwrite = true;
169              }
170              else
171              {
1720                usage("Unknown command line option " + currentArg);
173              }
174           }
175        }
1760       catch (ArrayIndexOutOfBoundsException e)
177        {
1780          usage("Command line option '" + currentArg + "' requires an option");
1790       }
180  
1810       if (inputFile == null || templateDir == null)
182        {
1830          usage("Mandatory parameter missing");
184        }
185  
1860       final CmpGenerator generator =
187           new CmpGenerator(
188                 outputDir, pkgPrefix, dataSource, templateDir, overwrite);
1890       generator.generateCmpBeans(inputFile);
1900    }
191  
192     /**
193      * Entry method for CMP bean generator.
194      *
195      * @param inputFile the input file
196      */
197     public final void generateCmpBeans (String inputFile)
198 (18)(19)         throws Exception
199     {
2000       final String methodName = "generateCmpBeans(String)";
2010       if (logger.isLoggable(Level.FINER))
202        {
2030          logger.entering(CLASSNAME, methodName, new Object[] {inputFile});
204        }
205  
206        try
207        {
2080          setUp();
209  
2100          final FileInputStream fin = new FileInputStream(inputFile);
2110          final SqlScanner sqlScanner = new SqlScanner(fin);
2120          final SqlParser sqlParser = new SqlParser(sqlScanner);
2130          final List sqlStatements = sqlParser.parse();
2140          for (final Iterator it = sqlStatements.iterator(); it.hasNext();)
215           {
2160             final SqlStatement statement = (SqlStatement) it.next();
2170             if (statement instanceof CreateTableStatement)
218              {
219                 // mBeanImportList.clear();
220                 // mHelperImportList.clear();
2210                generateCmpBean((CreateTableStatement) statement);
222              }
223              else
224              {
2250                logger.info("Skipping statement " + statement);
226              }
2270          }
228        }
2290       catch (Exception x)
230        {
2310          logger.log(Level.SEVERE, "Error during CMP generation", x);
2320          throw x;
2330       }
234  
2350       if (logger.isLoggable(Level.FINER))
236        {
2370          logger.exiting(CLASSNAME, methodName);
238        }
2390    }
240  
241 (20)   public String sqlNameToJavaName (String sqlName)
242     {
2430       final StringReader rdr = new StringReader(sqlName);
244        int c;
2450       boolean capitalizeNext = true;
2460       final StringBuffer result = new StringBuffer();
247        try
248        {
2490          while ((c = rdr.read()) != -1)
250           {
2510             final char chr = (char) c;
2520             switch (chr)
253              {
254                 case '_':
2550                   capitalizeNext = true;
2560                   break;
257                 default:
2580                   if (capitalizeNext)
259                    {
2600                      result.append(Character.toUpperCase(chr));
2610                      capitalizeNext = false;
262                    }
263                    else
264                    {
2650                      result.append(chr);
266                    }
267                    break;
268              }
2690          }
270        }
2710       catch (IOException e)
272        {
2730(21)         throw new RuntimeException("Huh???", e);
2740       }
2750       return result.toString();
276     }
277  
278     /**
279      * This method capitalizes the first character of the given string and,
280      * assuming that the argument is usually a java type, also takes care
281      * of arrays by replacing "[]" with "Array".
282      * @param s input to be modified.
283      * @return adapted String.
284      */
285     public String capitalize (String s)
286     {
287        final String str;
2880       if (s.endsWith(ARRAY_MAGIC))
289        {
2900          str = s.substring(0, s.length() - ARRAY_MAGIC_LENGTH) + "Array";
291        }
292        else
293        {
2940          str = s;
295        }
2960       final char c = str.charAt(0);
297        final String result;
2980       if (Character.isUpperCase(c))
299        {
3000           result = str;
301        }
302        else
303        {
3040          result = String.valueOf(Character.toUpperCase(c)) + str.substring(1);
305        }
3060       return result;
307     }
308  
309 (22)   public String getUnqualifiedJavaType (ColumnSpec column)
310           throws CmpGeneratorException
311     {
3120       final String result = TypeMapping.getJavaType(column, false);
3130       if (result == null)
314        {
3150          throw new RuntimeException("TypeMapping returned null for " + column);
316        }
3170       return result;
318     }
319  
320 (23)   public String getQualifiedJavaType (ColumnSpec column)
321           throws CmpGeneratorException
322     {
3230       return TypeMapping.getJavaType(column, true);
324     }
325  
326 (24)   public String unqualifyType (String type)
327     {
3280       return TypeMapping.unqualifyType(type);
329     }
330  
331     private void generateCmpBean (CreateTableStatement stmt)
332 (25)         throws Exception
333     {
3340       if (! hasPrimaryKey(stmt))
335        {
3360          logger.info("*** No PK Field available for "
337                 + stmt.getTableName() + ", skipping bean creation");
3380          return;
339        }
340  
3410       String baseName = stmt.getBeanName() + "Entity";
3420(26)      if (baseName == null)
343        {
3440          baseName = sqlNameToJavaName(stmt.getTableName());
345        }
3460       logger.info("*** Start creation of bean " + baseName);
347  
3480       final String valueInterface = mergeTemplate(
349              getVelocityContext(stmt, baseName), "GenerateValue.vtl");
3500       final File valueIfOutputFile = new File(mOutputDirectory,
351              baseName + "Value.java");
3520       writeFile(valueIfOutputFile, valueInterface);
353  
3540       final String valueImpl = mergeTemplate(
355              getVelocityContext(stmt, baseName), "GenerateValueImpl.vtl");
3560       final File valueImplOutputFile = new File(mOutputDirectory,
357              baseName + "ValueImpl.java");
3580       writeFile(valueImplOutputFile, valueImpl);
359  
3600       final String bean = mergeTemplate(
361              getVelocityContext(stmt, baseName), "GenerateBean.vtl");
3620       final File beanOutputFile = new File(mOutputDirectory,
363              baseName + "Bean.java");
3640       writeFile(beanOutputFile, bean);
365  
3660       if (checkIfHelperRequired(stmt))
367        {
3680          final String helper = mergeTemplate(
369                 getVelocityContext(stmt, baseName), "GenerateHelper.vtl");
3700          final File helperOutputFile = new File(mOutputDirectory,
371                 baseName + "TypeConverter.java");
3720          writeFile(helperOutputFile, helper);
373        }
3740    }
375  
376     private String mergeTemplate (VelocityContext ctx, String templateFile)
377 (27)         throws Exception
378     {
3790(28)      final StringWriter sw = new StringWriter();
3800       final Template template = Velocity.getTemplate(templateFile);
3810       template.merge(ctx, sw);
3820       return sw.getBuffer().toString();
383     }
384  
385     private void writeFile (File outputFile, final String data)
386           throws IOException
387     {
3880       if (outputFile.exists() && !mOverwrite)
389        {
3900          throw new RuntimeException(
391                 "Output file " + outputFile + " already exists");
392        }
3930       final FileWriter fout = new FileWriter(outputFile);
394        try
395        {
3960           fout.write(data);
397        }
398        finally
399        {
4000           IoUtil.close(fout);
4010       }
4020       logger.info("Wrote file " + outputFile + " successfully");
4030    }
404  
405     /**
406      * @param stmt
407    * @param baseName
408    * @return
409      * @throws Exception
410    */
411     private VelocityContext getVelocityContext (CreateTableStatement stmt,
412           String baseName)
413 (29)         throws Exception
414     {
4150       final Properties velocityProps = new Properties();
4160       velocityProps.put(RuntimeConstants.RESOURCE_LOADER, "file");
4170       velocityProps.put("file.resource.loader.class",
418              "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
4190       velocityProps.put(RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
420                mTemplateDir);
4210       velocityProps.put(RuntimeConstants.VM_LIBRARY, "macros.vm");
422        // velocityProps.put("runtime.log", "/tmp/velocity.log");
4230       velocityProps.put(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
424                logger.isLoggable(Level.FINER)
425                    ? "org.apache.velocity.runtime.log.SimpleLog4JLogSystem"
426                    : "org.apache.velocity.runtime.log.NullLogSystem");
4270       Velocity.init(velocityProps);
4280       final VelocityContext ctx = new VelocityContext();
4290       ctx.put("stmt", stmt);
4300       ctx.put("baseName", baseName);
4310       ctx.put("package", mPackagePrefix);
4320       ctx.put("datasource", mDataSource);
4330       ctx.put("cmpgen", this);
4340       return ctx;
435     }
436  
437     /**
438      *
439      * @throws CmpGeneratorException
440    */
441     private void setUp () throws CmpGeneratorException
442     {
4430       File outputDir = new File(mOutputBaseDirectory);
4440       if (!outputDir.exists())
445        {
4460          throw new CmpGeneratorException("Output directory does not exist");
447        }
4480       if (!outputDir.isDirectory())
449        {
4500          throw new CmpGeneratorException(
451              mOutputBaseDirectory + " is not a directory");
452        }
453  
4540       final StringTokenizer tok = new StringTokenizer(mPackagePrefix, ".");
455  
4560       while (tok.hasMoreTokens())
457        {
4580          outputDir = new File(outputDir, tok.nextToken());
459        }
4600       logger.finer("Generating package subdirectories for " + outputDir);
4610(30)      outputDir.mkdirs();
4620       mOutputDirectory = outputDir;
4630    }
464  
465  
466     private boolean hasPrimaryKey (CreateTableStatement statement)
467     {
4680       boolean hasPrimaryKey = false;
4690       for (final Iterator it = statement.getColumns().iterator();
4700           it.hasNext(); )
471        {
4720          final ColumnSpec column = (ColumnSpec) it.next();
4730          if (column.isPrimaryKey())
474           {
4750             hasPrimaryKey = true;
4760             break;
477           }
4780       }
4790       return hasPrimaryKey;
480     }
481  
482     /**
483      * Checks if a helper class is required for this bean
484      *
485      * a helper class is required as soon as a custom java type is used
486      * with a load method and a store method.
487      *
488      * @param statement the parsed SQL create statement
489      * @return true if a helper class is required, false otherwise
490      */
491     public boolean checkIfHelperRequired (CreateTableStatement statement)
492     {
4930       boolean needHelper = false;
494  
4950       for (final Iterator it = statement.getColumns().iterator();
4960           it.hasNext(); )
497        {
4980          final ColumnSpec column = (ColumnSpec) it.next();
4990          if (column.getJavaType() != null)
500           {
5010             if (column.getStoreMethod() != null)
502              {
5030                needHelper = true;
5040                break;
505              }
506           }
5070       }
5080       return needHelper;
509     }
510  
511 (31)   public String getVersion ()
512     {
5130       return "$Revision: 1.9 $";
514     }
515  
516 (32)   public Set buildBeanImportList (CreateTableStatement statement)
517           throws CmpGeneratorException
518     {
5190       final Set beanImportList = new TreeSet();
5200       for (final Iterator it = statement.getColumns().iterator();
5210           it.hasNext(); )
522        {
5230          final ColumnSpec column = (ColumnSpec) it.next();
5240          final String javaType = TypeMapping.getJavaType(column, true);
5250          if (javaType != null
526                 && !javaType.startsWith("java.lang")
527                 && !TypeMapping.isPrimitiveType(javaType))
528           {
5290             beanImportList.add(javaType);
530           }
5310       }
532  
5330       beanImportList.add("org.jcoderz.commons.types.Period");
5340       beanImportList.add("javax.ejb.CreateException");
5350       return beanImportList;
536     }
537  
538 (33)   public Set buildHelperImportList (CreateTableStatement statement)
539           throws CmpGeneratorException
540     {
5410       final Set helperImportList = new TreeSet();
5420       for (final Iterator it = statement.getColumns().iterator();
5430           it.hasNext(); )
544        {
5450          final ColumnSpec column = (ColumnSpec) it.next();
5460          final String javaType = TypeMapping.getJavaType(column, true);
5470(34)         if (javaType != null
548                 && !javaType.equals("java.sql.Date") // don't import date since
549                                                      // refs to date are always
550                                                      // fully qualified.
551                 && !javaType.startsWith("java.lang")
552                 && !TypeMapping.isPrimitiveType(javaType))
553           {
5540             helperImportList.add(javaType);
555           }
5560          final String complexType = column.getJavaType();
5570          if (complexType != null
558                 && !complexType.startsWith("java.lang")
559                 && !TypeMapping.isPrimitiveType(complexType))
560           {
5610             helperImportList.add(complexType);
562           }
5630       }
564  
565        // extra imports:
5660       helperImportList.add("org.jcoderz.commons.InconsistentDatabaseException");
5670       helperImportList.add("org.jcoderz.commons.types.Date");
5680       helperImportList.add("org.jcoderz.commons.types.Period");
5690       helperImportList.add("org.jcoderz.commons.util.Assert");
570  
5710       return helperImportList;
572     }
573  
574     /** {@inheritDoc} */
575     public String toString ()
576     {
5770       return "[Phoenix CmpGenerator " + getVersion() + "]";
578     }
579  
580 (35)   public boolean isPrimitiveType (String type)
581     {
5820       return TypeMapping.isPrimitiveType(type);
583     }
584  }

Findings in this File

f (36) System.out.print is used main class
f (37) System.out.print is used main class
f (38) System.out.print is used main class
f (39) System.out.print is used main class
f (40) System.out.print is used main class
f (41) System.out.print is used main class
f (42) System.out.print is used main class
f (43) System.out.print is used main class
f (44) System.out.print is used main class
f (45) System.out.print is used main class
c (1) 91 : 17 Expected @param tag for 'templateDir'.
c (2) 91 : 38 Expected @param tag for 'overwrite'.
i (3) 121 : 0 org.jcoderz.phoenix.cmpgen2.CmpGenerator.usage(String) invokes System.exit(...), which shuts down the entire virtual machine
c (4) 129 : 17 Expected @throws tag for 'Exception'.
d (5) 129 : 17 A method/constructor shouldn't explicitly throw java.lang.Exception
i (6) 146 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.main(String[]) makes literal string comparisons passing the literal as an argument
c (7) 146 : 17 Position literals first in String comparisons
i (8) 150 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.main(String[]) makes literal string comparisons passing the literal as an argument
c (9) 150 : 22 Position literals first in String comparisons
i (10) 154 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.main(String[]) makes literal string comparisons passing the literal as an argument
c (11) 154 : 22 Position literals first in String comparisons
i (12) 158 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.main(String[]) makes literal string comparisons passing the literal as an argument
c (13) 158 : 22 Position literals first in String comparisons
i (14) 162 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.main(String[]) makes literal string comparisons passing the literal as an argument
c (15) 162 : 22 Position literals first in String comparisons
i (16) 166 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.main(String[]) makes literal string comparisons passing the literal as an argument
c (17) 166 : 22 Position literals first in String comparisons
c (18) 198 : 17 Expected @throws tag for 'Exception'.
d (19) 198 : 17 A method/constructor shouldn't explicitly throw java.lang.Exception
c (20) 241 : 4 Missing a Javadoc comment.
i (21) 273 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.sqlNameToJavaName(String) throws exception with static message string
c (22) 309 : 4 Missing a Javadoc comment.
c (23) 320 : 4 Missing a Javadoc comment.
c (24) 326 : 4 Missing a Javadoc comment.
d (25) 332 : 17 A method/constructor shouldn't explicitly throw java.lang.Exception
i (26) 342 : 0 Redundant nullcheck of baseName, which is known to be non-null in org.jcoderz.phoenix.cmpgen2.CmpGenerator.generateCmpBean(CreateTableStatement)
d (27) 377 : 17 A method/constructor shouldn't explicitly throw java.lang.Exception
w (28) 379 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.mergeTemplate(VelocityContext, String) needlessly defines parameter with concrete classes
d (29) 413 : 17 A method/constructor shouldn't explicitly throw java.lang.Exception
w (30) 461 : 0 org.jcoderz.phoenix.cmpgen2.CmpGenerator.setUp() ignores exceptional return value of java.io.File.mkdirs()
c (31) 511 : 4 Missing a Javadoc comment.
c (32) 516 : 4 Missing a Javadoc comment.
c (33) 538 : 4 Missing a Javadoc comment.
i (34) 547 : 0 method org.jcoderz.phoenix.cmpgen2.CmpGenerator.buildHelperImportList(CreateTableStatement) makes literal string comparisons passing the literal as an argument
c (35) 580 : 4 Missing a Javadoc comment.