blob: 57856d736efcfc3b5790cb81206d9ff78c7847d7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.core.internal.resource.java.binary;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jpt.core.JptCorePlugin;
import org.eclipse.jpt.core.internal.utility.jdt.JPTTools;
import org.eclipse.jpt.core.resource.java.AccessAnnotation;
import org.eclipse.jpt.core.resource.java.AccessType;
import org.eclipse.jpt.core.resource.java.Annotation;
import org.eclipse.jpt.core.resource.java.JavaResourcePersistentAttribute;
import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType;
import org.eclipse.jpt.utility.MethodSignature;
import org.eclipse.jpt.utility.internal.ClassTools;
import org.eclipse.jpt.utility.internal.NameTools;
import org.eclipse.jpt.utility.internal.iterators.CloneIterator;
import org.eclipse.jpt.utility.internal.iterators.CloneListIterator;
/**
* binary persistent attribute (field or property)
*/
final class BinaryPersistentAttribute
extends BinaryPersistentMember
implements JavaResourcePersistentAttribute
{
private int modifiers;
private String typeName;
private boolean typeIsInterface;
private boolean typeIsEnum;
private final Vector<String> typeSuperclassNames = new Vector<String>();
private final Vector<String> typeInterfaceNames = new Vector<String>();
private final Vector<String> typeTypeArgumentNames = new Vector<String>();
BinaryPersistentAttribute(JavaResourcePersistentType parent, IField field) {
this(parent, new FieldAdapter(field));
}
BinaryPersistentAttribute(JavaResourcePersistentType parent, IMethod method) {
this(parent, new MethodAdapter(method));
}
private BinaryPersistentAttribute(JavaResourcePersistentType parent, Adapter adapter) {
super(parent, adapter);
this.modifiers = this.buildModifiers();
this.typeName = this.buildTypeName();
IType type = this.getType(); // shouldn't be an array...
this.typeIsInterface = this.buildTypeIsInterface(type);
this.typeIsEnum = this.buildTypeIsEnum(type);
this.typeSuperclassNames.addAll(this.buildTypeSuperclassNames(type));
this.typeInterfaceNames.addAll(this.buildTypeInterfaceNames(type));
this.typeTypeArgumentNames.addAll(this.buildTypeTypeArgumentNames());
}
// ******** overrides ********
@Override
public void update() {
super.update();
this.setModifiers(this.buildModifiers());
this.setTypeName(this.buildTypeName());
IType type = this.getType(); // shouldn't be an array...
this.setTypeIsInterface(this.buildTypeIsInterface(type));
this.setTypeIsEnum(this.buildTypeIsEnum(type));
this.setTypeSuperclassNames(this.buildTypeSuperclassNames(type));
this.setTypeInterfaceNames(this.buildTypeInterfaceNames(type));
this.setTypeTypeArgumentNames(this.buildTypeTypeArgumentNames());
}
@Override
public void toString(StringBuilder sb) {
sb.append(this.getName());
}
// ********** BinaryPersistentMember implementation **********
private Adapter getAdapter() {
return (Adapter) this.adapter;
}
@Override
Annotation buildMappingAnnotation(IAnnotation jdtAnnotation) {
return this.getAnnotationProvider().buildAttributeMappingAnnotation(this, jdtAnnotation);
}
@Override
Annotation buildSupportingAnnotation(IAnnotation jdtAnnotation) {
return this.getAnnotationProvider().buildAttributeSupportingAnnotation(this, jdtAnnotation);
}
@Override
Annotation buildNullSupportingAnnotation(String annotationName) {
return this.getAnnotationProvider().buildNullAttributeSupportingAnnotation(this, annotationName);
}
public Annotation getNullMappingAnnotation(String annotationName) {
return (annotationName == null) ? null : this.buildNullMappingAnnotation(annotationName);
}
private Annotation buildNullMappingAnnotation(String annotationName) {
return this.getAnnotationProvider().buildNullAttributeMappingAnnotation(this, annotationName);
}
@Override
ListIterator<String> validMappingAnnotationNames() {
return this.getAnnotationProvider().attributeMappingAnnotationNames();
}
@Override
ListIterator<String> validSupportingAnnotationNames() {
return this.getAnnotationProvider().attributeSupportingAnnotationNames();
}
// ********** JavaResourcePersistentAttribute implementation **********
public String getName() {
return this.getAdapter().getAttributeName();
}
public boolean isField() {
return this.getAdapter().isField();
}
public boolean isProperty() {
return ! this.isField();
}
public boolean isFor(MethodSignature methodSignature, int occurrence) {
throw new UnsupportedOperationException();
}
public boolean hasAnyPersistenceAnnotations() {
return (this.mappingAnnotationsSize() > 0)
|| (this.supportingAnnotationsSize() > 0);
}
public AccessType getSpecifiedAccess() {
AccessAnnotation accessAnnotation = (AccessAnnotation) this.getSupportingAnnotation(AccessAnnotation.ANNOTATION_NAME);
return accessAnnotation == null ? null : accessAnnotation.getValue();
}
public boolean typeIsSubTypeOf(String tn) {
return ((this.typeName != null) && this.typeName.equals(tn))
|| this.typeInterfaceNames.contains(tn)
|| this.typeSuperclassNames.contains(tn);
}
public boolean typeIsVariablePrimitive() {
return (this.typeName != null) && ClassTools.classNamedIsVariablePrimitive(this.typeName);
}
// ***** modifiers
public int getModifiers() {
return this.modifiers;
}
private void setModifiers(int modifiers) {
int old = this.modifiers;
this.modifiers = modifiers;
this.firePropertyChanged(MODIFIERS_PROPERTY, old, modifiers);
}
/**
* zero seems like a reasonable default...
*/
private int buildModifiers() {
try {
return this.getMember().getFlags();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return 0;
}
}
// ***** type name
public String getTypeName() {
return this.typeName;
}
private void setTypeName(String typeName) {
String old = this.typeName;
this.typeName = typeName;
this.firePropertyChanged(TYPE_NAME_PROPERTY, old, typeName);
}
/**
* JARs don't have array types;
* also, no generic type parameters
*/
private String buildTypeName() {
return convertTypeSignatureToTypeName(this.getTypeSignature());
}
// ***** type is interface
public boolean typeIsInterface() {
return this.typeIsInterface;
}
private void setTypeIsInterface(boolean typeIsInterface) {
boolean old = this.typeIsInterface;
this.typeIsInterface = typeIsInterface;
this.firePropertyChanged(TYPE_IS_INTERFACE_PROPERTY, old, typeIsInterface);
}
private boolean buildTypeIsInterface(IType type) {
try {
return (type != null) && type.isInterface();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return false;
}
}
// ***** type is enum
public boolean typeIsEnum() {
return this.typeIsEnum;
}
private void setTypeIsEnum(boolean typeIsEnum) {
boolean old = this.typeIsEnum;
this.typeIsEnum = typeIsEnum;
this.firePropertyChanged(TYPE_IS_ENUM_PROPERTY, old, typeIsEnum);
}
private boolean buildTypeIsEnum(IType type) {
try {
return (type != null) && type.isEnum();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return false;
}
}
// ***** type superclass hierarchy
public ListIterator<String> typeSuperclassNames() {
return new CloneListIterator<String>(this.typeSuperclassNames);
}
public boolean typeSuperclassNamesContains(String superclassName) {
return this.typeSuperclassNames.contains(superclassName);
}
private void setTypeSuperclassNames(List<String> typeSuperclassNames) {
this.synchronizeList(typeSuperclassNames, this.typeSuperclassNames, TYPE_SUPERCLASS_NAMES_COLLECTION);
}
private List<String> buildTypeSuperclassNames(IType type) {
if (type == null) {
return Collections.emptyList();
}
ArrayList<String> names = new ArrayList<String>();
type = this.findSuperclass(type);
while (type != null) {
names.add(type.getFullyQualifiedName()); // no parameters are included here
type = this.findSuperclass(type);
}
return names;
}
// ***** type interface hierarchy
public Iterator<String> typeInterfaceNames() {
return new CloneIterator<String>(this.typeInterfaceNames);
}
public boolean typeInterfaceNamesContains(String interfaceName) {
return this.typeInterfaceNames.contains(interfaceName);
}
private void setTypeInterfaceNames(Collection<String> typeInterfaceNames) {
this.synchronizeCollection(typeInterfaceNames, this.typeInterfaceNames, TYPE_INTERFACE_NAMES_COLLECTION);
}
private Collection<String> buildTypeInterfaceNames(IType type) {
if (type == null) {
return Collections.emptySet();
}
HashSet<String> names = new HashSet<String>();
while (type != null) {
this.addInterfaceNamesTo(type, names);
type = this.findSuperclass(type);
}
return names;
}
private void addInterfaceNamesTo(IType type, HashSet<String> names) {
for (String interfaceSignature : this.getSuperInterfaceTypeSignatures(type)) {
String interfaceName = convertTypeSignatureToTypeName(interfaceSignature);
names.add(interfaceName);
IType interfaceType = this.findType(interfaceName);
if (interfaceType != null) {
this.addInterfaceNamesTo(interfaceType, names); // recurse
}
}
}
// ***** type type argument names
public ListIterator<String> typeTypeArgumentNames() {
return new CloneListIterator<String>(this.typeTypeArgumentNames);
}
public int typeTypeArgumentNamesSize() {
return this.typeTypeArgumentNames.size();
}
public String getTypeTypeArgumentName(int index) {
return this.typeTypeArgumentNames.get(index);
}
private void setTypeTypeArgumentNames(List<String> typeTypeArgumentNames) {
this.synchronizeList(typeTypeArgumentNames, this.typeTypeArgumentNames, TYPE_TYPE_ARGUMENT_NAMES_COLLECTION);
}
/**
* these types can be arrays (e.g. "java.lang.String[]");
* but they won't have any further nested generic type arguments
* (e.g. "java.util.Collection<java.lang.String>")
*/
private List<String> buildTypeTypeArgumentNames() {
String typeSignature = this.getTypeSignature();
if (typeSignature == null) {
return Collections.emptyList();
}
String[] typeArgumentSignatures = Signature.getTypeArguments(typeSignature);
if (typeArgumentSignatures.length == 0) {
return Collections.emptyList();
}
ArrayList<String> names = new ArrayList<String>(typeArgumentSignatures.length);
for (String typeArgumentSignature : typeArgumentSignatures) {
names.add(convertTypeSignatureToTypeName(typeArgumentSignature));
}
return names;
}
// ********** convenience methods **********
private String getTypeSignature() {
try {
return this.getAdapter().getTypeSignature();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return null;
}
}
private IType findSuperclass(IType type) {
return this.findTypeBySignature(this.getSuperclassSignature(type));
}
private String getSuperclassSignature(IType type) {
try {
return type.getSuperclassTypeSignature();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return null;
}
}
private String[] getSuperInterfaceTypeSignatures(IType type) {
try {
return type.getSuperInterfaceTypeSignatures();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return EMPTY_STRING_ARRAY;
}
}
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private IType findTypeBySignature(String typeSignature) {
return (typeSignature == null) ? null : this.findType(convertTypeSignatureToTypeName_(typeSignature));
}
private IType getType() {
return (this.typeName == null) ? null : this.findType(this.typeName);
}
private IType findType(String fullyQualifiedName) {
try {
return this.getJavaProject().findType(fullyQualifiedName);
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return null;
}
}
private IJavaProject getJavaProject() {
return this.getMember().getJavaProject();
}
// ********** adapters **********
/**
* Adapt an IField or IMethod.
*/
interface Adapter
extends BinaryPersistentMember.Adapter
{
/**
* Return the field or getter method's "attribute" name
* (e.g. field "foo" -> "foo"; method "getFoo" -> "foo").
*/
String getAttributeName();
/**
* Return whether the attribute is a Java field (as opposed to a method).
*/
boolean isField();
/**
* Return the attribute's type signature.
*/
String getTypeSignature() throws JavaModelException;
}
/**
* IField adapter
*/
static class FieldAdapter
implements Adapter
{
final IField field;
FieldAdapter(IField field) {
super();
this.field = field;
}
public IField getMember() {
return this.field;
}
public boolean isPersistable() {
return this.field.exists() && JPTTools.fieldIsPersistable(new JPTToolsAdapter());
}
public IAnnotation[] getAnnotations() throws JavaModelException {
return this.field.getAnnotations();
}
public String getAttributeName() {
return this.field.getElementName();
}
public boolean isField() {
return true;
}
public String getTypeSignature() throws JavaModelException {
return this.field.getTypeSignature();
}
/**
* JPTTools needs an adapter so it can work with either an IField
* or an IVariableBinding etc.
*/
class JPTToolsAdapter implements JPTTools.FieldAdapter {
public int getModifiers() {
try {
return FieldAdapter.this.field.getFlags();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return 0;
}
}
}
}
/**
* IMethod adapter
*/
static class MethodAdapter
implements Adapter
{
final IMethod method;
static final IMethod[] EMPTY_METHOD_ARRAY = new IMethod[0];
MethodAdapter(IMethod method) {
super();
this.method = method;
}
public IMethod getMember() {
return this.method;
}
public boolean isPersistable() {
return JPTTools.methodIsPersistablePropertyGetter(new JPTToolsAdapter());
}
public IAnnotation[] getAnnotations() throws JavaModelException {
return this.method.getAnnotations();
}
public String getAttributeName() {
return NameTools.convertGetterMethodNameToPropertyName(this.method.getElementName());
}
public boolean isField() {
return false;
}
public String getTypeSignature() throws JavaModelException {
return this.method.getReturnType();
}
/**
* JPTTools needs an adapter so it can work with either an IMethod
* or an IMethodBinding etc.
*/
abstract static class AbstractJPTToolsAdapter
implements JPTTools.SimpleMethodAdapter
{
AbstractJPTToolsAdapter() {
super();
}
abstract IMethod getMethod();
public int getModifiers() {
try {
return this.getMethod().getFlags();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return 0;
}
}
public String getReturnTypeErasureName() {
return convertTypeSignatureToTypeName(this.getReturnTypeSignature());
}
private String getReturnTypeSignature() {
try {
return this.getMethod().getReturnType();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return null;
}
}
public boolean isConstructor() {
try {
return this.getMethod().isConstructor();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return false;
}
}
}
static class SimpleJPTToolsAdapter
extends AbstractJPTToolsAdapter
{
private final IMethod method;
SimpleJPTToolsAdapter(IMethod method) {
super();
this.method = method;
}
@Override
IMethod getMethod() {
return this.method;
}
}
class JPTToolsAdapter
extends AbstractJPTToolsAdapter
implements JPTTools.MethodAdapter
{
JPTToolsAdapter() {
super();
}
@Override
IMethod getMethod() {
return MethodAdapter.this.method;
}
public String getName() {
return this.getMethod().getElementName();
}
public int getParametersLength() {
return this.getMethod().getParameterTypes().length;
}
public JPTTools.SimpleMethodAdapter getSibling(String name) {
for (IMethod sibling : this.getSiblings()) {
if ((sibling.getParameterTypes().length == 0)
&& sibling.getElementName().equals(name)) {
return new SimpleJPTToolsAdapter(sibling);
}
}
return null;
}
public JPTTools.SimpleMethodAdapter getSibling(String name, String parameterTypeErasureName) {
for (IMethod sibling : this.getSiblings()) {
String[] parmTypes = sibling.getParameterTypes();
if ((parmTypes.length == 1)
&& sibling.getElementName().equals(name)
&& convertTypeSignatureToTypeName(parmTypes[0]).equals(parameterTypeErasureName)) {
return new SimpleJPTToolsAdapter(sibling);
}
}
return null;
}
private IMethod[] getSiblings() {
try {
return this.getMethod().getDeclaringType().getMethods();
} catch (JavaModelException ex) {
JptCorePlugin.log(ex);
return EMPTY_METHOD_ARRAY;
}
}
}
}
}