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