001package gu.sql2java.generator; 002 003import java.io.File; 004import java.io.FileOutputStream; 005import java.io.IOException; 006import java.io.OutputStreamWriter; 007import java.io.PrintWriter; 008import java.io.StringWriter; 009import java.io.Writer; 010import java.lang.reflect.Field; 011import java.net.URLClassLoader; 012import java.util.ArrayList; 013import java.util.Collections; 014import java.util.Date; 015import java.util.Hashtable; 016import java.util.Iterator; 017import java.util.List; 018import java.util.Properties; 019import java.util.StringTokenizer; 020import java.util.Vector; 021import java.util.regex.Matcher; 022import java.util.regex.Pattern; 023 024import org.apache.velocity.VelocityContext; 025import org.apache.velocity.app.FieldMethodizer; 026import org.apache.velocity.app.Velocity; 027import org.apache.velocity.context.Context; 028import org.apache.velocity.exception.ParseErrorException; 029import org.apache.velocity.exception.ResourceNotFoundException; 030import org.apache.velocity.runtime.RuntimeSingleton; 031import org.apache.velocity.tools.generic.EscapeTool; 032import org.apache.velocity.tools.generic.SortTool; 033 034import com.google.common.base.Joiner; 035import com.google.common.base.Predicate; 036import com.google.common.base.Throwables; 037import com.google.common.collect.Iterables; 038import com.google.common.collect.Lists; 039import com.google.common.primitives.Primitives; 040 041import net.gdface.utils.ClassLoaderUtils; 042 043import static com.google.common.base.Preconditions.checkNotNull;; 044 045public class CodeWriter { 046 public static final String NEW_LINE = System.getProperty("line.separator"); 047 protected static final String DEFAULT_BINARY_TYPE = "byte[]"; 048 protected static final String DEFAULT_BITSTAE_TYPE = "int"; 049 protected static Properties props; 050 public static String MGR_CLASS; 051 protected static String dateClassName; 052 protected static String timeClassName; 053 protected static String timestampClassName; 054 /** type for byte array,default 'byte[]',defined in properties */ 055 public static String binaryClassName; 056 private static String bitStateClassName; 057 private static Class<?> bitStateClass; 058 private static int bitStateClassSize; 059 private static String bitStateConstSuffix; 060 protected static Database db; 061 protected static Hashtable<String, String> includeHash; 062 protected static Hashtable<String, String> excludeHash; 063 protected static String basePackage; 064 /** 参见 {@link Column#getPreparedStatementMethod(String, String)} */ 065 private static final ThreadLocal<Boolean> fillNull = new ThreadLocal<Boolean>(){ 066 @Override 067 protected Boolean initialValue() { 068 return true; 069 }}; 070 protected static String destDir; 071 protected static String optimisticLockType; 072 protected static String optimisticLockColumn; 073 public static String classPrefix; 074 protected VelocityContext vc; 075 public Table table; 076 protected VelocityContext current_vc; 077 String current_fullfilename = ""; 078 String current_filename = ""; 079 /** 是否保存当前文件标志,模板可以通过改写此标志,跳过模板生成 */ 080 boolean save_current_file = true; 081 private static String[] extLibdirs; 082 private static String[] extClasspath; 083 private static URLClassLoader extensionClassLoader; 084 public CodeWriter(Database db, Properties props) { 085 try { 086 CodeWriter.db = db; 087 CodeWriter.props = props; 088 dateClassName = getProperty("jdbc2java.date", "java.sql.Date"); 089 timeClassName = getProperty("jdbc2java.time", "java.sql.Time"); 090 timestampClassName = getProperty("jdbc2java.timestamp", "java.sql.Timestamp"); 091 binaryClassName = getProperty("binary.type", DEFAULT_BINARY_TYPE); 092 bitStateClassName = getProperty("bitstate.type", DEFAULT_BITSTAE_TYPE); 093 bitStateClass = bitStateClassOf(bitStateClassName); 094 bitStateClassSize = bitSizeOf(bitStateClass); 095 bitStateConstSuffix = constSuffixOf(bitStateClassName); 096 basePackage = getPropertyRequired("codewriter.package"); 097 extLibdirs = getPropertyExploded("extension.tools.libdirs"); 098 extClasspath = getPropertyExploded("extension.tools.classpath"); 099 classPrefix = props.getProperty("codewriter.classprefix"); 100 this.setDestinationFolder(getPropertyRequired("codewriter.destdir")); 101 excludeHash = this.setHash(props.getProperty("tables.exclude")); 102 if (excludeHash.size() != 0) { 103 System.out.println("Excluding the following tables: " + props.getProperty("tables.exclude")); 104 } 105 if ((CodeWriter.includeHash = this.setHash(props.getProperty("tables.include"))).size() != 0) { 106 System.out.println("Including only the following tables: " + props.getProperty("tables.include")); 107 } 108 optimisticLockType = props.getProperty("optimisticlock.type", "none"); 109 optimisticLockColumn = props.getProperty("optimisticlock.column"); 110 } catch (Exception e) { 111 System.err.println("Threw an exception in the CodeWriter constructor:" + e.getMessage()); 112 e.printStackTrace(); 113 } 114 } 115 116 public void setDestinationFolder(String destDir) throws Exception { 117 CodeWriter.destDir = destDir; 118 if (destDir == null) { 119 throw new Exception("Missing property: codewriter.destdir"); 120 } 121 File dir = new File(destDir); 122 try { 123 dir.mkdirs(); 124 } catch (Exception e) { 125 // empty catch block 126 } 127 if (!dir.isDirectory() || !dir.canWrite()) { 128 throw new Exception("Cannot write to: " + destDir); 129 } 130 } 131 132 private Hashtable<String, String> setHash(String str) { 133 if (str == null || str.trim().equals("")) { 134 return new Hashtable<String, String>(); 135 } 136 Hashtable<String, String> hash = new Hashtable<String, String>(); 137 StringTokenizer st = new StringTokenizer(str); 138 while (st.hasMoreTokens()) { 139 String val = st.nextToken().toLowerCase(); 140 hash.put(val, val); 141 } 142 return hash; 143 } 144 145 public boolean checkTable(Table newTable) throws Exception { 146 System.out.println(" checking table " + newTable.getName() + " ..."); 147 boolean error = false; 148 Column[] primaryKeys = newTable.getPrimaryKeys(); 149 if (newTable.getColumns().length == 0) { 150 System.err.println(" WARN : no column found !"); 151 error = false; 152 } 153 if (primaryKeys.length == 0) { 154 System.err.println(" WARN : No primary key is defined on table " + newTable.getName()); 155 System.err.println(" Tables without primary key are not fully supported"); 156 error = false; 157 } else if (primaryKeys.length > 1) { 158 System.err.print(" WARN : Composite primary key "); 159 for (int ii = 0; ii < primaryKeys.length; ++ii) { 160 System.err.print(primaryKeys[ii].getFullName() + ", "); 161 } 162 System.err.println(); 163 System.err.println(" Tables with composite primary key are not fully supported"); 164 } else { 165 String normalKey; 166 Column pk = primaryKeys[0]; 167 String pkName = pk.getName(); 168 if (!pkName.equalsIgnoreCase(normalKey = newTable.getName() + "_id")) { 169 System.err.println(" WARN : primary key should be of form <TABLE>_ID"); 170 System.err.println(" found " + pkName + " expected " + normalKey); 171 } 172 if (!pk.isColumnNumeric()) { 173 System.err.println(" WARN : primary key should be a number "); 174 System.err.println(" found " + pk.getJavaType()); 175 } 176 } 177 return error; 178 } 179 180 public void checkDatabase() throws Exception { 181 System.out.println("Checking database tables"); 182 boolean error = false; 183 Table[] tables = db.getTables(); 184 for (int i = 0; i < tables.length; ++i) { 185 if (!CodeWriter.authorizeProcess(tables[i].getName(), "tables.include", "tables.exclude") 186 || !this.checkTable(tables[i])) 187 continue; 188 error = true; 189 } 190 if (error) { 191 System.err.println( 192 " Failed : at least one of the mandatory rule for sql2java is followed by your schema."); 193 System.err.println(" Please check the documentation for more information"); 194 System.exit(-1); 195 } 196 System.out.println(" Passed."); 197 } 198 199 public synchronized void process() throws Exception { 200 if ("true".equalsIgnoreCase(getProperty("check.database"))) { 201 this.checkDatabase(); 202 } 203 if ("true".equalsIgnoreCase(getProperty("check.only.database"))) { 204 return; 205 } 206 Properties vprops = new Properties(); 207 vprops.put(Velocity.RESOURCE_LOADER,"class,file"); 208 vprops.put(Velocity.FILE_RESOURCE_LOADER_PATH, Joiner.on(',').join(this.getLoadingPathExt())); 209 vprops.put("file.resource.loader.class", Sql2javaFileResourceLoader.class.getName()); 210 211 vprops.put("class.resource.loader.description", "Velocity Classpath Resource Loader"); 212 vprops.put("class.resource.loader.class",Sql2javaClasspathResourceLoader.class.getName()); 213 vprops.put("class.resource.loader.prefix", Joiner.on(',').join(this.getClassLoadingPath())); 214 215 vprops.put(Velocity.VM_LIBRARY, "/templates/velocity/includes/macros.include.vm"); 216 vprops.put(Velocity.SET_NULL_ALLOWED, "true"); 217 vprops.put(Velocity.INPUT_ENCODING,"UTF-8"); 218 vprops.put(Velocity.OUTPUT_ENCODING,"UTF-8"); 219 Velocity.init(vprops); 220 this.vc = new VelocityContext(); 221 this.vc.put("CodeWriter", (Object) new FieldMethodizer((Object) this)); 222 this.vc.put("codewriter", (Object) this); 223 this.vc.put("esc", new EscapeTool()); 224 this.vc.put("sorter", new SortTool()); 225 this.vc.put("pkg", (Object) basePackage); 226 this.vc.put("extensionPkg",getExtensionPkg()); 227 this.vc.put("isGeneral", Boolean.TRUE); 228 this.vc.put("pkgPath", (Object) basePackage.replace('.', '/')); 229 this.vc.put("strUtil",StringUtilities.getInstance()); 230 this.vc.put("fecha", new Date()); 231 this.current_vc = new VelocityContext((Context) this.vc); 232 generate("velocity.templates"); 233 // 如果定义了扩展模板的输出文件夹就用它替换destDir,用完后恢复 234 String destDirExt = getProperty("codewriter.destdir.extension",""); 235 String oldDest = destDir; 236 if(!destDirExt.isEmpty()){ 237 destDir = destDirExt; 238 } 239 try{ 240 // 生成外部扩展模板代码 241 generate("velocity.templates.extension"); 242 }finally{ 243 destDir = oldDest; 244 } 245 } 246 private void generate(String propName) throws Exception{ 247 if(null == getProperty(propName)) 248 return; 249 String[] schema_templates = this.getSchemaTemplates(propName); 250 for (int i = 0; i < schema_templates.length; ++i) { 251 this.writeComponent(schema_templates[i]); 252 } 253 if ("true".equalsIgnoreCase(getProperty("write.only.per.schema.templates"))) { 254 return; 255 } 256 Table[] tables = db.getTables(); 257 for (int i2 = 0; i2 < tables.length; ++i2) { 258 if (!CodeWriter.authorizeProcess(tables[i2].getName(), "tables.include", "tables.exclude")) 259 continue; 260 this.writeTable(tables[i2], propName); 261 } 262 } 263 private void writeTable(Table currentTable, String propName) throws Exception { 264 if (currentTable.getColumns().length == 0) { 265 return; 266 } 267 this.current_vc = new VelocityContext((Context) this.vc); 268 this.table = currentTable; 269 this.current_vc.put("table", (Object) currentTable); 270 String[] table_templates = this.getTableTemplates(propName); 271 for (int i = 0; i < table_templates.length; ++i) { 272 this.writeComponent(table_templates[i]); 273 } 274 } 275 276 private String clearHeadOfLoadPath(String templateName){ 277 checkNotNull(templateName); 278 List<String> loadPath = getLoadingPathExt(); 279 File template= new File(templateName); 280 for(String path:loadPath){ 281 File pfile = new File(path); 282 try { 283 if(template.getCanonicalPath().startsWith(pfile.getCanonicalPath())){ 284 String tmpl = template.getCanonicalPath().replace(pfile.getCanonicalPath(), "").replace('\\', '/'); 285 if(Velocity.resourceExists(tmpl)){ 286 return tmpl; 287 } 288 } 289 } catch (IOException e) { 290 throw new RuntimeException(e); 291 } 292 } 293 return templateName; 294 } 295 public void writeComponent(String templateName) throws Exception { 296 try { 297 templateName = clearHeadOfLoadPath(templateName); 298 System.out.println("Generating template " + templateName); 299 Velocity.getTemplate((String) templateName); 300 } catch (ResourceNotFoundException rnfe) { 301 System.err.println("Aborted writing component:" + templateName 302 + (this.table != null 303 ? new StringBuffer().append(" for table:").append(this.table.getName()).toString() 304 : "") 305 + " because Velocity could not find the resource."); 306 return; 307 } catch (ParseErrorException pee) { 308 System.err.println("Aborted writing component:" + templateName 309 + (this.table != null 310 ? new StringBuffer().append(" for table:").append(this.table.getName()).toString() 311 : "") 312 + " because there was a parse error in the resource.\n" + pee.getLocalizedMessage()); 313 return; 314 } catch (Exception e) { 315 System.err.println("Aborted writing component:" + templateName 316 + (this.table != null 317 ? new StringBuffer().append(" for table:").append(this.table.getName()).toString() 318 : "") 319 + " there was an error initializing the template.\n" + e.getLocalizedMessage()); 320 return; 321 } 322 StringWriter sw = new StringWriter(); 323 // reset 324 save_current_file = true; 325 this.current_vc.put("template", new File(templateName).getName()); 326 Velocity.mergeTemplate((String) templateName, (String) "UTF-8", (Context) this.current_vc, (Writer) sw); 327 if(save_current_file){ 328 System.out.println(" .... writing to " + this.current_fullfilename); 329 File file = new File(this.current_fullfilename); 330 new File(file.getParent()).mkdirs(); 331 PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.current_fullfilename),"UTF-8")); 332 // 换行符归一化:所有换行符替换为当前系统的换行符 333 String content = Pattern.compile("(\r\n|\n|\r)", Pattern.MULTILINE).matcher(sw.toString()).replaceAll(NEW_LINE); 334 writer.write(content); 335 writer.flush(); 336 writer.close(); 337 System.out.println(" " + this.current_filename + " done."); 338 }else 339 System.out.println(" " + this.current_filename + " skip."); 340 } 341 342 public void setCurrentFilename(String relpath_or_package, String fn) throws Exception { 343 this.current_filename = relpath_or_package.replace('.', File.separatorChar) + File.separatorChar + fn; 344 this.current_fullfilename = destDir + File.separatorChar + relpath_or_package.replace('.', File.separatorChar) 345 + File.separatorChar + fn; 346 UserCodeParser uc = new UserCodeParser(this.current_fullfilename); 347 this.current_vc.put("userCode", (Object) uc); 348 } 349 350 public void setCurrentJavaFilename(String relpath_or_package, String fn) throws Exception { 351 this.setCurrentFilename("java" + File.separatorChar + relpath_or_package, fn); 352 } 353 354 public void log(String logStr) { 355 System.out.println(" " + logStr); 356 } 357 358 public static String getClassPrefix() { 359 return classPrefix; 360 } 361 362 public Database getDb() { 363 return db; 364 } 365 366 public List<Table> getTables() { 367 Table[] tabs = db.getTables(); 368 ArrayList<Table> tables = new ArrayList<Table>(tabs.length); 369 for (int i = 0; i < tabs.length; ++i) { 370 tables.add(tabs[i]); 371 } 372 return tables; 373 } 374 375 public Table getTable(String tableName) { 376 return db.getTable(tableName); 377 } 378 379 public List<Table> getRelationTables() { 380 Table[] tabs = db.getTables(); 381 ArrayList<Table> tables = new ArrayList<Table>(tabs.length); 382 for (int i = 0; i < tabs.length; ++i) { 383 if (!tabs[i].isRelationTable()) 384 continue; 385 tables.add(tabs[i]); 386 } 387 return tables; 388 } 389 390 public String tableName() { 391 if (this.table == null) { 392 return ""; 393 } 394 return this.table.getName(); 395 } 396 397 public Table getTable() { 398 return this.table; 399 } 400 401 public static String getProperty(String key) { 402 String s = props.getProperty(key); 403 return s != null ? s.trim() : s; 404 } 405 406 public static String getProperty(String key, String defaultVal) { 407 String s = props.getProperty(key, defaultVal); 408 return s != null ? s.trim() : s; 409 } 410 public static String getPropertyRequired(String property) { 411 return checkNotNull(getProperty(property),"Missing property %s",property).trim(); 412 } 413 public static String[] getPropertyExploded(String key) { 414 return getPropertyExploded(key, ""); 415 } 416 417 public static List<String> getPropertyExplodedAsList(String key) { 418 return getPropertyExplodedAsList(key, ""); 419 } 420 421 public static List<String> getPropertyExplodedAsList(String mkey, String defaultValue) { 422 String v = getProperty(mkey); 423 if (v == null) { 424 v = defaultValue; 425 } 426 return CodeWriter.getExplodedStringAsList(v); 427 } 428 429 public static String[] getPropertyExploded(String mkey, String defaultValue) { 430 return getPropertyExplodedAsList(mkey,defaultValue).toArray(new String[0]); 431 } 432 433 public static List<String> getExplodedStringAsList(String value) { 434 if (value == null) { 435 return Collections.emptyList(); 436 } 437 ArrayList<String> al = new ArrayList<String>(); 438 StringTokenizer st = new StringTokenizer(value, " ,;\t"); 439 while (st.hasMoreTokens()) { 440 al.add(st.nextToken().trim()); 441 } 442 return al; 443 } 444 445 public static String[] getExplodedString(String value) { 446 return getExplodedStringAsList(value).toArray(new String[0]); 447 } 448 449 public static boolean getPropertyBoolean(String value){ 450 try{ 451 Pattern p = Pattern.compile("yes|true|on",Pattern.CASE_INSENSITIVE); 452 Matcher m = p.matcher(getProperty(value,"")); 453 return m.matches(); 454 }catch(Exception e){ 455 return false; 456 } 457 } 458 public List<String> getClassLoadingPath() { 459 return getPropertyExplodedAsList("velocity.templates.loadingpath", "/templates/velocity/includes,/templates/velocity"); 460 } 461 462 public List<String> getLoadingPathExt() { 463 return getPropertyExplodedAsList("velocity.templates.loadingpath.extension", ""); 464 } 465 public String[] getSchemaTemplates(String property) { 466 return this.getTemplates(property, true); 467 } 468 469 public String[] getTableTemplates(String property) { 470 return this.getTemplates(property, false); 471 } 472 473 public String[] getTemplates(String property, boolean perShema) { 474 Vector<String> files = new Vector<String>(); 475 // 支持多个模板路径 476 for(String path:getPropertyExploded(property)){ 477 this.recurseTemplate(files, path, perShema); 478 } 479 return files.toArray(new String[files.size()]); 480 } 481 482 public Vector<String> recurseTemplate(Vector<String> files, String folder, boolean perSchema) { 483 Iterable<String> dirEntries; 484 String schemaOrTable = perSchema ? "perschema" : "pertable"; 485 try { 486 String content = (String) RuntimeSingleton.getContent(folder).getData(); 487 String[] entires = content.split("\n"); 488 dirEntries = Iterables.filter(Lists.newArrayList(entires),new Predicate<String>() { 489 490 @Override 491 public boolean apply(String filename) { 492 if (filename.endsWith("/")) { 493 return true; 494 } 495 if (!filename.endsWith(".vm")) { 496 return false; 497 } 498 return CodeWriter.authorizeProcess(filename, "template.file.include", 499 "template.file.exclude"); 500 } 501 }); 502 } catch (ResourceNotFoundException e) { 503 return files; 504 } 505 506 for (Iterator<String> itor = dirEntries.iterator();itor.hasNext();) { 507 String file = itor.next(); 508 if (file.endsWith(".vm")) { 509 if (!CodeWriter.authorizeFile(folder, schemaOrTable)) 510 continue; 511 files.add(StringUtilities.combinePath(folder,file)); 512 continue; 513 } 514 this.recurseTemplate(files, StringUtilities.combinePath(folder,file), perSchema); 515 } 516 return files; 517 } 518 public static boolean authorizeProcess(String autorizePattern, String includeProperty, String excludeProperty) { 519 boolean accept = true; 520 String[] include = CodeWriter.getPropertyExploded(includeProperty); 521 String[] exclude = CodeWriter.getPropertyExploded(excludeProperty); 522 if (include.length != 0) { 523 if (CodeWriter.isInArray((String[]) include, (String) autorizePattern)) { 524 Velocity.getLog().info("Processing " + autorizePattern + " (specified in " + includeProperty + ")"); 525 return true; 526 } 527 accept = false; 528 } 529 if (exclude.length != 0 && CodeWriter.isInArray((String[]) exclude, (String) autorizePattern)) { 530 Velocity.getLog().info("Skipping " + autorizePattern + " (specified in " + excludeProperty + ")"); 531 return false; 532 } 533 return accept; 534 } 535 536 public static boolean folderContainsPattern(String folder, String[] patterns) { 537 if (patterns == null || folder == null) { 538 return false; 539 } 540 for (int i = 0; i < patterns.length; ++i) { 541 String pattern = "/" + patterns[i].toLowerCase() + "/"; 542 if (folder.toLowerCase().indexOf(pattern) == -1) 543 continue; 544 return true; 545 } 546 return false; 547 } 548 549 public static boolean authorizeFile(String folder, String schemaOrTable) { 550 if (folder.toLowerCase().indexOf(schemaOrTable.toLowerCase()) == -1) { 551 return false; 552 } 553 String[] include = CodeWriter.getPropertyExploded("template.folder.include"); 554 String[] exclude = CodeWriter.getPropertyExploded("template.folder.exclude"); 555 if (include.length != 0) { 556 if (CodeWriter.folderContainsPattern(folder, include)) { 557 return true; 558 } 559 return false; 560 } 561 if (exclude.length != 0) { 562 if (CodeWriter.folderContainsPattern(folder, exclude)) { 563 return false; 564 } 565 return true; 566 } 567 return true; 568 } 569 570 static { 571 MGR_CLASS = "Manager"; 572 classPrefix = ""; 573 } 574 575 public static String getBinaryClassName() { 576 return binaryClassName; 577 } 578 579 public static String getBitStateClassName() { 580 return bitStateClassName; 581 } 582 public static String getBitStateClassWrapName() { 583 return Primitives.wrap(bitStateClass).getSimpleName(); 584 } 585 public Class<?> getBitStateClass() { 586 return bitStateClass; 587 } 588 public static int getBitStateClassSize() { 589 return bitStateClassSize; 590 } 591 private static Class<?> bitStateClassOf(String className) { 592 switch(className){ 593 case "byte": 594 return byte.class; 595 case "short": 596 return short.class; 597 case "int": 598 return int.class; 599 case "long": 600 return long.class; 601 } 602 throw new IllegalArgumentException("INVALID class name for state type [" + className + "]"); 603 } 604 private static int bitSizeOf(Class<?> clazz) { 605 try { 606 // access field such as Integer.SIZE 607 Field size = Primitives.wrap(clazz).getField("SIZE"); 608 return size.getInt(null); 609 } catch (Exception e) { 610 Throwables.throwIfUnchecked(e); 611 throw new RuntimeException(e); 612 } 613 } 614 615 private static String constSuffixOf(String className) { 616 if("byte".equals(className)){ 617 return ""; 618 } 619 if("shot".equals(className)){ 620 return ""; 621 } 622 if("int".equals(className)){ 623 return ""; 624 } 625 if("long".equals(className)){ 626 return "L"; 627 } 628 629 throw new IllegalArgumentException("INVALID class name for state type [" + className + "]"); 630 } 631 632 public static String getBitStateConstSuffix() { 633 return bitStateConstSuffix; 634 } 635 public static int getBitStateMask() { 636 return (1<<Integer.numberOfTrailingZeros(bitStateClassSize))-1; 637 } 638 639 public static String getBitStateMaskHex() { 640 return "0x"+Integer.toHexString(getBitStateMask()); 641 } 642 public static int getBitStateClassShift() { 643 return Integer.numberOfTrailingZeros(bitStateClassSize); 644 } 645 public static boolean binaryIsByteBuffer() { 646 return !DEFAULT_BINARY_TYPE.equals(binaryClassName); 647 } 648 649 public static String getExtensionPkg(){ 650 String extPkg = getProperty("codewriter.package.extension",""); 651 return extPkg.isEmpty()?basePackage : extPkg; 652 } 653 654 public void setSaveCurrentFile(boolean save_current_fullfile) { 655 this.save_current_file = save_current_fullfile; 656 } 657 658 private static Class<?> loadClass(String classname,ClassLoader classLoader) throws ClassNotFoundException{ 659 return null == classLoader? 660 Class.forName(classname) : Class.forName(classname,true,classLoader); 661 } 662 /** 663 * 读取 'extension.tools.libdirs','extension.tools.classpath'分别对应{@code libdirs,classpath}参数<br> 664 * 如果上述property都没有定义则抛出异常 665 * @param classname 666 * @return 667 * @throws ClassNotFoundException 668 * @see {@link #loadExtensionClass(String, boolean, String[], String[])} 669 */ 670 public static Class<?> loadExtensionClass(String classname) throws ClassNotFoundException{ 671 return loadClass(classname,getExtensionClassLoader()); 672 } 673 private static synchronized URLClassLoader getExtensionClassLoader(){ 674 if(null ==extensionClassLoader){ 675 if( 0 == extLibdirs.length && 0 == extClasspath.length) 676 throw new IllegalStateException("property 'extension.tools.libdirs' and 'extension.tools.classpath' is all undefined"); 677 extensionClassLoader = ClassLoaderUtils.makeURLClassLoader(CodeWriter.class.getClassLoader(),true, extLibdirs, extClasspath); 678 } 679 return extensionClassLoader; 680 } 681 /** 682 * 使用当前类的class loader加载工具对象 683 * @param classname 684 * @return 685 * @throws ClassNotFoundException 686 * @throws InstantiationException 687 * @throws IllegalAccessException 688 */ 689 public Object loadTool(String classname) throws ClassNotFoundException, InstantiationException, IllegalAccessException{ 690 Class<?> clazz = loadClass(classname,null); 691 return clazz.newInstance(); 692 } 693 694 private static boolean isInArray(String[] ar, String code) { 695 if (ar == null) { 696 return false; 697 } 698 for (int i = 0; i < ar.length; ++i) { 699 if (!code.equalsIgnoreCase(ar[i])) 700 continue; 701 return true; 702 } 703 return false; 704 } 705 706 /** 707 * 返回类的源文件位置 708 * @param baseDir 源文件夹 709 * @param clazz 710 * @return 711 */ 712 public static String getSourceFile(String baseDir,Class<?> clazz){ 713 if(null == baseDir || null == clazz )return null; 714 return baseDir + File.separatorChar + clazz.getName().replace('.', File.separatorChar) + ".java"; 715 } 716 public static Boolean getFillNull(){ 717 return fillNull.get(); 718 } 719 public static void setFillNull(Boolean fill){ 720 fillNull.set(fill); 721 } 722 723}