/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.ui.properties;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.provider.IItemPropertyDescriptor;
import org.eclipse.emf.edit.provider.IItemPropertySource;
import org.eclipse.emf.edit.provider.ItemPropertyDescriptor;
import org.eclipse.emf.edit.ui.provider.PropertySource;
import org.eclipse.emf.mapping.Mapping;
import org.eclipse.emf.mapping.MappingHelper;
import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.types.DatatypeManager;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.metamodels.diagram.PresentationEntity;
import org.teiid.designer.metamodels.relational.Column;
import org.teiid.designer.metamodels.relational.ProcedureParameter;
import org.teiid.designer.metamodels.transformation.SqlAlias;
import org.teiid.designer.ui.UiConstants;
import org.teiid.designer.ui.properties.extension.ModelExtensionPropertySource;
import org.teiid.designer.ui.properties.udp.ExtensionPropertySource;
import org.teiid.designer.ui.viewsupport.DatatypeUtilities;
import org.teiid.designer.ui.viewsupport.ModelUtilities;
import org.teiid.designer.ui.viewsupport.StatusBarUpdater;
/**
* ModelObjectPropertySource is a specialization of PropertySource.
* The class creates {@link ModelObjectPropertyDescriptor} instances rather than
* EMF PropertyDescriptors. It also provides Metamodel Extension properties
* along with the core metamodel properties.
*
* @since 8.0
*/
public class ModelObjectPropertySource extends PropertySource {
public static final String SET = UiConstants.Util.getString("ModelObjectPropertySource.undoSetPrefix") + ' '; //$NON-NLS-1$
public static final String RESET = UiConstants.Util.getString("ModelObjectPropertySource.undoResetPrefix") + ' '; //$NON-NLS-1$
// ===================================
// Instance variables
private boolean isReadOnlyType = false;
private boolean isPrimaryMetamodelObject = false;
/**
* This is the extension metamodel.
*/
private ExtensionPropertySource extensionDelegate;
/**
* This is the new model extension framework (non-metamodel).
*/
private ModelExtensionPropertySource modelExtensionDelegate;
/**
* Collection of transient descriptors.
* @since 4.3
*/
private ITransientPropertyDescriptor[] transientDescriptors = new ITransientPropertyDescriptor[0];
/**
* Map with keys of property identifiers and values of transient descriptors.
* @since 4.3
*/
private Map idDescriptorMap;
private boolean initialized = false;
// ===================================
// Constructors
public ModelObjectPropertySource(Object object, IItemPropertySource itemPropertySource) {
super(object, itemPropertySource);
isReadOnlyType = (object instanceof PresentationEntity)
|| ( object instanceof Mapping )
|| ( object instanceof MappingHelper )
|| ( object instanceof SqlAlias );
if (object instanceof EObject ) {
final EObject eObject = (EObject)object;
if( ModelUtil.isLockedSourceObject(eObject) ) {
isReadOnlyType = true;
} else {
// Check for read-only resource
ModelResource mr = ModelUtilities.getModelResource(eObject);
if( mr != null && mr.isReadOnly() ) {
isReadOnlyType = true;
} else {
final DatatypeManager dtMgr = ModelerCore.getDatatypeManager(eObject,true);
if ( dtMgr.isSimpleDatatype(eObject) ) {
try {
isReadOnlyType = dtMgr.isBuiltInDatatype(eObject);
} catch (Exception e) {
UiConstants.Util.log(IStatus.ERROR, e, e.getClass().getName());
isReadOnlyType = false;
}
}
}
}
this.modelExtensionDelegate = new ModelExtensionPropertySource(eObject);
}
extensionDelegate = new ExtensionPropertySource((EObject) object);
}
// ===================================
// Methods
public boolean canDisplayExtensionProperties() {
return isPrimaryMetamodelObject;
}
/* (non-Javadoc)
* Overridden createPropertyDescriptor() method. Return a {@link ModelObjectPropertyDescriptor}.
*/
@Override
protected IPropertyDescriptor createPropertyDescriptor(
IItemPropertyDescriptor itemPropertyDescriptor) {
return new ModelObjectPropertyDescriptor(object, itemPropertyDescriptor);
}
/* (non-Javadoc)
* Overridden to intercept propertyId of ExtensionPropertyDescriptor and set the value on the
* extension object rather than the model object.
* @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue()
*/
@Override
public void setPropertyValue(Object propertyId, Object value) {
try {
if ( this.object != null && this.object instanceof EObject ) {
boolean started = ModelerCore.startTxn(SET + propertyId.toString(), this);
boolean succeeded = false;
try {
// see if non-metamodel model extension property
if (this.modelExtensionDelegate.isExtensionProperty(propertyId)) {
this.modelExtensionDelegate.setPropertyValue(propertyId, value);
succeeded = true;
return;
}
// see if model extension metamodel proeprty
if ( extensionDelegate.isExtensionProperty(propertyId) ) {
extensionDelegate.setPropertyValue(propertyId, value);
succeeded = true;
return;
} else if ( propertyId instanceof String ) {
//swj: workaround for non-standard handling of XSD enumeration values
final IItemPropertyDescriptor itemPropertyDescriptor = super.itemPropertySource.getPropertyDescriptor(this.object, propertyId);
final Object genericFeature = itemPropertyDescriptor.getFeature(object);
if (genericFeature instanceof EStructuralFeature) {
final EStructuralFeature feature = (EStructuralFeature)genericFeature;
final EClassifier eType = feature.getEType();
// if the feature is many and the type is EEnum, then the falue should be EList
if ( feature.isMany() && eType instanceof EEnum && ! ( value instanceof EList ) ) {
// non-standard value - set the value directly on the itemPropertyDescriptor
itemPropertyDescriptor.setPropertyValue(this.object, value);
succeeded = true;
return;
}
}
// the user edited a real property
final IItemPropertyDescriptor descriptor = this.itemPropertySource.getPropertyDescriptor(this.object, propertyId);
if( isValidPropertyChange((EObject)object, propertyId, value) ) {
if ( descriptor instanceof ItemPropertyDescriptor ) {
boolean success = ModelerCore.getModelEditor().setPropertyValue((EObject)object, value, (ItemPropertyDescriptor) descriptor);
if (success) {
succeeded = true;
return;
}
} else if (genericFeature instanceof EStructuralFeature) {
boolean success = ModelerCore.getModelEditor().setPropertyValue((EObject)object, value, genericFeature);
if (success) {
succeeded = true;
return;
}
descriptor.setPropertyValue(this.object, value);
succeeded = true;
} else {
descriptor.setPropertyValue(this.object, value);
succeeded = true;
}
}
}
} finally {
if ( started ) {
if ( succeeded ) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
} catch (Exception e) {
String objectString = object.toString();
if ( object instanceof EObject ) {
StatusBarUpdater.formatEObjectMessage((EObject) object);
}
String[] strings = new String[] { e.getClass().getName(), propertyId.toString(), value.toString(), objectString };
String message = UiConstants.Util.getString("ModelObjectPropertySource.setPropertyValueError", (Object[])strings); //$NON-NLS-1$
UiConstants.Util.log(IStatus.ERROR, e, message);
}
}
private boolean isValidPropertyChange(EObject eObject, Object propertyId, Object value) {
if( eObject instanceof Column ) {
if( propertyId instanceof String && ((String)propertyId).equals("length") && value instanceof Integer ) { //$NON-NLS-1$
Integer intValue = (Integer)value;
if( !DatatypeUtilities.canSetLength(eObject, intValue.intValue()) ) {
if( eObject instanceof Column ) {
// Throw up a Message dialog that you cannot change a char type length
UiConstants.Util.log(IStatus.WARNING, UiConstants.Util.getString("ModelObjectPropertySource.cannotChangeColumnCharLengthWarning")); //$NON-NLS-1$
} else {
// Throw up a Message dialog that you cannot change a char type length
UiConstants.Util.log(IStatus.WARNING, UiConstants.Util.getString("ModelObjectPropertySource.cannotChangeParameterCharLengthWarning")); //$NON-NLS-1$
}
return false;
}
}
} else if( eObject instanceof ProcedureParameter) {
if( propertyId instanceof String && ((String)propertyId).equals("length") && value instanceof Integer ) { //$NON-NLS-1$
Integer intValue = (Integer)value;
if( !DatatypeUtilities.canSetLength(eObject, intValue.intValue()) ) {
if( eObject instanceof Column ) {
// Throw up a Message dialog that you cannot change a char type length
UiConstants.Util.log(IStatus.WARNING, UiConstants.Util.getString("ModelObjectPropertySource.cannotChangeColumnCharLengthWarning")); //$NON-NLS-1$
} else {
// Throw up a Message dialog that you cannot change a char type length
UiConstants.Util.log(IStatus.WARNING, UiConstants.Util.getString("ModelObjectPropertySource.cannotChangeParameterCharLengthWarning")); //$NON-NLS-1$
}
return false;
}
}
}
return true;
}
private IPropertyDescriptor[] getAllDescriptors() {
IPropertyDescriptor[] emfDescriptors = super.getPropertyDescriptors();
IPropertyDescriptor[] extDescriptors = extensionDelegate.getPropertyDescriptors();
this.transientDescriptors = getTransientPropertyDescriptors(this.object);
IPropertyDescriptor[] modelExtensionDescriptors = this.modelExtensionDelegate.getPropertyDescriptors();
// keep track of the property IDs contributed by the transient descriptors
if (this.transientDescriptors.length > 0) {
this.idDescriptorMap = new HashMap();
for (int i = 0; i < this.transientDescriptors.length; ++i) {
this.idDescriptorMap.put(this.transientDescriptors[i].getId(), this.transientDescriptors[i]);
}
}
// combine all descriptors
IPropertyDescriptor[] result = new IPropertyDescriptor[emfDescriptors.length + extDescriptors.length
+ transientDescriptors.length + modelExtensionDescriptors.length];
int resultIndex = 0;
// add EMF descriptors
if (emfDescriptors.length != 0) {
for (int i = 0; i < emfDescriptors.length; ++i, ++resultIndex) {
result[resultIndex] = emfDescriptors[i];
}
}
// add model extension metamodel descriptors
if (extDescriptors.length != 0) {
for (int i = 0; i < extDescriptors.length; ++i, ++resultIndex) {
result[resultIndex] = extDescriptors[i];
}
}
// add transient descriptors
if (transientDescriptors.length != 0) {
for (int i = 0; i < transientDescriptors.length; ++i, ++resultIndex) {
result[resultIndex] = transientDescriptors[i];
}
}
// add non-metamodel model extension descriptors
if (modelExtensionDescriptors.length != 0) {
for (int i = 0; i < modelExtensionDescriptors.length; ++i, ++resultIndex) {
result[resultIndex] = modelExtensionDescriptors[i];
}
}
initialized = true;
return result;
}
/**
* Obtains the <code>ITransientPropertyDescriptor</code>s for the specified object.
* @param theObject the object whose transient property descriptors are being requested
* @return the transient property descriptors (never <code>null</code>)
* @since 4.3
*/
private ITransientPropertyDescriptor[] getTransientPropertyDescriptors(Object theObject) {
List temp = new ArrayList();
// create transient descriptor for the Object URI property (would be nice if this was an extension pt)
ITransientPropertyDescriptor id = new ObjectUriPropertyDescriptor();
if (id.supports(theObject)) {
id.setObject(theObject);
temp.add(id);
}
// create transient descriptor for the Namespace property
id = new NamespacePropertyDescriptor();
if (id.supports(theObject)) {
id.setObject(theObject);
temp.add(id);
}
// create transient descriptor for the Enumerated Values property
id = new EnumeratedValuesPropertyDescriptor();
if (id.supports(theObject)) {
id.setObject(theObject);
temp.add(id);
}
// return appropriate descriptors
ITransientPropertyDescriptor[] result;
if (!temp.isEmpty()) {
result = (ITransientPropertyDescriptor[])temp.toArray(new ITransientPropertyDescriptor[temp.size()]);
} else {
result = new ITransientPropertyDescriptor[0];
}
return result;
}
/* (non-Javadoc)
* Overridden to add the metamodel extension properties.
* @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors()
*/
@Override
public IPropertyDescriptor[] getPropertyDescriptors() {
// create an array of descriptors from the primary model object
IPropertyDescriptor[] descriptors = getAllDescriptors();
if ( isReadOnlyType ) {
// wrap each descriptor in a ReadOnlyPropertyDescriptor
IPropertyDescriptor[] result = new IPropertyDescriptor[descriptors.length];
for ( int i=0 ; i<descriptors.length ; ++i ) {
result[i] = new ReadOnlyPropertyDescriptor(descriptors[i], ReadOnlyPropertyDescriptor.READ_ONLY_PROPERTY);
}
descriptors = result;
}
return descriptors;
}
/* (non-Javadoc)
* Overridden to intercept propertyId of ExtensionPropertyDescriptor and get the value from the
* extension object rather than the model object.
* @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object)
*/
@Override
public Object getPropertyValue(Object propertyId) {
// see if non-metamodel model extension property
if ( this.modelExtensionDelegate.isExtensionProperty(propertyId) ) {
return this.modelExtensionDelegate.getPropertyValue(propertyId);
}
// see if metamodel model extension property
if ( extensionDelegate.isExtensionProperty(propertyId) ) {
return extensionDelegate.getPropertyValue(propertyId);
}
// check if transient descriptors can handle that property
ITransientPropertyDescriptor descriptor = null;
if( !initialized ) getPropertyDescriptors();
if (idDescriptorMap != null) {
descriptor = (ITransientPropertyDescriptor) this.idDescriptorMap.get(propertyId);
} // endif
if (descriptor != null) {
Object propValue = null;
boolean requiredStart = ModelerCore.startTxn(false,false,"Get Property Value",this); //$NON-NLS-1$
boolean succeeded = false;
try {
propValue = descriptor.getPropertyValue();
succeeded = true;
} finally {
// If we start txn, commit it
if(requiredStart) {
if(succeeded) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
return propValue;
}
Object result = null;
// ensure that EMF can provide a property descriptor for this property id
if ( itemPropertySource.getPropertyDescriptor(object, propertyId) != null ) {
try {
result = super.getPropertyValue(propertyId);
} catch (Exception e) {
String objectString = object.toString();
if ( object instanceof EObject ) {
StatusBarUpdater.formatEObjectMessage((EObject) object);
}
String[] strings = new String[] { e.getClass().getName(), propertyId.toString(), objectString };
String message = UiConstants.Util.getString("ModelObjectPropertySource.getPropertyValueError", (Object[])strings); //$NON-NLS-1$
UiConstants.Util.log(IStatus.ERROR, e, message);
}
}
return result;
}
/* (non-Javadoc)
* @see org.eclipse.ui.views.properties.IPropertySource#resetPropertyValue(java.lang.Object)
*/
@Override
public void resetPropertyValue(Object propertyId) {
try {
if ( this.object != null && this.object instanceof EObject ) {
boolean started = ModelerCore.startTxn(SET + propertyId.toString(), this);
boolean succeeded = false;
try {
// see if non-metamodel contributed extension property
if ( this.modelExtensionDelegate.isExtensionProperty(propertyId) ) {
this.modelExtensionDelegate.resetPropertyValue(propertyId);
succeeded = true;
return;
}
// see if extension metamodel contributed property
if ( extensionDelegate.isExtensionProperty(propertyId) ) {
extensionDelegate.resetPropertyValue(propertyId);
succeeded = true;
return;
} else if ( propertyId instanceof String ) {
// the user edited a real property
final ItemPropertyDescriptor descriptor = (ItemPropertyDescriptor) this.itemPropertySource.getPropertyDescriptor(this.object, propertyId);
descriptor.resetPropertyValue(object);
succeeded = true;
return;
}
} finally {
if ( started ) {
if ( succeeded ) {
ModelerCore.commitTxn();
} else {
ModelerCore.rollbackTxn();
}
}
}
}
} catch (Exception e) {
String objectString = object.toString();
if ( object instanceof EObject ) {
StatusBarUpdater.formatEObjectMessage((EObject) object);
}
String[] strings = new String[] { e.getClass().getName(), propertyId.toString(), objectString };
String message = UiConstants.Util.getString("ModelObjectPropertySource.resetPropertyValueError", (Object[])strings); //$NON-NLS-1$
UiConstants.Util.log(IStatus.ERROR, e, message);
}
}
}