/*
* 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.core.extension;
import static org.teiid.designer.core.ModelerCore.Util;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EObject;
import org.teiid.core.designer.properties.PropertyDefinition;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.core.designer.util.I18nUtil;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.util.ModelObjectClassNameVisitor;
import org.teiid.designer.core.util.ModelResourceContainerFactory;
import org.teiid.designer.core.util.ModelVisitorProcessor;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.extension.ExtensionPlugin;
import org.teiid.designer.extension.definition.ModelExtensionAssistant;
import org.teiid.designer.extension.definition.ModelExtensionDefinition;
import org.teiid.designer.extension.definition.ModelObjectExtensionAssistant;
import org.teiid.designer.extension.properties.ModelExtensionPropertyDefinition;
import org.teiid.designer.metamodels.core.Annotation;
/**
* The <code>EmfModelObjectExtensionAssistant</code> is a model extension assistant that knows how to work with {@link EObject}s.
*
* @since 8.0
*/
public class EmfModelObjectExtensionAssistant extends ModelObjectExtensionAssistant {
private static final String PREFIX = I18nUtil.getPropertyPrefix(EmfModelObjectExtensionAssistant.class);
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#cleanupModelResourceMEDs(java.lang.Object)
* @throws Exception if error trying to detect and remove incompatible MEDs from model resource
*/
@Override
public void cleanupModelResourceMEDs(Object modelResource) throws Exception {
CoreArgCheck.isNotNull(modelResource, "modelResource is null"); //$NON-NLS-1$
CoreArgCheck.isInstanceOf(ModelResource.class, modelResource);
ModelResource theModelResource = (ModelResource)modelResource;
Collection<String> supportedNamespaces = ModelExtensionUtils.getSupportedNamespaces(theModelResource);
for( String namespacePrefix : supportedNamespaces ) {
ModelExtensionAssistant assistant = ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(namespacePrefix);
ModelExtensionDefinition med = assistant.getModelExtensionDefinition();
if( assistant instanceof ModelObjectExtensionAssistant ) {
if (!med.extendsMetamodelUri(theModelResource.getModelAnnotation().getPrimaryMetamodelUri())) {
// remove MED
((ModelObjectExtensionAssistant)assistant).removeModelExtensionDefinition(modelResource);
} else if (!med.getSupportedModelTypes().contains(theModelResource.getModelType()) ) {
// remove MED
((ModelObjectExtensionAssistant)assistant).removeModelExtensionDefinition(modelResource);
}
}
}
}
/**
* @param modelObject the model object (must be either an {@link EObject}, {@link ModelResource}, or an {@link IFile}.
* @return the model resource or <code>null</code> if not found
*/
protected ModelResource getModelResource( Object modelObject ) throws Exception {
ModelResource modelResource = null;
if (modelObject instanceof ModelResource) {
modelResource = (ModelResource)modelObject;
} else if (modelObject instanceof EObject) {
modelResource = ModelerCore.getModelEditor().findModelResource((EObject)modelObject);
} else if (modelObject instanceof IFile) {
modelResource = ModelerCore.getModelEditor().findModelResource((IFile)modelObject);
}
return modelResource;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getOverriddenValue(java.lang.Object, java.lang.String)
* @throws IllegalArgumentException if the model object is not an {@link EObject}, if property is from a different namespace, or
* if the associated property definition can't be found
*/
@Override
public String getOverriddenValue( Object modelObject,
String propId ) throws Exception {
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
Annotation annotation = ModelExtensionUtils.getModelObjectAnnotation((EObject)modelObject, false);
if (annotation != null) {
EMap<String, String> tags = annotation.getTags();
return getOverriddenValue(modelObject, propId, tags.get(propId));
}
// model object is using default value
return null;
}
/**
* @param modelObject the model object whose property value is being requested (cannot be <code>null</code>)
* @param propId the identifier of the property being checked (cannot be <code>null</code> or empty)
* @param currentValue the model object's current property value (can be <code>null</code> or empty)
* @return the overridden value (never <code>null</code> but can be empty)
* @throws Exception if there is a problem obtaining the property value
*/
protected String getOverriddenValue( Object modelObject,
String propId,
String currentValue ) throws Exception {
// make sure the property definition is found
PropertyDefinition propDefn = getPropertyDefinition(modelObject, propId);
if (propDefn == null) {
return null;
}
String defaultValue = propDefn.getDefaultValue();
// don't add if value equals default value
if ((CoreStringUtil.isEmpty(currentValue) && CoreStringUtil.isEmpty(defaultValue))
|| CoreStringUtil.equals(currentValue, defaultValue)) {
currentValue = null;
}
// value is different than default value
return currentValue;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getOverriddenValues(java.lang.Object)
* @throws IllegalArgumentException if the model object is not an {@link EObject}
*/
@Override
public Properties getOverriddenValues( Object modelObject ) throws Exception {
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
Properties props = new Properties();
if (supportsMyNamespace(modelObject)) {
Annotation annotation = ModelExtensionUtils.getModelObjectAnnotation((EObject)modelObject, false);
if (annotation != null) {
ModelExtensionDefinition med = getModelExtensionDefinition();
EMap<String, String> tags = annotation.getTags();
for (String propId : tags.keySet()) {
// only get properties of my namespace
if (ModelExtensionPropertyDefinition.Utils.isExtensionPropertyId(propId, med)) {
try {
String overridenValue = getOverriddenValue(modelObject, propId, tags.get(propId));
if (!CoreStringUtil.isEmpty(overridenValue)) {
props.put(propId, overridenValue);
}
} catch (Exception e) {
Util.log(e);
}
}
}
}
}
return props;
}
/**
* @param modelObject the model object whose property definition is being requested (cannot be <code>null</code>)
* @param propId the property identifier whose property definition is being requested (cannot be <code>null</code> or empty)
* @return the property definition or <code>null</code> if not found
* @throws Exception if accessing a property definition or value
*/
protected ModelExtensionPropertyDefinition getPropertyDefinition( Object modelObject,
String propId ) throws Exception {
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
// make sure right namespace
if (ModelExtensionPropertyDefinition.Utils.isExtensionPropertyId(propId, getModelExtensionDefinition())) {
final Collection<String> modelTypes = getModelExtensionDefinition().getSupportedModelTypes();
boolean modelTypeSupported = false;
// if supported model types is empty then all model types are supported
if (modelTypes.isEmpty()) {
modelTypeSupported = true;
} else {
final ModelResource modelResource = getModelResource(modelObject);
if (modelResource != null) {
String modelTypeLiteral = modelResource.getModelType().getLiteral();
modelTypeSupported = modelTypes.contains(modelTypeLiteral);
}
}
if (modelTypeSupported) {
return getModelExtensionDefinition().getPropertyDefinition(modelObject.getClass().getName(), propId);
}
}
return null;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getPropertyDefinitions(java.lang.Object)
*/
@Override
public Collection<ModelExtensionPropertyDefinition> getPropertyDefinitions(Object modelObject) throws Exception {
if (!(modelObject instanceof EObject) || !supportsMyNamespace(modelObject)) {
return Collections.emptyList();
}
String metaclassName = modelObject.getClass().getName();
ModelExtensionDefinition med = getModelExtensionDefinition();
Collection<ModelExtensionPropertyDefinition> propDefinitions = new ArrayList<ModelExtensionPropertyDefinition>();
for (ModelExtensionPropertyDefinition potentialPropDefn : med.getPropertyDefinitions(metaclassName)) {
// let assistant determine if property definition is valid for model object
ModelExtensionPropertyDefinition propDefn = getPropertyDefinition(modelObject, potentialPropDefn.getId());
if (propDefn != null) {
propDefinitions.add(propDefn);
}
}
return propDefinitions;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getPropertyValue(java.lang.Object,
* java.lang.String)
*/
@Override
public String getPropertyValue( Object modelObject,
String propId ) throws Exception {
String value = getOverriddenValue(modelObject, propId);
// if no overridden value then return default value if property definition exists
if (CoreStringUtil.isEmpty(value)) {
PropertyDefinition propDefn = getPropertyDefinition(modelObject, propId);
return ((propDefn == null) ? null : propDefn.getDefaultValue());
}
return value;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getPropertyValues(java.lang.Object)
* @throws IllegalArgumentException if the model object is not an {@link EObject}
*/
@Override
public Properties getPropertyValues( Object modelObject ) throws Exception {
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
Properties props = new Properties();
if (supportsMyNamespace(modelObject)) {
// get properties with overridden values
props = getOverriddenValues(modelObject);
// add properties using default value
for (ModelExtensionPropertyDefinition propDefn : getModelExtensionDefinition().getPropertyDefinitions(modelObject.getClass()
.getName())) {
if (!props.containsKey(propDefn.getId())) {
String defaultValue = propDefn.getDefaultValue();
// add only if there is a default value
if (!CoreStringUtil.isEmpty(defaultValue)) {
props.put(propDefn.getId(), defaultValue);
}
}
}
}
return props;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getSupportedNamespaces(java.lang.Object)
*/
@Override
public Collection<String> getSupportedNamespaces( Object modelObject ) throws Exception {
ModelResource modelResource = getModelResource(modelObject);
if (modelResource == null) {
return Collections.emptyList();
}
return ModelExtensionUtils.getSupportedNamespaces(modelResource);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#hasExtensionProperties(java.io.File)
*/
@Override
public boolean hasExtensionProperties( File file ) throws Exception {
IWorkspace workspace = ModelerCore.getWorkspace();
IPath location = Path.fromOSString(file.getAbsolutePath());
IFile modelFile = workspace.getRoot().getFileForLocation(location);
if ((modelFile != null) && ModelUtil.isModelFile(modelFile.getFullPath())) {
ModelResource modelResource = getModelResource(modelFile);
if (modelResource != null) {
return ModelExtensionUtils.getSupportedNamespaces(modelResource).contains(getNamespacePrefix());
}
}
// none found
return false;
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#hasExtensionProperties(java.lang.Object)
*/
@Override
public boolean hasExtensionProperties( Object modelObject ) throws Exception {
return !getPropertyValues(modelObject).isEmpty();
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#isModelExtensionDefinitionRelated(java.lang.Object)
* @throws IllegalArgumentException if the model object is not an {@link EObject}
*/
@Override
public boolean isModelExtensionDefinitionRelated( Object modelObject ) throws Exception {
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
return ModelExtensionUtils.isModelExtensionDefinitionRelated((EObject)modelObject);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#getModelExtensionDefinition(java.lang.Object)
*/
@Override
public ModelExtensionDefinition getModelExtensionDefinition( Object modelObject ) throws Exception {
ModelResource modelResource = getModelResource(modelObject);
if (modelResource == null) {
return null;
}
return ModelExtensionUtils.getModelExtensionDefinition(this, modelResource);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#removeModelExtensionDefinition(java.lang.Object)
*/
@Override
public void removeModelExtensionDefinition( Object modelObject ) throws Exception {
ModelResource modelResource = getModelResource(modelObject);
if (modelResource != null) {
ModelExtensionUtils.removeModelExtensionDefinition(modelResource, getNamespacePrefix());
// find model objects with classes that match the extended metaclasses in the MED
ModelExtensionDefinition definition = getModelExtensionDefinition();
String[] metaclasses = definition.getExtendedMetaclasses();
ModelObjectClassNameVisitor visitor = new ModelObjectClassNameVisitor(Arrays.asList(metaclasses));
ModelVisitorProcessor processor = new ModelVisitorProcessor(visitor, ModelVisitorProcessor.MODE_VISIBLE_CONTAINMENTS);
processor.walk(modelResource, ModelVisitorProcessor.DEPTH_INFINITE);
// remove overridden properties
for (EObject eObject : visitor.getResult()) {
Annotation annotation = ModelExtensionUtils.getModelObjectAnnotation(eObject, false);
if (annotation != null) {
String metaclassName = eObject.getClass().getName();
for (ModelExtensionPropertyDefinition propDefn : definition.getPropertyDefinitions(metaclassName)) {
removeProperty(eObject, propDefn.getId());
}
}
}
}
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#removeProperty(java.lang.Object, java.lang.String)
*/
@Override
public void removeProperty( Object modelObject,
String propId ) throws Exception {
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
// make sure property defined in MED
if (!ModelExtensionPropertyDefinition.Utils.isExtensionPropertyId(propId, getModelExtensionDefinition())) {
throw new Exception(Util.getString(PREFIX + "wrongNamespacePrefix", propId, getNamespacePrefix())); //$NON-NLS-1$
}
ModelExtensionUtils.removeProperty((EObject)modelObject, propId, true);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#saveModelExtensionDefinition(java.lang.Object)
*/
@Override
public void saveModelExtensionDefinition( Object modelObject ) throws Exception {
ModelResource modelResource = getModelResource(modelObject);
assert (modelResource != null) : "Model resource should not be null"; //$NON-NLS-1$
boolean update = supportsMyNamespace(modelObject); // see if this is an update
ModelExtensionUtils.updateModelExtensionDefinition(modelResource, getModelExtensionDefinition());
// remove any properties that are no longer supported by the MED
if (update) {
// ModelExtensionRegistry registry = ExtensionPlugin.getInstance().getRegistry();
//
// for (Object eObject : modelResource.getEObjects()) {
// assert eObject instanceof EObject;
// // TODO remove orphaned properties from model object. getOverriddenValues only returns registered properties
// Properties overriddenProperties = getOverriddenValues(eObject);
//
// if (!overriddenProperties.isEmpty()) {
// String metaclassName = eObject.getClass().getName();
//
// for (String propId : overriddenProperties.stringPropertyNames()) {
// // remove property if it doesn't exist any more
// if (registry.getPropertyDefinition(metaclassName, propId) == null) {
// removeProperty(modelObject, propId);
// }
// }
// }
// }
}
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#setPropertyValue(java.lang.Object,
* java.lang.String, java.lang.String)
*/
@Override
public void setPropertyValue( Object modelObject,
String propId,
String newValue ) throws Exception {
PropertyDefinition propDefn = getPropertyDefinition(modelObject, propId);
if (propDefn == null) {
throw new Exception(Util.getString(PREFIX + "propertyDefinitionNotFound", propId)); //$NON-NLS-1$
}
CoreArgCheck.isInstanceOf(EObject.class, modelObject);
boolean valueIsDefault = CoreStringUtil.equals(propDefn.getDefaultValue(), newValue);
Annotation annotation = ModelExtensionUtils.getModelObjectAnnotation((EObject)modelObject, true);
// default values are not saved in the model object annotation
if (valueIsDefault) {
if (annotation != null) {
annotation.getTags().removeKey(propId);
// delete the model object annotation if no more properties AND there is no description
if (annotation.getTags().isEmpty() && annotation.getDescription() == null) {
ModelResourceContainerFactory.deleteAnnotation(annotation);
}
}
} else {
// value is not equal to default so create annotation if necessary
if (annotation == null) {
annotation = ModelExtensionUtils.getModelObjectAnnotation((EObject)modelObject, true);
}
// only save if new value is different than old value
String oldValue = (String)annotation.getTags().get(propId);
// only set value if different than the default
if (!CoreStringUtil.equals(oldValue, newValue)) {
// remove key if empty value
if (CoreStringUtil.isEmpty(newValue)) {
annotation.getTags().removeKey(propId);
} else {
annotation.getTags().put(propId, newValue);
}
}
}
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#supportsMedOperation(java.lang.String,
* java.lang.Object)
*/
@Override
public boolean supportsMedOperation( String proposedOperationName,
Object context ) {
CoreArgCheck.isNotEmpty(proposedOperationName, "proposedOperationName is empty"); //$NON-NLS-1$
try {
if (context instanceof IFile) {
ModelResource modelResource = getModelResource(context);
if (modelResource == null) {
return false;
}
Collection<String> modelTypes = getModelExtensionDefinition().getSupportedModelTypes();
// if supported model types is empty then all all model types are supported
if (!modelTypes.isEmpty()) {
String modelTypeLiteral = modelResource.getModelType().getLiteral();
if (!modelTypes.contains(modelTypeLiteral)) {
return false; // model type not supported
}
}
if (MedOperations.ADD_MED_TO_MODEL.equals(proposedOperationName)) {
// model must NOT be currently supporting namespace
if (!supportsMyNamespace(modelResource)) {
// make sure model is of right metamodel URI to be extended by the MED
String metamodelUri = getModelExtensionDefinition().getMetamodelUri();
// MED does not have URI yet
if (CoreStringUtil.isEmpty(metamodelUri)) {
return false;
}
// check to make sure MED metamodel URI is the same as model's
return metamodelUri.equals(modelResource.getPrimaryMetamodelUri());
}
// model already has namespace stored so can't be added again
return false;
}
if (MedOperations.DELETE_MED_FROM_MODEL.equals(proposedOperationName)) {
return supportsMyNamespace(modelResource); // model must be currently supporting namespace
}
}
} catch (Exception e) {
Util.log(e);
return false;
}
return super.supportsMedOperation(proposedOperationName, context);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.extension.definition.ModelObjectExtensionAssistant#supportsMyNamespace(java.lang.Object)
*/
@Override
public boolean supportsMyNamespace( Object modelObject ) throws Exception {
ModelResource modelResource = getModelResource(modelObject);
if (modelResource == null) {
return false;
}
return ModelExtensionUtils.isSupportedNamespace(modelResource, getNamespacePrefix());
}
@Override
public boolean supportsProperty(Object modelObject, String propId)
throws Exception {
return getPropertyDefinition(modelObject, propId) != null;
}
}