Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/JDOField.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/JDOField.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/JDOField.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/JDOField.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,231 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.meta.prop; + +import java.lang.reflect.Modifier; + + +/** + * A class to hold the properties of a field. + */ +final class JDOField +{ + /** + * The name of the field. + */ + final private String name; + + /** + * The type of the field. + */ + private String type = null; + + /** + * The access modifier of the field. + */ + private int modifiers = Modifier.PRIVATE; + + /** + * The JDO modifier of the field. + */ + private String jdoModifier = null; + + /** + * The annotation type. + */ + private String annotationType = null; + + /** + * Creates a new object with the given name. + * + * @param name The name of the field. + */ + JDOField(String name) + { + this.name = name; + } + + /** + * Returns the name of the field. + * + * @return The name of the field. + */ + public String getName() + { + return name; + } + + /** + * Sets the type of the field. The given classname should have a + * natural form(with dots) and is converted to a VM-similar + * notation(with slashes). + * + * @param type The natural classname. + */ + public void setType(String type) + { + this.type = NameHelper.fromCanonicalClassName(type); + } + + /** + * Returns the type of the field. + * + * @return The type of the field. + */ + public String getType() + { + return type; + } + + /** + * Returns the modifiers of the field. + * + * @param modifiers The modifiers of the field. + */ + public void setModifiers(int modifiers) + { + this.modifiers = modifiers; + } + + /** + * Returns the modifiers of the field. + * + * @return The modifiers of the field. + */ + public int getModifiers() + { + return modifiers; + } + + /** + * Sets the annotation type of the field. + * + * @param annotationType annotation type + */ + public void setAnnotationType(String annotationType) + { + this.annotationType = annotationType; + } + + /** + * Returns whether the field is annotated. + * + * @return true if annotated field + */ + public boolean isAnnotated() + { + return annotationType != null; + } + + /** + * Returns whether the field is a key primary. + * + * @return true if primary key. + */ + public boolean isKey() + { + return (annotationType != null + && annotationType.equals( + MetaDataProperties.ANNOTATION_TYPE_KEY)); + } + + /** + * Is the field in the default fetch group? + * + * @return Is the field in the default fetch group? + */ + public boolean isInDefaultFetchGroup() + { + return (annotationType != null + && annotationType.equals( + MetaDataProperties.ANNOTATION_TYPE_DFG)); + } + + /** + * Sets the modifiers of the field. + * + * @param jdoModifier the persistence modifier of the field + */ + public void setJdoModifier(String jdoModifier) + { + this.jdoModifier = jdoModifier; + } + + /** + * Returns whether the field is declared transient. + * + * @return true if declared transient field. + * @see #setJdoModifier + */ + public boolean isKnownTransient() + { + return (jdoModifier != null + && jdoModifier.equals(MetaDataProperties.JDO_TRANSIENT)); + } + + /** + * Returns whether the field is persistent. + * + * @return true if persistent field. + * @see #setJdoModifier + */ + public boolean isPersistent() + { + return (jdoModifier != null + && jdoModifier.equals(MetaDataProperties.JDO_PERSISTENT)); + } + + /** + * Returns whether the field is transactional. + * + * @return true if transactional field + * @see #setJdoModifier + */ + public boolean isTransactional() + { + return (jdoModifier != null + && jdoModifier.equals(MetaDataProperties.JDO_TRANSACTIONAL)); + } + + /** + * Returns whether the field is managed. + * + * @return true if managed field + */ + public boolean isManaged() + { + return (isPersistent() || isTransactional()); + } + + /** + * Creates a string-representation of the object. + * + * @return The string-representation of the object. + */ + public String toString() + { + return ('<' + "name:" + name + + ',' + MetaDataProperties.PROPERTY_TYPE + + ':' + type + + ',' + MetaDataProperties.PROPERTY_ACCESS_MODIFIER + + ':' + Modifier.toString(modifiers) + + ',' + MetaDataProperties.PROPERTY_JDO_MODIFIER + + ':' + jdoModifier + + ',' + MetaDataProperties.PROPERTY_ANNOTATION_TYPE + + ':' + annotationType + '>'); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/MetaDataProperties.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/MetaDataProperties.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/MetaDataProperties.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/MetaDataProperties.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,859 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.meta.prop; + +import java.lang.reflect.Modifier; + +import java.util.Iterator; +import java.util.Enumeration; +import java.util.Map; +import java.util.List; +import java.util.Collection; +import java.util.HashSet; +import java.util.HashMap; +import java.util.ArrayList; +import java.util.Properties; +import java.util.StringTokenizer; + +import java.text.MessageFormat; + +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException; + + + +/** + * This class parses properties containing meta data information + * about classes. The syntax of the properties is the following: + * + * This class is not thread safe. + */ +final class MetaDataProperties +{ + /** + * The delimiter of a property key between the class- and fieldname. + */ + static final char FIELD_DELIMITER = '#'; + + /** + * A string of delimiter characters between attributes. + */ + static final String PROPERTY_DELIMITERS = " \t,;"; + + /** + * A delimiter character between attribute name and attribute value + */ + static final char PROPERTY_ASSIGNER = ':'; + + // attribute names for classes and fields + static final String PROPERTY_ACCESS_MODIFIER = "access"; + static final String PROPERTY_JDO_MODIFIER = "jdo"; + static final String PROPERTY_SUPER_CLASSNAME = "super"; + static final String PROPERTY_OID_CLASSNAME = "oid"; + static final String PROPERTY_TYPE = "type"; + static final String PROPERTY_ANNOTATION_TYPE = "annotation"; + + // values of the access attribute of classes and fields. + static final String ACCESS_PRIVATE = "private"; + static final String ACCESS_PACKAGE_LOCAL = "package"; + static final String ACCESS_PROTECTED = "protected"; + static final String ACCESS_PUBLIC = "public"; + + // values of the jdo attribute of classes and fields. + static final String JDO_TRANSIENT = "transient"; + static final String JDO_PERSISTENT = "persistent"; + static final String JDO_TRANSACTIONAL = "transactional"; + + // values of the annotation type attribute of fields. + static final String ANNOTATION_TYPE_KEY = "key"; + static final String ANNOTATION_TYPE_DFG = "dfg"; + static final String ANNOTATION_TYPE_MEDIATED = "mediated"; + + /** + * The properties to parse. + */ + private Properties properties; + + /** + * A map of already read class properties. The keys are the + * classnames, the values are the appropriate + * JDOClass-object. + */ + private final Map cachedJDOClasses = new HashMap(); + + /** + * A constant for the cache indicating that a given classname + * if not specified in the properties. + */ + static private final JDOClass NULL = new JDOClass(null); + + /** + * A temporary vector (this is the reason why the implementation is not + * thread safe). + */ + private final List tmpTokens = new ArrayList(); + + /** + * Creates a new object with the given properties. + * + * @param props The properties. + */ + public MetaDataProperties(Properties props) + { + this.properties = props; + } + + /** + * Get the information about the class with the given name. + * + * @param classname The classname. + * @return The information about the class or null if no + * information is given. + * @throws EnhancerMetaDataUserException If something went wrong parsing + * the properties. + */ + public final JDOClass getJDOClass(String classname) + throws EnhancerMetaDataUserException + { + classname = NameHelper.toCanonicalClassName(classname); + JDOClass clazz = (JDOClass)cachedJDOClasses.get(classname); + if (clazz == NULL) { //already searched but not found + return null; + } + if (clazz != null) { + return clazz; + } + + //load it from the properties file + String s = properties.getProperty(classname); + if (s == null) { //class not defined + cachedJDOClasses.put(classname, NULL); + return null; + } + + //the class could be found in the properties + clazz = parseJDOClass(classname, s); //parse the class attributes + parseJDOFields(clazz); //parse all fields + validateDependencies(clazz); //check dependencies + cachedJDOClasses.put(clazz.getName(), clazz); + + return clazz; + } + + /** + * Gets the information about the specified field. + * + * @param classname The name of the class. + * @param fieldname The name of the field of the class. + * @return The information about the field or null if + * no information could be found. + * @throws EnhancerMetaDataUserException If something went wrong parsing + * the properties. + */ + public final JDOField getJDOField(String fieldname, + String classname) + throws EnhancerMetaDataUserException + { + JDOClass clazz = getJDOClass(classname); + return (clazz != null ? clazz.getField(fieldname) : null); + } + + /** + * Gets all classnames in the properties. + * + * @return All classnames in the properties. + */ + public final String[] getKnownClassNames() + { + Collection classnames = new HashSet(); + for (Enumeration names = properties.propertyNames(); + names.hasMoreElements();) { + String name = (String)names.nextElement(); + if (name.indexOf(FIELD_DELIMITER) < 0) { + classnames.add(NameHelper.fromCanonicalClassName(name)); + } + } + + return (String[])classnames.toArray(new String[classnames.size()]); + } + + /** + * Parses the attributes-string of a class and puts them into a + * JDOClass-object. + * + * @param classname The name of the class. + * @param attributes The attribute-string as specified in the properties. + * @return The create JDOClass-object. + * @throws EnhancerMetaDataUserException If something went wrong parsing + * the attributes. + */ + private final JDOClass parseJDOClass(String classname, + String attributes) + throws EnhancerMetaDataUserException + { + List props = parseProperties(attributes); + + // check each property + for (int i = 0; i < props.size(); i++) { + final Property prop = (Property)props.get(i); + validateClassProperty(prop, classname); + } + + // check dependencies of all properties + checkForDuplicateProperties(props, classname); + + // properties are OK - assign them to the JDOClass object + JDOClass clazz = new JDOClass(classname); + for (int i = 0; i < props.size(); i++) { + Property prop = (Property)props.get(i); + if (prop.name.equals(PROPERTY_ACCESS_MODIFIER)) { + clazz.setModifiers(getModifiers(prop.value)); + } else if (prop.name.equals(PROPERTY_JDO_MODIFIER)) { + clazz.setPersistent(prop.value.equals(JDO_PERSISTENT)); + } else if (prop.name.equals(PROPERTY_SUPER_CLASSNAME)) { + clazz.setSuperClassName(prop.value); + } else if (prop.name.equals(PROPERTY_OID_CLASSNAME)) { + clazz.setOidClassName(prop.value); + } + } + + return clazz; + } + + /** + * Checks if the given attribute-property of a class is valid. + * + * @param prop The attribute-property. + * @param classname The classname. + * @throws EnhancerMetaDataUserException If the validation failed. + */ + static private void validateClassProperty(Property prop, + String classname) + throws EnhancerMetaDataUserException + { + String value = prop.value; + if (prop.name == null) { + // try to guess the property name + if (value.equals(ACCESS_PUBLIC) + || value.equals(ACCESS_PROTECTED) + || value.equals(ACCESS_PACKAGE_LOCAL) + || value.equals(ACCESS_PRIVATE)) { + // assume access modifier + prop.name = PROPERTY_ACCESS_MODIFIER; + } else if (value.equals(JDO_PERSISTENT) + || value.equals(JDO_TRANSIENT)) { + // assume persistence modifier + prop.name = PROPERTY_JDO_MODIFIER; + } + //@olsen: not unique anymore, could also be oid class name + // else { + // //assume the the given value is the superclassname + // prop.name = PROPERTY_SUPER_CLASSNAME; + // } + } else { + // do we have a valid property name? + String name = prop.name; + checkPropertyName(prop.name, + new String[]{ + PROPERTY_ACCESS_MODIFIER, + PROPERTY_JDO_MODIFIER, + PROPERTY_SUPER_CLASSNAME, + PROPERTY_OID_CLASSNAME + }, + classname); + + // do we have a valid property value? + checkPropertyValue(prop, + new String[]{ + ACCESS_PUBLIC, + ACCESS_PROTECTED, + ACCESS_PACKAGE_LOCAL, + ACCESS_PRIVATE + }, + PROPERTY_ACCESS_MODIFIER, + classname); + checkPropertyValue(prop, + new String[]{ + JDO_TRANSIENT, + JDO_PERSISTENT + }, + PROPERTY_JDO_MODIFIER, + classname); + } + } + + /** + * Parses all fields of a given class. + * + * @param clazz the representation of the class + * @throws EnhancerMetaDataUserException on parse errors + */ + private final void parseJDOFields(JDOClass clazz) + throws EnhancerMetaDataUserException + { + //search for fields of the class + for (Enumeration names = properties.propertyNames(); + names.hasMoreElements();) { + String name = (String)names.nextElement(); + if (name.startsWith(clazz.getName() + FIELD_DELIMITER)) { + //field found + String fieldname + = name.substring(name.indexOf(FIELD_DELIMITER) + 1, + name.length()); + validateFieldName(fieldname, clazz.getName()); + clazz.addField(parseJDOField(properties.getProperty(name), + fieldname, clazz)); + } + } + clazz.sortFields(); + } + + /** + * Parses the attribute-string of a field. + * + * @param attributes The attribute-string. + * @param fieldname The fieldname. + * @param clazz The class to field belongs to. + * @throws EnhancerMetaDataUserException on parse errors + */ + private final JDOField parseJDOField(String attributes, + String fieldname, + JDOClass clazz) + throws EnhancerMetaDataUserException + { + List props = parseProperties(attributes); + + //check each property + for (int i = 0; i < props.size(); i++) { + Property prop = (Property)props.get(i); + validateFieldProperty(prop, fieldname, clazz.getName()); + } + + //check dependencies of all properties + checkForDuplicateProperties(props, + clazz.getName() + FIELD_DELIMITER + + fieldname); + + //properties are OK - assign them to the JDOField object + JDOField field = new JDOField(fieldname); + for (int i = 0; i < props.size(); i++) { + Property prop = (Property)props.get(i); + if (prop.name.equals(PROPERTY_ACCESS_MODIFIER)) { + field.setModifiers(getModifiers(prop.value)); + } else if (prop.name.equals(PROPERTY_JDO_MODIFIER)) { + field.setJdoModifier(prop.value); + } else if (prop.name.equals(PROPERTY_TYPE)) { + field.setType(prop.value); + } else if (prop.name.equals(PROPERTY_ANNOTATION_TYPE)) { + field.setAnnotationType(prop.value); + } + } + + return field; + } + + /** + * Checks if the given attribute-property if valid for a field. + * + * @param prop The attribute-property. + * @param fieldname The fieldname. + * @param classname The classname. + * @throws EnhancerMetaDataUserException If the check fails. + */ + private final void validateFieldProperty(Property prop, + String fieldname, + String classname) + throws EnhancerMetaDataUserException + { + //try to guess the property name + String value = prop.value; + if (prop.name == null) { + if (value.equals(ACCESS_PUBLIC) || + value.equals(ACCESS_PROTECTED) || + value.equals(ACCESS_PACKAGE_LOCAL) || + value.equals(ACCESS_PRIVATE)) { + // access modifier + prop.name = PROPERTY_ACCESS_MODIFIER; + } else if (value.equals(JDO_PERSISTENT) || + value.equals(JDO_TRANSIENT) || + value.equals(JDO_TRANSACTIONAL)) { + // persistence modifier + prop.name = PROPERTY_JDO_MODIFIER; + } else if (value.equals(ANNOTATION_TYPE_KEY) || + value.equals(ANNOTATION_TYPE_DFG) || + value.equals(ANNOTATION_TYPE_MEDIATED)) { + // annotation type + prop.name = PROPERTY_ANNOTATION_TYPE; + } else { + //assume the the given value is the type + prop.name = PROPERTY_TYPE; + } + } else { + String entry = classname + FIELD_DELIMITER + fieldname; + + //do we have a valid property name? + checkPropertyName(prop.name, + new String[]{ + PROPERTY_ACCESS_MODIFIER, + PROPERTY_JDO_MODIFIER, + PROPERTY_TYPE, + PROPERTY_ANNOTATION_TYPE + }, + entry); + + //do we have a valid property value + checkPropertyValue(prop, + new String[]{ + ACCESS_PUBLIC, + ACCESS_PROTECTED, + ACCESS_PACKAGE_LOCAL, + ACCESS_PRIVATE + }, + PROPERTY_ACCESS_MODIFIER, + entry); + checkPropertyValue(prop, + new String[]{ + JDO_PERSISTENT, + JDO_TRANSIENT, + JDO_TRANSACTIONAL + }, + PROPERTY_JDO_MODIFIER, + entry); + checkPropertyValue(prop, + new String[]{ + ANNOTATION_TYPE_KEY, + ANNOTATION_TYPE_DFG, + ANNOTATION_TYPE_MEDIATED + }, + PROPERTY_ANNOTATION_TYPE, + entry); + } + } + + /** + * Validates dependencies between a class and its fields and between. + * + * @param clazz the class + * @throws EnhancerMetaDataUserException if the validation fails + */ + private final void validateDependencies(JDOClass clazz) + throws EnhancerMetaDataUserException + { + final List fields = clazz.getFields(); + for (Iterator i = fields.iterator(); i.hasNext();) { + JDOField field = (JDOField)i.next(); + + // check the jdo field modifier + if (field.isPersistent() && clazz.isTransient()) { + // non-persistent classes cannot have persistent fields + final String msg + = getMsg(Msg.ERR_TRANSIENT_CLASS_WITH_PERSISTENT_FIELD, + new String[]{ + clazz.getName(), + field.getName() }); + throw new EnhancerMetaDataUserException(msg); + } + if (field.isTransactional() && clazz.isTransient()) { + // non-persistent classes cannot have transactional fields + final String msg + = getMsg(Msg.ERR_TRANSIENT_CLASS_WITH_TRANSACTIONAL_FIELD, + new String[]{ + clazz.getName(), + field.getName() }); + throw new EnhancerMetaDataUserException(msg); + } + if (!field.isKnownTransient() && !field.isManaged()) { + // unspecified persistence modifier + final String msg + = getMsg(Msg.ERR_UNSPECIFIED_FIELD_PERSISTENCE_MODIFIER, + new String[]{ + clazz.getName(), + field.getName() }); + throw new EnhancerMetaDataUserException(msg); + } + + // check annotation type + if (!field.isAnnotated() && field.isManaged()) { + // unspecified annotation type + final String msg + = getMsg(Msg.ERR_UNSPECIFIED_FIELD_ANNOTATION_TYPE, + new String[]{ + clazz.getName(), + field.getName() }); + throw new EnhancerMetaDataUserException(msg); + } + if (field.isAnnotated() && !field.isManaged()) { + // non managed field with annotation type + final String msg + = getMsg(Msg.ERR_NON_MANAGED_ANNOTATED_FIELD, + new String[]{ + clazz.getName(), + field.getName() }); + throw new EnhancerMetaDataUserException(msg); + } + if (field.isAnnotated() && clazz.isTransient()) { + // a non-persistent class cannot have an annotated field + final String msg + = getMsg(Msg.ERR_TRANSIENT_CLASS_WITH_ANNOTATED_FIELD, + new String[]{ + clazz.getName(), + field.getName() }); + throw new EnhancerMetaDataUserException(msg); + } + } + } + + /** + * Checks if a given fieldname is a valid Java identifier. + * + * @param fieldname The fieldname. + * @param classname The corresponding classname. + * @throws EnhancerMetaDataUserException If the check fails. + */ + static private void validateFieldName(String fieldname, + String classname) + throws EnhancerMetaDataUserException + { + if (fieldname.length() == 0) { + final String msg + = getMsg(Msg.ERR_EMPTY_FIELDNAME, + new String[]{ classname }); + throw new EnhancerMetaDataUserException(msg); + } + + if (!Character.isJavaIdentifierStart(fieldname.charAt(0))) { + final String msg + = getMsg(Msg.ERR_INVALID_FIELDNAME, + new String[]{ classname, fieldname }); + throw new EnhancerMetaDataUserException(msg); + } + + for (int i = fieldname.length() - 1; i >= 0; i--) { + final char c = fieldname.charAt(i); + if (!Character.isJavaIdentifierPart(c)) { + final String msg + = getMsg(Msg.ERR_INVALID_FIELDNAME, + new String[]{ classname, fieldname }); + throw new EnhancerMetaDataUserException(msg); + } + } + } + + /** + * Checks if an attribute-property was entered twice for a class or field. + * + * @param props The properties. + * @param entry The class- or fieldname. + * @throws EnhancerMetaDataUserException If the check fails. + */ + static private void checkForDuplicateProperties(List props, + String entry) + throws EnhancerMetaDataUserException + { + for (int i = 0; i < props.size(); i++) { + for (int j = i + 1; j < props.size(); j++) { + Property p1 = (Property)props.get(i); + Property p2 = (Property)props.get(j); + if (p1.name.equals(p2.name) && !p1.value.equals(p2.value)) { + final String msg + = getMsg(Msg.ERR_DUPLICATE_PROPERTY_NAME, + new String[]{ + entry, + p1.name, + p1.value, + p2.value }); + throw new EnhancerMetaDataUserException(msg); + } + } + } + } + + /** + * Checks if an attribute name is recognized by the parser. + * + * @param name The name of the attribute. + * @param validnames A list of valid names(the attribute name has to + * be in this list). + * @param entry The class- or fieldname. + * @throws EnhancerMetaDataUserException If the check fails. + */ + static private void checkPropertyName(String name, + String[] validnames, + String entry) + throws EnhancerMetaDataUserException + { + for (int i = 0; i < validnames.length; i++) { + if (name.equals(validnames[i])) { + return; + } + } + + final String msg + = getMsg(Msg.ERR_INVALID_PROPERTY_NAME, + new String[]{ entry, name }); + throw new EnhancerMetaDataUserException(msg); + } + + /** + * Checks if the given value of an attribute-property is recognized by + * by the parser if that value belongs to a given attribute name. + * + * @param prop The attribute-property(with name and value). + * @param validvalues A list of valid values. + * @param name The name of the attribute-property to check. + * @param entry The class- or fieldname. + * @throws EnhancerMetaDataUserException If the check fails. + */ + static private void checkPropertyValue(Property prop, + String[] validvalues, + String name, + String entry) + throws EnhancerMetaDataUserException + { + if ( !prop.name.equals(name)) { + return; + } + + for (int i = 0; i < validvalues.length; i++) { + if (prop.value.equals(validvalues[i])) { + return; + } + } + + final String msg + = getMsg(Msg.ERR_INVALID_PROPERTY_VALUE, + new String[]{ entry, name, prop.value }); + throw new EnhancerMetaDataUserException(msg); + } + + /** + * Formats an error message with the given parameters. + * + * @param msg The message with format strings. + * @param params The params to format the message with. + * @return The formatted error message. + */ + static final String getMsg(String msg, + String[] params) + { + return MessageFormat.format(msg, params); + } + + /** + * Parses the attribute-string of a class- or fieldname. + * + * @param attributes The attribute-string. + * @return A list of Propert<-objects for the attributes. + * @exception EnhancerMetaDataUserException If the parsing fails. + */ + final List parseProperties(String attributes) + throws EnhancerMetaDataUserException + { + tmpTokens.clear(); + for (StringTokenizer t + = new StringTokenizer(attributes, PROPERTY_DELIMITERS); + t.hasMoreTokens();) { + tmpTokens.add(parseProperty(t.nextToken())); + } + + return tmpTokens; + } + + /** + * Parses the given attribute and splits it into name and value. + * + * @param attribute The attribute-string. + * @return The Propert-object. + * @exception EnhancerMetaDataUserException If the parsing fails. + */ + private final Property parseProperty(String attribute) + throws EnhancerMetaDataUserException + { + Property prop = new Property(); + int idx = attribute.indexOf(PROPERTY_ASSIGNER); + if (idx < 0) { + prop.value = attribute; + } else { + prop.name = attribute.substring(0, idx); + prop.value = attribute.substring(idx + 1, attribute.length()); + if (prop.name.length() == 0 || prop.value.length() == 0) { + final String msg + = getMsg(Msg.ERR_EMPTY_PROPERTY_NAME_OR_VALUE, + new String[]{ attribute }); + throw new EnhancerMetaDataUserException(msg); + } + } + + return prop; + } + + /** + * Returns the modifier value for a Java modifier name. + */ + static private int getModifiers(String modifier) + { + if (modifier.equals(ACCESS_PUBLIC)) { + return Modifier.PUBLIC; + } + if (modifier.equals(ACCESS_PRIVATE)) { + return Modifier.PRIVATE; + } + if (modifier.equals(ACCESS_PROTECTED)) { + return Modifier.PROTECTED; + } + return 0; + } + + /** + * A simple test to run from the command line. + * + * @param argv The command line arguments. + */ + public static void main(String[] argv) + { + if (argv.length != 1) { + System.err.println("Error: no property filename specified"); + return; + } + final Properties p = new Properties(); + try { + java.io.InputStream in + = new java.io.FileInputStream(new java.io.File(argv[0])); + p.load(in); + in.close(); + System.out.println("PROPERTIES: " + p); + System.out.println("############"); + final MetaDataProperties props = new MetaDataProperties(p); + String[] classnames = props.getKnownClassNames(); + for (int i = 0; i < classnames.length; i++) { + String classname = classnames[i]; + System.out.println(classname + ": " + + props.getJDOClass(classname)); + } + } catch(Throwable ex) { + ex.printStackTrace(System.err); + } + } + + /** + * The holder-class for the name and the value of a property. + */ + static private final class Property + { + /** + * The name of the property. + */ + String name = null; + + /** + * The value of the property. + */ + String value = null; + + /** + * Creates a string-representation of this object. + * + * @return The string-representation of this object. + */ + public final String toString() + { + return '<' + name + ':' + value + '>'; + } + } + + //^olsen: -> Bundle.properties + + /** + * Holds all unformatted error messages. + */ + static private interface Msg + { + // the unformatted error messages + static final String PREFIX = "Error Parsing meta data properties: "; + + static final String ERR_EMPTY_FIELDNAME = + PREFIX + "The class ''{0}'' may not have an empty fieldname."; + + static final String ERR_INVALID_FIELDNAME = + PREFIX + "The field name ''{1}'' of class ''{0}'' is not valid."; + + static final String ERR_EMPTY_PROPERTY_NAME_OR_VALUE = + PREFIX + "The property name and value may not be empty if a ''" + + PROPERTY_ASSIGNER + "'' is specified: ''{0}''."; + + static final String ERR_INVALID_PROPERTY_NAME = + PREFIX + "Invalid property name for entry ''{0}'': ''{1}''."; + + static final String ERR_INVALID_PROPERTY_VALUE = + PREFIX + "Invalid value for property ''{1}'' of entry ''{0}'': ''{2}''."; + + static final String ERR_DUPLICATE_PROPERTY_NAME = + PREFIX + "The property ''{1}'' for the entry ''{0}'' entered twice with values: ''{2}'' and ''{3}''."; + + static final String ERR_UNSPECIFIED_FIELD_PERSISTENCE_MODIFIER = + PREFIX + "No persistence modifier specified for field: ''{0}.{1}''."; + + static final String ERR_TRANSIENT_CLASS_WITH_PERSISTENT_FIELD = + PREFIX + "A non-persistent class cannot have a persistent field(class ''{0}'' with field ''{1})''."; + + static final String ERR_TRANSIENT_CLASS_WITH_TRANSACTIONAL_FIELD = + PREFIX + "A non-persistent class cannot have a transactional field(class ''{0}'' with field ''{1})''."; + + static final String ERR_UNSPECIFIED_FIELD_ANNOTATION_TYPE = + PREFIX + "No annotation type specified for field: ''{0}.{1}''."; + + static final String ERR_TRANSIENT_CLASS_WITH_ANNOTATED_FIELD = + PREFIX + "A non-persistent class cannot have an annotated field(''{1}'' of class ''{0}'') can''t have a fetch group."; + + static final String ERR_NON_MANAGED_ANNOTATED_FIELD = + PREFIX + "A non-managed field(''{1}'' of class ''{0}'') can''t be a annotated."; + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/NameHelper.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/NameHelper.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/NameHelper.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/prop/NameHelper.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,50 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.meta.prop; + + +/** + * Some utility methods for classname conversion. + */ +final class NameHelper +{ + /** + * Converts a classname given in a given VM-similar notation(with slashes) + * into a canonical notation (with dots). + * + * @param classname The VM-similar notation of the classname. + * @return The canonical classname. + * @see #fromCanonicalClassName + */ + static String toCanonicalClassName(String classname) + { + return classname.replace('/', '.'); + } + + /** + * Converts a classname given in a canonical form(with dots) into + * a VM-similar notation (with slashes) + * + * @param classname The canonical classname. + * @return The VM-similar classname notation. + * @see #toCanonicalClassName + */ + static String fromCanonicalClassName(String classname) + { + return classname.replace('.', '/'); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataBaseModel.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataBaseModel.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataBaseModel.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataBaseModel.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,262 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.meta.util; + +import java.io.PrintWriter; + +import java.util.Iterator; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; + +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException; +import org.apache.jdo.impl.enhancer.util.Support; + + + +/** + * Provides the JDO meta information based on a JDO meta model. + */ +public abstract class EnhancerMetaDataBaseModel + extends Support + implements EnhancerMetaData +{ + // misc + protected boolean verbose = true; + protected final PrintWriter out; + + // default settings + static protected final HashSet unenhancableTypePrefixes = new HashSet(); + static + { + unenhancableTypePrefixes.add("java/"); + unenhancableTypePrefixes.add("javax/"); + } + + /** + * Creates an instance. + */ + public EnhancerMetaDataBaseModel(PrintWriter out, + boolean verbose) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + affirm(out != null); + this.out = out; + } + + /** + * Prints out a warning message. + * + * @param msg the message + */ + public void printWarning(String msg) + { + out.println(getI18N("enhancer.metadata.warning", msg)); + } + + /** + * Prints out a verbose message. + * + * @param msg the message + */ + public void printMessage(String msg) + { + if (verbose) { + out.println(getI18N("enhancer.metadata.message", msg)); + } + } + + /** + * Returns whether a class is not to be modified by the enhancer. + * + * @see EnhancerMetaData#isKnownUnenhancableClass(String) + */ + public boolean isKnownUnenhancableClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + //check the transient prefixes + for (Iterator i = unenhancableTypePrefixes.iterator(); i.hasNext();) { + final String typePrefix = (String)i.next(); + if (classPath.startsWith(typePrefix)) + return true; + } + return false; + } + + /** + * Returns whether a class is persistence-capable root class. + * + * @see EnhancerMetaData#isPersistenceCapableRootClass(String) + */ + public boolean isPersistenceCapableRootClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + return (isPersistenceCapableClass(classPath) + && (getPersistenceCapableSuperClass(classPath) == null)); + } + + /** + * Returns the name of the persistence-capable root class of a class. + * + * @see EnhancerMetaData#getPersistenceCapableRootClass(String) + */ + public String getPersistenceCapableRootClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + if (!isPersistenceCapableClass(classPath)) { + return null; + } + + String pcRootClass; + String clazz = classPath; + do { + pcRootClass = clazz; + clazz = getPersistenceCapableSuperClass(clazz); + } while (clazz != null); + return pcRootClass; + } + + /** + * Returns the name of the key class of the next persistence-capable + * superclass that defines one. + * + * @see EnhancerMetaData#getSuperKeyClass(String) + */ + public String getSuperKeyClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + for (String superClass = getPersistenceCapableSuperClass(classPath); + superClass != null; + superClass = getPersistenceCapableSuperClass(superClass)) { + final String superKeyClass = getKeyClass(superClass); + if (superKeyClass != null) { + return superKeyClass; + } + } + return null; + } + + /** + * Returns whether a field of a class is known to be either transient + * transactional or persistent. + * + * @see EnhancerMetaData#isManagedField(String, String) + */ + public boolean isManagedField(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + return (isPersistentField(classPath, fieldName) + || isTransactionalField(classPath, fieldName)); + } + + /** + * Returns the field flags of a declared, managed field of a class. + * + * @see EnhancerMetaData#getFieldFlags(String, String) + */ + public int getFieldFlags(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + if (!isManagedField(classPath, fieldName)) { + affirm(!isTransactionalField(classPath, fieldName)); + affirm(!isPersistentField(classPath, fieldName)); + affirm(!isKeyField(classPath, fieldName)); + affirm(!isDefaultFetchGroupField(classPath, fieldName)); + return 0; + } + //affirm(isManagedField(classPath, fieldName)); + + if (isTransactionalField(classPath, fieldName)) { + affirm(!isPersistentField(classPath, fieldName)); + affirm(!isKeyField(classPath, fieldName)); + // ignore any dfg membership of transactional fields + //affirm(!isDefaultFetchGroupField(classPath, fieldName)); + return CHECK_WRITE; + } + //affirm(!isTransactionalField(classPath, fieldName)); + affirm(isPersistentField(classPath, fieldName)); + + if (isKeyField(classPath, fieldName)) { + // ignore any dfg membership of key fields + //affirm(!isDefaultFetchGroupField(classPath, fieldName)); + return MEDIATE_WRITE; + } + //affirm(!isKeyField(classPath, fieldName)); + + if (isDefaultFetchGroupField(classPath, fieldName)) { + return CHECK_READ | CHECK_WRITE; + } + //affirm(!isDefaultFetchGroupField(classPath, fieldName)); + + return MEDIATE_READ | MEDIATE_WRITE; + } + + /** + * Returns an array of field names of all key fields of a class. + * + * @see EnhancerMetaData#getKeyFields(String) + */ + public String[] getKeyFields(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + final List keys = new ArrayList(); + final String[] fieldNames = getManagedFields(classPath); + final int n = fieldNames.length; + for (int i = 0; i < n; i++) { + if (isKeyField(classPath, fieldNames[i])) { + keys.add(fieldNames[i]); + } + } + return (String[])keys.toArray(new String[keys.size()]); + } + + /** + * Returns the field flags for some declared, managed fields of a class. + * + * @see EnhancerMetaData#getFieldFlags(String, String[]) + */ + public int[] getFieldFlags(String classPath, String[] fieldNames) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + final int n = (fieldNames != null ? fieldNames.length : 0); + final int[] flags = new int[n]; + for (int i = 0; i < n; i++) { + flags[i] = getFieldFlags(classPath, fieldNames[i]); + } + return flags; + } + + /** + * Returns the unique field index of some declared, managed fields of a + * class. + * + * @see EnhancerMetaData#getFieldNumber(String, String[]) + */ + public int[] getFieldNumber(String classPath, String[] fieldNames) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + final int n = (fieldNames != null ? fieldNames.length : 0); + final int[] flags = new int[n]; + for (int i = 0; i < n; i++) { + flags[i] = getFieldNumber(classPath, fieldNames[i]); + } + return flags; + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataTimer.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataTimer.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataTimer.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/meta/util/EnhancerMetaDataTimer.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,325 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.meta.util; + +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException; +import org.apache.jdo.impl.enhancer.util.Support; + + + + +public final class EnhancerMetaDataTimer + extends Support + implements EnhancerMetaData +{ + // delegate + final protected EnhancerMetaData delegate; + + /** + * Creates an instance. + */ + public EnhancerMetaDataTimer(EnhancerMetaData delegate) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + affirm(delegate); + this.delegate = delegate; + } + + public String getDeclaringClass(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getDeclaringClass(String,String)", + "EnhancerMetaData.getDeclaringClass(" + classPath + + ", " + fieldName + ")"); + return delegate.getDeclaringClass(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public void declareField(String classPath, + String fieldName, + String signature) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.declareField(String,String,String)", + "EnhancerMetaData.declareField(" + classPath + + ", " + fieldName + ", " + signature + ")"); + delegate.declareField(classPath, fieldName, signature); + } finally { + timer.pop(); + } + } + + public boolean isPersistenceCapableClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isPersistenceCapableClass(String)", + "EnhancerMetaData.isPersistenceCapableClass(" + classPath + ")"); + return delegate.isPersistenceCapableClass(classPath); + } finally { + timer.pop(); + } + } + + public boolean isSerializableClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isSerializableClass(String)", + "EnhancerMetaData.isSerializableClass(" + classPath + ")"); + return delegate.isSerializableClass(classPath); + } finally { + timer.pop(); + } + } + + public boolean isKnownUnenhancableClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isKnownUnenhancableClass(String)", + "EnhancerMetaData.isKnownUnenhancableClass(" + classPath + ")"); + return delegate.isKnownUnenhancableClass(classPath); + } finally { + timer.pop(); + } + } + + public boolean isPersistenceCapableRootClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isPersistenceCapableRootClass(String)", + "EnhancerMetaData.isPersistenceCapableRootClass(" + classPath + ")"); + return delegate.isPersistenceCapableRootClass(classPath); + } finally { + timer.pop(); + } + } + + public String getPersistenceCapableRootClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getPersistenceCapableRootClass(String)", + "EnhancerMetaData.getPersistenceCapableRootClass(" + classPath + ")"); + return delegate.getPersistenceCapableRootClass(classPath); + } finally { + timer.pop(); + } + } + + public String getPersistenceCapableSuperClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getPersistenceCapableSuperClass(String)", + "EnhancerMetaData.getPersistenceCapableSuperClass(" + classPath + ")"); + return delegate.getPersistenceCapableSuperClass(classPath); + } finally { + timer.pop(); + } + } + + public String getKeyClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getKeyClass(String)", + "EnhancerMetaData.getKeyClass(" + classPath + ")"); + return delegate.getKeyClass(classPath); + } finally { + timer.pop(); + } + } + + public String getSuperKeyClass(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getSuperKeyClass(String)", + "EnhancerMetaData.getSuperKeyClass(" + classPath + ")"); + return delegate.getSuperKeyClass(classPath); + } finally { + timer.pop(); + } + } + + public boolean isKnownNonManagedField(String classPath, + String fieldName, + String fieldSig) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isKnownNonManagedField(String,String,String)", + "EnhancerMetaData.isKnownNonManagedField(" + classPath + + ", " + fieldName + ", " + fieldSig + ")"); + return delegate.isKnownNonManagedField(classPath, + fieldName, fieldSig); + } finally { + timer.pop(); + } + } + + public boolean isManagedField(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isManagedField(String,String)", + "EnhancerMetaData.isManagedField(" + classPath + + ", " + fieldName + ")"); + return delegate.isManagedField(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public boolean isPersistentField(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isPersistentField(String,String)", + "EnhancerMetaData.isPersistentField(" + classPath + + ", " + fieldName + ")"); + return delegate.isPersistentField(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public boolean isTransactionalField(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isTransactionalField(String,String)", + "EnhancerMetaData.isTransactionalField(" + classPath + + ", " + fieldName + ")"); + return delegate.isTransactionalField(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public boolean isKeyField(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isKeyField(String,String)", + "EnhancerMetaData.isKeyField(" + classPath + + ", " + fieldName + ")"); + return delegate.isKeyField(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public boolean isDefaultFetchGroupField(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.isDefaultFetchGroupField(String,fieldName)", + "EnhancerMetaData.isDefaultFetchGroupField(" + classPath + + ", " + fieldName + ")"); + return delegate.isDefaultFetchGroupField(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public int getFieldFlags(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getFieldFlags(String, String)", + "EnhancerMetaData.getFieldFlags(" + classPath + + ", " + fieldName + ")"); + return delegate.getFieldFlags(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public int getFieldNumber(String classPath, String fieldName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getFieldNumber(String, String)", + "EnhancerMetaData.getFieldNumber(" + classPath + + ", " + fieldName + ")"); + return delegate.getFieldNumber(classPath, fieldName); + } finally { + timer.pop(); + } + } + + public String[] getManagedFields(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getManagedFields(String)", + "EnhancerMetaData.getmanagedFields(" + classPath + ")"); + return delegate.getManagedFields(classPath); + } finally { + timer.pop(); + } + } + + public String[] getKeyFields(String classPath) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getKeyFields(String)", + "EnhancerMetaData.getKeyFields(" + classPath + ")"); + return delegate.getKeyFields(classPath); + } finally { + timer.pop(); + } + } + + + public int[] getFieldFlags(String classPath, String[] fieldNames) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getFieldFlags(String, String[])", + "EnhancerMetaData.getFieldFlags(" + classPath + ")"); + return delegate.getFieldFlags(classPath, fieldNames); + } finally { + timer.pop(); + } + } + + + public int[] getFieldNumber(String classPath, String[] fieldNames) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + try { + timer.push("EnhancerMetaData.getFieldNumber(String, String[])", + "EnhancerMetaData.getFieldNumber(" + classPath + ")"); + return delegate.getFieldNumber(classPath, fieldNames); + } finally { + timer.pop(); + } + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/AnnotationTest.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/AnnotationTest.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/AnnotationTest.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/AnnotationTest.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,418 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.util; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Enumeration; +import java.util.List; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.io.IOException; +import java.io.DataInputStream; + +import org.apache.jdo.impl.enhancer.EnhancerFatalError; +import org.apache.jdo.impl.enhancer.JdoMetaMain; +import org.apache.jdo.impl.enhancer.classfile.ClassFile; +import org.apache.jdo.impl.enhancer.classfile.ClassMethod; +import org.apache.jdo.impl.enhancer.classfile.CodeAttribute; +import org.apache.jdo.impl.enhancer.classfile.ConstClass; +import org.apache.jdo.impl.enhancer.classfile.ConstFieldRef; +import org.apache.jdo.impl.enhancer.classfile.ConstMethodRef; +import org.apache.jdo.impl.enhancer.classfile.ConstNameAndType; +import org.apache.jdo.impl.enhancer.classfile.Descriptor; +import org.apache.jdo.impl.enhancer.classfile.Insn; +import org.apache.jdo.impl.enhancer.classfile.InsnConstOp; +import org.apache.jdo.impl.enhancer.classfile.VMConstants; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataUserException; + + + + + +/** + * Utility class for testing a class file for correct annotation. + * + * @author Martin Zaun + */ +public class AnnotationTest + extends JdoMetaMain +{ + // return values of internal test methods + static public final int AFFIRMATIVE = 1; + static public final int NEGATIVE = 0; + static public final int ERROR = -1; + + // ---------------------------------------------------------------------- + + private boolean verbose; + private String className; + private String classFileName; + private ClassFile classFile; + + public AnnotationTest(PrintWriter out, + PrintWriter err) + { + super(out, err); + } + + private int checkGetPutField(PrintWriter out, + Insn insn, + boolean jdoMethod) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + // get the instruction arguments + final InsnConstOp fieldInsn = (InsnConstOp)insn; + final ConstFieldRef fieldRef = (ConstFieldRef)fieldInsn.value(); + final ConstClass declClass = fieldRef.className(); + final String declClassName = declClass.asString(); + final ConstNameAndType fieldNameAndType = fieldRef.nameAndType(); + final String fieldName = fieldNameAndType.name().asString(); + final String fieldType = fieldNameAndType.signature().asString(); + + // check if field is known to be non-managed or not annotatable + final int res; + if (jdoMeta.isKnownNonManagedField(declClassName, + fieldName, fieldType)) { + if (false) { // verbose + out.println(" --- unannotated field access: " + + declClassName + "." + fieldName); + } + res = NEGATIVE; + } else if (jdoMethod) { + if (false) { // verbose + out.println(" --- unannotated field access: " + + declClassName + "." + fieldName); + } + res = NEGATIVE; + } else if (jdoMeta.isPersistenceCapableClass(declClassName) + && (fieldName.equals("jdoStateManager") + || fieldName.equals("jdoFlags"))) { + if (false) { // verbose + out.println(" --- unannotated field access: " + + declClassName + "." + fieldName); + } + res = NEGATIVE; + } else { + out.println(" !!! ERROR: missing annotation of field access: " + + declClassName + "." + fieldName); + res = ERROR; + } + return res; + } + + private int checkInvokeStatic(PrintWriter out, + Insn insn, + boolean jdoMethod) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + // get the instruction arguments + final InsnConstOp methodInsn = (InsnConstOp)insn; + final ConstMethodRef methodRef = (ConstMethodRef)methodInsn.value(); + final ConstClass declClass = methodRef.className(); + final String declClassName = declClass.asString(); + final ConstNameAndType methodNameAndType = methodRef.nameAndType(); + final String methodName = methodNameAndType.name().asString(); + final String methodType = methodNameAndType.signature().asString(); + + if (!methodName.startsWith("jdoSet") + && (!methodName.startsWith("jdoGet") + || methodName.equals("jdoGetManagedFieldCount"))) { + return NEGATIVE; + } + final String fieldName = methodName.substring(6); + + final int res; + final String fieldType; + if (methodName.startsWith("jdoGet")) { + fieldType = Descriptor.extractResultSig(methodType); + } else { + final String argSig = Descriptor.extractArgSig(methodType); + final int idx = Descriptor.nextSigElement(argSig, 0); + fieldType = argSig.substring(idx); + } + affirm(fieldType != null); + + // check if field is known to be non-managed or non-annotable + if (jdoMeta.isKnownNonManagedField(declClassName, + fieldName, fieldType)) { + out.println(" !!! ERROR: annotated access to non-managed field: " + + declClassName + "." + fieldName); + res = ERROR; + } else if (jdoMethod) { + out.println(" !!! ERROR: annotated field access in JDO method: " + + declClassName + "." + fieldName); + res = ERROR; + } else { + if (verbose) { + out.println(" +++ annotated field access: " + + declClassName + "." + fieldName); + } + res = AFFIRMATIVE; + } + + return res; + } + + private int hasAnnotation(PrintWriter out, + ClassMethod method, + String methodName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + final CodeAttribute codeAttr = method.codeAttribute(); + + // return if method is abstract or native + if (codeAttr == null) + return NEGATIVE; + + int res = NEGATIVE; + // don't annotate readObject(ObjectInputStream) or any jdo* methods + // except for jdoPreStore() and jdoPreDelete(). + final boolean jdoMethod + = ((methodName.startsWith("jdo") + && !(methodName.equals("jdoPreStore()") + || methodName.equals("jdoPreDelete()"))) + || methodName.equals("readObject(java.io.ObjectInputStream)")); + + // first instruction is a target + final Insn firstInsn = codeAttr.theCode(); + Insn insn = firstInsn.next(); + while (insn != null) { + switch(insn.opcode()) { + case VMConstants.opc_getfield: + case VMConstants.opc_putfield: { + final int r = checkGetPutField(out, insn, jdoMethod); + if (r < NEGATIVE) { + res = ERROR; + } + break; + } + case VMConstants.opc_invokestatic: { + final int r = checkInvokeStatic(out, insn, jdoMethod); + if (r < NEGATIVE) { + res = ERROR; + } else if (r > NEGATIVE) { + if (res == NEGATIVE) { + res = AFFIRMATIVE; + } + } + break; + } + default: + } + + insn = insn.next(); + } + + return res; + } + + private int testAnnotation(PrintWriter out) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + affirm(ERROR < NEGATIVE && NEGATIVE < AFFIRMATIVE); + affirm(classFile); + + int res = NEGATIVE; + + Enumeration e = classFile.methods().elements(); + while (e.hasMoreElements()) { + final ClassMethod method = (ClassMethod)e.nextElement(); + final String methodSig = method.signature().asString(); + final String methodArgs = Descriptor.userMethodArgs(methodSig); + final String methodName = method.name().asString() + methodArgs; + + // check class-specific enhancement + final StringWriter s = new StringWriter(); + int r = hasAnnotation(new PrintWriter(s), method, methodName); + if (r < NEGATIVE) { + out.println(" !!! ERROR: incorrect annotation in: " + + methodName); + out.println(s.toString()); + res = ERROR; + } else if (r == NEGATIVE) { + if (verbose) { + out.println(" --- not annotated: " + + methodName); + out.println(s.toString()); + } + } else { + affirm(r > NEGATIVE); + if (verbose) { + out.println(" +++ has correct annotation: " + + methodName); + out.println(s.toString()); + } + if (res == NEGATIVE) { + res = AFFIRMATIVE; + } + } + } + + return res; + } + + private int parseClass(PrintWriter out) + { + DataInputStream dis = null; + try { + affirm(className == null ^ classFileName == null); + if (className != null) { + dis = new DataInputStream(openClassInputStream(className)); + } else { + dis = new DataInputStream(openFileInputStream(classFileName)); + } + final boolean allowJDK12ClassFiles = true; + classFile = new ClassFile(dis, allowJDK12ClassFiles); + + // check user class name from ClassFile + final String userClassName + = classFile.className().asString().replace('/', '.'); + //^olsen: better throw user exception or error + affirm(className == null || className.equals(userClassName)); + out.println(" +++ parsed classfile"); + } catch (ClassFormatError ex) { + out.println(" !!! ERROR: format error when parsing class: " + + className); + out.println(" error: " + err); + return ERROR; + } catch (IOException ex) { + out.println(" !!! ERROR: exception while reading class: " + + className); + out.println(" exception: " + ex); + return ERROR; + } finally { + closeInputStream(dis); + } + + affirm(classFile); + return AFFIRMATIVE; + } + + private int test(PrintWriter out, + String className, + String classFileName) + throws EnhancerMetaDataUserException, EnhancerMetaDataFatalError + { + this.className = className; + this.classFileName = classFileName; + affirm(className == null ^ classFileName == null); + final String name = (className != null ? className : classFileName); + + if (verbose) { + out.println("-------------------------------------------------------------------------------"); + out.println(); + out.println("Test class for correct annotation: " + + name + " ..."); + } + + // check parsing class + StringWriter s = new StringWriter(); + if (parseClass(new PrintWriter(s)) <= NEGATIVE) { + out.println(); + out.println("!!! ERROR: failed parsing class: " + name); + out.println(s.toString()); + return ERROR; + } + + if (verbose) { + out.println(); + out.println("+++ parsed class: " + name); + out.println(s.toString()); + } + + // check annotation + s = new StringWriter(); + final int r = testAnnotation(new PrintWriter(s)); + if (r < NEGATIVE) { + out.println(); + out.println("!!! ERROR: incorrect annotation: " + name); + out.println(s.toString()); + return ERROR; + } + + if (r == NEGATIVE) { + out.println(); + out.println("--- class not annotated: " + name); + } else { + out.println(); + out.println("+++ class annotated: " + name); + } + if (verbose) { + out.println(s.toString()); + } + + return r; + } + + protected int test(PrintWriter out, + boolean verbose, + List classNames, + List classFileNames) + { + affirm(classNames); + this.verbose = verbose; + + out.println(); + out.println("AnnotationTest: Testing Classes for JDO Persistence-Capability Enhancement"); + + int nofFailed = 0; + final int all = classNames.size() + classFileNames.size(); + for (int i = 0; i < classNames.size(); i++) { + if (test(out, (String)classNames.get(i), null) < NEGATIVE) { + nofFailed++; + } + } + for (int i = 0; i < classFileNames.size(); i++) { + if (test(out, null, (String)classFileNames.get(i)) < NEGATIVE) { + nofFailed++; + } + } + final int nofPassed = all - nofFailed; + + out.println(); + out.println("AnnotationTest: Summary: TESTED: " + all + + " PASSED: " + nofPassed + + " FAILED: " + nofFailed); + return nofFailed; + } + + // ---------------------------------------------------------------------- + + /** + * Run the annotation test. + */ + protected int process() + { + //^olsen: to be extended for zip/jar file arguments + return test(out, options.verbose.value, + options.classNames, options.classFileNames); + } + + static public void main(String[] args) + { + final PrintWriter out = new PrintWriter(System.out, true); + out.println("--> AnnotationTest.main()"); + final AnnotationTest main = new AnnotationTest(out, out); + int res = main.run(args); + out.println("<-- AnnotationTest.main(): exit = " + res); + System.exit(res); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/Assertion.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/Assertion.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/Assertion.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/util/Assertion.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,45 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed 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.jdo.impl.enhancer.util; + + +/** + * Support for signalling internal implementation errors. + */ +public class Assertion { + + static protected final void affirm(boolean condition) { + if (!condition) + throw new InternalError("assertion failed."); + } + + static protected final void affirm(boolean condition, String msg) { + if (!condition) + throw new InternalError("assertion failed: " + msg); + } + + static protected final void affirm(Object object) { + if (object == null) + throw new InternalError("assertion failed."); + } + + static protected final void affirm(Object object, String msg) { + if (object == null) + throw new InternalError("assertion failed: " + msg); + } +}