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        private static final String PREFIX_DESTDIR = "destdir.";
296        private static final String PREFIX_PACKAGE = "package.";
297        private String backupDestDir;
298        private String backupExtensionPkg;
299        /**
300         * 改变脚本支持上下文<br>
301         * 如果为模板定义了目标输出文件夹('destdir.${template name}'属性),则使用指定的位置作为{@link #destDir}<br>
302         * 如果为模板定义包名('package.${template name}'属性),则使用指定的值为引擎上下文的'extensionPkg'属性值
303         * @param templateName 模板文件名
304         */
305        private void changeContextIfNeeded(String templateName){
306                String name = templateName.substring(templateName.lastIndexOf("/") + 1);
307                backupDestDir = destDir;
308                backupExtensionPkg = (String) this.vc.get("extensionPkg");
309                destDir = getProperty(PREFIX_DESTDIR + name, destDir);
310                this.vc.put("extensionPkg",getProperty(PREFIX_PACKAGE + name, backupExtensionPkg));
311
312        }
313        /**
314         * 恢复脚本支持上下文<br>
315         * 恢复{@link #destDir}的值,<br>
316         * 恢复引擎上下文的'extensionPkg'属性值<br>
317         * 需要与{@link #changeDestDirIfNeeded}成对调用
318         */
319        private void restoreContext(){
320                destDir = backupDestDir;
321                this.vc.put("extensionPkg",backupExtensionPkg);
322        }
323        
324        public void writeComponent(String templateName) throws Exception {
325                try {
326                        templateName = clearHeadOfLoadPath(templateName);
327                        System.out.println("Generating template " + templateName);
328                        Velocity.getTemplate((String) templateName);
329                } catch (ResourceNotFoundException rnfe) {
330                        System.err.println("Aborted writing component:" + templateName
331                                        + (this.table != null
332                                                        ? new StringBuffer().append(" for table:").append(this.table.getName()).toString()
333                                                        : "")
334                                        + " because Velocity could not find the resource.");
335                        return;
336                } catch (ParseErrorException pee) {
337                        System.err.println("Aborted writing component:" + templateName
338                                        + (this.table != null
339                                                        ? new StringBuffer().append(" for table:").append(this.table.getName()).toString()
340                                                        : "")
341                                        + " because there was a parse error in the resource.\n" + pee.getLocalizedMessage());
342                        return;
343                } catch (Exception e) {
344                        System.err.println("Aborted writing component:" + templateName
345                                        + (this.table != null
346                                                        ? new StringBuffer().append(" for table:").append(this.table.getName()).toString()
347                                                        : "")
348                                        + " there was an error initializing the template.\n" + e.getLocalizedMessage());
349                        return;
350                }
351                StringWriter sw = new StringWriter();
352                // reset
353                save_current_file = true;
354                this.current_vc.put("template", new File(templateName).getName());
355                try {
356                        changeContextIfNeeded(templateName);
357                        Velocity.mergeTemplate((String) templateName, (String) "UTF-8", (Context) this.current_vc, (Writer) sw);
358                } finally {
359                        restoreContext();
360                }
361                if(save_current_file){
362                        System.out.println(" .... writing to " + this.current_fullfilename);
363                        File file = new File(this.current_fullfilename);
364                        new File(file.getParent()).mkdirs();
365                        PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(this.current_fullfilename),"UTF-8"));
366                        // 换行符归一化:所有换行符替换为当前系统的换行符
367                        String content = Pattern.compile("(\r\n|\n|\r)", Pattern.MULTILINE).matcher(sw.toString()).replaceAll(NEW_LINE);
368                        writer.write(content);
369                        writer.flush();
370                        writer.close();
371                        System.out.println("    " + this.current_filename + " done.");
372                }else
373                        System.out.println("    " + this.current_filename + " skip.");
374        }
375
376        public void setCurrentFilename(String relpath_or_package, String fn) throws Exception {
377                this.current_filename = relpath_or_package.replace('.', File.separatorChar) + File.separatorChar + fn;
378                this.current_fullfilename = destDir + File.separatorChar + relpath_or_package.replace('.', File.separatorChar)
379                                + File.separatorChar + fn;
380                UserCodeParser uc = new UserCodeParser(this.current_fullfilename);
381                this.current_vc.put("userCode", (Object) uc);
382        }
383
384        public void setCurrentJavaFilename(String relpath_or_package, String fn) throws Exception {
385                this.setCurrentFilename("java" + File.separatorChar + relpath_or_package, fn);
386        }
387
388        public void log(String logStr) {
389                System.out.println("        " + logStr);
390        }
391
392        public static String getClassPrefix() {
393                return classPrefix;
394        }
395
396        public Database getDb() {
397                return db;
398        }
399
400        public List<Table> getTables() {
401                Table[] tabs = db.getTables();
402                ArrayList<Table> tables = new ArrayList<Table>(tabs.length);
403                for (int i = 0; i < tabs.length; ++i) {
404                        tables.add(tabs[i]);
405                }
406                return tables;
407        }
408
409        public Table getTable(String tableName) {
410                return db.getTable(tableName);
411        }
412
413        public List<Table> getRelationTables() {
414                Table[] tabs = db.getTables();
415                ArrayList<Table> tables = new ArrayList<Table>(tabs.length);
416                for (int i = 0; i < tabs.length; ++i) {
417                        if (!tabs[i].isRelationTable())
418                                continue;
419                        tables.add(tabs[i]);
420                }
421                return tables;
422        }
423
424        public String tableName() {
425                if (this.table == null) {
426                        return "";
427                }
428                return this.table.getName();
429        }
430
431        public Table getTable() {
432                return this.table;
433        }
434
435        public static String getProperty(String key) {
436                String s = props.getProperty(key);
437                return s != null ? s.trim() : s;
438        }
439
440        public static String getProperty(String key, String defaultVal) {
441                String s = props.getProperty(key, defaultVal);
442                return s != null ? s.trim() : s;
443        }
444        public static String getPropertyRequired(String property) {
445                return checkNotNull(getProperty(property),"Missing property %s",property).trim();
446        }
447        public static String[] getPropertyExploded(String key) {
448                return getPropertyExploded(key, "");
449        }
450
451        public static List<String> getPropertyExplodedAsList(String key) {
452                return getPropertyExplodedAsList(key, "");
453        }
454        
455        public static List<String> getPropertyExplodedAsList(String mkey, String defaultValue) {
456                String v = getProperty(mkey);
457                if (v == null) {
458                        v = defaultValue;
459                }
460                return CodeWriter.getExplodedStringAsList(v);
461        }
462        
463        public static String[] getPropertyExploded(String mkey, String defaultValue) {
464                return getPropertyExplodedAsList(mkey,defaultValue).toArray(new String[0]);
465        }
466        
467        public static List<String> getExplodedStringAsList(String value) {
468                if (value == null) {
469                        return Collections.emptyList();
470                }
471                ArrayList<String> al = new ArrayList<String>();
472                StringTokenizer st = new StringTokenizer(value, " ,;\t");
473                while (st.hasMoreTokens()) {
474                        al.add(st.nextToken().trim());
475                }
476                return al;
477        }
478        
479        public static String[] getExplodedString(String value) {
480                return getExplodedStringAsList(value).toArray(new String[0]);
481        }
482        
483        public static boolean getPropertyBoolean(String value){
484                try{
485                        Pattern p = Pattern.compile("yes|true|on",Pattern.CASE_INSENSITIVE);
486                        Matcher m = p.matcher(getProperty(value,""));
487                        return m.matches();
488                }catch(Exception e){
489                        return false;
490                }
491        }
492        public List<String> getClassLoadingPath() {
493                return getPropertyExplodedAsList("velocity.templates.loadingpath", "/templates/velocity/includes,/templates/velocity");
494        }
495
496        public List<String> getLoadingPathExt() {
497                return getPropertyExplodedAsList("velocity.templates.loadingpath.extension", "");
498        }
499        public String[] getSchemaTemplates(String property) {
500                return this.getTemplates(property, true);
501        }
502
503        public String[] getTableTemplates(String property) {
504                return this.getTemplates(property, false);
505        }
506
507        public String[] getTemplates(String property, boolean perShema) {
508                Vector<String> files = new Vector<String>();
509                // 支持多个模板路径
510                for(String path:getPropertyExploded(property)){
511                        this.recurseTemplate(files, path, perShema);
512                }
513                return files.toArray(new String[files.size()]);
514        }
515
516        public Vector<String> recurseTemplate(Vector<String> files, String folder, boolean perSchema) {
517                Iterable<String> dirEntries;
518                String schemaOrTable = perSchema ? "perschema" : "pertable";
519                try {
520                        String content = (String) RuntimeSingleton.getContent(folder).getData();
521                        String[] entires = content.split("\n");
522                        dirEntries = Iterables.filter(Lists.newArrayList(entires),new Predicate<String>() {
523
524                                @Override
525                                public boolean apply(String filename) {                                 
526                                        if (filename.endsWith("/")) {
527                                                return true;
528                                        }
529                                        if (!filename.endsWith(".vm")) {
530                                                return false;
531                                        }
532                                        return CodeWriter.authorizeProcess(filename, "template.file.include",
533                                                        "template.file.exclude");
534                                }
535                        });
536                } catch (ResourceNotFoundException e) {
537                        return files;
538                }
539
540                for (Iterator<String> itor = dirEntries.iterator();itor.hasNext();) {
541                        String file = itor.next();
542                        if (file.endsWith(".vm")) {
543                                if (!CodeWriter.authorizeFile(folder, schemaOrTable))
544                                        continue;
545                                files.add(StringUtilities.combinePath(folder,file));
546                                continue;
547                        }
548                        this.recurseTemplate(files, StringUtilities.combinePath(folder,file), perSchema);
549                }
550                return files;
551        }
552        public static boolean authorizeProcess(String autorizePattern, String includeProperty, String excludeProperty) {
553                boolean accept = true;
554                String[] include = CodeWriter.getPropertyExploded(includeProperty);
555                String[] exclude = CodeWriter.getPropertyExploded(excludeProperty);
556                if (include.length != 0) {
557                        if (CodeWriter.isInArray((String[]) include, (String) autorizePattern)) {
558                                Velocity.getLog().info("Processing " + autorizePattern + " (specified in " + includeProperty + ")");
559                                return true;
560                        }
561                        accept = false;
562                }
563                if (exclude.length != 0 && CodeWriter.isInArray((String[]) exclude, (String) autorizePattern)) {
564                        Velocity.getLog().info("Skipping " + autorizePattern + " (specified in " + excludeProperty + ")");
565                        return false;
566                }
567                return accept;
568        }
569
570        public static boolean folderContainsPattern(String folder, String[] patterns) {
571                if (patterns == null || folder == null) {
572                        return false;
573                }
574                for (int i = 0; i < patterns.length; ++i) {
575                        String pattern = "/" + patterns[i].toLowerCase() + "/";
576                        if (folder.toLowerCase().indexOf(pattern) == -1)
577                                continue;
578                        return true;
579                }
580                return false;
581        }
582
583        public static boolean authorizeFile(String folder, String schemaOrTable) {
584                if (folder.toLowerCase().indexOf(schemaOrTable.toLowerCase()) == -1) {
585                        return false;
586                }
587                String[] include = CodeWriter.getPropertyExploded("template.folder.include");
588                String[] exclude = CodeWriter.getPropertyExploded("template.folder.exclude");
589                if (include.length != 0) {
590                        if (CodeWriter.folderContainsPattern(folder, include)) {
591                                return true;
592                        }
593                        return false;
594                }
595                if (exclude.length != 0) {
596                        if (CodeWriter.folderContainsPattern(folder, exclude)) {
597                                return false;
598                        }
599                        return true;
600                }
601                return true;
602        }
603
604        static {
605                MGR_CLASS = "Manager";
606                classPrefix = "";
607        }
608
609        public static String getBinaryClassName() {
610                return binaryClassName;
611        }
612
613        public static String getBitStateClassName() {
614                return bitStateClassName;
615        }
616        public static String getBitStateClassWrapName() {
617                return Primitives.wrap(bitStateClass).getSimpleName();
618        }
619        public Class<?> getBitStateClass() {
620                return bitStateClass;
621        }
622        public static int getBitStateClassSize() {
623                return bitStateClassSize;
624        }
625        private static  Class<?> bitStateClassOf(String className) {
626                switch(className){
627                case "byte":
628                        return  byte.class;
629                case "short":
630                        return short.class;
631                case "int":
632                        return int.class;
633                case "long":
634                        return long.class;
635                }
636                throw new IllegalArgumentException("INVALID class name for state type [" + className + "]");
637        }
638        private static  int bitSizeOf(Class<?> clazz) {
639                try {
640                        // access field such as Integer.SIZE
641                        Field size = Primitives.wrap(clazz).getField("SIZE");
642                        return size.getInt(null);
643                } catch (Exception e) {
644                        Throwables.throwIfUnchecked(e);
645                        throw new RuntimeException(e);
646                }
647        }
648
649        private static  String constSuffixOf(String className) {
650                if("byte".equals(className)){
651                        return "";
652                }
653                if("shot".equals(className)){
654                        return "";
655                }
656                if("int".equals(className)){
657                        return "";
658                }
659                if("long".equals(className)){
660                        return "L";
661                }
662                
663                throw new IllegalArgumentException("INVALID class name for state type [" + className + "]");
664        }
665
666        public static String getBitStateConstSuffix() {
667                return bitStateConstSuffix;
668        }
669        public static int getBitStateMask() {
670                return (1<<Integer.numberOfTrailingZeros(bitStateClassSize))-1;
671        }
672        
673        public static String getBitStateMaskHex() {
674                return "0x"+Integer.toHexString(getBitStateMask());
675        }
676        public static int getBitStateClassShift() {
677                return Integer.numberOfTrailingZeros(bitStateClassSize);
678        }
679        public static boolean binaryIsByteBuffer() {
680                return !DEFAULT_BINARY_TYPE.equals(binaryClassName);
681        }
682
683        public static String getExtensionPkg(){
684                String extPkg = getProperty("codewriter.package.extension","");
685                return extPkg.isEmpty()?basePackage : extPkg;
686        }
687
688        public void setSaveCurrentFile(boolean save_current_fullfile) {
689                this.save_current_file = save_current_fullfile;
690        }
691
692        private static Class<?> loadClass(String classname,ClassLoader classLoader) throws ClassNotFoundException{
693                return null == classLoader?
694                                Class.forName(classname) :      Class.forName(classname,true,classLoader);
695        }
696        /**
697         * 读取 'extension.tools.libdirs','extension.tools.classpath'分别对应{@code libdirs,classpath}参数<br>
698         * 如果上述property都没有定义则抛出异常
699         * @param classname
700         * @return
701         * @throws ClassNotFoundException
702         * @see {@link #loadExtensionClass(String, boolean, String[], String[])}
703         */
704        public static Class<?> loadExtensionClass(String classname) throws ClassNotFoundException{
705                return loadClass(classname,getExtensionClassLoader());
706        }
707        private static synchronized URLClassLoader getExtensionClassLoader(){
708                if(null ==extensionClassLoader){
709                        if( 0 == extLibdirs.length && 0 == extClasspath.length)
710                                throw new IllegalStateException("property 'extension.tools.libdirs' and 'extension.tools.classpath' is all undefined");
711                        extensionClassLoader = ClassLoaderUtils.makeURLClassLoader(CodeWriter.class.getClassLoader(),true, extLibdirs, extClasspath);
712                }
713                return extensionClassLoader;
714        }
715        /**
716         * 使用当前类的class loader加载工具对象
717         * @param classname
718         * @return
719         * @throws ClassNotFoundException
720         * @throws InstantiationException
721         * @throws IllegalAccessException
722         */
723        public Object loadTool(String classname) throws ClassNotFoundException, InstantiationException, IllegalAccessException{
724                Class<?> clazz = loadClass(classname,null);
725                return clazz.newInstance();     
726        }
727        
728        private static boolean isInArray(String[] ar, String code) {
729                if (ar == null) {
730                        return false;
731                }
732                for (int i = 0; i < ar.length; ++i) {
733                        if (!code.equalsIgnoreCase(ar[i]))
734                                continue;
735                        return true;
736                }
737                return false;
738        }
739
740        /**
741         * 返回类的源文件位置
742         * @param baseDir 源文件夹
743         * @param clazz
744         * @return
745         */
746        public static String getSourceFile(String baseDir,Class<?> clazz){
747                if(null == baseDir || null == clazz )return null;
748                return baseDir + File.separatorChar + clazz.getName().replace('.', File.separatorChar) + ".java";
749        }
750        public static Boolean getFillNull(){
751                return fillNull.get();
752        }
753        public static void setFillNull(Boolean fill){
754                fillNull.set(fill);
755        }
756
757}