blob: 6b0351e98519549ead56b23abbdaed736bfaa709 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2007 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.content.java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jpt.core.internal.IJpaContentNode;
import org.eclipse.jpt.core.internal.IPersistentAttribute;
import org.eclipse.jpt.core.internal.IPersistentType;
import org.eclipse.jpt.core.internal.ITextRange;
import org.eclipse.jpt.core.internal.ITypeMapping;
import org.eclipse.jpt.core.internal.JpaCorePackage;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaBasicProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaEmbeddedIdProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaEmbeddedProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaIdProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaManyToManyProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaManyToOneProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaNullAttributeMappingProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaOneToManyProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaOneToOneProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaTransientProvider;
import org.eclipse.jpt.core.internal.content.java.mappings.JavaVersionProvider;
import org.eclipse.jpt.core.internal.jdtutility.Attribute;
import org.eclipse.jpt.core.internal.jdtutility.DeclarationAnnotationAdapter;
import org.eclipse.jpt.core.internal.platform.DefaultsContext;
import org.eclipse.jpt.utility.internal.Filter;
import org.eclipse.jpt.utility.internal.iterators.ArrayIterator;
import org.eclipse.jpt.utility.internal.iterators.TransformationIterator;
/**
* <!-- begin-user-doc -->
* A representation of the model object '<em><b>Java Persistent Attribute</b></em>'.
* <!-- end-user-doc -->
*
* <p>
* The following features are supported:
* <ul>
* <li>{@link org.eclipse.jpt.core.internal.content.java.JavaPersistentAttribute#getDefaultMapping <em>Default Mapping</em>}</li>
* <li>{@link org.eclipse.jpt.core.internal.content.java.JavaPersistentAttribute#getSpecifiedMapping <em>Specified Mapping</em>}</li>
* </ul>
* </p>
*
* @see org.eclipse.jpt.core.internal.content.java.JpaJavaPackage#getJavaPersistentAttribute()
* @model kind="class"
* @generated
*/
public class JavaPersistentAttribute extends JavaEObject
implements IPersistentAttribute
{
/**
* The cached value of the '{@link #getDefaultMapping() <em>Default Mapping</em>}' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getDefaultMapping()
* @generated
* @ordered
*/
protected IJavaAttributeMapping defaultMapping;
/**
* The cached value of the '{@link #getSpecifiedMapping() <em>Specified Mapping</em>}' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @see #getSpecifiedMapping()
* @generated
* @ordered
*/
protected IJavaAttributeMapping specifiedMapping;
private Attribute attribute;
// TODO move these to the platform
private IJavaAttributeMappingProvider[] attributeMappingProviders;
/**
* the "null" attribute mapping is used when the attribute is neither
* modified with a mapping annotation nor mapped by a "default" mapping
*/
// TODO move this to the platform
private IJavaAttributeMappingProvider nullAttributeMappingProvider;
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
protected JavaPersistentAttribute() {
super();
}
protected JavaPersistentAttribute(Attribute attribute) {
super();
this.attribute = attribute;
this.attributeMappingProviders = this.buildAttributeMappingProviders();
this.nullAttributeMappingProvider = this.buildNullAttributeMappingProvider();
this.setDefaultMapping(this.nullAttributeMappingProvider.buildMapping(this.attribute));
}
private IJavaAttributeMappingProvider[] buildAttributeMappingProviders() {
List<IJavaAttributeMappingProvider> providers = new ArrayList<IJavaAttributeMappingProvider>();
this.addAttributeMappingProvidersTo(providers);
return providers.toArray(new IJavaAttributeMappingProvider[providers.size()]);
}
/**
* Override this to specify more or different attribute mapping providers.
* The default includes the JPA spec-defined type mappings of
* Basic, Id, OneToOne, OneToMany, ManyToOne, ManyToMany, Embeddable, EmbeddedId.
*/
protected void addAttributeMappingProvidersTo(List<IJavaAttributeMappingProvider> providers) {
providers.add(JavaEmbeddedProvider.instance()); //bug 190344 need to test default embedded before basic
providers.add(JavaBasicProvider.instance());
providers.add(JavaIdProvider.instance());
providers.add(JavaTransientProvider.instance());
providers.add(JavaOneToManyProvider.instance());
providers.add(JavaManyToOneProvider.instance());
providers.add(JavaManyToManyProvider.instance());
providers.add(JavaOneToOneProvider.instance());
providers.add(JavaEmbeddedIdProvider.instance());
providers.add(JavaVersionProvider.instance());
}
protected IJavaAttributeMappingProvider buildNullAttributeMappingProvider() {
return JavaNullAttributeMappingProvider.instance();
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
protected EClass eStaticClass() {
return JpaJavaPackage.Literals.JAVA_PERSISTENT_ATTRIBUTE;
}
/**
* Returns the value of the '<em><b>Default Mapping</b></em>' containment reference.
* <!-- begin-user-doc -->
* <p>
* If the meaning of the '<em>Default Mapping</em>' containment reference isn't clear,
* there really should be more of a description here...
* </p>
* <!-- end-user-doc -->
* @return the value of the '<em>Default Mapping</em>' containment reference.
* @see #setDefaultMapping(IJavaAttributeMapping)
* @see org.eclipse.jpt.core.internal.content.java.JpaJavaPackage#getJavaPersistentAttribute_DefaultMapping()
* @model containment="true" required="true"
* @generated
*/
public IJavaAttributeMapping getDefaultMapping() {
return defaultMapping;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public NotificationChain basicSetDefaultMapping(IJavaAttributeMapping newDefaultMapping, NotificationChain msgs) {
IJavaAttributeMapping oldDefaultMapping = defaultMapping;
defaultMapping = newDefaultMapping;
if (eNotificationRequired()) {
ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING, oldDefaultMapping, newDefaultMapping);
if (msgs == null)
msgs = notification;
else
msgs.add(notification);
}
return msgs;
}
/**
* Sets the value of the '{@link org.eclipse.jpt.core.internal.content.java.JavaPersistentAttribute#getDefaultMapping <em>Default Mapping</em>}' containment reference.
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @param value the new value of the '<em>Default Mapping</em>' containment reference.
* @see #getDefaultMapping()
* @generated
*/
public void setDefaultMappingGen(IJavaAttributeMapping newDefaultMapping) {
if (newDefaultMapping != defaultMapping) {
NotificationChain msgs = null;
if (defaultMapping != null)
msgs = ((InternalEObject) defaultMapping).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING, null, msgs);
if (newDefaultMapping != null)
msgs = ((InternalEObject) newDefaultMapping).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING, null, msgs);
msgs = basicSetDefaultMapping(newDefaultMapping, msgs);
if (msgs != null)
msgs.dispatch();
}
else if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING, newDefaultMapping, newDefaultMapping));
}
/**
* clients do not set the "default" mapping
*/
private void setDefaultMapping(IJavaAttributeMapping defaultMapping) {
this.setDefaultMappingGen(defaultMapping);
}
/**
* Returns the value of the '<em><b>Specified Mapping</b></em>' containment reference.
* <!-- begin-user-doc -->
* <p>
* If the meaning of the '<em>Specified Mapping</em>' containment reference isn't clear,
* there really should be more of a description here...
* </p>
* <!-- end-user-doc -->
* @return the value of the '<em>Specified Mapping</em>' containment reference.
* @see #setSpecifiedMapping(IJavaAttributeMapping)
* @see org.eclipse.jpt.core.internal.content.java.JpaJavaPackage#getJavaPersistentAttribute_SpecifiedMapping()
* @model containment="true" required="true"
* @generated
*/
public IJavaAttributeMapping getSpecifiedMapping() {
return specifiedMapping;
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
public NotificationChain basicSetSpecifiedMapping(IJavaAttributeMapping newSpecifiedMapping, NotificationChain msgs) {
IJavaAttributeMapping oldSpecifiedMapping = specifiedMapping;
specifiedMapping = newSpecifiedMapping;
if (eNotificationRequired()) {
ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING, oldSpecifiedMapping, newSpecifiedMapping);
if (msgs == null)
msgs = notification;
else
msgs.add(notification);
}
return msgs;
}
/**
* Sets the value of the '{@link org.eclipse.jpt.core.internal.content.java.JavaPersistentAttribute#getSpecifiedMapping <em>Specified Mapping</em>}' containment reference.
* <!-- begin-user-doc -->
* clients do not set the "specified" mapping directly;
* call #setMappingKey(String, boolean) instead
* <!-- end-user-doc -->
* @param value the new value of the '<em>Specified Mapping</em>' containment reference.
* @see #getSpecifiedMapping()
* @generated
*/
public void setSpecifiedMappingGen(IJavaAttributeMapping newSpecifiedMapping) {
if (newSpecifiedMapping != specifiedMapping) {
NotificationChain msgs = null;
if (specifiedMapping != null)
msgs = ((InternalEObject) specifiedMapping).eInverseRemove(this, EOPPOSITE_FEATURE_BASE - JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING, null, msgs);
if (newSpecifiedMapping != null)
msgs = ((InternalEObject) newSpecifiedMapping).eInverseAdd(this, EOPPOSITE_FEATURE_BASE - JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING, null, msgs);
msgs = basicSetSpecifiedMapping(newSpecifiedMapping, msgs);
if (msgs != null)
msgs.dispatch();
}
else if (eNotificationRequired())
eNotify(new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING, newSpecifiedMapping, newSpecifiedMapping));
}
/**
* clients do not set the "specified" mapping;
* use #setMappingKey(String)
*/
private void setSpecifiedMapping(IJavaAttributeMapping specifiedMapping) {
this.setSpecifiedMappingGen(specifiedMapping);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
switch (featureID) {
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING :
return basicSetDefaultMapping(null, msgs);
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING :
return basicSetSpecifiedMapping(null, msgs);
}
return super.eInverseRemove(otherEnd, featureID, msgs);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public Object eGet(int featureID, boolean resolve, boolean coreType) {
switch (featureID) {
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING :
return getMapping();
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING :
return getDefaultMapping();
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING :
return getSpecifiedMapping();
}
return super.eGet(featureID, resolve, coreType);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eSet(int featureID, Object newValue) {
switch (featureID) {
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING :
setDefaultMapping((IJavaAttributeMapping) newValue);
return;
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING :
setSpecifiedMapping((IJavaAttributeMapping) newValue);
return;
}
super.eSet(featureID, newValue);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public void eUnset(int featureID) {
switch (featureID) {
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING :
setDefaultMapping((IJavaAttributeMapping) null);
return;
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING :
setSpecifiedMapping((IJavaAttributeMapping) null);
return;
}
super.eUnset(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public boolean eIsSet(int featureID) {
switch (featureID) {
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING :
return getMapping() != null;
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__DEFAULT_MAPPING :
return defaultMapping != null;
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__SPECIFIED_MAPPING :
return specifiedMapping != null;
}
return super.eIsSet(featureID);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public int eBaseStructuralFeatureID(int derivedFeatureID, Class<?> baseClass) {
if (baseClass == IJpaContentNode.class) {
switch (derivedFeatureID) {
default :
return -1;
}
}
if (baseClass == IPersistentAttribute.class) {
switch (derivedFeatureID) {
case JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING :
return JpaCorePackage.IPERSISTENT_ATTRIBUTE__MAPPING;
default :
return -1;
}
}
return super.eBaseStructuralFeatureID(derivedFeatureID, baseClass);
}
/**
* <!-- begin-user-doc -->
* <!-- end-user-doc -->
* @generated
*/
@Override
public int eDerivedStructuralFeatureID(int baseFeatureID, Class<?> baseClass) {
if (baseClass == IJpaContentNode.class) {
switch (baseFeatureID) {
default :
return -1;
}
}
if (baseClass == IPersistentAttribute.class) {
switch (baseFeatureID) {
case JpaCorePackage.IPERSISTENT_ATTRIBUTE__MAPPING :
return JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING;
default :
return -1;
}
}
return super.eDerivedStructuralFeatureID(baseFeatureID, baseClass);
}
public IPersistentType getPersistentType() {
return (IPersistentType) this.eContainer();
}
public IJavaAttributeMapping getMapping() {
return (this.specifiedMapping != null) ? this.specifiedMapping : this.defaultMapping;
}
public String getName() {
return this.attribute.attributeName();
}
public ITypeMapping typeMapping() {
return this.getPersistentType().getMapping();
}
public String mappingKey() {
return this.getMapping().getKey();
}
/**
* return null if there is no "default" mapping for the attribute
*/
public String defaultMappingKey() {
return this.defaultMapping.getKey();
}
/**
* return null if there is no "specified" mapping for the attribute
*/
public String specifiedMappingKey() {
return (this.specifiedMapping == null) ? null : this.specifiedMapping.getKey();
}
// TODO support morphing mappings, i.e. copying common settings over
// to the new mapping; this can't be done in the same was as XmlAttributeMapping
// since we don't know all the possible mapping types
public void setSpecifiedMappingKey(String newKey) {
String oldKey = this.specifiedMappingKey();
if (newKey == oldKey) {
return;
}
IJavaAttributeMapping old = this.getMapping();
if (newKey == null) {
// remove mapping annotation
this.setSpecifiedMapping(null);
this.attribute.removeAnnotation(this.declarationAnnotationAdapterForAttributeMappingKey(oldKey));
}
else {
// add or replace mapping annotation
this.setSpecifiedMapping(this.attributeMappingProviderFor(newKey).buildMapping(this.attribute));
if (oldKey != null) {
this.attribute.removeAnnotation(this.declarationAnnotationAdapterForAttributeMappingKey(oldKey));
}
this.attribute.newMarkerAnnotation(this.declarationAnnotationAdapterForAttributeMappingKey(newKey));
this.specifiedMapping.updateFromJava(getAttribute().astRoot());
}
if (this.eNotificationRequired()) {
this.eNotify(new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING, old, this.getMapping()));
}
}
private DeclarationAnnotationAdapter declarationAnnotationAdapterForAttributeMappingKey(String attributeMappingKey) {
return this.attributeMappingProviderFor(attributeMappingKey).declarationAnnotationAdapter();
}
/**
* throw an exception if the provider is not found
*/
private IJavaAttributeMappingProvider attributeMappingProviderFor(String attributeMappingKey) {
for (IJavaAttributeMappingProvider provider : this.attributeMappingProviders) {
if (provider.key() == attributeMappingKey) {
return provider;
}
}
throw new IllegalArgumentException("Unsupported attribute mapping key: " + attributeMappingKey);
}
public Iterator<String> candidateMappingKeys() {
return new TransformationIterator<IJavaAttributeMappingProvider, String>(new ArrayIterator<IJavaAttributeMappingProvider>(this.attributeMappingProviders)) {
@Override
protected String transform(IJavaAttributeMappingProvider provider) {
return provider.key();
}
};
}
public Object getId() {
return IJavaContentNodes.PERSISTENT_ATTRIBUTE_ID;
}
public Attribute getAttribute() {
return this.attribute;
}
public boolean includes(int offset) {
ITextRange fullTextRange = this.fullTextRange();
if (fullTextRange == null) {
//This happens if the attribute no longer exists in the java.
//The text selection event is fired before the update from java so our
//model has not yet had a chance to update appropriately. The list of
//JavaPersistentAttriubtes is stale at this point. For now, we are trying
//to avoid the NPE, not sure of the ultimate solution to these 2 threads accessing
//our model
return false;
}
return fullTextRange.includes(offset);
}
public ITextRange fullTextRange() {
return this.attribute.textRange();
}
public ITextRange validationTextRange() {
return this.selectionTextRange();
}
public ITextRange selectionTextRange() {
return this.attribute.nameTextRange();
}
public boolean isFor(IMember member) {
return this.attribute.wraps(member);
}
public void updateFromJava(CompilationUnit astRoot) {
// synchronize the "specified" mapping with the Java source
String jpaKey = this.specifiedMappingKey();
IJavaAttributeMappingProvider javaProvider = this.javaAttributeMappingProvider(astRoot);
String javaKey = ((javaProvider == null) ? null : javaProvider.key());
if (javaKey != jpaKey) {
IJavaAttributeMapping old = this.getMapping();
if (javaKey == null) {
// no mapping annotation found in Java source
this.setSpecifiedMapping(null);
}
else {
// the mapping has changed
this.setSpecifiedMapping(javaProvider.buildMapping(this.attribute));
}
if (this.eNotificationRequired()) {
this.eNotify(new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING, old, this.getMapping()));
}
}
// once the "specified" mapping is in place, update it from Java;
// unless it is null, in which case we update the "default" mapping from Java
this.getMapping().updateFromJava(astRoot);
}
/**
* return null if we can't find a mapping annotation on the attribute
*/
private IJavaAttributeMappingProvider javaAttributeMappingProvider(CompilationUnit astRoot) {
for (IJavaAttributeMappingProvider provider : this.attributeMappingProviders) {
if (this.attribute.containsAnnotation(provider.declarationAnnotationAdapter(), astRoot)) {
return provider;
}
}
return null;
}
public String primaryKeyColumnName() {
IJavaAttributeMapping mapping = this.getMapping();
return (mapping == null) ? null : mapping.primaryKeyColumnName();
}
/**
* the mapping might be "default", but it still might be a "null" mapping...
*/
public boolean mappingIsDefault() {
return this.specifiedMapping == null;
}
@Override
public Iterator<String> candidateValuesFor(int pos, Filter<String> filter, CompilationUnit astRoot) {
Iterator<String> result = super.candidateValuesFor(pos, filter, astRoot);
if (result != null) {
return result;
}
return this.getMapping().candidateValuesFor(pos, filter, astRoot);
}
/**
* check to see whether the "default" mapping has changed
*/
public void refreshDefaults(DefaultsContext defaultsContext) {
IJavaAttributeMappingProvider defaultProvider = this.defaultAttributeMappingProvider(defaultsContext);
if (defaultProvider.key() == this.defaultMapping.getKey()) {
return;
}
// the "default" mapping has changed
IJavaAttributeMapping old = this.getMapping();
this.setDefaultMapping(defaultProvider.buildMapping(this.attribute));
this.defaultMapping.updateFromJava(defaultsContext.astRoot());
if (this.eNotificationRequired()) {
this.eNotify(new ENotificationImpl(this, Notification.SET, JpaJavaPackage.JAVA_PERSISTENT_ATTRIBUTE__MAPPING, old, this.getMapping()));
}
}
/**
* return the first(?) provider that can supply a "default" mapping for the attribute;
* return the null provider if we can't find a provider
*/
private IJavaAttributeMappingProvider defaultAttributeMappingProvider(DefaultsContext defaultsContext) {
for (IJavaAttributeMappingProvider provider : this.attributeMappingProviders) {
if (provider.defaultApplies(this.attribute, defaultsContext)) {
return provider;
}
}
return this.nullAttributeMappingProvider;
}
public boolean isOverridableAttribute() {
return this.getMapping().isOverridableAttributeMapping();
}
public boolean isOverridableAssociation() {
return this.getMapping().isOverridableAssociationMapping();
}
public boolean isIdAttribute() {
return this.getMapping().isIdMapping();
}
}