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 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
97 final String projectName;
98 TableNamingStore tableNamingStore;
99
100
101 final Vector<TableDef> tablesInPackage = new Vector<TableDef>();
102
103
104 final Vector<TableDef> tablesInDatabase = new Vector<TableDef>();
105
106
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
125
126
127
128
129
130
131
132
133
134
135
136 public DSD(String file) throws IOException, ParsingDSDException,
137 IllegalityException, ResourceNotFoundException {
138 this(file, new TableNamingStore(), true);
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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
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
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
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
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) {
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) {
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
311 ee = null;
312 }
313 try {
314 f.delete();
315 } catch (Exception ee) {
316
317 ee = null;
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
393 ee = null;
394 }
395 try {
396 f.delete();
397 } catch (Exception ee) {
398
399 ee = null;
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
536
537
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
568
569
570
571
572
573
574
575
576 w.write(NAG_BLOCK);
577 w.write("}\n\n");
578 }
579
580
581
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
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
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
648
649
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
698
699
700
701
702
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
732
733
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
750
751 public String getProjectTableClassName() {
752 return projectName + "Table";
753 }
754
755
756
757
758 public String getProjectName() {
759 return projectName;
760 }
761
762 }
763
764
765
766
767
768
769
770
771