/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.mappingsmodel.meta; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel; import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWClassHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWMethodHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle.NodeReferenceScrubber; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalField; import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator; import org.eclipse.persistence.tools.workbench.utility.node.Node; import org.eclipse.persistence.tools.workbench.utility.string.StringTools; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.descriptors.DescriptorEvent; import org.eclipse.persistence.indirection.ValueHolder; import org.eclipse.persistence.mappings.DirectToFieldMapping; import org.eclipse.persistence.mappings.OneToOneMapping; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; import org.eclipse.persistence.oxm.mappings.XMLDirectMapping; import org.eclipse.persistence.internal.codegen.NonreflectiveAttributeDefinition; /** * This class models a Java class attribute and, if the attribute * is a collection (or map) or value holder, provides an extra bit of type information: * - the type of the value of the attribute (in the case of a value holder), * - the type of the elements in the collection (or entry values in a map) * (in the case the attribute *or* the value type is a collection (or map)) * - the type of the keys for the map (in the case the attribute *or* the value type is a map) */ public final class MWClassAttribute extends MWModel implements MWModifiable { private volatile String name; public static final String NAME_PROPERTY = "name"; private MWModifier modifier; // pseudo-final /** * Property change notification occurs here */ private MWTypeDeclaration typeDeclaration; // pseudo-final public static final String TYPE_PROPERTY = "type"; public static final String DIMENSIONALITY_PROPERTY = "dimensionality"; /** "virtual" property corresponding to the attribute's "declaration" */ public static final String DECLARATION_PROPERTY = "declaration"; /** * if the attribute is a value holder, * this indicates the type of object held by the value holder; * for now there is no need to support arrays... */ private MWClassHandle valueTypeHandle; public static final String VALUE_TYPE_PROPERTY = "valueType"; /** * if the attribute is a collection (or map), * this indicates the type of element held by the collection * (or entry values held by the map); * for now there is no need to support arrays... */ private MWClassHandle itemTypeHandle; public static final String ITEM_TYPE_PROPERTY = "itemType"; /** * if the attribute is a map, * this indicates the type of entry keys used by the map; * for now there is no need to support arrays... */ private MWClassHandle keyTypeHandle; public static final String KEY_TYPE_PROPERTY = "keyType"; /** * the accessor used to directly get the attribute */ private MWMethodHandle getMethodHandle; public static final String GET_METHOD_PROPERTY = "getMethod"; /** * the accessor used to directly set the attribute */ private MWMethodHandle setMethodHandle; public static final String SET_METHOD_PROPERTY = "setMethod"; /** * the accessor used to get the value of the attribute (for value holders only) */ private MWMethodHandle valueGetMethodHandle; public static final String VALUE_GET_METHOD_PROPERTY = "valueGetMethod"; /** * the accessor used to set the value of the attribute (for value holders only) */ private MWMethodHandle valueSetMethodHandle; public static final String VALUE_SET_METHOD_PROPERTY = "valueSetMethod"; /** * the accessor used to add an item to the collection (or map) */ private MWMethodHandle addMethodHandle; public static final String ADD_METHOD_PROPERTY = "addMethod"; /** * the accessor used to remove an item from the collection (or map) */ private MWMethodHandle removeMethodHandle; public static final String REMOVE_METHOD_PROPERTY = "removeMethod"; /** method name prefixes and suffixes */ private static final String GET_PREFIX = "get"; private static final String IS_PREFIX = "is"; private static final String SET_PREFIX = "set"; private static final String ADD_PREFIX = "addTo"; private static final String REMOVE_PREFIX = "removeFrom"; private static final String HOLDER_SUFFIX = "Holder"; private static final String CR = StringTools.CR; // ********** constructors ********** /** * Default constructor - for TopLink use only. */ private MWClassAttribute() { super(); } MWClassAttribute(MWClass parent, ExternalField externalField) { this(parent, externalField.getName()); this.refresh(externalField); } MWClassAttribute(MWClass parent, String name) { // default type is java.lang.Object this(parent, name, parent.typeFor(java.lang.Object.class)); } MWClassAttribute(MWClass parent, String name, MWClass type) { this(parent, name, type, 0); } MWClassAttribute(MWClass parent, String name, MWClass type, int dimensionality) { super(parent); this.name = name; this.initialize(type, dimensionality); } // ********** initialization ********** /** * initalize transient state */ protected void initialize() { super.initialize(); this.modifier = new MWModifier(this); } /** * some state is determined by the declaring type */ protected void initialize(Node parent) { super.initialize(parent); if (this.getDeclaringType().isInterface()) { this.modifier.setPublic(true); this.modifier.setStatic(true); this.modifier.setFinal(true); } this.getMethodHandle = new MWMethodHandle(this, this.buildGetMethodScrubber()); this.setMethodHandle = new MWMethodHandle(this, this.buildSetMethodScrubber()); this.valueGetMethodHandle = new MWMethodHandle(this, this.buildValueGetMethodScrubber()); this.valueSetMethodHandle = new MWMethodHandle(this, this.buildValueSetMethodScrubber()); this.addMethodHandle = new MWMethodHandle(this, this.buildAddMethodScrubber()); this.removeMethodHandle = new MWMethodHandle(this, this.buildRemoveMethodScrubber()); this.valueTypeHandle = new MWClassHandle(this, this.buildValueTypeScrubber()); this.itemTypeHandle = new MWClassHandle(this, this.buildItemTypeScrubber()); this.keyTypeHandle = new MWClassHandle(this, this.buildKeyTypeScrubber()); } private void initialize(MWClass type, int dimensionality) { this.typeDeclaration = new MWTypeDeclaration(this, type, dimensionality); // do NOT update the nested types here - it's not necessary and it // will cause a stack overflow when initializing the base types... ~bjv // this.updateNestedTypes(); } // ********** accessors ********** public MWClass getDeclaringType() { return (MWClass) this.getParent(); } public String getName() { return this.name; } public void setName(String name) { Object old = this.name; this.name = name; this.firePropertyChanged(NAME_PROPERTY, old, name); if (this.attributeValueHasChanged(old, name)) { this.firePropertyChanged(DECLARATION_PROPERTY, "name"); // don't waste time building the declaration - it's ignored this.getProject().nodeRenamed(this); this.updateAccessorNames(); } } public MWModifier getModifier() { return this.modifier; } public MWClass getType() { return this.typeDeclaration.getType(); } public void setType(MWClass type) { if (type.isVoid()) { throw new IllegalArgumentException("An attribute can not have a type of 'void'"); } MWClass old = this.typeDeclaration.getType(); this.typeDeclaration.setType(type); this.firePropertyChanged(TYPE_PROPERTY, old, type); if (old != type) { this.firePropertyChanged(DECLARATION_PROPERTY, "type"); // don't waste time building the declaration - it's ignored this.updateNestedTypes(old); this.updateGetAndSetMethods(); } } public int getDimensionality() { return this.typeDeclaration.getDimensionality(); } public void setDimensionality(int dim) { int old = this.typeDeclaration.getDimensionality(); this.typeDeclaration.setDimensionality(dim); this.firePropertyChanged(DIMENSIONALITY_PROPERTY, old, dim); if (old != dim) { this.firePropertyChanged(DECLARATION_PROPERTY, "dimensionality"); // don't waste time building the declaration - it's ignored this.updateNestedTypes(); this.updateGetAndSetMethods(); } } /** * the type of object held by a value holder */ public MWClass getValueType() { MWClass valueType = this.valueTypeHandle.getType(); if ((valueType == null) && this.canHaveValueType()) { valueType = this.objectType(); } return valueType; } /** * the type of object held by a value holder */ public void setValueType(MWClass valueType) { if (this.canHaveValueType() ^ (valueType != null)) { throw new IllegalArgumentException(valueType.toString()); } Object old = this.getValueType(); this.valueTypeHandle.setType((valueType == this.objectType()) ? null : valueType); this.firePropertyChanged(VALUE_TYPE_PROPERTY, this.getValueType()); if (old != this.getValueType()) { this.updateContainerTypes(); this.updateValueGetAndSetMethods(); } } /** * the type of items held by a container * (elements by a collection or entry values by a map) */ public MWClass getItemType() { MWClass itemType = this.itemTypeHandle.getType(); if ((itemType == null) && this.canHaveItemType()) { itemType = this.objectType(); } return itemType; } /** * the type of items held by a container * (elements by a collection or entry values by a map) */ public void setItemType(MWClass itemType) { if (this.canHaveItemType() ^ (itemType != null)) { throw new IllegalArgumentException(itemType.toString()); } Object old = this.getItemType(); this.itemTypeHandle.setType((itemType == this.objectType()) ? null : itemType); this.firePropertyChanged(ITEM_TYPE_PROPERTY, this.getItemType()); if (old != this.getItemType()) { this.updateAddAndRemoveMethods(); } } /** * the type of key used by a map */ public MWClass getKeyType() { MWClass keyType = this.keyTypeHandle.getType(); if ((keyType == null) && this.canHaveKeyType()) { keyType = this.objectType(); } return keyType; } /** * the type of key used by a map */ public void setKeyType(MWClass keyType) { if (this.canHaveKeyType() ^ (keyType != null)) { throw new IllegalArgumentException(keyType.toString()); } Object old = this.getKeyType(); this.keyTypeHandle.setType((keyType == this.objectType()) ? null : keyType); this.firePropertyChanged(KEY_TYPE_PROPERTY, old, this.getKeyType()); if (old != this.getKeyType()) { this.updateAddAndRemoveMethods(); } } public MWMethod getGetMethod() { return this.getMethodHandle.getMethod(); } public void setGetMethod(MWMethod getMethod) { MWMethod old = this.getGetMethod(); this.getMethodHandle.setMethod(getMethod); this.firePropertyChanged(GET_METHOD_PROPERTY, old, getMethod); if (old != getMethod) { if (old != null) { old.setAccessedAttribute(null); } if (getMethod != null) { getMethod.setAccessedAttribute(this); } } } public MWMethod getSetMethod() { return this.setMethodHandle.getMethod(); } public void setSetMethod(MWMethod setMethod) { MWMethod old = this.getSetMethod(); this.setMethodHandle.setMethod(setMethod); this.firePropertyChanged(SET_METHOD_PROPERTY, old, setMethod); if (old != setMethod) { if (old != null) { old.setAccessedAttribute(null); } if (setMethod != null) { setMethod.setAccessedAttribute(this); } } } public MWMethod getValueGetMethod() { return this.valueGetMethodHandle.getMethod(); } public void setValueGetMethod(MWMethod valueGetMethod) { MWMethod old = this.getValueGetMethod(); this.valueGetMethodHandle.setMethod(valueGetMethod); this.firePropertyChanged(VALUE_GET_METHOD_PROPERTY, old, valueGetMethod); if (old != valueGetMethod) { if (old != null) { old.setAccessedAttribute(null); } if (valueGetMethod != null) { valueGetMethod.setAccessedAttribute(this); } } } public MWMethod getValueSetMethod() { return this.valueSetMethodHandle.getMethod(); } public void setValueSetMethod(MWMethod valueSetMethod) { MWMethod old = this.getValueSetMethod(); this.valueSetMethodHandle.setMethod(valueSetMethod); this.firePropertyChanged(VALUE_SET_METHOD_PROPERTY, old, valueSetMethod); if (old != valueSetMethod) { if (old != null) { old.setAccessedAttribute(null); } if (valueSetMethod != null) { valueSetMethod.setAccessedAttribute(this); } } } public MWMethod getAddMethod() { return this.addMethodHandle.getMethod(); } public void setAddMethod(MWMethod addMethod) { MWMethod old = this.getAddMethod(); this.addMethodHandle.setMethod(addMethod); this.firePropertyChanged(ADD_METHOD_PROPERTY, old, addMethod); if (old != addMethod) { if (old != null) { old.setAccessedAttribute(null); } if (addMethod != null) { addMethod.setAccessedAttribute(this); } } } public MWMethod getRemoveMethod() { return this.removeMethodHandle.getMethod(); } public void setRemoveMethod(MWMethod removeMethod) { MWMethod old = this.getRemoveMethod(); this.removeMethodHandle.setMethod(removeMethod); this.firePropertyChanged(REMOVE_METHOD_PROPERTY, old, removeMethod); if (old != removeMethod) { if (old != null) { old.setAccessedAttribute(null); } if (removeMethod != null) { removeMethod.setAccessedAttribute(this); } } } // ********** Modifiable implementation ********** public boolean supportsAbstract() { return false; } public boolean canBeSetAbstract() { return false; } public boolean canBeSetFinal() { return ! this.getModifier().isVolatile(); } public boolean supportsInterface() { return false; } public boolean canBeSetInterface() { return false; } public boolean supportsNative() { return false; } public boolean canBeSetNative() { return false; } public boolean canBeSetPackage() { return true; } public boolean canBeSetPrivate() { return true; } public boolean canBeSetProtected() { return true; } public boolean canBeSetPublic() { return true; } public boolean canBeSetStatic() { return true; } public boolean supportsStrict() { return false; } public boolean canBeSetStrict() { return false; } public boolean supportsSynchronized() { return false; } public boolean canBeSetSynchronized() { return false; } public boolean supportsTransient() { return true; } public boolean canBeSetTransient() { return true; } public boolean supportsVolatile() { return true; } public boolean canBeSetVolatile() { return ! this.getModifier().isFinal(); } // 'final' and 'volatile' are mutually exclusive private static final int ALLOWED_MODIFIERS_FLAGS = Modifier.FINAL | Modifier.VOLATILE; public void modifierChanged(int oldCode, int newCode) { this.firePropertyChanged(MODIFIER_CODE_PROPERTY, oldCode, newCode); if (MWModifier.anyFlagsAreDifferent(ALLOWED_MODIFIERS_FLAGS, oldCode, newCode)) { this.modifier.allowedModifiersChanged(); } } public void accessLevelChanged(String oldValue, String newValue) { this.firePropertyChanged(MODIFIER_ACCESS_LEVEL_PROPERTY, oldValue, newValue); } // ********** queries ********** public boolean isStatic() { return this.getModifier().isStatic(); } public boolean isFinal() { return this.getModifier().isFinal(); } public String typeName() { return this.typeDeclaration.typeName(); } public String typeDeclaration() { return this.typeDeclaration.declaration(); } public boolean isArray() { return this.typeDeclaration.isArray(); } public boolean isValueHolder() { return this.typeDeclaration.isValueHolder(); } public boolean isTLValueHolder() { return this.typeDeclaration.isTLValueHolder(); } public boolean isBooleanPrimitive() { return this.typeDeclaration.isBooleanPrimitive(); } /** * e.g. return true if the attribute type is Object * and the specified type is String */ public boolean isAssignableFrom(MWClass type) { return this.typeDeclaration.isAssignableFrom(type); } public boolean mightBeAssignableFrom(MWClass type) { return this.typeDeclaration.mightBeAssignableFrom(type); } /** * e.g. return true if the attribute is Object and the specified type is String */ public boolean isAssignableTo(MWClass type) { return this.typeDeclaration.isAssignableTo(type); } public boolean mightBeAssignableTo(MWClass type) { return this.typeDeclaration.mightBeAssignableTo(type); } public boolean isAssignableToCollection() { return this.typeDeclaration.isAssignableToCollection(); } public boolean mightBeAssignableToCollection() { return this.typeDeclaration.mightBeAssignableToCollection(); } public boolean isAssignableToList() { return this.typeDeclaration.isAssignableToList(); } public boolean mightBeAssignableToList() { return this.typeDeclaration.mightBeAssignableToList(); } public boolean isAssignableToMap() { return this.typeDeclaration.isAssignableToMap(); } public boolean mightBeAssignableToMap() { return this.typeDeclaration.mightBeAssignableToMap(); } public boolean isAssignableToSet() { return this.typeDeclaration.isAssignableToSet(); } public boolean mightBeAssignableToSet() { return this.typeDeclaration.mightBeAssignableToSet(); } public boolean isAssignableToIndirectContainer() { return this.typeDeclaration.isAssignableToIndirectContainer(); } public boolean mightBeAssignableToIndirectContainer() { return this.typeDeclaration.mightBeAssignableToIndirectContainer(); } public boolean isContainer() { return this.isAssignableToCollection() || this.isAssignableToMap(); } public boolean mightBeContainer() { return this.mightBeAssignableToCollection() || this.mightBeAssignableToMap(); } public boolean isInstanceVariable() { return ! this.isStatic(); } public boolean isClassVariable() { return this.isStatic() && ! this.isFinal(); } public boolean isConstant() { return this.isStatic() && this.isFinal(); } public boolean isMappable() { return ! this.isStatic() && ! this.isFinal(); } public boolean canHaveValueType() { return this.isValueHolder(); } public boolean canHaveItemType() { return this.canHaveCollectionElementType() || this.canHaveMapValueType(); } public boolean canHaveCollectionElementType() { return this.mightBeAssignableToCollection() || (this.isValueHolder() && this.getValueType().mightBeAssignableToCollection()); } public boolean canHaveMapValueType() { return this.canHaveMapKeyAndValueTypes(); } public boolean canHaveKeyType() { return this.canHaveMapKeyAndValueTypes(); } /** * return whether the attribute is a map and can * have key and item (value) types */ public boolean canHaveMapKeyAndValueTypes() { return this.mightBeAssignableToMap() || (this.isValueHolder() && this.getValueType().mightBeAssignableToMap()); } public boolean canHaveValueGetAndSetMethods() { return this.canHaveValueType(); } public boolean canHaveAddAndRemoveMethods() { return this.canHaveItemType(); } /** * return the configured get method if it is specified; * otherwise return the "standard" get method if present; * otherwise return null */ public MWMethod guessGetMethod() { MWMethod method = this.getGetMethod(); return (method != null) ? method : this.standardGetMethod(); } /** * return the "standard" get method if present */ public MWMethod standardGetMethod() { return this.getDeclaringType().zeroArgumentMethodNamed(this.standardGetMethodName()); } /** * e.g. 'foo' => "getFoo[Holder]" or "isFoo" */ private String standardGetMethodName() { StringBuffer sb = new StringBuffer(this.getName().length() + 10); if (this.isBooleanPrimitive()) { sb.append(IS_PREFIX); } else { sb.append(GET_PREFIX); } StringTools.capitalizeOn(this.getName(), sb); if (this.isValueHolder()) { sb.append(HOLDER_SUFFIX); } return sb.toString(); } /** * return the configured set method if it is specified; * otherwise return the "standard" set method if present; * otherwise return null */ public MWMethod guessSetMethod() { MWMethod method = this.getSetMethod(); return (method != null) ? method : this.standardSetMethod(); } /** * return the "standard" set method if present */ public MWMethod standardSetMethod() { return this.getDeclaringType().oneArgumentMethodNamed(this.standardSetMethodName(), this.typeDeclaration); } /** * e.g. 'foo' => "setFoo[Holder]" */ private String standardSetMethodName() { StringBuffer sb = new StringBuffer(this.getName().length() + 10); sb.append(SET_PREFIX); StringTools.capitalizeOn(this.getName(), sb); if (this.isValueHolder()) { sb.append(HOLDER_SUFFIX); } return sb.toString(); } /** * return the configured "value" get method if it is specified; * otherwise return the "standard" "value" get method if present; * otherwise return null */ public MWMethod guessValueGetMethod() { MWMethod method = this.getValueGetMethod(); return (method != null) ? method : this.standardValueGetMethod(); } /** * return the "standard" "value" get method if present */ public MWMethod standardValueGetMethod() { return this.getDeclaringType().zeroArgumentMethodNamed(this.standardValueGetMethodName()); } /** * e.g. 'foo' => "getFoo" or "isFoo" */ private String standardValueGetMethodName() { StringBuffer sb = new StringBuffer(this.getName().length() + 3); if (this.isBooleanPrimitive()) { sb.append(IS_PREFIX); } else { sb.append(GET_PREFIX); } StringTools.capitalizeOn(this.getName(), sb); return sb.toString(); } /** * return the configured "value" set method if it is specified; * otherwise return the "standard" "value" set method if present; * otherwise return null */ public MWMethod guessValueSetMethod() { MWMethod method = this.getValueSetMethod(); return (method != null) ? method : this.standardValueSetMethod(); } /** * return the "standard" "value" set method if present */ public MWMethod standardValueSetMethod() { return this.getDeclaringType().oneArgumentMethodNamed(this.standardValueSetMethodName(), this.getValueType()); } /** * e.g. 'foo' => "setFoo" */ private String standardValueSetMethodName() { StringBuffer sb = new StringBuffer(this.getName().length() + 3); sb.append(SET_PREFIX); StringTools.capitalizeOn(this.getName(), sb); return sb.toString(); } /** * return the configured add method if it is specified; * otherwise return the "standard" add method if present; * otherwise return null */ public MWMethod guessAddMethod() { MWMethod method = this.getAddMethod(); return (method != null) ? method : this.standardAddMethod(); } /** * return the "standard" add method if present */ public MWMethod standardAddMethod() { if (this.canHaveKeyType()) { return this.getDeclaringType().twoArgumentMethodNamed(this.standardAddMethodName(), this.getKeyType(), this.getItemType()); } return this.getDeclaringType().oneArgumentMethodNamed(this.standardAddMethodName(), this.getItemType()); } /** * e.g. 'bars' => "addToBars" */ private String standardAddMethodName() { StringBuffer sb = new StringBuffer(this.getName().length() + 5); sb.append(ADD_PREFIX); StringTools.capitalizeOn(this.getName(), sb); return sb.toString(); } /** * return the configured remove method if it is specified; * otherwise return the "standard" remove method if present; * otherwise return null */ public MWMethod guessRemoveMethod() { MWMethod method = this.getRemoveMethod(); return (method != null) ? method : this.standardRemoveMethod(); } /** * return the "standard" remove method if present */ public MWMethod standardRemoveMethod() { MWClass parmType = this.canHaveKeyType() ? this.getKeyType() : this.getItemType(); return this.getDeclaringType().oneArgumentMethodNamed(this.standardRemoveMethodName(), parmType); } /** * e.g. 'bars' => "removeFromBars" */ private String standardRemoveMethodName() { StringBuffer sb = new StringBuffer(this.getName().length() + 10); sb.append(REMOVE_PREFIX); StringTools.capitalizeOn(this.getName(), sb); return sb.toString(); } public Iterator candidateGetMethods() { return this.getDeclaringType().candidateGetMethodsFor(this); } public Iterator candidateSetMethods() { return this.getDeclaringType().candidateSetMethodsFor(this); } public Iterator candidateValueGetMethods() { return this.canHaveValueType() ? this.getDeclaringType().candidateGetMethodsFor(this.getValueType()) : NullIterator.instance(); } public Iterator candidateValueSetMethods() { return this.canHaveValueType() ? this.getDeclaringType().candidateSetMethodsFor(this.getValueType()) : NullIterator.instance(); } public Iterator candidateAddMethods() { return this.canHaveItemType() ? this.canHaveKeyType() ? this.getDeclaringType().candidateAddMethodsFor(this.getKeyType(), this.getItemType()) : this.getDeclaringType().candidateAddMethodsFor(this.getItemType()) : NullIterator.instance(); } public Iterator candidateRemoveMethods() { return this.canHaveItemType() ? this.getDeclaringType().candidateRemoveMethodsFor(this.canHaveKeyType() ? this.getKeyType() : this.getItemType()) : NullIterator.instance(); } /** * NB: some elements in the collection may be null */ public Collection allAccessors() { Collection accessors = new ArrayList(); accessors.add(this.getGetMethod()); accessors.add(this.getSetMethod()); accessors.add(this.getValueGetMethod()); accessors.add(this.getValueSetMethod()); accessors.add(this.getAddMethod()); accessors.add(this.getRemoveMethod()); return accessors; } public MWMethodCodeGenPolicy accessorCodeGenPolicy(MWMethod accessor, MWClassCodeGenPolicy classCodeGenPolicy) { if (accessor == this.getGetMethod()) { return new MWGetMethodCodeGenPolicy(accessor, this, classCodeGenPolicy); } else if (accessor == this.getSetMethod()) { return new MWSetMethodCodeGenPolicy(accessor, this, classCodeGenPolicy); } else if (accessor == this.getValueGetMethod()) { return new MWValueGetMethodCodeGenPolicy(accessor, this, classCodeGenPolicy); } else if (accessor == this.getValueSetMethod()) { return new MWValueSetMethodCodeGenPolicy(accessor, this, classCodeGenPolicy); } else if (accessor == this.getAddMethod()) { return new MWAddMethodCodeGenPolicy(accessor, this, classCodeGenPolicy); } else if (accessor == this.getRemoveMethod()) { return new MWRemoveMethodCodeGenPolicy(accessor, this, classCodeGenPolicy); } else { throw new IllegalArgumentException(accessor.toString()); } } public MWMethodCodeGenPolicy accessorCodeGenPolicy(MWMethod accessor, MWClassAttribute backPointerAttribute, boolean isPrivateOwned, MWClassCodeGenPolicy classCodeGenPolicy) { if (backPointerAttribute == null) { return this.accessorCodeGenPolicy(accessor, classCodeGenPolicy); } if (accessor == this.getAddMethod()) { return new MWAddMethodCodeGenPolicy(accessor, this, backPointerAttribute, classCodeGenPolicy); } else if (accessor == this.getRemoveMethod()) { return new MWRemoveMethodCodeGenPolicy(accessor, this, backPointerAttribute, isPrivateOwned, classCodeGenPolicy); } else { throw new IllegalArgumentException(accessor.toString()); } } public boolean isEjb20Attribute() { return this.getDeclaringType().ejb20AttributesContains(this); } public boolean isUnknownPrimaryKeyAttribute() { return this.getDeclaringType().getUnknownPrimaryKeyAttribute() == this; } /** * code gen: * used by Wallace code gen */ NonreflectiveAttributeDefinition attributeDefinition() { NonreflectiveAttributeDefinition def = new NonreflectiveAttributeDefinition(); def.setAccessLevel(this.getModifier().accessLevel()); def.setType(this.typeDeclaration.declaration()); def.setName(this.getName()); return def; } /** * code gen: * initial value is only necessary when the attribute * is a value holder or a container */ public String initialValueSourceCode(MWClassCodeGenPolicy classCodeGenPolicy) { MWClass concreteValueType = null; if (this.isValueHolder()) { concreteValueType = this.getValueType(); } else if (this.getDimensionality() == 0) { concreteValueType = this.getType(); } else { // leave 'concreteValueType' null for an array concreteValueType = null; } if (concreteValueType == null) { // "new ValueHolder()" or "" return this.initialValueSourceCodeFor(null); } if (concreteValueType.isContainer()) { return this.initialContainerValueSourceCodeFor(classCodeGenPolicy, concreteValueType); } // "new ValueHolder()" or "" return this.initialValueSourceCodeFor(null); } /** * code gen: * return code to construct a new container, if possible */ private String initialContainerValueSourceCodeFor(MWClassCodeGenPolicy classCodeGenPolicy, MWClass containerValueType) { MWClass containerImplementationType = containerValueType.defaultContainerImplementationType(); if (containerImplementationType != null) { // e.g. "new ValueHolder(new ArrayList())" or "new ArrayList()" return this.initialValueSourceCodeFor(containerImplementationType); } StringBuffer sb = new StringBuffer(200); sb.append(CR); sb.append("\t\t"); sb.append(classCodeGenPolicy.collectionImplementationClassNotDeterminedComment(this, containerValueType)); sb.append(CR); sb.append("\t\t"); // "new ValueHolder()" or "" this.appendInitialValueSourceCodeFor(null, sb); return sb.toString(); } /** * code gen: * the specified concrete value type is the default "value" type for the attribute * (null if it is unknown); we use the zero-argument constructor if appropriate; * we return an empty string if the attribute is not a value holder and the concrete * value type is null or cannot be instantiated */ public String initialValueSourceCodeFor(MWClass concreteValueType) { StringBuffer sb = new StringBuffer(80); this.appendInitialValueSourceCodeFor(concreteValueType, sb); return sb.toString(); } private void appendInitialValueSourceCodeFor(MWClass concreteValueType, StringBuffer sb) { if (concreteValueType != null) { if ( ! concreteValueType.isInstantiable()) { concreteValueType = null; } } boolean valueHolder = this.isValueHolder(); if (valueHolder) { sb.append("new "); sb.append(this.typeFor(ValueHolder.class).getName()); sb.append('('); } if (concreteValueType != null) { sb.append("new "); sb.append(concreteValueType.getName()); sb.append("()"); } if (valueHolder) { sb.append(')'); } } private MWClass objectType() { return this.typeFor(java.lang.Object.class); } // ********** behavior ********** void refresh(ExternalField externalField) { if ( ! this.getName().equals(externalField.getName())) { throw new IllegalArgumentException(externalField.getName()); } this.getModifier().refresh(externalField.getModifiers()); this.typeDeclaration.refresh(externalField.getType()); // the value, element, and key types are set manually - they cannot be refreshed from the Java field } /** * This is used by MWMethod to ensure that a method can only be an accessor for a single * attribute. This is called by the method before the new attribute-accessor relationship * is set up. */ void removeAccessorMethod(MWMethod method) { if (method == this.getGetMethod()) { this.setGetMethod(null); } else if (method == this.getSetMethod()) { this.setSetMethod(null); } else if (method == this.getValueGetMethod()) { this.setValueGetMethod(null); } else if (method == this.getValueSetMethod()) { this.setValueSetMethod(null); } else if (method == this.getAddMethod()) { this.setAddMethod(null); } else if (method == this.getRemoveMethod()) { this.setRemoveMethod(null); } } /** * - when converting to a value holder move the old, non-value holder, type * to the value type; * - when converting to a container move the old, non-container, type * to the element type; * - when converting from a container within a value holder to a container, * leave the item type alone */ private void updateNestedTypes(MWClass oldType) { if (this.getDimensionality() == 0) { if (this.getType().isValueHolder() && ! oldType.isValueHolder()) { this.setValueType(oldType); } if (this.getType().isContainer() && ! oldType.isContainer()) { // don't call 'this.getItemType()' because it will not return null at this point if (this.itemTypeHandle.getType() == null) { this.setItemType(oldType); } } } this.updateNestedTypes(); } private void updateNestedTypes() { this.updateValueType(); this.updateContainerTypes(); } private void updateValueType() { if ( ! this.canHaveValueType()) { this.setValueType(null); } this.updateValueGetAndSetMethods(); } private void updateContainerTypes() { this.updateItemType(); this.updateKeyType(); } private void updateItemType() { if ( ! this.canHaveItemType()) { this.setItemType(null); } this.updateAddAndRemoveMethods(); } private void updateKeyType() { if ( ! this.canHaveKeyType()) { this.setKeyType(null); } this.updateAddAndRemoveMethods(); } private void updateGetAndSetMethods() { this.updateZeroArgumentMethod(this.getGetMethod(), this.getType(), this.getDimensionality()); this.updateSingleArgumentMethod(this.getSetMethod(), this.getType(), this.getDimensionality()); } private void updateValueGetAndSetMethods() { if (this.canHaveValueType()) { this.updateZeroArgumentMethod(this.getValueGetMethod(), this.getValueType()); this.updateSingleArgumentMethod(this.getValueSetMethod(), this.getValueType()); } else { this.setValueGetMethod(null); this.setValueSetMethod(null); } } private void updateAddAndRemoveMethods() { if (this.canHaveItemType()) { if (this.canHaveKeyType()) { this.updateTwoArgumentMethod(this.getAddMethod(), this.getKeyType(), this.getItemType()); this.updateSingleArgumentMethod(this.getRemoveMethod(), this.getKeyType()); } else { this.updateSingleArgumentMethod(this.getAddMethod(), this.getItemType()); this.updateSingleArgumentMethod(this.getRemoveMethod(), this.getItemType()); } } else { this.setAddMethod(null); this.setRemoveMethod(null); } } private void updateZeroArgumentMethod(MWMethod method, MWClass returnType) { this.updateZeroArgumentMethod(method, returnType, 0); } private void updateZeroArgumentMethod(MWMethod method, MWClass returnType, int returnTypeDimensionality) { if (method == null) { return; } method.setReturnType(returnType); method.setReturnTypeDimensionality(returnTypeDimensionality); method.clearMethodParameters(); } private void updateSingleArgumentMethod(MWMethod method, MWClass argumentType) { this.updateSingleArgumentMethod(method, argumentType, 0); } private void updateSingleArgumentMethod(MWMethod method, MWClass argumentType, int argumentDimensionality) { this.updateMethodArguments(method, new MWClass[] {argumentType}, new int[] {argumentDimensionality}); } private void updateTwoArgumentMethod(MWMethod method, MWClass argumentType1, MWClass argumentType2) { this.updateTwoArgumentMethod(method, argumentType1, 0, argumentType2, 0); } private void updateTwoArgumentMethod(MWMethod method, MWClass argumentType1, int argumentDimensionality1, MWClass argumentType2, int argumentDimensionality2) { this.updateMethodArguments(method, new MWClass[] {argumentType1, argumentType2}, new int[] {argumentDimensionality1, argumentDimensionality2}); } private void updateMethodArguments(MWMethod method, MWClass[] argumentTypes, int[] argumentDimensionalities) { if (method == null) { return; } int len = argumentTypes.length; if (argumentDimensionalities.length != len) { throw new IllegalArgumentException("arrays are different lengths"); } if (method.methodParametersSize() == 0) { for (int i = 0; i < len; i++) { method.addMethodParameter(argumentTypes[i], argumentDimensionalities[i]); } } else { while (method.methodParametersSize() < len) { int index = method.methodParametersSize(); method.addMethodParameter(argumentTypes[index], argumentDimensionalities[index]); } while (method.methodParametersSize() > len) { method.removeMethodParameter(len); } for (int i = 0; i < len; i++) { method.getMethodParameter(i).setType(argumentTypes[i]); method.getMethodParameter(i).setDimensionality(argumentDimensionalities[i]); } } } private void updateAccessorNames() { if (this.getGetMethod() != null) { this.getGetMethod().setName(this.standardGetMethodName()); } if (this.getSetMethod() != null) { this.getSetMethod().setName(this.standardSetMethodName()); } if (this.getValueGetMethod() != null) { this.getValueGetMethod().setName(this.standardValueGetMethodName()); } if (this.getValueSetMethod() != null) { this.getValueSetMethod().setName(this.standardValueSetMethodName()); } if (this.getAddMethod() != null) { this.getAddMethod().setName(this.standardAddMethodName()); } if (this.getRemoveMethod() != null) { this.getRemoveMethod().setName(this.standardRemoveMethodName()); } } /** * Generate methods: * - get and set methods @see #generateGetAndSetMethods() * - "value" get and set methods if appropriate @see #generateValueGetAndSetMethods() * - add and remove methods if appropriate @see #generateAddAndRemoveMethods() . */ public void generateAllAccessors() { this.generateGetAndSetMethods(); if (this.canHaveValueGetAndSetMethods()) { this.generateValueGetAndSetMethods(); } if (this.canHaveAddAndRemoveMethods()) { this.generateAddAndRemoveMethods(); } } public void generateGetAndSetMethods() { this.generateGetMethod(); this.generateSetMethod(); } private void generateGetMethod() { MWMethod getMethod = this.guessGetMethod(); if (getMethod == null) { getMethod = this.getDeclaringType().addMethod(this.standardGetMethodName()); // new getters for value holders, collections, or maps should be protected if (this.isValueHolder() || this.isContainer()) { getMethod.getModifier().setProtected(true); } } getMethod.setReturnType(this.getType()); getMethod.setReturnTypeDimensionality(this.getDimensionality()); // EJB getters must be abstract; non-EJB getters must not be abstract getMethod.getModifier().setAbstract(this.isEjb20Attribute()); // EJB getters must be public. if (this.isEjb20Attribute()) { getMethod.getModifier().setPublic(true); } this.setGetMethod(getMethod); } private void generateSetMethod() { MWMethod setMethod = this.guessSetMethod(); if (setMethod == null) { setMethod = this.getDeclaringType().addMethod(this.standardSetMethodName()); setMethod.addMethodParameter(this.getType(), this.getDimensionality()); // new setters for value holders, collections, or maps should be protected if (this.isValueHolder() || this.isContainer()) { setMethod.getModifier().setProtected(true); } } // EJB setters must be abstract; non-EJB setters must not be abstract setMethod.getModifier().setAbstract(this.isEjb20Attribute()); // EJB setters must be public and must return void if (this.isEjb20Attribute()) { setMethod.getModifier().setPublic(true); setMethod.setReturnType(this.getRepository().voidType()); } this.setSetMethod(setMethod); } public void generateValueGetAndSetMethods() { this.generateValueGetMethod(); this.generateValueSetMethod(); } public void generateValueGetMethod() { MWMethod valueGetMethod = this.guessValueGetMethod(); if (valueGetMethod == null) { valueGetMethod = this.getDeclaringType().addMethod(this.standardValueGetMethodName()); // vew value get methods for collections or maps should be protected if (this.getValueType().isContainer()) { valueGetMethod.getModifier().setProtected(true); } } valueGetMethod.setReturnType(this.getValueType()); valueGetMethod.setReturnTypeDimensionality(0); this.setValueGetMethod(valueGetMethod); } public void generateValueSetMethod() { MWMethod valueSetMethod = this.guessValueSetMethod(); if (valueSetMethod == null) { valueSetMethod = this.getDeclaringType().addMethod(this.standardValueSetMethodName()); valueSetMethod.addMethodParameter(this.getValueType()); // new value set methods for collections or maps should be protected if (this.getValueType().isContainer()) { valueSetMethod.getModifier().setProtected(true); } } this.setValueSetMethod(valueSetMethod); } public void generateAddAndRemoveMethods() { this.generateAddMethod(); this.generateRemoveMethod(); } private void generateAddMethod() { if (this.canHaveKeyType()) { this.generateAddMethodForMap(); } else { this.generateAddMethodForCollection(); } } private void generateAddMethodForCollection() { MWMethod addMethod = this.guessAddMethod(); if (addMethod == null) { addMethod = this.getDeclaringType().addMethod(this.standardAddMethodName()); addMethod.addMethodParameter(this.getItemType()); } this.setAddMethod(addMethod); } private void generateAddMethodForMap() { MWMethod addMethod = this.guessAddMethod(); if (addMethod == null) { addMethod = this.getDeclaringType().addMethod(this.standardAddMethodName()); addMethod.addMethodParameter(this.getKeyType()); addMethod.addMethodParameter(this.getItemType()); } this.setAddMethod(addMethod); } private void generateRemoveMethod() { if (this.canHaveKeyType()) { this.generateRemoveMethodForMap(); } else { this.generateRemoveMethodForCollection(); } } private void generateRemoveMethodForCollection() { MWMethod removeMethod = this.guessRemoveMethod(); if (removeMethod == null) { removeMethod = this.getDeclaringType().addMethod(this.standardRemoveMethodName()); removeMethod.addMethodParameter(this.getItemType()); } this.setRemoveMethod(removeMethod); } private void generateRemoveMethodForMap() { MWMethod removeMethod = this.guessRemoveMethod(); if (removeMethod == null) { removeMethod = this.getDeclaringType().addMethod(this.standardRemoveMethodName()); removeMethod.addMethodParameter(this.getKeyType()); } this.setRemoveMethod(removeMethod); } public void setEjb20Attribute(boolean value) { if (this.isEjb20Attribute() == value) { return; } if (value) { this.getDeclaringType().changeToEjb20(this); } else { this.getDeclaringType().changeFromEjb20(this); } } public void addAccessorCodeGenPoliciesTo(MWClassCodeGenPolicy classCodeGenPolicy) { this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, this.getGetMethod()); this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, this.getSetMethod()); this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, this.getValueGetMethod()); this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, this.getValueSetMethod()); this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, this.getAddMethod()); this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, this.getRemoveMethod()); } private void addAccessorCodeGenPolicyTo(MWClassCodeGenPolicy classCodeGenPolicy, MWMethod method) { if (method != null) { classCodeGenPolicy.addAccessorCodeGenPolicy(method, this.accessorCodeGenPolicy(method, classCodeGenPolicy)); } } public void addAccessorCodeGenPoliciesTo(MWClassCodeGenPolicy classCodeGenPolicy, MWMapping mapping) { // as of now, only add and remove methods have mapping-added information this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, mapping, this.getAddMethod()); this.addAccessorCodeGenPolicyTo(classCodeGenPolicy, mapping, this.getRemoveMethod()); } private void addAccessorCodeGenPolicyTo(MWClassCodeGenPolicy classCodeGenPolicy, MWMapping mapping, MWMethod method) { if (method != null) { classCodeGenPolicy.addAccessorCodeGenPolicy(method, mapping.accessorCodeGenPolicy(method, classCodeGenPolicy)); } } // ********** problems *********** protected void addProblemsTo(List currentProblems) { super.addProblemsTo(currentProblems); // @see #addDescriptorProblemsTo(List) } /** * NB: until we have a visible class repository, these problems * are added to the descriptor's problems */ void addDescriptorProblemsTo(List currentProblems) { this.checkGetMethod(currentProblems); this.checkSetMethod(currentProblems); this.checkValueGetMethod(currentProblems); this.checkValueSetMethod(currentProblems); this.checkAddMethod(currentProblems); this.checkRemoveMethod(currentProblems); } private void checkGetMethod(List currentProblems) { MWMethod getMethod = this.getGetMethod(); if (getMethod == null) { return; } if (getMethod.methodParametersSize() == 0) { if ( ! getMethod.getReturnTypeDeclaration().mightBeAssignableFrom(this.typeDeclaration)) { currentProblems.add(this.buildProblem(ProblemConstants.GET_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.GET_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkSetMethod(List currentProblems) { MWMethod setMethod = this.getSetMethod(); if (setMethod == null) { return; } if (setMethod.methodParametersSize() == 1) { if ( ! setMethod.getMethodParameter().mightBeAssignableTo(this.typeDeclaration)) { currentProblems.add(this.buildProblem(ProblemConstants.SET_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.SET_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkValueGetMethod(List currentProblems) { MWMethod valueGetMethod = this.getValueGetMethod(); if (valueGetMethod == null) { return; } if (valueGetMethod.methodParametersSize() == 0) { if ( ! valueGetMethod.getReturnTypeDeclaration().mightBeAssignableFrom(this.getValueType())) { currentProblems.add(this.buildProblem(ProblemConstants.VALUE_GET_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.VALUE_GET_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkValueSetMethod(List currentProblems) { MWMethod valueSetMethod = this.getValueSetMethod(); if (valueSetMethod == null) { return; } if (valueSetMethod.methodParametersSize() == 1) { if ( ! valueSetMethod.getMethodParameter().mightBeAssignableTo(this.getValueType())) { currentProblems.add(this.buildProblem(ProblemConstants.VALUE_SET_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.VALUE_SET_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkAddMethod(List currentProblems) { if (this.canHaveItemType()) { if (this.canHaveKeyType()) { this.checkAddMethodForMap(currentProblems); } else { this.checkAddMethodForCollection(currentProblems); } } } private void checkAddMethodForCollection(List currentProblems) { MWMethod addMethod = this.getAddMethod(); if (addMethod == null) { return; } if (addMethod.methodParametersSize() == 1) { if ( ! addMethod.getMethodParameter().mightBeAssignableTo(this.getItemType())) { currentProblems.add(this.buildProblem(ProblemConstants.ADD_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.ADD_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkAddMethodForMap(List currentProblems) { MWMethod addMethod = this.getAddMethod(); if (addMethod == null) { return; } if (addMethod.methodParametersSize() == 2) { if ( ! addMethod.getMethodParameter(0).mightBeAssignableTo(this.getKeyType()) || ! addMethod.getMethodParameter(1).mightBeAssignableTo(this.getItemType())) { currentProblems.add(this.buildProblem(ProblemConstants.MAP_ADD_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.MAP_ADD_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkRemoveMethod(List currentProblems) { if (this.canHaveItemType()) { if (this.canHaveKeyType()) { this.checkRemoveMethodForMap(currentProblems); } else { this.checkRemoveMethodForCollection(currentProblems); } } } private void checkRemoveMethodForCollection(List currentProblems) { MWMethod removeMethod = this.getRemoveMethod(); if (removeMethod == null) { return; } if (removeMethod.methodParametersSize() == 1) { if ( ! removeMethod.getMethodParameter().mightBeAssignableTo(this.getItemType())) { currentProblems.add(this.buildProblem(ProblemConstants.REMOVE_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.REMOVE_METHOD_PARMS_SIZE_INVALID, this.getName())); } } private void checkRemoveMethodForMap(List currentProblems) { MWMethod removeMethod = this.getRemoveMethod(); if (removeMethod == null) { return; } if (removeMethod.methodParametersSize() == 1) { if ( ! removeMethod.getMethodParameter().mightBeAssignableTo(this.getKeyType())) { currentProblems.add(this.buildProblem(ProblemConstants.MAP_REMOVE_METHOD_VS_ATTRIBUTE_MISMATCH, this.getName())); } } else { currentProblems.add(this.buildProblem(ProblemConstants.MAP_REMOVE_METHOD_PARMS_SIZE_INVALID, this.getName())); } } // ********** containment hierarchy ********** protected void addChildrenTo(List children) { super.addChildrenTo(children); children.add(this.modifier); children.add(this.typeDeclaration); children.add(this.getMethodHandle); children.add(this.setMethodHandle); children.add(this.valueGetMethodHandle); children.add(this.valueSetMethodHandle); children.add(this.addMethodHandle); children.add(this.removeMethodHandle); children.add(this.itemTypeHandle); children.add(this.valueTypeHandle); children.add(this.keyTypeHandle); } private NodeReferenceScrubber buildGetMethodScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setGetMethod(null); } public String toString() { return "MWClassAttribute.buildGetMethodScrubber()"; } }; } private NodeReferenceScrubber buildSetMethodScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setSetMethod(null); } public String toString() { return "MWClassAttribute.buildSetMethodScrubber()"; } }; } private NodeReferenceScrubber buildValueGetMethodScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setValueGetMethod(null); } public String toString() { return "MWClassAttribute.buildValueGetMethodScrubber()"; } }; } private NodeReferenceScrubber buildValueSetMethodScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setValueSetMethod(null); } public String toString() { return "MWClassAttribute.buildValueSetMethodScrubber()"; } }; } private NodeReferenceScrubber buildAddMethodScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setAddMethod(null); } public String toString() { return "MWClassAttribute.buildAddMethodScrubber()"; } }; } private NodeReferenceScrubber buildRemoveMethodScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setRemoveMethod(null); } public String toString() { return "MWClassAttribute.buildRemoveMethodScrubber()"; } }; } private NodeReferenceScrubber buildValueTypeScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setValueType(null); } public String toString() { return "MWClassAttribute.buildValueTypeScrubber()"; } }; } private NodeReferenceScrubber buildItemTypeScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setItemType(null); } public String toString() { return "MWClassAttribute.buildItemTypeScrubber()"; } }; } private NodeReferenceScrubber buildKeyTypeScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWClassAttribute.this.setKeyType(null); } public String toString() { return "MWClassAttribute.buildKeyTypeScrubber()"; } }; } public void nodeRenamed(Node node) { super.nodeRenamed(node); if (this.getType() == node) { this.firePropertyChanged(DECLARATION_PROPERTY, "type"); // don't waste time building the declaration - it's ignored } } // ********** displaying and printing ********** public String displayString() { return this.getName(); } public void toString(StringBuffer sb) { sb.append(this.getName()); } /** * e.g. "foo : java.lang.Object" */ public String nameWithType() { return this.nameWithType(true); } /** * e.g. "foo : Object" */ public String nameWithShortType() { return this.nameWithType(false); } private String nameWithType(boolean fullyQualified) { StringBuffer sb = new StringBuffer(); sb.append(this.name); sb.append(" : "); // sorta like the UML convention if (fullyQualified) { this.typeDeclaration.printDeclarationOn(sb); } else { this.typeDeclaration.printShortDeclarationOn(sb); } return sb.toString(); } // ********** TopLink methods ********** public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(MWClassAttribute.class); descriptor.addDirectMapping("name", "name/text()"); XMLDirectMapping modifierMapping = (XMLDirectMapping) descriptor.addDirectMapping("modifier", "getModifierForTopLink", "setModifierForTopLink", "modifier/text()"); modifierMapping.setNullValue(new Integer(0)); XMLCompositeObjectMapping typeDeclarationMapping = new XMLCompositeObjectMapping(); typeDeclarationMapping.setAttributeName("typeDeclaration"); typeDeclarationMapping.setReferenceClass(MWTypeDeclaration.class); typeDeclarationMapping.setXPath("type-declaration"); descriptor.addMapping(typeDeclarationMapping); XMLCompositeObjectMapping valueTypeHandleMapping = new XMLCompositeObjectMapping(); valueTypeHandleMapping.setAttributeName("valueTypeHandle"); valueTypeHandleMapping.setGetMethodName("getValueTypeHandleForTopLink"); valueTypeHandleMapping.setSetMethodName("setValueTypeHandleForTopLink"); valueTypeHandleMapping.setReferenceClass(MWClassHandle.class); valueTypeHandleMapping.setXPath("value-type-handle"); descriptor.addMapping(valueTypeHandleMapping); XMLCompositeObjectMapping itemTypeHandleMapping = new XMLCompositeObjectMapping(); itemTypeHandleMapping.setAttributeName("itemTypeHandle"); itemTypeHandleMapping.setGetMethodName("getItemTypeHandleForTopLink"); itemTypeHandleMapping.setSetMethodName("setItemTypeHandleForTopLink"); itemTypeHandleMapping.setReferenceClass(MWClassHandle.class); itemTypeHandleMapping.setXPath("item-type-handle"); descriptor.addMapping(itemTypeHandleMapping); XMLCompositeObjectMapping keyTypeHandleMapping = new XMLCompositeObjectMapping(); keyTypeHandleMapping.setAttributeName("keyTypeHandle"); keyTypeHandleMapping.setGetMethodName("getKeyTypeHandleForTopLink"); keyTypeHandleMapping.setSetMethodName("setKeyTypeHandleForTopLink"); keyTypeHandleMapping.setReferenceClass(MWClassHandle.class); keyTypeHandleMapping.setXPath("key-type-handle"); descriptor.addMapping(keyTypeHandleMapping); XMLCompositeObjectMapping getMethodHandleMapping = new XMLCompositeObjectMapping(); getMethodHandleMapping.setAttributeName("getMethodHandle"); getMethodHandleMapping.setGetMethodName("getGetMethodHandleForTopLink"); getMethodHandleMapping.setSetMethodName("setGetMethodHandleForTopLink"); getMethodHandleMapping.setReferenceClass(MWMethodHandle.class); getMethodHandleMapping.setXPath("get-method-handle"); descriptor.addMapping(getMethodHandleMapping); XMLCompositeObjectMapping setMethodHandleMapping = new XMLCompositeObjectMapping(); setMethodHandleMapping.setAttributeName("setMethodHandle"); setMethodHandleMapping.setGetMethodName("getSetMethodHandleForTopLink"); setMethodHandleMapping.setSetMethodName("setSetMethodHandleForTopLink"); setMethodHandleMapping.setReferenceClass(MWMethodHandle.class); setMethodHandleMapping.setXPath("set-method-handle"); descriptor.addMapping(setMethodHandleMapping); XMLCompositeObjectMapping valueGetMethodHandleMapping = new XMLCompositeObjectMapping(); valueGetMethodHandleMapping.setAttributeName("valueGetMethodHandle"); valueGetMethodHandleMapping.setGetMethodName("getValueGetMethodHandleForTopLink"); valueGetMethodHandleMapping.setSetMethodName("setValueGetMethodHandleForTopLink"); valueGetMethodHandleMapping.setReferenceClass(MWMethodHandle.class); valueGetMethodHandleMapping.setXPath("value-get-method-handle"); descriptor.addMapping(valueGetMethodHandleMapping); XMLCompositeObjectMapping valueSetMethodHandleMapping = new XMLCompositeObjectMapping(); valueSetMethodHandleMapping.setAttributeName("valueSetMethodHandle"); valueSetMethodHandleMapping.setGetMethodName("getValueSetMethodHandleForTopLink"); valueSetMethodHandleMapping.setSetMethodName("setValueSetMethodHandleForTopLink"); valueSetMethodHandleMapping.setReferenceClass(MWMethodHandle.class); valueSetMethodHandleMapping.setXPath("value-set-method-handle"); descriptor.addMapping(valueSetMethodHandleMapping); XMLCompositeObjectMapping addMethodHandleMapping = new XMLCompositeObjectMapping(); addMethodHandleMapping.setAttributeName("addMethodHandle"); addMethodHandleMapping.setGetMethodName("getAddMethodHandleForTopLink"); addMethodHandleMapping.setSetMethodName("setAddMethodHandleForTopLink"); addMethodHandleMapping.setReferenceClass(MWMethodHandle.class); addMethodHandleMapping.setXPath("add-method-handle"); descriptor.addMapping(addMethodHandleMapping); XMLCompositeObjectMapping removeMethodHandleMapping = new XMLCompositeObjectMapping(); removeMethodHandleMapping.setAttributeName("removeMethodHandle"); removeMethodHandleMapping.setGetMethodName("getRemoveMethodHandleForTopLink"); removeMethodHandleMapping.setSetMethodName("setRemoveMethodHandleForTopLink"); removeMethodHandleMapping.setReferenceClass(MWMethodHandle.class); removeMethodHandleMapping.setXPath("remove-method-handle"); descriptor.addMapping(removeMethodHandleMapping); return descriptor; } /** * store the modifier as an int */ private int getModifierForTopLink() { return this.modifier.getCode(); } private void setModifierForTopLink(int code) { this.modifier.setCodeForTopLink(code); } private MWClassHandle getValueTypeHandleForTopLink() { return (this.valueTypeHandle.getType() == null) ? null : this.valueTypeHandle; } private void setValueTypeHandleForTopLink(MWClassHandle valueTypeHandle) { NodeReferenceScrubber scrubber = this.buildValueTypeScrubber(); this.valueTypeHandle = ((valueTypeHandle == null) ? new MWClassHandle(this, scrubber) : valueTypeHandle.setScrubber(scrubber)); } private MWClassHandle getItemTypeHandleForTopLink() { return (this.itemTypeHandle.getType() == null) ? null : this.itemTypeHandle; } private void setItemTypeHandleForTopLink(MWClassHandle itemTypeHandle) { NodeReferenceScrubber scrubber = this.buildItemTypeScrubber(); this.itemTypeHandle = ((itemTypeHandle == null) ? new MWClassHandle(this, scrubber) : itemTypeHandle.setScrubber(scrubber)); } private MWClassHandle getKeyTypeHandleForTopLink() { return (this.keyTypeHandle.getType() == null) ? null : this.keyTypeHandle; } private void setKeyTypeHandleForTopLink(MWClassHandle keyTypeHandle) { NodeReferenceScrubber scrubber = this.buildKeyTypeScrubber(); this.keyTypeHandle = ((keyTypeHandle == null) ? new MWClassHandle(this, scrubber) : keyTypeHandle.setScrubber(scrubber)); } private MWMethodHandle getGetMethodHandleForTopLink() { return (this.getMethodHandle.getMethod() == null) ? null : this.getMethodHandle; } private void setGetMethodHandleForTopLink(MWMethodHandle getMethodHandle) { NodeReferenceScrubber scrubber = this.buildGetMethodScrubber(); this.getMethodHandle = ((getMethodHandle == null) ? new MWMethodHandle(this, scrubber) : getMethodHandle.setScrubber(scrubber)); } private MWMethodHandle getSetMethodHandleForTopLink() { return (this.setMethodHandle.getMethod() == null) ? null : setMethodHandle; } private void setSetMethodHandleForTopLink(MWMethodHandle setMethodHandle) { NodeReferenceScrubber scrubber = this.buildSetMethodScrubber(); this.setMethodHandle = ((setMethodHandle == null) ? new MWMethodHandle(this, scrubber) : setMethodHandle.setScrubber(scrubber)); } private MWMethodHandle getValueGetMethodHandleForTopLink() { return (this.valueGetMethodHandle.getMethod() == null) ? null : valueGetMethodHandle; } private void setValueGetMethodHandleForTopLink(MWMethodHandle valueGetMethodHandle) { NodeReferenceScrubber scrubber = this.buildValueGetMethodScrubber(); this.valueGetMethodHandle = ((valueGetMethodHandle == null) ? new MWMethodHandle(this, scrubber) : valueGetMethodHandle.setScrubber(scrubber)); } private MWMethodHandle getValueSetMethodHandleForTopLink() { return (this.valueSetMethodHandle.getMethod() == null) ? null : valueSetMethodHandle; } private void setValueSetMethodHandleForTopLink(MWMethodHandle valueSetMethodHandle) { NodeReferenceScrubber scrubber = this.buildValueSetMethodScrubber(); this.valueSetMethodHandle = ((valueSetMethodHandle == null) ? new MWMethodHandle(this, scrubber) : valueSetMethodHandle.setScrubber(scrubber)); } private MWMethodHandle getAddMethodHandleForTopLink() { return (this.addMethodHandle.getMethod() == null) ? null : this.addMethodHandle; } private void setAddMethodHandleForTopLink(MWMethodHandle addMethodHandle) { NodeReferenceScrubber scrubber = this.buildAddMethodScrubber(); this.addMethodHandle = ((addMethodHandle == null) ? new MWMethodHandle(this, scrubber) : addMethodHandle.setScrubber(scrubber)); } private MWMethodHandle getRemoveMethodHandleForTopLink() { return (this.removeMethodHandle.getMethod() == null) ? null : this.removeMethodHandle; } private void setRemoveMethodHandleForTopLink(MWMethodHandle removeMethodHandle) { NodeReferenceScrubber scrubber = this.buildRemoveMethodScrubber(); this.removeMethodHandle = ((removeMethodHandle == null) ? new MWMethodHandle(this, scrubber) : removeMethodHandle.setScrubber(scrubber)); } }