All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.validation.impl.ConcreteSyntaxDiagnosticProvider Maven / Gradle / Ivy

There is a newer version: 2.4.3
Show newest version
/*******************************************************************************
 * Copyright (c) 2010 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.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.validation.IAssignmentQuantityAllocator;
import org.eclipse.xtext.validation.IAssignmentQuantityAllocator.IQuantities;
import org.eclipse.xtext.validation.IConcreteSyntaxConstraintProvider.ConstraintType;
import org.eclipse.xtext.validation.IConcreteSyntaxConstraintProvider.ISyntaxConstraint;
import org.eclipse.xtext.validation.IConcreteSyntaxDiagnosticProvider;

import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
 * @author Moritz Eysholdt - Initial contribution and API
 */
public class ConcreteSyntaxDiagnosticProvider implements IConcreteSyntaxDiagnosticProvider {

	public abstract class AbstractConcreteSyntaxDiagnostic implements IConcreteSyntaxDiagnostic {
		protected Set involved;
		protected ISyntaxConstraint rule;
		protected EObject source;

		public AbstractConcreteSyntaxDiagnostic(ISyntaxConstraint rule, EObject source, Set involved) {
			super();
			this.rule = rule;
			this.source = source;
			this.involved = involved;
		}

		protected void appendConstraint(StringBuffer msg) {
			if (involved.size() > 1) {
				msg.append(" Constraint: ");
				msg.append(getConstraint());
				msg.append(" Quantities: ");
				msg.append(getValue());
			}
		}

		public List getChildren() {
			return Collections.emptyList();
		}

		protected List getChildren(ISyntaxConstraint obj, final Set included) {
			ArrayList r = new ArrayList();
			for (ISyntaxConstraint o : obj.getContents())
				if (included.contains(o))
					r.add(getConstraint(o, included));
			return r;
		}

		protected ISyntaxConstraint getCommonContainer() {
			Iterator i = involved.iterator();
			if (!i.hasNext())
				return null;
			ISyntaxConstraint root = i.next();
			while (i.hasNext())
				root = root.findCommonContainer(i.next());
			return root;
		}

		public String getConstraint() {
			if (involved.size() > 1) {
				ISyntaxConstraint commonContainer = getCommonContainer();
				return getConstraint(commonContainer, getInvolvedIncludingContainers(commonContainer));
			} else
				return "";
		}

		protected String getConstraint(ISyntaxConstraint element, Set all) {
			switch (element.getType()) {
				case ASSIGNMENT:
					return ((Assignment) element.getGrammarElement()).getFeature() + element.getCardinality();
				case GROUP:
					List children1 = getChildren(element, all);
					if (children1.size() == 1)
						return children1.get(0) + element.getCardinality();
					return "(" + Joiner.on(" ").join(children1) + ")" + element.getCardinality();
				case ALTERNATIVE:
					List children2 = getChildren(element, all);
					return "(" + Joiner.on("|").join(children2) + ")" + element.getCardinality();
				case ACTION:
					return "{" + ((Action) element.getGrammarElement()).getType().getClassifier().getName() + "}";
			}
			return "";
		}

		public List getData() {
			return Arrays.asList(source);
		}

		public Throwable getException() {
			return null;
		}

		protected Set getInvolvedIncludingContainers(ISyntaxConstraint commonRoot) {
			HashSet all = new HashSet(involved);
			all.add(commonRoot);
			for (ISyntaxConstraint a : involved) {
				ISyntaxConstraint cnt = a.getContainer();
				while (all.add(cnt))
					cnt = cnt.getContainer();
			}
			return all;
		}

		public ParserRule getRule() {
			return GrammarUtil.containingParserRule(rule.getGrammarElement());
		}

		public int getSeverity() {
			return Diagnostic.ERROR;
		}

		public String getSource() {
			return EmfFormatter.objPath(source);
		}

		public String getValue() {
			Set feats = new HashSet();
			for (ISyntaxConstraint a : involved)
				if (a.getType() == ConstraintType.ASSIGNMENT)
					feats.add(a.getAssignmentFeature(source.eClass()));
			return Joiner.on(", ").join(Iterables.transform(feats, new Function() {
				public String apply(EStructuralFeature from) {
					return from.getName() + ":" + quantityAllocator.getFeatureQuantity(source, from);
				}
			}));
		}

		@Override
		public String toString() {
			return getMessage() + " Source: " + getSource();
		}

	}

	public class ConcreteSyntaxAssignmentMissingDiagnostic extends AbstractConcreteSyntaxDiagnostic {
		protected EStructuralFeature feature;
		protected Set requiredTypes;

		public ConcreteSyntaxAssignmentMissingDiagnostic(ISyntaxConstraint rule, EObject source,
				EStructuralFeature feature, Set involved) {
			super(rule, source, involved);
			this.feature = feature;
			this.requiredTypes = Sets.newHashSet();
			collectAssignments(this.rule, this.feature, requiredTypes);
		}

		protected void collectAssignments(ISyntaxConstraint ele, EStructuralFeature matcher, Set types) {
			if (ele.getType() == ConstraintType.ASSIGNMENT && ele.getAssignmentName().equals(matcher.getName()))
				types.addAll(ele.getSemanticTypes());
			for (ISyntaxConstraint e : ele.getContents())
				collectAssignments(e, matcher, types);
		}

		public int getCode() {
			if (requiredTypes.isEmpty())
				return ERROR_ASSIGNMENT_MISSING;
			return ERROR_ASSIGNMENT_PROHIBITED;
		}

		public String getMessage() {
			StringBuffer msg = new StringBuffer();
			msg.append("The feature ");
			msg.append(feature.getEContainingClass().getName());
			if (feature.getEContainingClass() != source.eClass()) {
				msg.append("(");
				msg.append(source.eClass().getName());
				msg.append(")");
			}
			msg.append(".");
			msg.append(feature.getName());
			switch (getCode()) {
				case ERROR_ASSIGNMENT_MISSING:
					msg.append(" contains non-transient values but has no corresponding assignment in rule ");
					msg.append(getRule().getName());
					break;
				case ERROR_ASSIGNMENT_PROHIBITED:
					msg.append(" is not allowed to contain non-transient values. ");
					msg.append("The object needs to be of type ");
					msg.append(Joiner.on(" or ").join(Iterables.transform(requiredTypes, new Function() {
						public String apply(EClass from) {
							return from.getName();
						}
					})));
			}
			msg.append(".");
			appendConstraint(msg);
			return msg.toString();
		}
	}

	public class ConcreteSyntaxFeatureDiagnostic extends AbstractConcreteSyntaxDiagnostic {

		protected EStructuralFeature feature;
		protected int min, max, actual;

		public ConcreteSyntaxFeatureDiagnostic(ISyntaxConstraint rule, IQuantities source, EStructuralFeature feature,
				int actual, int min, int max, Set involved) {
			super(rule, source.getEObject(), involved);
			this.feature = feature;
			this.actual = actual;
			this.min = min;
			this.max = max;
		}

		public int getActual() {
			return actual;
		}

		public int getCode() {
			if (feature == null)
				return ERROR_FEATURE_MISSING;
			if (feature.isMany()) {
				if (actual < min && actual > max)
					return ERROR_LIST_UNDECIDEABLE;
				if (actual < min)
					return ERROR_LIST_TOO_FEW;
				if (actual > max)
					return ERROR_LIST_TOO_MANY;
			} else {
				if (actual < min)
					return ERROR_VALUE_REQUIRED;
				if (actual > max)
					return ERROR_VALUE_PROHIBITED;
			}
			return 0;
		}

		@Override
		public List getData() {
			return Arrays.asList(source, feature);
		}

		public EStructuralFeature getFeature() {
			return feature;
		}

		public int getMax() {
			return max;
		}

		public String getMessage() {
			StringBuffer msg = new StringBuffer();
			msg.append("Feature ");
			msg.append(source.eClass().getName());
			msg.append(".");
			msg.append(feature.getName());
			switch (getCode()) {
				case ERROR_LIST_UNDECIDEABLE:
					msg.append(" holds " + actual + " non-transient value(s), but at least " + min + " and at most "
							+ max + " are expected. ");
					msg.append("Since this is impossible, please resolve other constraint-violations first. ");
					msg.append("If that doesn't help, ask Chuck Norris for help or file a bug report.");
					break;
				case ERROR_LIST_TOO_FEW:
					msg.append(" holds " + actual + " non-transient value(s), ");
					if (min == max)
						msg.append("but exactly " + min + " are required.");
					else
						msg.append("but at least " + min + " are required.");
					break;
				case ERROR_LIST_TOO_MANY:
					msg.append(" holds " + actual + " non-transient value(s), ");
					if (min == max)
						msg.append("but exactly " + max + " are required.");
					else
						msg.append("but at most " + max + " are allowed.");
					break;
				case ERROR_VALUE_REQUIRED:
					msg.append(" must be set.");
					break;
				case ERROR_VALUE_PROHIBITED:
					msg.append(" must not be set.");
					break;
			}
			appendConstraint(msg);
			//			msg.append(" min:" + min);
			//			msg.append(" max:" + max);
			return msg.toString();
		}

		public int getMin() {
			return min;
		}

	}

	public class ConcreteSyntaxFeatureMissingDiagnostic extends AbstractConcreteSyntaxDiagnostic {
		protected ISyntaxConstraint element;

		public ConcreteSyntaxFeatureMissingDiagnostic(ISyntaxConstraint rule, EObject source,
				ISyntaxConstraint element, Set involved) {
			super(rule, source, involved);
			this.element = element;
		}

		public int getCode() {
			return ERROR_FEATURE_MISSING;
		}

		public String getMessage() {
			StringBuffer msg = new StringBuffer();
			msg.append("A feature named '");
			msg.append(element.getAssignmentName());
			msg.append("' in ");
			msg.append(source.eClass().getName());
			msg.append(" is needed for serialization with rule ");
			msg.append(getRule().getName());
			msg.append(".");
			appendConstraint(msg);
			return msg.toString();
		}
	}

	public class ConcreteSyntaxObjectDiagnostic extends AbstractConcreteSyntaxDiagnostic {
		public ConcreteSyntaxObjectDiagnostic(ISyntaxConstraint rule, EObject source, Set involved) {
			super(rule, source, involved);
		}

		public int getCode() {
			return ERROR_WRONG_TYPE;
		}

		public String getMessage() {
			StringBuffer msg = new StringBuffer();
			msg.append("An object of type ");
			msg.append(Joiner.on(" or ").join(Iterables.transform(getSemanticTypes(), new Function() {
				public String apply(EClass from) {
					return from.getName();
				}
			})));
			msg.append(" is needed instead of ");
			msg.append(source.eClass().getName());
			msg.append(" for serialization with rule ");
			msg.append(getRule().getName());
			msg.append(".");
			appendConstraint(msg);
			return msg.toString();
		}

		protected Set getSemanticTypes() {
			Set types = Sets.newHashSet();
			for (ISyntaxConstraint c : involved)
				if (c.getSemanticTypesToCheck() != null)
					types.addAll(c.getSemanticTypesToCheck());
			return types;
		}
	}

	@Inject
	protected IAssignmentQuantityAllocator quantityAllocator;

	public IConcreteSyntaxDiagnostic createAssignmentMissingDiagnostic(ISyntaxConstraint rule, EObject source,
			EStructuralFeature feature, Set involved) {
		return new ConcreteSyntaxAssignmentMissingDiagnostic(rule, source, feature, involved);
	}

	public IConcreteSyntaxDiagnostic createFeatureMissingDiagnostic(ISyntaxConstraint rule, EObject source,
			ISyntaxConstraint element, Set involved) {
		return new ConcreteSyntaxFeatureMissingDiagnostic(rule, source, element, involved);
	}

	public IConcreteSyntaxDiagnostic createFeatureQuantityDiagnostic(ISyntaxConstraint rule, IQuantities source,
			EStructuralFeature feature, int actual, int min, int max, Set involved) {
		return new ConcreteSyntaxFeatureDiagnostic(rule, source, feature, actual, min, max, involved);
	}

	public IConcreteSyntaxDiagnostic createUnexpectedTypeDiagnostic(ISyntaxConstraint rule, EObject source,
			Set involved) {
		return new ConcreteSyntaxObjectDiagnostic(rule, source, involved);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy