diff options
author | bvosburgh | 2010-04-13 19:02:25 +0000 |
---|---|---|
committer | bvosburgh | 2010-04-13 19:02:25 +0000 |
commit | b1d6030eaef59063c87452d9b42ab6bb951d94dd (patch) | |
tree | 58ab1d3169cce8989c5a6660ba662ea2f323b778 /jpa | |
parent | a983b1db0395bc424bf3d818b504ebd62128e769 (diff) | |
download | webtools.dali-b1d6030eaef59063c87452d9b42ab6bb951d94dd.tar.gz webtools.dali-b1d6030eaef59063c87452d9b42ab6bb951d94dd.tar.xz webtools.dali-b1d6030eaef59063c87452d9b42ab6bb951d94dd.zip |
[305079] canonical metamodel for nested classes
Diffstat (limited to 'jpa')
27 files changed, 1349 insertions, 750 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaFactory.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaFactory.java index 3a24c053f0..bf311dab43 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaFactory.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaFactory.java @@ -27,6 +27,7 @@ import org.eclipse.jpt.core.context.java.JavaAttributeMapping; import org.eclipse.jpt.core.context.java.JavaAttributeOverride; import org.eclipse.jpt.core.context.java.JavaAttributeOverrideContainer; import org.eclipse.jpt.core.context.java.JavaBaseColumn; +import org.eclipse.jpt.core.context.java.JavaBaseColumn.Owner; import org.eclipse.jpt.core.context.java.JavaBaseJoinColumn; import org.eclipse.jpt.core.context.java.JavaBasicMapping; import org.eclipse.jpt.core.context.java.JavaColumn; @@ -68,7 +69,6 @@ import org.eclipse.jpt.core.context.java.JavaTransientMapping; import org.eclipse.jpt.core.context.java.JavaTypeMapping; import org.eclipse.jpt.core.context.java.JavaUniqueConstraint; import org.eclipse.jpt.core.context.java.JavaVersionMapping; -import org.eclipse.jpt.core.context.java.JavaBaseColumn.Owner; import org.eclipse.jpt.core.context.orm.OrmXml; import org.eclipse.jpt.core.context.persistence.MappingFileRef; import org.eclipse.jpt.core.context.persistence.PersistenceXml; @@ -130,8 +130,8 @@ import org.eclipse.jpt.core.internal.jpa2.context.java.NullJavaMapKeyColumn2_0; import org.eclipse.jpt.core.internal.jpa2.context.java.NullJavaOrderColumn2_0; import org.eclipse.jpt.core.internal.jpa2.context.java.NullJavaOrphanRemoval2_0; import org.eclipse.jpt.core.jpa2.JpaFactory2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; import org.eclipse.jpt.core.jpa2.context.Orderable2_0; -import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheable2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheableHolder2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCollectionTable2_0; @@ -168,7 +168,7 @@ public abstract class AbstractJpaFactory return new GenericJpaProject(config); } - public PersistentType2_0.MetamodelSynchronizer buildPersistentTypeMetamodelSynchronizer(PersistentType2_0 persistentType) { + public MetamodelSourceType.Synchronizer buildMetamodelSynchronizer(MetamodelSourceType sourceType) { return null; } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaProject.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaProject.java index fdcfdb67fa..42280b830f 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaProject.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/AbstractJpaProject.java @@ -924,7 +924,7 @@ public abstract class AbstractJpaProject // ********** metamodel ********** - public Iterable<JavaResourcePersistentType2_0> getGeneratedMetamodelTypes() { + public Iterable<JavaResourcePersistentType2_0> getGeneratedMetamodelTopLevelTypes() { if (this.metamodelSourceFolderName == null) { return EmptyIterable.instance(); } @@ -932,25 +932,23 @@ public abstract class AbstractJpaProject return new FilteringIterable<JavaResourcePersistentType2_0>(this.getInternalSourceJavaResourcePersistentTypes2_0()) { @Override protected boolean accept(JavaResourcePersistentType2_0 jrpt) { - return jrpt.isGeneratedMetamodel(genSourceFolder); + return jrpt.isGeneratedMetamodelTopLevelType(genSourceFolder); } }; } - public JavaResourcePersistentType2_0 getGeneratedMetamodelType(IFile file) { + public JavaResourcePersistentType2_0 getGeneratedMetamodelTopLevelType(IFile file) { JavaResourceCompilationUnit jrcu = this.getJavaResourceCompilationUnit(file); if (jrcu == null) { return null; // hmmm... } + // TODO add API to JRCU to get top-level persistent type Iterator<JavaResourcePersistentType> types = jrcu.persistentTypes(); if ( ! types.hasNext()) { return null; // no types in the file } JavaResourcePersistentType2_0 jrpt = (JavaResourcePersistentType2_0) types.next(); - if (types.hasNext()) { - return null; // should have only a single type in the file - } - return jrpt.isGeneratedMetamodel() ? jrpt : null; + return jrpt.isGeneratedMetamodelTopLevelType() ? jrpt : null; } protected JavaResourceCompilationUnit getJavaResourceCompilationUnit(IFile file) { diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/orm/GenericOrmIdClassReference.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/orm/GenericOrmIdClassReference.java index 34a0624550..5b9a999399 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/orm/GenericOrmIdClassReference.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/orm/GenericOrmIdClassReference.java @@ -78,15 +78,15 @@ public class GenericOrmIdClassReference String oldClassName = this.specifiedIdClassName; this.specifiedIdClassName = newClassName; if (valuesAreDifferent(oldClassName, newClassName)) { - if (getIdClassElement() != null) { - getIdClassElement().setClassName(newClassName); - if (getIdClassElement().isUnset()) { + if (getIdXmlClassRef() != null) { + getIdXmlClassRef().setClassName(newClassName); + if (getIdXmlClassRef().isUnset()) { removeIdClassElement(); } } else if (newClassName != null) { addIdClassElement(); - getIdClassElement().setClassName(newClassName); + getIdXmlClassRef().setClassName(newClassName); } } firePropertyChanged(SPECIFIED_ID_CLASS_NAME_PROPERTY, oldClassName, newClassName); @@ -99,11 +99,8 @@ public class GenericOrmIdClassReference } protected String buildSpecifiedIdClassName() { - XmlClassReference element = getIdClassElement(); - if (element != null) { - return element.getClassName(); - } - return null; + XmlClassReference idXmlClassRef = this.getIdXmlClassRef(); + return (idXmlClassRef == null) ? null : idXmlClassRef.getClassName(); } public String getDefaultIdClassName() { @@ -156,8 +153,8 @@ public class GenericOrmIdClassReference return (XmlIdClassContainer) getResourceTypeMapping(); } - protected XmlClassReference getIdClassElement() { - return getResourceIdClassContainer().getIdClass(); + protected XmlClassReference getIdXmlClassRef() { + return this.getResourceIdClassContainer().getIdClass(); } protected void addIdClassElement() { @@ -169,10 +166,17 @@ public class GenericOrmIdClassReference } protected JavaResourcePersistentType getResourceIdClass() { - XmlClassReference element = getIdClassElement(); - String className = (element == null) ? - null : element.getClassName(); - return getEntityMappings().resolveJavaResourcePersistentType(className); + XmlClassReference idXmlClassRef = this.getIdXmlClassRef(); + if (idXmlClassRef == null) { + return null; + } + + String className = idXmlClassRef.getClassName(); + if (className == null) { + return null; + } + + return this.getEntityMappings().resolveJavaResourcePersistentType(className); } protected EntityMappings getEntityMappings() { @@ -208,9 +212,10 @@ public class GenericOrmIdClassReference // **************** validation ******************************************** public TextRange getValidationTextRange() { - XmlClassReference element = getIdClassElement(); - return (element == null) ? - getTypeMapping().getValidationTextRange() : element.getClassNameTextRange(); + XmlClassReference idXmlClassRef = getIdXmlClassRef(); + return (idXmlClassRef == null) ? + this.getTypeMapping().getValidationTextRange() : + idXmlClassRef.getClassNameTextRange(); } @Override diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/persistence/AbstractPersistenceUnit.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/persistence/AbstractPersistenceUnit.java index 12e161aca5..0a593cef94 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/persistence/AbstractPersistenceUnit.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/context/persistence/AbstractPersistenceUnit.java @@ -10,17 +10,22 @@ package org.eclipse.jpt.core.internal.context.persistence; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.Set; import java.util.Vector; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jpt.core.JpaProject; import org.eclipse.jpt.core.JpaStructureNode; import org.eclipse.jpt.core.JptCorePlugin; import org.eclipse.jpt.core.context.AccessType; @@ -28,6 +33,7 @@ import org.eclipse.jpt.core.context.Embeddable; import org.eclipse.jpt.core.context.Entity; import org.eclipse.jpt.core.context.Generator; import org.eclipse.jpt.core.context.MappingFilePersistenceUnitDefaults; +import org.eclipse.jpt.core.context.PersistentAttribute; import org.eclipse.jpt.core.context.PersistentType; import org.eclipse.jpt.core.context.Query; import org.eclipse.jpt.core.context.TypeMapping; @@ -42,13 +48,16 @@ import org.eclipse.jpt.core.context.persistence.PersistenceUnitTransactionType; import org.eclipse.jpt.core.context.persistence.PersistentTypeContainer; import org.eclipse.jpt.core.internal.validation.DefaultJpaValidationMessages; import org.eclipse.jpt.core.internal.validation.JpaValidationMessages; +import org.eclipse.jpt.core.jpa2.JpaFactory2_0; import org.eclipse.jpt.core.jpa2.JpaProject2_0; import org.eclipse.jpt.core.jpa2.context.MappingFilePersistenceUnitDefaults2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; import org.eclipse.jpt.core.jpa2.context.persistence.PersistenceUnit2_0; import org.eclipse.jpt.core.jpa2.context.persistence.options.SharedCacheMode; import org.eclipse.jpt.core.jpa2.context.persistence.options.ValidationMode; import org.eclipse.jpt.core.jpa2.resource.java.JavaResourcePersistentType2_0; +import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType; import org.eclipse.jpt.core.resource.persistence.PersistenceFactory; import org.eclipse.jpt.core.resource.persistence.XmlJarFileRef; import org.eclipse.jpt.core.resource.persistence.XmlJavaClassRef; @@ -56,6 +65,7 @@ import org.eclipse.jpt.core.resource.persistence.XmlMappingFileRef; import org.eclipse.jpt.core.resource.persistence.XmlPersistenceUnit; import org.eclipse.jpt.core.resource.persistence.XmlProperties; import org.eclipse.jpt.core.resource.persistence.XmlProperty; +import org.eclipse.jpt.core.utility.BodySourceWriter; import org.eclipse.jpt.core.utility.TextRange; import org.eclipse.jpt.utility.internal.CollectionTools; import org.eclipse.jpt.utility.internal.HashBag; @@ -70,6 +80,7 @@ import org.eclipse.jpt.utility.internal.iterables.TransformationIterable; import org.eclipse.jpt.utility.internal.iterators.CloneIterator; import org.eclipse.jpt.utility.internal.iterators.CloneListIterator; import org.eclipse.jpt.utility.internal.iterators.EmptyIterator; +import org.eclipse.jpt.utility.internal.iterators.EmptyListIterator; import org.eclipse.jpt.utility.internal.iterators.FilteringIterator; import org.eclipse.jpt.utility.internal.iterators.TransformationIterator; import org.eclipse.wst.validation.internal.provisional.core.IMessage; @@ -1690,7 +1701,7 @@ public abstract class AbstractPersistenceUnit } protected Iterable<IFile> getGeneratedMetamodelFiles() { - return new TransformationIterable<JavaResourcePersistentType2_0, IFile>(this.getGeneratedMetamodelTypes()) { + return new TransformationIterable<JavaResourcePersistentType2_0, IFile>(this.getGeneratedMetamodelTopLevelTypes()) { @Override protected IFile transform(JavaResourcePersistentType2_0 jrpt) { return jrpt.getFile(); @@ -1698,26 +1709,97 @@ public abstract class AbstractPersistenceUnit }; } - protected Iterable<JavaResourcePersistentType2_0> getGeneratedMetamodelTypes() { - return ((JpaProject2_0) this.getJpaProject()).getGeneratedMetamodelTypes(); + protected Iterable<JavaResourcePersistentType2_0> getGeneratedMetamodelTopLevelTypes() { + return ((JpaProject2_0) this.getJpaProject()).getGeneratedMetamodelTopLevelTypes(); } /** - * If we have the same persistent type in multiple locations, the last one - * we encounter wins (i.e. the classes in the orm.xml take precedence). + * Not the prettiest code.... */ public void synchronizeMetamodel() { - // gather the persistent unit's types (eliminating duplicates, ignoring case) - HashMap<String, PersistentType2_0> persistentTypes = new HashMap<String, PersistentType2_0>(); - this.addContainerPersistentTypesTo(this.getJarFileRefs(), persistentTypes); - this.addPersistentTypesTo(this.getNonNullClassRefPersistentTypes(), persistentTypes); - this.addContainerPersistentTypesTo(this.getMappingFileRefs(), persistentTypes); + // gather up the persistent unit's types, eliminating duplicates; + // if we have persistent types with the same name in multiple locations, + // the last one we encounter wins (i.e. the classes in the orm.xml take + // precedence) + HashMap<String, PersistentType2_0> allPersistentTypes = new HashMap<String, PersistentType2_0>(); + this.addPersistentTypesTo_(this.getJarFileRefs(), allPersistentTypes); + this.addPersistentTypesTo(this.getNonNullClassRefPersistentTypes(), allPersistentTypes); + this.addPersistentTypesTo_(this.getMappingFileRefs(), allPersistentTypes); + + // build a list of the top-level types and a tree of their associated + // member types etc. + ArrayList<MetamodelSourceType> topLevelTypes = new ArrayList<MetamodelSourceType>(allPersistentTypes.size()); + HashMap<String, Collection<MetamodelSourceType>> memberTypeTree = new HashMap<String, Collection<MetamodelSourceType>>(); + for (PersistentType2_0 type : allPersistentTypes.values()) { + String declaringTypeName = type.getDeclaringTypeName(); + MetamodelSourceType memberType = type; + while (true) { + if (declaringTypeName == null) { + topLevelTypes.add(memberType); + break; // stop at the top-level type + } + + // associate the member type with its declaring type + Collection<MetamodelSourceType> memberTypes = memberTypeTree.get(declaringTypeName); + if (memberTypes == null) { + memberTypes = new ArrayList<MetamodelSourceType>(); + memberTypeTree.put(declaringTypeName, memberTypes); + } + memberTypes.add(memberType); + + // move out to the member type's declaring type + String memberTypeName = declaringTypeName; + // check for a context persistent type + memberType = allPersistentTypes.get(memberTypeName); + if (memberType != null) { + break; // stop - this will be processed in the outer 'for' loop + } + // check for a Java resource persistent type + JavaResourcePersistentType jrpt = this.getJpaProject().getJavaResourcePersistentType(memberTypeName); + if (jrpt != null) { + declaringTypeName = jrpt.getDeclaringTypeName(); + } else { + // check for a JDT type + IType jdtType = this.findJdtType(memberTypeName); + if (jdtType != null) { + IType jdtDeclaringType = jdtType.getDeclaringType(); + declaringTypeName = (jdtDeclaringType == null) ? null : jdtDeclaringType.getFullyQualifiedName('.'); + } else { + // assume we have a non-persistent top-level type...? + declaringTypeName = null; + } + } + if (declaringTypeName == null) { + memberType = this.selectSourceType(topLevelTypes, memberTypeName); + } else { + memberType = this.selectSourceType(memberTypeTree.get(declaringTypeName), memberTypeName); + } + if (memberType != null) { + break; // stop - this type has already been processed + } + memberType = this.buildNonPersistentMetamodelSourceType(memberTypeName); + } + } + + // remove any top-level type whose name differs from another only by case, + // since, on Windows, file names are case-insensitive :-( + // sort the original list so we end up with the same top-level type + // remaining every time (i.e. the one that sorts out first) + Collections.sort(topLevelTypes, MetamodelSourceType.COMPARATOR); + HashSet<String> names = new HashSet<String>(topLevelTypes.size()); + for (Iterator<MetamodelSourceType> stream = topLevelTypes.iterator(); stream.hasNext(); ) { + MetamodelSourceType topLevelType = stream.next(); + // hopefully this is case-insensitive enough... + if ( ! names.add(topLevelType.getName().toLowerCase())) { + stream.remove(); + } + } // copy the list of metamodel files... HashSet<IFile> deadMetamodelFiles = new HashSet<IFile>(this.metamodelFiles); this.metamodelFiles.clear(); - for (PersistentType2_0 persistentType : persistentTypes.values()) { - IFile metamodelFile = persistentType.getMetamodelFile(); + for (MetamodelSourceType topLevelType : topLevelTypes) { + IFile metamodelFile = topLevelType.getMetamodelFile(); // ...remove whatever files are still present... deadMetamodelFiles.remove(metamodelFile); // ...rebuild the list of metamodel files... @@ -1732,13 +1814,14 @@ public abstract class AbstractPersistenceUnit for (IFile deadMetamodelFile : deadMetamodelFiles) { this.deleteMetamodelFile(deadMetamodelFile); } + // now generate the metamodel classes - for (PersistentType2_0 persistentType : persistentTypes.values()) { - persistentType.synchronizeMetamodel(); + for (MetamodelSourceType topLevelType : topLevelTypes) { + topLevelType.synchronizeMetamodel(memberTypeTree); } } - protected void addContainerPersistentTypesTo(Iterable<? extends PersistentTypeContainer> ptContainers, HashMap<String, PersistentType2_0> persistentTypeMap) { + protected void addPersistentTypesTo_(Iterable<? extends PersistentTypeContainer> ptContainers, HashMap<String, PersistentType2_0> persistentTypeMap) { for (PersistentTypeContainer ptContainer : ptContainers) { this.addPersistentTypesTo(ptContainer.getPersistentTypes(), persistentTypeMap); } @@ -1747,10 +1830,33 @@ public abstract class AbstractPersistenceUnit protected void addPersistentTypesTo(Iterable<? extends PersistentType> persistentTypes, HashMap<String, PersistentType2_0> persistentTypeMap) { for (PersistentType persistentType : persistentTypes) { if (persistentType.getName() != null) { - // hopefully this is case-insensitive enough... - persistentTypeMap.put(persistentType.getName().toLowerCase(), (PersistentType2_0) persistentType); + persistentTypeMap.put(persistentType.getName(), (PersistentType2_0) persistentType); + } + } + } + + protected MetamodelSourceType selectSourceType(Iterable<MetamodelSourceType> types, String typeName) { + if (types != null) { + for (MetamodelSourceType type : types) { + if (type.getName().equals(typeName)) { + return type; + } } } + return null; + } + + protected MetamodelSourceType buildNonPersistentMetamodelSourceType(String nonPersistentTypeName) { + return new NonPersistentMetamodelSourceType(nonPersistentTypeName, this.getJpaProject()); + } + + protected IType findJdtType(String typeName) { + try { + return this.getJpaProject().getJavaProject().findType(typeName); + } catch (JavaModelException ex) { + JptCorePlugin.log(ex); + return null; + } } protected void deleteMetamodelFile(IFile file) { @@ -1768,11 +1874,67 @@ public abstract class AbstractPersistenceUnit } protected boolean fileIsGeneratedMetamodel(IFile file) { - return ((JpaProject2_0) this.getJpaProject()).getGeneratedMetamodelType(file) != null; + return ((JpaProject2_0) this.getJpaProject()).getGeneratedMetamodelTopLevelType(file) != null; } public void disposeMetamodel() { this.metamodelFiles.clear(); } + // ***** Metamodel source for non-persistent types + protected static class NonPersistentMetamodelSourceType + implements MetamodelSourceType + { + protected final String name; + protected final JpaProject jpaProject; + protected final MetamodelSourceType.Synchronizer metamodelSynchronizer; + + protected NonPersistentMetamodelSourceType(String name, JpaProject jpaProject) { + super(); + this.name = name; + this.jpaProject = jpaProject; + this.metamodelSynchronizer = this.buildMetamodelSynchronizer(); + } + + protected MetamodelSourceType.Synchronizer buildMetamodelSynchronizer() { + return this.getJpaFactory().buildMetamodelSynchronizer(this); + } + + protected JpaFactory2_0 getJpaFactory() { + return (JpaFactory2_0) this.getJpaProject().getJpaPlatform().getJpaFactory(); + } + + public String getName() { + return this.name; + } + + public boolean isManaged() { + return false; + } + + public PersistentType getSuperPersistentType() { + return null; + } + + public <T extends PersistentAttribute> ListIterator<T> attributes() { + return EmptyListIterator.instance(); + } + + public IFile getMetamodelFile() { + return this.metamodelSynchronizer.getFile(); + } + + public JpaProject getJpaProject() { + return this.jpaProject; + } + + public void synchronizeMetamodel(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + this.metamodelSynchronizer.synchronize(memberTypeTree); + } + + public void printBodySourceOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + this.metamodelSynchronizer.printBodySourceOn(pw, memberTypeTree); + } + } + } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/orm/GenericOrmPersistentType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/orm/GenericOrmPersistentType.java index 9ce20731cd..71b2ad97e5 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/orm/GenericOrmPersistentType.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/orm/GenericOrmPersistentType.java @@ -15,6 +15,7 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import java.util.Map; import java.util.Vector; import org.eclipse.core.resources.IFile; @@ -39,7 +40,8 @@ import org.eclipse.jpt.core.internal.context.orm.AbstractOrmXmlContextNode; import org.eclipse.jpt.core.internal.validation.DefaultJpaValidationMessages; import org.eclipse.jpt.core.internal.validation.JpaValidationMessages; import org.eclipse.jpt.core.jpa2.JpaFactory2_0; -import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; +import org.eclipse.jpt.core.jpa2.context.java.JavaPersistentType2_0; import org.eclipse.jpt.core.jpa2.context.orm.OrmPersistentType2_0; import org.eclipse.jpt.core.resource.java.JavaResourcePersistentAttribute; import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType; @@ -48,6 +50,7 @@ import org.eclipse.jpt.core.resource.orm.OrmPackage; import org.eclipse.jpt.core.resource.orm.XmlAttributeMapping; import org.eclipse.jpt.core.resource.orm.XmlTypeMapping; import org.eclipse.jpt.core.resource.xml.EmfTools; +import org.eclipse.jpt.core.utility.BodySourceWriter; import org.eclipse.jpt.core.utility.TextRange; import org.eclipse.jpt.utility.internal.ClassName; import org.eclipse.jpt.utility.internal.CollectionTools; @@ -90,9 +93,11 @@ public class GenericOrmPersistentType protected PersistentType superPersistentType; + protected String declaringTypeName; + protected JavaPersistentType javaPersistentType; - protected final PersistentType2_0.MetamodelSynchronizer metamodelSynchronizer; + protected final MetamodelSourceType.Synchronizer metamodelSynchronizer; public GenericOrmPersistentType(EntityMappings parent, XmlTypeMapping resourceMapping) { @@ -102,12 +107,15 @@ public class GenericOrmPersistentType this.defaultAccess = this.buildDefaultAccess(); this.javaPersistentType = this.buildJavaPersistentType(); this.superPersistentType = this.buildSuperPersistentType(); + this.declaringTypeName = this.buildDeclaringTypeName(); this.initializeAttributes(); this.metamodelSynchronizer = this.buildMetamodelSynchronizer(); } - protected PersistentType2_0.MetamodelSynchronizer buildMetamodelSynchronizer() { - return ((JpaFactory2_0) this.getJpaFactory()).buildPersistentTypeMetamodelSynchronizer(this); + protected MetamodelSourceType.Synchronizer buildMetamodelSynchronizer() { + return this.isJpa2_0Compatible() ? + ((JpaFactory2_0) this.getJpaFactory()).buildMetamodelSynchronizer(this) : + null; } @@ -119,6 +127,7 @@ public class GenericOrmPersistentType this.mapping.update(); this.updateJavaPersistentType(); this.updateSuperPersistentType(); + this.setDeclaringTypeName(this.buildDeclaringTypeName()); this.updateAttributes(); } @@ -138,8 +147,9 @@ public class GenericOrmPersistentType // ********** name ********** public String getName() { - String className = this.mapping.getClass_(); - return className != null ? className.replace('$', '.') : null; + String name = this.mapping.getClass_(); + // nested class names are specified with a '$' in orm.xml + return (name == null) ? null : name.replace('$', '.'); } public String getShortName(){ @@ -687,8 +697,8 @@ public class GenericOrmPersistentType return this.getEntityMappings().resolveJavaResourcePersistentType(this.getName()); } - protected JavaResourcePersistentType getJavaResourcePersistentType(String qualifiedClassName) { - return this.getJpaProject().getJavaResourcePersistentType(qualifiedClassName); + protected JavaResourcePersistentType getJavaResourcePersistentType(String className) { + return this.getJpaProject().getJavaResourcePersistentType(className); } protected JavaPersistentType buildJavaPersistentType(JavaResourcePersistentType jrpt) { @@ -757,6 +767,28 @@ public class GenericOrmPersistentType } + // ********** declaring type name ********** + + public String getDeclaringTypeName() { + return this.declaringTypeName; + } + + protected void setDeclaringTypeName(String declaringTypeName) { + String old = this.declaringTypeName; + this.declaringTypeName = declaringTypeName; + this.firePropertyChanged(DECLARING_TYPE_NAME_PROPERTY, old, declaringTypeName); + } + + protected String buildDeclaringTypeName() { + return this.isJpa2_0Compatible() ? buildDeclaringTypeName_() : null; + } + + protected String buildDeclaringTypeName_() { + return (this.javaPersistentType == null) ? + null : ((JavaPersistentType2_0) this.javaPersistentType).getDeclaringTypeName(); + } + + // ********** metamodel ********** public IFile getMetamodelFile() { @@ -767,13 +799,23 @@ public class GenericOrmPersistentType // do nothing - probably shouldn't be called... } + public boolean isManaged() { + return true; + } + /** * All orm.xml persistent types must be able to generate a static metamodel * because 1.0 orm.xml files can be referenced from 2.0 persistence.xml files. */ - public void synchronizeMetamodel() { + public void synchronizeMetamodel(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + if (this.javaPersistentType != null) { + this.metamodelSynchronizer.synchronize(memberTypeTree); + } + } + + public void printBodySourceOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { if (this.javaPersistentType != null) { - this.metamodelSynchronizer.synchronize(); + this.metamodelSynchronizer.printBodySourceOn(pw, memberTypeTree); } } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/persistence/GenericClassRef.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/persistence/GenericClassRef.java index 423301aec9..7b679a02d9 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/persistence/GenericClassRef.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa1/context/persistence/GenericClassRef.java @@ -27,6 +27,7 @@ import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType; import org.eclipse.jpt.core.resource.persistence.XmlJavaClassRef; import org.eclipse.jpt.core.utility.TextRange; import org.eclipse.jpt.utility.internal.StringTools; +import org.eclipse.jpt.utility.internal.Tools; import org.eclipse.wst.validation.internal.provisional.core.IMessage; import org.eclipse.wst.validation.internal.provisional.core.IReporter; @@ -120,7 +121,7 @@ public class GenericClassRef // ********** queries ********** public boolean isFor(String typeName) { - return (this.className != null) && this.className.replace('$', '.').equals(typeName); + return Tools.valuesAreEqual(typeName, this.getJavaClassName()); } public boolean isVirtual() { @@ -155,6 +156,14 @@ public class GenericClassRef this.firePropertyChanged(CLASS_NAME_PROPERTY, old, newClassName); } + /** + * Nested classes will be qualified with a '$'; the Java name is qualified + * with a '.'. Like <code>className</code>, this can be <code>null</code>. + */ + protected String getJavaClassName() { + return StringTools.stringIsEmpty(this.className) ? null : this.className.replace('$', '.'); + } + // ********** java persistent type ********** @@ -194,7 +203,8 @@ public class GenericClassRef } protected JavaResourcePersistentType getJavaResourcePersistentType() { - return (this.className == null) ? null : this.getJpaProject().getJavaResourcePersistentType(this.className.replace('$', '.')); + String javaClassName = this.getJavaClassName(); + return (javaClassName == null) ? null : this.getJpaProject().getJavaResourcePersistentType(javaClassName); } @@ -247,7 +257,7 @@ public class GenericClassRef DefaultJpaValidationMessages.buildMessage( IMessage.HIGH_SEVERITY, JpaValidationMessages.PERSISTENCE_UNIT_NONEXISTENT_CLASS, - new String[] {this.className}, + new String[] {this.getJavaClassName()}, this, this.getValidationTextRange() ) @@ -255,16 +265,18 @@ public class GenericClassRef return; } - // 190062 validate Java class only if this is the only reference to it + // 190062 validate Java class only if this is the only reference to it; + // i.e. the persistence.xml ref is the only ref - none of the mapping + // files reference the same class boolean validateJavaPersistentType = true; - for (Iterator<MappingFileRef> stream = this.getPersistenceUnit().mappingFileRefsContaining(this.className); stream.hasNext(); ) { + for (Iterator<MappingFileRef> stream = this.getPersistenceUnit().mappingFileRefsContaining(this.getJavaClassName()); stream.hasNext(); ) { validateJavaPersistentType = false; MappingFileRef mappingFileRef = stream.next(); messages.add( DefaultJpaValidationMessages.buildMessage( IMessage.LOW_SEVERITY, JpaValidationMessages.PERSISTENCE_UNIT_REDUNDANT_CLASS, - new String[] {this.className, mappingFileRef.getFileName()}, + new String[] {this.getJavaClassName(), mappingFileRef.getFileName()}, this, this.getValidationTextRange() ) @@ -289,7 +301,7 @@ public class GenericClassRef @Override public void toString(StringBuilder sb) { - sb.append(this.className); + sb.append(this.getJavaClassName()); } } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericJpaFactory2_0.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericJpaFactory2_0.java index 3fb05ca72e..32c814c96c 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericJpaFactory2_0.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericJpaFactory2_0.java @@ -16,6 +16,7 @@ import org.eclipse.jpt.core.context.PersistentType; import org.eclipse.jpt.core.context.java.JavaAssociationOverride; import org.eclipse.jpt.core.context.java.JavaAssociationOverrideContainer; import org.eclipse.jpt.core.context.java.JavaAssociationOverrideRelationshipReference; +import org.eclipse.jpt.core.context.java.JavaBaseColumn.Owner; import org.eclipse.jpt.core.context.java.JavaColumn; import org.eclipse.jpt.core.context.java.JavaEmbeddable; import org.eclipse.jpt.core.context.java.JavaJpaContextNode; @@ -27,7 +28,6 @@ import org.eclipse.jpt.core.context.java.JavaOneToOneMapping; import org.eclipse.jpt.core.context.java.JavaPersistentAttribute; import org.eclipse.jpt.core.context.java.JavaPersistentType; import org.eclipse.jpt.core.context.java.JavaSequenceGenerator; -import org.eclipse.jpt.core.context.java.JavaBaseColumn.Owner; import org.eclipse.jpt.core.internal.AbstractJpaFactory; import org.eclipse.jpt.core.internal.jpa1.context.java.GenericJavaAssociationOverrideContainer; import org.eclipse.jpt.core.internal.jpa1.context.java.GenericJavaColumn; @@ -48,7 +48,7 @@ import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaPersistentType import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaSequenceGenerator2_0; import org.eclipse.jpt.core.internal.jpa2.context.java.VirtualAssociationOverride2_0Annotation; import org.eclipse.jpt.core.jpa2.JpaProject2_0; -import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheable2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheableHolder2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCollectionTable2_0; @@ -89,8 +89,8 @@ public class GenericJpaFactory2_0 } @Override - public PersistentType2_0.MetamodelSynchronizer buildPersistentTypeMetamodelSynchronizer(PersistentType2_0 persistentType) { - return new GenericPersistentTypeMetamodelSynchronizer(persistentType); + public MetamodelSourceType.Synchronizer buildMetamodelSynchronizer(MetamodelSourceType sourceType) { + return new GenericMetamodelSynchronizer(sourceType); } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericMetamodelSynchronizer.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericMetamodelSynchronizer.java new file mode 100644 index 0000000000..8dc8d64205 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericMetamodelSynchronizer.java @@ -0,0 +1,432 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 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.jpa2; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IPackageFragment; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jpt.core.JptCorePlugin; +import org.eclipse.jpt.core.context.AttributeMapping; +import org.eclipse.jpt.core.context.PersistentAttribute; +import org.eclipse.jpt.core.context.PersistentType; +import org.eclipse.jpt.core.jpa2.JpaProject2_0; +import org.eclipse.jpt.core.jpa2.context.AttributeMapping2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelField; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; +import org.eclipse.jpt.core.jpa2.resource.java.JPA2_0; +import org.eclipse.jpt.core.jpa2.resource.java.JavaResourcePersistentType2_0; +import org.eclipse.jpt.core.utility.BodySourceWriter; +import org.eclipse.jpt.utility.internal.ClassName; +import org.eclipse.jpt.utility.internal.SimpleStack; +import org.eclipse.jpt.utility.internal.StringTools; + +import com.ibm.icu.text.DateFormat; +import com.ibm.icu.text.SimpleDateFormat; + +/** + * For now, the "synchronization" is simple brute-force: we generate the source + * code and then compare it with what is already present in the file. + * If the new source is different, we replace the file contents; otherwise, we + * leave the file unchanged. + */ +@SuppressWarnings("nls") +public class GenericMetamodelSynchronizer + implements MetamodelSourceType.Synchronizer +{ + protected final MetamodelSourceType sourceType; + + + public GenericMetamodelSynchronizer(MetamodelSourceType sourceType) { + super(); + this.sourceType = sourceType; + } + + public IFile getFile() { + return (IFile) this.getPackageFragment().getCompilationUnit(this.getFileName()).getResource(); + } + + + // ********** synchronize ********** + + public void synchronize(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + try { + this.synchronize_(memberTypeTree); + } catch (JavaModelException ex) { + JptCorePlugin.log(ex); + } + } + + protected void synchronize_(Map<String, Collection<MetamodelSourceType>> memberTypeTree) throws JavaModelException { + IPackageFragment pkg = this.getPackageFragment(); + String fileName = this.getFileName(); + + ICompilationUnit compilationUnit = pkg.getCompilationUnit(fileName); + if (compilationUnit.exists()) { + // overwrite existing file if it has changed (ignoring the timestamp) + String newSource = this.buildSource(compilationUnit, memberTypeTree); + if (newSource != null) { + pkg.createCompilationUnit(fileName, newSource, true, null); // true=force + } + } else { + // write a new file, creating the package folders if necessary + if ( ! pkg.exists()) { + this.getSourceFolder().createPackageFragment(pkg.getElementName(), true, null); // true=force + } + pkg.createCompilationUnit(fileName, this.buildSource(memberTypeTree), false, null); // false=no force + } + } + + /** + * pre-condition: the compilation unit exists + * + * return null if the old source is not to be replaced + */ + protected String buildSource(ICompilationUnit compilationUnit, Map<String, Collection<MetamodelSourceType>> memberTypeTree) throws JavaModelException { + IFile file = (IFile) compilationUnit.getResource(); + JavaResourcePersistentType2_0 genType = this.getJpaProject().getGeneratedMetamodelTopLevelType(file); + if (genType == null) { + return null; // the file exists, but its source is not a generated metamodel top-level class + } + + String oldSource = compilationUnit.getSource(); + int oldLength = oldSource.length(); + + String newSource = this.buildSource(memberTypeTree); + int newLength = newSource.length(); + if (newLength != oldLength) { + return newSource; + } + + String date = genType.getGeneratedAnnotation().getDate(); // if we get here, this will be non-empty + int dateBegin = oldSource.indexOf(date); + if (dateBegin == -1) { + return null; // hmmm... + } + int dateEnd = dateBegin + date.length(); + if (dateEnd > oldLength) { + return null; // hmmm... + } + + if (newSource.regionMatches(0, oldSource, 0, dateBegin) && + newSource.regionMatches(dateEnd, oldSource, dateEnd, oldLength - dateEnd)) { + return null; + } + return newSource; + } + + + // ********** package/file ********** + + protected IPackageFragment getPackageFragment() { + return this.getSourceFolder().getPackageFragment(this.getPackageName()); + } + + protected IPackageFragmentRoot getSourceFolder() { + return this.getJpaProject().getMetamodelPackageFragmentRoot(); + } + + protected JpaProject2_0 getJpaProject() { + return (JpaProject2_0) this.sourceType.getJpaProject(); + } + + protected String getPackageName() { + return this.buildPackageName(this.sourceType.getName()); + } + + protected String buildPackageName(String topLevelSourceTypeName) { + return this.buildPackageName_(ClassName.getPackageName(topLevelSourceTypeName)); + } + + // TODO + protected String buildPackageName_(String sourcePackageName) { + // the default is to store the metamodel class in the same package as the source type + return sourcePackageName; + } + + protected String getFileName() { + return ClassName.getSimpleName(this.getClassName()) + ".java"; + } + + protected String getClassName() { + return this.buildClassName(this.sourceType.getName()); + } + + protected String buildClassName(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + return this.buildClassName(this.sourceType.getName(), memberTypeTree); + } + + protected String buildClassName(String sourceTypeName, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + String current = sourceTypeName; + SimpleStack<String> stack = new SimpleStack<String>(); + + while (true) { + stack.push(ClassName.getSimpleName(current)); + String declaringTypeName = this.getDeclaringTypeName(current, memberTypeTree); + if (declaringTypeName == null) { + break; + } + current = declaringTypeName; + } + + StringBuilder sb = new StringBuilder(sourceTypeName.length() + 10); + sb.append(this.buildPackageName(current)); + while ( ! stack.isEmpty()) { + sb.append('.'); + sb.append(this.buildSimpleClassName(stack.pop())); + } + return sb.toString(); + } + + protected String buildClassName(String topLevelSourceTypeName) { + return this.buildPackageName(topLevelSourceTypeName) + '.' + this.buildSimpleClassName(ClassName.getSimpleName(topLevelSourceTypeName)); + } + + protected String getSimpleClassName() { + return this.buildSimpleClassName(ClassName.getSimpleName(this.sourceType.getName())); + } + + // TODO + protected String buildSimpleClassName(String simpleSourceTypeName) { + // the default is to simply append an underscore to the source type name + return simpleSourceTypeName + '_'; + } + + + // ********** source code ********** + + /** + * build the "body" source first; then build the "package" and "imports" source + * and concatenate the "body" source to it + */ + protected String buildSource(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + // build the body source first so we can gather up the import statements + BodySourceWriter bodySourceWriter = this.buildBodySourceWriter(memberTypeTree); + + StringWriter sw = new StringWriter(bodySourceWriter.getLength() + 2000); + PrintWriter pw = new PrintWriter(sw); + this.printPackageAndImportsOn(pw, bodySourceWriter); + pw.print(bodySourceWriter.getSource()); + return sw.toString(); + } + + protected BodySourceWriter buildBodySourceWriter(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + BodySourceWriter pw = new BodySourceWriter(this.getPackageName(), this.getClassName()); + this.printBodySourceOn(pw, memberTypeTree); + return pw; + } + + public void printBodySourceOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + this.printClassDeclarationOn(pw, memberTypeTree); + pw.print(" {"); + pw.println(); + + pw.indent(); + boolean attributesPrinted = this.printAttributesOn(pw); + this.printMemberTypesOn(pw, memberTypeTree, attributesPrinted); + pw.undent(); + + pw.print('}'); + pw.println(); // EOF + } + + + // ********** class declaration ********** + + protected void printClassDeclarationOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + boolean topLevel = this.sourceTypeIsTopLevel(memberTypeTree); + if (topLevel) { + this.printGeneratedAnnotationOn(pw); + } + if (this.sourceType.isManaged()) { + this.printStaticMetamodelAnnotationOn(pw); + } + + pw.print("public "); + if ( ! topLevel) { + pw.print("static "); + } + pw.print("class "); + pw.print(this.getSimpleClassName()); // this is always the simple name + PersistentType superPersistentType = this.sourceType.getSuperPersistentType(); + if (superPersistentType != null) { + pw.print(" extends "); + pw.printTypeDeclaration(this.buildClassName(superPersistentType.getName(), memberTypeTree)); + } + } + + /** + * Return whether the source type is a top level type. + * This can be inferred from the specified member type tree. + */ + protected boolean sourceTypeIsTopLevel(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + return this.sourceTypeIsTopLevel(this.sourceType.getName(), memberTypeTree); + } + + /** + * Return whether the specified source type is a top level type. + * This can be inferred from the specified member type tree. + */ + protected boolean sourceTypeIsTopLevel(String sourceTypeName, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + return this.getDeclaringTypeName(sourceTypeName, memberTypeTree) == null; + } + + /** + * Return the name of the specified source type's declaring type, as + * implied by the specified member type tree. Return null if the source + * type is a top-level type. + */ + protected String getDeclaringTypeName(String sourceTypeName, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + int lastPeriod = sourceTypeName.lastIndexOf('.'); + if (lastPeriod == -1) { + return null; // default package top-level type + } + String declaringTypeName = sourceTypeName.substring(0, lastPeriod); + return (memberTypeTree.get(declaringTypeName) == null) ? null : declaringTypeName; + } + + protected void printGeneratedAnnotationOn(BodySourceWriter pw) { + pw.printAnnotation("javax.annotation.Generated"); + pw.print('('); + pw.print("value="); + pw.printStringLiteral(JavaResourcePersistentType2_0.METAMODEL_GENERATED_ANNOTATION_VALUE); + pw.print(", "); + pw.print("date="); + pw.printStringLiteral(format(new Date())); + pw.print(')'); + pw.println(); + } + + protected void printStaticMetamodelAnnotationOn(BodySourceWriter pw) { + pw.printAnnotation(JPA2_0.STATIC_METAMODEL); + pw.print('('); + pw.printTypeDeclaration(this.sourceType.getName()); + pw.print(".class"); + pw.print(')'); + pw.println(); + } + + /** + * {@link SimpleDateFormat} is not thread-safe. + */ + protected static synchronized String format(Date date) { + return DATE_FORMAT.format(date); + } + /** + * Recommended date format is ISO 8601. + * @see javax.annotation.Generated + */ + private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + + + // ********** attributes ********** + + /** + * Return whether any attributes were printed. + */ + protected boolean printAttributesOn(BodySourceWriter pw) { + boolean printed = false; + for (Iterator<PersistentAttribute> stream = this.sourceType.attributes(); stream.hasNext(); ) { + this.printAttributeOn(stream.next(), pw); + printed = true; + } + return printed; + } + + protected void printAttributeOn(PersistentAttribute persistentAttribute, BodySourceWriter pw) { + AttributeMapping attributeMapping = persistentAttribute.getMapping(); + if (attributeMapping != null) { // probably shouldn't be null? + this.printAttributeMappingOn(attributeMapping, pw); + } + } + + protected void printAttributeMappingOn(AttributeMapping attributeMapping, BodySourceWriter pw) { + MetamodelField field = ((AttributeMapping2_0) attributeMapping).getMetamodelField(); + if (field != null) { + this.printFieldOn(field, pw); + } + } + + protected void printFieldOn(MetamodelField field, BodySourceWriter pw) { + for (String modifier : field.getModifiers()) { + pw.print(modifier); + pw.print(' '); + } + pw.printTypeDeclaration(field.getTypeName()); + pw.print('<'); + for (Iterator<String> stream = field.getTypeArgumentNames().iterator(); stream.hasNext(); ) { + pw.printTypeDeclaration(stream.next()); + if (stream.hasNext()) { + pw.print(", "); + } + } + pw.print('>'); + pw.print(' '); + pw.print(field.getName()); + pw.print(';'); + pw.println(); + } + + + // ********** member types ********** + + protected void printMemberTypesOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree, boolean attributesPrinted) { + Collection<MetamodelSourceType> memberTypes = memberTypeTree.get(this.sourceType.getName()); + if (memberTypes != null) { + if (attributesPrinted) { + pw.println(); + } + for (Iterator<MetamodelSourceType> stream = memberTypes.iterator(); stream.hasNext(); ) { + stream.next().printBodySourceOn(pw, memberTypeTree); + if (stream.hasNext()) { + pw.println(); + } + } + } + } + + + // ********** package and imports ********** + + protected void printPackageAndImportsOn(PrintWriter pw, BodySourceWriter bodySourceWriter) { + if (this.getPackageName().length() != 0) { + pw.print("package "); + pw.print(this.getPackageName()); + pw.print(';'); + pw.println(); + pw.println(); + } + + for (String import_ : bodySourceWriter.getImports()) { + pw.print("import "); + pw.print(import_); + pw.print(';'); + pw.println(); + } + pw.println(); + } + + + // ********** misc ********** + + @Override + public String toString() { + return StringTools.buildToStringFor(this, this.sourceType.getName()); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericPersistentTypeMetamodelSynchronizer.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericPersistentTypeMetamodelSynchronizer.java deleted file mode 100644 index 6e6108c88a..0000000000 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/GenericPersistentTypeMetamodelSynchronizer.java +++ /dev/null @@ -1,604 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2010 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.jpa2; - -import java.io.PrintWriter; -import java.io.Serializable; -import java.io.StringWriter; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.TreeSet; -import java.util.Map.Entry; - -import org.eclipse.core.resources.IFile; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IPackageFragment; -import org.eclipse.jdt.core.IPackageFragmentRoot; -import org.eclipse.jdt.core.JavaModelException; -import org.eclipse.jpt.core.JptCorePlugin; -import org.eclipse.jpt.core.context.AttributeMapping; -import org.eclipse.jpt.core.context.PersistentAttribute; -import org.eclipse.jpt.core.context.PersistentType; -import org.eclipse.jpt.core.jpa2.JpaProject2_0; -import org.eclipse.jpt.core.jpa2.context.AttributeMapping2_0; -import org.eclipse.jpt.core.jpa2.context.MetamodelField; -import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; -import org.eclipse.jpt.core.jpa2.resource.java.JPA2_0; -import org.eclipse.jpt.core.jpa2.resource.java.JavaResourcePersistentType2_0; -import org.eclipse.jpt.utility.Filter; -import org.eclipse.jpt.utility.internal.ClassName; -import org.eclipse.jpt.utility.internal.CollectionTools; -import org.eclipse.jpt.utility.internal.IndentingPrintWriter; -import org.eclipse.jpt.utility.internal.StringTools; -import org.eclipse.jpt.utility.internal.Transformer; -import org.eclipse.jpt.utility.internal.iterables.FilteringIterable; -import org.eclipse.jpt.utility.internal.iterables.TransformationIterable; - -import com.ibm.icu.text.Collator; -import com.ibm.icu.text.DateFormat; -import com.ibm.icu.text.SimpleDateFormat; - -/** - * For now, the "synchronization" is simple brute-force: we generate the source - * code and then compare it with what is already present in the file. - * If the new source is different, we replace the file contents; otherwise, we - * leave the file unchanged. - */ -@SuppressWarnings("nls") -public class GenericPersistentTypeMetamodelSynchronizer - implements PersistentType2_0.MetamodelSynchronizer -{ - protected final PersistentType2_0 persistentType; - - - public GenericPersistentTypeMetamodelSynchronizer(PersistentType2_0 persistentType) { - super(); - this.persistentType = persistentType; - } - - public IFile getFile() { - return (IFile) this.getPackageFragment().getCompilationUnit(this.getFileName()).getResource(); - } - - - // ********** synchronize ********** - - public void synchronize() { - try { - this.synchronize_(); - } catch (JavaModelException ex) { - JptCorePlugin.log(ex); - } - } - - protected void synchronize_() throws JavaModelException { - IPackageFragment pkg = this.getPackageFragment(); - String fileName = this.getFileName(); - - ICompilationUnit compilationUnit = pkg.getCompilationUnit(fileName); - if (compilationUnit.exists()) { - // overwrite existing file if it has changed (ignoring the timestamp) - String newSource = this.buildSource(compilationUnit); - if (newSource != null) { - pkg.createCompilationUnit(fileName, newSource, true, null); // true=force - } - } else { - // write a new file, creating the package folders if necessary - if ( ! pkg.exists()) { - this.getSourceFolder().createPackageFragment(pkg.getElementName(), true, null); // true=force - } - pkg.createCompilationUnit(fileName, this.buildSource(), false, null); // false=no force - } - } - - /** - * pre-condition: the compilation unit exists - * - * return null if the old source is not to be replaced - */ - protected String buildSource(ICompilationUnit compilationUnit) throws JavaModelException { - IFile file = (IFile) compilationUnit.getResource(); - JavaResourcePersistentType2_0 genType = this.getJpaProject().getGeneratedMetamodelType(file); - if (genType == null) { - return null; // the file exists, but its source is not a generated metamodel class - } - - String oldSource = compilationUnit.getSource(); - int oldLength = oldSource.length(); - - String newSource = this.buildSource(); - int newLength = newSource.length(); - if (newLength != oldLength) { - return newSource; - } - - String date = genType.getGeneratedAnnotation().getDate(); // if we get here, this will be non-empty - int dateBegin = oldSource.indexOf(date); - if (dateBegin == -1) { - return null; // hmmm... - } - int dateEnd = dateBegin + date.length(); - if (dateEnd > oldLength) { - return null; // hmmm... - } - - if (newSource.regionMatches(0, oldSource, 0, dateBegin) && - newSource.regionMatches(dateEnd, oldSource, dateEnd, oldLength - dateEnd)) { - return null; - } - return newSource; - } - - - // ********** package/file ********** - - protected IPackageFragment getPackageFragment() { - return this.getSourceFolder().getPackageFragment(this.getPackageName()); - } - - protected IPackageFragmentRoot getSourceFolder() { - return this.getJpaProject().getMetamodelPackageFragmentRoot(); - } - - protected JpaProject2_0 getJpaProject() { - return (JpaProject2_0) this.persistentType.getJpaProject(); - } - - // TODO - protected String getPackageName() { - // the default is to store the metamodel in the same package as the model - return ClassName.getPackageName(this.getMetamodelClassName()); - } - - protected String getFileName() { - return ClassName.getSimpleName(this.getMetamodelClassName()) + ".java"; - } - - protected String getMetamodelClassName() { - return this.buildMetamodelClassName(this.persistentType.getName()); - } - - // TODO - protected String buildMetamodelClassName(String className) { - // the default is to simply append an underscore to the model class name - return className + '_'; - } - - - // ********** source code ********** - - /** - * build the "body" source first; then build the "package" and "imports" source - * and concatenate the "body" source to it - */ - protected String buildSource() { - // build the body source first so we can gather up the import statements - BodySourceWriter bodySourceWriter = this.buildBodySourceWriter(); - - StringWriter sw = new StringWriter(bodySourceWriter.getLength() + 2000); - PrintWriter pw = new PrintWriter(sw); - this.printPackageAndImportsOn(pw, bodySourceWriter); - pw.print(bodySourceWriter.getSource()); - return sw.toString(); - } - - protected BodySourceWriter buildBodySourceWriter() { - BodySourceWriter pw = new BodySourceWriter(this.getPackageName(), this.getMetamodelClassName()); - this.printBodySourceOn(pw); - return pw; - } - - protected void printBodySourceOn(BodySourceWriter pw) { - this.printClassDeclarationOn(pw); - pw.print(" {"); - pw.println(); - - pw.indent(); - this.printAttributesOn(pw); - pw.undent(); - - pw.print('}'); - pw.println(); // EOF - } - - - // ********** class declaration ********** - - protected void printClassDeclarationOn(BodySourceWriter pw) { - this.printGeneratedAnnotationOn(pw); - this.printStaticMetamodelAnnotationOn(pw); - - pw.print("public class "); - pw.printTypeDeclaration(this.getMetamodelClassName()); - PersistentType superPersistentType = this.persistentType.getSuperPersistentType(); - if (superPersistentType != null) { - pw.print(" extends "); - pw.printTypeDeclaration(this.buildMetamodelClassName(superPersistentType.getName())); - } - } - - protected void printStaticMetamodelAnnotationOn(BodySourceWriter pw) { - pw.printAnnotation(JPA2_0.STATIC_METAMODEL); - pw.print('('); - pw.printTypeDeclaration(this.persistentType.getName()); - pw.print(".class"); - pw.print(')'); - pw.println(); - } - - protected void printGeneratedAnnotationOn(BodySourceWriter pw) { - pw.printAnnotation("javax.annotation.Generated"); - pw.print('('); - pw.print("value="); - pw.printStringLiteral(JavaResourcePersistentType2_0.METAMODEL_GENERATED_ANNOTATION_VALUE); - pw.print(", "); - pw.print("date="); - pw.printStringLiteral(format(new Date())); - pw.print(')'); - pw.println(); - } - - /** - * {@link SimpleDateFormat} is not thread-safe. - */ - protected static synchronized String format(Date date) { - return DATE_FORMAT.format(date); - } - /** - * Recommended date format is ISO 8601. - * @see javax.annotation.Generated - */ - private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); - - - // ********** attributes ********** - - protected void printAttributesOn(BodySourceWriter pw) { - for (Iterator<PersistentAttribute> stream = this.persistentType.attributes(); stream.hasNext(); ) { - this.printAttributeOn(stream.next(), pw); - } - } - - protected void printAttributeOn(PersistentAttribute persistentAttribute, BodySourceWriter pw) { - AttributeMapping attributeMapping = persistentAttribute.getMapping(); - if (attributeMapping != null) { // probably shouldn't be null? - this.printAttributeMappingOn(attributeMapping, pw); - } - } - - protected void printAttributeMappingOn(AttributeMapping attributeMapping, BodySourceWriter pw) { - MetamodelField field = ((AttributeMapping2_0) attributeMapping).getMetamodelField(); - if (field != null) { - this.printFieldOn(field, pw); - } - } - - protected void printFieldOn(MetamodelField field, BodySourceWriter pw) { - for (String modifier : field.getModifiers()) { - pw.print(modifier); - pw.print(' '); - } - pw.printTypeDeclaration(field.getTypeName()); - pw.print('<'); - for (Iterator<String> stream = field.getTypeArgumentNames().iterator(); stream.hasNext(); ) { - pw.printTypeDeclaration(stream.next()); - if (stream.hasNext()) { - pw.print(", "); - } - } - pw.print('>'); - pw.print(' '); - pw.print(field.getName()); - pw.print(';'); - pw.println(); - } - - - // ********** package and imports ********** - - protected void printPackageAndImportsOn(PrintWriter pw, BodySourceWriter bodySourceWriter) { - if (this.getPackageName().length() != 0) { - pw.print("package "); - pw.print(this.getPackageName()); - pw.print(';'); - pw.println(); - pw.println(); - } - - for (String import_ : bodySourceWriter.getImports()) { - pw.print("import "); - pw.print(import_); - pw.print(';'); - pw.println(); - } - pw.println(); - } - - - // ********** source writer ********** - - /** - * Extend IndentingPrintWriter with some methods that facilitate building - * class source code. - */ - protected static class BodySourceWriter - extends IndentingPrintWriter - { - protected final String packageName; - protected final String className; - // key = short class name; value = import package - protected final HashMap<String, ImportPackage> imports = new HashMap<String, ImportPackage>(); - - protected BodySourceWriter(String packageName, String className) { - super(new StringWriter(2000)); - this.packageName = packageName; - this.className = className; - } - - protected String getSource() { - return this.out.toString(); - } - - protected int getLength() { - return ((StringWriter) this.out).getBuffer().length(); - } - - protected void printVisibility(String visibilityModifier) { - if (visibilityModifier.length() != 0) { - this.print(visibilityModifier); - this.print(' '); - } - } - - protected void printAnnotation(String annotationName) { - this.print('@'); - this.printTypeDeclaration(annotationName); - } - - protected void printTypeDeclaration(String typeDeclaration) { - this.print(this.buildImportedTypeDeclaration(typeDeclaration)); - } - - protected void printField(String fieldName, String typeDeclaration, String visibility) { - this.printVisibility(visibility); - this.printTypeDeclaration(typeDeclaration); - this.print(' '); - this.print(fieldName); - this.print(';'); - this.println(); - this.println(); - } - - protected void printParameterizedField(String fieldName, String typeDeclaration, String parameterTypeDeclaration, String visibility) { - this.printVisibility(visibility); - this.printTypeDeclaration(typeDeclaration); - this.print('<'); - this.printTypeDeclaration(parameterTypeDeclaration); - this.print('>'); - this.print(' '); - this.print(fieldName); - this.print(';'); - this.println(); - this.println(); - } - - /** - * Convert the specified string to a String Literal and print it, - * adding the surrounding double-quotes and escaping characters - * as necessary. - */ - void printStringLiteral(String string) { - StringTools.convertToJavaStringLiteralOn(string, this); - } - - - // ********** imports ********** - - // ***** writing - /** - * Return the specified class's "imported" name. - * The class declaration must be of the form: - * "int" - * "int[]" (not "[I") - * "java.lang.Object" - * "java.lang.Object[]" (not "[Ljava.lang.Object;") - * "java.util.Map.Entry" (not "java.util.Map$Entry") - * "java.util.Map.Entry[][]" (not "[[Ljava.util.Map$Entry;") - * - * To really do this right, we would need to gather all the types from - * the "unamed" (default) package that were referenced in the - * compilation unit beforehand. *Any* collisions with one of these - * types would have to be fully qualified (whether it was from - * 'java.lang' or the same package as the current compilation unit). - * In other words, if we have any types from the "unnamed" package, - * results are unpredictable.... - */ - protected String buildImportedTypeDeclaration(String typeDeclaration) { - if (this.typeDeclarationIsMemberClass(typeDeclaration)) { - // no need for an import, just return the partially-qualified name - return this.buildMemberClassTypeDeclaration(typeDeclaration); - } - int last = typeDeclaration.lastIndexOf('.'); - String currentPackageName = (last == -1) ? "" : typeDeclaration.substring(0, last); - String shortTypeDeclaration = typeDeclaration.substring(last + 1); - String shortElementTypeName = shortTypeDeclaration; - while (shortElementTypeName.endsWith("[]")) { - shortElementTypeName = shortElementTypeName.substring(0, shortElementTypeName.length() - 2); - } - ImportPackage prev = this.imports.get(shortElementTypeName); - if (prev == null) { - // this is the first class with this short element type name - this.imports.put(shortElementTypeName, new ImportPackage(currentPackageName)); - return shortTypeDeclaration; - } - if (prev.packageName.equals(currentPackageName)) { - // this element type has already been imported - return shortTypeDeclaration; - } - if (currentPackageName.equals(this.packageName) && - prev.packageName.equals("java.lang")) { - // we force the 'java.lang' class to be explicitly imported - prev.collision = true; - } - // another class with the same short element type name has been - // previously imported, so this one must be used fully-qualified - return typeDeclaration; - } - - /** - * e.g. "foo.bar.Employee.PK" will return true - */ - protected boolean typeDeclarationIsMemberClass(String typeDeclaration) { - return (typeDeclaration.length() > this.className.length()) - && typeDeclaration.startsWith(this.className) - && (typeDeclaration.charAt(this.className.length()) == '.'); - } - - /** - * e.g. "foo.bar.Employee.PK" will return "Employee.PK" - * this prevents collisions with other imported classes (e.g. "joo.jar.PK") - */ - protected String buildMemberClassTypeDeclaration(String typeDeclaration) { - int index = this.packageName.length(); - if (index != 0) { - index++; // bump past the '.' - } - return typeDeclaration.substring(index); - } - - // ***** reading - protected Iterable<String> getImports() { - return this.getSortedRequiredImports(); - } - - /** - * transform our map entries to class names - */ - protected Iterable<String> getSortedRequiredImports() { - return new TransformationIterable<Map.Entry<String, ImportPackage>, String>(this.getSortedRequiredImportEntries(), this.buildImportEntriesTransformer()); - } - - protected Transformer<Map.Entry<String, ImportPackage>, String> buildImportEntriesTransformer() { - return IMPORT_ENTRIES_TRANSFORMER; - } - - protected static final Transformer<Map.Entry<String, ImportPackage>, String> IMPORT_ENTRIES_TRANSFORMER = new ImportEntriesTransformer(); - - protected static class ImportEntriesTransformer - implements Transformer<Map.Entry<String, ImportPackage>, String> - { - public String transform(Entry<String, ImportPackage> importEntry) { - String pkg = importEntry.getValue().packageName; - String type = importEntry.getKey(); - StringBuilder sb = new StringBuilder(pkg.length() + 1 + type.length()); - sb.append(pkg); - sb.append('.'); - sb.append(type); - return sb.toString(); - } - } - - /** - * sort by package first, then class (*not* by fully-qualified class name) - */ - protected Iterable<Map.Entry<String, ImportPackage>> getSortedRequiredImportEntries() { - TreeSet<Map.Entry<String, ImportPackage>> sortedEntries = new TreeSet<Map.Entry<String, ImportPackage>>(this.buildImportEntriesComparator()); - CollectionTools.addAll(sortedEntries, this.getRequiredImportEntries()); - return sortedEntries; - } - - protected Comparator<Map.Entry<String, ImportPackage>> buildImportEntriesComparator() { - return IMPORT_ENTRIES_COMPARATOR; - } - - protected static final Comparator<Map.Entry<String, ImportPackage>> IMPORT_ENTRIES_COMPARATOR = new ImportEntriesComparator(); - - protected static class ImportEntriesComparator - implements Comparator<Map.Entry<String, ImportPackage>>, Serializable - { - public int compare(Map.Entry<String, ImportPackage> e1, Map.Entry<String, ImportPackage> e2) { - Collator collator = Collator.getInstance(); - int pkg = collator.compare(e1.getValue().packageName, e2.getValue().packageName); - return (pkg == 0) ? collator.compare(e1.getKey(), e2.getKey()) : pkg; - } - } - - /** - * strip off any non-required imports (e.g. "java.lang.Object') - */ - protected Iterable<Map.Entry<String, ImportPackage>> getRequiredImportEntries() { - return new FilteringIterable<Map.Entry<String, ImportPackage>>(this.imports.entrySet(), this.buildRequiredImportEntriesFilter()); - } - - protected Filter<Map.Entry<String, ImportPackage>> buildRequiredImportEntriesFilter() { - return new RequiredImportEntriesFilter(); - } - - protected class RequiredImportEntriesFilter - implements Filter<Map.Entry<String, ImportPackage>> - { - public boolean accept(Map.Entry<String, ImportPackage> importEntry) { - return this.packageMustBeImported(importEntry.getValue()); - } - - protected boolean packageMustBeImported(ImportPackage importPackage) { - String pkg = importPackage.packageName; - if (pkg.equals("")) { - // cannot import a type from the "unnamed" package - return false; - } - if (pkg.equals("java.lang")) { - // we must import from 'java.lang' if we also have a class in the same package - return importPackage.collision; - } - if (pkg.equals(BodySourceWriter.this.packageName)) { - // we never need to import a class from the same package - return false; - } - return true; - } - } - - /** - * We need a 'collision' flag for when we encounter a class from - * 'java.lang' followed by a class from the current compilation unit's - * package. We will need to include the explicit import of the - * 'java.lang' class and all the references to the other class will - * have to be fully-qualified. - * - * If the classes are encountered in the opposite order (i.e. the class - * from the current compilation unit's package followed by the class - * from 'java.lang'), we do *not* need to import the first class while - * all the references to the 'java.lang' class will be fully-qualified. - * - * Unfortunately, we still have a problem: if we reference a class from - * 'java.lang' and there is a conflicting class from the current - * compilation unit's package (but that class is *not* revealed to us - * here), the simple name will be resolved to the non-'java.lang' class. - * Unless we simply force an import of *all* 'java.lang' classes.... :-( - * - * This shouldn't happen very often. :-) - */ - protected static class ImportPackage { - protected final String packageName; - protected boolean collision = false; - - protected ImportPackage(String packageName) { - super(); - this.packageName = packageName; - } - } - - } - -} diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/context/java/GenericJavaPersistentType2_0.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/context/java/GenericJavaPersistentType2_0.java index 62848905c9..46939ff675 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/context/java/GenericJavaPersistentType2_0.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jpa2/context/java/GenericJavaPersistentType2_0.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009 Oracle. All rights reserved. + * Copyright (c) 2009, 2010 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. @@ -9,18 +9,21 @@ ******************************************************************************/ package org.eclipse.jpt.core.internal.jpa2.context.java; +import java.util.Collection; import java.util.Iterator; +import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.jpt.core.context.AccessType; import org.eclipse.jpt.core.context.PersistentType; import org.eclipse.jpt.core.internal.context.java.AbstractJavaPersistentType; import org.eclipse.jpt.core.jpa2.JpaFactory2_0; -import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; import org.eclipse.jpt.core.jpa2.context.java.JavaPersistentType2_0; import org.eclipse.jpt.core.jpa2.resource.java.Access2_0Annotation; import org.eclipse.jpt.core.resource.java.JavaResourcePersistentAttribute; import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType; +import org.eclipse.jpt.core.utility.BodySourceWriter; /** * JPA 2.0 Java persistent type. @@ -30,15 +33,30 @@ public class GenericJavaPersistentType2_0 extends AbstractJavaPersistentType implements JavaPersistentType2_0 { - protected final PersistentType2_0.MetamodelSynchronizer metamodelSynchronizer; + protected String declaringTypeName; + + protected final MetamodelSourceType.Synchronizer metamodelSynchronizer; + public GenericJavaPersistentType2_0(PersistentType.Owner parent, JavaResourcePersistentType jrpt) { super(parent, jrpt); this.metamodelSynchronizer = this.buildMetamodelSynchronizer(); } - protected PersistentType2_0.MetamodelSynchronizer buildMetamodelSynchronizer() { - return ((JpaFactory2_0) this.getJpaFactory()).buildPersistentTypeMetamodelSynchronizer(this); + @Override + protected void initialize(JavaResourcePersistentType jrpt) { + super.initialize(jrpt); + this.declaringTypeName = this.buildDeclaringTypeName(); + } + + protected MetamodelSourceType.Synchronizer buildMetamodelSynchronizer() { + return ((JpaFactory2_0) this.getJpaFactory()).buildMetamodelSynchronizer(this); + } + + @Override + public void update() { + super.update(); + this.setDeclaringTypeName(this.buildDeclaringTypeName()); } @@ -84,6 +102,23 @@ public class GenericJavaPersistentType2_0 } + // ********** declaring type name ********** + + public String getDeclaringTypeName() { + return this.declaringTypeName; + } + + protected void setDeclaringTypeName(String declaringTypeName) { + String old = this.declaringTypeName; + this.declaringTypeName = declaringTypeName; + this.firePropertyChanged(DECLARING_TYPE_NAME_PROPERTY, old, declaringTypeName); + } + + protected String buildDeclaringTypeName() { + return this.resourcePersistentType.getDeclaringTypeName(); + } + + // ********** metamodel ********** public IFile getMetamodelFile() { @@ -94,10 +129,18 @@ public class GenericJavaPersistentType2_0 // do nothing - probably shouldn't be called... } - public void synchronizeMetamodel() { - this.metamodelSynchronizer.synchronize(); + public boolean isManaged() { + return true; + } + + public void synchronizeMetamodel(Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + this.metamodelSynchronizer.synchronize(memberTypeTree); } + public void printBodySourceOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree) { + this.metamodelSynchronizer.printBodySourceOn(pw, memberTypeTree); + } + public void disposeMetamodel() { // do nothing - probably shouldn't be called... } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/binary/BinaryPersistentType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/binary/BinaryPersistentType.java index cc21a6e52e..887d519751 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/binary/BinaryPersistentType.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/binary/BinaryPersistentType.java @@ -48,6 +48,8 @@ final class BinaryPersistentType private String superclassQualifiedName; + private String declaringTypeName; + private boolean abstract_; // 'abstract' is a reserved word private final Vector<JavaResourcePersistentAttribute> fields; @@ -64,6 +66,7 @@ final class BinaryPersistentType this.name = this.buildName(); this.qualifiedName = this.buildQualifiedName(); this.superclassQualifiedName = this.buildSuperclassQualifiedName(); + this.declaringTypeName = this.buildDeclaringTypeName(); this.abstract_ = this.buildAbstract(); this.fields = this.buildFields(); this.methods = this.buildMethods(); @@ -80,6 +83,7 @@ final class BinaryPersistentType this.setName(this.buildName()); this.setQualifiedName(this.buildQualifiedName()); this.setSuperclassQualifiedName(this.buildSuperclassQualifiedName()); + this.setDeclaringTypeName(this.buildDeclaringTypeName()); this.setAbstract(this.buildAbstract()); this.updateFields(); this.updateMethods(); @@ -177,6 +181,22 @@ final class BinaryPersistentType } } + // ***** declaring type name + public String getDeclaringTypeName() { + return this.declaringTypeName; + } + + private void setDeclaringTypeName(String declaringTypeName) { + String old = this.declaringTypeName; + this.declaringTypeName = declaringTypeName; + this.firePropertyChanged(DECLARING_TYPE_NAME_PROPERTY, old, declaringTypeName); + } + + private String buildDeclaringTypeName() { + IType declaringType = this.getMember().getDeclaringType(); + return (declaringType == null) ? null : declaringType.getFullyQualifiedName('.'); // no parameters are included here + } + // ***** abstract public boolean isAbstract() { return this.abstract_; @@ -544,11 +564,15 @@ final class BinaryPersistentType throw new UnsupportedOperationException(); } - public boolean isGeneratedMetamodel(IPackageFragmentRoot sourceFolder) { + public boolean isGeneratedMetamodelTopLevelType(IPackageFragmentRoot sourceFolder) { + throw new UnsupportedOperationException(); + } + + public boolean isGeneratedMetamodelTopLevelType() { throw new UnsupportedOperationException(); } - public boolean isGeneratedMetamodel() { + public boolean isMetamodel() { throw new UnsupportedOperationException(); } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentMember.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentMember.java index 87ac15f5e0..114613bdfd 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentMember.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentMember.java @@ -40,11 +40,11 @@ import org.eclipse.jpt.utility.internal.iterators.SingleElementIterator; /** * Java source persistent member (annotations, "persistable") */ -abstract class SourcePersistentMember<E extends Member> +abstract class SourcePersistentMember<M extends Member> extends SourceNode implements JavaResourcePersistentMember { - final E member; + final M member; /** * annotations; no duplicates (java compiler has an error for duplicates) @@ -56,7 +56,7 @@ abstract class SourcePersistentMember<E extends Member> // ********** construction/initialization ********** - SourcePersistentMember(JavaResourceNode parent, E member) { + SourcePersistentMember(JavaResourceNode parent, M member) { super(parent); this.member = member; } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentType.java index 219e12a1de..bb20e9b7c6 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentType.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/resource/java/source/SourcePersistentType.java @@ -62,6 +62,8 @@ final class SourcePersistentType private String superclassQualifiedName; + private String declaringTypeName; + private boolean abstract_; // 'abstract' is a reserved word private final Vector<JavaResourcePersistentType> types; @@ -134,6 +136,7 @@ final class SourcePersistentType this.name = this.buildName(astRoot); this.qualifiedName = this.buildQualifiedName(astRoot); this.superclassQualifiedName = this.buildSuperclassQualifiedName(astRoot); + this.declaringTypeName = this.buildDeclaringTypeName(astRoot); this.abstract_ = this.buildAbstract(astRoot); this.initializeTypes(astRoot); this.initializeFields(astRoot); @@ -173,6 +176,7 @@ final class SourcePersistentType this.syncName(this.buildName(astRoot)); this.syncQualifiedName(this.buildQualifiedName(astRoot)); this.syncSuperclassQualifiedName(this.buildSuperclassQualifiedName(astRoot)); + this.syncDeclaringTypeName(this.buildDeclaringTypeName(astRoot)); this.syncAbstract(this.buildAbstract(astRoot)); this.syncTypes(astRoot); this.syncFields(astRoot); @@ -309,6 +313,27 @@ final class SourcePersistentType return (superclass == null) ? null : superclass.getTypeDeclaration().getQualifiedName(); } + // ***** declaring type name + public String getDeclaringTypeName() { + return this.declaringTypeName; + } + + private void syncDeclaringTypeName(String astDeclaringTypeName) { + String old = this.declaringTypeName; + this.declaringTypeName = astDeclaringTypeName; + this.firePropertyChanged(DECLARING_TYPE_NAME_PROPERTY, old, astDeclaringTypeName); + } + + private String buildDeclaringTypeName(CompilationUnit astRoot) { + ITypeBinding binding = this.member.getBinding(astRoot); + if (binding == null) { + return null; + } + ITypeBinding declaringClass = binding.getDeclaringClass(); + return (declaringClass == null) ? null : declaringClass.getTypeDeclaration().getQualifiedName(); + } + + // ***** abstract public boolean isAbstract() { return this.abstract_; @@ -654,29 +679,50 @@ final class SourcePersistentType /** * The type must be:<ul> * <li>in the specified source folder - * <li>annotated with <code>@javax.persistence.metamodel.StaticMetamodel</code> - * <li>annotated with <code>@javax.annotation.Generated</code> with the appropriate - * <code>value</code> + * <li>a top-level type + * <li>annotated with <code>@javax.annotation.Generated</code> with + * the appropriate <code>value</code> and <code>date</code> + * <li>either itself or one of its nested types annotated with + * <code>@javax.persistence.metamodel.StaticMetamodel</code> * </ul> */ - public boolean isGeneratedMetamodel(IPackageFragmentRoot sourceFolder) { + public boolean isGeneratedMetamodelTopLevelType(IPackageFragmentRoot sourceFolder) { if ( ! this.getSourceFolder().equals(sourceFolder)) { return false; } - return this.isGeneratedMetamodel(); + return this.isGeneratedMetamodelTopLevelType(); } /** * The type must be:<ul> - * <li>annotated with <code>@javax.persistence.metamodel.StaticMetamodel</code> - * <li>annotated with <code>@javax.annotation.Generated</code> with the appropriate - * <code>value</code> and <code>date</code> + * <li>a top-level type + * <li>annotated with <code>@javax.annotation.Generated</code> with + * the appropriate <code>value</code> and <code>date</code> + * <li>either itself or one of its nested types annotated with + * <code>@javax.persistence.metamodel.StaticMetamodel</code> * </ul> */ - public boolean isGeneratedMetamodel() { - if (this.staticMetamodelAnnotation == null) { + public boolean isGeneratedMetamodelTopLevelType() { + if ( ! this.isGenerated()) { return false; } + // if we get here we know we have a top-level type, since only top-level + // types are annotated @Generated; now see if anything is a metamodel + for (Iterator<JavaResourcePersistentType> stream = this.allTypes(); stream.hasNext(); ) { + JavaResourcePersistentType2_0 type = (JavaResourcePersistentType2_0) stream.next(); + if (type.isMetamodel()) { + return true; + } + } + return false; + } + + /** + * The type must be annotated with + * <code>@javax.annotation.Generated</code> with the appropriate + * <code>value</code> and <code>date</code>. + */ + protected boolean isGenerated() { if (this.generatedAnnotation == null) { return false; } @@ -692,6 +738,14 @@ final class SourcePersistentType return true; } + /** + * The type must be annotated with + * <code>@javax.persistence.metamodel.StaticMetamodel</code>. + */ + public boolean isMetamodel() { + return this.staticMetamodelAnnotation != null; + } + private IPackageFragmentRoot getSourceFolder() { return (IPackageFragmentRoot) this.getJavaResourceCompilationUnit().getCompilationUnit().getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java index 26de333d42..f3438cfe8f 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 Oracle. All rights reserved. + * Copyright (c) 2005, 2010 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. @@ -98,10 +98,12 @@ public class JDTType */ public TypeDeclaration getBodyDeclaration(CompilationUnit astRoot) { Type declaringType = this.getDeclaringType(); - return (declaringType == null) ? - this.getTopLevelTypeDeclaration(astRoot) - : - this.getNestedTypeDeclaration(declaringType.getBodyDeclaration(astRoot)); + if (declaringType == null) { + return this.getTopLevelTypeDeclaration(astRoot); + } + TypeDeclaration typeDeclaration = declaringType.getBodyDeclaration(astRoot); + // the type declaration can be null when the source is completely hosed + return (typeDeclaration == null) ? null : this.getNestedTypeDeclaration(typeDeclaration); } public boolean isPersistable(CompilationUnit astRoot) { diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaFactory2_0.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaFactory2_0.java index 655659e95f..57bdc33923 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaFactory2_0.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaFactory2_0.java @@ -19,7 +19,7 @@ import org.eclipse.jpt.core.context.java.JavaColumn; import org.eclipse.jpt.core.context.java.JavaJpaContextNode; import org.eclipse.jpt.core.context.java.JavaNamedColumn; import org.eclipse.jpt.core.context.java.JavaPersistentAttribute; -import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheable2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheableHolder2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCollectionTable2_0; @@ -60,7 +60,7 @@ public interface JpaFactory2_0 */ DatabaseIdentifierAdapter buildDatabaseIdentifierAdapter(JpaDataSource dataSource); - PersistentType2_0.MetamodelSynchronizer buildPersistentTypeMetamodelSynchronizer(PersistentType2_0 persistentType); + MetamodelSourceType.Synchronizer buildMetamodelSynchronizer(MetamodelSourceType sourceType); // ********** Java Context Model ********** diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaProject2_0.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaProject2_0.java index a8a137c4ff..8137241489 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaProject2_0.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/JpaProject2_0.java @@ -64,23 +64,23 @@ public interface JpaProject2_0 /** * Return the JPA project's generated metamodel Java resource persistent - * types. + * top-level types. * @see org.eclipse.jpt.core.internal.resource.java.source.SourcePersistentType#isGeneratedMetamodel(IPackageFragmentRoot) */ - Iterable<JavaResourcePersistentType2_0> getGeneratedMetamodelTypes(); + Iterable<JavaResourcePersistentType2_0> getGeneratedMetamodelTopLevelTypes(); /** - * Return the generated metamodel Java resource persistent type in the - * specified file. Return null if any of the following is true:<ul> + * Return the top-level generated metamodel Java resource persistent type + * in the specified file. Return null if any of the following is true:<ul> * <li>the file is not a Java source file - * <li>the file does not contain the source for one and only one Java class - * <li>the Java class is not annotated with the appropriate - * <code>javax.persistence.metamodel.StaticMetamodel</code> annotation - * <li>the Java class is not annotated with the appropriate + * <li>the top-level Java class is not annotated with the appropriate * <code>javax.annotation.Generated</code> annotation + * <li>neither the top-level Java class nor any of its nested classes + * is annotated with the appropriate + * <code>javax.persistence.metamodel.StaticMetamodel</code> annotation * <ul> */ - JavaResourcePersistentType2_0 getGeneratedMetamodelType(IFile file); + JavaResourcePersistentType2_0 getGeneratedMetamodelTopLevelType(IFile file); // ********** construction config ********** diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/MetamodelSourceType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/MetamodelSourceType.java new file mode 100644 index 0000000000..f94469bec2 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/MetamodelSourceType.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2010 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.jpa2.context; + +import java.util.Collection; +import java.util.Comparator; +import java.util.ListIterator; +import java.util.Map; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jpt.core.JpaProject; +import org.eclipse.jpt.core.context.PersistentAttribute; +import org.eclipse.jpt.core.context.PersistentType; +import org.eclipse.jpt.core.utility.BodySourceWriter; + +import com.ibm.icu.text.Collator; + +/** + * JPA 2.0 metamodel source type. + * <p> + * Provisional API: This interface is part of an interim API that is still + * under development and expected to change significantly before reaching + * stability. It is available at this early stage to solicit feedback from + * pioneering adopters on the understanding that any code that uses this API + * will almost certainly be broken (repeatedly) as the API evolves. + * + * @version 2.3 + * @since 2.3 + */ +public interface MetamodelSourceType { + + /** + * Return the source type's name. + */ + String getName(); + + /** + * Return whether the source type is "managed" (i.e. persistent). + */ + boolean isManaged(); + + /** + * Return the source type's super type. + */ + PersistentType getSuperPersistentType(); + + /** + * Return the source type's attributes. + */ + <T extends PersistentAttribute> ListIterator<T> attributes(); + + /** + * Return the file generated as a result of the metamodel synchronization. + */ + IFile getMetamodelFile(); + + /** + * Return the source type's JPA project. + */ + JpaProject getJpaProject(); + + /** + * Synchronize the source type's metamodel, using the specified member type + * tree. + */ + void synchronizeMetamodel(Map<String, Collection<MetamodelSourceType>> memberTypeTree); + + /** + * Print the body of the source type's metamodel class on the specified + * writer, using the specified member type tree. + */ + void printBodySourceOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree); + + + /** + * {@link Comparator} that can be used to compare source types. + */ + Comparator<MetamodelSourceType> COMPARATOR = + new Comparator<MetamodelSourceType>() { + public int compare(MetamodelSourceType type1, MetamodelSourceType type2) { + return Collator.getInstance().compare(type1.getName(), type2.getName()); + } + }; + + + /** + * This interface is used by the source type to synchronize the metamodel + * as required by changes to the context model. + */ + interface Synchronizer { + /** + * Return the file generated as a result of the metamodel synchronization. + */ + IFile getFile(); + + /** + * Synchronize the metamodel with the current state of the source + * type, using the specified member type tree. + */ + void synchronize(Map<String, Collection<MetamodelSourceType>> memberTypeTree); + + /** + * Print the body of the metamodel class on the specified writer, + * using the specified member type tree. + */ + void printBodySourceOn(BodySourceWriter pw, Map<String, Collection<MetamodelSourceType>> memberTypeTree); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/PersistentType2_0.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/PersistentType2_0.java index f8df1e5988..6170a1bc7d 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/PersistentType2_0.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/context/PersistentType2_0.java @@ -9,9 +9,7 @@ ******************************************************************************/ package org.eclipse.jpt.core.jpa2.context; -import org.eclipse.core.resources.IFile; import org.eclipse.jpt.core.context.PersistentType; -import org.eclipse.jpt.core.jpa2.MetamodelSynchronizer; /** * JPA 2.0 context persistent type. @@ -26,28 +24,14 @@ import org.eclipse.jpt.core.jpa2.MetamodelSynchronizer; * @since 2.3 */ public interface PersistentType2_0 - extends PersistentType, MetamodelSynchronizer + extends PersistentType, MetamodelSourceType { /** - * Return the file generated as a result of the metamodel synchronization. + * Return the name of the persistent type's "declaring type". + * Return <code>null</code> if the persistent type is a top-level type. + * The declaring type may or may not be a persistent type. */ - IFile getMetamodelFile(); - - /** - * This interface is used by the persistent type to synchonize the metamodel - * as required by changes to the context model. - */ - interface MetamodelSynchronizer { - /** - * Return the file generated as a result of the metamodel synchronization. - */ - IFile getFile(); - - /** - * Synchronize the metamodel with the current state of the persistent - * type. - */ - void synchronize(); - } + String getDeclaringTypeName(); + String DECLARING_TYPE_NAME_PROPERTY = "declaringTypeName"; //$NON-NLS-1$ } diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/resource/java/JavaResourcePersistentType2_0.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/resource/java/JavaResourcePersistentType2_0.java index f463f335b6..b00ba0b4c8 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/resource/java/JavaResourcePersistentType2_0.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/jpa2/resource/java/JavaResourcePersistentType2_0.java @@ -36,15 +36,22 @@ public interface JavaResourcePersistentType2_0 GeneratedAnnotation getGeneratedAnnotation(); /** - * Return whether the type is a metamodel type generated in the specified - * source folder. + * Return whether the type is a metamodel top-level type generated in the + * specified source folder. */ - boolean isGeneratedMetamodel(IPackageFragmentRoot sourceFolder); + boolean isGeneratedMetamodelTopLevelType(IPackageFragmentRoot sourceFolder); /** - * Return whether the type is a generated metamodel type. + * Return whether the type is a generated metamodel top-level type. + * The type is generated and either it or one of its nested types is a + * metamodel type. */ - boolean isGeneratedMetamodel(); + boolean isGeneratedMetamodelTopLevelType(); + + /** + * Return whether the type is a metamodel type. + */ + boolean isMetamodel(); /** * The value used to tag a generated type: diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/resource/java/JavaResourcePersistentType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/resource/java/JavaResourcePersistentType.java index 31ebf6b6a0..14db8475e3 100644 --- a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/resource/java/JavaResourcePersistentType.java +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/resource/java/JavaResourcePersistentType.java @@ -45,6 +45,13 @@ public interface JavaResourcePersistentType String SUPERCLASS_QUALIFIED_NAME_PROPERTY = "superclassQualifiedName"; //$NON-NLS-1$ /** + * Return the name of the type's "declaring type". + * Return <code>null</code> if the type is a top-level type. + */ + String getDeclaringTypeName(); + String DECLARING_TYPE_NAME_PROPERTY = "declaringTypeName"; //$NON-NLS-1$ + + /** * Return whether the type is abstract. */ boolean isAbstract(); diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/utility/BodySourceWriter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/utility/BodySourceWriter.java new file mode 100644 index 0000000000..0be7b18053 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/utility/BodySourceWriter.java @@ -0,0 +1,303 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 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.utility; + +import java.io.Serializable; +import java.io.StringWriter; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeSet; + +import org.eclipse.jpt.utility.Filter; +import org.eclipse.jpt.utility.IndentingPrintWriter; +import org.eclipse.jpt.utility.internal.CollectionTools; +import org.eclipse.jpt.utility.internal.StringTools; +import org.eclipse.jpt.utility.internal.Transformer; +import org.eclipse.jpt.utility.internal.iterables.FilteringIterable; +import org.eclipse.jpt.utility.internal.iterables.TransformationIterable; + +import com.ibm.icu.text.Collator; + +/** + * Extend {@link IndentingPrintWriter} with some methods that facilitate + * building class source code. + */ +@SuppressWarnings("nls") +public class BodySourceWriter + extends IndentingPrintWriter +{ + protected final String packageName; + protected final String className; + // key = short class name; value = import package + protected final HashMap<String, ImportPackage> imports = new HashMap<String, ImportPackage>(); + + public BodySourceWriter(String packageName, String className) { + super(new StringWriter(2000)); + this.packageName = packageName; + this.className = className; + } + + public String getSource() { + return this.out.toString(); + } + + public int getLength() { + return ((StringWriter) this.out).getBuffer().length(); + } + + protected void printVisibility(String visibilityModifier) { + if (visibilityModifier.length() != 0) { + this.print(visibilityModifier); + this.print(' '); + } + } + + public void printAnnotation(String annotationName) { + this.print('@'); + this.printTypeDeclaration(annotationName); + } + + public void printTypeDeclaration(String typeDeclaration) { + this.print(this.buildImportedTypeDeclaration(typeDeclaration)); + } + + protected void printField(String fieldName, String typeDeclaration, String visibility) { + this.printVisibility(visibility); + this.printTypeDeclaration(typeDeclaration); + this.print(' '); + this.print(fieldName); + this.print(';'); + this.println(); + this.println(); + } + + protected void printParameterizedField(String fieldName, String typeDeclaration, String parameterTypeDeclaration, String visibility) { + this.printVisibility(visibility); + this.printTypeDeclaration(typeDeclaration); + this.print('<'); + this.printTypeDeclaration(parameterTypeDeclaration); + this.print('>'); + this.print(' '); + this.print(fieldName); + this.print(';'); + this.println(); + this.println(); + } + + /** + * Convert the specified string to a <em>String Literal</em> and print it, + * adding the surrounding double-quotes and escaping characters + * as necessary. + */ + public void printStringLiteral(String string) { + StringTools.convertToJavaStringLiteralOn(string, this); + } + + + // ********** imports ********** + + // ***** writing + /** + * Return the specified class's "imported" name. + * The class declaration must be of the form: + * "int" + * "int[]" (not "[I") + * "java.lang.Object" + * "java.lang.Object[]" (not "[Ljava.lang.Object;") + * "java.util.Map.Entry" (not "java.util.Map$Entry") + * "java.util.Map.Entry[][]" (not "[[Ljava.util.Map$Entry;") + * + * To really do this right, we would need to gather all the types from + * the "unamed" (default) package that were referenced in the + * compilation unit beforehand. *Any* collisions with one of these + * types would have to be fully qualified (whether it was from + * 'java.lang' or the same package as the current compilation unit). + * In other words, if we have any types from the "unnamed" package, + * results are unpredictable.... + */ + protected String buildImportedTypeDeclaration(String typeDeclaration) { + if (this.typeDeclarationIsMemberClass(typeDeclaration)) { + // no need for an import, just return the partially-qualified name + return this.buildMemberClassTypeDeclaration(typeDeclaration); + } + int last = typeDeclaration.lastIndexOf('.'); + String currentPackageName = (last == -1) ? "" : typeDeclaration.substring(0, last); + String shortTypeDeclaration = typeDeclaration.substring(last + 1); + String shortElementTypeName = shortTypeDeclaration; + while (shortElementTypeName.endsWith("[]")) { + shortElementTypeName = shortElementTypeName.substring(0, shortElementTypeName.length() - 2); + } + ImportPackage prev = this.imports.get(shortElementTypeName); + if (prev == null) { + // this is the first class with this short element type name + this.imports.put(shortElementTypeName, new ImportPackage(currentPackageName)); + return shortTypeDeclaration; + } + if (prev.packageName.equals(currentPackageName)) { + // this element type has already been imported + return shortTypeDeclaration; + } + if (currentPackageName.equals(this.packageName) && + prev.packageName.equals("java.lang")) { + // we force the 'java.lang' class to be explicitly imported + prev.collision = true; + } + // another class with the same short element type name has been + // previously imported, so this one must be used fully-qualified + return typeDeclaration; + } + + /** + * e.g. "foo.bar.Employee.PK" will return true + */ + protected boolean typeDeclarationIsMemberClass(String typeDeclaration) { + return (typeDeclaration.length() > this.className.length()) + && typeDeclaration.startsWith(this.className) + && (typeDeclaration.charAt(this.className.length()) == '.'); + } + + /** + * e.g. "foo.bar.Employee.PK" will return "Employee.PK" + * this prevents collisions with other imported classes (e.g. "joo.jar.PK") + */ + protected String buildMemberClassTypeDeclaration(String typeDeclaration) { + int index = this.packageName.length(); + if (index != 0) { + index++; // bump past the '.' + } + return typeDeclaration.substring(index); + } + + // ***** reading + public Iterable<String> getImports() { + return this.getSortedRequiredImports(); + } + + /** + * transform our map entries to class names + */ + protected Iterable<String> getSortedRequiredImports() { + return new TransformationIterable<Map.Entry<String, ImportPackage>, String>(this.getSortedRequiredImportEntries(), this.buildImportEntriesTransformer()); + } + + protected Transformer<Map.Entry<String, ImportPackage>, String> buildImportEntriesTransformer() { + return IMPORT_ENTRIES_TRANSFORMER; + } + + protected static final Transformer<Map.Entry<String, ImportPackage>, String> IMPORT_ENTRIES_TRANSFORMER = new ImportEntriesTransformer(); + + protected static class ImportEntriesTransformer + implements Transformer<Map.Entry<String, ImportPackage>, String> + { + public String transform(Entry<String, ImportPackage> importEntry) { + String pkg = importEntry.getValue().packageName; + String type = importEntry.getKey(); + StringBuilder sb = new StringBuilder(pkg.length() + 1 + type.length()); + sb.append(pkg); + sb.append('.'); + sb.append(type); + return sb.toString(); + } + } + + /** + * sort by package first, then class (*not* by fully-qualified class name) + */ + protected Iterable<Map.Entry<String, ImportPackage>> getSortedRequiredImportEntries() { + TreeSet<Map.Entry<String, ImportPackage>> sortedEntries = new TreeSet<Map.Entry<String, ImportPackage>>(this.buildImportEntriesComparator()); + CollectionTools.addAll(sortedEntries, this.getRequiredImportEntries()); + return sortedEntries; + } + + protected Comparator<Map.Entry<String, ImportPackage>> buildImportEntriesComparator() { + return IMPORT_ENTRIES_COMPARATOR; + } + + protected static final Comparator<Map.Entry<String, ImportPackage>> IMPORT_ENTRIES_COMPARATOR = new ImportEntriesComparator(); + + protected static class ImportEntriesComparator + implements Comparator<Map.Entry<String, ImportPackage>>, Serializable + { + public int compare(Map.Entry<String, ImportPackage> e1, Map.Entry<String, ImportPackage> e2) { + Collator collator = Collator.getInstance(); + int pkg = collator.compare(e1.getValue().packageName, e2.getValue().packageName); + return (pkg == 0) ? collator.compare(e1.getKey(), e2.getKey()) : pkg; + } + } + + /** + * strip off any non-required imports (e.g. "java.lang.Object') + */ + protected Iterable<Map.Entry<String, ImportPackage>> getRequiredImportEntries() { + return new FilteringIterable<Map.Entry<String, ImportPackage>>(this.imports.entrySet(), this.buildRequiredImportEntriesFilter()); + } + + protected Filter<Map.Entry<String, ImportPackage>> buildRequiredImportEntriesFilter() { + return new RequiredImportEntriesFilter(); + } + + protected class RequiredImportEntriesFilter + implements Filter<Map.Entry<String, ImportPackage>> + { + public boolean accept(Map.Entry<String, ImportPackage> importEntry) { + return this.packageMustBeImported(importEntry.getValue()); + } + + protected boolean packageMustBeImported(ImportPackage importPackage) { + String pkg = importPackage.packageName; + if (pkg.equals("")) { + // cannot import a type from the "unnamed" package + return false; + } + if (pkg.equals("java.lang")) { + // we must import from 'java.lang' if we also have a class in the same package + return importPackage.collision; + } + if (pkg.equals(BodySourceWriter.this.packageName)) { + // we never need to import a class from the same package + return false; + } + return true; + } + } + + /** + * We need a 'collision' flag for when we encounter a class from + * 'java.lang' followed by a class from the current compilation unit's + * package. We will need to include the explicit import of the + * 'java.lang' class and all the references to the other class will + * have to be fully-qualified. + * + * If the classes are encountered in the opposite order (i.e. the class + * from the current compilation unit's package followed by the class + * from 'java.lang'), we do *not* need to import the first class while + * all the references to the 'java.lang' class will be fully-qualified. + * + * Unfortunately, we still have a problem: if we reference a class from + * 'java.lang' and there is a conflicting class from the current + * compilation unit's package (but that class is *not* revealed to us + * here), the simple name will be resolved to the non-'java.lang' class. + * Unless we simply force an import of *all* 'java.lang' classes.... :-( + * + * This shouldn't happen very often. :-) + */ + protected static class ImportPackage { + protected final String packageName; + protected boolean collision = false; + + protected ImportPackage(String packageName) { + super(); + this.packageName = packageName; + } + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.eclipselink.core/src/org/eclipse/jpt/eclipselink/core/internal/v2_0/EclipseLink2_0JpaFactory.java b/jpa/plugins/org.eclipse.jpt.eclipselink.core/src/org/eclipse/jpt/eclipselink/core/internal/v2_0/EclipseLink2_0JpaFactory.java index 06bf6b944a..023a1a807e 100644 --- a/jpa/plugins/org.eclipse.jpt.eclipselink.core/src/org/eclipse/jpt/eclipselink/core/internal/v2_0/EclipseLink2_0JpaFactory.java +++ b/jpa/plugins/org.eclipse.jpt.eclipselink.core/src/org/eclipse/jpt/eclipselink/core/internal/v2_0/EclipseLink2_0JpaFactory.java @@ -27,7 +27,7 @@ import org.eclipse.jpt.core.context.java.JavaSequenceGenerator; import org.eclipse.jpt.core.context.java.JavaBaseColumn.Owner; import org.eclipse.jpt.core.internal.jpa1.context.java.GenericJavaAssociationOverrideContainer; import org.eclipse.jpt.core.internal.jpa1.context.java.GenericJavaColumn; -import org.eclipse.jpt.core.internal.jpa2.GenericPersistentTypeMetamodelSynchronizer; +import org.eclipse.jpt.core.internal.jpa2.GenericMetamodelSynchronizer; import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaAssociationOverrideRelationshipReference2_0; import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaCacheable2_0; import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaCollectionTable2_0; @@ -40,6 +40,7 @@ import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaPersistentType import org.eclipse.jpt.core.internal.jpa2.context.java.GenericJavaSequenceGenerator2_0; import org.eclipse.jpt.core.internal.jpa2.context.java.VirtualAssociationOverride2_0Annotation; import org.eclipse.jpt.core.jpa2.JpaProject2_0; +import org.eclipse.jpt.core.jpa2.context.MetamodelSourceType; import org.eclipse.jpt.core.jpa2.context.PersistentType2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheable2_0; import org.eclipse.jpt.core.jpa2.context.java.JavaCacheableHolder2_0; @@ -81,8 +82,8 @@ public class EclipseLink2_0JpaFactory } @Override - public PersistentType2_0.MetamodelSynchronizer buildPersistentTypeMetamodelSynchronizer(PersistentType2_0 persistentType) { - return new GenericPersistentTypeMetamodelSynchronizer(persistentType); + public MetamodelSourceType.Synchronizer buildMetamodelSynchronizer(MetamodelSourceType sourceType) { + return new GenericMetamodelSynchronizer(sourceType); } // ********** Java Context Model ********** diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/IndentingPrintWriter.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/IndentingPrintWriter.java index a712c7ac62..35fa70a79b 100644 --- a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/IndentingPrintWriter.java +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/IndentingPrintWriter.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 Oracle. All rights reserved. + * Copyright (c) 2005, 2010 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. @@ -7,7 +7,7 @@ * Contributors: * Oracle - initial API and implementation ******************************************************************************/ -package org.eclipse.jpt.utility.internal; +package org.eclipse.jpt.utility; import java.io.PrintWriter; import java.io.Writer; @@ -15,8 +15,9 @@ import java.io.Writer; /** * Extend {@link PrintWriter} to automatically indent new lines. */ -public class IndentingPrintWriter extends PrintWriter { - +public class IndentingPrintWriter + extends PrintWriter +{ private final String indent; private int indentLevel; private boolean needsIndent; @@ -141,10 +142,13 @@ public class IndentingPrintWriter extends PrintWriter { /** * Allow the indent level to be set directly. + * Return the previous indent level. */ - public void setIndentLevel(int indentLevel) { + public int setIndentLevel(int indentLevel) { synchronized (this.lock) { + int old = this.indentLevel; this.indentLevel = indentLevel; + return old; } } diff --git a/jpa/tests/org.eclipse.jpt.db.tests/src/org/eclipse/jpt/db/tests/internal/platforms/DTPPlatformTests.java b/jpa/tests/org.eclipse.jpt.db.tests/src/org/eclipse/jpt/db/tests/internal/platforms/DTPPlatformTests.java index 93a577956c..7c3ecf1fc2 100644 --- a/jpa/tests/org.eclipse.jpt.db.tests/src/org/eclipse/jpt/db/tests/internal/platforms/DTPPlatformTests.java +++ b/jpa/tests/org.eclipse.jpt.db.tests/src/org/eclipse/jpt/db/tests/internal/platforms/DTPPlatformTests.java @@ -57,8 +57,8 @@ import org.eclipse.jpt.db.Sequence; import org.eclipse.jpt.db.Table; import org.eclipse.jpt.db.ForeignKey.ColumnPair; import org.eclipse.jpt.db.tests.internal.JptDbTestsPlugin; +import org.eclipse.jpt.utility.IndentingPrintWriter; import org.eclipse.jpt.utility.internal.ReflectionTools; -import org.eclipse.jpt.utility.internal.IndentingPrintWriter; import org.eclipse.jpt.utility.internal.StringTools; import org.eclipse.jpt.utility.internal.iterators.ResultSetIterator; diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/ClassNameTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/ClassNameTests.java index 5588858142..3b31f37e9a 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/ClassNameTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/ClassNameTests.java @@ -69,6 +69,9 @@ public class ClassNameTests extends TestCase { assertEquals("Object[]", ClassName.getSimpleName(java.lang.Object[].class.getName())); assertEquals("Object[][]", ClassName.getSimpleName(java.lang.Object[][].class.getName())); + assertEquals(java.util.Map.class.getSimpleName(), ClassName.getSimpleName(java.util.Map.class.getName())); + assertEquals(java.util.Map.Entry.class.getSimpleName(), ClassName.getSimpleName(java.util.Map.Entry.class.getName())); + assertEquals("int", ClassName.getSimpleName(int.class.getName())); assertEquals("int[]", ClassName.getSimpleName(int[].class.getName())); assertEquals("int[][]", ClassName.getSimpleName(int[][].class.getName())); @@ -86,10 +89,13 @@ public class ClassNameTests extends TestCase { } public void testGetPackageName() throws Exception { - assertEquals("java.lang", ClassName.getPackageName(java.lang.Object.class.getName())); + assertEquals(java.lang.Object.class.getPackage().getName(), ClassName.getPackageName(java.lang.Object.class.getName())); assertEquals("", ClassName.getPackageName(java.lang.Object[].class.getName())); assertEquals("", ClassName.getPackageName(java.lang.Object[][].class.getName())); + assertEquals(java.util.Map.class.getPackage().getName(), ClassName.getPackageName(java.util.Map.class.getName())); + assertEquals(java.util.Map.Entry.class.getPackage().getName(), ClassName.getPackageName(java.util.Map.Entry.class.getName())); + assertEquals("", ClassName.getPackageName(int.class.getName())); assertEquals("", ClassName.getPackageName(int[].class.getName())); assertEquals("", ClassName.getPackageName(int[][].class.getName())); diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/IndentingPrintWriterTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/IndentingPrintWriterTests.java index f91064f4a6..cb19b1c640 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/IndentingPrintWriterTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/IndentingPrintWriterTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2009 Oracle. All rights reserved. + * Copyright (c) 2005, 2010 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. @@ -11,7 +11,8 @@ package org.eclipse.jpt.utility.tests.internal; import java.io.StringWriter; import junit.framework.TestCase; -import org.eclipse.jpt.utility.internal.IndentingPrintWriter; + +import org.eclipse.jpt.utility.IndentingPrintWriter; @SuppressWarnings("nls") public class IndentingPrintWriterTests extends TestCase { diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/swing/TreeModelAdapterTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/swing/TreeModelAdapterTests.java index 257096ce20..760d17b36c 100644 --- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/swing/TreeModelAdapterTests.java +++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/swing/TreeModelAdapterTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2009 Oracle. All rights reserved. + * Copyright (c) 2007, 2010 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. @@ -24,8 +24,8 @@ import javax.swing.tree.TreeModel; import junit.framework.TestCase; +import org.eclipse.jpt.utility.IndentingPrintWriter; import org.eclipse.jpt.utility.internal.HashBag; -import org.eclipse.jpt.utility.internal.IndentingPrintWriter; import org.eclipse.jpt.utility.internal.StringTools; import org.eclipse.jpt.utility.internal.iterators.ReadOnlyIterator; import org.eclipse.jpt.utility.internal.model.AbstractModel; |