blob: 7becc89143d0d438fa533c4eb858dafe6f1aef18 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2015 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.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.eclipse.jpt.common.core.resource.java.JavaResourceAnnotatedElement;
import org.eclipse.jpt.common.core.resource.java.JavaResourceField;
import org.eclipse.jpt.common.core.resource.java.JavaResourceMethod;
import org.eclipse.jpt.common.core.resource.java.JavaResourceType;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.internal.collection.CollectionTools;
import org.eclipse.jpt.common.utility.internal.iterable.IterableTools;
import org.eclipse.jpt.common.utility.predicate.Predicate;
import org.eclipse.jpt.jaxb.core.context.JaxbAttributesContainer;
import org.eclipse.jpt.jaxb.core.context.JaxbClassMapping;
import org.eclipse.jpt.jaxb.core.context.JaxbPersistentAttribute;
import org.eclipse.jpt.jaxb.core.context.XmlAccessType;
import org.eclipse.jpt.jaxb.core.context.java.JavaPersistentAttribute;
import org.eclipse.wst.validation.internal.provisional.core.IMessage;
import org.eclipse.wst.validation.internal.provisional.core.IReporter;
public class GenericJavaAttributesContainer
extends AbstractJavaContextNode
implements JaxbAttributesContainer<JavaPersistentAttribute> {
protected JavaResourceType javaResourceType;
protected JaxbAttributesContainer.Context owner;
protected final Vector<JavaPersistentAttribute> attributes = new Vector<JavaPersistentAttribute>();
public GenericJavaAttributesContainer(
JaxbClassMapping parent, JaxbAttributesContainer.Context owner, JavaResourceType resourceType) {
super(parent);
this.javaResourceType = resourceType;
this.owner = owner;
initializeAttributes();
}
public JaxbClassMapping getClassMapping() {
return (JaxbClassMapping) getParent();
}
public boolean isFor(JavaResourceType jrt) {
return this.javaResourceType == jrt;
}
// ********** synchronize/update **********
@Override
public void synchronizeWithResourceModel() {
super.synchronizeWithResourceModel();
this.synchronizeNodesWithResourceModel(this.getAttributes());
}
@Override
public void update() {
super.update();
this.updateAttributes();
}
// ********** access type **********
protected XmlAccessType getAccessType() {
return this.owner.getAccessType();
}
// ********** attributes **********
public Iterable<JavaPersistentAttribute> getAttributes() {
return IterableTools.cloneLive(this.attributes);
}
public int getAttributesSize() {
return this.attributes.size();
}
protected void addAttribute(JavaPersistentAttribute attribute) {
if (this.attributes.add(attribute)) {
this.owner.attributeAdded(attribute);
}
}
protected void removeAttribute(JavaPersistentAttribute attribute) {
if (this.attributes.remove(attribute)) {
this.owner.attributeRemoved(attribute);
}
}
protected JavaPersistentAttribute buildField(JavaResourceField resourceField) {
return getFactory().buildJavaPersistentField(getClassMapping(), resourceField);
}
protected JavaPersistentAttribute buildProperty(JavaResourceMethod resourceGetter, JavaResourceMethod resourceSetter) {
return getFactory().buildJavaPersistentProperty(getClassMapping(), resourceGetter, resourceSetter);
}
protected void initializeAttributes() {
if (getClassMapping().isXmlTransient()) {
return;
}
if (getAccessType() == XmlAccessType.PUBLIC_MEMBER) {
this.initializePublicMemberAccessAttributes();
}
else if (getAccessType() == XmlAccessType.FIELD) {
this.intializeFieldAccessAttributes();
}
else if (getAccessType() == XmlAccessType.PROPERTY) {
this.intializePropertyAccessAttributes();
}
else if (getAccessType() == XmlAccessType.NONE) {
this.intializeNoneAccessAttributes();
}
}
/**
* Initialize the attributes for XmlAccessType.PUBLIC_MEMBER
* 1. all public, non-static, non-transient fields (transient modifier, @XmlTransient is brought to the context model)
* 2. all annotated fields that aren't public
* 3. all public getter/setter javabeans pairs
* 4. all annotated methods (some will have a matching getter/setter, some will be standalone)
*/
private void initializePublicMemberAccessAttributes() {
this.initializeFieldAttributes(JavaResourceField.IS_RELEVANT_FOR_PUBLIC_MEMBER_ACCESS);
Collection<JavaResourceMethod> resourceMethods = CollectionTools.hashBag(this.getResourceMethods());
//iterate through all persistable resource method getters
for (JavaResourceMethod getterMethod : this.getResourcePropertyGetters()) {
JavaResourceMethod setterMethod = JavaResourceMethod.SET_METHOD_TRANSFORMER.transform(getterMethod);
if (includePublicMemberAccessProperty(getterMethod, setterMethod)) {
this.attributes.add(this.buildProperty(getterMethod, setterMethod));
}
resourceMethods.remove(getterMethod);
resourceMethods.remove(setterMethod);
}
this.initializeRemainingResourceMethodAttributes(resourceMethods);
}
/**
* Initialize the attributes for XmlAccessType.FIELD
* 1. all non-transient fields
* 2. all annotated methods getters/setters
*/
private void intializeFieldAccessAttributes() {
this.initializeFieldAttributes(JavaResourceField.IS_RELEVANT_FOR_FIELD_ACCESS);
this.initializeAnnotatedPropertyAttributes();
}
/**
* Initialize the attributes for XmlAccessType.PROPERTY
* 1. all getter/setter javabeans pairs
* 2. all annotated fields
* 3. all annotated methods getters/setters that don't have a matching pair
*/
private void intializePropertyAccessAttributes() {
this.initializeFieldAttributes(JavaResourceAnnotatedElement.IS_ANNOTATED);
Collection<JavaResourceMethod> resourceMethods = CollectionTools.hashBag(this.getResourceMethods());
//iterate through all resource methods searching for persistable getters
for (JavaResourceMethod getterMethod : this.getResourcePropertyGetters()) {
JavaResourceMethod setterMethod = JavaResourceMethod.SET_METHOD_TRANSFORMER.transform(getterMethod);
if (this.includeProperty(getterMethod, setterMethod)) {
this.attributes.add(this.buildProperty(getterMethod, setterMethod));
}
resourceMethods.remove(getterMethod);
resourceMethods.remove(setterMethod);
}
this.initializeRemainingResourceMethodAttributes(resourceMethods);
}
/**
* Initialize the attributes for XmlAccessType.NONE
* 1. all annotated fields
* 2. all annotated methods getters/setters (some will have a matching getter/setter, some will be standalone)
*/
private void intializeNoneAccessAttributes() {
this.initializeFieldAttributes(JavaResourceAnnotatedElement.IS_ANNOTATED);
this.initializeAnnotatedPropertyAttributes();
}
private void initializeFieldAttributes(Predicate<? super JavaResourceField> filter) {
for (JavaResourceField resourceField : this.getResourceFields(filter)) {
this.attributes.add(this.buildField(resourceField));
}
}
private void initializeRemainingResourceMethodAttributes(Collection<JavaResourceMethod> resourceMethods) {
//iterate through remaining resource methods and search for those that are annotated.
//all getter methods will already be used.
for (JavaResourceMethod resourceMethod : resourceMethods) {
if (resourceMethod.isAnnotated()) {
//annotated setter(or other random method) with no corresponding getter, bring into context model for validation purposes
this.attributes.add(this.buildProperty(null, resourceMethod));
}
}
}
/**
* Return whether the pair of methods (that form a "property") are to be
* included in the contailner.
* Lists do not need a setter....
* An annotated getter with no setter is still brought into the context
* model (for validation purposes)....
*/
private boolean includeProperty(JavaResourceMethod getterMethod, JavaResourceMethod setterMethod) {
return (setterMethod != null) ||
getterMethod.getTypeBinding().getQualifiedName().equals(java.util.List.class.getName()) ||
getterMethod.isAnnotated();
}
private boolean includePublicMemberAccessProperty(JavaResourceMethod getterMethod, JavaResourceMethod setterMethod) {
if (getterMethod.isPublic()) {
if (setterMethod != null) {
if (setterMethod.isPublic()) {
return true;
}
}
//Lists do not have to have a corresponding setter method
else if (getterMethod.getTypeBinding().getQualifiedName().equals(java.util.List.class.getName())) {
return true;
}
else if (getterMethod.isAnnotated()) {
//annotated getter with no corresponding setter, bring into context model for validation purposes
return true;
}
}
else if (getterMethod.isAnnotated() || (setterMethod != null && setterMethod.isAnnotated())) {
return true;
}
return false;
}
private void initializeAnnotatedPropertyAttributes() {
Collection<JavaResourceMethod> resourceMethods = CollectionTools.hashBag(this.getResourceMethods());
//iterate through all resource methods searching for persistable getters
for (JavaResourceMethod getterMethod : this.getResourcePropertyGetters()) {
JavaResourceMethod setterMethod = JavaResourceMethod.SET_METHOD_TRANSFORMER.transform(getterMethod);
if (getterMethod.isAnnotated() || (setterMethod != null && setterMethod.isAnnotated())) {
this.attributes.add(this.buildProperty(getterMethod, setterMethod));
}
resourceMethods.remove(getterMethod);
resourceMethods.remove(setterMethod);
}
this.initializeRemainingResourceMethodAttributes(resourceMethods);
}
protected Iterable<JavaResourceField> getResourceFields() {
return this.javaResourceType.getFields();
}
protected Iterable<JavaResourceMethod> getResourceMethods() {
return this.javaResourceType.getMethods();
}
protected Iterable<JavaResourceField> getResourceFields(Predicate<? super JavaResourceField> filter) {
return IterableTools.filter(getResourceFields(), filter);
}
protected Iterable<JavaResourceMethod> filterResourceMethods(Predicate<JavaResourceMethod> filter) {
return IterableTools.filter(getResourceMethods(), filter);
}
protected Iterable<JavaResourceMethod> getResourcePropertyGetters() {
return this.filterResourceMethods(JavaResourceMethod.IS_PROPERTY_GETTER);
}
/**
* The attributes are synchronized during the <em>update</em> because
* the list of resource attributes is determined by the access type
* which can be controlled in a number of different places....
*/
protected void updateAttributes() {
if (getClassMapping().isXmlTransient()) {
for (JavaPersistentAttribute contextAttribute : getAttributes()) {
this.removeAttribute(contextAttribute);
}
return;
}
if (getAccessType() == XmlAccessType.PUBLIC_MEMBER) {
this.syncPublicMemberAccessAttributes();
}
else if (getAccessType() == XmlAccessType.FIELD) {
this.syncFieldAccessAttributes();
}
else if (getAccessType() == XmlAccessType.PROPERTY) {
this.syncPropertyAccessAttributes();
}
else if (getAccessType() == XmlAccessType.NONE) {
this.syncNoneAccessAttributes();
}
}
/**
* Sync the attributes for XmlAccessType.PUBLIC_MEMBER
* 1. all public, non-static, non-transient fields (transient modifier, @XmlTransient is brought to the context model)
* 2. all annotated fields that aren't public
* 3. all public getter/setter javabeans pairs
* 4. all annotated methods (some will have a matching getter/setter, some will be standalone)
*/
private void syncPublicMemberAccessAttributes() {
HashSet<JavaPersistentAttribute> contextAttributes = CollectionTools.hashSet(this.getAttributes());
this.syncFieldAttributes(contextAttributes, JavaResourceField.IS_RELEVANT_FOR_PUBLIC_MEMBER_ACCESS);
Collection<JavaResourceMethod> resourceMethods = CollectionTools.hashBag(this.getResourceMethods());
//iterate through all persistable resource method getters
for (JavaResourceMethod getterMethod : this.getResourcePropertyGetters()) {
JavaResourceMethod setterMethod = JavaResourceMethod.SET_METHOD_TRANSFORMER.transform(getterMethod);
if (includePublicMemberAccessProperty(getterMethod, setterMethod)) {
boolean match = false;
for (Iterator<JavaPersistentAttribute> stream = contextAttributes.iterator(); stream.hasNext();) {
JavaPersistentAttribute contextAttribute = stream.next();
if (contextAttribute.isFor(getterMethod, setterMethod)) {
match = true;
contextAttribute.update();
stream.remove();
break;
}
}
if (!match) {
this.addAttribute(this.buildProperty(getterMethod, setterMethod));
}
resourceMethods.remove(getterMethod);
resourceMethods.remove(setterMethod);
}
}
this.syncRemainingResourceMethods(contextAttributes, resourceMethods);
}
/**
* Initialize the attributes for XmlAccessType.FIELD
* 1. all non-transient fields
* 2. all annotated methods getters/setters
*/
private void syncFieldAccessAttributes() {
HashSet<JavaPersistentAttribute> contextAttributes = CollectionTools.hashSet(this.getAttributes());
this.syncFieldAttributes(contextAttributes, JavaResourceField.IS_RELEVANT_FOR_FIELD_ACCESS);
this.syncAnnotatedPropertyAttributes(contextAttributes);
}
/**
* Initialize the attributes for XmlAccessType.PROPERTY
* 1. all getter/setter javabeans pairs
* 2. all annotated fields
* 3. all annotated methods getters/setters that don't have a matching pair
*/
private void syncPropertyAccessAttributes() {
HashSet<JavaPersistentAttribute> contextAttributes = CollectionTools.hashSet(this.getAttributes());
this.syncFieldAttributes(contextAttributes, JavaResourceAnnotatedElement.IS_ANNOTATED);
Collection<JavaResourceMethod> resourceMethods = CollectionTools.hashBag(this.getResourceMethods());
//iterate through all resource methods searching for persistable getters
for (JavaResourceMethod getterMethod : this.getResourcePropertyGetters()) {
JavaResourceMethod setterMethod = JavaResourceMethod.SET_METHOD_TRANSFORMER.transform(getterMethod);
if (this.includeProperty(getterMethod, setterMethod)) {
boolean match = false;
for (Iterator<JavaPersistentAttribute> stream = contextAttributes.iterator(); stream.hasNext();) {
JavaPersistentAttribute contextAttribute = stream.next();
if (contextAttribute.isFor(getterMethod, setterMethod)) {
match = true;
contextAttribute.update();
stream.remove();
break;
}
}
if (!match) {
this.addAttribute(this.buildProperty(getterMethod, setterMethod));
}
}
resourceMethods.remove(getterMethod);
resourceMethods.remove(setterMethod);
}
this.syncRemainingResourceMethods(contextAttributes, resourceMethods);
}
/**
* Initialize the attributes for XmlAccessType.NONE
* 1. all annotated fields
* 2. all annotated methods getters/setters (some will have a matching getter/setter, some will be standalone)
*/
private void syncNoneAccessAttributes() {
HashSet<JavaPersistentAttribute> contextAttributes = CollectionTools.hashSet(this.getAttributes());
this.syncFieldAttributes(contextAttributes, JavaResourceAnnotatedElement.IS_ANNOTATED);
this.syncAnnotatedPropertyAttributes(contextAttributes);
}
private void syncAnnotatedPropertyAttributes(HashSet<JavaPersistentAttribute> contextAttributes) {
Collection<JavaResourceMethod> resourceMethods = CollectionTools.hashBag(this.getResourceMethods());
//iterate through all resource methods searching for persistable getters
for (JavaResourceMethod getterMethod : this.getResourcePropertyGetters()) {
JavaResourceMethod setterMethod = JavaResourceMethod.SET_METHOD_TRANSFORMER.transform(getterMethod);
if (getterMethod.isAnnotated() || (setterMethod != null && setterMethod.isAnnotated())) {
boolean match = false;
for (Iterator<JavaPersistentAttribute> stream = contextAttributes.iterator(); stream.hasNext();) {
JavaPersistentAttribute contextAttribute = stream.next();
if (contextAttribute.isFor(getterMethod, setterMethod)) {
match = true;
contextAttribute.update();
stream.remove();
break;
}
}
if (!match) {
this.addAttribute(this.buildProperty(getterMethod, setterMethod));
}
}
resourceMethods.remove(getterMethod);
resourceMethods.remove(setterMethod);
}
this.syncRemainingResourceMethods(contextAttributes, resourceMethods);
}
private void syncFieldAttributes(HashSet<JavaPersistentAttribute> contextAttributes, Predicate<? super JavaResourceField> filter) {
for (JavaResourceField resourceField : this.getResourceFields(filter)) {
boolean match = false;
for (Iterator<JavaPersistentAttribute> stream = contextAttributes.iterator(); stream.hasNext(); ) {
JavaPersistentAttribute contextAttribute = stream.next();
if (contextAttribute.isFor(resourceField)) {
match = true;
contextAttribute.update();
stream.remove();
break;
}
}
if (!match) {
// added elements are sync'ed during construction or will be
// updated during the next "update" (which is triggered by
// their addition to the model)
this.addAttribute(this.buildField(resourceField));
}
}
}
private void syncRemainingResourceMethods(HashSet<JavaPersistentAttribute> contextAttributes, Collection<JavaResourceMethod> resourceMethods) {
//iterate through remaining resource methods and search for those that are annotated.
//all getter methods will already be used.
for (JavaResourceMethod resourceMethod : resourceMethods) {
if (resourceMethod.isAnnotated()) {
boolean match = false;
//annotated setter(or other random method) with no corresponding getter, bring into context model for validation purposes
for (Iterator<JavaPersistentAttribute> stream = contextAttributes.iterator(); stream.hasNext();) {
JavaPersistentAttribute contextAttribute = stream.next();
if (contextAttribute.isFor(null, resourceMethod)) {
match = true;
contextAttribute.update();
stream.remove();
break;
}
}
if (!match) {
this.addAttribute(this.buildProperty(null, resourceMethod));
}
}
}
// remove any leftover context attributes
for (JavaPersistentAttribute contextAttribute : contextAttributes) {
this.removeAttribute(contextAttribute);
}
}
// ********** validation **********
@Override
public void validate(List<IMessage> messages, IReporter reporter) {
super.validate(messages, reporter);
for (JaxbPersistentAttribute attribute : getAttributes()) {
attribute.validate(messages, reporter);
}
}
@Override
public TextRange getValidationTextRange() {
return getClassMapping().getValidationTextRange();
}
}