blob: f846e4d692693de81379eecf01c446bd87166723 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 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.java;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jpt.common.core.internal.utility.JDTTools;
import org.eclipse.jpt.common.core.resource.java.JavaResourceType;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.Filter;
import org.eclipse.jpt.common.utility.internal.Bag;
import org.eclipse.jpt.common.utility.internal.CollectionTools;
import org.eclipse.jpt.common.utility.internal.StringTools;
import org.eclipse.jpt.common.utility.internal.iterables.ChainIterable;
import org.eclipse.jpt.common.utility.internal.iterables.CompositeIterable;
import org.eclipse.jpt.common.utility.internal.iterables.EmptyIterable;
import org.eclipse.jpt.common.utility.internal.iterables.EmptyListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.FilteringIterable;
import org.eclipse.jpt.common.utility.internal.iterables.ListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.LiveCloneIterable;
import org.eclipse.jpt.common.utility.internal.iterables.SingleElementIterable;
import org.eclipse.jpt.common.utility.internal.iterables.SubIterableWrapper;
import org.eclipse.jpt.common.utility.internal.iterables.TransformationIterable;
import org.eclipse.jpt.jaxb.core.MappingKeys;
import org.eclipse.jpt.jaxb.core.context.JaxbAttributeMapping;
import org.eclipse.jpt.jaxb.core.context.JaxbAttributesContainer;
import org.eclipse.jpt.jaxb.core.context.JaxbClass;
import org.eclipse.jpt.jaxb.core.context.JaxbClassMapping;
import org.eclipse.jpt.jaxb.core.context.JaxbPackage;
import org.eclipse.jpt.jaxb.core.context.JaxbPackageInfo;
import org.eclipse.jpt.jaxb.core.context.JaxbPersistentAttribute;
import org.eclipse.jpt.jaxb.core.context.JaxbType;
import org.eclipse.jpt.jaxb.core.context.XmlAccessOrder;
import org.eclipse.jpt.jaxb.core.context.XmlAccessType;
import org.eclipse.jpt.jaxb.core.context.XmlNamedNodeMapping;
import org.eclipse.jpt.jaxb.core.internal.validation.DefaultValidationMessages;
import org.eclipse.jpt.jaxb.core.internal.validation.JaxbValidationMessages;
import org.eclipse.jpt.jaxb.core.resource.java.JAXB;
import org.eclipse.jpt.jaxb.core.resource.java.XmlAccessorOrderAnnotation;
import org.eclipse.jpt.jaxb.core.resource.java.XmlAccessorTypeAnnotation;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
import org.omg.CORBA.PUBLIC_MEMBER;
public class GenericJavaClassMapping
extends AbstractJavaTypeMapping
implements JaxbClassMapping {
protected String specifiedFactoryClass;
protected String factoryMethod;
protected final PropOrderContainer propOrderContainer;
protected JaxbClassMapping superclass;
protected XmlAccessType defaultAccessType;
protected XmlAccessType specifiedAccessType;
protected XmlAccessOrder defaultAccessOrder;
protected XmlAccessOrder specifiedAccessOrder;
protected boolean hasRootElementInHierarchy_loaded = false;
protected boolean hasRootElementInHierarchy = false;
protected final JaxbAttributesContainer attributesContainer;
protected final Map<JaxbClassMapping, JaxbAttributesContainer> includedAttributesContainers;
public GenericJavaClassMapping(JaxbClass parent) {
super(parent);
this.includedAttributesContainers = new Hashtable<JaxbClassMapping, JaxbAttributesContainer>();
this.propOrderContainer = new PropOrderContainer();
initFactoryClass();
initFactoryMethod();
initPropOrder();
initSpecifiedAccessType();
initDefaultAccessType();
initSpecifiedAccessOrder();
initDefaultAccessOrder();
this.attributesContainer = new GenericJavaAttributesContainer(this, buildAttributesContainerOwner(), getJavaResourceType());
initIncludedAttributes();
}
@Override
public JavaResourceType getJavaResourceType() {
return (JavaResourceType) super.getJavaResourceType();
}
@Override
public JaxbClass getJaxbType() {
return (JaxbClass) super.getJaxbType();
}
public JaxbPackageInfo getPackageInfo() {
JaxbPackage jaxbPackage = getJaxbPackage();
// jaxb package may be null during initialization/update
return (jaxbPackage == null) ? null : jaxbPackage.getPackageInfo();
}
// ***** sync/update *****
@Override
public void synchronizeWithResourceModel() {
super.synchronizeWithResourceModel();
syncFactoryClass();
syncFactoryMethod();
syncPropOrder();
syncSpecifiedAccessType();
syncSpecifiedAccessOrder();
this.attributesContainer.synchronizeWithResourceModel();
syncIncludedAttributes();
}
@Override
public void update() {
super.update();
updateSuperclass();
updateDefaultAccessType();
updateDefaultAccessOrder();
this.hasRootElementInHierarchy_loaded = false; // triggers that the value must be recalculated on next request
this.attributesContainer.update();
updateIncludedAttributes();
}
// ***** factory class *****
public String getFactoryClass() {
return (this.specifiedFactoryClass != null) ? this.specifiedFactoryClass : JAXB.XML_TYPE__DEFAULT_FACTORY_CLASS;
}
public String getSpecifiedFactoryClass() {
return this.specifiedFactoryClass;
}
public void setSpecifiedFactoryClass(String factoryClass) {
getXmlTypeAnnotation().setFactoryClass(factoryClass);
setSpecifiedFactoryClass_(factoryClass);
}
protected void setSpecifiedFactoryClass_(String factoryClass) {
String old = this.specifiedFactoryClass;
this.specifiedFactoryClass = factoryClass;
firePropertyChanged(SPECIFIED_FACTORY_CLASS_PROPERTY, old, factoryClass);
}
protected String getResourceFactoryClass() {
return getXmlTypeAnnotation().getFactoryClass();
}
protected void initFactoryClass() {
this.specifiedFactoryClass = getResourceFactoryClass();
}
protected void syncFactoryClass() {
setSpecifiedFactoryClass_(getResourceFactoryClass());
}
// ***** factory method *****
public String getFactoryMethod() {
return this.factoryMethod;
}
public void setFactoryMethod(String factoryMethod) {
getXmlTypeAnnotation().setFactoryMethod(factoryMethod);
setFactoryMethod_(factoryMethod);
}
protected void setFactoryMethod_(String factoryMethod) {
String old = this.factoryMethod;
this.factoryMethod = factoryMethod;
firePropertyChanged(FACTORY_METHOD_PROPERTY, old, factoryMethod);
}
protected String getResourceFactoryMethod() {
return getXmlTypeAnnotation().getFactoryMethod();
}
protected void initFactoryMethod() {
this.factoryMethod = getResourceFactoryMethod();
}
protected void syncFactoryMethod() {
setFactoryMethod_(getResourceFactoryMethod());
}
// ***** prop order *****
public ListIterable<String> getPropOrder() {
return this.propOrderContainer.getContextElements();
}
public String getProp(int index) {
return this.propOrderContainer.getContextElement(index);
}
public int getPropOrderSize() {
return this.propOrderContainer.getContextElementsSize();
}
public void addProp(int index, String prop) {
getXmlTypeAnnotation().addProp(index, prop);
this.propOrderContainer.addContextElement(index, prop);
}
public void removeProp(String prop) {
this.removeProp(this.propOrderContainer.indexOfContextElement(prop));
}
public void removeProp(int index) {
this.getXmlTypeAnnotation().removeProp(index);
this.propOrderContainer.removeContextElement(index);
}
public void moveProp(int targetIndex, int sourceIndex) {
this.getXmlTypeAnnotation().moveProp(targetIndex, sourceIndex);
this.propOrderContainer.moveContextElement(targetIndex, sourceIndex);
}
protected void initPropOrder() {
this.propOrderContainer.initialize();
}
protected void syncPropOrder() {
this.propOrderContainer.synchronizeWithResourceModel();
}
protected ListIterable<String> getResourcePropOrder() {
ListIterable<String> result = getXmlTypeAnnotation().getPropOrder();
if (CollectionTools.size(result) == 1 && StringTools.EMPTY_STRING.equals(CollectionTools.get(result, 0))) {
return EmptyListIterable.instance();
}
return result;
}
// ***** XmlAccessorType *****
public XmlAccessType getAccessType() {
return (this.specifiedAccessType != null) ? this.specifiedAccessType : this.defaultAccessType;
}
public XmlAccessType getDefaultAccessType() {
return this.defaultAccessType;
}
protected void setDefaultAccessType_(XmlAccessType access) {
XmlAccessType old = this.defaultAccessType;
this.defaultAccessType = access;
firePropertyChanged(DEFAULT_ACCESS_TYPE_PROPERTY, old, access);
}
public XmlAccessType getSpecifiedAccessType() {
return this.specifiedAccessType;
}
public void setSpecifiedAccessType(XmlAccessType access) {
getXmlAccessorTypeAnnotation().setValue(XmlAccessType.toJavaResourceModel(access));
setSpecifiedAccessType_(access);
}
protected void setSpecifiedAccessType_(XmlAccessType access) {
XmlAccessType old = this.specifiedAccessType;
this.specifiedAccessType = access;
firePropertyChanged(SPECIFIED_ACCESS_TYPE_PROPERTY, old, access);
}
protected void initDefaultAccessType() {
this.defaultAccessType = buildDefaultAccessType();
}
protected void updateDefaultAccessType() {
setDefaultAccessType_(buildDefaultAccessType());
}
/**
* Default access type is determined by the following, in order of precedence:
* - @XmlAccessorType annotation on a mapped super class
* - @XmlAccessorType annotation on the package
* - default access type of {@link PUBLIC_MEMBER}
*/
protected XmlAccessType buildDefaultAccessType() {
XmlAccessType accessType = getSuperclassAccessType();
if (accessType != null) {
return accessType;
}
accessType = getPackageAccessType();
if (accessType != null) {
return accessType;
}
return XmlAccessType.PUBLIC_MEMBER;
}
protected XmlAccessType getSuperclassAccessType() {
return this.superclass == null ? null : this.superclass.getSpecifiedAccessType();
}
protected XmlAccessType getPackageAccessType() {
JaxbPackageInfo packageInfo = getPackageInfo();
return (packageInfo == null) ? null : packageInfo.getAccessType();
}
protected XmlAccessorTypeAnnotation getXmlAccessorTypeAnnotation() {
return (XmlAccessorTypeAnnotation) getJavaResourceType().getNonNullAnnotation(JAXB.XML_ACCESSOR_TYPE);
}
protected XmlAccessType getResourceAccessType() {
return XmlAccessType.fromJavaResourceModel(getXmlAccessorTypeAnnotation().getValue());
}
protected void initSpecifiedAccessType() {
this.specifiedAccessType = getResourceAccessType();
}
protected void syncSpecifiedAccessType() {
setSpecifiedAccessType_(getResourceAccessType());
}
// ***** XmlAccessorOrder *****
public XmlAccessOrder getAccessOrder() {
return (this.specifiedAccessOrder != null) ? this.specifiedAccessOrder : this.defaultAccessOrder;
}
public XmlAccessOrder getDefaultAccessOrder() {
return this.defaultAccessOrder;
}
protected void setDefaultAccessOrder_(XmlAccessOrder accessOrder) {
XmlAccessOrder old = this.defaultAccessOrder;
this.defaultAccessOrder = accessOrder;
firePropertyChanged(DEFAULT_ACCESS_ORDER_PROPERTY, old, accessOrder);
}
public XmlAccessOrder getSpecifiedAccessOrder() {
return this.specifiedAccessOrder;
}
public void setSpecifiedAccessOrder(XmlAccessOrder accessOrder) {
getXmlAccessorOrderAnnotation().setValue(XmlAccessOrder.toJavaResourceModel(accessOrder));
setSpecifiedAccessOrder_(accessOrder);
}
protected void setSpecifiedAccessOrder_(XmlAccessOrder accessOrder) {
XmlAccessOrder old = this.specifiedAccessOrder;
this.specifiedAccessOrder = accessOrder;
firePropertyChanged(SPECIFIED_ACCESS_ORDER_PROPERTY, old, accessOrder);
}
protected void initDefaultAccessOrder() {
this.defaultAccessOrder = buildDefaultAccessOrder();
}
protected void updateDefaultAccessOrder() {
setDefaultAccessOrder_(buildDefaultAccessOrder());
}
/**
* Default access order is determined by the following, in order of precedence:
* - @XmlAccessorOrder annotation on a mapped super class
* - @XmlAccessorOrder annotation on the package
* - default access order of {@link UNDEFINED}
*/
protected XmlAccessOrder buildDefaultAccessOrder() {
XmlAccessOrder accessOrder = getSuperclassAccessOrder();
if (accessOrder != null) {
return accessOrder;
}
accessOrder = getPackageAccessOrder();
if (accessOrder != null) {
return accessOrder;
}
return XmlAccessOrder.UNDEFINED;
}
protected XmlAccessOrder getSuperclassAccessOrder() {
JaxbClassMapping superclass = this.superclass;
while (superclass != null) {
XmlAccessOrder accessOrder = superclass.getSpecifiedAccessOrder();
if (accessOrder != null) {
return accessOrder;
}
superclass = superclass.getSuperclass();
}
return null;
}
protected XmlAccessOrder getPackageAccessOrder() {
JaxbPackageInfo packageInfo = getPackageInfo();
return packageInfo == null ? null : packageInfo.getAccessOrder();
}
protected XmlAccessorOrderAnnotation getXmlAccessorOrderAnnotation() {
return (XmlAccessorOrderAnnotation) getJavaResourceType().getNonNullAnnotation(JAXB.XML_ACCESSOR_ORDER);
}
protected XmlAccessOrder getResourceAccessOrder() {
return XmlAccessOrder.fromJavaResourceModel(getXmlAccessorOrderAnnotation().getValue());
}
protected void initSpecifiedAccessOrder() {
this.specifiedAccessOrder = getResourceAccessOrder();
}
protected void syncSpecifiedAccessOrder() {
setSpecifiedAccessOrder_(getResourceAccessOrder());
}
// ********** super class **********
public JaxbClassMapping getSuperclass() {
return this.superclass;
}
protected void setSuperclass_(JaxbClassMapping superclass) {
JaxbClassMapping old = this.superclass;
this.superclass = superclass;
this.firePropertyChanged(SUPERCLASS_PROPERTY, old, superclass);
}
protected void updateSuperclass() {
setSuperclass_(findSuperclass());
}
protected JaxbClassMapping findSuperclass() {
JavaResourceType resourceType = getSuperclass(getJavaResourceType());
while (resourceType != null && resourceType != this) {
JaxbType jaxbType = getJaxbProject().getContextRoot().getType(resourceType.getQualifiedName());
// if the superclass is not a class, return null
if (jaxbType == null || jaxbType.getKind() != JaxbType.Kind.CLASS) {
return null;
}
JaxbClassMapping jaxbClassMapping = ((JaxbClass) jaxbType).getMapping();
// rare for a non-null superclass to not be mapped, but potentially possible mid-update
if (jaxbClassMapping != null) {
return jaxbClassMapping;
}
else {
resourceType = getSuperclass(resourceType);
}
}
return null;
}
protected JavaResourceType getSuperclass(JavaResourceType resourceType) {
String superclassName = resourceType.getSuperclassQualifiedName();
if (superclassName == null) {
return null;
}
return (JavaResourceType) getJaxbProject().getJavaResourceType(
superclassName, JavaResourceType.Kind.TYPE);
}
// ***** attributes *****
public Iterable<JaxbPersistentAttribute> getAttributes() {
return this.attributesContainer.getAttributes();
}
public int getAttributesSize() {
return this.attributesContainer.getAttributesSize();
}
protected GenericJavaAttributesContainer.Owner buildAttributesContainerOwner() {
return new GenericJavaAttributesContainer.Owner() {
public XmlAccessType getAccessType() {
return GenericJavaClassMapping.this.getAccessType();
}
public void fireAttributeAdded(JaxbPersistentAttribute attribute) {
GenericJavaClassMapping.this.fireItemAdded(ATTRIBUTES_COLLECTION, attribute);
}
public void fireAttributeRemoved(JaxbPersistentAttribute attribute) {
GenericJavaClassMapping.this.fireItemRemoved(ATTRIBUTES_COLLECTION, attribute);
}
};
}
// ***** included attributes *****
public Iterable<JaxbPersistentAttribute> getIncludedAttributes() {
return new CompositeIterable<JaxbPersistentAttribute>(getIncludedAttributeSets());
}
protected Iterable<Iterable<JaxbPersistentAttribute>> getIncludedAttributeSets() {
return new TransformationIterable<JaxbAttributesContainer, Iterable<JaxbPersistentAttribute>>(
getIncludedAttributesContainers()) {
@Override
protected Iterable<JaxbPersistentAttribute> transform(JaxbAttributesContainer attributesContainer) {
return attributesContainer.getAttributes();
}
};
}
protected Iterable<JaxbAttributesContainer> getIncludedAttributesContainers() {
return new LiveCloneIterable<JaxbAttributesContainer>(this.includedAttributesContainers.values()); // read-only
}
public int getIncludedAttributesSize() {
int size = 0;
for (JaxbAttributesContainer attributesContainer : getIncludedAttributesContainers()) {
size += attributesContainer.getAttributesSize();
}
return size;
}
protected void initIncludedAttributes() {
// xml transient classes have no included attributes
if (isXmlTransient()) {
return;
}
JaxbClassMapping superclass = this.superclass;
// only add inherited attributes for superclasses up until a mapped class is encountered
while (superclass != null && superclass.isXmlTransient()) {
this.includedAttributesContainers.put(superclass, buildIncludedAttributesContainer(superclass));
superclass = superclass.getSuperclass();
}
}
protected void syncIncludedAttributes() {
for (JaxbAttributesContainer attributesContainer : this.includedAttributesContainers.values()) {
attributesContainer.synchronizeWithResourceModel();
}
}
protected void updateIncludedAttributes() {
HashSet<JaxbClassMapping> oldSuperclasses
= CollectionTools.set(this.includedAttributesContainers.keySet());
Set<JaxbPersistentAttribute> oldAttributes = CollectionTools.set(getIncludedAttributes());
if (! isXmlTransient()) {
JaxbClassMapping superclass = this.superclass;
// only add inherited attributes for superclasses up until a mapped class is encountered
while (superclass != null && superclass.isXmlTransient()) {
if (this.includedAttributesContainers.containsKey(superclass)) {
this.includedAttributesContainers.get(superclass).update();
oldSuperclasses.remove(superclass);
}
else {
this.includedAttributesContainers.put(superclass, buildIncludedAttributesContainer(superclass));
}
superclass = superclass.getSuperclass();
}
}
for (JaxbClassMapping oldSuperclass : oldSuperclasses) {
this.includedAttributesContainers.remove(oldSuperclass);
}
Set<JaxbPersistentAttribute> newAttributes = CollectionTools.set(getIncludedAttributes());
if (CollectionTools.elementsAreDifferent(oldAttributes, newAttributes)) {
fireCollectionChanged(INCLUDED_ATTRIBUTES_COLLECTION, newAttributes);
}
}
protected JaxbAttributesContainer buildIncludedAttributesContainer(JaxbClassMapping jaxbClassMapping) {
return new GenericJavaAttributesContainer(this, buildIncludedAttributesContainerOwner(), jaxbClassMapping.getJaxbType().getJavaResourceType());
}
protected GenericJavaAttributesContainer.Owner buildIncludedAttributesContainerOwner() {
return new GenericJavaAttributesContainer.Owner() {
public XmlAccessType getAccessType() {
return GenericJavaClassMapping.this.getAccessType();
}
public void fireAttributeAdded(JaxbPersistentAttribute attribute) {
GenericJavaClassMapping.this.fireItemAdded(INCLUDED_ATTRIBUTES_COLLECTION, attribute);
}
public void fireAttributeRemoved(JaxbPersistentAttribute attribute) {
GenericJavaClassMapping.this.fireItemRemoved(INCLUDED_ATTRIBUTES_COLLECTION, attribute);
}
};
}
// ***** misc attributes *****
public Iterable<JaxbPersistentAttribute> getAllLocallyDefinedAttributes() {
return new CompositeIterable<JaxbPersistentAttribute>(
getAttributes(),
getIncludedAttributes());
}
public Iterable<JaxbPersistentAttribute> getInheritedAttributes() {
return new CompositeIterable<JaxbPersistentAttribute>(
getIncludedAttributes(),
getOtherInheritedAttributes());
}
public Iterable<JaxbPersistentAttribute> getAllAttributes() {
return new CompositeIterable<JaxbPersistentAttribute>(
getAttributes(),
getIncludedAttributes(),
getOtherInheritedAttributes());
}
/**
* return those inherited attributes that are not included
*/
protected Iterable<JaxbPersistentAttribute> getOtherInheritedAttributes() {
return new CompositeIterable<JaxbPersistentAttribute>(
new TransformationIterable<JaxbClassMapping, Iterable<JaxbPersistentAttribute>>(
new ChainIterable<JaxbClassMapping>(getSuperclass()) {
@Override
protected JaxbClassMapping nextLink(JaxbClassMapping currentLink) {
return currentLink.getSuperclass();
}
}) {
@Override
protected Iterable<JaxbPersistentAttribute> transform(JaxbClassMapping o) {
return o.getAttributes();
}
});
}
// ***** subClasses *****
@Override
public boolean hasRootElementInHierarchy() {
if (! this.hasRootElementInHierarchy_loaded) {
this.hasRootElementInHierarchy = calculateHasRootElementInHierarchy();
this.hasRootElementInHierarchy_loaded = true;
}
return this.hasRootElementInHierarchy;
}
protected boolean calculateHasRootElementInHierarchy() {
if (this.getXmlRootElement() != null) {
return true;
}
for (JaxbType jaxbType : getJaxbProject().getContextRoot().getTypes()) {
if (jaxbType.getMapping() != null
&& ! jaxbType.getMapping().isXmlTransient()
&& jaxbType.getMapping().getXmlRootElement() != null
&& JDTTools.typeIsSubType(
getJaxbProject().getJavaProject(),
jaxbType.getFullyQualifiedName(), getJaxbType().getFullyQualifiedName())) {
return true;
}
}
return false;
}
// ***** misc *****
@Override
protected Iterable<String> getTransientReferencedXmlTypeNames() {
return new CompositeIterable<String>(
super.getTransientReferencedXmlTypeNames(),
new SingleElementIterable(getJavaResourceType().getSuperclassQualifiedName()));
}
@Override
protected Iterable<String> getNonTransientReferencedXmlTypeNames() {
return new CompositeIterable<String>(
super.getNonTransientReferencedXmlTypeNames(),
new SingleElementIterable(getJavaResourceType().getSuperclassQualifiedName()),
new CompositeIterable<String>(
new TransformationIterable<JaxbPersistentAttribute, Iterable<String>>(getAttributes()) {
@Override
protected Iterable<String> transform(JaxbPersistentAttribute o) {
return o.getMapping().getReferencedXmlTypeNames();
}
}));
}
public JaxbAttributeMapping getXmlIdMapping() {
Iterator<XmlNamedNodeMapping> allXmlIdMappings =
new FilteringIterable<XmlNamedNodeMapping>(
new SubIterableWrapper<JaxbAttributeMapping, XmlNamedNodeMapping>(
new FilteringIterable<JaxbAttributeMapping>(
new TransformationIterable<JaxbPersistentAttribute, JaxbAttributeMapping>(getAllAttributes()) {
@Override
protected JaxbAttributeMapping transform(JaxbPersistentAttribute o) {
return o.getMapping();
}
}) {
@Override
protected boolean accept(JaxbAttributeMapping o) {
return (o.getKey() == MappingKeys.XML_ELEMENT_ATTRIBUTE_MAPPING_KEY
|| o.getKey() == MappingKeys.XML_ATTRIBUTE_ATTRIBUTE_MAPPING_KEY);
}
})) {
@Override
protected boolean accept(XmlNamedNodeMapping o) {
return o.getXmlID() != null;
}
}.iterator();
return (allXmlIdMappings.hasNext()) ? allXmlIdMappings.next() : null;
}
protected Iterable<? extends JaxbAttributeMapping> getAttributeMappings() {
return new TransformationIterable<JaxbPersistentAttribute, JaxbAttributeMapping>(getAttributes()) {
@Override
protected JaxbAttributeMapping transform(JaxbPersistentAttribute attribute) {
return attribute.getMapping();
}
};
}
// ***** content assist *****
@Override
public Iterable<String> getJavaCompletionProposals(
int pos, Filter<String> filter, CompilationUnit astRoot) {
Iterable<String> result = super.getJavaCompletionProposals(pos, filter, astRoot);
if (! CollectionTools.isEmpty(result)) {
return result;
}
if (propTouches(pos, astRoot)) {
return getPropProposals(filter);
}
// TODO - factory methods?
for (JaxbPersistentAttribute attribute : this.getAttributes()) {
result = attribute.getJavaCompletionProposals(pos, filter, astRoot);
if (!CollectionTools.isEmpty(result)) {
return result;
}
}
return EmptyIterable.instance();
}
protected Iterable<String> getPropProposals(Filter<String> filter) {
return StringTools.convertToJavaStringLiterals(
new FilteringIterable<String>(
new TransformationIterable<JaxbPersistentAttribute, String>(getAllLocallyDefinedAttributes()) {
@Override
protected String transform(JaxbPersistentAttribute o) {
return o.getName();
}
},
filter));
}
// ***** validation *****
@Override
public void validate(List<IMessage> messages, IReporter reporter, CompilationUnit astRoot) {
super.validate(messages, reporter, astRoot);
validateConstructor(messages, reporter, astRoot);
validatePropOrder(messages, reporter, astRoot);
validateXmlAnyAttributeMapping(messages, astRoot);
validateXmlAnyElementMapping(messages, astRoot);
validateXmlValueMapping(messages, astRoot);
validateXmlIDs(messages, astRoot);
for (JaxbPersistentAttribute attribute : getAttributes()) {
attribute.validate(messages, reporter, astRoot);
}
}
protected void validateConstructor(List<IMessage> messages, IReporter reporter, CompilationUnit astRoot) {
// TODO - factory class/method
if (! JAXB.XML_TYPE__DEFAULT_FACTORY_CLASS.equals(getFactoryClass())) {
if (StringTools.stringIsEmpty(getFactoryMethod())) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_TYPE__UNSPECIFIED_FACTORY_METHOD,
this,
getFactoryClassTextRange(astRoot)));
}
}
else {
if (getFactoryMethod() == null
&& getJaxbType().getXmlJavaTypeAdapter() == null
&& ! getJavaResourceType().hasPublicOrProtectedNoArgConstructor()) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_TYPE__NO_PUBLIC_OR_PROTECTED_CONSTRUCTOR,
this,
getValidationTextRange(astRoot)));
}
}
}
protected void validatePropOrder(List<IMessage> messages, IReporter reporter, CompilationUnit astRoot) {
if (CollectionTools.isEmpty(getPropOrder())) {
return;
}
// no duplicates
// all attributes/included attributes with XmlElement/s/Ref/s must be listed
// no nonexistent attributes (attributes mapped otherwise allowed) ...
// *except* no transient attributes allowed
Bag<String> props = CollectionTools.bag(getPropOrder());
Set<String> allAttributes = new HashSet<String>();
Set<String> requiredAttributes = new HashSet<String>();
Set<String> transientAttributes = new HashSet<String>();
for (JaxbPersistentAttribute attribute : getAllLocallyDefinedAttributes()) {
allAttributes.add(attribute.getName());
transientAttributes.add(attribute.getName());
}
for (JaxbPersistentAttribute attribute : getAllLocallyDefinedAttributes()) {
if (attribute.getMapping().isParticleMapping()) {
requiredAttributes.add(attribute.getName());
}
if (! attribute.getMapping().isTransient()) {
// remove transients (rather than previous algorithm of adding them)
// there may be two attributes of the same name (one field, one property) in
// a class, which is a correct configuration. we want to know if *every* attribute
// of a given name is transient (or if there's a single non-transient attribute)
transientAttributes.remove(attribute.getName());
}
}
Set<Integer> duplicateProps = new HashSet<Integer>();
Set<String> missingProps = new HashSet<String>(requiredAttributes);
Set<Integer> nonexistentProps = new HashSet<Integer>();
Set<Integer> transientProps = new HashSet<Integer>();
for (int i = 0; i < getPropOrderSize(); i ++ ) {
String prop = getProp(i);
if (props.count(prop) > 1) {
duplicateProps.add(i);
}
if (missingProps.contains(prop)) {
missingProps.remove(prop);
}
if (! allAttributes.contains(prop)) {
nonexistentProps.add(i);
}
if (transientAttributes.contains(prop)) {
transientProps.add(i);
}
}
for (int i : duplicateProps) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_TYPE__DUPLICATE_PROP,
new String[] { getProp(i) },
this,
getPropTextRange(i, astRoot)));
}
for (String missingProp : missingProps) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_TYPE__MISSING_PROP,
new String[] { missingProp },
this,
getPropOrderTextRange(astRoot)));
}
for (int i : nonexistentProps) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_TYPE__NONEXISTENT_PROP,
new String[] { getProp(i) },
this,
getPropTextRange(i, astRoot)));
}
for (int i : transientProps) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_TYPE__TRANSIENT_PROP,
new String [] { getProp(i) },
this,
getPropTextRange(i, astRoot)));
}
}
protected void validateXmlAnyAttributeMapping(List<IMessage> messages, CompilationUnit astRoot) {
Set<JaxbPersistentAttribute> localAttributes = new HashSet<JaxbPersistentAttribute>();
Set<JaxbPersistentAttribute> allAttributes = new HashSet<JaxbPersistentAttribute>();
for (JaxbPersistentAttribute attribute : getAttributes()) {
if (attribute.getMappingKey() == MappingKeys.XML_ANY_ATTRIBUTE_ATTRIBUTE_MAPPING_KEY) {
localAttributes.add(attribute);
allAttributes.add(attribute);
}
}
for (JaxbPersistentAttribute attribute : getInheritedAttributes()) {
if (attribute.getMappingKey() == MappingKeys.XML_ANY_ATTRIBUTE_ATTRIBUTE_MAPPING_KEY) {
allAttributes.add(attribute);
}
}
if (allAttributes.size() > 1) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_ANY_ATTRIBUTE__MULTIPLE_MAPPINGS_DEFINED,
this,
getValidationTextRange(astRoot)));
for (JaxbPersistentAttribute anyAttribute : localAttributes) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_ANY_ATTRIBUTE__MULTIPLE_MAPPINGS_DEFINED,
anyAttribute.getMapping(),
anyAttribute.getMapping().getValidationTextRange(astRoot)));
}
}
}
protected void validateXmlAnyElementMapping(List<IMessage> messages, CompilationUnit astRoot) {
Set<JaxbPersistentAttribute> localAttributes = new HashSet<JaxbPersistentAttribute>();
Set<JaxbPersistentAttribute> allAttributes = new HashSet<JaxbPersistentAttribute>();
for (JaxbPersistentAttribute attribute : getAttributes()) {
if (attribute.getMappingKey() == MappingKeys.XML_ANY_ELEMENT_ATTRIBUTE_MAPPING_KEY) {
localAttributes.add(attribute);
allAttributes.add(attribute);
}
}
for (JaxbPersistentAttribute attribute : getInheritedAttributes()) {
if (attribute.getMappingKey() == MappingKeys.XML_ANY_ELEMENT_ATTRIBUTE_MAPPING_KEY) {
allAttributes.add(attribute);
}
}
if (allAttributes.size() > 1) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_ANY_ELEMENT__MULTIPLE_MAPPINGS_DEFINED,
this,
getValidationTextRange(astRoot)));
for (JaxbPersistentAttribute anyAttribute : localAttributes) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_ANY_ELEMENT__MULTIPLE_MAPPINGS_DEFINED,
anyAttribute.getMapping(),
anyAttribute.getMapping().getValidationTextRange(astRoot)));
}
}
}
protected void validateXmlValueMapping(List<IMessage> messages, CompilationUnit astRoot) {
Set<JaxbPersistentAttribute> localAttributes = new HashSet<JaxbPersistentAttribute>();
Set<JaxbPersistentAttribute> allAttributes = new HashSet<JaxbPersistentAttribute>();
for (JaxbPersistentAttribute attribute : getAttributes()) {
if (attribute.getMappingKey() == MappingKeys.XML_VALUE_ATTRIBUTE_MAPPING_KEY) {
localAttributes.add(attribute);
allAttributes.add(attribute);
}
}
for (JaxbPersistentAttribute attribute : getInheritedAttributes()) {
if (attribute.getMappingKey() == MappingKeys.XML_VALUE_ATTRIBUTE_MAPPING_KEY) {
allAttributes.add(attribute);
}
}
if (allAttributes.size() > 1) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_VALUE__MULTIPLE_MAPPINGS_DEFINED,
this,
getValidationTextRange(astRoot)));
for (JaxbPersistentAttribute anyAttribute : localAttributes) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_VALUE__MULTIPLE_MAPPINGS_DEFINED,
anyAttribute.getMapping(),
anyAttribute.getMapping().getValidationTextRange(astRoot)));
}
}
}
protected void validateXmlIDs(List<IMessage> messages, CompilationUnit astRoot) {
Set<JaxbPersistentAttribute> localAttributes = new HashSet<JaxbPersistentAttribute>();
Set<JaxbPersistentAttribute> allAttributes = new HashSet<JaxbPersistentAttribute>();
for (JaxbPersistentAttribute attribute : getAttributes()) {
if ((attribute.getMappingKey() == MappingKeys.XML_ATTRIBUTE_ATTRIBUTE_MAPPING_KEY
|| attribute.getMappingKey() == MappingKeys.XML_ELEMENT_ATTRIBUTE_MAPPING_KEY)
&& ((XmlNamedNodeMapping) attribute.getMapping()).getXmlID() != null) {
localAttributes.add(attribute);
allAttributes.add(attribute);
}
}
for (JaxbPersistentAttribute attribute : getInheritedAttributes()) {
if ((attribute.getMappingKey() == MappingKeys.XML_ATTRIBUTE_ATTRIBUTE_MAPPING_KEY
|| attribute.getMappingKey() == MappingKeys.XML_ELEMENT_ATTRIBUTE_MAPPING_KEY)
&& ((XmlNamedNodeMapping) attribute.getMapping()).getXmlID() != null) {
allAttributes.add(attribute);
}
}
if (allAttributes.size() > 1) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_ID__MULTIPLE_MAPPINGS_DEFINED,
this,
getValidationTextRange(astRoot)));
for (JaxbPersistentAttribute anyAttribute : localAttributes) {
messages.add(
DefaultValidationMessages.buildMessage(
IMessage.HIGH_SEVERITY,
JaxbValidationMessages.XML_ID__MULTIPLE_MAPPINGS_DEFINED,
anyAttribute.getMapping(),
anyAttribute.getMapping().getValidationTextRange(astRoot)));
}
}
}
protected TextRange getFactoryClassTextRange(CompilationUnit astRoot) {
TextRange result = getXmlTypeAnnotation().getFactoryClassTextRange(astRoot);
return (result != null) ? result : getValidationTextRange(astRoot);
}
protected TextRange getFactoryMethodTextRange(CompilationUnit astRoot) {
TextRange result = getXmlTypeAnnotation().getFactoryMethodTextRange(astRoot);
return (result != null) ? result : getValidationTextRange(astRoot);
}
protected TextRange getPropOrderTextRange(CompilationUnit astRoot) {
TextRange result = getXmlTypeAnnotation().getPropOrderTextRange(astRoot);
return (result != null) ? result : getValidationTextRange(astRoot);
}
protected TextRange getPropTextRange(int index, CompilationUnit astRoot) {
return getXmlTypeAnnotation().getPropTextRange(index, astRoot);
}
protected boolean propTouches(int pos, CompilationUnit astRoot) {
if (getXmlTypeAnnotation().propOrderTouches(pos, astRoot)) {
for (int i = 0; i < getXmlTypeAnnotation().getPropOrderSize(); i ++ ) {
if (getXmlTypeAnnotation().propTouches(i, pos, astRoot)) {
return true;
}
}
}
return false;
}
/**
* xml prop order container
*/
protected class PropOrderContainer
extends ListContainer<String, String> {
@Override
protected String getContextElementsPropertyName() {
return PROP_ORDER_LIST;
}
@Override
protected String buildContextElement(String resourceElement) {
return resourceElement;
}
@Override
protected ListIterable<String> getResourceElements() {
return GenericJavaClassMapping.this.getResourcePropOrder();
}
@Override
protected String getResourceElement(String contextElement) {
return contextElement;
}
}
}