1
2
3 package net.sourceforge.sql2java;
4
5
6
7 import java.io.File;
8 import java.io.FileFilter;
9 import java.io.FileOutputStream;
10 import java.io.OutputStreamWriter;
11 import java.io.PrintWriter;
12 import java.io.StringWriter;
13 import java.util.ArrayList;
14 import java.util.Date;
15 import java.util.Hashtable;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Properties;
19 import java.util.StringTokenizer;
20 import java.util.Vector;
21
22 import org.apache.velocity.VelocityContext;
23 import org.apache.velocity.app.FieldMethodizer;
24 import org.apache.velocity.app.Velocity;
25 import org.apache.velocity.exception.ParseErrorException;
26 import org.apache.velocity.exception.ResourceNotFoundException;
27
28
29
30 public class CodeWriter
31 {
32 static protected Properties props;
33
34 public static String MGR_CLASS="Manager";
35
36 protected static String dateClassName;
37 protected static String timeClassName;
38 protected static String timestampClassName;
39
40 protected static Database db;
41 protected static Hashtable includeHash, excludeHash;
42
43 protected static String basePackage;
44 protected static String destDir;
45 protected static String optimisticLockType;
46 protected static String optimisticLockColumn;
47 public static String classPrefix;
48
49 protected VelocityContext vc;
50
51 public Table table;
52 protected VelocityContext current_vc;
53
54
55
56
57
58 /** The Default Constructor
59 * @author Kelvin Nishikawa
60 * @param props Properties for configuring this instance
61 */
62 public CodeWriter (Database db, Properties props) {
63 try {
64 CodeWriter.db = db;
65 CodeWriter.props = props;
66
67 dateClassName = props.getProperty("jdbc2java.date", "java.sql.Date");
68 timeClassName = props.getProperty("jdbc2java.time", "java.sql.Time");
69 timestampClassName = props.getProperty("jdbc2java.timestamp", "java.sql.Timestamp");
70
71
72 basePackage = props.getProperty("codewriter.package");
73 if(basePackage == null)
74 {
75 throw new Exception("Missing property: codewriter.package");
76 }
77
78 classPrefix = props.getProperty("codewriter.classprefix");
79 setDestinationFolder(props.getProperty("codewriter.destdir"));
80
81 excludeHash = setHash(props.getProperty("tables.exclude"));
82 if (excludeHash.size() != 0)
83 System.out.println("Excluding the following tables: " + props.getProperty("tables.exclude"));
84 includeHash = setHash(props.getProperty("tables.include"));
85 if (includeHash.size() != 0)
86 System.out.println("Including only the following tables: " + props.getProperty("tables.include"));
87
88 optimisticLockType = props.getProperty("optimisticlock.type", "none");
89 optimisticLockColumn = props.getProperty("optimisticlock.column");
90
91 } catch (Exception e) {
92
93 System.err.println("Threw an exception in the CodeWriter constructor:" + e.getMessage());
94 e.printStackTrace();
95 }
96 }
97
98
99 public void setDestinationFolder(String destDir) throws Exception
100 {
101 CodeWriter.destDir = destDir;
102 if(destDir == null)
103 {
104 throw new Exception("Missing property: codewriter.destdir");
105 }
106
107 File dir = new File(destDir);
108 try {
109 dir.mkdirs();
110 } catch (Exception e) {
111
112 }
113
114 if(!dir.isDirectory() || !dir.canWrite())
115 {
116 throw new Exception("Cannot write to: " + destDir);
117 }
118 }
119
120
121 private Hashtable setHash(String str)
122 {
123 if(str == null || str.trim().equals(""))
124 return new Hashtable();
125 else
126 {
127 Hashtable hash = new Hashtable();
128 StringTokenizer st = new StringTokenizer(str);
129 while(st.hasMoreTokens())
130 {
131 String val = st.nextToken().toLowerCase();
132 hash.put(val, val);
133 }
134
135 return hash;
136 }
137 }
138
139 public boolean checkTable(Table newTable) throws Exception
140 {
141 System.out.println(" checking table " + newTable.getName() + " ...");
142 boolean error = false;
143 Column[] primaryKeys = newTable.getPrimaryKeys();
144 if (newTable.getColumns().length == 0)
145 {
146 System.err.println(" WARN : no column found !");
147 error = false;
148 }
149 if (primaryKeys.length == 0)
150 {
151 System.err.println(" WARN : No primary key is defined on table " + newTable.getName());
152 System.err.println(" Tables without primary key are not fully supported");
153 error = false;
154 }
155 else
156 {
157 if (primaryKeys.length > 1)
158 {
159 System.err.print(" WARN : Composite primary key ");
160 for (int ii = 0; ii < primaryKeys.length; ii++)
161 System.err.print(primaryKeys[ii].getFullName() + ", ");
162 System.err.println();
163 System.err.println(" Tables with composite primary key are not fully supported");
164 }
165 else
166 {
167 Column pk = (Column) primaryKeys[0];
168 String pkName = pk.getName();
169 String normalKey = newTable.getName() + "_id";
170 if (pkName.equalsIgnoreCase(normalKey) == false)
171 {
172 System.err.println(" WARN : primary key should be of form <TABLE>_ID");
173 System.err.println(" found " + pkName + " expected " + normalKey);
174 }
175 if (pk.isColumnNumeric() == false)
176 {
177 System.err.println(" WARN : primary key should be a number ");
178 System.err.println(" found " + pk.getJavaType());
179 }
180 }
181 }
182 return error;
183 }
184
185 public void checkDatabase() throws Exception
186 {
187 System.out.println("Checking database tables");
188 boolean error = false;
189 Table tables[] = db.getTables();
190 for (int i = 0; i < tables.length; i++)
191 {
192 if (authorizeProcess(tables[i].getName(), "tables.include", "tables.exclude"))
193 {
194 boolean b = checkTable(tables[i]);
195 if (b == true)
196 error = true;
197 }
198 }
199 if (error == true)
200 {
201 System.err.println(" Failed : at least one of the mandatory rule for sql2java is followed by your schema.");
202 System.err.println(" Please check the documentation for more information");
203 System.exit(-1);
204 }
205 System.out.println(" Passed.");
206 }
207
208
209
210
211
212 /** The entry point for generating code. */
213 public synchronized void process() throws Exception
214 {
215 if ("true".equalsIgnoreCase(Main.getProperty("check.database")))
216 checkDatabase();
217
218 if ("true".equalsIgnoreCase(Main.getProperty("check.only.database")))
219 return;
220
221
222 Properties vprops = new Properties();
223 vprops.put("file.resource.loader.path", getLoadingPath());
224 vprops.put("velocimacro.library", "macros.include.vm");
225
226 Velocity.init(vprops);
227 vc = new VelocityContext();
228 vc.put("CodeWriter", new FieldMethodizer( this ));
229 vc.put("codewriter", this );
230 vc.put("pkg", basePackage);
231 vc.put("pkgPath", basePackage.replace('.', '/'));
232 vc.put("strUtil", StringUtilities.getInstance());
233 vc.put("fecha", new Date());
234 current_vc = new VelocityContext(vc);
235
236
237 String[] schema_templates = getSchemaTemplates("velocity.templates");
238 for(int i=0; i<schema_templates.length; i++) {
239 writeComponent(schema_templates[i]);
240 }
241 if ("true".equalsIgnoreCase(Main.getProperty("write.only.per.schema.templates")))
242 return;
243
244
245 Table tables[] = db.getTables();
246 for(int i = 0; i < tables.length; i++) {
247 if (authorizeProcess(tables[i].getName(), "tables.include", "tables.exclude"))
248 writeTable(tables[i]);
249 }
250 }
251
252 private void writeTable(Table currentTable) throws Exception
253 {
254 if (currentTable.getColumns().length == 0) {
255 return;
256 }
257 current_vc = new VelocityContext(vc);
258 this.table = currentTable;
259 current_vc.put("table", currentTable);
260
261 String[] table_templates = getTableTemplates("velocity.templates");
262 for(int i=0; i<table_templates.length; i++) {
263 writeComponent(table_templates[i]);
264 }
265 }
266
267 /** This method creates a file and generates the class; it is based on the original SQL2Java methods
268 * The filename for the class is based on the value of the Velocity variable passed in.
269 * @author Kelvin Nishikawa
270 * @param templateName The template to parse and generate from
271 * @param variableFileName A velocity variable on which to base the filename
272 * @throws Exception (IOExceptions?)
273 */
274 public void writeComponent(String templateName) throws Exception {
275
276
277 try
278 {
279 System.out.println("Generating template " + templateName);
280 Velocity.getTemplate(templateName);
281 }
282 catch (ResourceNotFoundException rnfe) {
283 System.err.println( "Aborted writing component:" + templateName
284 + (table!=null?(" for table:" + table.getName()):"")
285 + " because Velocity could not find the resource." );
286 return;
287 }
288 catch (ParseErrorException pee) {
289 System.err.println( "Aborted writing component:" + templateName
290 + (table!=null?(" for table:" + table.getName()):"")
291 + " because there was a parse error in the resource.\n" + pee.getLocalizedMessage() );
292 return;
293 }
294 catch (Exception e) {
295 System.err.println( "Aborted writing component:" + templateName
296 + (table!=null?(" for table:" + table.getName()):"")
297 + " there was an error initializing the template.\n" + e.getLocalizedMessage() );
298 return;
299 }
300
301
302
303 StringWriter sw = new StringWriter();
304 Velocity.mergeTemplate(templateName ,Velocity.ENCODING_DEFAULT, current_vc ,sw);
305
306 System.out.println(" .... writing to " + current_fullfilename);
307
308
309 File file = new File(current_fullfilename);
310 (new File(file.getParent())).mkdirs();
311 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(current_fullfilename)));
312 writer.write(sw.toString());
313 writer.flush();
314 writer.close();
315 System.out.println(" " + current_filename + " done.");
316 }
317
318
319
320
321
322 String current_fullfilename = "";
323 String current_filename = "";
324
325 public void setCurrentFilename(String relpath_or_package, String fn) throws Exception {
326 current_filename = relpath_or_package.replace('.', File.separatorChar) + File.separatorChar + fn;
327 current_fullfilename = destDir + File.separatorChar +
328 relpath_or_package.replace('.', File.separatorChar) + File.separatorChar + fn;
329 UserCodeParser uc = new UserCodeParser(current_fullfilename);
330 current_vc.put("userCode", uc);
331 }
332
333 public void setCurrentJavaFilename(String relpath_or_package, String fn) throws Exception {
334 setCurrentFilename("java" + File.separatorChar + relpath_or_package, fn);
335 }
336
337
338 /** System.out.println() wrapper */
339 public void log(String logStr ) {
340 System.out.println(" " + logStr );
341 }
342
343 public String getClassPrefix() {
344 return classPrefix;
345 }
346
347 /** Public accessor for db */
348 public Database getDb() {
349 return db;
350 }
351
352 /** Public db.getTables() */
353 public List getTables() {
354 Table[] tabs = db.getTables();
355 List tables = new ArrayList(tabs.length);
356 for ( int i = 0; i < tabs.length; i++ ) tables.add(tabs[i]);
357 return tables;
358 }
359
360 /** Public db.getTable() */
361 public Table getTable(String tableName) {
362 return db.getTable(tableName);
363 }
364
365 /** Returns from db.getTables() those having isRelationTable() evaluating to true. */
366 public List getRelationTables() {
367 Table[] tabs = db.getTables();
368 List tables = new ArrayList(tabs.length);
369 for ( int i = 0; i < tabs.length; i++ ) {
370 if (tabs[i].isRelationTable()) {
371 tables.add(tabs[i]);
372 }
373 }
374 return tables;
375 }
376
377 /** Return table.getName() */
378 public String tableName() {
379 if (table == null) return "";
380 return table.getName();
381 }
382
383 /** public accessor for table */
384 public Table getTable() {
385 return table;
386 }
387
388 /** Check if a list contains an item that is equal() to a string */
389 public boolean listContainsString( List list, String string ) {
390 Object obj = null;
391 for ( Iterator iter = list.iterator();
392 iter.hasNext();
393 obj = iter.next() ) {
394 if ( string.equals(obj) ) return true;
395 }
396 return false;
397 }
398
399
400
401
402
403 /** Convenience property chop method
404 * @author Kelvin Nishikawa
405 * @param key the property to get from this.props
406 * @return the associated value
407 */
408 static public String getProperty(String key)
409 {
410 String s = props.getProperty(key);
411 return s!=null?s.trim():s;
412 }
413
414 /** Convenience property chop method
415 * @author Kelvin Nishikawa
416 * @param key the property to get from this.props
417 * @param default_val the default value to return in case not found
418 * @return the associated value
419 */
420 static public String getProperty(String key, String default_val) {
421 String s = props.getProperty(key,default_val);
422 return s!=null?s.trim():s;
423 }
424
425
426 /**
427 * Return as a String array the key's value.
428 */
429 static public String[] getPropertyExploded(String key)
430 {
431 return getPropertyExploded(key, "");
432 }
433
434 static public String[] getPropertyExploded(String mkey, String defaultValue)
435 {
436 String v = getProperty(mkey);
437 if (v==null) {
438 v = defaultValue;
439 }
440 return getExplodedString(v);
441 }
442
443 static public String[] getExplodedString(String value)
444 {
445 ArrayList al = new ArrayList();
446 if (value == null)
447 return new String[0];
448
449 StringTokenizer st = new StringTokenizer(value, " ,;\t");
450 while (st.hasMoreTokens()) {
451 al.add(st.nextToken().trim());
452 }
453
454 return (String[])al.toArray(new String[al.size()]);
455 }
456
457 /**
458 * get loading path
459 */
460 public String getLoadingPath()
461 {
462 String ret = "";
463 String[] paths = getPropertyExploded("velocity.templates.loadingpath", ".");
464 for(int i=0; i<paths.length; i++) {
465 ret = ret + paths[i] + ",";
466 }
467 System.out.println("getLoadingPath = " + ret);
468 return ret;
469 }
470
471 public String[] getSchemaTemplates(String property)
472 {
473 return getTemplates(property, true);
474 }
475
476 public String[] getTableTemplates(String property)
477 {
478 return getTemplates(property, false);
479 }
480
481 public String[] getTemplates(String property, boolean perShema)
482 {
483 Vector files = new Vector();
484 recurseTemplate(files, Main.getProperty(property), perShema);
485 return (String[])files.toArray(new String[files.size()]);
486 }
487
488 /**
489 * recurse in the template folders
490 */
491 public Vector recurseTemplate(Vector files, String folder, boolean perSchema)
492 {
493 String schemaOrTable = "perschema";
494 if (perSchema == false)
495 schemaOrTable = "pertable";
496 FileFilter filter = new FileFilter()
497 {
498 public boolean accept(File filename)
499 {
500 if (filename.isDirectory())
501 return true;
502 if (filename.getName().endsWith(".vm") == false)
503 return false;
504 return authorizeProcess(filename.getName(), "template.file.include", "template.file.exclude");
505 }
506 };
507
508 File[] dirEntries = (new File(folder)).listFiles(filter);
509 if (dirEntries == null)
510 return files;
511 for (int i=0; i<dirEntries.length; i++)
512 {
513
514 if (dirEntries[i].isFile())
515 {
516
517 if (authorizeFile(folder, schemaOrTable))
518 {
519 files.add(folder + "/" + dirEntries[i].getName());
520 }
521 }
522 else if(dirEntries[i].isDirectory())
523 recurseTemplate(files, folder + "/" + dirEntries[i].getName(), perSchema);
524 }
525 return files;
526 }
527
528 /**
529 * check the value for include & exclude lists defined in the config file
530 */
531 public static boolean authorizeProcess(String autorizePattern, String includeProperty, String excludeProperty)
532 {
533 boolean accept = true;
534 String include[] = getPropertyExploded(includeProperty);
535 String exclude[] = getPropertyExploded(excludeProperty);
536 if (include.length != 0)
537 {
538 if (Main.isInArray(include, autorizePattern))
539 {
540 System.out.println("Processing " + autorizePattern + " (specified in " + includeProperty + ")");
541 return true;
542 }
543 accept = false;
544 }
545 if (exclude.length != 0)
546 {
547 if (Main.isInArray(exclude, autorizePattern))
548 {
549 System.out.println("Skipping " + autorizePattern + " (specified in " + excludeProperty + ")");
550 return false;
551 }
552 }
553 return accept;
554 }
555
556 public static boolean folderContainsPattern(String folder, String[] patterns)
557 {
558 if (patterns == null || folder == null)
559 return false;
560 for (int i = 0; i < patterns.length; i ++)
561 {
562 String pattern = "/" + patterns[i].toLowerCase() + "/";
563 if (folder.toLowerCase().indexOf(pattern) != -1)
564 {
565 return true;
566 }
567 }
568 return false;
569 }
570
571 /**
572 * is this file to be processed or not ?
573 */
574 public static boolean authorizeFile(String folder, String schemaOrTable)
575 {
576 if (folder.toLowerCase().indexOf(schemaOrTable.toLowerCase()) == -1)
577 return false;
578 String include[] = getPropertyExploded("template.folder.include");
579 String exclude[] = getPropertyExploded("template.folder.exclude");
580 if (include.length != 0)
581 {
582 if (folderContainsPattern(folder, include) == true)
583 return true;
584 return false;
585 }
586 if (exclude.length != 0)
587 {
588 if (folderContainsPattern(folder, exclude) == true)
589 return false;
590 else
591 return true;
592 }
593 return true;
594 }
595 }