blob: ca6546963dad92df5f1f6deaf553372a2bea5f8b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2008 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;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jpt.core.internal.utility.jdt.JDTTools;
import org.eclipse.jpt.core.internal.utility.jdt.JDTType;
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.core.resource.java.JpaCompilationUnit;
import org.eclipse.jpt.core.resource.java.NestableAnnotation;
import org.eclipse.jpt.core.utility.jdt.Type;
import org.eclipse.jpt.utility.MethodSignature;
import org.eclipse.jpt.utility.internal.CollectionTools;
import org.eclipse.jpt.utility.internal.Counter;
import org.eclipse.jpt.utility.internal.iterators.CloneIterator;
import org.eclipse.jpt.utility.internal.iterators.CompositeIterator;
import org.eclipse.jpt.utility.internal.iterators.FilteringIterator;
public class JavaResourcePersistentTypeImpl
extends AbstractJavaResourcePersistentMember<Type>
implements JavaResourcePersistentType
{
private String qualifiedName;
private String name;
private String superClassQualifiedName;
private boolean abstract_; // 'abstract' is a reserved word
/**
* store all member types including those that aren't persistable so we can
* generate validation errors
*/
private final Vector<JavaResourcePersistentType> nestedTypes;
private final Vector<JavaResourcePersistentAttribute> fields;
private final Vector<JavaResourcePersistentAttribute> methods;
private AccessType accessType;
// ********** construction **********
/**
* build top-level persistent type
*/
// TODO use JPA factory
public static JavaResourcePersistentType newInstance(
JpaCompilationUnit jpaCompilationUnit,
TypeDeclaration typeDeclaration,
CompilationUnit astRoot) {
Type type = new JDTType(
typeDeclaration,
jpaCompilationUnit.getCompilationUnit(),
jpaCompilationUnit.getModifySharedDocumentCommandExecutorProvider(),
jpaCompilationUnit.getAnnotationEditFormatter());
JavaResourcePersistentType jrpt = new JavaResourcePersistentTypeImpl(jpaCompilationUnit, type);
jrpt.initialize(astRoot);
return jrpt;
}
/**
* build nested persistent type
*/
// TODO use JPA factory
protected static JavaResourcePersistentType newInstance(
JpaCompilationUnit jpaCompilationUnit,
Type declaringType,
TypeDeclaration typeDeclaration,
int occurrence,
CompilationUnit astRoot) {
Type type = new JDTType(
declaringType,
typeDeclaration,
occurrence,
jpaCompilationUnit.getCompilationUnit(),
jpaCompilationUnit.getModifySharedDocumentCommandExecutorProvider(),
jpaCompilationUnit.getAnnotationEditFormatter());
JavaResourcePersistentType jrpt = new JavaResourcePersistentTypeImpl(jpaCompilationUnit, type);
jrpt.initialize(astRoot);
return jrpt;
}
public JavaResourcePersistentTypeImpl(JpaCompilationUnit jpaCompilationUnit, Type type) {
super(jpaCompilationUnit, type);
this.nestedTypes = new Vector<JavaResourcePersistentType>();
this.fields = new Vector<JavaResourcePersistentAttribute>();
this.methods = new Vector<JavaResourcePersistentAttribute>();
}
@Override
public void initialize(CompilationUnit astRoot) {
super.initialize(astRoot);
this.qualifiedName = this.buildQualifiedName(astRoot);
this.name = this.buildName(astRoot);
this.superClassQualifiedName = this.buildSuperClassQualifiedName(astRoot);
this.abstract_ = this.buildAbstract(astRoot);
this.initializeNestedTypes(astRoot);
this.initializeFields(astRoot);
this.initializeMethods(astRoot);
this.accessType = this.buildAccessType();
}
protected void initializeNestedTypes(CompilationUnit astRoot) {
CounterMap counters = new CounterMap();
for (TypeDeclaration td : this.getMember().getTypes(astRoot)) {
String tdName = td.getName().getFullyQualifiedName();
int occurrence = counters.increment(tdName);
this.nestedTypes.add(this.buildNestedType(td, occurrence, astRoot));
}
}
protected void initializeFields(CompilationUnit astRoot) {
CounterMap counters = new CounterMap();
for (FieldDeclaration fieldDeclaration : this.getMember().getFields(astRoot)) {
for (VariableDeclarationFragment fragment : fragments(fieldDeclaration)) {
String fieldName = fragment.getName().getFullyQualifiedName();
int occurrence = counters.increment(fieldName);
this.fields.add(this.buildField(fieldName, occurrence, astRoot));
}
}
}
protected void initializeMethods(CompilationUnit astRoot) {
CounterMap counters = new CounterMap();
for (MethodDeclaration methodDeclaration : this.getMember().getMethods(astRoot)) {
MethodSignature signature = JDTTools.buildMethodSignature(methodDeclaration);
int occurrence = counters.increment(signature);
this.methods.add(this.buildMethod(signature, occurrence, astRoot));
}
}
// ********** AbstractJavaResourcePersistentMember implementation **********
@Override
protected Annotation buildMappingAnnotation(String mappingAnnotationName) {
return getAnnotationProvider().buildTypeMappingAnnotation(this, getMember(), mappingAnnotationName);
}
@Override
protected Annotation buildNullMappingAnnotation(String annotationName) {
return getAnnotationProvider().buildNullTypeMappingAnnotation(this, getMember(), annotationName);
}
@Override
protected Annotation buildAnnotation(String annotationName) {
return getAnnotationProvider().buildTypeAnnotation(this, getMember(), annotationName);
}
@Override
protected Annotation buildNullAnnotation(String annotationName) {
return getAnnotationProvider().buildNullTypeAnnotation(this, getMember(), annotationName);
}
@Override
protected ListIterator<String> possibleMappingAnnotationNames() {
return getAnnotationProvider().typeMappingAnnotationNames();
}
@Override
protected boolean isPossibleAnnotation(String annotationName) {
return CollectionTools.contains(getAnnotationProvider().typeAnnotationNames(), annotationName);
}
@Override
protected boolean isPossibleMappingAnnotation(String annotationName) {
return CollectionTools.contains(getAnnotationProvider().typeMappingAnnotationNames(), annotationName);
}
@Override
@SuppressWarnings("unchecked")
//overriding purely to suppress the warning you get at the class level
public ListIterator<NestableAnnotation> annotations(String nestableAnnotationName, String containerAnnotationName) {
return super.annotations(nestableAnnotationName, containerAnnotationName);
}
@Override
@SuppressWarnings("unchecked")
//overriding purely to suppress the warning you get at the class level
public Iterator<Annotation> mappingAnnotations() {
return super.mappingAnnotations();
}
@Override
@SuppressWarnings("unchecked")
//overriding purely to suppress the warning you get at the class level
public Iterator<Annotation> annotations() {
return super.annotations();
}
@Override
public void resolveTypes(CompilationUnit astRoot) {
super.resolveTypes(astRoot);
this.setSuperClassQualifiedName(this.buildSuperClassQualifiedName(astRoot));
for (Iterator<JavaResourcePersistentAttribute> stream = this.fields_(); stream.hasNext(); ) {
stream.next().resolveTypes(astRoot);
}
// a new type can trigger a method parameter type to be a resolved,
// fully-qualified name, so we need to rebuild our list of methods:
// "setFoo(Foo)" is not the same as "setFoo(com.bar.Foo)"
// and, vice-versa, a removed type can "unresolve" a parameter type
this.updateMethods(astRoot);
for (Iterator<JavaResourcePersistentAttribute> stream = this.methods_(); stream.hasNext(); ) {
stream.next().resolveTypes(astRoot);
}
for (Iterator<JavaResourcePersistentType> stream = this.nestedTypes_(); stream.hasNext(); ) {
stream.next().resolveTypes(astRoot);
}
}
@Override
public void toString(StringBuilder sb) {
sb.append(this.name);
}
// ******** JavaResourcePersistentType implementation ********
public JavaResourcePersistentType getJavaPersistentTypeResource(String fullyQualifiedTypeName) {
// TODO not sure why a null name is coming through here... ~bjv
// if (fullyQualifiedTypeName.equals(this.getQualifiedName())) {
if (this.getQualifiedName().equals(fullyQualifiedTypeName)) {
return this;
}
for (Iterator<JavaResourcePersistentType> stream = this.nestedTypes(); stream.hasNext(); ) {
JavaResourcePersistentType jrpt = stream.next();
// recurse
JavaResourcePersistentType nestedJRPT = jrpt.getJavaPersistentTypeResource(fullyQualifiedTypeName);
if (nestedJRPT != null) {
return nestedJRPT;
}
}
return null;
}
/**
* check only persistable attributes
*/
public boolean hasAnyAttributeAnnotations() {
for (Iterator<JavaResourcePersistentAttribute> stream = this.attributes(); stream.hasNext(); ) {
if (stream.next().hasAnyAnnotation()) {
return true;
}
}
return false;
}
// ********** nested types **********
/**
* return only persistable nested types
*/
public Iterator<JavaResourcePersistentType> nestedTypes() {
//TODO since we are filtering how do we handle the case where a type becomes persistable?
//what kind of change notificiation for that case?
return new FilteringIterator<JavaResourcePersistentType, JavaResourcePersistentType>(this.nestedTypes_()) {
@Override
protected boolean accept(JavaResourcePersistentType jrpt) {
return jrpt.isPersistable();
}
};
}
/**
* *all* nested types
*/
protected Iterator<JavaResourcePersistentType> nestedTypes_() {
return new CloneIterator<JavaResourcePersistentType>(this.nestedTypes);
}
protected void addNestedType(JavaResourcePersistentType nestedType) {
this.addItemToCollection(nestedType, this.nestedTypes, NESTED_TYPES_COLLECTION);
}
protected void removeNestedType(JavaResourcePersistentType nestedType) {
this.removeItemFromCollection(nestedType, this.nestedTypes, NESTED_TYPES_COLLECTION);
}
protected void removeNestedTypes(Collection<JavaResourcePersistentType> remove) {
this.removeItemsFromCollection(remove, this.nestedTypes, NESTED_TYPES_COLLECTION);
}
protected JavaResourcePersistentType getNestedType(String typeName, int occurrence) {
for (Iterator<JavaResourcePersistentType> stream = this.nestedTypes_(); stream.hasNext(); ) {
JavaResourcePersistentType nestedType = stream.next();
if (nestedType.isFor(typeName, occurrence)) {
return nestedType;
}
}
return null;
}
// ********** attributes **********
/**
* return only persistable attributes
*/
// TODO since we are filtering, how do we handle the case where an attribute becomes persistable?
// what kind of change notification for that case?
public Iterator<JavaResourcePersistentAttribute> attributes() {
return persistableMembers(this.attributes_());
}
/**
* *all* fields and methods
*/
@SuppressWarnings("unchecked")
protected Iterator<JavaResourcePersistentAttribute> attributes_() {
return new CompositeIterator<JavaResourcePersistentAttribute>(this.fields_(), this.methods_());
}
// ********** fields **********
/**
* return only persistable fields
*/
public Iterator<JavaResourcePersistentAttribute> fields() {
return persistableMembers(this.fields_());
}
/**
* *all* fields
*/
protected Iterator<JavaResourcePersistentAttribute> fields_() {
return new CloneIterator<JavaResourcePersistentAttribute>(this.fields);
}
protected void addField(JavaResourcePersistentAttribute attribute) {
this.addItemToCollection(attribute, this.fields, ATTRIBUTES_COLLECTION);
}
protected void removeField(JavaResourcePersistentAttribute attribute) {
this.removeItemFromCollection(attribute, this.fields, ATTRIBUTES_COLLECTION);
}
protected void removeFields(Collection<JavaResourcePersistentAttribute> remove) {
this.removeItemsFromCollection(remove, this.fields, ATTRIBUTES_COLLECTION);
}
protected JavaResourcePersistentAttribute getField(String fieldName, int occurrence) {
for (Iterator<JavaResourcePersistentAttribute> stream = this.fields_(); stream.hasNext(); ) {
JavaResourcePersistentAttribute field = stream.next();
if (field.isFor(fieldName, occurrence)) {
return field;
}
}
return null;
}
// ********** methods **********
/**
* return only persistable properties
*/
public Iterator<JavaResourcePersistentAttribute> properties() {
return persistableMembers(this.methods_());
}
/**
* *all* methods
*/
protected Iterator<JavaResourcePersistentAttribute> methods_() {
return new CloneIterator<JavaResourcePersistentAttribute>(this.methods);
}
protected void addMethod(JavaResourcePersistentAttribute attribute) {
this.addItemToCollection(attribute, this.methods, ATTRIBUTES_COLLECTION);
}
protected void removeMethod(JavaResourcePersistentAttribute attribute) {
this.removeItemFromCollection(attribute, this.methods, ATTRIBUTES_COLLECTION);
}
protected void removeMethods(Collection<JavaResourcePersistentAttribute> remove) {
this.removeItemsFromCollection(remove, this.methods, ATTRIBUTES_COLLECTION);
}
protected JavaResourcePersistentAttribute getMethod(MethodSignature signature, int occurrence) {
for (Iterator<JavaResourcePersistentAttribute> stream = this.methods_(); stream.hasNext(); ) {
JavaResourcePersistentAttribute method = stream.next();
if (method.isFor(signature, occurrence)) {
return method;
}
}
return null;
}
// ********** simple instance variables **********
public String getQualifiedName() {
return this.qualifiedName;
}
protected void setQualifiedName(String qualifiedName) {
String old = this.qualifiedName;
this.qualifiedName = qualifiedName;
this.firePropertyChanged(QUALIFIED_NAME_PROPERTY, old, qualifiedName);
}
public String getName() {
return this.name;
}
protected void setName(String name) {
String old = this.name;
this.name = name;
this.firePropertyChanged(NAME_PROPERTY, old, name);
}
public String getSuperClassQualifiedName() {
return this.superClassQualifiedName;
}
protected void setSuperClassQualifiedName(String superClassQualifiedName) {
String old = this.superClassQualifiedName;
this.superClassQualifiedName = superClassQualifiedName;
this.firePropertyChanged(SUPER_CLASS_QUALIFIED_NAME_PROPERTY, old, superClassQualifiedName);
}
public boolean isAbstract() {
return this.abstract_;
}
protected void setAbstract(boolean abstract_) {
boolean old = this.abstract_;
this.abstract_ = abstract_;
this.firePropertyChanged(ABSTRACT_PROPERTY, old, abstract_);
}
public AccessType getAccess() {
return this.accessType;
}
// TODO
//seems we could have a public changeAccess() api which would
//move all annotations from fields to their corresponding methods or vice versa
//though of course it's more complicated than that since what if the
//corresponding field/method does not exist?
//making this internal since it should only be set based on changes in the source, the
//context model should not need to set this
protected void setAccess(AccessType accessType) {
AccessType old = this.accessType;
this.accessType = accessType;
this.firePropertyChanged(ACCESS_PROPERTY, old, accessType);
}
// ********** update from Java **********
@Override
public void updateFromJava(CompilationUnit astRoot) {
super.updateFromJava(astRoot);
this.setQualifiedName(this.buildQualifiedName(astRoot));
this.setName(this.buildName(astRoot));
this.setSuperClassQualifiedName(this.buildSuperClassQualifiedName(astRoot));
this.setAbstract(this.buildAbstract(astRoot));
this.updateNestedTypes(astRoot);
this.updateFields(astRoot);
this.updateMethods(astRoot);
this.setAccess(this.buildAccessType());
}
protected void updateNestedTypes(CompilationUnit astRoot) {
CounterMap counters = new CounterMap();
@SuppressWarnings("unchecked")
Vector<JavaResourcePersistentType> nestedTypesToRemove = (Vector<JavaResourcePersistentType>) this.nestedTypes.clone();
for (TypeDeclaration typeDeclaration : this.getMember().getTypes(astRoot)) {
String tdName = typeDeclaration.getName().getFullyQualifiedName();
int occurrence = counters.increment(tdName);
JavaResourcePersistentType nestedType = this.getNestedType(tdName, occurrence);
if (nestedType == null) {
this.addNestedType(this.buildNestedType(typeDeclaration, occurrence, astRoot));
} else {
nestedTypesToRemove.remove(nestedType);
nestedType.updateFromJava(astRoot);
}
}
this.removeNestedTypes(nestedTypesToRemove);
}
protected void updateFields(CompilationUnit astRoot) {
CounterMap counters = new CounterMap();
@SuppressWarnings("unchecked")
Vector<JavaResourcePersistentAttribute> fieldsToRemove = (Vector<JavaResourcePersistentAttribute>) this.fields.clone();
for (FieldDeclaration fieldDeclaration : this.getMember().getFields(astRoot)) {
for (VariableDeclarationFragment fragment : fragments(fieldDeclaration)) {
String fieldName = fragment.getName().getFullyQualifiedName();
int occurrence = counters.increment(fieldName);
JavaResourcePersistentAttribute field = this.getField(fieldName, occurrence);
if (field == null) {
this.addField(this.buildField(fieldName, occurrence, astRoot));
} else {
fieldsToRemove.remove(field);
field.updateFromJava(astRoot);
}
}
}
this.removeFields(fieldsToRemove);
}
protected void updateMethods(CompilationUnit astRoot) {
CounterMap counters = new CounterMap();
@SuppressWarnings("unchecked")
Vector<JavaResourcePersistentAttribute> methodsToRemove = (Vector<JavaResourcePersistentAttribute>) this.methods.clone();
for (MethodDeclaration methodDeclaration : this.getMember().getMethods(astRoot)) {
MethodSignature signature = JDTTools.buildMethodSignature(methodDeclaration);
int occurrence = counters.increment(signature);
JavaResourcePersistentAttribute method = this.getMethod(signature, occurrence);
if (method == null) {
this.addMethod(this.buildMethod(signature, occurrence, astRoot));
} else {
methodsToRemove.remove(method);
method.updateFromJava(astRoot);
}
}
this.removeMethods(methodsToRemove);
}
// ********** building state from AST **********
protected String buildQualifiedName(CompilationUnit astRoot) {
return this.getMember().getBinding(astRoot).getQualifiedName();
}
protected String buildName(CompilationUnit astRoot) {
return this.getMember().getBinding(astRoot).getName();
}
protected String buildSuperClassQualifiedName(CompilationUnit astRoot) {
ITypeBinding typeBinding = this.getMember().getBinding(astRoot);
if (typeBinding == null) {
return null;
}
ITypeBinding superClassTypeBinding = typeBinding.getSuperclass();
if (superClassTypeBinding == null) {
return null;
}
return superClassTypeBinding.getTypeDeclaration().getQualifiedName();
}
protected boolean buildAbstract(CompilationUnit astRoot) {
return Modifier.isAbstract(this.getMember().getBinding(astRoot).getModifiers());
}
// TODO use JPA factory
protected JavaResourcePersistentType buildNestedType(TypeDeclaration nestedTypeDeclaration, int occurrence, CompilationUnit astRoot) {
return newInstance(this.getJpaCompilationUnit(), this.getMember(), nestedTypeDeclaration, occurrence, astRoot);
}
// TODO use JPA factory
protected JavaResourcePersistentAttribute buildField(String fieldName, int occurrence, CompilationUnit astRoot) {
return JavaResourcePersistentAttributeImpl.newInstance(this, this.getMember(), fieldName, occurrence, this.getJpaCompilationUnit(), astRoot);
}
// TODO use JPA factory
protected JavaResourcePersistentAttribute buildMethod(MethodSignature signature, int occurrence, CompilationUnit astRoot) {
return JavaResourcePersistentAttributeImpl.newInstance(this, this.getMember(), signature, occurrence, this.getJpaCompilationUnit(), astRoot);
}
/**
* Return the AccessType currently implied by the Java source code:
* - if only Fields are annotated => FIELD
* - if only Properties are annotated => PROPERTY
* - if both Fields and Properties are annotated => FIELD
* - if nothing is annotated
* - and fields exist => FIELD
* - and properties exist, but no fields exist => PROPERTY
* - and neither fields nor properties exist => null at this level (FIELD in the context model)
*/
protected AccessType buildAccessType() {
boolean hasPersistableFields = false;
boolean hasPersistableProperties = false;
for (JavaResourcePersistentAttribute field : CollectionTools.iterable(fields())) {
hasPersistableFields = true;
if (field.hasAnyAnnotation()) {
// any field is annotated => FIELD
return AccessType.FIELD;
}
}
for (JavaResourcePersistentAttribute property : CollectionTools.iterable(properties())) {
hasPersistableProperties = true;
if (property.hasAnyAnnotation()) {
// none of the fields are annotated and a getter is annotated => PROPERTY
return AccessType.PROPERTY;
}
}
if (hasPersistableProperties && !hasPersistableFields) {
return AccessType.PROPERTY;
}
//no annotations exist, access is null at the resource model level
return null;
}
// ********** static methods **********
// minimize scope of suppressed warnings
@SuppressWarnings("unchecked")
protected static List<VariableDeclarationFragment> fragments(FieldDeclaration fd) {
return fd.fragments();
}
// ********** StringCounter **********
protected static class CounterMap {
HashMap<Object, Counter> counters = new HashMap<Object, Counter>();
/**
* Return the incremented count for the specified object.
*/
int increment(Object o) {
Counter counter = this.counters.get(o);
if (counter == null) {
counter = new Counter();
this.counters.put(o, counter);
}
counter.increment();
return counter.count();
}
}
}