/*******************************************************************************
* Copyright (c) 2007, 2014 Borland Software Corporation and others.
*
* 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:
* Borland Software Corporation - initial API and implementation
* Alex Paperno - bugs 267917
* Christopher Gerking - bug 427237
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.runtime.project;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.m2m.internal.qvt.oml.QvtMessage;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalUtil;
import org.eclipse.m2m.internal.qvt.oml.common.MdaException;
import org.eclipse.m2m.internal.qvt.oml.compiler.CompiledUnit;
import org.eclipse.m2m.internal.qvt.oml.compiler.QvtCompilerOptions;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImperativeOperation;
import org.eclipse.m2m.internal.qvt.oml.expressions.ImportKind;
import org.eclipse.m2m.internal.qvt.oml.expressions.MappingParameter;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModelParameter;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModelType;
import org.eclipse.m2m.internal.qvt.oml.expressions.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModuleImport;
import org.eclipse.m2m.internal.qvt.oml.expressions.OperationalTransformation;
import org.eclipse.m2m.internal.qvt.oml.expressions.VarParameter;
import org.eclipse.m2m.internal.qvt.oml.runtime.QvtRuntimePlugin;
import org.eclipse.m2m.internal.qvt.oml.runtime.project.QvtTransformation.TransformationParameter;
import org.eclipse.m2m.internal.qvt.oml.runtime.project.config.EMFType;
import org.eclipse.m2m.internal.qvt.oml.runtime.project.config.QvtConfigurationProperty;
import org.eclipse.m2m.internal.qvt.oml.stdlib.ConversionUtils;
import org.eclipse.ocl.types.VoidType;
import org.eclipse.osgi.util.NLS;
public abstract class QvtModule {
private QvtCompilerOptions myQvtCompilerOptions;
protected QvtModule() {
}
public abstract CompiledUnit getUnit() throws MdaException;
/**
* Obtain QVTO Module instance with at most semantic warnings.
* <br>
* IO errors and syntax errors are wrapped with MdaException.
* Semantic errors are thrown as MdaException (see MdaException.getStatus(), org.eclipse.core.runtime.MultiStatus),
* this can be turned off with QvtCompilerOptions.setModuleWithErrorAllowed()
*
* @return org.eclipse.m2m.internal.qvt.oml.expressions.Module instance
* @throws MdaException
*/
public abstract Module getModule() throws MdaException;
public abstract ResourceSet getResourceSet();
public abstract void cleanup();
public List<TransformationParameter> getParameters() throws MdaException {
Module module = getModule();
if(module instanceof OperationalTransformation == false) {
return Collections.emptyList();
}
OperationalTransformation transfModule = (OperationalTransformation) module;
ImperativeOperation mainMethod = QvtOperationalParserUtil.getMainOperation(module);
if(!transfModule.isIsBlackbox() && mainMethod == null) {
return Collections.emptyList();
}
List<TransformationParameter> transfParams = new ArrayList<TransformationParameter>(transfModule.getModelParameter().size());
for (ModelParameter modelParam : transfModule.getModelParameter()) {
MappingParameter refinedParam = findMainParameter(mainMethod, modelParam);
transfParams.add(createTransfParam(modelParam, refinedParam));
}
if (transfParams.isEmpty() && mainMethod != null) {
for (EParameter mainParam : mainMethod.getEParameters()) {
transfParams.add(createTransfParam((MappingParameter) mainParam));
}
for (VarParameter mainParam : mainMethod.getResult()) {
if (mainParam.getEType() instanceof VoidType<?>) {
continue;
}
transfParams.add(createTransfParam((MappingParameter) mainParam));
}
}
return transfParams;
}
public QvtCompilerOptions getQvtCompilerOptions() {
return myQvtCompilerOptions;
}
public void setQvtCompilerOptions(QvtCompilerOptions options) {
myQvtCompilerOptions = options;
}
private TransformationParameter createTransfParam(final MappingParameter mainParam) {
return new TransformationParameter() {
public DirectionKind getDirectionKind() {
if (mainParam.getKind() == org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind.IN) {
return DirectionKind.IN;
}
if (mainParam.getKind() == org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind.OUT) {
return DirectionKind.OUT;
}
return DirectionKind.INOUT;
}
public String getEntryName() {
return mainParam.getName() != null ? mainParam.getName() : ""; //$NON-NLS-1$
}
public EClassifier getEntryType() {
return mainParam.getEType();
}
public List<EPackage> getMetamodels() {
EObject rootContainer = EcoreUtil.getRootContainer(mainParam.getEType());
if (rootContainer instanceof EPackage) {
return Collections.singletonList((EPackage) rootContainer);
}
return Collections.emptyList();
}
public String getModelTypeName() {
if (getMetamodels().isEmpty()) {
return ""; //$NON-NLS-1$
}
return getMetamodels().get(0).getName();
}
public String getName() {
return getEntryName();
}
};
}
private TransformationParameter createTransfParam(final ModelParameter modelParam, final MappingParameter refinedParam) {
return new TransformationParameter() {
public DirectionKind getDirectionKind() {
if (modelParam.getKind() == org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind.IN) {
return DirectionKind.IN;
}
if (modelParam.getKind() == org.eclipse.m2m.internal.qvt.oml.expressions.DirectionKind.OUT) {
return DirectionKind.OUT;
}
return DirectionKind.INOUT;
}
public String getEntryName() {
return refinedParam != null ? refinedParam.getName() : ""; //$NON-NLS-1$
}
public EClassifier getEntryType() {
return refinedParam != null ? refinedParam.getEType() : null;
}
public List<EPackage> getMetamodels() {
return ((ModelType)modelParam.getType()).getMetamodel();
}
public String getModelTypeName() {
if (modelParam.getEType() instanceof ModelType) {
return ((ModelType) modelParam.getEType()).getName();
}
return ""; //$NON-NLS-1$
}
public String getName() {
return modelParam.getName();
}
};
}
private MappingParameter findMainParameter(ImperativeOperation mainMethod, ModelParameter modelParam) {
if (mainMethod != null) {
for (EParameter mainParam : mainMethod.getEParameters()) {
if (((MappingParameter) mainParam).getExtent() == modelParam) {
return (MappingParameter) mainParam;
}
}
for (VarParameter mainParam : mainMethod.getResult()) {
if (((MappingParameter) mainParam).getExtent() == modelParam) {
return (MappingParameter) mainParam;
}
}
}
return null;
}
/**
* Collects all the configuration properties used by this module
* including the ones declared by imported modules
* @return LinkedHashSet of properties
* @throws MdaException as {@link #getModule()} does
*/
public Set<QvtConfigurationProperty> getConfigurationProperties() throws MdaException {
CompiledUnit unit = getUnit();
if (unit == null) {
return Collections.<QvtConfigurationProperty>emptySet();
}
Set<Module> moduleSet = new HashSet<Module>();
for (Module nextModule : unit.getModules()) {
collectImports(nextModule, ImportKind.EXTENSION, moduleSet);
}
Set<QvtConfigurationProperty> propSet = new LinkedHashSet<QvtConfigurationProperty>();
for (Module m : moduleSet) {
collectProperties(m, propSet);
}
return propSet;
}
/**
* @return A string representation uniquely identifying the given QVT module.
*/
@Override
public abstract String toString();
protected void checkModuleErrors(CompiledUnit unit) throws MdaException {
// List<QvtMessage> errors = new ArrayList<QvtMessage>();
// FIXME
// no need to check imports recursively as the compiled modules reports error on bad imports
// => for now, get only the directly owned errors, including "import has compilation error!"
// TransformationUtil.getErrors(unit, errors);
List<QvtMessage> errors = unit.getErrors();
if(errors.isEmpty()) {
return;
}
MultiStatus multistatus = new MultiStatus(QvtRuntimePlugin.ID, 1, NLS.bind(Messages.TransformationUtil_ParseTransformationError, toString(), errors.size()), null);
for (QvtMessage msg : errors) {
IStatus status = new Status(msg.getSeverity() == QvtMessage.SEVERITY_ERROR ? IStatus.ERROR : IStatus.WARNING,
QvtRuntimePlugin.ID, 1, msg.toString(), null);
multistatus.merge(status);
}
throw new MdaException(multistatus);
}
/**
* Collects all the imported modules (including ones imported implicitly), non-recursive,
* cyclic-import compatible
* @param module - root module
* @param kind - the kind of import to select or <code>null</code> is any kind is acceptable
* @param moduleSet - resulting set
*/
private void collectImports(Module module, ImportKind kind, Set<Module> moduleSet) {
assert module != null;
// WFS on imports graph
Queue<Module> queue = new LinkedList<Module>();
queue.offer(module);
while (!queue.isEmpty()) {
Module m = queue.poll();
moduleSet.add(m);
for (ModuleImport imp : m.getModuleImport()) {
if(kind == null || kind == imp.getKind()) {
Module element = imp.getImportedModule();
if (!moduleSet.contains(element)) {
moduleSet.add(element);
queue.offer(element);
}
}
}
}
}
/**
* Collects configuration properties declared by a mapping module
* @param module - a mapping module
* @param propSet - a set to put properties to
*/
private void collectProperties(Module module, Set<QvtConfigurationProperty> propSet) {
for (EStructuralFeature property : module.getConfigProperty()) {
EClassifier type = property.getEType();
if (type instanceof EDataType) {
ConversionUtils.setupConversionDelegate(type);
propSet.add(
new QvtConfigurationProperty(
property.getName(),
new EMFType((EDataType) type)
)
);
} else if (QvtOperationalUtil.isPrimitiveType(type)) {
propSet.add(
new QvtConfigurationProperty(
property.getName(),
new QvtPrimitiveType(type)
)
);
}
}
}
}