blob: 60376a77c24431c2ac599662c5719d06146d250b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 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 v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Borland Software Corporation - initial API and implementation
* Alex Paperno - bug 416584
* Christopher Gerking - bug 537041
*******************************************************************************/
package org.eclipse.m2m.internal.qvt.oml.compiler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicDiagnostic;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.Diagnostician;
import org.eclipse.m2m.internal.qvt.oml.QvtMessage;
import org.eclipse.m2m.internal.qvt.oml.ast.binding.ASTBindingHelper;
import org.eclipse.m2m.internal.qvt.oml.ast.binding.IModuleSourceInfo;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QVTOTypeResolver;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnvFactory;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalFileEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalModuleEnv;
import org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalStdLibrary;
import org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalParserUtil;
import org.eclipse.m2m.internal.qvt.oml.cst.CSTFactory;
import org.eclipse.m2m.internal.qvt.oml.cst.UnitCS;
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.Module;
import org.eclipse.m2m.internal.qvt.oml.expressions.ModuleImport;
import org.eclipse.ocl.Environment;
import org.eclipse.ocl.utilities.ASTNode;
public class CompiledUnit {
private List<String> fQname;
private URI fUri;
private List<QvtMessage> fAllProblems;
private List<CompiledUnit> fImports;
private List<QvtOperationalModuleEnv> moduleEnvs;
private ResourceSet fResourceSet;
// FIXME - add compilationUnit CST element
UnitCS fUnitCST;
CompiledUnit(List<String> qualifiedName, URI uri, List<? extends QvtOperationalModuleEnv> modules, ResourceSet resourceSet) {
if(qualifiedName == null || modules == null || uri == null) {
throw new IllegalArgumentException();
}
this.fUri = uri;
this.fQname = qualifiedName;
this.moduleEnvs = new ArrayList<QvtOperationalModuleEnv>(modules);
this.fResourceSet = resourceSet;
ArrayList<QvtMessage> problems = new ArrayList<QvtMessage>();
this.fAllProblems = problems;
for (QvtOperationalModuleEnv next : modules) {
fAllProblems.addAll(next.getAllProblemMessages());
}
if(!problems.isEmpty()) {
problems.trimToSize();
}
}
public static ResourceSetImpl createResourceSet() {
return createResourceSet(null);
}
static ResourceSetImpl createResourceSet(EPackage.Registry registry) {
ResourceSetImpl rs = new ResourceSetImpl();
if(registry != null) {
rs.setPackageRegistry(registry);
}
rs.setURIResourceMap(new PlatformNamespaceUriResourceMap(rs));
return rs;
}
public CompiledUnit(URI xmiURI, ResourceSet resourceSet) {
this(resourceSet.getResource(xmiURI, true), new HashMap<URI, CompiledUnit>());
}
CompiledUnit(Resource unitXMIResource, Map<URI, CompiledUnit> unitMap) {
this.fUri = unitXMIResource.getURI();
this.moduleEnvs = new LinkedList<QvtOperationalModuleEnv>();
this.fAllProblems = new ArrayList<QvtMessage>();
this.fImports = new UniqueEList<CompiledUnit>();
this.fResourceSet = unitXMIResource.getResourceSet();
unitMap.put(fUri, this);
if (!BlackboxUnitResolver.isBlackboxUnitURI(fUri)) {
computeImports(unitXMIResource, unitMap);
}
// Note: Environment initialization should be optional as it make sense to be used
// only in case this unit is to be a compilation dependency to parsed CST Unit
for(EObject rootElement : new ArrayList<EObject>(unitXMIResource.getContents())) {
if(false == rootElement instanceof Module) {
continue;
}
Module nextModule = (Module) rootElement;
QvtOperationalModuleEnv nextModuleEnv = null;
if (BlackboxUnitResolver.isBlackboxUnitURI(fUri)) {
nextModuleEnv = (QvtOperationalModuleEnv) ASTBindingHelper.resolveEnvironment(nextModule);
}
else {
nextModuleEnv = QvtOperationalEnvFactory.INSTANCE.createModuleEnvironment(nextModule);
QVTOTypeResolver typeResolver = nextModuleEnv.getTypeResolver();
// FIXME -
// 1) workaround to make Environment available with the module for
// non-transformation execution context
// 2) move this initialization code to QVTEnvironment related classes
ASTBindingHelper.createCST2ASTBinding(CSTFactory.eINSTANCE.createLibraryCS(), nextModule, nextModuleEnv);
for (EOperation nextOper : nextModule.getEOperations()) {
if(nextOper instanceof ImperativeOperation) {
ImperativeOperation imperativeOper = (ImperativeOperation) nextOper;
EClassifier ctxType = QvtOperationalParserUtil.getContextualType(imperativeOper);
if(ctxType != null) {
typeResolver.resolveAdditionalOperation(ctxType, imperativeOper);
}
}
}
for (CompiledUnit importedUnit : getCompiledImports()) {
for (QvtOperationalModuleEnv importedEnv : importedUnit.moduleEnvs) {
nextModuleEnv.addImport(ImportKind.ACCESS, importedEnv);
}
}
}
this.moduleEnvs.add(nextModuleEnv);
}
//validate(unitXMIResource, this.fAllProblems);
}
/*
* TODO - EcoreValidator is quite strict concerning operation signatures etc,
* causing a lot of errors. Next, override those validateXXX operations, eventually
* the OCLValidator if too strict for the QVTO purposes.
* Move to a separate validation related class
*/
@SuppressWarnings("unused")
private void validate(Resource res, List<QvtMessage> problems) {
BasicDiagnostic diagnostics = new BasicDiagnostic();
IModuleSourceInfo sourceInfo = null;
if(res instanceof ExeXMIResource) {
sourceInfo = ((ExeXMIResource) res).getSourceInfo();
}
Diagnostician diagnostician = new Diagnostician();
Map<Object, Object> context = new HashMap<Object, Object>();
context.put(Environment.class, new QvtOperationalEnvFactory().createEnvironment());
for (EObject root : res.getContents()) {
diagnostician.validate(root, diagnostics, context);
List<Diagnostic> children = diagnostics.getChildren();
if(!children.isEmpty()) {
for (Diagnostic diagnostic : children) {
int resourceSeverity = diagnostic.getSeverity();
// FIXME - support other severity levels in QVT, or use Diagnostic too?
int messageSeverity = (resourceSeverity == Diagnostic.ERROR) ?
QvtMessage.SEVERITY_ERROR : QvtMessage.SEVERITY_WARNING;
int offset = -1;
int len = 0;
List<?> data = diagnostic.getData();
if(!data.isEmpty()) {
Object obj = data.get(0);
if(obj instanceof ASTNode) {
ASTNode astNode = (ASTNode) obj;
offset = astNode.getStartPosition();
len = astNode.getEndPosition() - offset;
}
}
int lineNum = -1;
if(offset >= 0 && sourceInfo != null) {
lineNum = sourceInfo.getLineNumberProvider().getLineNumber(offset);
}
QvtMessage problem = new QvtMessage(
diagnostic.getMessage(), messageSeverity, offset,
len, lineNum);
addProblem(problem);
}
}
}
}
private void computeImports(Resource unitResource, Map<URI, CompiledUnit> unitMap) {
for (EObject next : unitResource.getContents()) {
if(next instanceof Module) {
computeImports((Module) next, unitMap);
}
}
}
private void computeImports(Module module, Map<URI, CompiledUnit> unitMap) {
EList<ModuleImport> imports = module.getModuleImport();
for (ModuleImport nextImport : imports) {
Module importedModule = nextImport.getImportedModule();
if(importedModule != null && importedModule.eResource() != null) {
Resource importedResource = importedModule.eResource();
URI importedResourceURI = importedResource.getURI();
// Note: skip QVT Standard Library as it's imported implicitly
if(!importedResourceURI.equals(QvtOperationalStdLibrary.NS_URI)) {
CompiledUnit importedUnit = unitMap.get(importedResourceURI);
if(importedUnit == null) {
importedUnit = new CompiledUnit(importedResource, unitMap);
unitMap.put(importedResourceURI, importedUnit);
}
fImports.add(importedUnit);
}
} else {
throw new IllegalArgumentException("imported module must be in a resource: " + String.valueOf(importedModule)); //$NON-NLS-1$
}
}
}
void addProblem(QvtMessage problem) {
if(problem == null) {
throw new IllegalArgumentException();
}
fAllProblems.add(problem);
}
void setImports(List<CompiledUnit> imports) {
this.fImports = imports;
}
public String getName() {
return fQname.get(fQname.size() - 1);
}
public List<QvtOperationalModuleEnv> getModuleEnvironments() {
if (moduleEnvs.size() == 1 && moduleEnvs.get(0) instanceof QvtOperationalFileEnv) {
return ((QvtOperationalFileEnv) moduleEnvs.get(0)).getInnerEnvironments();
}
else {
return moduleEnvs;
}
}
public List<QvtMessage> getErrors() {
List<QvtMessage> errors = new ArrayList<QvtMessage>();
for (QvtMessage nextMessage : fAllProblems) {
if(nextMessage.getSeverity() == QvtMessage.SEVERITY_ERROR) {
errors.add(nextMessage);
}
}
return errors;
}
public List<QvtMessage> getWarnings() {
List<QvtMessage> warnings = new ArrayList<QvtMessage>();
for (QvtMessage nextMessage : fAllProblems) {
if(nextMessage.getSeverity() == QvtMessage.SEVERITY_WARNING) {
warnings.add(nextMessage);
}
}
return warnings;
}
public List<CompiledUnit> getCompiledImports() {
return fImports != null ? fImports : Collections.<CompiledUnit>emptyList();
}
public List<Module> getModules() {
List<Module> modules = new ArrayList<Module>(moduleEnvs.size());
for (QvtOperationalModuleEnv next : moduleEnvs) {
if (next instanceof QvtOperationalFileEnv) {
for (QvtOperationalModuleEnv inner : ((QvtOperationalFileEnv)next).getInnerEnvironments()) {
if(inner.getModuleContextType() != null) {
modules.add(inner.getModuleContextType());
}
}
}
if(next.getModuleContextType() != null) {
modules.add(next.getModuleContextType());
}
}
return modules;
}
public URI getURI() {
return fUri;
}
public UnitCS getUnitCST() {
return fUnitCST;
}
public List<QvtMessage> getProblems() {
return fAllProblems;
}
public ResourceSet getResourceSet() {
return fResourceSet;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof CompiledUnit) {
CompiledUnit another = (CompiledUnit) obj;
return fUri.equals(another.fUri);
}
return super.equals(obj);
}
@Override
public int hashCode() {
return fUri.hashCode();
}
@Override
public String toString() {
return fUri.toString();
}
}