/*******************************************************************************
* 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.util.List;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
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.utility.ClassTools;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
/**
* This class provides the type information for a variable
* declaration (instance variable, method parameter,
* or method return type):
* the type of the variable
* the "dimensionality" of the variable
* non-arrayed variables have a dimensionality of 0
* arrays have a dimensionality of 1 or more
*/
public final class MWTypeDeclaration extends MWModel {
private MWClassHandle typeHandle;
private volatile int dimensionality;
private static final String BRACKETS = "[]";
// ********** constructors/initialization **********
/**
* Default constructor - for TopLink use only.
*/
private MWTypeDeclaration() {
super();
}
public MWTypeDeclaration(MWModel parent) {
super(parent);
}
public MWTypeDeclaration(MWModel parent, MWClass type) {
this(parent, type, 0);
}
public MWTypeDeclaration(MWModel parent, MWClass type, int dimensionality) {
this(parent);
this.setType(type);
this.setDimensionality(dimensionality);
}
MWTypeDeclaration(MWModel parent, ExternalClassDescription externalClassDescription) {
this(parent);
this.refresh(externalClassDescription);
}
protected void initialize(Node parent) {
super.initialize(parent);
this.typeHandle = new MWClassHandle(this, this.buildTypeScrubber());
}
// ********** accessors **********
public MWClass getType() {
return this.typeHandle.getType();
}
public void setType(MWClass type) {
if (type == null) {
throw new NullPointerException();
}
this.typeHandle.setType(type);
}
public int getDimensionality() {
return this.dimensionality;
}
// TODO create an IllegalDimensionException
// JSpinner catches the IllegalArgumentException and beeps at you ... doh
public void setDimensionality(int dimensionality) {
if ((dimensionality != 0) && ( ! this.allowsDimension())) {
throw new IllegalArgumentException("The type declaration 'void' cannot have a dimension: " + new Integer(dimensionality));
}
this.dimensionality = dimensionality;
}
// ********** queries **********
public String typeName() {
return this.getType().getName();
}
public String shortTypeName() {
return this.getType().shortName();
}
boolean matches(MWTypeDeclaration other) {
return (this.dimensionality == other.getDimensionality())
&& (this.getType() == other.getType());
}
boolean hasSameSignatureAs(ExternalClassDescription externalClassDescription) {
return (this.dimensionality == externalClassDescription.getArrayDepth())
&& (this.getType() == this.typeNamed(externalClassDescription.getElementTypeName()));
}
boolean hasSameSignatureAs(MWMethodParameter methodParameter) {
return (this.dimensionality == methodParameter.getDimensionality())
&& (this.getType() == methodParameter.getType());
}
boolean isArray() {
return ! this.isScalar();
}
boolean isScalar() {
return this.dimensionality == 0;
}
boolean isVoid() {
return (this.dimensionality == 0)
&& this.getType().isVoid();
}
boolean isValueHolder() {
return (this.dimensionality == 0)
&& this.getType().isValueHolder();
}
boolean isTLValueHolder() {
return (this.dimensionality == 0)
&& "oracle.toplink.indirection.ValueHolderInterface".equals(this.getType().fullName());
}
boolean isBooleanPrimitive() {
return (this.dimensionality == 0)
&& this.getType().isBooleanPrimitive();
}
/**
* return whether the type declaration may be assigned a non-zero
* dimension - the return type declaration 'void' cannot
*/
boolean allowsDimension() {
return ! this.getType().isVoid();
}
/**
* Return whether a variable with this type declaration
* can be assigned a value with the specified non-array type.
* For example:
* Object foo = new String(); // this is valid
* String foo = new Object(); // this is invalid
* See "The Java Language Specification" 5.1.4
* @see java.lang.Class#isAssignableFrom(java.lang.Class)
*/
boolean isAssignableFrom(MWClass otherType) {
return this.isAssignableFrom(otherType, 0);
}
boolean mightBeAssignableFrom(MWClass otherType) {
return this.mightBeAssignableFrom(otherType, 0);
}
/**
* Return whether a variable with this type declaration
* can be assigned a value with the specified type declaration.
* For example:
* Object foo = new String(); // this is valid
* String foo = new Object(); // this is invalid
* See "The Java Language Specification" 5.1.4
* @see java.lang.Class#isAssignableFrom(java.lang.Class)
*/
boolean isAssignableFrom(MWClass otherType, int otherDimensionality) {
return this.targetIsAssignableFromSource(this.getType(), this.dimensionality, otherType, otherDimensionality);
}
boolean mightBeAssignableFrom(MWClass otherType, int otherDimensionality) {
return this.targetMightBeAssignableFromSource(this.getType(), this.dimensionality, otherType, otherDimensionality);
}
/**
* Return whether a variable with this type declaration
* can be assigned a value with the specified type declaration.
* For example:
* Object foo = new String(); // this is valid
* String foo = new Object(); // this is invalid
* See "The Java Language Specification" 5.1.4
* @see java.lang.Class#isAssignableFrom(java.lang.Class)
*/
boolean isAssignableFrom(MWTypeDeclaration other) {
return this.isAssignableFrom(other.getType(), other.getDimensionality());
}
boolean mightBeAssignableFrom(MWTypeDeclaration other) {
return this.mightBeAssignableFrom(other.getType(), other.getDimensionality());
}
/**
* Return whether a value with this type declaration
* can be assigned to a variable with the specified non-array type declaration.
* For example:
* Object foo = new String(); // this is valid
* String foo = new Object(); // this is invalid
* See "The Java Language Specification" 5.1.4
* @see java.lang.Class#isAssignableFrom(java.lang.Class)
*/
boolean isAssignableTo(MWClass otherType) {
return this.isAssignableTo(otherType, 0);
}
boolean mightBeAssignableTo(MWClass otherType) {
return this.mightBeAssignableTo(otherType, 0);
}
/**
* Return whether a value with this type declaration
* can be assigned to a variable with the specified type declaration.
* For example:
* Object foo = new String(); // this is valid
* String foo = new Object(); // this is invalid
* See "The Java Language Specification" 5.1.4
* @see java.lang.Class#isAssignableFrom(java.lang.Class)
*/
boolean isAssignableTo(MWClass otherType, int otherDimensionality) {
return this.targetIsAssignableFromSource(otherType, otherDimensionality, this.getType(), this.dimensionality);
}
boolean mightBeAssignableTo(MWClass otherType, int otherDimensionality) {
return this.targetMightBeAssignableFromSource(otherType, otherDimensionality, this.getType(), this.dimensionality);
}
/**
* Return whether a value with this type declaration
* can be assigned to a variable with the specified type declaration.
* For example:
* Object foo = new String(); // this is valid
* String foo = new Object(); // this is invalid
* See "The Java Language Specification" 5.1.4
* @see java.lang.Class#isAssignableFrom(java.lang.Class)
*/
boolean isAssignableTo(MWTypeDeclaration other) {
return this.isAssignableTo(other.getType(), other.getDimensionality());
}
boolean mightBeAssignableTo(MWTypeDeclaration other) {
return this.mightBeAssignableTo(other.getType(), other.getDimensionality());
}
private boolean targetIsAssignableFromSource(MWClass targetType, int targetDimensionality, MWClass sourceType, int sourceDimensionality) {
// e.g. Object[][] := String[][]
if (targetDimensionality == sourceDimensionality) {
// delegate to type
return targetType.isAssignableFrom(sourceType);
}
return this.targetIsAssignableFromSource2(targetType, targetDimensionality, sourceType, sourceDimensionality);
}
private boolean targetIsAssignableFromSource2(MWClass targetType, int targetDimensionality, MWClass sourceType, int sourceDimensionality) {
// e.g. Object[][] := String[]
if (targetDimensionality > sourceDimensionality) {
// an array cannot be expanded into a larger-dimensioned array
return false;
}
// targetDimensionality < sourceDimensionality
// all arrays are assignable to three, pre-defined types
// e.g. Object[] := String[][]
// (which is reduced to: Object := String[] )
return targetType.isObject()
|| targetType.isCloneable()
|| targetType.isSerializable();
}
private boolean targetMightBeAssignableFromSource(MWClass targetType, int targetDimensionality, MWClass sourceType, int sourceDimensionality) {
// e.g. Object[][] := String[][]
if (targetDimensionality == sourceDimensionality) {
// delegate to type
return targetType.mightBeAssignableFrom(sourceType);
}
return this.targetIsAssignableFromSource2(targetType, targetDimensionality, sourceType, sourceDimensionality);
}
boolean isAssignableToCollection() {
return (this.dimensionality == 0)
&& this.getType().isAssignableToCollection();
}
boolean mightBeAssignableToCollection() {
return (this.dimensionality == 0)
&& this.getType().mightBeAssignableToCollection();
}
boolean isAssignableToList() {
return (this.dimensionality == 0)
&& this.getType().isAssignableToList();
}
boolean mightBeAssignableToList() {
return (this.dimensionality == 0)
&& this.getType().mightBeAssignableToList();
}
boolean isAssignableToSet() {
return (this.dimensionality == 0)
&& this.getType().isAssignableToSet();
}
boolean mightBeAssignableToSet() {
return (this.dimensionality == 0)
&& this.getType().mightBeAssignableToSet();
}
boolean isAssignableToMap() {
return (this.dimensionality == 0)
&& this.getType().isAssignableToMap();
}
boolean mightBeAssignableToMap() {
return (this.dimensionality == 0)
&& this.getType().mightBeAssignableToMap();
}
boolean isAssignableToIndirectContainer() {
return (this.dimensionality == 0)
&& this.getType().isAssignableToIndirectContainer();
}
boolean mightBeAssignableToIndirectContainer() {
return (this.dimensionality == 0)
&& this.getType().mightBeAssignableToIndirectContainer();
}
// ********** behavior **********
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
children.add(this.typeHandle);
}
private NodeReferenceScrubber buildTypeScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWTypeDeclaration.this.setType(null);
}
public String toString() {
return "MWTypeDeclaration.buildTypeScrubber()";
}
};
}
void refresh(ExternalClassDescription externalClassDescription) {
// set the type first, since allowable dimensions is determined by type
this.setType(this.typeNamed(externalClassDescription.getElementTypeName()));
this.setDimensionality(externalClassDescription.getArrayDepth());
}
// ********** displaying and printing **********
/**
* e.g. "java.lang.Object[]"
*/
public String displayString() {
int dim = this.dimensionality;
if (dim == 0) {
return this.typeName();
}
String typeName = this.typeName();
StringBuffer sb = new StringBuffer(typeName.length() + (dim << 1));
sb.append(typeName);
for (int i = dim; i-- > 0; ) {
sb.append(BRACKETS);
}
return sb.toString();
}
/**
* e.g. "Object[] (java.lang)"
*/
public String displayStringWithPackage() {
int dim = this.dimensionality;
if (dim == 0) {
return this.getType().displayStringWithPackage();
}
StringBuffer sb = new StringBuffer(200);
sb.append(this.shortTypeName());
for (int i = dim; i-- > 0; ) {
sb.append(BRACKETS);
}
if (this.getType().isReferenceType()) {
sb.append(" (");
sb.append(this.getType().packageDisplayName());
sb.append(')');
}
return sb.toString();
}
public void toString(StringBuffer sb) {
sb.append(this.displayString());
}
void printDeclarationOn(StringBuffer sb) {
sb.append(this.typeName());
this.printBracketsOn(sb);
}
void printShortDeclarationOn(StringBuffer sb) {
sb.append(this.shortTypeName());
this.printBracketsOn(sb);
}
String declaration() {
StringBuffer sb = new StringBuffer(200);
this.printDeclarationOn(sb);
return sb.toString();
}
void printSignatureOn(StringBuffer sb) {
this.printDeclarationOn(sb);
}
void printShortSignatureOn(StringBuffer sb) {
this.printShortDeclarationOn(sb);
}
String defaultReturnValueString() {
StringBuffer sb = new StringBuffer(200);
this.printDefaultReturnValueOn(sb);
return sb.toString();
}
void printDefaultReturnValueOn(StringBuffer sb) {
if (this.isArray()) {
sb.append("null");
} else {
this.getType().printDefaultReturnValueOn(sb);
}
}
private void printBracketsOn(StringBuffer sb) {
for (int i = this.dimensionality; i-- > 0; ) {
sb.append(BRACKETS);
}
}
// ********** TopLink methods **********
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWTypeDeclaration.class);
// 'type' will never be null
XMLCompositeObjectMapping typeHandleMapping = new XMLCompositeObjectMapping();
typeHandleMapping.setAttributeName("typeHandle");
typeHandleMapping.setReferenceClass(MWClassHandle.class);
typeHandleMapping.setGetMethodName("getTypeHandleForTopLink");
typeHandleMapping.setSetMethodName("setTypeHandleForTopLink");
typeHandleMapping.setXPath("type-handle");
descriptor.addMapping(typeHandleMapping);
XMLDirectMapping dimMapping = (XMLDirectMapping) descriptor.addDirectMapping("dimensionality", "dimensionality/text()");
dimMapping.setNullValue(new Integer(0));
return descriptor;
}
/**
* check for null
*/
private MWClassHandle getTypeHandleForTopLink() {
return (this.typeHandle.getType() == null) ? null : this.typeHandle;
}
private void setTypeHandleForTopLink(MWClassHandle typeHandle) {
NodeReferenceScrubber scrubber = this.buildTypeScrubber();
this.typeHandle = ((typeHandle == null) ? new MWClassHandle(this, scrubber) : typeHandle.setScrubber(scrubber));
}
// ********** legacy TopLink 6.0 methods **********
public static XMLDescriptor legacy60BuildDescriptor() {
XMLDescriptor descriptor = MWModel.legacy60BuildStandardDescriptor();
descriptor.setJavaClass(MWTypeDeclaration.class);
// 'type' will never be null
XMLCompositeObjectMapping typeHandleMapping = new XMLCompositeObjectMapping();
typeHandleMapping.setAttributeName("typeHandle");
typeHandleMapping.setReferenceClass(MWClassHandle.class);
typeHandleMapping.setGetMethodName("getTypeHandleForTopLink");
typeHandleMapping.setSetMethodName("setTypeHandleForTopLink");
typeHandleMapping.setXPath("type-handle");
descriptor.addMapping(typeHandleMapping);
XMLDirectMapping dimMapping = (XMLDirectMapping) descriptor.addDirectMapping("dimensionality", "dimensionality/text()");
dimMapping.setNullValue(new Integer(0));
return descriptor;
}
// ********** legacy TopLink 5.0 methods **********
private void legacy50SetTypeNameForTopLink(String typeName) {
this.typeHandle = new MWClassHandle(this, this.buildTypeScrubber());
if (ClassTools.classNamedIsArray(typeName)) {
this.dimensionality = ClassTools.arrayDepthForClassNamed(typeName);
typeName = ClassTools.elementTypeNameForClassNamed(typeName);
}
this.typeHandle.legacySetTypeNameForTopLink(typeName);
}
/**
* necessary because writes are performed during the read
* on class stuff in 5.0 class override code
*/
private String legacyGetTypeNameForTopLink() {
return this.typeHandle.legacyGetTypeNameForTopLink();
}
}