| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.enhance; |
| |
| import java.io.File; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.Writer; |
| import java.lang.reflect.Modifier; |
| import java.security.AccessController; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.apache.openjpa.conf.OpenJPAConfiguration; |
| import org.apache.openjpa.conf.OpenJPAConfigurationImpl; |
| import org.apache.openjpa.lib.conf.Configuration; |
| import org.apache.openjpa.lib.conf.Configurations; |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.meta.ClassArgParser; |
| import org.apache.openjpa.lib.util.ClassUtil; |
| import org.apache.openjpa.lib.util.CodeFormat; |
| import org.apache.openjpa.lib.util.Files; |
| import org.apache.openjpa.lib.util.J2DoPrivHelper; |
| import org.apache.openjpa.lib.util.Localizer; |
| import org.apache.openjpa.lib.util.Options; |
| import org.apache.openjpa.lib.util.StringUtil; |
| import org.apache.openjpa.meta.AccessCode; |
| import org.apache.openjpa.meta.ClassMetaData; |
| import org.apache.openjpa.meta.DelegatingMetaDataFactory; |
| import org.apache.openjpa.meta.FieldMetaData; |
| import org.apache.openjpa.meta.JavaTypes; |
| import org.apache.openjpa.meta.MetaDataFactory; |
| import org.apache.openjpa.meta.MetaDataModes; |
| import org.apache.openjpa.meta.MetaDataRepository; |
| import org.apache.openjpa.util.InvalidStateException; |
| import org.apache.openjpa.util.UserException; |
| |
| import serp.bytecode.BCClass; |
| import serp.bytecode.BCClassLoader; |
| import serp.bytecode.Project; |
| |
| /** |
| * Generates a class appropriate for use as an application identity class. |
| * |
| * @author Patrick Linskey |
| * @author Abe White |
| */ |
| public class ApplicationIdTool { |
| |
| public static final String TOKEN_DEFAULT = "::"; |
| |
| private static final String TOKENIZER_CUSTOM = "Tokenizer"; |
| private static final String TOKENIZER_STD = "StringTokenizer"; |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (ApplicationIdTool.class); |
| |
| private final Log _log; |
| private final Class _type; |
| private final ClassMetaData _meta; |
| private boolean _abstract = false; |
| private FieldMetaData[] _fields = null; |
| private boolean _ignore = true; |
| private File _dir = null; |
| private Writer _writer = null; |
| private String _code = null; |
| private String _token = TOKEN_DEFAULT; |
| private CodeFormat _format = null; |
| |
| /** |
| * Constructs a new ApplicationIdTool capable of generating an |
| * object id class for <code>type</code>. |
| */ |
| public ApplicationIdTool(OpenJPAConfiguration conf, Class type) { |
| _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE); |
| _type = type; |
| |
| MetaDataRepository repos = conf.newMetaDataRepositoryInstance(); |
| repos.setValidate(MetaDataRepository.VALIDATE_NONE); |
| repos.setSourceMode(MetaDataModes.MODE_MAPPING, false); |
| loadObjectIds(repos, true); |
| _meta = repos.getMetaData(type, null, false); |
| if (_meta != null) { |
| _abstract = Modifier.isAbstract(_meta.getDescribedType(). |
| getModifiers()); |
| _fields = getDeclaredPrimaryKeyFields(_meta); |
| } |
| } |
| |
| /** |
| * Constructs a new tool instance capable of generating an |
| * object id class for <code>meta</code>. |
| */ |
| public ApplicationIdTool(OpenJPAConfiguration conf, Class type, |
| ClassMetaData meta) { |
| _log = conf.getLog(OpenJPAConfiguration.LOG_ENHANCE); |
| |
| _type = type; |
| _meta = meta; |
| if (_meta != null) { |
| _abstract = Modifier.isAbstract(_meta.getDescribedType(). |
| getModifiers()); |
| _fields = getDeclaredPrimaryKeyFields(_meta); |
| } |
| } |
| |
| /** |
| * Return metadata for primary key fields declared in the given class. |
| */ |
| private static FieldMetaData[] getDeclaredPrimaryKeyFields |
| (ClassMetaData meta) { |
| if (meta.getPCSuperclass() == null) |
| return meta.getPrimaryKeyFields(); |
| |
| // remove the primary key fields that are not declared |
| // in the current class |
| FieldMetaData[] fields = meta.getPrimaryKeyFields(); |
| List decs = new ArrayList(fields.length); |
| for (FieldMetaData field : fields) |
| if (field.getDeclaringType() == meta.getDescribedType()) |
| decs.add(field); |
| return (FieldMetaData[]) decs.toArray(new FieldMetaData[decs.size()]); |
| } |
| |
| /** |
| * Return false if this tool is configured to throw an exception on |
| * an attempt to generate an id class for a type that does not use |
| * application identity. |
| */ |
| public boolean getIgnoreErrors() { |
| return _ignore; |
| } |
| |
| /** |
| * Set to false if this tool should throw an exception on |
| * an attempt to generate an id class for a type that does not use |
| * application identity. |
| */ |
| public void setIgnoreErrors(boolean ignore) { |
| _ignore = ignore; |
| } |
| |
| /** |
| * The code formatter for the generated Java code. |
| */ |
| public CodeFormat getCodeFormat() { |
| return _format; |
| } |
| |
| /** |
| * Set the code formatter for the generated Java code. |
| */ |
| public void setCodeFormat(CodeFormat format) { |
| _format = format; |
| } |
| |
| /** |
| * The directory to write source to. Defaults to the directory |
| * of the Java file for the set type. If the given directory does not |
| * match the package of the object id, the package structure will be |
| * created below the directory. |
| */ |
| public File getDirectory() { |
| return _dir; |
| } |
| |
| /** |
| * The directory to write source to. Defaults to the directory |
| * of the Java file for the set type. If the given directory does not |
| * match the package of the object id, the package structure will be |
| * created below the directory. |
| */ |
| public void setDirectory(File dir) { |
| _dir = dir; |
| } |
| |
| /** |
| * The token to use to separate stringified primary key field values. |
| */ |
| public String getToken() { |
| return _token; |
| } |
| |
| /** |
| * The token to use to separate stringified primary key field values. |
| */ |
| public void setToken(String token) { |
| _token = token; |
| } |
| |
| /** |
| * The writer to write source to, or null to write to default file. |
| */ |
| public Writer getWriter() { |
| return _writer; |
| } |
| |
| /** |
| * The writer to write source to, or null to write to default file. |
| */ |
| public void setWriter(Writer writer) { |
| _writer = writer; |
| } |
| |
| /** |
| * Return the type we are generating an application id for. |
| */ |
| public Class getType() { |
| return _type; |
| } |
| |
| /** |
| * Return metadata for the type we are generating an application id for. |
| */ |
| public ClassMetaData getMetaData() { |
| return _meta; |
| } |
| |
| /** |
| * Return the code generated for the application id, or null |
| * if invalid class or the {@link #run} method has not been called. |
| */ |
| public String getCode() { |
| return _code; |
| } |
| |
| /** |
| * Returns true if the application identity class should be an inner class. |
| */ |
| public boolean isInnerClass() { |
| Class oidClass = _meta.getObjectIdType(); |
| return oidClass.getName().indexOf('$') != -1; |
| } |
| |
| /** |
| * Returns the short class name for the object id class. |
| */ |
| private String getClassName() { |
| if (_meta.isOpenJPAIdentity()) |
| return null; |
| |
| // convert from SomeClass$ID to ID |
| String className = ClassUtil.getClassName(_meta.getObjectIdType()); |
| if (isInnerClass()) |
| className = className.substring(className.lastIndexOf('$') + 1); |
| return className; |
| } |
| |
| /** |
| * Generates the sourcecode for the application id class; returns |
| * false if the class is invalid. |
| */ |
| public boolean run() { |
| if (_log.isInfoEnabled()) |
| _log.info(_loc.get("appid-start", _type)); |
| |
| // ensure that this type is a candidate for application identity |
| if (_meta == null |
| || _meta.getIdentityType() != ClassMetaData.ID_APPLICATION |
| || _meta.isOpenJPAIdentity()) { |
| if (!_ignore) |
| throw new UserException(_loc.get("appid-invalid", _type)); |
| |
| // else just warn |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("appid-warn", _type)); |
| return false; |
| } |
| |
| Class oidClass = _meta.getObjectIdType(); |
| Class superOidClass = null; |
| |
| // allow diff oid class in subclass (horizontal) |
| if (_meta.getPCSuperclass() != null) { |
| superOidClass = _meta.getPCSuperclassMetaData().getObjectIdType(); |
| if (oidClass == null || oidClass.equals(superOidClass)) { |
| // just warn |
| if (_log.isWarnEnabled()) |
| _log.warn(_loc.get("appid-warn", _type)); |
| return false; |
| } |
| } |
| |
| // ensure that an id class is declared |
| if (oidClass == null) |
| throw new UserException(_loc.get("no-id-class", _type)). |
| setFatal(true); |
| |
| // ensure there is at least one pk field if we are |
| // non-absract, and see if we have any byte[] |
| boolean bytes = false; |
| for (int i = 0; !bytes && i < _fields.length; i++) |
| bytes = _fields[i].getDeclaredType() == byte[].class; |
| |
| // collect info on id type |
| String className = getClassName(); |
| String packageName = ClassUtil.getPackageName(oidClass); |
| String packageDec = ""; |
| if (packageName.length() > 0) |
| packageDec = "package " + packageName + ";"; |
| |
| String imports = getImports(); |
| String fieldDecs = getFieldDeclarations(); |
| String constructor = getConstructor(superOidClass != null); |
| String properties = getProperties(); |
| String fromStringCode = getFromStringCode(superOidClass != null); |
| String toStringCode = getToStringCode(superOidClass != null); |
| String equalsCode = getEqualsCode(superOidClass != null); |
| String hashCodeCode = getHashCodeCode(superOidClass != null); |
| |
| // build the java code |
| CodeFormat code = newCodeFormat(); |
| if (!isInnerClass() && packageDec.length() > 0) |
| code.append(packageDec).afterSection(); |
| |
| if (!isInnerClass() && imports.length() > 0) |
| code.append(imports).afterSection(); |
| |
| code.append("/**").endl(). |
| append(" * "). |
| append(_loc.get("appid-comment-for", _type.getName())). |
| endl(). |
| append(" *").endl(). |
| append(" * ").append(_loc.get("appid-comment-gen")).endl(). |
| append(" * ").append(getClass().getName()).endl(). |
| append(" */").endl(); |
| code.append("public "); |
| if (isInnerClass()) |
| code.append("static "); |
| code.append("class ").append(className); |
| if (code.getBraceOnSameLine()) |
| code.append(" "); |
| else |
| code.endl().tab(); |
| |
| if (superOidClass != null) { |
| code.append("extends " + ClassUtil.getClassName(superOidClass)); |
| if (code.getBraceOnSameLine()) |
| code.append(" "); |
| else |
| code.endl().tab(); |
| } |
| code.append("implements Serializable").openBrace(1).endl(); |
| |
| // if we use a byte array we need a static array for encoding to string |
| if (bytes) { |
| code.tab().append("private static final char[] HEX = "). |
| append("new char[] {").endl(); |
| code.tab(2).append("'0', '1', '2', '3', '4', '5', '6', '7',"). |
| endl(); |
| code.tab(2).append("'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'"). |
| endl(); |
| code.tab().append("};").endl(2); |
| } |
| |
| // static block to register class |
| code.tab().append("static").openBrace(2).endl(); |
| code.tab(2).append("// register persistent class in JVM").endl(); |
| code.tab(2).append("try { Class.forName").openParen(true). |
| append("\"").append(_type.getName()).append("\""). |
| closeParen().append(";").append(" }").endl(); |
| code.tab(2).append("catch").openParen(true). |
| append("Exception e").closeParen().append(" {}").endl(); |
| |
| code.closeBrace(2); |
| |
| // field declarations |
| if (fieldDecs.length() > 0) |
| code.endl(2).append(fieldDecs); |
| |
| // default constructor |
| code.afterSection().tab().append("public ").append(className). |
| parens().openBrace(2).endl(); |
| code.closeBrace(2); |
| |
| // string constructor |
| code.afterSection().append(constructor); |
| |
| // properties |
| if (properties.length() > 0) |
| code.afterSection().append(properties); |
| |
| // toString, equals, hashCode methods |
| if (toStringCode.length() > 0) |
| code.afterSection().append(toStringCode); |
| if (hashCodeCode.length() > 0) |
| code.afterSection().append(hashCodeCode); |
| if (equalsCode.length() > 0) |
| code.afterSection().append(equalsCode); |
| if (fromStringCode.length() > 0) |
| code.afterSection().append(fromStringCode); |
| |
| // if we have any byte array fields, we have to add the extra |
| // methods for handling byte arrays |
| if (bytes) { |
| code.afterSection().append(getToBytesByteArrayCode()); |
| code.afterSection().append(getToStringByteArrayCode()); |
| code.afterSection().append(getEqualsByteArrayCode()); |
| code.afterSection().append(getHashCodeByteArrayCode()); |
| } |
| |
| // base classes might need to define a custom tokenizer |
| if (superOidClass == null && getTokenizer(false) == TOKENIZER_CUSTOM) |
| code.afterSection().append(getCustomTokenizerClass()); |
| |
| code.endl(); |
| code.closeBrace(1); |
| |
| _code = code.toString(); |
| |
| // if this is an inner class, then indent the entire |
| // code unit one tab level |
| if (isInnerClass()) { |
| // indent the entire code block one level to make it |
| // a propertly indented innder class |
| _code = code.getTab() + |
| StringUtil.replace(_code, |
| J2DoPrivHelper.getLineSeparator(), |
| J2DoPrivHelper.getLineSeparator() + code.getTab()); |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Writes the generated code to the proper file. |
| */ |
| public void record() |
| throws IOException { |
| if (_code == null) |
| return; |
| |
| Writer writer = _writer; |
| if (writer == null) { |
| File file = getFile(); |
| Files.backup(file, false); |
| writer = new FileWriter(file); |
| } |
| |
| PrintWriter printer = new PrintWriter(writer); |
| printer.print(_code); |
| printer.flush(); |
| |
| if (_writer == null) |
| writer.close(); |
| } |
| |
| /** |
| * Return the necessary imports for the class. |
| */ |
| private String getImports() { |
| Set pkgs = getImportPackages(); |
| |
| CodeFormat imports = newCodeFormat(); |
| String base = ClassUtil.getPackageName(_meta.getObjectIdType()); |
| String pkg; |
| for (Object o : pkgs) { |
| pkg = (String) o; |
| if (pkg.length() > 0 && !"java.lang".equals(pkg) |
| && !base.equals(pkg)) { |
| if (imports.length() > 0) |
| imports.endl(); |
| imports.append("import ").append(pkg).append(".*;"); |
| } |
| } |
| return imports.toString(); |
| } |
| |
| /** |
| * Returns the collection of packages that need to be imported. |
| */ |
| public Set getImportPackages() { |
| Set pkgs = new TreeSet(); |
| pkgs.add(ClassUtil.getPackageName(_type)); |
| |
| Class superOidClass = null; |
| if (_meta != null && _meta.getPCSuperclassMetaData() != null) |
| superOidClass = _meta.getPCSuperclassMetaData().getObjectIdType(); |
| if (superOidClass != null) |
| pkgs.add(ClassUtil.getPackageName(superOidClass)); |
| |
| pkgs.add("java.io"); |
| pkgs.add("java.util"); |
| Class type; |
| for (FieldMetaData field : _fields) { |
| type = field.getObjectIdFieldType(); |
| if (type != byte[].class && type != char[].class |
| && !type.getName().startsWith("java.sql.")) { |
| pkgs.add(ClassUtil.getPackageName(type)); |
| } |
| } |
| return pkgs; |
| } |
| |
| /** |
| * Return the code to declare all primary key fields. |
| */ |
| private String getFieldDeclarations() { |
| CodeFormat code = newCodeFormat(); |
| for (int i = 0; i < _fields.length; i++) { |
| if (i > 0) |
| code.endl(); |
| code.tab().append("public ").append(getTypeName(_fields[i])). |
| append(" ").append(_fields[i].getName()).append(";"); |
| } |
| return code.toString(); |
| } |
| |
| /** |
| * Return the type name to declare the given field as. |
| */ |
| private String getTypeName(FieldMetaData fmd) { |
| Class type = fmd.getObjectIdFieldType(); |
| if (type == byte[].class) |
| return "byte[]"; |
| if (type == char[].class) |
| return "char[]"; |
| if (type.getName().startsWith("java.sql.")) |
| return type.getName(); |
| return ClassUtil.getClassName(type); |
| } |
| |
| /** |
| * Return the getters and setters for all primary key fields. |
| */ |
| private String getProperties() { |
| if (AccessCode.isExplicit(_meta.getAccessType()) |
| && AccessCode.isField(_meta.getAccessType())) |
| return ""; |
| |
| CodeFormat code = newCodeFormat(); |
| String propName; |
| String typeName; |
| for (int i = 0; i < _fields.length; i++) { |
| if (i > 0) |
| code.afterSection(); |
| typeName = getTypeName(_fields[i]); |
| propName = StringUtil.capitalize(_fields[i].getName()); |
| |
| code.tab().append("public ").append(typeName).append(" "); |
| if (_fields[i].getDeclaredTypeCode() == JavaTypes.BOOLEAN |
| || _fields[i].getDeclaredTypeCode() == JavaTypes.BOOLEAN_OBJ) |
| code.append("is"); |
| else |
| code.append("get"); |
| code.append(propName).parens().openBrace(2).endl(); |
| code.tab(2).append("return ").append(_fields[i].getName()). |
| append(";").endl(); |
| code.closeBrace(2); |
| code.afterSection(); |
| |
| code.tab().append("public void set").append(propName); |
| code.openParen(true).append(typeName).append(" "). |
| append(_fields[i].getName()).closeParen(); |
| code.openBrace(2).endl(); |
| code.tab(2).append("this.").append(_fields[i].getName()). |
| append(" = ").append(_fields[i].getName()).append(";"). |
| endl(); |
| code.closeBrace(2); |
| } |
| return code.toString(); |
| } |
| |
| /** |
| * Return the string constructor code. |
| */ |
| private String getConstructor(boolean hasSuperclass) { |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("public "); |
| code.append(getClassName()); |
| code.openParen(true).append("String str").closeParen(); |
| code.openBrace(2).endl(); |
| |
| if (_fields.length != 0 || (hasSuperclass |
| && _meta.getPrimaryKeyFields().length > 0)) { |
| code.tab(2).append("fromString").openParen(true). |
| append("str").closeParen().append(";").endl(); |
| } |
| |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Create the fromString method that parses the result of our toString |
| * method. If we have superclasses with id fields, this will call |
| * super.fromString() so that the parent class can parse its own fields. |
| */ |
| private String getFromStringCode(boolean hasSuperclass) { |
| // if we are below a concrete class then we cannot declare any |
| // more primary key fields; thus, just use the parent invocation |
| if (hasConcreteSuperclass()) |
| return ""; |
| if (_fields.length == 0) |
| return ""; |
| hasSuperclass = hasSuperclass && getDeclaredPrimaryKeyFields |
| (_meta.getPCSuperclassMetaData()).length > 0; |
| |
| String toke = getTokenizer(hasSuperclass); |
| CodeFormat code = newCodeFormat(); |
| if (_abstract || hasSuperclass) |
| code.tab().append("protected ").append(toke). |
| append(" fromString"); |
| else |
| code.tab().append("private void fromString"); |
| code.openParen(true).append("String str").closeParen(); |
| code.openBrace(2).endl(); |
| |
| // if we have any Object-type fields, die immediately |
| for (FieldMetaData fieldMetaData : _fields) { |
| if (fieldMetaData.getObjectIdFieldType() != Object.class) |
| continue; |
| code.tab(2).append("throw new UnsupportedOperationException"). |
| parens().append(";").endl(); |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| if (toke != null) { |
| code.tab(2).append(toke).append(" toke = "); |
| if (hasSuperclass) { |
| // call super.fromString(str) to get the tokenizer that was |
| // used to parse the superclass |
| code.append("super.fromString").openParen(true). |
| append("str").closeParen(); |
| } else { |
| // otherwise construct a new tokenizer with the string |
| code.append("new ").append(toke).openParen(true). |
| append("str"); |
| if (toke == TOKENIZER_STD) |
| code.append(", \"").append(_token).append("\""); |
| code.closeParen(); |
| } |
| code.append(";").endl(); |
| } |
| |
| for (FieldMetaData field : _fields) { |
| if (toke != null) { |
| code.tab(2).append("str = toke.nextToken").parens(). |
| append(";").endl(); |
| } |
| code.tab(2).append(getConversionCode(field, "str")).endl(); |
| } |
| if (_abstract || hasSuperclass) |
| code.tab(2).append("return toke;").endl(); |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Returns the type of tokenizer to use, or null if none. |
| */ |
| private String getTokenizer(boolean hasSuperclass) { |
| if (!_abstract && !hasSuperclass && _fields.length == 1) |
| return null; |
| if (_token.length() == 1) |
| return TOKENIZER_STD; |
| return TOKENIZER_CUSTOM; |
| } |
| |
| /** |
| * Get parsing code for the given field. |
| */ |
| private String getConversionCode(FieldMetaData field, String var) { |
| CodeFormat parse = newCodeFormat(); |
| if (field.getName().equals(var)) |
| parse.append("this."); |
| parse.append(field.getName()).append(" = "); |
| |
| Class type = field.getObjectIdFieldType(); |
| if (type == Date.class) { |
| parse.append("new Date").openParen(true). |
| append("Long.parseLong").openParen(true). |
| append(var).closeParen().closeParen(); |
| } else if (type == java.sql.Date.class |
| || type == java.sql.Timestamp.class |
| || type == java.sql.Time.class) { |
| parse.append(type.getName()).append(".valueOf").openParen(true). |
| append(var).closeParen(); |
| } else if (type == String.class) |
| parse.append(var); |
| else if (type == Character.class) { |
| parse.append("new Character").openParen(true).append(var). |
| append(".charAt").openParen(true).append(0). |
| closeParen().closeParen(); |
| } else if (type == byte[].class) |
| parse.append("toBytes").openParen(true).append(var).closeParen(); |
| else if (type == char[].class) |
| parse.append(var).append(".toCharArray").parens(); |
| else if (!type.isPrimitive()) { |
| parse.append("new ").append(ClassUtil.getClassName(type)). |
| openParen(true).append(var).closeParen(); |
| } else // primitive |
| { |
| switch (type.getName().charAt(0)) { |
| case 'b': |
| if (type == boolean.class) |
| parse.append("\"true\".equals").openParen(true). |
| append(var).closeParen(); |
| else |
| parse.append("Byte.parseByte").openParen(true). |
| append(var).closeParen(); |
| break; |
| case 'c': |
| parse.append(var).append(".charAt").openParen(true). |
| append(0).closeParen(); |
| break; |
| case 'd': |
| parse.append("Double.parseDouble").openParen(true). |
| append(var).closeParen(); |
| break; |
| case 'f': |
| parse.append("Float.parseFloat").openParen(true). |
| append(var).closeParen(); |
| break; |
| case 'i': |
| parse.append("Integer.parseInt").openParen(true). |
| append(var).closeParen(); |
| break; |
| case 'l': |
| parse.append("Long.parseLong").openParen(true). |
| append(var).closeParen(); |
| break; |
| case 's': |
| parse.append("Short.parseShort").openParen(true). |
| append(var).closeParen(); |
| break; |
| } |
| } |
| |
| if (!type.isPrimitive() && type != byte[].class) { |
| CodeFormat isNull = newCodeFormat(); |
| isNull.append("if").openParen(true).append("\"null\".equals"). |
| openParen(true).append(var).closeParen().closeParen(). |
| endl().tab(3); |
| if (field.getName().equals(var)) |
| isNull.append("this."); |
| isNull.append(field.getName()).append(" = null;").endl(); |
| isNull.tab(2).append("else").endl(); |
| isNull.tab(3).append(parse); |
| parse = isNull; |
| } |
| |
| return parse.append(";").toString(); |
| } |
| |
| /** |
| * Return an equality method that compares all pk variables. |
| * Must deal correctly with both primitives and objects. |
| */ |
| private String getEqualsCode(boolean hasSuperclass) { |
| // if we are below a concrete class then we cannot declare any |
| // more primary key fields; thus, just use the parent invocation |
| if (hasConcreteSuperclass() || (hasSuperclass && _fields.length == 0)) |
| return ""; |
| |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("public boolean equals").openParen(true). |
| append("Object obj").closeParen().openBrace(2).endl(); |
| |
| code.tab(2).append("if").openParen(true). |
| append("this == obj").closeParen().endl(); |
| code.tab(3).append("return true;").endl(); |
| |
| // call super.equals() if we have a superclass |
| String className = getClassName(); |
| if (hasSuperclass) { |
| code.tab(2).append("if").openParen(true). |
| append("!super.equals").openParen(true). |
| append("obj").closeParen().closeParen().endl(); |
| code.tab(3).append("return false;").endl(); |
| } else { |
| code.tab(2).append("if").openParen(true). |
| append("obj == null || obj.getClass").parens(). |
| append(" != ").append("getClass").parens(). |
| closeParen().endl(); |
| code.tab(3).append("return false;").endl(); |
| } |
| |
| String name; |
| Class type; |
| for (int i = 0; i < _fields.length; i++) { |
| if (i == 0) { |
| code.endl().tab(2).append(className).append(" other = "). |
| openParen(false).append(className).closeParen(). |
| append(" obj;").endl(); |
| } |
| |
| // if this is not the first field, add an && |
| if (i == 0) |
| code.tab(2).append("return "); |
| else |
| code.endl().tab(3).append("&& "); |
| |
| name = _fields[i].getName(); |
| type = _fields[i].getObjectIdFieldType(); |
| if (type.isPrimitive()) { |
| code.openParen(false).append(name).append(" == "). |
| append("other.").append(name).closeParen(); |
| } else if (type == byte[].class) { |
| code.openParen(false).append("equals").openParen(true). |
| append(name).append(", ").append("other."). |
| append(name).closeParen().closeParen(); |
| } else if (type == char[].class) { |
| // ((name == null && other.name == null) |
| // || (name != null && String.valueOf (name). |
| // equals (String.valueOf (other.name)))) |
| code.append("(").openParen(false).append(name). |
| append(" == null && other.").append(name). |
| append(" == null").closeParen().endl(); |
| code.tab(3).append("|| "); |
| code.openParen(false).append(name).append(" != null "). |
| append("&& String.valueOf").openParen(true).append(name). |
| closeParen().append(".").endl(); |
| code.tab(3).append("equals").openParen(true). |
| append("String.valueOf").openParen(true). |
| append("other.").append(name).closeParen().closeParen(). |
| closeParen().append(")"); |
| } else { |
| // ((name == null && other.name == null) |
| // || (name != null && name.equals (other.name))) |
| code.append("(").openParen(false).append(name). |
| append(" == null && other.").append(name). |
| append(" == null").closeParen().endl(); |
| code.tab(3).append("|| "); |
| code.openParen(false).append(name).append(" != null "). |
| append("&& ").append(name).append(".equals"). |
| openParen(true).append("other.").append(name). |
| closeParen().closeParen().append(")"); |
| } |
| } |
| |
| // no _fields: just return true after checking instanceof |
| if (_fields.length == 0) |
| code.tab(2).append("return true;").endl(); |
| else |
| code.append(";").endl(); |
| |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Return a hashCode method that takes into account all |
| * primary key values. Must deal correctly with both primitives and objects. |
| */ |
| private String getHashCodeCode(boolean hasSuperclass) { |
| // if we are below a concrete class then we cannot declare any |
| // more primary key fields; thus, just use the parent invocation |
| if (hasConcreteSuperclass() || (hasSuperclass && _fields.length == 0)) |
| return ""; |
| |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("public int hashCode").parens(). |
| openBrace(2).endl(); |
| |
| if (_fields.length == 0) |
| code.tab(2).append("return 17;").endl(); |
| else if (_fields.length == 1 && !hasSuperclass) { |
| code.tab(2).append("return "); |
| appendHashCodeCode(_fields[0], code); |
| code.append(";").endl(); |
| } else { |
| code.tab(2).append("int rs = "); |
| if (hasSuperclass) { |
| // call super.hashCode() if we have a superclass |
| code.append("super.hashCode").openParen(true). |
| closeParen().append(";"); |
| } else |
| code.append("17;"); |
| code.endl(); |
| |
| for (FieldMetaData field : _fields) { |
| code.tab(2).append("rs = rs * 37 + "); |
| appendHashCodeCode(field, code); |
| code.append(";").endl(); |
| } |
| code.tab(2).append("return rs;").endl(); |
| } |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Return true if this class has a concrete superclass. |
| */ |
| private boolean hasConcreteSuperclass() { |
| for (ClassMetaData sup = _meta.getPCSuperclassMetaData(); |
| sup != null; sup = sup.getPCSuperclassMetaData()) { |
| if (!Modifier.isAbstract(sup.getDescribedType().getModifiers())) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Append code calculating the hashcode for the given field. |
| */ |
| private void appendHashCodeCode(FieldMetaData field, CodeFormat code) { |
| String name = field.getName(); |
| if ("rs".equals(name)) |
| name = "this." + name; |
| Class type = field.getObjectIdFieldType(); |
| if (type.isPrimitive()) { |
| if (type == boolean.class) { |
| // ((name) ? 1 : 0) |
| code.append("(").openParen(false).append(name).closeParen(). |
| append(" ? 1 : 0").append(")"); |
| } else if (type == long.class) { |
| // (int) (name ^ (name >>> 32)) |
| code.openParen(false).append("int").closeParen(). |
| append(" ").openParen(false).append(name). |
| append(" ^ ").openParen(false).append(name). |
| append(" >>> 32").closeParen().closeParen(); |
| } else if (type == double.class) { |
| // (int) (Double.doubleToLongBits (name) |
| // ^ (Double.doubleToLongBits (name) >>> 32)) |
| code.openParen(false).append("int").closeParen(). |
| append(" ").openParen(false). |
| append("Double.doubleToLongBits").openParen(true). |
| append(name).closeParen().endl(); |
| code.tab(3).append("^ ").openParen(false). |
| append("Double.doubleToLongBits").openParen(true). |
| append(name).closeParen().append(" >>> 32"). |
| closeParen().closeParen(); |
| } else if (type == float.class) { |
| // Float.floatToIntBits (name) |
| code.append("Float.floatToIntBits").openParen(true). |
| append(name).closeParen(); |
| } else if (type == int.class) |
| code.append(name); |
| else { |
| // (int) name |
| code.openParen(false).append("int").closeParen(). |
| append(" ").append(name); |
| } |
| } else if (type == byte[].class) { |
| // hashCode (name); |
| code.append("hashCode").openParen(true).append(name). |
| closeParen(); |
| } else if (type == char[].class) { |
| // ((name == null) ? 0 : String.valueOf (name).hashCode ()) |
| code.append("(").openParen(false).append(name). |
| append(" == null").closeParen().append(" ? 0 : "). |
| append("String.valueOf").openParen(true).append(name). |
| closeParen().append(".hashCode").parens().append(")"); |
| } else { |
| // ((name == null) ? 0 : name.hashCode ()) |
| code.append("(").openParen(false).append(name). |
| append(" == null").closeParen().append(" ? 0 : "). |
| append(name).append(".hashCode").parens().append(")"); |
| } |
| } |
| |
| /** |
| * Return a method to create a string containing the primary key |
| * values that define the application id object. |
| */ |
| private String getToStringCode(boolean hasSuperclass) { |
| // if we are below a concrete class then we cannot declare any |
| // more primary key fields; thus, just use the parent invocation |
| if (hasConcreteSuperclass() || (hasSuperclass && _fields.length == 0)) |
| return ""; |
| |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("public String toString").parens(). |
| openBrace(2).endl(); |
| |
| String name; |
| Class type; |
| String appendDelimiter = "+ \"" + _token + "\" + "; |
| for (int i = 0; i < _fields.length; i++) { |
| // if this is not the first field, add a + |
| if (i == 0) { |
| code.tab(2).append("return "); |
| |
| // add in the super.toString() if we have a parent |
| if (hasSuperclass && getDeclaredPrimaryKeyFields |
| (_meta.getPCSuperclassMetaData()).length > 0) { |
| code.append("super.toString").parens(); |
| code.endl().tab(3).append(appendDelimiter); |
| } |
| } else |
| code.endl().tab(3).append(appendDelimiter); |
| |
| name = _fields[i].getName(); |
| type = _fields[i].getObjectIdFieldType(); |
| if (type == String.class) |
| code.append(name); |
| else if (type == byte[].class) |
| code.append("toString").openParen(true). |
| append(name).closeParen(); |
| else if (type == char[].class) |
| code.openParen(true).openParen(true).append(name). |
| append(" == null").closeParen().append(" ? \"null\""). |
| append(": String.valueOf").openParen(true). |
| append(name).closeParen().closeParen(); |
| else if (type == Date.class) |
| code.openParen(true).openParen(true).append(name). |
| append(" == null").closeParen().append(" ? \"null\""). |
| endl().tab(4).append(": String.valueOf"). |
| openParen(true).append(name).append(".getTime"). |
| parens().closeParen().closeParen(); |
| else |
| code.append("String.valueOf").openParen(true). |
| append(name).closeParen(); |
| } |
| |
| // no fields; just use "" |
| if (_fields.length == 0) |
| code.tab(2).append("return \"\""); |
| code.append(";").endl(); |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Code to convert a string to a byte array. |
| * |
| * @see org.apache.openjpa.lib.util.Base16Encoder#decode |
| */ |
| private String getToBytesByteArrayCode() { |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("private static byte[] toBytes").openParen(true). |
| append("String s").closeParen().openBrace(2).endl(); |
| |
| code.tab(2).append("if").openParen(true).append("\"null\".equals"). |
| openParen(true).append("s").closeParen().closeParen().endl(); |
| code.tab(3).append("return null;").endl(2); |
| |
| code.tab(2).append("int len = s.length").parens(). |
| append(";").endl(); |
| code.tab(2).append("byte[] r = new byte[len / 2];").endl(); |
| code.tab(2).append("for").openParen(true). |
| append("int i = 0; i < r.length; i++").closeParen(). |
| openBrace(3).endl(); |
| code.tab(3).append("int digit1 = s.charAt").openParen(true). |
| append("i * 2").closeParen().append(", "). |
| append("digit2 = s.charAt").openParen(true). |
| append("i * 2 + 1").closeParen().append(";").endl(); |
| code.tab(3).append("if").openParen(true). |
| append("digit1 >= '0' && digit1 <= '9'").closeParen().endl(); |
| code.tab(4).append("digit1 -= '0';").endl(); |
| code.tab(3).append("else if").openParen(true). |
| append("digit1 >= 'A' && digit1 <= 'F'").closeParen().endl(); |
| code.tab(4).append("digit1 -= 'A' - 10;").endl(); |
| code.tab(3).append("if").openParen(true). |
| append("digit2 >= '0' && digit2 <= '9'").closeParen().endl(); |
| code.tab(4).append("digit2 -= '0';").endl(); |
| code.tab(3).append("else if").openParen(true). |
| append("digit2 >= 'A' && digit2 <= 'F'").closeParen().endl(); |
| code.tab(4).append("digit2 -= 'A' - 10;").endl(2); |
| code.tab(3).append("r[i] = (byte) ").openParen(false). |
| openParen(false).append("digit1 << 4").closeParen(). |
| append(" + digit2").closeParen().append(";").endl(); |
| code.closeBrace(3).endl(); |
| code.tab(2).append("return r;").endl(); |
| |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Code to convert a byte array to a string. |
| * |
| * @see org.apache.openjpa.lib.util.Base16Encoder#encode |
| */ |
| private String getToStringByteArrayCode() { |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("private static String toString").openParen(true). |
| append("byte[] b").closeParen().openBrace(2).endl(); |
| |
| code.tab(2).append("if").openParen(true). |
| append("b == null").closeParen().endl(); |
| code.tab(3).append("return \"null\";").endl(2); |
| |
| code.tab(2).append("StringBuilder r = new StringBuilder"). |
| openParen(true).append("b.length * 2").closeParen(). |
| append(";").endl(); |
| code.tab(2).append("for").openParen(true). |
| append("int i = 0; i < b.length; i++").closeParen().endl(); |
| code.tab(3).append("for").openParen(true). |
| append("int j = 1; j >= 0; j--").closeParen().endl(); |
| code.tab(4).append("r.append").openParen(true). |
| append("HEX[").openParen(false).append("b[i] >> "). |
| openParen(false).append("j * 4").closeParen().closeParen(). |
| append(" & 0xF]").closeParen().append(";").endl(); |
| code.tab(2).append("return r.toString").parens(). |
| append(";").endl(); |
| |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Code to test if two byte arrays are equal. |
| */ |
| private String getEqualsByteArrayCode() { |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("private static boolean equals").openParen(true). |
| append("byte[] b1, byte[] b2").closeParen().openBrace(2).endl(); |
| |
| code.tab(2).append("if").openParen(true). |
| append("b1 == null && b2 == null").closeParen().endl(); |
| code.tab(3).append("return true;").endl(); |
| code.tab(2).append("if").openParen(true). |
| append("b1 == null || b2 == null").closeParen().endl(); |
| code.tab(3).append("return false;").endl(); |
| code.tab(2).append("if").openParen(true). |
| append("b1.length != b2.length").closeParen().endl(); |
| code.tab(3).append("return false;").endl(); |
| code.tab(2).append("for").openParen(true). |
| append("int i = 0; i < b1.length; i++").closeParen().endl(); |
| code.tab(3).append("if").openParen(true). |
| append("b1[i] != b2[i]").closeParen().endl(); |
| code.tab(4).append("return false;").endl(); |
| code.tab(2).append("return true;").endl(); |
| |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| private String getHashCodeByteArrayCode() { |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("private static int hashCode").openParen(true). |
| append("byte[] b").closeParen().openBrace(2).endl(); |
| |
| code.tab(2).append("if").openParen(true).append("b == null"). |
| closeParen().endl(); |
| code.tab(3).append("return 0;").endl(); |
| code.tab(2).append("int sum = 0;").endl(); |
| code.tab(2).append("for").openParen(true). |
| append("int i = 0; i < b.length; i++").closeParen().endl(); |
| code.tab(3).append("sum += b[i];").endl(); |
| code.tab(2).append("return sum;").endl(); |
| |
| code.closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Code defining a tokenizer class. |
| */ |
| private String getCustomTokenizerClass() { |
| CodeFormat code = newCodeFormat(); |
| code.tab().append("protected static class "). |
| append(TOKENIZER_CUSTOM).openBrace(2).endl(); |
| |
| code.tab(2).append("private final String str;").endl(); |
| code.tab(2).append("private int last;").afterSection(); |
| |
| code.tab(2).append("public Tokenizer (String str)"). |
| openBrace(3).endl(); |
| code.tab(3).append("this.str = str;").endl(); |
| code.closeBrace(3).afterSection(); |
| |
| code.tab(2).append("public String nextToken ()"). |
| openBrace(3).endl(); |
| code.tab(3).append("int next = str.indexOf").openParen(true). |
| append("\"").append(_token).append("\", last").closeParen(). |
| append(";").endl(); |
| code.tab(3).append("String part;").endl(); |
| code.tab(3).append("if").openParen(true).append("next == -1"). |
| closeParen().openBrace(4).endl(); |
| code.tab(4).append("part = str.substring").openParen(true). |
| append("last").closeParen().append(";").endl(); |
| code.tab(4).append("last = str.length").parens().append(";"). |
| endl().closeBrace(4); |
| if (!code.getBraceOnSameLine()) |
| code.endl().tab(3); |
| else |
| code.append(" "); |
| code.append("else").openBrace(4).endl(); |
| code.tab(4).append("part = str.substring").openParen(true). |
| append("last, next").closeParen().append(";").endl(); |
| code.tab(4).append("last = next + ").append(_token.length()). |
| append(";").endl().closeBrace(4).endl(); |
| |
| code.tab(3).append("return part;").endl(); |
| code.closeBrace(3); |
| code.endl().closeBrace(2); |
| return code.toString(); |
| } |
| |
| /** |
| * Return the file that this tool should output to. |
| */ |
| private File getFile() { |
| if (_meta == null) |
| return null; |
| |
| String packageName = ClassUtil.getPackageName(_meta.getObjectIdType()); |
| String fileName = ClassUtil.getClassName(_meta.getObjectIdType()) |
| + ".java"; |
| |
| // if pc class in same package as oid class, try to find pc .java file |
| File dir = null; |
| if (_dir == null && ClassUtil.getPackageName(_type).equals(packageName)) { |
| dir = Files.getSourceFile(_type); |
| if (dir != null) |
| dir = dir.getParentFile(); |
| } |
| if (dir == null) |
| dir = Files.getPackageFile(_dir, packageName, true); |
| return new File(dir, fileName); |
| } |
| |
| /** |
| * Return a copy of the correct code format. |
| */ |
| private CodeFormat newCodeFormat() { |
| return (_format == null) ? new CodeFormat() |
| : (CodeFormat) _format.clone(); |
| } |
| |
| /** |
| * Usage: java org.apache.openjpa.enhance.ApplicationIdTool [option]* |
| * <class name | .java file | .class file | .jdo file>+ |
| * Where the following options are recognized. |
| * <ul> |
| * <li><i>-properties/-p <properties file></i>: The path to a OpenJPA |
| * properties file containing information as outlined in |
| * {@link Configuration}; optional.</li> |
| * <li><i>-<property name> <property value></i>: All bean |
| * properties of the standard OpenJPA {@link OpenJPAConfiguration} can be |
| * set by using their names and supplying a value.</li> |
| * <li><i>-directory/-d <output directory></i>: Path to the base |
| * source directory. The package structure will be created beneath |
| * this directory if necessary. If not specified, the tool will try |
| * to locate the .java file in the CLASSPATH and output to the same |
| * directory; failing that, it will use the current directory as |
| * the base directory. |
| * <li><i>-ignoreErrors/-i <true/t | false/f></i>: If false, an |
| * exception will be thrown if the tool encounters any class that |
| * does not use application identity or uses the identity class of |
| * its superclass; defaults to true.</li> |
| * <li><i>-token/-t <token></i>: The token to use to separate |
| * stingified primary key field values in the stringified oid.</li> |
| * <li><i>-name/-n <id class name></i>: The name of the identity |
| * class to generate. If this option is specified, you must only |
| * give a single class argument. If the class metadata names an object |
| * id class, this argument is ignored.</li> |
| * <li><i>-suffix/-s <id class suffix></i>: A string to suffix each |
| * persistent class with to create the identity class name. This is |
| * overridden by <code>-name</code> or by any identity class name |
| * specified in metadata.</li> |
| * <li><i>-codeFormat/-cf.<property name> < property value></i> |
| * : Arguments like this will be used to configure the bean |
| * properties of the internal {@link CodeFormat}.</li> |
| * </ul> |
| * Each additional argument can be either the full class name of the |
| * type to create an id class for, the path to the .java file for the type, |
| * the path to the .class file for the type, or the path to a .jdo file |
| * listing one or more types. If a .java file already exists for an |
| * application id, it will be backed up to a file named |
| * <orig file name>~. |
| */ |
| public static void main(String[] args) |
| throws IOException, ClassNotFoundException { |
| Options opts = new Options(); |
| final String[] arguments = opts.setFromCmdLine(args); |
| boolean ret = Configurations.runAgainstAllAnchors(opts, |
| new Configurations.Runnable() { |
| @Override |
| public boolean run(Options opts) |
| throws ClassNotFoundException, IOException { |
| OpenJPAConfiguration conf = new OpenJPAConfigurationImpl(); |
| try { |
| return ApplicationIdTool.run(conf, arguments, opts); |
| } finally { |
| conf.close(); |
| } |
| } |
| }); |
| // START - ALLOW PRINT STATEMENTS |
| if (!ret) |
| System.err.println(_loc.get("appid-usage")); |
| // STOP - ALLOW PRINT STATEMENTS |
| } |
| |
| /** |
| * Run the application id tool with the given command-line and |
| * given configuration. Returns false if invalid options were given. |
| */ |
| public static boolean run(OpenJPAConfiguration conf, String[] args, |
| Options opts) |
| throws IOException, ClassNotFoundException { |
| Flags flags = new Flags(); |
| flags.ignoreErrors = opts.removeBooleanProperty |
| ("ignoreErrors", "i", flags.ignoreErrors); |
| flags.directory = Files.getFile(opts.removeProperty("directory", "d", |
| null), null); |
| flags.token = opts.removeProperty("token", "t", flags.token); |
| flags.name = opts.removeProperty("name", "n", flags.name); |
| flags.suffix = opts.removeProperty("suffix", "s", flags.suffix); |
| |
| // separate the properties for the customizer and code format |
| Options formatOpts = new Options(); |
| Map.Entry entry; |
| String key; |
| for (Iterator itr = opts.entrySet().iterator(); itr.hasNext();) { |
| entry = (Map.Entry) itr.next(); |
| key = (String) entry.getKey(); |
| if (key.startsWith("codeFormat.")) { |
| formatOpts.put(key.substring(11), entry.getValue()); |
| itr.remove(); |
| } else if (key.startsWith("cf.")) { |
| formatOpts.put(key.substring(3), entry.getValue()); |
| itr.remove(); |
| } |
| } |
| if (!formatOpts.isEmpty()) { |
| flags.format = new CodeFormat(); |
| formatOpts.setInto(flags.format); |
| } |
| |
| Configurations.populateConfiguration(conf, opts); |
| ClassLoader loader = conf.getClassResolverInstance(). |
| getClassLoader(ApplicationIdTool.class, null); |
| return run(conf, args, flags, loader); |
| } |
| |
| /** |
| * Run the tool. Returns false if invalid options were given. |
| */ |
| public static boolean run(OpenJPAConfiguration conf, String[] args, |
| Flags flags, ClassLoader loader) |
| throws IOException, ClassNotFoundException { |
| MetaDataRepository repos = conf.newMetaDataRepositoryInstance(); |
| repos.setValidate(MetaDataRepository.VALIDATE_NONE, true); |
| loadObjectIds(repos, flags.name == null && flags.suffix == null); |
| |
| Log log = conf.getLog(OpenJPAConfiguration.LOG_TOOL); |
| Collection classes; |
| if (args.length == 0) { |
| log.info(_loc.get("running-all-classes")); |
| classes = repos.loadPersistentTypes(true, loader); |
| } else { |
| ClassArgParser cap = conf.getMetaDataRepositoryInstance(). |
| getMetaDataFactory().newClassArgParser(); |
| cap.setClassLoader(loader); |
| classes = new HashSet(); |
| for (String arg : args) { |
| classes.addAll(Arrays.asList(cap.parseTypes(arg))); |
| } |
| } |
| if (flags.name != null && classes.size() > 1) |
| throw new UserException(_loc.get("name-mult-args", classes)); |
| |
| ApplicationIdTool tool; |
| Class cls; |
| ClassMetaData meta; |
| BCClassLoader bc = AccessController |
| .doPrivileged(J2DoPrivHelper.newBCClassLoaderAction(new Project())); |
| for (Object aClass : classes) { |
| cls = (Class) aClass; |
| log.info(_loc.get("appid-running", cls)); |
| |
| meta = repos.getMetaData(cls, null, false); |
| setObjectIdType(meta, flags, bc); |
| |
| tool = new ApplicationIdTool(conf, cls, meta); |
| tool.setDirectory(flags.directory); |
| tool.setIgnoreErrors(flags.ignoreErrors); |
| tool.setToken(flags.token); |
| tool.setCodeFormat(flags.format); |
| if (tool.run()) { |
| log.info(_loc.get("appid-output", tool.getFile())); |
| tool.record(); |
| } |
| else |
| log.info(_loc.get("appid-norun")); |
| } |
| bc.getProject().clear(); |
| return true; |
| } |
| |
| /** |
| * Set the object id type of the given metadata. |
| */ |
| private static void setObjectIdType(ClassMetaData meta, Flags flags, |
| BCClassLoader bc) |
| throws ClassNotFoundException { |
| if (meta == null || (meta.getObjectIdType() != null |
| && (!meta.isOpenJPAIdentity() || flags.name == null)) |
| || getDeclaredPrimaryKeyFields(meta).length == 0) |
| return; |
| |
| Class desc = meta.getDescribedType(); |
| Class cls = null; |
| if (flags.name != null) |
| cls = loadClass(desc, flags.name, bc); |
| else if (flags.suffix != null) |
| cls = loadClass(desc, desc.getName() + flags.suffix, bc); |
| meta.setObjectIdType(cls, false); |
| } |
| |
| /** |
| * Load the given class name even if it does not exist. |
| */ |
| private static Class loadClass(Class context, String name, |
| BCClassLoader bc) |
| throws ClassNotFoundException { |
| if (name.indexOf('.') == -1 && context.getName().indexOf('.') != -1) |
| name = ClassUtil.getPackageName(context) + "." + name; |
| |
| // first try with regular class loader |
| ClassLoader loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getClassLoaderAction(context)); |
| if (loader == null) |
| loader = AccessController.doPrivileged( |
| J2DoPrivHelper.getContextClassLoaderAction()); |
| try { |
| return Class.forName(name, false, loader); |
| } catch (Throwable t) { |
| } |
| |
| // create class |
| BCClass oid = bc.getProject().loadClass(name, null); |
| oid.addDefaultConstructor(); |
| return Class.forName(name, false, bc); |
| } |
| |
| /** |
| * Tell the metadata factory to load object id classes even if they don't |
| * exist. |
| */ |
| private static void loadObjectIds(MetaDataRepository repos, boolean fatal) { |
| MetaDataFactory mdf = repos.getMetaDataFactory(); |
| if (mdf instanceof DelegatingMetaDataFactory) |
| mdf = ((DelegatingMetaDataFactory) mdf).getInnermostDelegate(); |
| if (mdf instanceof ObjectIdLoader) |
| ((ObjectIdLoader) mdf).setLoadObjectIds(); |
| else if (fatal) |
| throw new InvalidStateException(_loc.get("factory-not-oidloader")). |
| setFatal(true); |
| } |
| |
| /** |
| * Run flags. |
| */ |
| public static class Flags { |
| |
| public File directory = null; |
| public boolean ignoreErrors = true; |
| public String token = TOKEN_DEFAULT; |
| public CodeFormat format = null; |
| public String name = null; |
| public String suffix = null; |
| } |
| |
| /** |
| * Interface implemented by metadata factories that can load non-existant |
| * object id classes. |
| */ |
| public interface ObjectIdLoader |
| { |
| /** |
| * Turn on the loading of all identity classes, even if they don't |
| * exist. |
| */ |
| void setLoadObjectIds (); |
| } |
| } |