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}