blob: 44478a2b8aaf50ad494ab16b0ac795c22b41e6f5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.mylyn.docs.intent.client.compiler;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.common.util.Monitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.AbstractRuntimeCompilationException;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.CompilationErrorType;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.CompilationException;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.InvalidReferenceException;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.InvalidValueException;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.PackageNotFoundResolveException;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.PackageRegistrationException;
import org.eclipse.mylyn.docs.intent.client.compiler.errors.ResolveException;
import org.eclipse.mylyn.docs.intent.client.compiler.generator.modelgeneration.ModelingUnitGenerator;
import org.eclipse.mylyn.docs.intent.client.compiler.generator.modelgeneration.StructuralFeatureGenerator;
import org.eclipse.mylyn.docs.intent.client.compiler.generator.modellinking.ModelingUnitLinkResolver;
import org.eclipse.mylyn.docs.intent.client.compiler.utils.IntentCompilerInformationHolder;
import org.eclipse.mylyn.docs.intent.client.compiler.validator.GeneratedElementValidator;
import org.eclipse.mylyn.docs.intent.core.compiler.UnresolvedContributionHolder;
import org.eclipse.mylyn.docs.intent.core.compiler.UnresolvedReferenceHolder;
import org.eclipse.mylyn.docs.intent.core.document.UnitInstruction;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ModelingUnit;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ModelingUnitInstructionReference;
import org.eclipse.mylyn.docs.intent.core.modelingunit.ResourceDeclaration;
/**
* Modeling Unit Compiler : generate the elements described in modeling units and register those elements in
* an information Handler ; also in charged of handling compilation errors if the described models are
* incorrect.
*
* @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a>
*/
public class ModelingUnitCompiler {
/**
* Compilation mode : compiling modeling units defining EPackages only.
*/
private static final boolean EPACKAGE_DECLARATION_ONLY = true;
/**
* Compilation mode : compiling all the other modeling units (mutual exclusion with the previous mode).
*/
private static final boolean ALL_MODELING_UNITS_EXCEPT_EPACKAGES_DECLARATION = false;
/**
* The ModelingUnitGenerator used to generate the element described in one
* org.eclipse.mylyn.docs.intent.core.modelingunit.
*/
private final ModelingUnitGenerator modelingUnitGenerator;
/**
* The entity used to hold informations about compilation.
*/
private final IntentCompilerInformationHolder informationHolder;
/**
* The linkResolver used to resolved the links in modelingUnits.
*/
private final ModelingUnitLinkResolver linkResolver;
/**
* The repository registry containing the modeling units to compile (and define available EPackages).
*/
private EPackage.Registry packageRegistry;
/**
* The progressMonitor to use for compilation ; if canceled, the compilation will stop immediately.
*/
private Monitor progressMonitor;
/**
* ModelingUnitCompiler constructor.
*
* @param packageRegistry
* the repository package registry
* @param linkResolver
* the linkResolver used to resolved the links in modelingUnits
* @param informationHolder
* the entity used to hold informations about compilation
* @param progressMonitor
* the progressMonitor to use for compilation
*/
public ModelingUnitCompiler(EPackage.Registry packageRegistry, ModelingUnitLinkResolver linkResolver,
IntentCompilerInformationHolder informationHolder, Monitor progressMonitor) {
this.packageRegistry = packageRegistry;
this.informationHolder = informationHolder;
this.modelingUnitGenerator = new ModelingUnitGenerator(linkResolver, informationHolder,
progressMonitor);
this.linkResolver = linkResolver;
this.progressMonitor = progressMonitor;
}
/**
* Compile the given modelingUnits, compiling EPackages declaration first, and then other elements.
*
* @param modelingUnits
* the modeling units to compile
* @return the informationHolder containing all the needed informations (generated elements, errors,
* mapping to resources).
*/
public IntentCompilerInformationHolder compile(List<ModelingUnit> modelingUnits) {
if (!progressMonitor.isCanceled()) {
// Step 0 : initialization of the information Holder
informationHolder.initialize();
}
if (!progressMonitor.isCanceled()) {
// Step 1 : compiling modeling units defining EPackages
compileAllWithMode(modelingUnits, EPACKAGE_DECLARATION_ONLY);
}
if (!progressMonitor.isCanceled()) {
// Step 2 : compiling all the other modeling units
compileAllWithMode(modelingUnits, ALL_MODELING_UNITS_EXCEPT_EPACKAGES_DECLARATION);
}
if (!progressMonitor.isCanceled()) {
// Step 3 : handle the unresolved contribution instructions
for (String unresolvedName : informationHolder.getAllUnresolvedContributionsNames()) {
// For each contribution instruction, we generate a compilationStatus
for (UnresolvedContributionHolder unresolvedContributionHolder : informationHolder
.getUnresolvedContributions(unresolvedName)) {
if (!unresolvedContributionHolder.isResolved()) {
informationHolder
.registerCompilationExceptionAsCompilationStatus(new CompilationException(
unresolvedContributionHolder.getReferencedContribution(),
CompilationErrorType.INVALID_REFERENCE_ERROR,
"The element "
+ unresolvedName
+ " cannot be resolved. This contribution instruction will be ignored. "));
}
}
}
}
return IntentCompilerInformationHolder.getInstance();
}
/**
* Compile the given modeling units and register the generated objects into the informationHolder.
*
* @param modelingUnits
* the modelingUnits to compile
* @param generateOnlyEPackages
* indicates if we only consider EPackages
*/
protected void compileAllWithMode(List<ModelingUnit> modelingUnits, boolean generateOnlyEPackages) {
// Step 1 : initialization.
modelingUnitGenerator.clearResourceDeclarations();
// Step 2.1 : Compilation of each org.eclipse.mylyn.docs.intent.core.modelingunit contained in the
// list (without resolving links)
for (ModelingUnit modelingUnitToCompile : modelingUnits) {
if (!progressMonitor.isCanceled()) {
this.compileModelingUnit(modelingUnitToCompile, generateOnlyEPackages);
}
}
// Step 2.3 : link Resolving
if (!progressMonitor.isCanceled()) {
resolveLinks();
}
// Step 2.4 : we associate each generated object in the given resource
if (!progressMonitor.isCanceled()) {
if (!generateOnlyEPackages) {
mapResourceDeclarationToGeneratedObjects();
}
}
// Step 2.5 : Validation
if (!progressMonitor.isCanceled()) {
validateGeneratedElements(generateOnlyEPackages);
}
// TODO Handle compilation Time.
}
/**
* Maps the resource declarations detected by the generator to the generatedElements ; if invalid
* reference are found, register those errors as compilationStatus.
*/
protected void mapResourceDeclarationToGeneratedObjects() {
// For each declared resource
List<ResourceDeclaration> resourcesDeclarations = modelingUnitGenerator.getResourceDeclarations();
for (ResourceDeclaration resource : resourcesDeclarations) {
if (resource.getContent().isEmpty()) {
informationHolder.addResource(resource);
}
// For each reference to a generated Object
for (ModelingUnitInstructionReference newContainedElementRefrence : resource.getContent()) {
// We resolve this reference
try {
EObject newContainedElement = linkResolver.resolveReferenceInElementList(resource, null,
newContainedElementRefrence.getIntentHref());
// and add it to the resource mapping of the informationHolder
informationHolder.addResourceToGeneratedElementMapping(resource, newContainedElement);
} catch (InvalidReferenceException e) {
// If the reference cannot be resolved, we register a new compilation status.
informationHolder
.registerCompilationExceptionAsCompilationStatus(new CompilationException(
resource, CompilationErrorType.INVALID_REFERENCE_ERROR, e.getMessage()));
}
}
}
}
/**
* Generates and return the elements described in the given modeling Unit, and register errors in the
* described model as compilationStatus.
*
* @param modelingUnitToCompile
* the modeling Unit to inspect
* @param generateOnlyEPackages
* indicates if we only consider EPackages
* @return a list containing the elements described in the given modeling Unit
*/
protected List<EObject> compileModelingUnit(ModelingUnit modelingUnitToCompile,
boolean generateOnlyEPackages) {
List<EObject> generatedObjects = new ArrayList<EObject>();
AbstractRuntimeCompilationException thrownException = null;
CompilationErrorType compilationErrorType = null;
informationHolder.setCurrentImportedPackages(getImportedPackages(modelingUnitToCompile,
generateOnlyEPackages));
try {
modelingUnitGenerator.setGenerateOnlyEPackages(generateOnlyEPackages);
modelingUnitGenerator.generate(modelingUnitToCompile);
return generatedObjects;
} catch (ResolveException e) {
thrownException = e;
} catch (PackageNotFoundResolveException e) {
thrownException = e;
compilationErrorType = CompilationErrorType.PACKAGE_NOT_FOUND_ERROR;
} catch (PackageRegistrationException e) {
thrownException = e;
compilationErrorType = CompilationErrorType.PACKAGE_REGISTRATION_ERROR;
} catch (InvalidReferenceException e) {
thrownException = e;
compilationErrorType = CompilationErrorType.INVALID_REFERENCE_ERROR;
} catch (InvalidValueException e) {
thrownException = e;
compilationErrorType = CompilationErrorType.INVALID_VALUE_ERROR;
}
CompilationException compilationException = new CompilationException(
thrownException.getInvalidInstruction(), compilationErrorType, thrownException.getMessage());
compilationException.setStackTrace(thrownException.getStackTrace());
return generatedObjects;
}
/**
* Returns the packages imported by the given modelingUnit.
*
* @param modelingUnitToCompile
* the modelingUnit to inspect
* @param generateOnlyEPackages
* indicates if the compiler is currently generating EPackages only
* @return the packages imported by the given modelingUnit
*/
protected List<String> getImportedPackages(ModelingUnit modelingUnitToCompile,
boolean generateOnlyEPackages) {
// TODO define a priority between EPackages to consider
List<String> importedPackages = new ArrayList<String>();
importedPackages.add(EcorePackage.eINSTANCE.getNsURI());
for (String ePackage : packageRegistry.keySet()) {
importedPackages.add(ePackage);
}
return importedPackages;
}
/**
* Resolves the unresolvedReference (registered in the InformationHolder) using a linkResolver.
*/
protected void resolveLinks() {
for (EObject elementContainingUnresolvedReference : informationHolder.getCurrentCreatedElements()) {
for (Iterator<UnresolvedReferenceHolder> iterator = informationHolder
.getUnresolvedReferencesByGeneratedElement(elementContainingUnresolvedReference)
.iterator(); iterator.hasNext();) {
UnresolvedReferenceHolder referenceHolder = iterator.next();
// This list will contains the resolved value of the reference
List<Object> referenceValue = new ArrayList<Object>();
try {
try {
EObject referencedElement = linkResolver.resolveReferenceInElementList(
referenceHolder.getInstructionContainer(),
// should be referenceHolder.getConcernedFeature().eClass()
null, referenceHolder.getTextualReference());
referenceValue.add(referencedElement);
} catch (InvalidReferenceException e) {
// If the link to resolve is not an instance,
// we can use the imported package to resolve it
referenceValue.add(linkResolver.resolveEClassifierUsingPackage(
referenceHolder.getInstructionContainer(),
informationHolder.getCurrentImportedPackages(),
referenceHolder.getTextualReference()));
}
StructuralFeatureGenerator.setFeatureValueInElement(
referenceHolder.getInstructionContainer(), elementContainingUnresolvedReference,
referenceHolder.getConcernedFeature(), referenceValue);
} catch (InvalidValueException e) {
// If the reference cannot be resolved with both ways
// we register a compilation status
informationHolder
.registerCompilationExceptionAsCompilationStatus(new CompilationException(e
.getInvalidInstruction(), CompilationErrorType.INVALID_REFERENCE_ERROR, e
.getMessage()));
iterator.remove();
} catch (ResolveException e) {
// If the reference cannot be resolved with both ways
// we register a compilation status
informationHolder
.registerCompilationExceptionAsCompilationStatus(new CompilationException(e
.getInvalidInstruction(), CompilationErrorType.INVALID_REFERENCE_ERROR, e
.getMessage()));
iterator.remove();
}
}
}
}
/**
* Validate the generated Elements and create a Compilation Status if the generation Failed.
*
* @param validateOnlyEPackages
* if true, only EPackages are validated / registered. Otherwise EPackages are ignored
*/
private void validateGeneratedElements(boolean validateOnlyEPackages) {
for (EObject generatedElement : Sets.newLinkedHashSet(informationHolder.getCurrentCreatedElements())) {
if (validateOnlyEPackages && generatedElement instanceof EPackage) {
EPackage ePackage = (EPackage)generatedElement;
UnitInstruction instanciation = informationHolder
.getInstanciationInstructionByCreatedElement(generatedElement);
if (!validateGeneratedElement(generatedElement)) {
linkResolver.registerAsInvalidPackage(instanciation, ePackage);
} else {
linkResolver.registerInPackageRegistry(instanciation, ePackage);
}
} else {
validateGeneratedElement(generatedElement);
}
}
}
/**
* Validate the generated Elements and create a Compilation Status if the generation Failed.
*
* @param generatedElement
* the element to validate
* @return true if the element is valid
*/
private boolean validateGeneratedElement(EObject generatedElement) {
UnitInstruction instanciation = informationHolder
.getInstanciationInstructionByCreatedElement(generatedElement);
if (instanciation != null) {
GeneratedElementValidator validator = new GeneratedElementValidator(instanciation,
generatedElement);
Diagnostic diagnostic;
boolean hasErrors = false;
try {
diagnostic = validator.validate();
informationHolder.registerDiagnosticAsCompilationStatusList(generatedElement, diagnostic);
} catch (CompilationException e) {
informationHolder.registerCompilationExceptionAsCompilationStatus(e);
hasErrors = true;
}
return !hasErrors;
}
return false;
}
}