/******************************************************************************* * 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.utils; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import org.eclipse.emf.common.util.BasicEList; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.mylyn.docs.intent.client.compiler.errors.CompilationException; import org.eclipse.mylyn.docs.intent.client.compiler.errors.InvalidValueException; import org.eclipse.mylyn.docs.intent.core.compiler.CompilationInformationHolder; import org.eclipse.mylyn.docs.intent.core.compiler.CompilationMessageType; import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatus; import org.eclipse.mylyn.docs.intent.core.compiler.CompilationStatusManager; import org.eclipse.mylyn.docs.intent.core.compiler.CompilerFactory; import org.eclipse.mylyn.docs.intent.core.compiler.StringToEObjectMap; 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.ContributionInstruction; import org.eclipse.mylyn.docs.intent.core.modelingunit.InstanciationInstruction; import org.eclipse.mylyn.docs.intent.core.modelingunit.ModelingUnit; import org.eclipse.mylyn.docs.intent.core.modelingunit.ModelingUnitInstruction; import org.eclipse.mylyn.docs.intent.core.modelingunit.ResourceDeclaration; /** * Class storing all the informations that can be needed by any entity of the compilation process. * * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> */ public final class IntentCompilerInformationHolder { /** * A {@link Predicate} used to determine wether an Instanciation instruction is valid or not. */ private static final IsValidInstanciationInstructionPredicate IS_VALID_INSTANCIATION_INSTRUCTION_PREDICATE = new IsValidInstanciationInstructionPredicate(); /** * Current instance of the information Holder (singleton). */ private static IntentCompilerInformationHolder currentInstance; /** * The informationHolder used to store informations. */ private CompilationInformationHolder informationHolder; /** * List of the currentImportedPackages. */ private List<String> currentImportedPackages; /** * The {@link CompilationStatusManager} holding status information. */ private CompilationStatusManager statusManager; /** * IntentCompilerInformationHolder constructor. */ private IntentCompilerInformationHolder() { this.informationHolder = CompilerFactory.eINSTANCE.createCompilationInformationHolder(); } /** * Returns the current instance of the information Holder (singleton). * * @return the current instance of the information Holder (singleton) */ public static IntentCompilerInformationHolder getInstance() { if (currentInstance == null) { currentInstance = new IntentCompilerInformationHolder(); } return currentInstance; } /** * Initialize this IntentCompilerInformationHolder by clearing all informations. */ public void initialize() { informationHolder = CompilerFactory.eINSTANCE.createCompilationInformationHolder(); if (statusManager == null) { statusManager = CompilerFactory.eINSTANCE.createCompilationStatusManager(); } else { statusManager.getCompilationStatusList().clear(); statusManager.getModelingUnitToStatusList().clear(); } } public CompilationStatusManager getStatusManager() { return statusManager; } /** * Add the given element to the current created element list. * * @param instruction * the instruction that declared this element * @param createdElement * the element to register */ public void addCreatedElementsToCurrentList(UnitInstruction instruction, EObject createdElement) { if (createdElement != null) { this.getCurrentCreatedElements().add(createdElement); BasicEList<UnitInstruction> unitInstructions = new BasicEList<UnitInstruction>(); unitInstructions.add(instruction); this.informationHolder.getCreatedElementsToInstructions().put(createdElement, unitInstructions); } } /** * Resolves the contributed instanciation instruction. * * @param contributionInstruction * the contribution instruction */ private void referenceContributionInstruction(ContributionInstruction contributionInstruction) { if (contributionInstruction.getContributionReference() != null && contributionInstruction.getContributionReference().getReferencedInstruction() != null) { Iterator<Entry<EObject, EList<UnitInstruction>>> entryIterator = this.informationHolder .getCreatedElementsToInstructions().entrySet().iterator(); ModelingUnitInstruction targetInstanciationInstruction = contributionInstruction .getContributionReference().getReferencedInstruction(); boolean instanciationInstructionFound = false; while (entryIterator.hasNext() && !instanciationInstructionFound) { Entry<EObject, EList<UnitInstruction>> entry = entryIterator.next(); if (entry.getValue().contains(targetInstanciationInstruction)) { entry.getValue().add(contributionInstruction); instanciationInstructionFound = true; } } } } /** * Returns the instruction that declared the given element. * * @param createdElement * the element to inspect * @return the instruction that declared the given element */ public UnitInstruction getInstanciationInstructionByCreatedElement(EObject createdElement) { if (this.informationHolder.getCreatedElementsToInstructions().get(createdElement) != null) { return this.informationHolder.getCreatedElementsToInstructions().get(createdElement).iterator() .next(); } return null; } /** * Returns the instruction related to the given element. * * @param createdElement * the created element * @return the related instructions */ public Collection<UnitInstruction> getAllInstructionsByCreatedElement(EObject createdElement) { return this.informationHolder.getCreatedElementsToInstructions().get(createdElement); } /** * Returns all the Instanciation instructions that have a non-null name (e.g new EClass e1 {}). * * @return all the instanciation instructions that have a non-null name (e.g new EClass e1 {}) */ public Set<UnitInstruction> getAllInstanciationsInstructions() { return Sets.newLinkedHashSet(Iterables.filter( Iterables.concat(this.informationHolder.getCreatedElementsToInstructions().values()), IS_VALID_INSTANCIATION_INSTRUCTION_PREDICATE)); } /** * Returns the object associated to the given name (used for resolving reference). * * @param type * the type of the researched element. * @param name * Name of the searched object. * @return the object associated to the given name */ public Object getCreatedInstanceByName(EClassifier type, String name) { Object createdInstance = null; if (type != null) { return this.informationHolder.getTypeToNameToElementsMap().get(type).getNameToElement().get(name); } else { // If type is null we search this instance for any type for (EClassifier possibleType : this.informationHolder.getTypeToNameToElementsMap().keySet()) { createdInstance = this.informationHolder.getTypeToNameToElementsMap().get(possibleType) .getNameToElement().get(name); if (createdInstance != null) { break; } } return createdInstance; } } /** * Add the given resource (which is empty) to the map. * * @param resource * the resource to map with the given element */ public void addResource(ResourceDeclaration resource) { if (this.informationHolder.getResourceToContainedElements().get(resource) == null) { this.informationHolder.getResourceToContainedElements().put(resource, new BasicEList<EObject>()); } } /** * Add the given generated element to the given resource's content list. * * @param resource * the resource to map with the given element * @param newContainedElement * the element to map with the given resource */ public void addResourceToGeneratedElementMapping(ResourceDeclaration resource, EObject newContainedElement) { if (this.informationHolder.getResourceToContainedElements().get(resource) == null) { this.informationHolder.getResourceToContainedElements().put(resource, new BasicEList<EObject>()); } this.informationHolder.getResourceToContainedElements().get(resource).add(newContainedElement); } /** * Returns the declared resources. * * @return the declared resources. */ public Set<ResourceDeclaration> getDeclaredResources() { return this.informationHolder.getResourceToContainedElements().keySet(); } /** * Returns the given's resource content (generated elements). * * @param resource * the resource to inspect * @return the given's resource content (generated elements) */ public EList<EObject> getResourceContent(ResourceDeclaration resource) { return this.informationHolder.getResourceToContainedElements().get(resource); } /** * Add to the unresolvedReference list of the given element the given referenceHolder. * * @param generatedElement * the element that contains the unresolved reference to add * @param referenceHolder * the referenceHolder containing the feature containing the unresolved reference and the * textual value of it */ public void addUnresolvedReference(EObject generatedElement, UnresolvedReferenceHolder referenceHolder) { if (this.informationHolder.getElementToUnresolvedReferenceMap().get(generatedElement) == null) { this.informationHolder.getElementToUnresolvedReferenceMap().put(generatedElement, new BasicEList<UnresolvedReferenceHolder>()); } this.informationHolder.getElementToUnresolvedReferenceMap().get(generatedElement) .add(referenceHolder); } /** * Returns a list containing the unresolved references for the given element. * * @param elementContainingUnresolvedReference * the element to inspect * @return a list containing the unresolved references for the given element */ public EList<UnresolvedReferenceHolder> getUnresolvedReferencesByGeneratedElement( EObject elementContainingUnresolvedReference) { EList<UnresolvedReferenceHolder> unresolvedRefs = this.informationHolder .getElementToUnresolvedReferenceMap().get(elementContainingUnresolvedReference); if (unresolvedRefs == null) { unresolvedRefs = new BasicEList<UnresolvedReferenceHolder>(); } return unresolvedRefs; } /** * Sets the list of current imported packages (URIs form). * * @param importedPackages * the list of current imported packages (URIs form) */ public void setCurrentImportedPackages(List<String> importedPackages) { this.currentImportedPackages = importedPackages; } /** * Returns the list of current imported packages (URIs form). * * @return the list of current imported packages (URIs form) */ public List<String> getCurrentImportedPackages() { return currentImportedPackages; } public List<EObject> getCurrentCreatedElements() { return this.informationHolder.getCurrentGeneratedElementList(); } /** * Try to add the given createdElement identified by the given name ; throws an exception if an element * has already been registered with this name. * * @param name * name (identifier) of the element to register * @param createdElement * the createdElement * @param instruction * the instruction that declared this element */ public void addNameToCreatedElementEntry(final String name, EObject createdElement, InstanciationInstruction instruction) { if (name != null) { // Step 1: look for an element registered with the same name Iterable<Entry<EClassifier, StringToEObjectMap>> alreadyExistingNames = Iterables.filter( this.informationHolder.getTypeToNameToElementsMap().entrySet(), new Predicate<Entry<EClassifier, StringToEObjectMap>>() { public boolean apply(Entry<EClassifier, StringToEObjectMap> entry) { if (entry != null && entry.getValue() != null && entry.getValue().getNameToElement() != null) { return entry.getValue().getNameToElement().keySet().contains(name); } return false; } }); if (alreadyExistingNames != null && alreadyExistingNames.iterator().hasNext()) { Entry<EClassifier, StringToEObjectMap> alreadyExistingEntry = alreadyExistingNames.iterator() .next(); String message = "The name " + name + " has already been used to identify an element (of type " + alreadyExistingEntry.getKey().getName() + ")."; throw new InvalidValueException(instruction, message); } // Step 2: register the element StringToEObjectMap nameToElement = this.informationHolder.getTypeToNameToElementsMap().get( createdElement.eClass()); if (nameToElement == null) { nameToElement = CompilerFactory.eINSTANCE.createStringToEObjectMap(); this.informationHolder.getTypeToNameToElementsMap().put(createdElement.eClass(), nameToElement); } // Otherwise, we register the given element nameToElement.getNameToElement().put(name, createdElement); } this.addCreatedElementsToCurrentList(instruction, createdElement); } /** * Checks if a contribution is unresolved. * * @param contributionInstruction * the contribution instruction * @return true if the contribution can be resolved */ public boolean isUnresolvedContribution(ContributionInstruction contributionInstruction) { boolean isUnresolved = this.informationHolder.getUnresolvedContributions().get( contributionInstruction.getContributionReference().getIntentHref()) == null; if (!isUnresolved) { isUnresolved = true; for (UnresolvedContributionHolder holder : this.informationHolder.getUnresolvedContributions() .get(contributionInstruction.getContributionReference().getIntentHref())) { if (holder.getReferencedContribution() == contributionInstruction) { isUnresolved = isUnresolved && !holder.isResolved(); } } } return isUnresolved; } /** * Returns the unresolved contributions associated to the contributed element matching the given name. * * @param contributedElementName * the element name * @return the unresolved contributions */ public EList<UnresolvedContributionHolder> getContributionsAssociatedTo(String contributedElementName) { if (this.informationHolder.getUnresolvedContributions().get(contributedElementName) != null) { return new BasicEList<UnresolvedContributionHolder>(this.informationHolder .getUnresolvedContributions().get(contributedElementName)); } return new BasicEList<UnresolvedContributionHolder>(); } /** * Return the list of unresolved contribution instructions for the element with the given name. * * @param contributedElementName * the name of the element that is associated with unresolved contribution exceptions. * @return the list of unresolved contribution instructions for the element with the given name */ public EList<UnresolvedContributionHolder> getUnresolvedContributions(String contributedElementName) { if (this.informationHolder.getUnresolvedContributions().get(contributedElementName) != null) { return this.informationHolder.getUnresolvedContributions().get(contributedElementName); } return new BasicEList<UnresolvedContributionHolder>(); } /** * Returns the textual reference that remained unresolved for contribution instructions. * * @return the textual reference that remained unresolved for contribution instructions */ public Set<String> getAllUnresolvedContributionsNames() { return this.informationHolder.getUnresolvedContributions().keySet(); } /** * returns the validation informations related to the createdElements. * * @return a mapping between ModelingUnits and the validation informations related to the createdElements */ public EList<CompilationStatus> getCompilationStatusList() { return this.statusManager.getCompilationStatusList(); } /** * Registers the given compilationException as a new CompilationStatus. * * @param compilationException * the compilation Exception to register */ public void registerCompilationExceptionAsCompilationStatus(CompilationException compilationException) { EList<CompilationStatus> compilationStatusList = new BasicEList<CompilationStatus>(); // Step 1 : we convert each the CompilationException in a CompilationStatus CompilationStatus newStatus = CompilerFactory.eINSTANCE.createCompilationStatus(); newStatus.setMessage(compilationException.getMessage()); newStatus.setSeverity(CompilationStatusConverter .createStatusSeverityFromCompilationExceptionErrorType(compilationException.getType())); newStatus.setType(CompilationStatusConverter .createStatusTypeFromCompilationExceptionErrorType(compilationException.getType())); newStatus.setTarget(compilationException.getInvalidInstruction()); // and add this status to the diagnostic list. compilationStatusList.add(newStatus); // Step 2 : we register the new Diagnostics in the informationHolder addStatusListToInformationHolder( getModelingUnitForInstruction(compilationException.getInvalidInstruction()), compilationStatusList); } /** * Register the given diagnostic for the given generatedElement as a new CompilationStatus. * * @param generatedElement * the generatedElement containing diagnostic errors * @param diagnostic * the diagnostic to register */ public void registerDiagnosticAsCompilationStatusList(EObject generatedElement, Diagnostic diagnostic) { EList<CompilationStatus> compilationStatusList = new BasicEList<CompilationStatus>(); for (Diagnostic subDiagnostic : diagnostic.getChildren()) { // Step 1 : we convert each sub-diagnostic in a CompilationStatus CompilationStatus newStatus = CompilerFactory.eINSTANCE.createCompilationStatus(); newStatus.setMessage(subDiagnostic.getMessage()); newStatus.setSeverity(CompilationStatusConverter .createStatusSeverityFromDiagnosticSeverity(subDiagnostic)); newStatus.setType(CompilationMessageType.VALIDATION_ERROR); newStatus.setTarget(this.getInstanciationInstructionByCreatedElement(generatedElement)); // and add this status to the diagnostic list. compilationStatusList.add(newStatus); } // Step 2 : we register the new Diagnostics in the informationHolder addStatusListToInformationHolder( getModelingUnitForInstruction(getInstanciationInstructionByCreatedElement(generatedElement)), compilationStatusList); } /** * Adds the given list of compilation status to the global status list and the map * ModelingUnitToStatusList. * * @param unit * the modeling unit containing the given status list * @param compilationStatusList * the list of status to add */ private void addStatusListToInformationHolder(ModelingUnit unit, EList<CompilationStatus> compilationStatusList) { if (unit != null) { // First in the general Status List this.statusManager.getCompilationStatusList().addAll(compilationStatusList); // Then in the ModelingUnit to StatusList map. if (this.statusManager.getModelingUnitToStatusList().get(unit) == null) { this.statusManager.getModelingUnitToStatusList().put(unit, new BasicEList<CompilationStatus>()); } this.statusManager.getModelingUnitToStatusList().get(unit).addAll(compilationStatusList); } } /** * Returns the modeling unit associated to the given instruction. * * @param instruction * the instruction to inspect * @return the modeling unit associated to the given instruction */ private ModelingUnit getModelingUnitForInstruction(UnitInstruction instruction) { ModelingUnit foundModelingUnit = null; if (instruction != null) { foundModelingUnit = (ModelingUnit)instruction.getUnit(); UnitInstruction parentInstruction = instruction; while (foundModelingUnit == null) { parentInstruction = (UnitInstruction)parentInstruction.eContainer(); if (parentInstruction != null) { foundModelingUnit = (ModelingUnit)parentInstruction.getUnit(); } else { foundModelingUnit = null; } } } return foundModelingUnit; } /** * Add to the mapping between textual reference and contribution instructions the given contribution * instruction. * * @param instanceTextualReference * The textual reference to the contributed instance * @param contributionInstruction * the contribution instruction to add to the unresolvedCOntribution. */ public void addUnresolvedContribution(String instanceTextualReference, ContributionInstruction contributionInstruction) { if (this.informationHolder.getUnresolvedContributions().get(instanceTextualReference) == null) { this.informationHolder.getUnresolvedContributions().put(instanceTextualReference, new BasicEList<UnresolvedContributionHolder>()); } if (!isRegisteredUnresolvedContribution(contributionInstruction)) { UnresolvedContributionHolder holder = CompilerFactory.eINSTANCE .createUnresolvedContributionHolder(); holder.setResolved(false); holder.setReferencedContribution(contributionInstruction); this.informationHolder.getUnresolvedContributions().get(instanceTextualReference).add(holder); } } /** * Checks whether a contribution is registered or not. * * @param contributionInstruction * the contribution * @return true if the contribution is already registered as unresolved */ public boolean isRegisteredUnresolvedContribution(ContributionInstruction contributionInstruction) { boolean isRegistered = false; for (UnresolvedContributionHolder holder : this.informationHolder.getUnresolvedContributions().get( contributionInstruction.getContributionReference().getIntentHref())) { isRegistered = isRegistered || (holder.getReferencedContribution() == contributionInstruction); } return isRegistered; } /** * Sets the given contribution instruction as resolved (i.e. remove it from its * unresolvedContributionHolder) ; if the unresolvedContributionHolder associated to the textual reference * is empty, we remove it from the map. * * @param contributionInstruction * the contributionInstruction to consider as resolved. */ public void setContributionInstructionAsResolved(ContributionInstruction contributionInstruction) { for (UnresolvedContributionHolder holder : informationHolder.getUnresolvedContributions().get( contributionInstruction.getContributionReference().getIntentHref())) { if (holder.getReferencedContribution() == contributionInstruction) { holder.setResolved(true); } } referenceContributionInstruction(contributionInstruction); } /** * A {@link Predicate} used to determine wether an Instanciation instruction is valid or not. * * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> */ private static final class IsValidInstanciationInstructionPredicate implements Predicate<UnitInstruction> { /** * {@inheritDoc} * * @see com.google.common.base.Predicate#apply(java.lang.Object) */ public boolean apply(UnitInstruction instruction) { return instruction instanceof InstanciationInstruction && ((InstanciationInstruction)instruction).getName() != null; } } }