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