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