| /** |
| * Copyright (c) 2017 Eclipse contributors and others. |
| * 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 |
| */ |
| package org.eclipse.emf.ecore.util; |
| |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| 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.DiagnosticChain; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.ResourceLocator; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EAnnotation; |
| import org.eclipse.emf.ecore.EAnnotationValidator; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EDataType; |
| import org.eclipse.emf.ecore.EModelElement; |
| import org.eclipse.emf.ecore.ENamedElement; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EPackage; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.ETypedElement; |
| import org.eclipse.emf.ecore.EValidator; |
| import org.eclipse.emf.ecore.EcoreFactory; |
| import org.eclipse.emf.ecore.EcorePackage; |
| import org.eclipse.emf.ecore.impl.EPackageImpl; |
| import org.eclipse.emf.ecore.plugin.EcorePlugin; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.xml.type.XMLTypeFactory; |
| |
| |
| /** |
| * A basic implementation of an {@link EAnnotationValidator annotation validator}. |
| * <p> |
| * An implementation must specialize the {@link #getResourceLocator()} method in order for the {@link #getValidLocationDescription()} method to function correctly. |
| * The most straight-forward way to implement an annotation validator is to model the supported keys, |
| * specializing {@link #getPropertyClasses(EModelElement)} with one or more {@link EClass classes} that |
| * can be {@link #createInstance(EClass, EAnnotation) instantiated} to represent the information in the annotation. |
| * These classes are used to induce {@link #getProperties(EModelElement) a mapping} of keys onto the underlying annotation model's features. |
| * If the annotation model includes references, |
| * {@link #validateReferenceDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EReference, String, List, DiagnosticChain, Map) validateReferenceDetailValueLiteral} |
| * and {@link #convertPropertyReferenceValueToLiteralItem(EObject, EReference, Object)} must also be specialized. |
| * Alternatively an implementation can specialize {@link #validateDetail(EAnnotation, EModelElement, Map.Entry, DiagnosticChain, Map)} without providing a modeled representation. |
| * The annotation validator's {@link #getAssistant() assistant} is especially useful for inducing a user interface based on the modeled annotation representation. |
| * </p> |
| * |
| * @see EAnnotationValidator |
| * @see Assistant |
| * @since 2.14 |
| */ |
| public abstract class BasicEAnnotationValidator implements EAnnotationValidator |
| { |
| /** |
| * @see #reportInvalidLocation(EAnnotation, DiagnosticChain, Map) |
| */ |
| public static final int INVALID_LOCATION = 1; |
| |
| /** |
| * @see #reportDuplicate(EAnnotation, EAnnotation, EModelElement, DiagnosticChain, Map) |
| */ |
| public static final int INVALID_DUPLICATE = 2; |
| |
| /** |
| * @see #reportInvalidReferenceLiteral(EAnnotation, EModelElement, Map.Entry, EReference, String, DiagnosticChain, Map) |
| */ |
| public static final int INVALID_REFERENCE_LITERAL = 3; |
| |
| /** |
| * @see #createValueDiagnostic(EAnnotation, EModelElement, Map.Entry, EStructuralFeature) |
| */ |
| public static final int INVALID_DETAIL_VALUE = 4; |
| |
| /** |
| * @see #reportInvalidValueLiteral(EAnnotation, EModelElement, Map.Entry, EAttribute, String, EDataType, DiagnosticChain, RuntimeException, Map) |
| */ |
| public static final int INVALID_VALUE_LITERAL = 5; |
| |
| /** |
| * @see #reportIgnoredAnnotations(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) |
| */ |
| public static final int IGNORED_ANNOTATIONS = 6; |
| |
| /** |
| * @see #reportIgnoredContents(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) |
| */ |
| public static final int IGNORED_CONTENTS = 7; |
| |
| /** |
| * @see #reportIgnoredReferences(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) |
| */ |
| public static final int IGNORED_REFERENCES = 8; |
| |
| /** |
| * @see #reportInvalidReference(EAnnotation, EModelElement, EObject, DiagnosticChain, Map) |
| */ |
| public static final int INVALID_REFERENCE = 9; |
| |
| /** |
| * @see #reportInvalidAnnotation(EAnnotation, EModelElement, EAnnotation, DiagnosticChain, Map) |
| */ |
| public static final int INVALID_ANNOTATION = 10; |
| |
| /** |
| * @see #reportInvalidContent(EAnnotation, EModelElement, EObject, DiagnosticChain, Map) |
| */ |
| public static final int INVALID_CONTENT = 11; |
| |
| /** |
| * @see #reportIgnoredEntry(EAnnotation, EModelElement, Map.Entry, DiagnosticChain, Map) |
| */ |
| public static final int IGNORED_ENTRY = 12; |
| |
| /** |
| * @see #reportMissingEntry(EAnnotation, EModelElement, String, EStructuralFeature, DiagnosticChain, Map) |
| */ |
| public static final int MISSING_ENTRY = 13; |
| |
| /** |
| * @see #reportMissingRequiredEntryValue(EAnnotation, EModelElement, EStructuralFeature, List, DiagnosticChain, Map) |
| */ |
| public static final int MISSING_REQUIRED_ENTRY_VALUE = 14; |
| |
| /** |
| * @see #reportTooFewValues(EAnnotation, EModelElement, Map.Entry, EStructuralFeature, List, int, int, DiagnosticChain, Map) |
| */ |
| public static final int TOO_FEW_VALUES = 15; |
| |
| /** |
| * @see #reportTooManyValues(EAnnotation, EModelElement, Map.Entry, EStructuralFeature, List, int, int, DiagnosticChain, Map) |
| */ |
| public static final int TOO_MANY_VALUES = 16; |
| |
| /** |
| * The {@link EAnnotation#getSource() annotation source} validated by this annotation validator. |
| */ |
| protected final String annotationSource; |
| |
| /** |
| * The name used in messages for this validator's annotations. |
| */ |
| protected final String annotationName; |
| |
| /** |
| * The {@link Diagnostic#getSource() source} used in this validator's diagnostics. |
| */ |
| protected final String diagnosticSource; |
| |
| /** |
| * The {@link Assistant assistant} used by the framework to induce a user interface. |
| */ |
| protected final Assistant assistant; |
| |
| /** |
| * Creates an instance for the given {@link EAnnotation#getSource() annotation source} validated by this annotation validator. |
| * |
| * @param annotationSource the annotation source validated by this annotation validator. |
| * @param annotationName the name used in this validator's diagnostics |
| * @param diagnosticSource the {@link Diagnostic#getSource() diagnostic source} used in this validator's diagnostics. |
| */ |
| public BasicEAnnotationValidator(String annotationSource, String annotationName, String diagnosticSource) |
| { |
| this.annotationSource = annotationSource; |
| this.annotationName = annotationName; |
| this.diagnosticSource = diagnosticSource; |
| this.assistant = createAssistant(); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public String getAnnotationSource() |
| { |
| return annotationSource; |
| } |
| |
| /** |
| * Returns the resource locator for fetching implementation-specific messages. |
| * @return the resource locator for fetching model-specific messages. |
| */ |
| protected abstract ResourceLocator getResourceLocator(); |
| |
| /** |
| * Returns the model classes used to represent annotations for the given model element. |
| * <p> |
| * Typically an annotation validator implementation will return a single class. |
| * An induced user interface will generally require the ability to {@link #createInstance(EClass, EAnnotation) create instances} of the classes returned by this method. |
| * The annotation validator implementation itself does not require that ability. |
| * </p> |
| * @param eModelElement the model element in question. |
| * @return the model classes used to represent annotations for the given model element. |
| */ |
| protected abstract List<EClass> getPropertyClasses(EModelElement eModelElement); |
| |
| /** |
| * Returns the assistant provided by this annotation validator |
| * which is generally useful to provide access to protected methods that are needed primarily for inducing a user interface that represents the annotations in a more structured form. |
| * @return the assistant provided by this annotation validator. |
| */ |
| public Assistant getAssistant() |
| { |
| return assistant; |
| } |
| |
| /** |
| * Creates the assistant. |
| * <p> |
| * Generally derived classes will not need to specialize this method because all methods of the assistant delegate back to the annotation validator. |
| * </p> |
| * @return the assistant. |
| */ |
| protected Assistant createAssistant() |
| { |
| return new Assistant(this) |
| { |
| }; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * This implementation returns <code>false</code> if the containing {@link EAnnotation#getEModelElement()} is <code>null</code>, |
| * if the {@link #isValidLocation(EAnnotation, EModelElement)} returns <code>false</code> for the containing model element, |
| * or if this is not the first annotation with this annotation source in the model element and {@link #isDuplicateValid(EModelElement, EAnnotation, EAnnotation) isDuplicateValue} returns <code>false</code>. |
| * </p> |
| */ |
| public boolean isValidLocation(EAnnotation eAnnotation) |
| { |
| EModelElement eModelElement = eAnnotation.getEModelElement(); |
| if (eModelElement == null || !isValidLocation(eAnnotation, eModelElement)) |
| { |
| return false; |
| } |
| else |
| { |
| EAnnotation otherEAnnotation = eModelElement.getEAnnotation(annotationSource); |
| return otherEAnnotation == null || otherEAnnotation == eAnnotation || isDuplicateValid(eModelElement, otherEAnnotation, eAnnotation); |
| } |
| } |
| |
| /** |
| * Returns whether this annotation {@link EAnnotation#getEModelElement() contained} by this model element is valid at this location. |
| * <p> |
| * This implementation returns <code>false</code> if the element is not a {@link ENamedElement named element}. |
| * It's typically the case that annotations on annotations aren't meaningful and valid. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @return whether this annotation contained by this model element is valid at this location. |
| * |
| * @see #isValidLocation(EAnnotation) |
| * @see #validate(EAnnotation, DiagnosticChain, Map) |
| */ |
| protected boolean isValidLocation(EAnnotation eAnnotation, EModelElement eModelElement) |
| { |
| return eModelElement instanceof ENamedElement; |
| } |
| |
| /** |
| * Returns whether the given two annotations, both with the annotation validator's annotation source, both {@link EModelElement#getEAnnotation(String) contained} by the given model element, are valid. |
| * <p> |
| * This implementation returns <code>false</code> because it's typically the case that only the primary annotation is meaningful and valid. |
| * </p> |
| * @param eModelElement the model element that contains both annotations in its {@link EModelElement#getEAnnotations() annotations} feature. |
| * @param primaryEAnnotation the first annotation in the model element's details. |
| * @param secondaryEAnnotation a subsequent annotation in the model element's details. |
| * @return whether these two annotations, both of which have this annotation validator's annotation source, are valid. |
| * @see #isValidLocation(EAnnotation) |
| * @see #validate(EAnnotation, DiagnosticChain, Map) |
| */ |
| protected boolean isDuplicateValid(EModelElement eModelElement, EAnnotation primaryEAnnotation, EAnnotation secondaryEAnnotation) |
| { |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * This implementation checks if the {@link #isValidLocation(EAnnotation, EModelElement) location is valid} |
| * and {@link #reportInvalidLocation(EAnnotation, DiagnosticChain, Map) reports an invalid location} if it is not. |
| * Then checks for {@link #isDuplicateValid(EModelElement, EAnnotation, EAnnotation) invalid duplicates} |
| * and {@link #reportDuplicate(EAnnotation, EAnnotation, EModelElement, DiagnosticChain, Map) reports a duplicate} if it is an invalid duplicate. |
| * Then it {@link #validateReferences(EAnnotation, EModelElement, DiagnosticChain, Map) validates the references} and {@link #validateDetails(EAnnotation, EModelElement, DiagnosticChain, Map) validates the details}. |
| * </p> |
| */ |
| public boolean validate(EAnnotation eAnnotation, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| boolean result; |
| EModelElement eModelElement = eAnnotation.getEModelElement(); |
| if (eModelElement != null && isValidLocation(eAnnotation, eModelElement)) |
| { |
| EAnnotation otherEAnnotation = eModelElement.getEAnnotation(annotationSource); |
| if (otherEAnnotation == eAnnotation || isDuplicateValid(eModelElement, otherEAnnotation, eAnnotation)) |
| { |
| result = validateReferences(eAnnotation, eModelElement, diagnostics, context); |
| if (result || diagnostics != null) |
| { |
| result &= validateContents(eAnnotation, eModelElement, diagnostics, context); |
| } |
| if (result || diagnostics != null) |
| { |
| result &= validateAnnotations(eAnnotation, eModelElement, diagnostics, context); |
| } |
| if (result || diagnostics != null) |
| { |
| result &= validateDetails(eAnnotation, eModelElement, diagnostics, context); |
| } |
| } |
| else |
| { |
| result = false; |
| if (diagnostics != null) |
| { |
| reportDuplicate(otherEAnnotation, eAnnotation, eModelElement, diagnostics, context); |
| } |
| } |
| } |
| else |
| { |
| result = false; |
| if (diagnostics != null) |
| { |
| reportInvalidLocation(eAnnotation, diagnostics, context); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether this annotation's {@link EAnnotation#getReferences() references} are valid. |
| * <p> |
| * This implementation checks whether {@link #isReferencesSupported(EAnnotation, EModelElement) references are supported}. |
| * If not, it checks whether the references are empty and if not {@link #reportIgnoredReferences(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) reports ignored references}. |
| * If references are supported, then for each reference, it tests whether that reference is among the {@link #getValidReferences(EAnnotation, EModelElement, Collection) valid references}, |
| * passing in this annotation's references to determine the valid references, |
| * and {@link #reportInvalidReference(EAnnotation, EModelElement, EObject, DiagnosticChain, Map) reports an invalid reference} for each not present in the valid references. |
| * </p> |
| * <p> |
| * It's typically the case that annotations ignore references. |
| * If that's not the case, specialize {@link #isReferencesSupported(EAnnotation, EModelElement)} |
| * and {@link #getValidReferences(EAnnotation, EModelElement, Collection)}. |
| * An implementation may override this method to report missing required references. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether this annotation's references are valid. |
| * @see #isReferencesSupported(EAnnotation, EModelElement) |
| */ |
| protected boolean validateReferences(EAnnotation eAnnotation, EModelElement eModelElement, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| EList<EObject> references = eAnnotation.getReferences(); |
| if (!isReferencesSupported(eAnnotation, eModelElement)) |
| { |
| boolean result = references.isEmpty(); |
| if (!result && diagnostics != null) |
| { |
| reportIgnoredReferences(eAnnotation, eModelElement, references, diagnostics, context); |
| } |
| return result; |
| } |
| else |
| { |
| boolean result = true; |
| Collection<?> validReferences = getValidReferences(eAnnotation, eModelElement, references); |
| for (EObject reference : references) |
| { |
| if (!validReferences.contains(reference)) |
| { |
| result = false; |
| if (diagnostics == null) |
| { |
| break; |
| } |
| else |
| { |
| reportInvalidReference(eAnnotation, eModelElement, reference, diagnostics, context); |
| } |
| } |
| } |
| return result; |
| } |
| } |
| |
| /** |
| * Returns whether {@link EAnnotation#getReferences() references} are meaningful for this annotation. |
| * <p> |
| * This method used to determine how references should be {@link #validateReferences(EAnnotation, EModelElement, DiagnosticChain, Map) validated}. |
| * Also, an induced user interface should avoid providing the ability to specify references when this returns <code>false</code>. |
| * Implementations that override this to ever return <code>true</code> should also override {@link #getValidReferences(EAnnotation, EModelElement, Collection)} to control the valid choices. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @return whether references are meaningful for this annotation. |
| * @see Assistant#isReferencesSupported(EAnnotation) |
| * @see #validateReferences(EAnnotation, EModelElement, DiagnosticChain, Map) |
| */ |
| protected boolean isReferencesSupported(EAnnotation eAnnotation, EModelElement eModelElement) |
| { |
| return false; |
| } |
| |
| /** |
| * Returns the filtered collection of references that are valid for this annotation. |
| * <p> |
| * An induced user interface should provide the ability to specify only the references returned by this method. |
| * The references argument may contain all reachable objects, some subset there of, or none at all; |
| * an implementation may choose to filter from this collection or to provide its own result, including objects not in this collection. |
| * This implementation returns the references argument if {@link #isReferencesSupported(EAnnotation, EModelElement) references are supported}, or an empty list otherwise. |
| * It is also used to {@link #validateReferences(EAnnotation, EModelElement, DiagnosticChain, Map) determine} which references are valid. |
| * An implementation that overrides this should also override {@link #isReferencesSupported(EAnnotation, EModelElement)}. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param references all reachable objects, some subset there of, or none at all. |
| * @return the objects that are valid as references for this annotation. |
| * @see Assistant#getValidReferences(EAnnotation, Collection) |
| */ |
| protected Collection<?> getValidReferences(EAnnotation eAnnotation, EModelElement eModelElement, Collection<?> references) |
| { |
| return isReferencesSupported(eAnnotation, eModelElement) ? references : Collections.emptyList(); |
| } |
| |
| /** |
| * Returns whether this annotation's {@link EAnnotation#getContents() contents} are valid. |
| * <p> |
| * This implementation checks whether {@link #isContentsSupported(EAnnotation, EModelElement) contents are supported}. |
| * If not, it checks whether the contents are empty and if not {@link #reportIgnoredContents(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) reports ignored contents}. |
| * If contents are supported, then for each content, it tests whether that content is among the {@link #getValidContents(EAnnotation, EModelElement, Collection) valid contents}, |
| * passing in this annotation's contents to determine the valid contents, |
| * and {@link #reportInvalidContent(EAnnotation, EModelElement, EObject, DiagnosticChain, Map) reports an invalid content} for each not present in the valid contents. |
| * </p> |
| * <p> |
| * It's typically the case that annotations ignore contents. |
| * If that's not the case, specialize {@link #isContentsSupported(EAnnotation, EModelElement)} |
| * and {@link #getValidContents(EAnnotation, EModelElement, Collection)}. |
| * An implementation may override this method to report missing required contents. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether this annotation's contents are valid. |
| * @see #isContentsSupported(EAnnotation, EModelElement) |
| * @see #getValidContents(EAnnotation, EModelElement, Collection) |
| */ |
| protected boolean validateContents(EAnnotation eAnnotation, EModelElement eModelElement, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| EList<EObject> contents = eAnnotation.getContents(); |
| if (!isContentsSupported(eAnnotation, eModelElement)) |
| { |
| boolean result = contents.isEmpty(); |
| if (!result && diagnostics != null) |
| { |
| reportIgnoredContents(eAnnotation, eModelElement, contents, diagnostics, context); |
| } |
| return result; |
| } |
| else |
| { |
| boolean result = true; |
| Collection<?> validContents = getValidContents(eAnnotation, eModelElement, contents); |
| for (EObject content : contents) |
| { |
| if (!validContents.contains(content)) |
| { |
| result = false; |
| if (diagnostics == null) |
| { |
| break; |
| } |
| else |
| { |
| reportInvalidContent(eAnnotation, eModelElement, content, diagnostics, context); |
| } |
| } |
| } |
| return result; |
| } |
| } |
| |
| /** |
| * Returns whether {@link EAnnotation#getContents() contents} are meaningful for this annotation. |
| * <p> |
| * This method used to determine how contents should be {@link #validateContents(EAnnotation, EModelElement, DiagnosticChain, Map) validated}. |
| * Also, an induced user interface should avoid providing the ability to specify contents when this returns <code>false</code>. |
| * Implementations that override this to ever return <code>true</code> should also override {@link #getValidContents(EAnnotation, EModelElement, Collection)} to control the valid choices. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @return whether contents are meaningful for this annotation. |
| * @see Assistant#isContentsSupported(EAnnotation) |
| * @see #validateContents(EAnnotation, EModelElement, DiagnosticChain, Map) |
| */ |
| protected boolean isContentsSupported(EAnnotation eAnnotation, EModelElement eModelElement) |
| { |
| return false; |
| } |
| |
| /** |
| * Returns the filtered collection of contents that are valid for this annotation. |
| * <p> |
| * An induced user interface should provide the ability to specify only the contents returned by this method. |
| * The contents argument may contain nothing at all, or the {@link EAnnotation#getContents() current contents} of the annotation; |
| * an implementation may choose to filter from this collection or to provide its own result, including objects not in this collection |
| * but it should not remove objects currently contained by the annotation that are valid. |
| * This implementation returns the contents argument if {@link #isContentsSupported(EAnnotation, EModelElement) contents are supported}, or an empty list otherwise. |
| * It is also used to {@link #validateContents(EAnnotation, EModelElement, DiagnosticChain, Map) determine} which contents are valid |
| * and should therefore <b>not</b> remove values from the provided contents argument if they are valid. |
| * An implementation that overrides this should also override {@link #isContentsSupported(EAnnotation, EModelElement)}. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param contents nothing at all, or the {@link EAnnotation#getContents() current or potential contents} of the annotation. |
| * @return the objects that are valid as contents for this annotation. |
| * @see Assistant#getValidContents(EAnnotation, Collection) |
| */ |
| protected Collection<? extends EObject> getValidContents(EAnnotation eAnnotation, EModelElement eModelElement, Collection<? extends EObject> contents) |
| { |
| return isContentsSupported(eAnnotation, eModelElement) ? contents : Collections.<EObject> emptyList(); |
| } |
| |
| /** |
| * Returns whether this annotation's {@link EAnnotation#getEAnnotations() nested annotations} are valid. |
| * <p> |
| * This implementation iterates over the nested annotations, and if there is at least one for which there is no {@link org.eclipse.emf.ecore.EAnnotationValidator.Registry#getEAnnotationValidator(String) registered annotation validator} |
| * or for which the registered annotation validator does not consider this nested annotation {@link #isValidLocation(EAnnotation) valid at this location}, |
| * it {@link #reportIgnoredAnnotations(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) reports ignored annotations}. |
| * It's typically the case that annotations ignore nested annotations. |
| * If that's not the case, you should override this method and specialize {@link #isAnnotationsSupported(EAnnotation, EModelElement)} |
| * and consider specializing {@link #getValidAnnotations(EAnnotation, EModelElement, Collection)} |
| * </p> |
| * <p> |
| * This implementation checks whether {@link #isAnnotationsSupported(EAnnotation, EModelElement) nested annotations are supported}. |
| * If not, it checks whether the {@link #getValidAnnotations(EAnnotation, EModelElement, Collection) valid annotations}, |
| * passing in this annotation's nested annotations to determine the valid annotations, |
| * contain all the nested annotations; if not it {@link #reportIgnoredAnnotations(EAnnotation, EModelElement, Collection, DiagnosticChain, Map) reports ignored annotations}. |
| * If nested annotations are supported, then for each nested annotation, it tests whether that annotation is among the {@link #getValidAnnotations(EAnnotation, EModelElement, Collection) valid annotations} |
| * and {@link #reportInvalidAnnotation(EAnnotation, EModelElement, EAnnotation, DiagnosticChain, Map) reports an invalid annotation} for each not present in the valid annotations. |
| * </p> |
| * <p> |
| * It's typically the case that annotations ignore nested annotations. |
| * If that's not the case, specialize {@link #isAnnotationsSupported(EAnnotation, EModelElement)} |
| * and {@link #getValidAnnotations(EAnnotation, EModelElement, Collection)}. |
| * An implementation may override this method to report missing required nested annotations. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether this annotation's nested annotations are valid. |
| * @see #isAnnotationsSupported(EAnnotation, EModelElement) |
| */ |
| protected boolean validateAnnotations(EAnnotation eAnnotation, EModelElement eModelElement, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| EList<EAnnotation> annotations = eAnnotation.getEAnnotations(); |
| if (!annotations.isEmpty()) |
| { |
| Collection<? extends EAnnotation> validAnnotations = getValidAnnotations(eAnnotation, eModelElement, annotations); |
| if (!isAnnotationsSupported(eAnnotation, eModelElement)) |
| { |
| if (!validAnnotations.containsAll(annotations)) |
| { |
| if (diagnostics != null) |
| { |
| List<EAnnotation> ignoredAnnotations = new ArrayList<EAnnotation>(annotations); |
| ignoredAnnotations.removeAll(validAnnotations); |
| reportIgnoredAnnotations(eAnnotation, eModelElement, ignoredAnnotations, diagnostics, context); |
| } |
| return false; |
| } |
| else |
| { |
| return true; |
| } |
| } |
| else |
| { |
| boolean result = true; |
| for (EAnnotation nestedEAnnotation : annotations) |
| { |
| if (!validAnnotations.contains(nestedEAnnotation)) |
| { |
| result = false; |
| if (diagnostics == null) |
| { |
| break; |
| } |
| else |
| { |
| reportInvalidAnnotation(eAnnotation, eModelElement, nestedEAnnotation, diagnostics, context); |
| } |
| } |
| } |
| return result; |
| } |
| } |
| else |
| { |
| return true; |
| } |
| } |
| |
| /** |
| * Returns whether {@link EAnnotation#getEAnnotations() nested annotations} are meaningful for this annotation. |
| * <p> |
| * This method used to determine how nested annotations should be {@link #validateAnnotations(EAnnotation, EModelElement, DiagnosticChain, Map) validated}. |
| * Also, an induced user interface should avoid providing the ability to specify nested annotations when this returns <code>false</code>. |
| * Implementations that override this to ever return <code>true</code> should also override {@link #getValidAnnotations(EAnnotation, EModelElement, Collection)} to control the valid choices. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @return whether nested annotations are meaningful for this annotation. |
| * @see Assistant#isAnnotationsSupported(EAnnotation) |
| * @see #validateAnnotations(EAnnotation, EModelElement, DiagnosticChain, Map) |
| */ |
| protected boolean isAnnotationsSupported(EAnnotation eAnnotation, EModelElement eModelElement) |
| { |
| return false; |
| } |
| |
| /** |
| * Returns the filtered collection of nested annotations that are valid for this annotation. |
| * <p> |
| * The annotations argument typically contains the {@link EModelElement#getEAnnotations() current nested annotations} of the specified annotation; |
| * an implementation may choose to filter from this collection, |
| * but it should <b>not</b> remove nested annotations currently contained by the annotation that are valid. |
| * This implementation takes into account the fact that annotations may be specifically designed to annotate other annotations, |
| * i.e., that the nested annotation source might correspond to a {@link org.eclipse.emf.ecore.EAnnotationValidator.Registry#getEAnnotationValidator(String) registered annotation validator} |
| * that considers its annotations {@link BasicEAnnotationValidator#isValidLocation(EAnnotation, EModelElement) valid} when contained by the specified annotation. |
| * As such, this implementation does not remove nested annotations for which there is a registered validator that considers its annotation valid in the specified annotation. |
| * Note that this method is used to {@link #validateAnnotations(EAnnotation, EModelElement, DiagnosticChain, Map) determine} which nested annotations are valid |
| * and that is why it should <b>not</b> remove values from the provided annotations argument if they are valid. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param annotations typically the {@link EModelElement#getEAnnotations() current nested annotations} of the annotation. |
| * @return the nested annotations that are valid as annotations for this annotation. |
| * @see Assistant#getValidAnnotations(EAnnotation, Collection) |
| */ |
| protected Collection<? extends EAnnotation> getValidAnnotations(EAnnotation eAnnotation, EModelElement eModelElement, Collection<? extends EAnnotation> annotations) |
| { |
| List<EAnnotation> result = new ArrayList<EAnnotation>(annotations); |
| for (EAnnotation nestedEAnnotation : annotations) |
| { |
| EAnnotationValidator eAnnotationValidator = EAnnotationValidator.Registry.INSTANCE.getEAnnotationValidator(nestedEAnnotation.getSource()); |
| if (!(eAnnotationValidator instanceof BasicEAnnotationValidator) || !((BasicEAnnotationValidator)eAnnotationValidator).isValidLocation(nestedEAnnotation, eAnnotation)) |
| { |
| result.remove(nestedEAnnotation); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the filtered collection of nested annotations that are valid for this annotation. |
| * <p> |
| * An induced user interface should provide the ability to specify only the nested annotations returned by this method. |
| * The annotations argument may contain nothing at all, or the {@link EModelElement#getEAnnotations() current nested annotations} of the specified annotation; |
| * an implementation may choose to filter from this collection or to provide its own result, including objects not in this collection, |
| * but it should <b>not</b> remove nested annotations currently contained by the annotation that are valid. |
| * This implementation takes into account the fact that annotations may be specifically designed to annotate other annotations, |
| * i.e., that the nested annotation source might correspond to a {@link org.eclipse.emf.ecore.EAnnotationValidator.Registry#getEAnnotationValidator(String) registered annotation validator} |
| * that considers its annotations {@link BasicEAnnotationValidator#isValidLocation(EAnnotation, EModelElement) valid} when contained by the specified annotation. |
| * As such, this implementation does not remove nested annotations for which there is a registered validator that considers its annotation valid in the specified annotation. |
| * Also, this implementation's result will include an additional annotation for each registered annotation validator that considers its annotations valid when nested in this annotation. |
| * In fact, an override should <b>only</b> add values to those returned by this implementation. |
| * An implementation that overrides this method should also override {@link #isAnnotationsSupported(EAnnotation, EModelElement)}. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param annotations nothing at all, or the {@link EModelElement#getEAnnotations() current or potential nested annotations} of the annotation. |
| * @return the nested annotations that are valid as annotations for this annotation. |
| * @see Assistant#getValidAnnotations(EAnnotation, Collection) |
| */ |
| protected Collection<? extends EAnnotation> getAllValidAnnotations(EAnnotation eAnnotation, EModelElement eModelElement, Collection<? extends EAnnotation> annotations) |
| { |
| List<EAnnotation> result = new ArrayList<EAnnotation>(getValidAnnotations(eAnnotation, eModelElement, annotations)); |
| for (String annotationSource : EAnnotationValidator.Registry.INSTANCE.keySet()) |
| { |
| EAnnotationValidator eAnnotationValidator; |
| try |
| { |
| eAnnotationValidator = EAnnotationValidator.Registry.INSTANCE.getEAnnotationValidator(annotationSource); |
| } |
| catch (RuntimeException exception) |
| { |
| eAnnotationValidator = null; |
| EcorePlugin.INSTANCE.log(exception); |
| } |
| |
| if (eAnnotationValidator instanceof BasicEAnnotationValidator) |
| { |
| BasicEAnnotationValidator basicEAnnotationValidator = (BasicEAnnotationValidator)eAnnotationValidator; |
| EAnnotation nestedEAnnotation = EcoreFactory.eINSTANCE.createEAnnotation(); |
| nestedEAnnotation.setSource(annotationSource); |
| EAnnotation otherEAnnotation = eAnnotation.getEAnnotation(annotationSource); |
| if ((otherEAnnotation == null || basicEAnnotationValidator.isDuplicateValid(eAnnotation, otherEAnnotation, nestedEAnnotation)) |
| && ((BasicEAnnotationValidator)eAnnotationValidator).isValidLocation(nestedEAnnotation, eAnnotation)) |
| { |
| result.add(nestedEAnnotation); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether this annotation's {@link EAnnotation#getDetails() details} are valid. |
| * <p> |
| * This implementation uses the {@link #getProperties(EModelElement) properties of the model element}. |
| * For each detail, it determines whether there is a corresponding feature in the properties. |
| * If not, it validates the detail {@link #validateDetail(EAnnotation, EModelElement, java.util.Map.Entry, DiagnosticChain, Map) without a property feature}. |
| * If so, it validates the detail {@link #validateFeatureDetail(EAnnotation, EModelElement, java.util.Map.Entry, EStructuralFeature, DiagnosticChain, Map) with the property feature}. |
| * If all the details are valid, |
| * it will check whether any {@link EStructuralFeature#isRequired() required} property feature is absent from the details |
| * and {@link #reportMissingEntry(EAnnotation, EModelElement, String, EStructuralFeature, DiagnosticChain, Map) reports it missing}. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether this annotation's details are valid. |
| */ |
| protected boolean validateDetails(EAnnotation eAnnotation, EModelElement eModelElement, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| boolean result = false; |
| Map<String, EStructuralFeature> properties = new LinkedHashMap<String, EStructuralFeature>(getProperties(eModelElement)); |
| for (Map.Entry<String, String> entry : eAnnotation.getDetails()) |
| { |
| String key = entry.getKey(); |
| EStructuralFeature feature = properties.remove(key); |
| if (feature == null) |
| { |
| result &= validateDetail(eAnnotation, eModelElement, entry, diagnostics, context); |
| } |
| else |
| { |
| result &= validateFeatureDetail(eAnnotation, eModelElement, entry, feature, diagnostics, context); |
| } |
| if (!result && diagnostics == null) |
| { |
| return false; |
| } |
| } |
| |
| if (result) |
| { |
| for (Map.Entry<String, EStructuralFeature> entry : properties.entrySet()) |
| { |
| EStructuralFeature feature = entry.getValue(); |
| if (feature.isRequired()) |
| { |
| result = false; |
| if (diagnostics == null) |
| { |
| break; |
| } |
| else |
| { |
| reportMissingEntry(eAnnotation, eModelElement, entry.getKey(), feature, diagnostics, context); |
| } |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Returns a map from key to {@link EStructuralFeature feature}. |
| * These represents the keys that are considered valid and can be processed by this annotation validator. |
| * <p> |
| * This implementation uses {@link #getPropertyClasses(EModelElement)}, iterating over each class and each feature of each class, adding to the map each {@link #isIncludedProperty(EModelElement, EClass, EStructuralFeature) included} feature. |
| * If that method returns an empty list, then implementation returns an empty map. |
| * In that case, {@link #validateDetails(EAnnotation, EModelElement, DiagnosticChain, Map) validating the details} of any annotation will report all detail entries as being ignored. |
| * An annotation validator implement must override either this method, the <code>getPropertyClasses</code> method or the <code>validateDetails</code> method. |
| * </p> |
| * @param eModelElement the model element that is being annotated. |
| * @return a map from key to feature. |
| * @see #validateDetails(EAnnotation, EModelElement, DiagnosticChain, Map) |
| * @see #getPropertyClasses(EModelElement) |
| */ |
| protected Map<String, EStructuralFeature> getProperties(EModelElement eModelElement) |
| { |
| Map<String, EStructuralFeature> properties = new LinkedHashMap<String, EStructuralFeature>(); |
| for (EClass eClass : getPropertyClasses(eModelElement)) |
| { |
| for (EStructuralFeature eStructuralFeature : eClass.getEAllStructuralFeatures()) |
| { |
| if (isIncludedProperty(eModelElement, eClass, eStructuralFeature)) |
| { |
| properties.put(eStructuralFeature.getName(), eStructuralFeature); |
| } |
| } |
| } |
| return Collections.unmodifiableMap(properties); |
| } |
| |
| /** |
| * Returns whether the given structural feature of the given class for the given model element is {@link #getProperties(EModelElement) included as a property}. |
| * @param eModelElement the model element. |
| * @param eClass the class used to model the annotation for the model element. |
| * @param eStructuralFeature a structural feature of the class. |
| * @return whether the given structural feature of the given class for the given model element is included as a property. |
| */ |
| protected boolean isIncludedProperty(EModelElement eModelElement, EClass eClass, EStructuralFeature eStructuralFeature) |
| { |
| return true; |
| } |
| |
| /** |
| * Creates an instance of the modeled representation for the given annotation. |
| * <p> |
| * This implementation {@link EcoreUtil#create(EClass) creates} an instance and {@link #initialize(EObject, EAnnotation) initializes} it. |
| * </p> |
| * @param eClass the class to be instantiated. |
| * @param eAnnotation the annotation with the information that needs to be represented. |
| * @return creates an instance of the modeled representation for the given annotation. |
| */ |
| protected EObject createInstance(EClass eClass, EAnnotation eAnnotation) |
| { |
| EModelElement eModelElement = eAnnotation.getEModelElement(); |
| if (!getPropertyClasses(eModelElement).contains(eClass)) |
| { |
| throw new IllegalArgumentException("The eClass is not a property class of the model element"); |
| } |
| else |
| { |
| return initialize(EcoreUtil.create(eClass), eAnnotation); |
| } |
| } |
| |
| /** |
| * Returns an initialized instance of the given object for the given annotation. |
| * <p> |
| * This implementation handles only the case of modeled attributes. |
| * For each {@link EAnnotation#getDetails() detail entry}, |
| * it looks up the corresponding {@link #getProperties(EModelElement) property} via the key. |
| * If it's not an {@link EAttribute} it will throw an {@link UnsupportedOperationException}. |
| * If the attribute property is {@link EStructuralFeature#isMany() multi-valued}, |
| * it {@link #split(EAnnotation, EModelElement, Map.Entry, String, EStructuralFeature, DiagnosticChain, Map) splits} the detail entry value, and {@link EcoreUtil#convertToString(EDataType, Object) converts} each literal item into a value of the {@link EAttribute#getEAttributeType() attribute's data type}. |
| * If it's single-valued, the literal value is directly converted to the attributes data type. |
| * The resulting list value or single value is {@link EObject#eSet(EStructuralFeature, Object) reflectively set} into the instance. |
| * If the model representation includes references, |
| * an annotation validator implementation must specialize this method for the {@link Assistant#createInstance(EClass, EAnnotation) assistant} to function correctly. |
| * </p> |
| * @param eObject the object to initialize. |
| * @param eAnnotation the annotation used to initialize the object. |
| * @return an initialized instance. |
| */ |
| protected EObject initialize(EObject eObject, EAnnotation eAnnotation) |
| { |
| EModelElement eModelElement = eAnnotation.getEModelElement(); |
| Map<String, EStructuralFeature> properties = getProperties(eModelElement); |
| for (Map.Entry<String, String> entry : eAnnotation.getDetails()) |
| { |
| String key = entry.getKey(); |
| EStructuralFeature eStructuralFeature = properties.get(key); |
| if (eStructuralFeature instanceof EAttribute) |
| { |
| EDataType eDataType = (EDataType)eStructuralFeature.getEType(); |
| String literalValue = entry.getValue(); |
| if (eStructuralFeature.isMany()) |
| { |
| List<String> literalValues = split(eAnnotation, eModelElement, entry, literalValue, eStructuralFeature, null, null); |
| List<Object> value = new ArrayList<Object>(); |
| if (literalValues != null) |
| { |
| for (String itemLiteralValue : literalValues) |
| { |
| value.add(EcoreUtil.createFromString(eDataType, itemLiteralValue)); |
| } |
| } |
| eObject.eSet(eStructuralFeature, value); |
| } |
| else |
| { |
| eObject.eSet(eStructuralFeature, EcoreUtil.createFromString(eDataType, literalValue)); |
| } |
| } |
| else if (eStructuralFeature != null) |
| { |
| throw new UnsupportedOperationException("Initializing of references is not supported"); |
| } |
| } |
| return eObject; |
| } |
| |
| /** |
| * Returns the value of the feature of the modeled object converted to a literal representation as used in {@link EAnnotation#getDetails() detail entry}. |
| * <p> |
| * This implementation handles both {@link EAttribute} and {@link EReference}. |
| * For a {@link EStructuralFeature#isMany() multi-valued} feature, it {@link #convertPropertyReferenceValueToLiteralItem(EObject, EReference, Object) converts} for each item in the list, and {@link #join(EObject, EStructuralFeature, List) joins} them into a single string. |
| * For a single-valued feature, it returns the {@link #convertPropertyReferenceValueToLiteralItem(EObject, EReference, Object) converted} value. |
| * This method is not used by the validator but is useful for specializing the {@link Assistant#convertPropertyValueToLiteral(EObject, EStructuralFeature, Object)}. |
| * </p> |
| * @param eObject the modeled object. |
| * @param eStructuralFeature a feature of that object. |
| * @param value the value to converted to a literal representation. |
| * @return the value of the feature of the modeled object converted to a literal representation. |
| * @see #convertPropertyValueToLiteralItem(EObject, EStructuralFeature, Object) |
| */ |
| protected String convertPropertyValueToLiteral(EObject eObject, EStructuralFeature eStructuralFeature, Object value) |
| { |
| if (eStructuralFeature.isMany()) |
| { |
| List<String> result = new ArrayList<String>(); |
| if (value != null) |
| { |
| @SuppressWarnings("unchecked") |
| List<Object> values = (List<Object>)value; |
| for (Object valueItem : values) |
| { |
| result.add(convertPropertyValueToLiteralItem(eObject, eStructuralFeature, valueItem)); |
| } |
| } |
| return join(eObject, eStructuralFeature, result); |
| } |
| else |
| { |
| return convertPropertyValueToLiteralItem(eObject, eStructuralFeature, value); |
| } |
| } |
| |
| /** |
| * Returns the single value of the feature's {@link ETypedElement#getEType() type} for the modeled object converted to a literal representation as used in {@link EAnnotation#getDetails() detail entry}. |
| * <p> |
| * This implementation delegates to {@link #convertPropertyAttributeValueToLiteralItem(EObject, EAttribute, Object)} or {@link #convertPropertyReferenceValueToLiteralItem(EObject, EReference, Object)} as appropriate. |
| * </p> |
| * @param eObject the modeled object. |
| * @param eStructuralFeature a feature of that object. |
| * @param value the value of the feature's type to converted to a literal representation. |
| * @return a value of the feature's type converted to a literal representation. |
| * @see #convertPropertyValueToLiteral(EObject, EStructuralFeature, Object) |
| */ |
| protected String convertPropertyValueToLiteralItem(EObject eObject, EStructuralFeature eStructuralFeature, Object value) |
| { |
| if (eStructuralFeature instanceof EAttribute) |
| { |
| return convertPropertyAttributeValueToLiteralItem(eObject, (EAttribute)eStructuralFeature, value); |
| } |
| else |
| { |
| return convertPropertyReferenceValueToLiteralItem(eObject, (EReference)eStructuralFeature, value); |
| } |
| } |
| |
| /** |
| * Returns the single value of the attribute's {@link ETypedElement#getEType() type} for the modeled object converted to a literal representation as used in {@link EAnnotation#getDetails() detail entry}. |
| * <p> |
| * This implementation simple uses {@link EcoreUtil#convertToString(EDataType, Object)}. |
| * </p> |
| * @param eObject the modeled object. |
| * @param eAttribute an attribute feature of that object. |
| * @param value the value of the feature's type to converted to a literal representation. |
| * @return a value of the feature's type converted to a literal representation. |
| * @see #convertPropertyValueToLiteral(EObject, EStructuralFeature, Object) |
| */ |
| protected String convertPropertyAttributeValueToLiteralItem(EObject eObject, EAttribute eAttribute, Object value) |
| { |
| return EcoreUtil.convertToString(eAttribute.getEAttributeType(), value); |
| } |
| |
| /** |
| * Returns the single value of the references's {@link ETypedElement#getEType() type} for the modeled object converted to a literal representation as used in {@link EAnnotation#getDetails() detail entry}. |
| * <p> |
| * This implementation is incomplete. |
| * It can't generally be known how to represent a reference to an object. |
| * This implementation looks for a feature called "name", {@link EObject#eGet(EStructuralFeature) gets} the value, and if it's a string returns it. |
| * Failing that, it throws an {@link UnsupportedOperationException}. |
| * </p> |
| * @param eObject the modeled object. |
| * @param eReference a reference feature of that object. |
| * @param value the value of the reference's type to converted to a literal representation. |
| * @return a value of the references's type converted to a literal representation. |
| * @see #convertPropertyValueToLiteral(EObject, EStructuralFeature, Object) |
| */ |
| protected String convertPropertyReferenceValueToLiteralItem(EObject eObject, EReference eReference, Object value) |
| { |
| if (value == null) |
| { |
| return null; |
| } |
| else if (value instanceof EObject) |
| { |
| EObject valueEObject = (EObject)value; |
| EStructuralFeature eStructuralFeature = valueEObject.eClass().getEStructuralFeature("name"); |
| if (eStructuralFeature != null) |
| { |
| Object name = valueEObject.eGet(eStructuralFeature); |
| if (name instanceof String) |
| { |
| return name.toString(); |
| } |
| } |
| } |
| |
| throw new UnsupportedOperationException("Unable to convert '" + value + "' to a literal value for feature " + eReference + " of " + eObject); |
| } |
| |
| /** |
| * Returns the joined list of values of this modeled object's feature. |
| * <p> |
| * This implementation simply separates the individual literal value items with a " ". |
| * </p> |
| * @param eObject the modeled object. |
| * @param eStructuralFeature a feature of that object. |
| * @param literalValues the literal value to join into a single value. |
| * @return |
| */ |
| protected String join(EObject eObject, EStructuralFeature eStructuralFeature, List<String> literalValues) |
| { |
| return XMLTypeFactory.eINSTANCE.convertENTITIESBase(literalValues); |
| } |
| |
| /** |
| * Returns whether the given feature of the given modeled representation is meaningful for the current state of the model. |
| * <p> |
| * This method is used to induce the {@link Assistant#getApplicableProperties(EObject, EAnnotation) applicable properties} by the assistant. |
| * It is not directly used by the annotation validator itself. |
| * This implementation always returns <code>true</code>. |
| * </p> |
| * @param eObject the modeled object in question. |
| * @param eStructuralFeature a feature of that object. |
| * @return whether the given feature of the given modeled representation is meaningful for the current state of the model. |
| */ |
| protected boolean isApplicable(EObject eObject, EStructuralFeature eStructuralFeature) |
| { |
| return true; |
| } |
| |
| /** |
| * Returns whether this detail entry is valid. |
| * <p> |
| * This method is only called when there is no {@link #getProperties(EModelElement) property} associated with this entry's {@link java.util.Map.Entry#getKey() key}. |
| * This implementation always {@link #reportIgnoredEntry(EAnnotation, EModelElement, Map.Entry, DiagnosticChain, Map) reports an ignored entry}. |
| * An annotation validator implementation may choose to support validation by specializing this method, rather than {@link #getPropertyClasses(EModelElement) providing a modeled representation}, |
| * but the {@link Assistant assistant} will not provide any useful support. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether this detail entry is valid. |
| */ |
| protected boolean validateDetail(EAnnotation eAnnotation, EModelElement eModelElement, Map.Entry<String, String> entry, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| if (diagnostics != null) |
| { |
| reportIgnoredEntry(eAnnotation, eModelElement, entry, diagnostics, context); |
| } |
| return false; |
| } |
| |
| /** |
| * Returns whether the value of this detail entry for the corresponding feature is valid. |
| * <p> |
| * This implementation delegates |
| * to {@link #validateAttributeDetailLiteralValue(EAnnotation, EModelElement, java.util.Map.Entry, EAttribute, List, DiagnosticChain, Map) validateAttributeDetail} |
| * or to {@link #validateReferenceDetailLiteralValue(EAnnotation, EModelElement, Map.Entry, EReference, List, DiagnosticChain, Map) validateReferenceDetail} |
| * depending on whether the feature is an {@link EAttribute} or an {@link EReference}. |
| * It {@link #createValueDiagnostic(EAnnotation, EModelElement, java.util.Map.Entry, EStructuralFeature) creates} a place holder diagnostic that it passed to those methods, |
| * so all diagnostics gathered by those methods are grouped as {@link Diagnostic#getChildren() children} of the placeholder. |
| * Both those methods build the corresponding modeled values as a side-effect. |
| * If the detail entry is otherwise valid, |
| * the modeled value {@link #validateFeatureDetailValue(EAnnotation, EModelElement, java.util.Map.Entry, EStructuralFeature, List, DiagnosticChain, Map) is validated}. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param feature the {@link #getProperties(EModelElement) property} associated with entry. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether the value of this detail entry for the corresponding feature is valid. |
| */ |
| protected boolean validateFeatureDetail( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EStructuralFeature feature, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| BasicDiagnostic badValueDiagnostic = diagnostics == null ? null : createValueDiagnostic(eAnnotation, eModelElement, entry, feature); |
| List<Object> values = new ArrayList<Object>(); |
| boolean result = feature instanceof EAttribute |
| ? validateAttributeDetailLiteralValue(eAnnotation, eModelElement, entry, (EAttribute)feature, values, badValueDiagnostic, context) |
| : validateReferenceDetailLiteralValue(eAnnotation, eModelElement, entry, (EReference)feature, values, badValueDiagnostic, context); |
| if (result) |
| { |
| result &= validateFeatureDetailValue(eAnnotation, eModelElement, entry, feature, values, badValueDiagnostic, context); |
| } |
| if (!result && diagnostics != null) |
| { |
| diagnostics.add(badValueDiagnostic); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether the modeled values for this detail entry's corresponding feature are valid. |
| * <p> |
| * For a {@link EStructuralFeature#isMany() many-valued} feature, it validates that the {@link EStructuralFeature#getLowerBound() lower} and {@link EStructuralFeature#getUpperBound() upper} bounds are respected. |
| * For a singled valued feature that is {@link EStructuralFeature#isRequired() required}, it validates that the value is present; |
| * in the single-valued case, the values list should contain a single value. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param feature the {@link #getProperties(EModelElement) property} associated with entry. |
| * @param values the list of instance values for this entry. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether the modeled values for this detail entry's corresponding feature are valid. |
| */ |
| protected boolean validateFeatureDetailValue( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EStructuralFeature feature, |
| List<Object> values, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| boolean result = true; |
| int size = values.size(); |
| if (feature.isMany()) |
| { |
| int lowerBound = feature.getLowerBound(); |
| if (lowerBound > 0 && size < lowerBound) |
| { |
| if (diagnostics != null) |
| { |
| reportTooFewValues(eAnnotation, eModelElement, entry, feature, values, size, lowerBound, diagnostics, context); |
| } |
| result = false; |
| } |
| int upperBound = feature.getUpperBound(); |
| if (upperBound > 0 && size > upperBound) |
| { |
| if (diagnostics != null) |
| { |
| reportTooManyValues(eAnnotation, eModelElement, entry, feature, values, size, upperBound, diagnostics, context); |
| } |
| result = false; |
| } |
| } |
| else |
| { |
| if (feature.isRequired()) |
| { |
| if (size != 0 || values.get(0) == null) |
| { |
| result = false; |
| if (diagnostics != null) |
| { |
| reportMissingRequiredEntryValue(eAnnotation, eModelElement, feature, values, diagnostics, context); |
| } |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether the literal value of this detail entry for the corresponding attribute is valid. |
| * <p> |
| * This implementation, |
| * for a {@link EStructuralFeature#isMany() many-valued} attribute, |
| * {@link #split(EAnnotation, EModelElement, Map.Entry, String, EStructuralFeature, DiagnosticChain, Map) splits} the detail value, if present, into a list of literal values |
| * and {@link #validateAttributeDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EAttribute, String, List, DiagnosticChain, Map) validates each literal value}. |
| * For a single-valued attribute, it {@link #validateAttributeDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EAttribute, String, List, DiagnosticChain, Map) directly validates} the literal value. |
| * As a side-effect, each literal value of a many-valued attribute, or the literal value of a single-valued attribute, |
| * is converted to an instance of the attribute's {@link EAttribute#getEAttributeType() data type} and is added to the data values list. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param attribute feature the {@link EAttribute attribute} {@link #getProperties(EModelElement) property} associated with entry. |
| * @param dataValues the list in which to accumulate valid instance values. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether the literal value of this detail entry for the corresponding attribute is valid. |
| * |
| * @see #validateAttributeDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EAttribute, String, List, DiagnosticChain, Map) |
| */ |
| protected boolean validateAttributeDetailLiteralValue( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EAttribute attribute, |
| List<Object> dataValues, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| boolean result = true; |
| String literalValue = entry.getValue(); |
| if (attribute.isMany()) |
| { |
| List<String> literalValues = split(eAnnotation, eModelElement, entry, literalValue, attribute, diagnostics, context); |
| if (literalValues != null) |
| { |
| for (String literalValueItem : literalValues) |
| { |
| result &= validateAttributeDetailValueLiteral(eAnnotation, eModelElement, entry, attribute, literalValueItem, dataValues, diagnostics, context); |
| if (!result && diagnostics == null) |
| { |
| break; |
| } |
| } |
| } |
| } |
| else |
| { |
| result = validateAttributeDetailValueLiteral(eAnnotation, eModelElement, entry, attribute, literalValue, dataValues, diagnostics, context); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether the given literal value is valid with respect to this detail entry's corresponding attribute's {@link EAttribute#getEAttributeType() data type}. |
| * As a side-effect, if the literal value can be converted to an instance value, the corresponding instance value is added to the list of data values. |
| * <p> |
| * This implementation first tries to {@link EcoreUtil#createFromString(EDataType, String) convert} the literal value to an instance value of the data type. |
| * If that fails, it creates a diagnostic that includes the exception {@link Exception#getLocalizedMessage() message}. |
| * Otherwise, it adds the instance value to the list of values |
| * and {@link EValidator#validate(EDataType, Object, DiagnosticChain, Map) validates} the instance value. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param attribute feature the {@link EAttribute attribute} {@link #getProperties(EModelElement) property} associated with entry. |
| * @param literalValue the literal value of the data type. |
| * @param dataValues the list in which to accumulate a valid instance value. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether the given literal value is valid with respect to this detail entry's corresponding attribute's data type. |
| */ |
| protected boolean validateAttributeDetailValueLiteral( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EAttribute attribute, |
| String literalValue, |
| List<Object> dataValues, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| EDataType dataType = attribute.getEAttributeType(); |
| boolean result; |
| try |
| { |
| Object value = EcoreUtil.createFromString(dataType, literalValue); |
| dataValues.add(value); |
| EValidator rootEValidator = getRootEValidator(context); |
| result = rootEValidator == null || rootEValidator.validate(dataType, value, diagnostics, context); |
| } |
| catch (RuntimeException exception) |
| { |
| result = false; |
| if (diagnostics != null) |
| { |
| reportInvalidValueLiteral(eAnnotation, eModelElement, entry, attribute, literalValue, dataType, diagnostics, exception, context); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether the literal value of this detail entry for the corresponding reference is valid. |
| * <p> |
| * This implementation, |
| * for a {@link EStructuralFeature#isMany() many-valued} reference, |
| * {@link #split(EAnnotation, EModelElement, Map.Entry, String, EStructuralFeature, DiagnosticChain, Map) splits} the detail value, if present, into a list of literal values |
| * and {@link #validateReferenceDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EReference, String, List, DiagnosticChain, Map) validates each literal value}. |
| * For a single-valued attribute, it {@link #validateReferenceDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EReference, String, List, DiagnosticChain, Map) directly validates} the literal value. |
| * As a side-effect, each literal value of a many-valued reference, or the literal value of a single-valued reference, |
| * is converted to an instance of the references's {@link EReference#getEReferenceType() class} and added to the reference values list. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param reference the {@link EReference reference} {@link #getProperties(EModelElement) property} associated with entry. |
| * @param referenceValues the list in which to accumulate valid instance values. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether the literal value of this detail entry for the corresponding reference is valid. |
| */ |
| protected boolean validateReferenceDetailLiteralValue( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EReference reference, |
| List<Object> referenceValues, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| boolean result = true; |
| String literalValue = entry.getValue(); |
| if (reference.isMany()) |
| { |
| List<String> literalValues = split(eAnnotation, eModelElement, entry, literalValue, reference, diagnostics, context); |
| if (literalValues != null) |
| { |
| for (String literalValueItem : literalValues) |
| { |
| result &= validateReferenceDetailValueLiteral(eAnnotation, eModelElement, entry, reference, literalValueItem, referenceValues, diagnostics, context); |
| if (!result && diagnostics == null) |
| { |
| break; |
| } |
| } |
| } |
| } |
| else |
| { |
| result = validateReferenceDetailValueLiteral(eAnnotation, eModelElement, entry, reference, literalValue, referenceValues, diagnostics, context); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns whether the given literal value is valid with respect to this detail entry's corresponding reference's {@link EReference#getEReferenceType() class}. |
| * As a side-effect, if the literal value can be converted to an instance value, the corresponding instance value is added to the list of reference values. |
| * <p> |
| * This implementation always returns <code>false</code> and {@link #reportInvalidReferenceLiteral(EAnnotation, EModelElement, Map.Entry, EReference, String, DiagnosticChain, Map) reports an invalid reference literal}. |
| * An annotation validator implementation that supports reference literals must specialize this method. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param reference the {@link EReference reference} {@link #getProperties(EModelElement) property} associated with entry. |
| * @param literalValue the literal value of the class. |
| * @param referenceValues the list in which to accumulate valid instance values. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return whether the given literal value is valid with respect to this detail entry's corresponding references's class. |
| */ |
| protected boolean validateReferenceDetailValueLiteral( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EReference reference, |
| String literalValue, |
| List<Object> referenceValues, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| if (diagnostics != null) |
| { |
| reportInvalidReferenceLiteral(eAnnotation, eModelElement, entry, reference, literalValue, diagnostics, context); |
| } |
| return false; |
| } |
| |
| /** |
| * Splits the literal value into a list of literal values as appropriate for this feature. |
| * <p> |
| * This implementation splits the values at whitespace boundaries for all features.. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param eModelElement the annotation's {@link EAnnotation#getEModelElement() containing} model element. |
| * @param entry the annotation {@link EAnnotation#getDetails() detail} in question. |
| * @param literalValue a literal value of this feature's {@link EStructuralFeature#getEType() type}. |
| * @param feature the {@link #getProperties(EModelElement) property} associated with entry. |
| * @param diagnostics a place to accumulate diagnostics; if it's <code>null</code>, no diagnostics should be produced. |
| * @param context a place to cache information, if it's <code>null</code>, no cache is supported. |
| * @return splits the literal value into a list of literal values as appropriate for this feature. |
| * |
| * @see #validateAttributeDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EAttribute, String, List, DiagnosticChain, Map) |
| * @see #validateReferenceDetailValueLiteral(EAnnotation, EModelElement, Map.Entry, EReference, String, List, DiagnosticChain, Map) |
| */ |
| protected List<String> split( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| String literalValue, |
| EStructuralFeature feature, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| return XMLTypeFactory.eINSTANCE.createENTITIESBase(literalValue); |
| } |
| |
| /** |
| * @see #INVALID_REFERENCE_LITERAL |
| */ |
| protected void reportInvalidReferenceLiteral( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EReference reference, |
| String literalValue, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_REFERENCE_LITERAL, |
| getString( |
| getEcoreResourceLocator(), |
| "_UI_InvalidValue_diagnostic", |
| literalValue, |
| getString(getEcoreResourceLocator(), "_UI_InvalidReferenceValue_substitution", reference.getEReferenceType().getName())), |
| literalValue, |
| reference.getEReferenceType())); |
| } |
| |
| /** |
| * @see #INVALID_VALUE_LITERAL |
| */ |
| protected void reportInvalidValueLiteral( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EAttribute attribute, |
| String literalValue, |
| EDataType dataType, |
| DiagnosticChain diagnostics, |
| RuntimeException exception, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_VALUE_LITERAL, |
| getString(getEcoreResourceLocator(), "_UI_InvalidValue_diagnostic", literalValue, exception.getLocalizedMessage()), |
| literalValue, |
| dataType)); |
| } |
| |
| /** |
| * @see #MISSING_REQUIRED_ENTRY_VALUE |
| */ |
| protected void reportMissingRequiredEntryValue( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| EStructuralFeature feature, |
| List<Object> values, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| MISSING_REQUIRED_ENTRY_VALUE, |
| getString(getEcoreResourceLocator(), "_UI_InvalidValueRequiredFeatureMustBeSet_diagnostic"), |
| (Object)null)); |
| } |
| |
| /** |
| * @see #TOO_FEW_VALUES |
| */ |
| protected void reportTooFewValues( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EStructuralFeature feature, |
| List<Object> values, |
| int size, |
| int lowerBound, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic(Diagnostic.WARNING, TOO_FEW_VALUES, getString(getEcoreResourceLocator(), "_UI_InvalidValueFeatureHasTooFewValues_diagnostic", size, lowerBound), values)); |
| } |
| |
| /** |
| * @see #TOO_MANY_VALUES |
| */ |
| protected void reportTooManyValues( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Map.Entry<String, String> entry, |
| EStructuralFeature feature, |
| List<Object> values, |
| int size, |
| int upperBound, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic(Diagnostic.WARNING, TOO_MANY_VALUES, getString(getEcoreResourceLocator(), "_UI_InvalidValueFeatureHasTooManyValues_diagnostic", size, upperBound), values)); |
| } |
| |
| /** |
| * @see #INVALID_LOCATION |
| */ |
| protected void reportInvalidLocation(EAnnotation eAnnotation, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_LOCATION, |
| getString(getEcoreResourceLocator(), "_UI_InvalidAnnotationLocation_diagnostic", annotationName, getValidLocationDescription()), |
| eAnnotation, |
| EcorePackage.Literals.EANNOTATION__SOURCE)); |
| } |
| |
| /** |
| * @see #INVALID_DUPLICATE |
| */ |
| protected void reportDuplicate( |
| EAnnotation primaryEAnnotation, |
| EAnnotation secondaryEAnnotation, |
| EModelElement eModelElement, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_DUPLICATE, |
| getString(getEcoreResourceLocator(), "_UI_InvalidDuplicateAnnotation_diagnostic", annotationName, getValidLocationDescription()), |
| secondaryEAnnotation, |
| EcorePackage.Literals.EANNOTATION__SOURCE, |
| primaryEAnnotation)); |
| } |
| |
| /** |
| * @see #IGNORED_ENTRY |
| */ |
| protected void reportIgnoredEntry(EAnnotation eAnnotation, EModelElement eModelElement, Map.Entry<String, String> entry, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| IGNORED_ENTRY, |
| getString(getEcoreResourceLocator(), "_UI_InvalidAnnotationEntryKey_diagnostic", annotationName, entry.getKey()), |
| entry, |
| EcorePackage.Literals.ESTRING_TO_STRING_MAP_ENTRY__KEY)); |
| } |
| |
| /** |
| * @see #MISSING_ENTRY |
| */ |
| protected void reportMissingEntry( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| String key, |
| EStructuralFeature property, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| MISSING_ENTRY, |
| getString(getEcoreResourceLocator(), "_UI_MissingAnnotationEntryKey_diagnostic", key), |
| eAnnotation, |
| EcorePackage.Literals.EANNOTATION__DETAILS)); |
| } |
| |
| /** |
| * @see #IGNORED_REFERENCES |
| */ |
| protected void reportIgnoredReferences( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Collection<? extends EObject> ignoredReferences, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| List<Object> data = new ArrayList<Object>(); |
| data.add(eAnnotation); |
| data.add(EcorePackage.Literals.EANNOTATION__REFERENCES); |
| data.addAll(ignoredReferences); |
| diagnostics.add( |
| createDiagnostic(Diagnostic.WARNING, IGNORED_REFERENCES, getString(getEcoreResourceLocator(), "_UI_IgnoredAnnotationReferences_diagnostic", annotationName), data.toArray())); |
| } |
| |
| /** |
| * @see #INVALID_REFERENCE |
| */ |
| protected void reportInvalidReference(EAnnotation eAnnotation, EModelElement eModelElement, EObject reference, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_REFERENCE, |
| getString(getEcoreResourceLocator(), "_UI_InvalidAnnotationReference_diagnostic", annotationName, EObjectValidator.getObjectLabel(reference, context)), |
| eAnnotation, |
| EcorePackage.Literals.EANNOTATION__REFERENCES, |
| reference)); |
| } |
| |
| /** |
| * @see #INVALID_REFERENCE |
| */ |
| protected void reportIgnoredContents( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Collection<? extends EObject> ignoredContents, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| List<Object> data = new ArrayList<Object>(); |
| data.add(eAnnotation); |
| data.add(EcorePackage.Literals.EANNOTATION__CONTENTS); |
| data.addAll(ignoredContents); |
| diagnostics.add( |
| createDiagnostic(Diagnostic.WARNING, INVALID_REFERENCE, getString(getEcoreResourceLocator(), "_UI_IgnoredAnnotationContents_diagnostic", annotationName), data.toArray())); |
| } |
| |
| /** |
| * @see #INVALID_CONTENT |
| */ |
| protected void reportInvalidContent(EAnnotation eAnnotation, EModelElement eModelElement, EObject content, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_CONTENT, |
| getString(getEcoreResourceLocator(), "_UI_InvalidAnnotationContent_diagnostic", annotationName, EObjectValidator.getObjectLabel(content, context)), |
| eAnnotation, |
| EcorePackage.Literals.EANNOTATION__CONTENTS, |
| content)); |
| } |
| |
| /** |
| * @see #IGNORED_ANNOTATIONS |
| */ |
| protected void reportIgnoredAnnotations( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| Collection<? extends EAnnotation> ignoredAnnotations, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| List<Object> data = new ArrayList<Object>(); |
| data.add(eAnnotation); |
| data.add(EcorePackage.Literals.EMODEL_ELEMENT__EANNOTATIONS); |
| data.addAll(ignoredAnnotations); |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| IGNORED_ANNOTATIONS, |
| getString(getEcoreResourceLocator(), "_UI_IgnoredAnnotationAnnotations_diagnostic", annotationName), |
| data.toArray())); |
| } |
| |
| /** |
| * @see #INVALID_ANNOTATION |
| */ |
| protected void reportInvalidAnnotation( |
| EAnnotation eAnnotation, |
| EModelElement eModelElement, |
| EAnnotation nestedEAnnotation, |
| DiagnosticChain diagnostics, |
| Map<Object, Object> context) |
| { |
| diagnostics.add( |
| createDiagnostic( |
| Diagnostic.WARNING, |
| INVALID_ANNOTATION, |
| getString(getEcoreResourceLocator(), "_UI_InvalidAnnotationAnnotation_diagnostic", annotationName, EObjectValidator.getObjectLabel(nestedEAnnotation, context)), |
| eAnnotation, |
| EcorePackage.Literals.EANNOTATION__CONTENTS, |
| nestedEAnnotation)); |
| } |
| |
| /** |
| * Returns a description of the valid locations supported for annotations of this annotation validator. |
| * <p> |
| * A annotation validator implementation must provide the key <code>"_UI_Valid" + {@link #annotationName annotationName} + "AnnotationLocation_substitution"</code> in its {@link #getResourceLocator() resource locator} |
| * with a very short description of the valid locations of annotations. |
| * </p> |
| * @return a description of the valid locations supported for annotations of this annotation validator. |
| * @see #reportInvalidLocation(EAnnotation, DiagnosticChain, Map) |
| */ |
| protected String getValidLocationDescription() |
| { |
| return getString(getResourceLocator(), "_UI_Valid" + annotationName + "AnnotationLocation_substitution"); |
| } |
| |
| /** |
| * Creates the placeholder diagnostic used by {@link #validateFeatureDetail(EAnnotation, EModelElement, Map.Entry, EStructuralFeature, DiagnosticChain, Map) validateFeatureDetail}. |
| * Diagnostics about problems with the value of a {@link EAnnotation#getDetails() detail entry} will be nested as {@link Diagnostic#getChildren() children} of this annotation. |
| * @param eAnnotation the annotation. |
| * @param eModelElement the model element of that annotation. |
| * @param entry the entry. |
| * @param feature the feature. |
| * @return Creates a placeholder diagnostic. |
| */ |
| protected BasicDiagnostic createValueDiagnostic(EAnnotation eAnnotation, EModelElement eModelElement, Map.Entry<String, String> entry, EStructuralFeature feature) |
| { |
| return createDiagnostic( |
| Diagnostic.OK, |
| INVALID_DETAIL_VALUE, |
| getString(getEcoreResourceLocator(), "_UI_InvalidAnnotationEntryValue_diagnostic", annotationName, entry.getKey()), |
| entry, |
| EcorePackage.Literals.ESTRING_TO_STRING_MAP_ENTRY__VALUE, |
| feature); |
| } |
| |
| /** |
| * Returns the resource locator for fetching messages supported directly by the base implementation. |
| * @return the resource locator for fetching messages supported directly by the base implementation. |
| */ |
| protected ResourceLocator getEcoreResourceLocator() |
| { |
| return EcorePlugin.INSTANCE; |
| } |
| |
| /** |
| * Creates a diagnostic using the given parameters and the {@link #annotationSource}. |
| * @param severity |
| * @param code |
| * @param message |
| * @param data |
| * @return a diagnostic. |
| */ |
| protected BasicDiagnostic createDiagnostic(int severity, int code, String message, Object... data) |
| { |
| return new BasicDiagnostic(severity, diagnosticSource, code, message, data); |
| } |
| |
| /** |
| * Fetches a translated string from the resource locator using the message key and the give substitutions, if any. |
| * @param resourceLocator |
| * @param key |
| * @param substitutions |
| * @return the translated string for the message key. |
| */ |
| protected String getString(ResourceLocator resourceLocator, String key, Object... substitutions) |
| { |
| return substitutions == null ? resourceLocator.getString(key) : resourceLocator.getString(key, substitutions); |
| } |
| |
| /** |
| * Returns the root validator of the context. |
| * @param context the context. |
| * @return the root validator |
| */ |
| protected EValidator getRootEValidator(Map<Object, Object> context) |
| { |
| if (context != null) |
| { |
| EValidator result = (EValidator)context.get(EValidator.class); |
| if (result != null) |
| { |
| return result; |
| } |
| } |
| |
| return Diagnostician.INSTANCE; |
| } |
| |
| /** |
| * Returns the package loaded from the location specified by the given URI. |
| * If the resource loads successfully---the org.eclipse.ecore.xmi jar must be on the class---and it contains an {@link EPackage}, |
| * that package is registered in the {@link org.eclipse.emf.ecore.EPackage.Registry#INSTANCE}. |
| * |
| * @param uri the location of a resource containing an EPackage. |
| * @return the loaded package registered package. |
| */ |
| protected static EPackage loadEPackage(String uri) |
| { |
| Resource resource = new EPackageImpl() |
| { |
| @Override |
| public Resource createResource(String uri) |
| { |
| Resource resource = super.createResource(uri); |
| resource.getContents().clear(); |
| resource.unload(); |
| return resource; |
| } |
| }.createResource(uri); |
| |
| try |
| { |
| resource.load(null); |
| EPackage ePackage = (EPackage)EcoreUtil.getObjectByType(resource.getContents(), EcorePackage.Literals.EPACKAGE); |
| if (ePackage != null) |
| { |
| String nsURI = ePackage.getNsURI(); |
| EPackage.Registry.INSTANCE.put(nsURI, ePackage); |
| resource.setURI(URI.createURI(nsURI)); |
| } |
| return ePackage; |
| } |
| catch (IOException e) |
| { |
| return null; |
| } |
| } |
| |
| /** |
| * An assistant that is useful for inducing a user interface that represents the annotation information in a more structured way |
| * using {@link #getPropertyClasses(EModelElement) modeled objects} that are created by {@link #createInstance(EClass, EAnnotation)}. |
| * This implementation delegates to the {@link BasicEAnnotationValidator annotation validator} which in turn provides an {@link BasicEAnnotationValidator#getAssistant() accessor} for its to corresponding assistant. |
| * This class generally does not need to be specialized nor instantiated because every annotation validator provides an assistant. |
| * This class therefore is abstract though none of its methods are abstract. |
| */ |
| public static abstract class Assistant |
| { |
| /** |
| * The annotation validator to which this assistant delegates. |
| */ |
| protected final BasicEAnnotationValidator eAnnotationValidator; |
| |
| /** |
| * Creates an instance that delegates to the give annotation validator. |
| */ |
| public Assistant(BasicEAnnotationValidator eAnnotationValidator) |
| { |
| this.eAnnotationValidator = eAnnotationValidator; |
| } |
| |
| /** |
| * Returns whether this annotation with this annotation validator's {@link EAnnotation#getSource() annotation source} is valid at its {@link EAnnotation#getEModelElement() current location}. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#isValidLocation(EAnnotation)}. |
| * An induced user interface can use this to determine if it can/should use this assistant's information for representing modeled annotation information. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @return whether this annotation with this annotation validator's annotation source is valid at its current location. |
| */ |
| public boolean isValidLocation(EAnnotation eAnnotation) |
| { |
| return eAnnotationValidator.isValidLocation(eAnnotation); |
| } |
| |
| /** |
| * Returns a map from key to {@link EStructuralFeature feature}. |
| * These represents the keys that are considered valid and can be processed by this annotation validator. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#getProperties(EModelElement)}. |
| * An induced user interface can use this method to determine which properties to display. |
| * </p> |
| * @param eModelElement the model element that is being annotated. |
| * @return a map from key to feature. |
| * @see #getApplicableProperties(EObject, EAnnotation) |
| */ |
| public Map<String, EStructuralFeature> getProperties(EModelElement eModelElement) |
| { |
| return eAnnotationValidator.getProperties(eModelElement); |
| } |
| |
| /** |
| * Returns the model classes used to represent annotations for the given model element. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#getPropertyClasses(EModelElement)}. |
| * An induced user interface can use this method to determine which instances to create for display purposes. |
| * </p> |
| * @param eModelElement the model element in question. |
| * @return the model classes used to represent annotations for the given model element. |
| */ |
| public List<EClass> getPropertyClasses(EModelElement eModelElement) |
| { |
| return eAnnotationValidator.getPropertyClasses(eModelElement); |
| } |
| |
| /** |
| * Creates an initialized instance of the modeled representation for the given annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#createInstance(EClass, EAnnotation)}. |
| * An induced user interface can use this method to create instances for display purposes. |
| * </p> |
| * @param eClass the class to be instantiated. |
| * @param eAnnotation the annotation with the information that needs to be represented. |
| * @return creates an initialized instance of the modeled representation for the given annotation. |
| */ |
| public EObject createInstance(EClass eClass, EAnnotation eAnnotation) |
| { |
| return eAnnotationValidator.createInstance(eClass, eAnnotation); |
| } |
| |
| public String convertPropertyValueToLiteral(EObject eObject, EStructuralFeature eStructuralFeature, Object value) |
| { |
| return eAnnotationValidator.convertPropertyValueToLiteral(eObject, eStructuralFeature, value); |
| } |
| |
| /** |
| * Returns the {@link #getProperties(EModelElement) subset of properties} that are applicable for the current state of the modeled annotation instance. |
| * <p> |
| * This subset includes only those properties of the annotation's {@link EAnnotation#getEModelElement() containing} model element |
| * for which {@link BasicEAnnotationValidator#isApplicable(EObject, EStructuralFeature)} returns <code>true</code>. |
| * An induced user interface should avoid displaying properties that are not applicable for the current state of the modeled annotation instance. |
| * </p> |
| * @param eObject the modeled instance in question. |
| * @param eAnnotation the corresponding annotation of that modeled instance. |
| * @return the subset of properties that are applicable for the current state of the modeled annotation instance. |
| */ |
| public Map<String, EStructuralFeature> getApplicableProperties(EObject eObject, EAnnotation eAnnotation) |
| { |
| EModelElement eModelElement = eAnnotation.getEModelElement(); |
| Map<String, EStructuralFeature> properties = getProperties(eModelElement); |
| Map<String, EStructuralFeature> result = new LinkedHashMap<String, EStructuralFeature>(); |
| for (Map.Entry<String, EStructuralFeature> entry : properties.entrySet()) |
| { |
| EStructuralFeature eStructuralFeature = entry.getValue(); |
| if (eAnnotationValidator.isApplicable(eObject, eStructuralFeature)) |
| { |
| result.put(entry.getKey(), eStructuralFeature); |
| } |
| } |
| return Collections.unmodifiableMap(result); |
| } |
| |
| /** |
| * Returns whether {@link EAnnotation#getReferences() references} are meaningful for this annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#isReferencesSupported(EAnnotation, EModelElement)}, |
| * passing in the {@link EAnnotation#getEModelElement() containing} model element. |
| * An induced user interface should avoid providing the ability to specify references when this returns <code>false</code>. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @return whether references are meaningful for this annotation. |
| */ |
| public boolean isReferencesSupported(EAnnotation eAnnotation) |
| { |
| return eAnnotationValidator.isReferencesSupported(eAnnotation, eAnnotation.getEModelElement()); |
| } |
| |
| /** |
| * Returns the filtered collection of references that are valid for this annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#getValidReferences(EAnnotation, EModelElement, Collection)}, passing in the {@link EAnnotation#getEModelElement() containing model element}. |
| * An induced user interface should provide the ability to specify only the references returned by this method. |
| * The references argument may contain all reachable objects, some subset there of, or none at all; |
| * an implementation may choose to filter from this collection or to provide its own result, including objects not in this collection. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param references all reachable objects, some subset there of, or none at all. |
| * @return the objects that are valid as references for this annotation. |
| */ |
| public Collection<?> getValidReferences(EAnnotation eAnnotation, Collection<?> references) |
| { |
| return eAnnotationValidator.getValidReferences(eAnnotation, eAnnotation.getEModelElement(), references); |
| } |
| |
| /** |
| * Returns whether {@link EAnnotation#getContents() contents} are meaningful for this annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#isContentsSupported(EAnnotation, EModelElement)}, |
| * passing in the {@link EAnnotation#getEModelElement() containing} model element and an empty list. |
| * An induced user interface should avoid providing the ability to specify contents when this returns <code>false</code>. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @return whether contents are meaningful for this annotation. |
| */ |
| public boolean isContentsSupported(EAnnotation eAnnotation) |
| { |
| return eAnnotationValidator.isContentsSupported(eAnnotation, eAnnotation.getEModelElement()); |
| } |
| |
| /** |
| * Returns the filtered collection of contents that are valid for this annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#getValidContents(EAnnotation, EModelElement, Collection)} |
| * passing in the {@link EAnnotation#getEModelElement() containing} model element. |
| * An induced user interface should provide the ability to specify only the contents returned by this method. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param contents nothing at all, or the {@link EAnnotation#getContents() potential contents} of the annotation. |
| * @return the objects that are valid as contents for this annotation. |
| */ |
| public Collection<? extends EObject> getValidContents(EAnnotation eAnnotation, Collection<? extends EObject> contents) |
| { |
| return eAnnotationValidator.getValidContents(eAnnotation, eAnnotation.getEModelElement(), contents); |
| } |
| |
| /** |
| * Returns whether {@link EAnnotation#getEAnnotations() nested annotations} are meaningful for this annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#isAnnotationsSupported(EAnnotation, EModelElement)}, passing in the {@link EAnnotation#getEModelElement() containing model element}. |
| * An induced user interface should avoid providing the ability to specify nested annotations when this returns <code>false</code>. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @return whether annotations are meaningful for this annotation. |
| */ |
| public boolean isAnnotationsSupported(EAnnotation eAnnotation) |
| { |
| return eAnnotationValidator.isAnnotationsSupported(eAnnotation, eAnnotation.getEModelElement()); |
| } |
| |
| /** |
| * Returns the filtered collection of nested annotations that are valid for this annotation. |
| * <p> |
| * The implementation delegates to {@link BasicEAnnotationValidator#getAllValidAnnotations(EAnnotation, EModelElement, Collection)} |
| * passing in the {@link EAnnotation#getEModelElement() containing} model element. |
| * An induced user interface should provide the ability to specify only the nested annotations returned by this method. |
| * </p> |
| * @param eAnnotation the annotation in question. |
| * @param annotations nothing at all, or the {@link EModelElement#getEAnnotations() current or potential nested annotations} of the annotation. |
| * @return the annotations that are valid as nested annotations for this annotation. |
| */ |
| public Collection<? extends EAnnotation> getValidAnnotations(EAnnotation eAnnotation, Collection<? extends EAnnotation> annotations) |
| { |
| return eAnnotationValidator.getAllValidAnnotations(eAnnotation, eAnnotation.getEModelElement(), annotations); |
| } |
| } |
| } |