org.eclipse.xtext.validation.NamesAreUniqueValidationHelper Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2009 itemis AG (http://www.itemis.eu) 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.xtext.validation;
import java.util.Iterator;
import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.SimpleAttributeResolver;
import java.util.Map;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
/**
* @author Sebastian Zarnekow - Initial contribution and API
*/
public class NamesAreUniqueValidationHelper implements INamesAreUniqueValidationHelper {
private ImmutableSet clusterTypes = getClusterTypes();
/**
* Initialize the set of clustering types. A type is considered to be clustering
* if any instance of that type has to have a unique name when
* it is transformed to an {@link org.eclipse.xtext.resource.IEObjectDescription}.
* Instances that do not belong to the same cluster may have the same exported name.
* A clustering type will often be some kind of root type in a type hierarchy.
*/
protected ImmutableSet getClusterTypes() {
return ImmutableSet.of(EcorePackage.Literals.EOBJECT);
}
public void checkUniqueNames(Iterable descriptions,
ValidationMessageAcceptor acceptor) {
checkUniqueNames(descriptions, null, acceptor);
}
/**
*
* {@inheritDoc}
*
* The cancel indicator will be queried everytime a description has been processed.
* It should provide a fast answer about its canceled state.
*/
public void checkUniqueNames(Iterable descriptions,
CancelIndicator cancelIndicator,
ValidationMessageAcceptor acceptor) {
Iterator iter = descriptions.iterator();
if (!iter.hasNext())
return;
Map> clusterToNames = Maps.newHashMap();
while(iter.hasNext()) {
IEObjectDescription description = iter.next();
checkDescriptionForDuplicatedName(description, clusterToNames, acceptor);
if (cancelIndicator != null && cancelIndicator.isCanceled())
return;
}
}
protected void checkDescriptionForDuplicatedName(
IEObjectDescription description,
Map> clusterTypeToName,
ValidationMessageAcceptor acceptor) {
EObject object = description.getEObjectOrProxy();
EClass eClass = object.eClass();
QualifiedName qualifiedName = description.getName();
EClass clusterType = getAssociatedClusterType(eClass);
Map nameToDescription = clusterTypeToName.get(clusterType);
if (nameToDescription == null) {
nameToDescription = Maps.newHashMap();
nameToDescription.put(qualifiedName, description);
clusterTypeToName.put(clusterType, nameToDescription);
} else {
if (nameToDescription.containsKey(qualifiedName)) {
IEObjectDescription prevDescription = nameToDescription.get(qualifiedName);
if (prevDescription != null) {
createDuplicateNameError(prevDescription, clusterType, acceptor);
nameToDescription.put(qualifiedName, null);
}
createDuplicateNameError(description, clusterType, acceptor);
} else {
nameToDescription.put(qualifiedName, description);
}
}
}
protected void createDuplicateNameError(IEObjectDescription description, EClass clusterType, ValidationMessageAcceptor acceptor) {
EObject object = description.getEObjectOrProxy();
EStructuralFeature feature = getNameFeature(object);
acceptor.acceptError(
getDuplicateNameErrorMessage(description, clusterType, feature),
object,
feature,
ValidationMessageAcceptor.INSIGNIFICANT_INDEX,
getErrorCode());
}
/**
* Returns null
. Clients may override if they desire to attach an error code to the created errors.
*/
protected String getErrorCode() {
// TODO use built-in codes to allow generic quickfixes
return null;
}
/**
* Build the error message for duplicated names. The default implementation will provider error messages
* of this form:
*
* - Duplicate Entity 'Sample'
* - Duplicate Property 'Sample' in Entity 'EntityName'
*
* If the container information will be helpful to locate the error or to understand the error
* it will be used, otherwise only the simple format will be build. Clients may override different
* methods that influence the error message.
* @see #getNameFeature(EObject)
* @see #getTypeLabel(EClass)
* @see #getContainerForErrorMessage(EObject)
* @see #isContainerInformationHelpful(IEObjectDescription, String)
* @see #isContainerInformationHelpful(IEObjectDescription, EObject, String, EStructuralFeature)
*/
public String getDuplicateNameErrorMessage(IEObjectDescription description, EClass clusterType, EStructuralFeature feature) {
EObject object = description.getEObjectOrProxy();
String shortName = String.valueOf(feature != null ? object.eGet(feature) : "");
StringBuilder result = new StringBuilder(64);
result.append("Duplicate ");
result.append(getTypeLabel(clusterType));
result.append(" '");
result.append(shortName);
result.append("'");
if (isContainerInformationHelpful(description, shortName)) {
EObject container = getContainerForErrorMessage(object);
if (container != null) {
String containerTypeLabel = getTypeLabel(container.eClass());
EStructuralFeature containerNameFeature = getNameFeature(container);
if (isContainerInformationHelpful(description, container, containerTypeLabel, containerNameFeature)) {
result.append(" in ");
result.append(containerTypeLabel);
if (containerNameFeature != null) {
String containerName = String.valueOf(container.eGet(containerNameFeature));
if (containerName != null) {
result.append(" '");
result.append(containerName);
result.append("'");
}
}
}
}
}
return result.toString();
}
protected boolean isContainerInformationHelpful(IEObjectDescription description, EObject container,
String containerTypeLabel, EStructuralFeature containerNameFeature) {
return containerTypeLabel != null && containerNameFeature != null;
}
protected boolean isContainerInformationHelpful(IEObjectDescription description, String shortName) {
return true;
}
protected EObject getContainerForErrorMessage(EObject object) {
return object.eContainer();
}
protected String getTypeLabel(EClass eClass) {
String name = eClass.getName();
return name;
}
protected EStructuralFeature getNameFeature(EObject object) {
return SimpleAttributeResolver.NAME_RESOLVER.getAttribute(object);
}
/**
* Return the type that describes the set of instances that should have unique names.
* The default information will return the topmost type or the first super type that is contained
* in the set of cluster types ({@link NamesAreUniqueValidationHelper#getClusterTypes()}).
* Only the first super type will be taken into account when walking the hierarchy.
*/
protected EClass getAssociatedClusterType(EClass eClass) {
if (clusterTypes.contains(eClass))
return eClass;
EList superTypes = eClass.getESuperTypes();
if (superTypes.isEmpty())
return eClass;
return getAssociatedClusterType(superTypes.get(0));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy