View Javadoc
1   /*
2    * $Source$
3    * $Revision$
4    *
5    * Copyright (C) 2000 William Chesters
6    *
7    * Part of Melati (http://melati.org), a framework for the rapid
8    * development of clean, maintainable web applications.
9    *
10   * Melati is free software; Permission is granted to copy, distribute
11   * and/or modify this software under the terms either:
12   *
13   * a) the GNU General Public License as published by the Free Software
14   *    Foundation; either version 2 of the License, or (at your option)
15   *    any later version,
16   *
17   *    or
18   *
19   * b) any version of the Melati Software License, as published
20   *    at http://melati.org
21   *
22   * You should have received a copy of the GNU General Public License and
23   * the Melati Software License along with this program;
24   * if not, write to the Free Software Foundation, Inc.,
25   * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26   * GNU General Public License and visit http://melati.org to obtain the
27   * Melati Software License.
28   *
29   * Feel free to contact the Developers of Melati (http://melati.org),
30   * if you would like to work out a different arrangement than the options
31   * outlined here.  It is our intention to allow Melati to be used by as
32   * wide an audience as possible.
33   *
34   * This program is distributed in the hope that it will be useful,
35   * but WITHOUT ANY WARRANTY; without even the implied warranty of
36   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
37   * GNU General Public License for more details.
38   *
39   * Contact details for copyright holder:
40   *
41   *     William Chesters <williamc At paneris.org>
42   *     http://paneris.org/~williamc
43   *     Obrechtstraat 114, 2517VX Den Haag, The Netherlands
44   */
45  
46  package org.melati.poem.prepro;
47  
48  import java.util.Enumeration;
49  import java.util.Vector;
50  import java.io.FileNotFoundException;
51  import java.io.InputStreamReader;
52  import java.io.Writer;
53  import java.io.File;
54  import java.io.FileWriter;
55  import java.io.FileReader;
56  import java.io.BufferedWriter;
57  import java.io.Reader;
58  import java.io.BufferedReader;
59  import java.io.StreamTokenizer;
60  import java.io.IOException;
61  
62  /**
63   * The <code>DSD</code> class is an application which parses a DSD
64   * (Data Structure Definition) file and outputs Java code, creating
65   * a POEM view of a database.
66   * <p>
67   * The generated files are placed in the current directory, which is
68   * assumed to be similar to <code>org/paneris/myapp/model/</code> and
69   * also in a new directory below it called <code>generated</code>.
70   * 
71   * If a file with the same name as a file that is about to be generated already exists, 
72   * then the file is checked for the presence of a magic string. If the string is present 
73   * then the file is replaced.
74   *
75   * @author  William Chesters
76   */
77  
78  public class DSD {
79  
80    static final String NAG_BLOCK = 
81        "  // programmer's domain-specific code here\n" +
82        "  // Don't forget to delete first line to prevent overwriting\n";
83  
84    static final String autogenStamp =
85      "// Do not edit this file!  " +
86      "It was generated by Melati POEM's DSD preprocessor.";
87    
88    static final String deleteMe = "// Delete this line to prevent overwriting of this file";
89  
90    private final Vector<String> packageComponents = new Vector<String>();
91    final String packageName;
92    private final File dsdFile, dsdDir, dsdDirGen;
93    private final String name;
94    final String databaseClassName, databaseBaseClassName;
95    final String databaseTablesClassName, databaseTablesBaseClassName;
96    /** The project name*/
97    final String projectName;
98    TableNamingStore tableNamingStore;
99  
100   /* All tables defined in this DSD */
101   final Vector<TableDef> tablesInPackage = new Vector<TableDef>();
102 
103   /* All tables defined in this, and any imported, DSDs including Poem.dsd */
104   final Vector<TableDef> tablesInDatabase = new Vector<TableDef>();
105 
106   /* A list of imported DSDs */
107   final Vector<DSD> importedDSDs = new Vector<DSD>();
108 
109   public boolean hasAnExtenedTable;
110 
111   static void expect(StreamTokenizer tokens, String what)
112                                       throws ParsingDSDException {
113     if (tokens.ttype != StreamTokenizer.TT_WORD || !tokens.sval.equals(what))
114       throw new ParsingDSDException(what, tokens);
115   }
116 
117   static void expect(StreamTokenizer tokens, char what)
118                                       throws ParsingDSDException {
119     if (tokens.ttype != what)
120       throw new ParsingDSDException("" + what, tokens);
121   }
122 
123  /**
124   * Constructor.
125   *
126   * @param file  the name of the DSD file to open
127   * @throws ResourceNotFoundException
128   *    if a DSD file cannot be found
129   * @throws ParsingDSDException
130   *    if an unexpected token is encountered
131   * @throws IllegalityException
132   *    if a semantic incoherence is detected
133   * @throws IOException
134   *    if a problem with the file system is encountered
135   */
136   public DSD(String file) throws IOException, ParsingDSDException,
137                             IllegalityException, ResourceNotFoundException {
138     this(file, new TableNamingStore(), true);
139   }
140 
141  /**
142   * Constructor.
143   *
144   * @param file  the name of the DSD file to open
145   * @param names a {@link TableNamingStore} containing names encountered so far
146   * @param includePoem whether to include the Poem tables
147   * @throws ResourceNotFoundException
148   *    if a DSD file cannot be found
149   * @throws ParsingDSDException
150   *    if an unexpected token is encountered
151   * @throws IllegalityException
152   *    if a semantic incoherence is detected
153   * @throws IOException
154   *    if a problem with the file system is encountered
155   */
156   public DSD(String file, TableNamingStore names, boolean includePoem)
157       throws ResourceNotFoundException, ParsingDSDException,
158              IllegalityException, IOException {
159     tableNamingStore = names;
160     dsdFile = new File(file);
161     String dsdFileName = dsdFile.getName();
162     int dot = dsdFileName.lastIndexOf('.');
163     name = dot == -1 ? dsdFileName : dsdFileName.substring(0, dot);
164 
165     projectName = StringUtils.capitalised(name);
166     databaseClassName = projectName + "Database";
167     databaseBaseClassName = projectName + "DatabaseBase";
168     databaseTablesClassName = projectName + "DatabaseTables";
169     databaseTablesBaseClassName = projectName + "DatabaseTablesBase";
170     dsdDir = new File(new File(dsdFile.getAbsolutePath()).getParent());
171     dsdDirGen = new File(
172                   dsdDir.getAbsolutePath() + File.separator + "generated");
173 
174     /* Read in the default Poem tables, if appropriate */
175     if (includePoem && !"Poem".equals(projectName)) {
176       DSD poemDSD = new DSD(filePath("org.melati.poem.Poem.dsd"),
177                             tableNamingStore, false);
178       Vector <TableDef>poemTables = poemDSD.tablesInPackage;
179       for(int i = 0; i < poemTables.size(); i++)
180         tablesInDatabase.addElement(poemTables.elementAt(i));
181     }
182     Reader reader = null;
183     try {
184       reader = new BufferedReader(new FileReader(file));
185     } catch (FileNotFoundException e) {
186       if (file.indexOf("!") != -1) {
187         String resourceName = file.substring(file.indexOf("!") + 2);
188         reader = new BufferedReader(
189                    new InputStreamReader(Thread.currentThread()
190                         .getContextClassLoader()
191                           .getResourceAsStream(resourceName)));
192       } else
193         throw e;
194     }
195     try {
196       StreamTokenizer tokens = new StreamTokenizer(reader);
197       tokens.slashSlashComments(true);
198       tokens.slashStarComments(true);
199       tokens.wordChars('_', '_');
200 
201       tokens.nextToken();
202       expect(tokens, "package");
203 
204       StringBuffer packageBuffer = new StringBuffer();
205       for (;;) {
206         if (tokens.nextToken() != StreamTokenizer.TT_WORD)
207           throw new ParsingDSDException("<package component>", tokens);
208         packageComponents.addElement(tokens.sval);
209         packageBuffer.append(tokens.sval);
210         if (tokens.nextToken() != '.') break;
211         packageBuffer.append('.');
212       }
213       packageName = packageBuffer.toString();
214 
215       expect(tokens, ';');
216       tokens.nextToken();
217 
218       /* Read in imported databases */
219       while (tokens.ttype != StreamTokenizer.TT_EOF) {
220 
221         if (!tokens.sval.equals("import"))
222           break;
223 
224         if (tokens.nextToken() != StreamTokenizer.TT_WORD)
225           throw new ParsingDSDException("<import component>", tokens);
226 
227         String importDSD = tokens.sval;
228         tokens.nextToken();
229         expect(tokens, ';');
230         tokens.nextToken();
231 
232         DSD dsd = new DSD(filePath(importDSD), tableNamingStore, false);
233         importedDSDs.addElement(dsd);
234 
235         Vector<TableDef> packageTables = dsd.tablesInPackage;
236         for(int i = 0; i < packageTables.size(); i++)
237           tablesInDatabase.addElement(packageTables.elementAt(i));
238       }
239 
240       /* Read in tables */
241       for (int t = 0; tokens.ttype != StreamTokenizer.TT_EOF; ++t) {
242         boolean isAbstract;
243 
244         if (tokens.ttype != StreamTokenizer.TT_WORD)
245           throw new ParsingDSDException("table", tokens);
246 
247         if (tokens.sval.equals("abstract")) {
248           isAbstract = true;
249           tokens.nextToken();
250         } else
251           isAbstract = false;
252 
253         expect(tokens, "table");
254 
255         tokens.nextToken();
256         TableDef table = new TableDef(this, tokens, t, isAbstract, tableNamingStore);
257         tablesInPackage.addElement(table);
258         tablesInDatabase.addElement(table);
259       }
260     } finally {
261       reader.close();
262     }
263 
264   }
265 
266   void createJava(String nameP, Generator proc, boolean overwrite)
267       throws IOException {
268     if (!dsdDirGen.exists()) {
269       dsdDirGen.mkdir();
270     }
271     File f = null;
272     // FIXME flag tied to directory
273     if (overwrite) {
274       f = new File(dsdDirGen, nameP + ".java");
275     } else {
276       f = new File(dsdDir, nameP + ".java");
277     }
278     if (f.exists())
279       if (overwrite) { // FIXME Nasty - overwrite here meaning generating base package
280         if(containsText(f, autogenStamp))
281           System.err.println("Replacing " + f);
282         else
283           throw new TargetExistsDSDException(f);
284       } else {
285         if (containsText(f, deleteMe))
286           System.err.println("Replacing unmodified " + f);
287         else {
288           System.err.println("Leaving existing " + f);
289           return;
290         }
291       }
292     else
293       System.err.println("Creating " + f);
294 
295     Writer w = new BufferedWriter(new FileWriter(f));
296     try {
297       if (overwrite) { // FIXME Nasty - overwrite here meaning generating base package
298         w.write(autogenStamp + "\n" + "\n");
299         w.write("package " + packageName + ".generated;\n" );
300       } else {
301         w.write(deleteMe + "\n" + "\n");
302         w.write("package " + packageName + ";\n" );
303       }
304       w.write("\n\n");
305       proc.process(w);
306     } catch (IOException e) {
307       try {
308         w.close();
309       } catch (Exception ee) {
310         // If we fail here the cause is reported below
311         ee = null; // shut PMD up
312       }
313       try {
314         f.delete();
315       } catch (Exception ee) {
316         // If we fail here the cause is reported below
317         ee = null; // shut PMD up
318       }
319       throw e;
320     }
321     w.write("\n");
322     w.close();
323   }
324 
325   private boolean containsText(File file, String text) throws FileNotFoundException, IOException, TargetExistsDSDException {
326     BufferedReader r = new BufferedReader(new FileReader(file));
327     boolean found = true;
328     try {
329       String firstLine = r.readLine();
330       if (firstLine != null && !firstLine.equals(text))
331         found = false;
332     } finally {
333       r.close();
334     }
335     return found;
336   }
337 
338   void createPackageHTML(Generator proc, boolean overwrite)
339       throws IOException {
340     File f = null;
341     if (overwrite) {
342       f = new File(dsdDirGen, "package.html");
343     } else {
344       f = new File(dsdDir, "package.html");
345     }
346     if (f.exists()) {
347       if (overwrite) {
348         BufferedReader r = new BufferedReader(new FileReader(f));
349         try {
350           for(int i = 0; i < 8; i++) {r.readLine(); }
351           String ninthLine = r.readLine();
352           if (ninthLine == null || ninthLine.indexOf(autogenStamp) != -1)
353             System.err.println("Replacing " + f);
354           else {
355             System.err.println(ninthLine);
356             throw new TargetExistsDSDException(f);
357           }
358         } finally {
359           r.close();
360         }
361       } else {
362         System.err.println("Leaving existing " + f);
363         return;
364       }
365     } else
366       System.err.println("Creating " + f);
367 
368     Writer w = new BufferedWriter(new FileWriter(f));
369     try {
370       w.write("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n" +
371               "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n" +
372               "   \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n" +
373               "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
374               "<head>\n" +
375               " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=us-ascii\" />\n" +
376               " <title>" + packageName);
377       if (overwrite)
378         w.write(".generated");
379       w.write("</title>\n" +
380               "</head>\n" +
381               "<!-- " + autogenStamp + "-->\n" +
382               "<body>\n");
383       proc.process(w);
384 
385       w.write("</body>\n" +
386               "</html>\n" +
387               "\n");
388     } catch (IOException e) {
389       try {
390         w.close();
391       } catch (Exception ee) {
392         // If we fail here the cause is reported below
393         ee = null; // shut PMD up
394       }
395       try {
396         f.delete();
397       } catch (Exception ee) {
398         // If we fail here the cause is reported below
399         ee = null; // shut PMD up
400       }
401       throw e;
402     }
403     w.write("\n");
404     w.close();
405   }
406 
407 
408   void generateDatabaseBaseJava(Writer w) throws IOException {
409     if (packageName.equals("org.melati.poem")) {
410       w.write("import org.melati.poem.Database;\n");
411     } else {
412       w.write("import org.melati.poem.PoemDatabase;\n");
413     }
414     w.write("import org.melati.poem.DefinitionSource;\n");
415 
416     for (Enumeration<TableDef> t = tablesInDatabase.elements(); t.hasMoreElements();) {
417       TableDef td = t.nextElement();
418       if (!(td.tableNamingInfo.hidden || td.isAbstract)) {
419         w.write(td.tableNamingInfo.importPersistentString());
420         w.write(td.tableNamingInfo.importTableString());
421       }
422     }
423 
424     w.write("\n" +
425             "/**\n" +
426             " * Melati POEM generated Database base class.\n" +
427             " */\n");
428     w.write("public class " + databaseBaseClassName + " extends " +
429             (packageName.equals("org.melati.poem") &&
430                  name.equalsIgnoreCase("Poem") ?
431                "Database" : "PoemDatabase") +" {\n\n");
432 
433     for (Enumeration<TableDef> t = tablesInDatabase.elements(); t.hasMoreElements();) {
434       TableDef td = t.nextElement();
435       if (!td.tableNamingInfo.hidden)
436         td.generateTableDeclarationJava(w);
437     }
438     
439     w.write("\n"); 
440     if(hasAnExtenedTable)
441       w.write("  @SuppressWarnings({ \"unchecked\", \"rawtypes\" })\n");
442     w.write("  protected " + databaseBaseClassName + "() {\n");
443 
444     for (Enumeration<TableDef> t = tablesInDatabase.elements(); t.hasMoreElements();) {
445       TableDef td = t.nextElement();
446       if (!td.tableNamingInfo.hidden)
447         td.generateTableDefinitionJava(w);
448     }
449 
450     w.write("  }\n");
451 
452     for (Enumeration<TableDef> t = tablesInDatabase.elements(); t.hasMoreElements();) {
453       TableDef td = t.nextElement();
454       if (!td.tableNamingInfo.hidden) {
455         w.write('\n');
456         td.generateTableAccessorJava(w);
457       }
458     }
459     w.write("}\n\n");
460   }
461 
462   void generateDatabaseJava(Writer w) throws IOException {
463     w.write("import " + packageName + ".generated." +
464               databaseBaseClassName + ";\n");
465     w.write("\n" +
466             "/**\n" +
467             " * Melati POEM generated, programmer modifiable stub.\n" +
468             " */\n");
469     w.write("public class " + databaseClassName +
470             " extends " + databaseBaseClassName +
471             "\n                            implements " + databaseTablesClassName);
472     w.write(" {\n");
473     w.write(NAG_BLOCK);
474     w.write("}\n\n");
475   }
476 
477   void generateDatabaseTablesBaseJava(Writer w) throws IOException {
478     w.write("// " + tablesInDatabase.size() + " tables in database\n");
479     for (Enumeration<TableDef> t = tablesInDatabase.elements(); t.hasMoreElements();) {
480       TableDef td = t.nextElement();
481       if (td.isAbstract) w.write("// abstract ");
482       if (td.tableNamingInfo.hidden) w.write ("// hidden ");
483       w.write(td.tableNamingInfo.importTableString());
484       if (td.isAbstract) w.write("// abstract ");
485       w.write(td.tableNamingInfo.importPersistentString());
486     }
487     for (int j = 0; j < importedDSDs.size(); j++) {
488       DSD dsd = importedDSDs.elementAt(j);
489       w.write("import " + dsd.packageName + "."+
490               dsd.databaseTablesClassName + ";\n");
491     }
492 
493     w.write("\n" +
494             "/**\n" +
495             " * Melati POEM generated base interface to the tables in \n" +
496             " * " + packageName + ".\n" +
497             " */\n");
498     w.write("public interface " + databaseTablesBaseClassName);
499     boolean first = true;
500     for (Enumeration<DSD> t = importedDSDs.elements(); t.hasMoreElements();) {
501       DSD dsd = t.nextElement();
502       if (first) {
503         w.write("\n                       extends " + dsd.databaseTablesClassName);
504         first = false;
505       }
506       else {
507         w.write(",\n                               " +
508                 dsd.databaseTablesClassName);
509       }
510     }
511     w.write(" {\n\n");
512     for (Enumeration<TableDef> t = tablesInDatabase.elements(); t.hasMoreElements();) {
513       TableDef td = t.nextElement();
514       if (!td.tableNamingInfo.hidden)
515         td.generateTableAccessorDefnJava(w);
516     }
517     w.write("}\n\n");
518   }
519 
520   void generateDatabaseTablesJava(Writer w) throws IOException {
521     w.write("import " + packageName + ".generated." +
522               databaseTablesBaseClassName + ";\n");
523     w.write("\n" +
524             "/**\n" +
525             " * Melati POEM generated, " +
526             "programmer modifiable interface stub.\n" +
527             " */\n");
528     w.write("public interface " + databaseTablesClassName +
529             " extends " + databaseTablesBaseClassName + " {\n" );
530     w.write(NAG_BLOCK);
531     w.write("}\n\n");
532   }
533 
534   /**
535    * Generate the project table from which all project tables inherit. 
536    * 
537    * @param w the project table writer
538    */
539   void generateProjectTableJava(Writer w) throws IOException {
540     w.write("import org.melati.poem.JdbcTable;\n");
541     w.write("import org.melati.poem.DefinitionSource;\n");
542     w.write("import org.melati.poem.Database;\n");
543     w.write("import org.melati.poem.Persistent;\n");
544     w.write("import org.melati.poem.PoemException;\n");
545 
546     w.write("\n" +
547             "/**\n" +
548             " * Melati POEM generated, " +
549             "programmer modifiable inheritance hook.\n" +
550             " */\n");
551     w.write("public class " + getProjectTableClassName() +
552             "<P extends Persistent> extends JdbcTable<P> {\n");
553     
554     w.write("\n /**\n" + "  * Constructor. \n" + "  * \n" 
555             + "  * See " + "org.melati.poem.prepro.DSD" + "#generateProjectTableJava \n"
556             + "  * @param database          the POEM database we are using\n"
557             + "  * @param name              the name of this <code>Table</code>\n"
558             + "  * @param definitionSource  which definition is being used\n"
559             + "  * @throws PoemException    if anything goes wrong\n" + "  */\n");
560 
561     w.write("\n" + "  public " + getProjectTableClassName() + "(\n"
562             + "      Database database, String name,\n"
563             + "      DefinitionSource definitionSource)"
564             + " throws PoemException {\n"
565             + "    super(database, name, definitionSource);\n" + "  }\n" + "\n");
566 
567     //w.write("\n /**\n" + "  * Constructor.\n" + "  *\n" 
568     //        + "  * See " + "org.melati.poem.prepro.DSD" + "#generateProjectTableJava \n"
569     //        + "  * @param database          the POEM database we are using\n"
570     //        + "  * @param name              the name of this <code>Table</code>\n"
571     //        + "  * @throws PoemException    if anything goes wrong\n" + "  */\n");
572     //w.write("  public " + getProjectTableClassName() + "(\n"
573     //        + "      Database database, String name)" + " throws PoemException {\n"
574     //        + "    this(database, name, DefinitionSource.dsd);\n" + "  }\n" + "\n");
575 
576     w.write(NAG_BLOCK);
577     w.write("}\n\n");
578   }
579   
580   /**
581    * Generate the java files.
582    */
583   void generateJava() throws IOException, IllegalityException {
584     final DSD this_ = this;
585 
586     createJava(databaseBaseClassName,
587                new Generator() {
588                  public void process(Writer w) throws IOException {
589                    this_.generateDatabaseBaseJava(w);
590                  }
591                },
592                true);
593 
594     createJava(databaseClassName,
595                new Generator() {
596                  public void process(Writer w) throws IOException {
597                    this_.generateDatabaseJava(w);
598                  }
599                },
600                false);
601 
602     createJava(databaseTablesBaseClassName,
603                new Generator() {
604                  public void process(Writer w) throws IOException {
605                    this_.generateDatabaseTablesBaseJava(w);
606                  }
607                },
608                true);
609 
610     createJava(databaseTablesClassName,
611             new Generator() {
612               public void process(Writer w) throws IOException {
613                 this_.generateDatabaseTablesJava(w);
614               }
615             },
616             false);
617 
618     createJava(getProjectTableClassName(),
619             new Generator() {
620               public void process(Writer w) throws IOException {
621                 this_.generateProjectTableJava(w);
622               }
623             },
624             false);
625     
626     // Create a default package.html if it does not exist
627     createPackageHTML(new Generator() {
628       public void process(Writer w) throws IOException {
629         w.write("<p>The POEM-generated model classes for " +
630         packageName + ".</p>\n");
631       }
632     }, false);
633 
634     // Create a package.html for the generated files
635     createPackageHTML(new Generator() {
636       public void process(Writer w) throws IOException {
637         w.write("<p>The POEM-generated support classes for " +
638         packageName + ".</p>\n");
639       }
640     }, true);
641 
642     for (Enumeration<TableDef> t = tablesInPackage.elements(); t.hasMoreElements();)
643       t.nextElement().generateJava();
644   }
645 
646   /**
647    * This returns the path to a file (which we assume is a DSD) by
648    * finding the Database class it generated and which the user has
649    * compiled.
650    */
651   String filePath(String resource) throws ResourceNotFoundException {
652     int ext = resource.lastIndexOf('.');
653     if (ext == -1)
654       throw new ResourceNotFoundException(resource,
655         "I can't find the type of this resource (i.e. the file's extension)");
656     int file = resource.lastIndexOf('.', ext - 1);
657     if (file == -1)
658       throw new ResourceNotFoundException(resource,
659                   "I can't find a package name for this resource");
660     String packageNameLocal = resource.substring(0, file);
661     String fileName = resource.substring(file + 1, ext);
662     String extension = resource.substring(ext + 1);
663     String fileToLookFor = fileName + "." + extension;
664     String databaseName = StringUtils.capitalised(fileName.toLowerCase()) +
665                             "DatabaseTables";
666     Class<?> database;
667     try {
668       database = Class.forName(packageNameLocal + "." + databaseName);
669     } catch (Exception e) {
670       throw new ResourceNotFoundException(resource,
671                   "I can't find the database class associated with this "+
672                   "resource (" + packageNameLocal + "." + databaseName + "). " +
673                   "Is it in your classpath?", e);
674     }
675     java.net.URL url = database.getResource(fileToLookFor);
676     if (url == null || url.getFile() == null || url.getFile().equals(""))
677       throw new ResourceNotFoundException(resource,
678                   "I can't find the resource from the database class file. "+
679                   "Is " + fileToLookFor +" in your classpath?");
680     return url.getFile();
681   }
682 
683 
684   static String javadocFormat(String indent2,
685                                      String string) {
686       return javadocFormat(2,Integer.parseInt(indent2), string);
687   }
688   static String javadocFormat(String string) {
689       return javadocFormat(2, 1, string);
690   }
691   static String javadocFormat(String indent1, String indent2,
692                                      String string) {
693       return javadocFormat(Integer.parseInt(indent1),Integer.parseInt(indent2),
694                            string);
695   }
696  /**
697   * Format a string to fit an indented javadoc comment.
698   *
699   * @param indent1 the indentation before the asterisk
700   * @param indent2 the indentation after  the asterisk
701   * @param string  the <code>String</code> to format
702   * @return the formatted string
703   */
704   static String javadocFormat(int indent1, int indent2, String string) {
705     int lineWidth = 77;
706     int index = indent1;
707     StringBuffer b = new StringBuffer();
708     for (int i = 0; i < indent1; i++) b.append(" ");
709     b.append("*");
710     index += 1;
711     for (int i = 0; i < indent2; i++)b.append(" ");
712     index += indent2;
713     int available = lineWidth - index;
714     if (string.length() <= available) {
715       b.append(string);
716       b.append(" \n");
717     } else {
718       int prevSpace = string.lastIndexOf(' ',available);
719       int incision = available;
720       if (prevSpace != -1)
721         incision = prevSpace + 1;
722       b.append(string.substring(0, incision));
723       b.append("\n");
724       b.append(javadocFormat(indent1, indent2, string.substring(incision)));
725     }
726     return b.toString();
727   }
728 
729 
730  /**
731   * Run me.
732   * @param args the arument array
733   * @throws Exception if an exception occurs
734   */
735   public static void main(String[] args) throws Exception {
736     if (args.length == 1) {
737       DSD dsd = new DSD(args[0]);
738       dsd.generateJava();
739     } else if (args.length == 2) {
740       DSD dsd = new DSD(args[0], new TableNamingStore(), false);
741       dsd.generateJava();
742     } else {
743        System.err.println(
744           "Usage: java org.melati.poem.prepro.DSD <dsd file> [false]");
745     }
746   }
747 
748   /**
749    * @return the name of the class from which all project tables inherit
750    */
751   public String getProjectTableClassName() {
752     return projectName + "Table";
753   }
754 
755   /**
756    * @return the project name
757    */
758   public String getProjectName() {
759     return projectName;
760   }
761 
762 }
763 
764 
765 
766 
767 
768 
769 
770 
771