blob: 5b61923364eab50abd38b4c470b6fbf2474a053b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 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.jaxb.core.internal.context;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jpt.common.utility.internal.ClassName;
import org.eclipse.jpt.common.utility.internal.CollectionTools;
import org.eclipse.jpt.common.utility.internal.StringTools;
import org.eclipse.jpt.common.utility.internal.iterables.FilteringIterable;
import org.eclipse.jpt.common.utility.internal.iterables.LiveCloneIterable;
import org.eclipse.jpt.common.utility.internal.iterables.SnapshotCloneIterable;
import org.eclipse.jpt.common.utility.internal.iterables.SubIterableWrapper;
import org.eclipse.jpt.common.utility.internal.iterables.TransformationIterable;
import org.eclipse.jpt.jaxb.core.JaxbProject;
import org.eclipse.jpt.jaxb.core.context.JaxbClass;
import org.eclipse.jpt.jaxb.core.context.JaxbContextRoot;
import org.eclipse.jpt.jaxb.core.context.JaxbPackage;
import org.eclipse.jpt.jaxb.core.context.JaxbPersistentClass;
import org.eclipse.jpt.jaxb.core.context.JaxbPersistentEnum;
import org.eclipse.jpt.jaxb.core.context.JaxbRegistry;
import org.eclipse.jpt.jaxb.core.context.JaxbTransientClass;
import org.eclipse.jpt.jaxb.core.context.JaxbType;
import org.eclipse.jpt.jaxb.core.resource.java.JAXB;
import org.eclipse.jpt.jaxb.core.resource.java.JavaResourceAbstractType;
import org.eclipse.jpt.jaxb.core.resource.java.JavaResourceEnum;
import org.eclipse.jpt.jaxb.core.resource.java.JavaResourcePackage;
import org.eclipse.jpt.jaxb.core.resource.java.JavaResourceType;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
/**
* the context model root
*/
public class GenericContextRoot
extends AbstractJaxbContextNode
implements JaxbContextRoot {
/* This object has no parent, so it must point to the JAXB project explicitly. */
protected final JaxbProject jaxbProject;
/* The map of package name to JaxbPackage objects */
protected final Map<String, JaxbPackage> packages;
/* The map of class name to JaxbType objects */
protected final Map<String, JaxbType> types;
public GenericContextRoot(JaxbProject jaxbProject) {
super(null); // the JAXB project is not really a "parent"...
if (jaxbProject == null) {
throw new NullPointerException();
}
this.jaxbProject = jaxbProject;
this.packages = new HashMap<String, JaxbPackage>();
this.types = new HashMap<String, JaxbType>();
initialize();
}
@Override
public JaxbContextRoot getContextRoot() {
return this;
}
@Override
protected boolean requiresParent() {
return false;
}
protected void initialize() {
// keep a master list of all types that we've processed so we don't process them again
final Set<String> totalTypes = CollectionTools.<String>set();
// keep an running list of types that we need to scan for further referenced types
final Set<String> typesToScan = CollectionTools.<String>set();
// process packages with annotations first
for (String pkg : calculateInitialPackageNames()) {
this.packages.put(pkg, buildPackage(pkg));
}
// process registry classes before other classes (they are completely determined by annotation)
for (JavaResourceType registryResourceType : calculateRegistries()) {
String className = registryResourceType.getQualifiedName();
totalTypes.add(className);
typesToScan.add(className);
this.types.put(registryResourceType.getName(), buildRegistry(registryResourceType));
}
// calculate initial set of persistent types (annotated with @XmlType)
final Set<JavaResourceAbstractType> resourceTypesToProcess = calculateInitialPersistentTypes();
// while there are resource types to process or types to scan, continue to do so
while (! resourceTypesToProcess.isEmpty() || ! typesToScan.isEmpty()) {
for (JavaResourceAbstractType resourceType : new SnapshotCloneIterable<JavaResourceAbstractType>(resourceTypesToProcess)) {
String className = resourceType.getQualifiedName();
totalTypes.add(className);
typesToScan.add(className);
JaxbType.Kind jaxbTypeKind = calculateJaxbTypeKind(resourceType);
this.types.put(resourceType.getName(), buildType(jaxbTypeKind, resourceType));
resourceTypesToProcess.remove(resourceType);
}
for (String typeToScan : new SnapshotCloneIterable<String>(typesToScan)) {
JaxbType jaxbType = getType(typeToScan);
if (jaxbType != null) {
for (String referencedTypeName : jaxbType.getDirectlyReferencedTypeNames()) {
if (! totalTypes.contains(referencedTypeName)) {
JavaResourceAbstractType referencedType = getJaxbProject().getJavaResourceType(referencedTypeName);
if (referencedType != null) {
resourceTypesToProcess.add(referencedType);
}
}
}
}
typesToScan.remove(typeToScan);
}
}
// once all classes have been processed, add packages
for (String pkg : calculatePackageNames(totalTypes)) {
if (! this.packages.containsKey(pkg)) {
this.packages.put(pkg, buildPackage(pkg));
}
}
}
@Override
public void synchronizeWithResourceModel() {
super.synchronizeWithResourceModel();
for (JaxbPackage each : getPackages()) {
each.synchronizeWithResourceModel();
}
for (JaxbType each : getTypes()) {
each.synchronizeWithResourceModel();
}
}
@Override
public void update() {
super.update();
// keep a master list of these so that objects are updated only once
final Set<String> packagesToUpdate = CollectionTools.<String>set();
final Set<String> typesToUpdate = CollectionTools.<String>set();
// keep a (shrinking) running list of these so that we know which ones we do eventually need to remove
final Set<String> packagesToRemove = CollectionTools.set(this.packages.keySet());
final Set<String> typesToRemove = CollectionTools.set(this.types.keySet());
// keep a master list of all types that we've processed so we don't process them again
final Set<String> totalTypes = CollectionTools.<String>set();
// keep an running list of types that we need to scan for further referenced types
final Set<String> typesToScan = CollectionTools.<String>set();
// process packages with annotations first
for (String pkg : calculateInitialPackageNames()) {
if (this.packages.containsKey(pkg)) {
packagesToUpdate.add(pkg);
packagesToRemove.remove(pkg);
}
else {
this.addPackage(this.buildPackage(pkg));
}
}
// process registry classes before other classes (they are completely determined by annotation)
for (JavaResourceType registryResourceType : calculateRegistries()) {
String className = registryResourceType.getQualifiedName();
typesToRemove.remove(className);
totalTypes.add(className);
typesToScan.add(className);
if (this.types.containsKey(className)) {
if (this.types.get(className).getKind() == JaxbType.Kind.REGISTRY) {
typesToUpdate.add(className);
}
else {
this.removeType(className); // this will remove a type of another kind
this.addType(buildRegistry(registryResourceType));
}
}
else {
this.addType(buildRegistry(registryResourceType));
}
}
// calculate initial set of persistent types (annotated with @XmlType)
final Set<JavaResourceAbstractType> resourceTypesToProcess = calculateInitialPersistentTypes();
// while there are resource types to process or types to scan, continue to do so
while (! resourceTypesToProcess.isEmpty() || ! typesToScan.isEmpty()) {
for (JavaResourceAbstractType resourceType : new SnapshotCloneIterable<JavaResourceAbstractType>(resourceTypesToProcess)) {
String className = resourceType.getQualifiedName();
typesToRemove.remove(className);
totalTypes.add(className);
typesToScan.add(className);
processType(resourceType, typesToUpdate);
resourceTypesToProcess.remove(resourceType);
}
for (String typeToScan : new SnapshotCloneIterable<String>(typesToScan)) {
JaxbType jaxbType = getType(typeToScan);
if (jaxbType != null) {
for (String referencedTypeName : jaxbType.getDirectlyReferencedTypeNames()) {
if (! StringTools.stringIsEmpty(referencedTypeName) && ! totalTypes.contains(referencedTypeName)) {
JavaResourceAbstractType referencedType = getJaxbProject().getJavaResourceType(referencedTypeName);
if (referencedType != null) {
resourceTypesToProcess.add(referencedType);
}
}
}
}
typesToScan.remove(typeToScan);
}
}
// once all classes have been processed, add packages
for (String pkg : calculatePackageNames(totalTypes)) {
if (this.packages.containsKey(pkg)) {
packagesToUpdate.add(pkg);
packagesToRemove.remove(pkg);
}
else {
this.addPackage(this.buildPackage(pkg));
}
}
for (String packageToUpdate : packagesToUpdate) {
this.packages.get(packageToUpdate).update();
}
for (String typeToUpdate : typesToUpdate) {
this.types.get(typeToUpdate).update();
}
for (String packageToRemove : packagesToRemove) {
removePackage(packageToRemove);
}
for (String typeToRemove : typesToRemove) {
removeType(typeToRemove);
}
}
/**
* calculate set of packages that can be determined purely by presence of package annotations
*/
protected Set<String> calculateInitialPackageNames() {
return CollectionTools.set(
new TransformationIterable<JavaResourcePackage, String>(
getJaxbProject().getAnnotatedJavaResourcePackages()) {
@Override
protected String transform(JavaResourcePackage o) {
return o.getName();
}
});
}
/**
* calculate set of packages that can be determined from type names
*/
protected Set<String> calculatePackageNames(Set<String> typeNames) {
Set<String> packageNames = CollectionTools.<String>set();
for (String typeName : typeNames) {
packageNames.add(ClassName.getPackageName(typeName));
}
return packageNames;
}
/*
* Calculate set of registries
* (this should be all resource types with the @XmlRegistry annotation)
*/
protected Set<JavaResourceType> calculateRegistries() {
return CollectionTools.set(
new SubIterableWrapper<JavaResourceAbstractType, JavaResourceType>(
new FilteringIterable<JavaResourceAbstractType>(
getJaxbProject().getJavaSourceResourceTypes()) {
@Override
protected boolean accept(JavaResourceAbstractType o) {
return o.getKind() == JavaResourceAbstractType.Kind.TYPE
&& o.getAnnotation(JAXB.XML_REGISTRY) != null;
}
}));
}
/*
* Calculate set of resource types annotated with @XmlType (and not @XmlRegistry)
*/
protected Set<JavaResourceAbstractType> calculateInitialPersistentTypes() {
return CollectionTools.set(
new FilteringIterable<JavaResourceAbstractType>(getJaxbProject().getJavaSourceResourceTypes()) {
@Override
protected boolean accept(JavaResourceAbstractType o) {
return o.getAnnotation(JAXB.XML_TYPE) != null && o.getAnnotation(JAXB.XML_REGISTRY) == null;
}
});
}
protected void processType(JavaResourceAbstractType resourceType, Set<String> typesToUpdate) {
JaxbType.Kind jaxbTypeKind = calculateJaxbTypeKind(resourceType);
String className = resourceType.getQualifiedName();
if (this.types.containsKey(className)) {
if (this.types.get(className).getKind() == jaxbTypeKind) {
typesToUpdate.add(className);
return;
}
else {
this.removeType(className); // this will remove a type of another kind
}
}
this.addType(buildType(jaxbTypeKind, resourceType));
}
protected JaxbType.Kind calculateJaxbTypeKind(JavaResourceAbstractType resourceType) {
if (resourceType.getKind() == JavaResourceAbstractType.Kind.ENUM) {
return JaxbType.Kind.PERSISTENT_ENUM;
}
// else is of kind TYPE
else if (resourceType.getAnnotation(JAXB.XML_REGISTRY) != null) {
return JaxbType.Kind.REGISTRY;
}
else if (resourceType.getAnnotation(JAXB.XML_TRANSIENT) != null) {
return JaxbType.Kind.TRANSIENT;
}
else {
return JaxbType.Kind.PERSISTENT_CLASS;
}
}
protected JaxbType buildType(JaxbType.Kind jaxbTypeKind, JavaResourceAbstractType resourceType) {
if (jaxbTypeKind == JaxbType.Kind.PERSISTENT_ENUM) {
return buildPersistentEnum((JavaResourceEnum) resourceType);
}
else if (jaxbTypeKind == JaxbType.Kind.REGISTRY) {
return buildRegistry((JavaResourceType) resourceType);
}
else if (jaxbTypeKind == JaxbType.Kind.TRANSIENT) {
return buildTransientClass((JavaResourceType) resourceType);
}
else {
return buildPersistentClass((JavaResourceType) resourceType);
}
}
// ********** AbstractJaxbNode overrides **********
@Override
public JaxbProject getJaxbProject() {
return this.jaxbProject;
}
@Override
public IResource getResource() {
return this.getProject();
}
protected IProject getProject() {
return this.jaxbProject.getProject();
}
// ************* packages ***************
public Iterable<JaxbPackage> getPackages() {
return new LiveCloneIterable<JaxbPackage>(this.packages.values());
}
public int getPackagesSize() {
return this.packages.size();
}
public JaxbPackage getPackage(String packageName) {
for (JaxbPackage jaxbPackage : this.getPackages()) {
if (StringTools.stringsAreEqual(jaxbPackage.getName(), packageName)) {
return jaxbPackage;
}
}
return null;
}
protected JaxbPackage addPackage(JaxbPackage contextPackage) {
if (this.packages.containsKey(contextPackage.getName())) {
throw new IllegalArgumentException("Package with that name already exists."); //$NON-NLS-1$
}
this.packages.put(contextPackage.getName(), contextPackage);
fireItemAdded(PACKAGES_COLLECTION, contextPackage);
return contextPackage;
}
protected void removePackage(JaxbPackage contextPackage) {
this.removePackage(contextPackage.getName());
}
protected void removePackage(String packageName) {
if (! this.packages.containsKey(packageName)) {
throw new IllegalArgumentException("No package with that name exists."); //$NON-NLS-1$
}
JaxbPackage removedPackage = this.packages.remove(packageName);
fireItemRemoved(PACKAGES_COLLECTION, removedPackage);
}
protected JaxbPackage buildPackage(String packageName) {
return this.getFactory().buildPackage(this, packageName);
}
protected boolean isEmpty(JaxbPackage jaxbPackage) {
return jaxbPackage.isEmpty();
}
// ********** types ***********
public Iterable<JaxbType> getTypes() {
return new LiveCloneIterable<JaxbType>(this.types.values());
}
public int getTypesSize() {
return this.types.size();
}
public JaxbType getType(String typeName) {
return this.types.get(typeName);
}
protected void addType(JaxbType type) {
if (this.types.containsKey(type.getFullyQualifiedName())) {
throw new IllegalArgumentException("Type with that name already exists."); //$NON-NLS-1$
}
this.types.put(type.getFullyQualifiedName(), type);
fireItemAdded(TYPES_COLLECTION, type);
}
protected void removeType(JaxbType type) {
removeType(type.getFullyQualifiedName());
}
protected void removeType(String typeName) {
if (! this.types.containsKey(typeName)) {
throw new IllegalArgumentException("No type with that name exists."); //$NON-NLS-1$
}
JaxbType removedType = this.types.remove(typeName);
fireItemRemoved(TYPES_COLLECTION, removedType);
}
public Iterable<JaxbType> getTypes(final JaxbPackage jaxbPackage) {
return new FilteringIterable<JaxbType>(getTypes()) {
@Override
protected boolean accept(JaxbType o) {
return o.getPackageName().equals(jaxbPackage.getName());
}
};
}
// ********** registries **********
public Iterable<JaxbRegistry> getRegistries() {
return new SubIterableWrapper<JaxbType, JaxbRegistry>(
new FilteringIterable<JaxbType>(getTypes()) {
@Override
protected boolean accept(JaxbType o) {
return o.getKind() == JaxbType.Kind.REGISTRY;
}
});
}
protected JaxbRegistry buildRegistry(JavaResourceType resourceType) {
return this.getFactory().buildRegistry(this, resourceType);
}
public Iterable<JaxbRegistry> getRegistries(final JaxbPackage jaxbPackage) {
return new FilteringIterable<JaxbRegistry>(getRegistries()) {
@Override
protected boolean accept(JaxbRegistry o) {
return o.getPackageName().equals(jaxbPackage.getName());
}
};
}
// ********** transient types **********
public Iterable<JaxbTransientClass> getTransientClasses() {
return new SubIterableWrapper<JaxbType, JaxbTransientClass>(
new FilteringIterable<JaxbType>(getTypes()) {
@Override
protected boolean accept(JaxbType o) {
return o.getKind() == JaxbType.Kind.TRANSIENT;
}
});
}
protected JaxbTransientClass buildTransientClass(JavaResourceType resourceType) {
return this.getFactory().buildJavaTransientClass(this, resourceType);
}
public Iterable<JaxbTransientClass> getTransientClasses(final JaxbPackage jaxbPackage) {
return new FilteringIterable<JaxbTransientClass>(getTransientClasses()) {
@Override
protected boolean accept(JaxbTransientClass o) {
return o.getPackageName().equals(jaxbPackage.getName());
}
};
}
public JaxbTransientClass getTransientClass(String className) {
for (JaxbTransientClass jaxbClass : this.getTransientClasses()) {
if (StringTools.stringsAreEqual(jaxbClass.getFullyQualifiedName(), className)) {
return jaxbClass;
}
}
return null;
}
// ********** persistent classes **********
public Iterable<JaxbPersistentClass> getPersistentClasses() {
return new SubIterableWrapper<JaxbType, JaxbPersistentClass>(
new FilteringIterable<JaxbType>(getTypes()) {
@Override
protected boolean accept(JaxbType o) {
return o.getKind() == JaxbType.Kind.PERSISTENT_CLASS;
}
});
}
protected JaxbPersistentClass buildPersistentClass(JavaResourceType resourceType) {
return this.getFactory().buildJavaPersistentClass(this, resourceType);
}
public Iterable<JaxbPersistentClass> getPersistentClasses(final JaxbPackage jaxbPackage) {
return new FilteringIterable<JaxbPersistentClass>(getPersistentClasses()) {
@Override
protected boolean accept(JaxbPersistentClass o) {
return o.getPackageName().equals(jaxbPackage.getName());
}
};
}
public JaxbPersistentClass getPersistentClass(String className) {
for (JaxbPersistentClass jaxbClass : this.getPersistentClasses()) {
if (StringTools.stringsAreEqual(jaxbClass.getFullyQualifiedName(), className)) {
return jaxbClass;
}
}
return null;
}
public JaxbClass getClass(String fullyQualifiedTypeName) {
JaxbPersistentClass jaxbClass= this.getPersistentClass(fullyQualifiedTypeName);
return jaxbClass != null ? jaxbClass : this.getTransientClass(fullyQualifiedTypeName);
}
// ********** persistent enums **********
public Iterable<JaxbPersistentEnum> getPersistentEnums() {
return new SubIterableWrapper<JaxbType, JaxbPersistentEnum>(
new FilteringIterable<JaxbType>(getTypes()) {
@Override
protected boolean accept(JaxbType o) {
return o.getKind() == JaxbType.Kind.PERSISTENT_ENUM;
}
});
}
protected JaxbPersistentEnum buildPersistentEnum(JavaResourceEnum resourceEnum) {
return this.getFactory().buildJavaPersistentEnum(this, resourceEnum);
}
public Iterable<JaxbPersistentEnum> getPersistentEnums(final JaxbPackage jaxbPackage) {
return new FilteringIterable<JaxbPersistentEnum>(getPersistentEnums()) {
@Override
protected boolean accept(JaxbPersistentEnum o) {
return o.getPackageName().equals(jaxbPackage.getName());
}
};
}
public JaxbPersistentEnum getPersistentEnum(String enumName) {
for (JaxbPersistentEnum jaxbEnum : this.getPersistentEnums()) {
if (StringTools.stringsAreEqual(jaxbEnum.getFullyQualifiedName(), enumName)) {
return jaxbEnum;
}
}
return null;
}
@Override
public void stateChanged() {
super.stateChanged();
// forward to JAXB project
this.jaxbProject.stateChanged();
}
// **************** validation ********************************************
public void validate(List<IMessage> messages, IReporter reporter) {
for (JaxbPackage pkg : this.packages.values()) {
pkg.validate(messages, reporter);
}
for (JaxbType type : this.types.values()) {
type.validate(messages, reporter);
}
}
}