| /******************************************************************************* |
| * 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(); |
| } |
| } |