/*******************************************************************************
* 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.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.TreeSet;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWAttributeHandle;
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.MWHandle.NodeReferenceScrubber;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalClassDescription;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalConstructor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalMethod;
import org.eclipse.persistence.tools.workbench.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneListIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.TransformationIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.eclipse.persistence.oxm.mappings.XMLTransformationMapping;
import org.eclipse.persistence.sessions.Record;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.internal.codegen.NonreflectiveMethodDefinition;
/**
* This class models a Java method or constructor.
*/
public final class MWMethod
extends MWModel implements MWModifiable
{
private volatile String name;
public static final String NAME_PROPERTY = "name";
private MWModifier modifier; // pseudo-final
/**
* the "signature" property is a virtual property that clients can listen to;
* those clients will be notified when the method's "extended" signature changes
*/
public static final String SIGNATURE_PROPERTY = "signature";
private MWTypeDeclaration returnTypeDeclaration;
public static final String RETURN_TYPE_PROPERTY = "returnType";
public static final String RETURN_TYPE_DIMENSIONALITY_PROPERTY = "returnTypeDimensionality";
private List methodParameters;
public static final String METHOD_PARAMETERS_LIST = "methodParameters";
private Collection exceptionTypeHandles;
public static final String EXCEPTION_TYPES_COLLECTION = "exceptionTypes";
private NodeReferenceScrubber exceptionTypeScrubber;
/** this differentiates methods and constructors */
private volatile boolean constructor;
public static final String CONSTRUCTOR_PROPERTY = "constructor";
/** a method can be an "accessor" for an attribute (e.g. getFoo(), setFoo(Foo), addFoo(Foo)) */
private MWAttributeHandle accessedAttributeHandle;
private static final MWClass[] EMPTY_TYPE_ARRAY = new MWClass[0];
private static final int[] EMPTY_INT_ARRAY = new int[0];
// ********** constructors **********
/**
* Default constructor - for TopLink use only.
*/
private MWMethod() {
super();
}
MWMethod(MWClass declaringType, String name) {
super(declaringType);
this.name = name;
this.setConstructor(name.equals(this.getDeclaringType().shortName()));
}
MWMethod(MWClass declaringType, String name, MWClass returnType) {
this(declaringType, name);
this.setReturnType(returnType);
}
MWMethod(MWClass declaringType, String name, MWClass returnType, int dimensionality) {
this(declaringType, name);
this.setReturnTypeDeclaration(returnType, dimensionality);
}
MWMethod(MWClass declaringType, ExternalConstructor externalConstructor) {
// Java constructors return their names fully-qualified, despite what the JavaDocs say
this(declaringType, shortNameFor(externalConstructor));
this.setConstructor(true);
this.initializeParameterTypeDeclarations(externalConstructor.getParameterTypes());
this.refresh(externalConstructor);
}
MWMethod(MWClass declaringType, ExternalMethod externalMethod) {
this(declaringType, externalMethod.getName());
this.setConstructor(false);
this.initializeParameterTypeDeclarations(externalMethod.getParameterTypes());
this.refresh(externalMethod);
}
private static String shortNameFor(ExternalConstructor externalConstructor) {
return ClassTools.shortNameForClassNamed(externalConstructor.getName());
}
/**
* public DeclaringType()
*/
static MWMethod buildZeroArgumentConstructor(MWClass declaringType) {
MWMethod ctor = new MWMethod(declaringType, declaringType.shortName());
ctor.setConstructor(true);
return ctor;
}
// ********** initialization **********
/**
* initialize transient state
*/
protected void initialize() {
super.initialize();
this.modifier = new MWModifier(this);
}
/**
* initialize persistent state
*/
protected void initialize(Node parent) {
super.initialize(parent);
this.returnTypeDeclaration = this.buildDefaultReturnTypeDeclaration();
this.methodParameters = new Vector();
this.exceptionTypeHandles = new Vector();
this.accessedAttributeHandle = new MWAttributeHandle(this, this.buildAccessedAttributeScrubber());
}
private void initializeParameterTypeDeclarations(ExternalClassDescription[] externalParameters) {
int len = externalParameters.length;
for (int i = 0; i < len; i++) {
this.methodParameters.add(this.buildMethodParameter(externalParameters[i]));
}
}
// ********** accessors **********
public MWClass getDeclaringType() {
return (MWClass) this.getParent();
}
// ***** name
public String getName() {
return this.name;
}
public void setName(String name) {
String old = this.name;
this.name = name;
this.firePropertyChanged(NAME_PROPERTY, old, name);
if (this.attributeValueHasChanged(old, name)) {
this.setConstructor(name.equals(this.getDeclaringType().shortName()));
this.getProject().nodeRenamed(this);
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "name"); // don't waste time building the signature - it's ignored
}
}
// ***** modifier
public MWModifier getModifier() {
return this.modifier;
}
// ***** returnTypeDeclaration
MWTypeDeclaration getReturnTypeDeclaration() {
return this.returnTypeDeclaration;
}
public MWClass getReturnType() {
if (this.isConstructor()) {
throw new IllegalStateException("A constructor \"" + this + "\" does not have a return type.");
}
return this.returnTypeDeclaration.getType();
}
public int getReturnTypeDimensionality() {
if (this.isConstructor()) {
throw new IllegalStateException("A constructor \"" + this + "\" does not have a return type.");
}
return this.returnTypeDeclaration.getDimensionality();
}
/**
* private - for internal use only;
* used when converting a regular method to a constructor and vice versa
*/
private void setReturnTypeDeclaration(MWTypeDeclaration returnTypeDeclaration) {
this.returnTypeDeclaration = returnTypeDeclaration;
}
public void setReturnType(MWClass returnType) {
Object old = this.getReturnType();
this.returnTypeDeclaration.setType(returnType);
this.firePropertyChanged(RETURN_TYPE_PROPERTY, old, returnType);
if (attributeValueHasChanged(old, returnType)) {
if (returnType.isVoid()) {
this.setReturnTypeDimensionality(0);
}
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "returnType"); // don't waste time building the signature - it's ignored
}
}
private void setReturnTypeDeclaration(MWClass returnType, int dimensionality) {
this.setReturnType(returnType);
this.setReturnTypeDimensionality(dimensionality);
}
public void setReturnTypeDimensionality(int dimensionality) {
int old = this.getReturnTypeDimensionality();
this.returnTypeDeclaration.setDimensionality(dimensionality);
this.firePropertyChanged(RETURN_TYPE_DIMENSIONALITY_PROPERTY, old, dimensionality);
if (old != dimensionality) {
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "returnTypeDimensionality"); // don't waste time building the signature - it's ignored
}
}
// ***** methodParameters
public ListIterator methodParameters() {
return new CloneListIterator(this.methodParameters) {
protected void remove(int index) {
MWMethod.this.removeMethodParameter(index);
}
public String toString() {
return "MWMethod.methodParameters()";
}
};
}
public int methodParametersSize() {
return this.methodParameters.size();
}
public MWMethodParameter getMethodParameter(int index) {
return (MWMethodParameter) this.methodParameters.get(index);
}
/**
* return the first parameter type declaration;
* useful for when there is only a single parameter
*/
public MWMethodParameter getMethodParameter() {
if (this.methodParametersSize() != 1) {
throw new IllegalStateException("This method \"" + this + "\" contains more than one parameter type declaration.");
}
return this.getMethodParameter(0);
}
public MWMethodParameter addMethodParameter(MWClass parameterType) {
return this.addMethodParameter(parameterType, 0);
}
public MWMethodParameter addMethodParameter(MWClass parameterType, int parameterDimensionality) {
return this.addMethodParameter(this.buildMethodParameter(parameterType, parameterDimensionality));
}
private MWMethodParameter addMethodParameter(MWMethodParameter methodParameter) {
if (methodParameter.getType().isVoid()) {
throw new IllegalArgumentException("Cannot add a parameter with type void");
}
this.addItemToList(methodParameter, this.methodParameters, METHOD_PARAMETERS_LIST);
this.parameterChanged();
return methodParameter;
}
public void removeMethodParameter(int index) {
this.removeMethodParameter(this.getMethodParameter(index));
}
public void removeMethodParameter(MWMethodParameter methodParameter) {
this.removeItemFromList(methodParameter, this.methodParameters, METHOD_PARAMETERS_LIST);
this.parameterChanged();
}
public void removeMethodParameters(Iterator params) {
while (params.hasNext()) {
this.removeMethodParameter((MWMethodParameter) params.next());
}
}
public void removeMethodParameters(Collection params) {
this.removeMethodParameters(params.iterator());
}
public void clearMethodParameters() {
this.removeMethodParameters(this.methodParameters());
}
void parameterChanged() {
this.getProject().nodeRenamed(this);
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "methodParameters"); // don't waste time building the signature - it's ignored
}
// ***** exceptionTypes
private Iterator exceptionTypeHandles() {
return new CloneIterator(this.exceptionTypeHandles) {
protected void remove(Object current) {
MWMethod.this.removeExceptionTypeHandle((MWClassHandle) current);
}
public String toString() {
return "MWMethod.exceptionTypeHandles()";
}
};
}
void removeExceptionTypeHandle(MWClassHandle handle) {
this.exceptionTypeHandles.remove(handle);
this.fireItemRemoved(EXCEPTION_TYPES_COLLECTION, handle.getType());
}
public Iterator exceptionTypes() {
return new TransformationIterator(this.exceptionTypeHandles()) {
protected Object transform(Object next) {
return ((MWClassHandle) next).getType();
}
public String toString() {
return "MWMethod.exceptionTypes()";
}
};
}
public int exceptionTypesSize() {
return this.exceptionTypeHandles.size();
}
public boolean containsExceptionType(MWClass exceptionType) {
synchronized (this.exceptionTypeHandles) {
for (Iterator stream = this.exceptionTypeHandles.iterator(); stream.hasNext(); ) {
if (((MWClassHandle) stream.next()).getType() == exceptionType) {
return true;
}
}
return false;
}
}
public void addExceptionType(MWClass exceptionType) {
// check to make sure that the exception isn't in there already
if ( ! this.containsExceptionType(exceptionType)) {
this.exceptionTypeHandles.add(new MWClassHandle(this, exceptionType, this.exceptionTypeScrubber()));
this.fireItemAdded(EXCEPTION_TYPES_COLLECTION, exceptionType);
}
}
public void addExceptionTypes(Collection exceptionTypes) {
this.addExceptionTypes(exceptionTypes.iterator());
}
public void addExceptionTypes(Iterator exceptionTypes) {
while (exceptionTypes.hasNext()) {
this.addExceptionType((MWClass) exceptionTypes.next());
}
}
public void removeExceptionType(MWClass exceptionType) {
for (Iterator stream = this.exceptionTypes(); stream.hasNext(); ) {
if (stream.next() == exceptionType) {
stream.remove();
return;
}
}
throw new IllegalArgumentException(exceptionType.toString());
}
public void removeExceptionTypes(Collection exceptionTypes) {
this.removeExceptionTypes(exceptionTypes.iterator());
}
public void removeExceptionTypes(Iterator exceptionTypes) {
while (exceptionTypes.hasNext()) {
this.removeExceptionType((MWClass) exceptionTypes.next());
}
}
public void clearExceptionTypes() {
// use #exceptionTypeHandles() for minor performance tweak:
for (Iterator stream = this.exceptionTypeHandles(); stream.hasNext(); ) {
stream.next();
stream.remove();
}
}
// ***** constructor
public boolean isConstructor() {
return this.constructor;
}
public void setConstructor(boolean constructor) {
boolean old = this.constructor;
this.constructor = constructor;
this.firePropertyChanged(CONSTRUCTOR_PROPERTY, old, constructor);
// we need to tweak the return type declaration when changing...
if (old != constructor) {
if (constructor) {
this.setReturnTypeDeclaration(null);
this.getModifier().setAbstract(false);
this.getModifier().setFinal(false);
this.getModifier().setSynchronized(false);
this.getModifier().setStatic(false);
this.getModifier().setNative(false);
} else {
this.setReturnTypeDeclaration(this.buildDefaultReturnTypeDeclaration());
}
// constructors and non-constructors allow different sets of modifiers
this.allowedModifiersChanged();
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "constructor"); // don't waste time building the signature - it's ignored
}
}
// ***** accessedAttribute
MWClassAttribute getAccessedAttribute() {
return this.accessedAttributeHandle.getAttribute();
}
void setAccessedAttribute(MWClassAttribute attribute) {
if (this.getAccessedAttribute() != null) {
this.getAccessedAttribute().removeAccessorMethod(this);
}
this.accessedAttributeHandle.setAttribute(attribute);
}
// ********** Modifiable implementation **********
public boolean supportsAbstract() {
return true;
}
public boolean canBeSetAbstract() {
if (this.isConstructor()) {
return false;
}
if (this.getModifier().isPrivate()) {
return false;
}
if (this.getModifier().isStatic()) {
return false;
}
if (this.isFinal()) {
return false;
}
if (this.getModifier().isNative()) {
return false;
}
if (this.isStrict()) {
return false;
}
if (this.getModifier().isSynchronized()) {
return false;
}
return this.getDeclaringType().isAbstract();
}
public boolean canBeSetFinal() {
if (this.isConstructor()) {
return false;
}
if (this.isAbstract()) {
return false;
}
return true;
}
public boolean supportsInterface() {
return false;
}
public boolean canBeSetInterface() {
return false;
}
public boolean supportsNative() {
return true;
}
public boolean canBeSetNative() {
if (this.isConstructor()) {
return false;
}
if (this.isAbstract()) {
return false;
}
if (this.isStrict()) {
return false;
}
return true;
}
public boolean canBeSetPackage() {
return true;
}
public boolean canBeSetPrivate() {
return ! this.isAbstract();
}
public boolean canBeSetProtected() {
return true;
}
public boolean canBeSetPublic() {
return true;
}
public boolean canBeSetStatic() {
if (this.isConstructor()) {
return false;
}
if (this.isAbstract()) {
return false;
}
return true;
}
public boolean supportsStrict() {
return true;
}
public boolean canBeSetStrict() {
if (this.isConstructor()) {
return false;
}
if (this.isAbstract()) {
return false;
}
if (this.getModifier().isNative()) {
return false;
}
return true;
}
public boolean supportsSynchronized() {
return true;
}
public boolean canBeSetSynchronized() {
if (this.isConstructor()) {
return false;
}
if (this.isAbstract()) {
return false;
}
return true;
}
public boolean supportsTransient() {
return false;
}
public boolean canBeSetTransient() {
return false;
}
public boolean supportsVolatile() {
return false;
}
public boolean canBeSetVolatile() {
return false;
}
/**
* if any of these modifiers change for the method, the method's
* "allowed" modifiers may have changed (e.g. if the method is
* no longer 'abstract', it can now be 'final').
*/
private static final int ALLOWED_MODIFIERS_FLAGS =
Modifier.ABSTRACT |
Modifier.FINAL |
Modifier.NATIVE |
Modifier.PRIVATE |
Modifier.STATIC |
Modifier.STRICT |
Modifier.SYNCHRONIZED;
public void modifierChanged(int oldCode, int newCode) {
this.firePropertyChanged(MODIFIER_CODE_PROPERTY, oldCode, newCode);
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "modifier"); // don't waste time building the signature - it's ignored
if (MWModifier.anyFlagsAreDifferent(ALLOWED_MODIFIERS_FLAGS, oldCode, newCode)) {
this.allowedModifiersChanged();
}
}
/**
* the method's "allowed" modifiers
*/
void allowedModifiersChanged() {
this.modifier.allowedModifiersChanged();
}
public void accessLevelChanged(String oldValue, String newValue) {
this.firePropertyChanged(MODIFIER_ACCESS_LEVEL_PROPERTY, oldValue, newValue);
}
// ********** queries **********
boolean hasSameSignatureAs(ExternalConstructor externalConstructor) {
if ( ! this.isConstructor()) {
return false;
}
// Java constructors return a fully-qualified name(!)
String javaName = shortNameFor(externalConstructor);
return this.hasSameSignatureAs(javaName, externalConstructor.getParameterTypes());
}
boolean hasSameSignatureAs(ExternalMethod externalMethod) {
if (this.isConstructor()) {
return false;
}
return this.hasSameSignatureAs(externalMethod.getName(), externalMethod.getParameterTypes());
}
private boolean hasSameSignatureAs(String javaName, ExternalClassDescription[] externalParameters) {
if ( ! this.getName().equals(javaName)) {
return false;
}
int externalParametersLength = externalParameters.length;
if (this.methodParametersSize() != externalParametersLength) {
return false;
}
for (int i = 0; i < externalParametersLength; i++) {
if ( ! this.getMethodParameter(i).hasSameSignatureAs(externalParameters[i])) {
return false;
}
}
return true;
}
public boolean hasSameSignatureAs(MWMethod method) {
if ( ! this.getName().equals(method.getName())) {
return false;
}
int methodParametersSize = method.methodParametersSize();
if (this.methodParametersSize() != methodParametersSize) {
return false;
}
for (int i = 0; i < methodParametersSize; i++) {
if ( ! this.getMethodParameter(i).hasSameSignatureAs(method.getMethodParameter(i))) {
return false;
}
}
return true;
}
/**
* return true if the method has the specified name and zero arguments
*/
boolean hasSignature(String methodName) {
return this.hasSignature(methodName, EMPTY_TYPE_ARRAY, EMPTY_INT_ARRAY);
}
/**
* return true if the method has the specified name and a single argument
* that matches the specified type and dimensionality;
* the argument must be an *exact* match
*/
boolean hasSignature(String methodName, MWClass type, int dimensionality) {
return this.hasSignature(methodName, new MWClass[] {type}, new int[] {dimensionality});
}
/**
* return true if the method has the specified name and two arguments
* that match the specified types and dimensionalities in the given order;
* the arguments must be *exact* matches
*/
boolean hasSignature(String methodName, MWClass type1, int dimensionality1, MWClass type2, int dimensionality2) {
return this.hasSignature(methodName, new MWClass[] {type1, type2}, new int[] {dimensionality1, dimensionality2});
}
/**
* return true if the method has the specified name and arguments
* that match the specified types and dimensionalities in the given order;
* the arguments must be *exact* matches
*/
boolean hasSignature(String methodName, MWClass[] types, int[] dimensionalities) {
if (types.length != dimensionalities.length) {
throw new IllegalArgumentException("arrays are different lengths: " + types.length + " vs. " + dimensionalities.length);
}
if ( ! this.getName().equals(methodName)) {
return false;
}
if (this.methodParametersSize() != types.length) {
return false;
}
for (int i = types.length; i-- > 0; ) {
MWMethodParameter parm = this.getMethodParameter(i);
if (parm.getType() != types[i]) {
return false;
}
if (parm.getDimensionality() != dimensionalities[i]) {
return false;
}
}
return true;
}
/**
* interface methods are implied to be abstract...
*/
public boolean isAbstract() {
return this.getModifier().isAbstract() || this.getDeclaringType().isInterface();
}
public boolean isStatic() {
return this.getModifier().isStatic();
}
/**
* private methods are implied to be final...
* methods for final classes are implied to be final...
*/
public boolean isFinal() {
return this.getModifier().isFinal()
|| this.getModifier().isPrivate()
|| this.getDeclaringType().getModifier().isFinal();
}
/**
* methods for strictfp classes are implied to be strictfp...
*/
public boolean isStrict() {
return this.getModifier().isStrict()
|| this.getDeclaringType().isStrict();
}
public boolean isInstanceMethod() {
return ( ! this.isConstructor())
&& ( ! this.isStatic());
}
public boolean returnTypeIsVoid() {
return this.returnTypeDeclaration.isVoid();
}
public boolean returnTypeIsArray() {
return this.returnTypeDeclaration.isArray();
}
public boolean throwsException(MWClass exceptionType) {
return this.containsExceptionType(exceptionType);
}
/**
* return whether the method bears a passing resemblance to a getter:
*/
private boolean looksLikeAGetMethod() {
return this.isInstanceMethod()
&& ( ! this.returnTypeIsVoid())
&& (this.methodParametersSize() == 0)
&& this.getName().startsWith("get");
}
/**
* return whether the method looks like a standard getter and it is abstract
*/
public boolean isEjb20GetMethod() {
return this.looksLikeAGetMethod()
&& this.isAbstract();
}
/**
* return whether the method bears a passing resemblance to a getter
*/
private boolean looksLikeASetMethod() {
return this.isInstanceMethod()
&& this.returnTypeIsVoid()
&& (this.methodParametersSize() == 1)
&& this.getName().startsWith("set");
}
/**
* return whether the method looks like a standard setter and
* is abstract and is named similarly to the specified get method
*/
boolean isEjb20SetMethodFor(MWMethod ejb20GetMethod) {
return this.looksLikeASetMethod()
&& this.isAbstract()
&& this.getName().endsWith(ejb20GetMethod.getName().substring(3))
&& (this.methodParametersSize() == 1)
&& this.getMethodParameter(0).matches(ejb20GetMethod.returnTypeDeclaration);
}
/**
* get, set, value get, value set, add, or remove method
*/
public boolean isAccessor() {
return this.getAccessedAttribute() != null;
}
public boolean isZeroArgumentConstructor() {
return this.isConstructor()
&& this.methodParametersSize() == 0;
}
/**
* return whether this method can be used by TopLink as a get method
* - must have 0 parameters
* - must return something (not void)
*/
boolean isCandidateTopLinkGetMethod() {
return this.isInstanceMethod()
&& this.methodParametersSize() == 0
&& ! this.returnTypeDeclaration.isVoid();
}
/**
* return whether this method can be used by TopLink as a set method
* - must have 1 parameter
*/
boolean isCandidateTopLinkSetMethod() {
return this.isInstanceMethod()
&& this.methodParametersSize() == 1;
}
boolean isCandidateGetMethodFor(MWClassAttribute attribute) {
return this.isCandidateGetMethodFor(attribute.getType(), attribute.getDimensionality());
}
boolean isCandidateGetMethodFor(MWClass attributeType) {
return this.isCandidateGetMethodFor(attributeType, 0);
}
boolean isCandidateGetMethodFor(MWClass attributeType, int attributeDimensionality) {
return this.isZeroArgumentInstanceMethodWithCompatibleReturnType(attributeType, attributeDimensionality);
}
/**
* the return type need only be *compatible* with the specified type declaration
*/
private boolean isZeroArgumentInstanceMethodWithCompatibleReturnType(MWClass type, int dimensionality) {
return this.isInstanceMethod()
&& this.methodParametersSize() == 0
&& this.returnTypeDeclaration.mightBeAssignableFrom(type, dimensionality);
}
boolean isCandidateSetMethodFor(MWClassAttribute attribute) {
return this.isCandidateSetMethodFor(attribute.getType(), attribute.getDimensionality());
}
boolean isCandidateSetMethodFor(MWClass attributeType) {
return this.isCandidateSetMethodFor(attributeType, 0);
}
boolean isCandidateSetMethodFor(MWClass attributeType, int attributeDimensionality) {
return this.isInstanceMethodWithCompatibleArgument(attributeType, attributeDimensionality);
}
/**
* the argument need only be *compatible* with the specified type declaration
*/
private boolean isInstanceMethodWithCompatibleArgument(MWClass type, int dimensionality) {
return this.isInstanceMethod()
&& this.methodParametersSize() == 1
&& this.getMethodParameter().mightBeAssignableFrom(type, dimensionality);
}
boolean isCandidateAddMethodFor(MWClass itemType) {
return this.isInstanceMethodWithCompatibleArgument(itemType, 0);
}
boolean isCandidateAddMethodFor(MWClass keyType, MWClass itemType) {
return this.isInstanceMethod()
&& this.methodParametersSize() == 2
&& this.getMethodParameter(0).mightBeAssignableFrom(keyType, 0)
&& this.getMethodParameter(1).mightBeAssignableFrom(itemType, 0);
}
boolean isCandidateRemoveMethodFor(MWClass itemOrKeyType) {
return this.isInstanceMethodWithCompatibleArgument(itemOrKeyType, 0);
}
/**
* used by map container policy
* @see org.eclipse.persistence.internal.queries.MapContainerPolicy
*/
public boolean isCandidateMapContainerPolicyKeyMethod() {
return this.isInstanceMethod()
&& (this.methodParametersSize() == 0)
&& ( ! this.returnTypeDeclaration.isVoid());
}
/**
* used by inheritance policy;
* [public|protected|private] static Class|Object foo(Record)
* @see org.eclipse.persistence.descriptors.MethodClassExtractor
*/
public boolean isCandidateClassExtractionMethod() {
return this.isStatic()
&& this.methodParametersSize() == 1
&& this.getMethodParameter().getDimensionality() == 0
&& ((this.getMethodParameter().getType() == this.typeFor(Record.class)))
&& this.returnTypeDeclaration.isAssignableFrom(this.typeFor(Class.class));
}
/**
* used by clone copy policy;
* must be a zero-argument instance method with
* an appropriate return type;
* this method will be invoked on a domain object
* @see org.eclipse.persistence.descriptors.copying.CloneCopyPolicy#initialize(org.eclipse.persistence.sessions.Session)
*/
public boolean isCandidateCloneMethod() {
return this.isInstanceMethod()
&& this.methodParametersSize() == 0
&& this.returnTypeDeclaration.mightBeAssignableFrom(this.getDeclaringType());
}
/**
* used by events policy;
* must be a single-argument public method returning void that takes
* a DescriptorEvent as the argument
* @see org.eclipse.persistence.descriptors.DescriptorEventManager#findMethod(int)
*/
public boolean isCandidateDescriptorEventMethod() {
return this.isInstanceMethod()
&& this.methodParametersSize() == 1
&& ((this.getMethodParameter().getType() == this.typeFor(org.eclipse.persistence.descriptors.DescriptorEvent.class))
|| (this.getMethodParameter().getType() == this.typeFor(org.eclipse.persistence.sessions.Session.class)));
}
/**
* used by instantiation policy;
* must be a static zero-argument method with an appropriate return type;
* this method will be invoked on the *factory* class to instantiate a new
* factory instance
* @see org.eclipse.persistence.internal.descriptors.InstantiationPolicy
*/
public boolean isCandidateFactoryMethod() {
return this.isCandidateInstantiationMethod();
}
/**
* used by instantiation policy;
* must be a static zero-argument method with an appropriate return type;
* this method will be invoked on the *descriptor* class to instantiate a new
* domain object
* @see org.eclipse.persistence.internal.descriptors.InstantiationPolicy
*/
public boolean isCandidateInstantiationMethod() {
return this.isCandidateInstantiationMethodFor(this.getDeclaringType());
}
/**
* used by instantiation policy;
* must be a static zero-argument method with an appropriate return type;
* this method will be invoked on the specified class to instantiate a new
* instance of that class
* @see org.eclipse.persistence.internal.descriptors.InstantiationPolicy
*/
private boolean isCandidateInstantiationMethodFor(MWClass type) {
return this.isStatic()
&& this.isCandidateFactoryInstantiationMethodFor(type);
}
/**
* used by instantiation policy;
* must be a non-constructor zero-argument method with an appropriate return type;
* this method will be invoked on a factory to instantiate a new
* domain object
* @see org.eclipse.persistence.internal.descriptors.InstantiationPolicy
*/
public boolean isCandidateFactoryInstantiationMethodFor(MWClass type) {
return ( ! this.isConstructor())
&& this.methodParametersSize() == 0
&& this.returnTypeDeclaration.mightBeAssignableFrom(type);
}
/**
* used by after load policy;
* must be a static single-argument method that takes
* a descriptor as the argument
* @see org.eclipse.persistence.descriptors.ClassDescriptor#applyAmendmentMethod(org.eclipse.persistence.descriptors.DescriptorEvent)
*/
public boolean isCandidateDescriptorAfterLoadMethod() {
return this.isStatic()
&& this.methodParametersSize() == 1
&& this.getMethodParameter().getDimensionality() == 0
&& ((this.getMethodParameter().getType() == this.typeFor(ClassDescriptor.class)));
}
/**
* used by transformation mapping;
* parms must be exact matches:
* [public|protected|private] [not void] foo(Record)
* [public|protected|private] [not void] foo(Record, org.eclipse.persistence.sessions.Session)
* @see org.eclipse.persistence.mappings.transformers.MethodBasedAttributeTransformer#initialize(org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping)
*/
public boolean isCandidateAttributeTransformerMethod() {
if (this.isConstructor()) {
return false;
}
if (this.getReturnType().isVoid()) {
return false;
}
if (this.methodParametersSize() == 1) {
MWMethodParameter parm = this.getMethodParameter(0);
if (parm.getDimensionality() == 0) {
if (parm.getType().isAssignableTo(this.typeFor(Record.class))) {
return true;
}
return false;
}
return false;
}
if (this.methodParametersSize() == 2) {
MWMethodParameter parm1 = this.getMethodParameter(0);
MWMethodParameter parm2 = this.getMethodParameter(1);
if ((parm1.getDimensionality() == 0) && (parm2.getDimensionality() == 0)) {
if ((parm1.getType().isAssignableTo(this.typeFor(Record.class))) && (parm2.getType() == this.typeFor(org.eclipse.persistence.sessions.Session.class))) {
return true;
}
return false;
}
return false;
}
return false; // wrong number of parameters
}
/**
* used by transformation mapping;
* parms must be exact matches:
* [public|protected|private] [not void] foo()
* [public|protected|private] [not void] foo(org.eclipse.persistence.sessions.Session)
* @see org.eclipse.persistence.mappings.transformers.MethodBasedFieldTransformer#initialize(org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping)
*/
public boolean isCandidateFieldTransformerMethod() {
if (this.isConstructor()) {
return false;
}
if (this.getReturnType().isVoid()) {
return false;
}
if (this.methodParametersSize() == 0) {
return true;
}
if (this.methodParametersSize() == 1) {
MWMethodParameter parm = this.getMethodParameter(0);
if (parm.getDimensionality() == 0) {
if (parm.getType() == this.typeFor(org.eclipse.persistence.sessions.Session.class)) {
return true;
}
return false;
}
return false;
}
return false; // wrong number of parameters
}
/**
* Used for code gen
*/
NonreflectiveMethodDefinition methodDefinition(MWMethodCodeGenPolicy codeGenPolicy) {
NonreflectiveMethodDefinition def = new NonreflectiveMethodDefinition();
def.setAccessLevel(this.getModifier().accessLevel());
def.setIsConstructor(this.isConstructor());
def.setIsAbstract(this.isAbstract());
if ( ! this.isConstructor()) {
def.setReturnType(this.returnTypeDeclaration.declaration());
}
def.setName(this.getName());
codeGenPolicy.insertArguments(def);
for (Iterator stream = this.exceptionTypes(); stream.hasNext(); ) {
def.addException(((MWClass) stream.next()).getName());
}
codeGenPolicy.insertMethodBody(def);
return def;
}
// ********** behavior **********
/**
* containment hierarchy
*/
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
children.add(this.modifier);
if ( ! this.isConstructor()) {
children.add(this.returnTypeDeclaration);
}
synchronized (this.methodParameters) { children.addAll(this.methodParameters); }
children.add(this.accessedAttributeHandle);
synchronized (this.exceptionTypeHandles) { children.addAll(this.exceptionTypeHandles); }
}
private NodeReferenceScrubber buildAccessedAttributeScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWMethod.this.setAccessedAttribute(null);
}
public String toString() {
return "MWMethod.buildAccessedAttributeScrubber()";
}
};
}
private NodeReferenceScrubber exceptionTypeScrubber() {
if (this.exceptionTypeScrubber == null) {
this.exceptionTypeScrubber = this.buildExceptionTypeScrubber();
}
return this.exceptionTypeScrubber;
}
private NodeReferenceScrubber buildExceptionTypeScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWMethod.this.removeExceptionTypeHandle((MWClassHandle) handle);
}
public String toString() {
return "MWMethod.buildExceptionTypeScrubber()";
}
};
}
public void nodeRenamed(Node node) {
super.nodeRenamed(node);
if (this.isConstructor()) {
if (this.getDeclaringType() == node) {
this.setName(this.getDeclaringType().shortName());
}
} else {
if (this.getReturnType() == node) {
this.firePropertyChanged(MWMethod.SIGNATURE_PROPERTY, "returnType"); // don't waste time building the signature - it's ignored
}
}
}
void refresh(ExternalConstructor externalConstructor) {
if ( ! this.hasSameSignatureAs(externalConstructor)) {
throw new IllegalArgumentException(externalConstructor.toString());
}
this.getModifier().refresh(externalConstructor.getModifiers());
// a constructor does not have a return type declaration
this.refreshExceptionTypes(externalConstructor.getExceptionTypes());
}
void refresh(ExternalMethod externalMethod) {
if ( ! this.hasSameSignatureAs(externalMethod)) {
throw new IllegalArgumentException(externalMethod.toString());
}
this.getModifier().refresh(externalMethod.getModifiers());
this.returnTypeDeclaration.refresh(externalMethod.getReturnType());
this.refreshExceptionTypes(externalMethod.getExceptionTypes());
}
private void refreshExceptionTypes(ExternalClassDescription[] externalExceptions) {
Collection removedExceptionTypes = CollectionTools.collection(this.exceptionTypes());
for (int i = externalExceptions.length; i-- > 0; ) {
if ( ! removedExceptionTypes.remove(this.typeNamed(externalExceptions[i].getName()))) {
this.addExceptionType(this.typeNamed(externalExceptions[i].getName()));
}
}
this.removeExceptionTypes(removedExceptionTypes);
}
// ********** factory methods **********
private MWMethodParameter buildMethodParameter(ExternalClassDescription externalClassDescription) {
return new MWMethodParameter(this, externalClassDescription);
}
private MWMethodParameter buildMethodParameter(MWClass type, int dimensionality) {
return new MWMethodParameter(this, type, dimensionality);
}
private MWTypeDeclaration buildDefaultReturnTypeDeclaration() {
return new MWTypeDeclaration(this, this.typeFor(void.class));
}
// ********** sorting **********
/**
* override to sort constructors first, then sort by signature
* @see MWRPersistentObject.compareTo(Object)
*/
public int compareTo(Object object) {
MWMethod otherMethod = null;
try {
otherMethod = (MWMethod) object;
} catch (ClassCastException cce) {
throw new IllegalArgumentException("Object \"" + object + "\" must be an instance of MWMethod.");
}
if (this.isConstructor() && ( ! otherMethod.isConstructor())) {
return -1;
}
if (( ! this.isConstructor()) && otherMethod.isConstructor()) {
return 1;
}
return super.compareTo(object);
}
// ********** displaying and printing **********
/**
* return the fully-qualified signature
*/
public String displayString() {
return this.signature();
}
public void toString(StringBuffer sb) {
sb.append(this.shortSignature());
}
public String signature() {
StringBuffer sb = new StringBuffer(200);
this.printSignatureOn(sb);
return sb.toString();
}
public String signatureWithReturnType() {
StringBuffer sb = new StringBuffer(200);
this.printSignatureWithReturnTypeOn(sb);
return sb.toString();
}
public String shortSignature() {
StringBuffer sb = new StringBuffer(200);
this.printShortSignatureOn(sb);
return sb.toString();
}
public String shortSignatureWithReturnType() {
StringBuffer sb = new StringBuffer(200);
this.printShortSignatureWithReturnTypeOn(sb);
return sb.toString();
}
public void printSignatureWithReturnTypeOn(StringBuffer sb) {
this.printSignatureOn(sb);
this.printReturnTypeOn(sb, true);
}
public void printSignatureOn(StringBuffer sb) {
this.printSignatureOn(sb, true);
}
public void printShortSignatureWithReturnTypeOn(StringBuffer sb) {
this.printShortSignatureOn(sb);
this.printReturnTypeOn(sb, false);
}
public void printShortSignatureOn(StringBuffer sb) {
this.printSignatureOn(sb, false);
}
private void printReturnTypeOn(StringBuffer sb, boolean printFullyQualifiedTypeNames) {
if ( ! this.isConstructor()) {
sb.append(" : ");
if (printFullyQualifiedTypeNames) {
this.returnTypeDeclaration.printSignatureOn(sb);
} else {
this.returnTypeDeclaration.printShortSignatureOn(sb);
}
}
}
private void printSignatureOn(StringBuffer sb, boolean printFullyQualifiedTypeNames) {
sb.append(this.name);
sb.append('(');
synchronized (this.methodParameters) {
for (Iterator stream = this.methodParameters.iterator(); stream.hasNext(); ) {
MWMethodParameter methodParameter = (MWMethodParameter) stream.next();
if (printFullyQualifiedTypeNames) {
methodParameter.printSignatureOn(sb);
} else {
methodParameter.printShortSignatureOn(sb);
}
if (stream.hasNext()) {
sb.append(", ");
}
}
}
sb.append(')');
}
// ********** TopLink methods **********
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWMethod.class);
descriptor.addDirectMapping("name", "name/text()");
XMLDirectMapping constructorMapping = (XMLDirectMapping) descriptor.addDirectMapping("constructor", "constructor/text()");
constructorMapping.setNullValue(Boolean.FALSE);
XMLDirectMapping modifierMapping = (XMLDirectMapping) descriptor.addDirectMapping("modifier", "getModifierForTopLink", "setModifierForTopLink", "modifier/text()");
modifierMapping.setNullValue(new Integer(0));
XMLCompositeObjectMapping returnTypeDeclarationMapping = new XMLCompositeObjectMapping();
returnTypeDeclarationMapping.setAttributeName("returnTypeDeclaration");
returnTypeDeclarationMapping.setReferenceClass(MWTypeDeclaration.class);
returnTypeDeclarationMapping.setXPath("return-type-declaration");
descriptor.addMapping(returnTypeDeclarationMapping);
XMLCompositeCollectionMapping parameterTypeDeclarationsMapping = new XMLCompositeCollectionMapping();
parameterTypeDeclarationsMapping.setAttributeName("methodParameters");
parameterTypeDeclarationsMapping.setReferenceClass(MWMethodParameter.class);
parameterTypeDeclarationsMapping.setXPath("method-parameters/method-parameter");
descriptor.addMapping(parameterTypeDeclarationsMapping);
XMLCompositeCollectionMapping exceptionTypeHandlesMapping = new XMLCompositeCollectionMapping();
exceptionTypeHandlesMapping.setAttributeName("exceptionTypeHandles" );
exceptionTypeHandlesMapping.setGetMethodName("getExceptionTypeHandlesForTopLink" );
exceptionTypeHandlesMapping.setSetMethodName("setExceptionTypeHandlesForTopLink" );
exceptionTypeHandlesMapping.setReferenceClass(MWClassHandle.class);
exceptionTypeHandlesMapping.setXPath("exception-type-handles/class-handle" );
descriptor.addMapping(exceptionTypeHandlesMapping);
XMLCompositeObjectMapping accessedAttributeHandleMapping = new XMLCompositeObjectMapping();
accessedAttributeHandleMapping.setAttributeName("accessedAttributeHandle");
accessedAttributeHandleMapping.setGetMethodName("getAccessedAttributeHandleForTopLink");
accessedAttributeHandleMapping.setSetMethodName("setAccessedAttributeHandleForTopLink");
accessedAttributeHandleMapping.setReferenceClass(MWAttributeHandle.class);
accessedAttributeHandleMapping.setXPath("accessed-attribute-handle");
descriptor.addMapping(accessedAttributeHandleMapping);
return descriptor;
}
/**
* store the modifier as an int
*/
private int getModifierForTopLink() {
return this.modifier.getCode();
}
private void setModifierForTopLink(int code) {
this.modifier.setCodeForTopLink(code);
}
/**
* sort the exception type handles for TopLink
*/
private Collection getExceptionTypeHandlesForTopLink() {
synchronized (this.exceptionTypeHandles) {
return new TreeSet(this.exceptionTypeHandles);
}
}
private void setExceptionTypeHandlesForTopLink(Collection handles) {
for (Iterator stream = handles.iterator(); stream.hasNext(); ) {
((MWClassHandle) stream.next()).setScrubber(this.exceptionTypeScrubber());
}
this.exceptionTypeHandles = handles;
}
/**
* check for null
*/
private MWAttributeHandle getAccessedAttributeHandleForTopLink() {
return (this.accessedAttributeHandle.getAttribute() == null) ? null : this.accessedAttributeHandle;
}
private void setAccessedAttributeHandleForTopLink(MWAttributeHandle accessedAttributeHandle) {
NodeReferenceScrubber scrubber = this.buildAccessedAttributeScrubber();
this.accessedAttributeHandle = ((accessedAttributeHandle == null) ? new MWAttributeHandle(this, scrubber) : accessedAttributeHandle.setScrubber(scrubber));
}
}