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

org.eclipse.xtext.serializer.sequencer.GenericSemanticSequencer Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2011 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.serializer.sequencer;

import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraint;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraintContext;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraintElement;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IFeatureInfo;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.RelationalDependencyType;
import org.eclipse.xtext.serializer.sequencer.ISemanticNodeProvider.INodesForEObjectProvider;
import org.eclipse.xtext.serializer.sequencer.ITransientValueService.ValueTransient;
import org.eclipse.xtext.serializer.tokens.ICrossReferenceSerializer;
import org.eclipse.xtext.serializer.tokens.IEnumLiteralSerializer;
import org.eclipse.xtext.serializer.tokens.IKeywordSerializer;
import org.eclipse.xtext.serializer.tokens.IValueSerializer;
import org.eclipse.xtext.util.EmfFormatter;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Tuples;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
 * @author Moritz Eysholdt - Initial contribution and API
 */
public class GenericSemanticSequencer extends AbstractSemanticSequencer {

	protected abstract class Allocation {

		public Allocation() {
			super();
		}

		public abstract void accept(EObject semanticObj, IConstraintElement constraint);

		@Override
		public String toString() {
			return toString("");
		}

		public abstract String toString(String prefix);
	}

	protected class AllocationValue extends Allocation {
		protected int index;

		protected INode node;

		protected Object value;

		protected boolean optional;

		public AllocationValue(Object value, int index, boolean optional, INode node) {
			super();
			this.value = value;
			this.index = index;
			this.optional = optional;
			this.node = node;
		}

		@Override
		public void accept(EObject semanticObj, IConstraintElement constraint) {
			acceptSemantic(semanticObj, constraint, value, index, node);
		}

		public INode getNode() {
			return node;
		}

		public Object getValue() {
			return value;
		}

		@Override
		public String toString(String prefix) {
			return value instanceof EObject ? EmfFormatter.objPath((EObject) value) : value.toString();
		}
	}

	protected class AlternativeAllocation extends Allocation {
		protected Quantity child;

		public AlternativeAllocation(Quantity child) {
			super();
			this.child = child;
		}

		@Override
		public void accept(EObject semanticObj, IConstraintElement constraint) {
			child.accept(semanticObj);
		}

		protected Quantity getChild() {
			return child;
		}

		@Override
		public String toString(String prefix) {
			String newPrefix = "  " + prefix;
			return "Alt-Choice {\n" + newPrefix + child.toString(newPrefix) + "\n" + prefix + "}";
		}
	}

	protected abstract class Feature2Assignment {
		public abstract IFeatureInfo getFeature();

		public abstract List getValuesFor(IConstraintElement assignment);

		public abstract boolean isAmbiguous();

		@Override
		public String toString() {
			List result = Lists.newArrayList();
			for (IConstraintElement assign : getFeature().getAssignments()) {
				result.add(assign + "=>(" + Joiner.on(", ").join(getValuesFor(assign)) + ")");
			}
			return Joiner.on(", ").join(result);
		}
	}

	protected class GroupAllocation extends Allocation {
		protected List children = Lists.newArrayList();

		public GroupAllocation() {
			super();
		}

		public GroupAllocation(List children) {
			super();
			this.children = children;
		}

		@Override
		public void accept(EObject semanticObj, IConstraintElement constraint) {
			for (Quantity q : children)
				q.accept(semanticObj);
		}

		public void addChild(Quantity quantity) {
			children.add(quantity);
		}

		public List getChildren() {
			return children;
		}

		@Override
		public String toString(String prefix) {
			String newPrefix = "  " + prefix;
			StringBuilder r = new StringBuilder();
			r.append("Group {");
			for (Quantity child : children) {
				r.append("\n");
				r.append(newPrefix);
				r.append(child.getConstraintElement());
				r.append(" => ");
				r.append(child.toString(newPrefix));
			}
			r.append("\n");
			r.append(prefix);
			r.append("}");
			return r.toString();
		}

	}

	protected class MVFeature2AssignmentAmbiguous extends Feature2Assignment {
		protected List assignments;

		protected int[] quantities; //TODO: implement

		protected List values;

		public MVFeature2AssignmentAmbiguous(List assignments, List values) {
			super();
			this.assignments = assignments;
			this.values = values;
			this.quantities = new int[assignments.get(0).getFeatureInfo().getAssignments().length];
		}

		@Override
		public IFeatureInfo getFeature() {
			return assignments.get(0).getFeatureInfo();
		}

		@Override
		public List getValuesFor(IConstraintElement assignment) {
			return assignments.contains(assignment) ? values : Collections. emptyList();
		}

		@Override
		public boolean isAmbiguous() {
			int undefs = 0;
			for (IConstraintElement ass : assignments)
				if (quantities[ass.getFeatureAssignmentID()] == UNDEFINED_QUANTITY)
					undefs++;
			return undefs > 1;
		}

	}

	protected class MVFeature2AssignmentUnambiguous extends Feature2Assignment {

		protected IConstraintElement assignment;

		protected List values;

		public MVFeature2AssignmentUnambiguous(IConstraintElement assignment, List values) {
			super();
			this.assignment = assignment;
			this.values = values;
		}

		@Override
		public IFeatureInfo getFeature() {
			return assignment.getFeatureInfo();
		}

		@Override
		public List getValuesFor(IConstraintElement assignment) {
			return assignment == this.assignment ? values : Collections. emptyList();
		}

		@Override
		public boolean isAmbiguous() {
			return false;
		}

	}

	protected static class Quantity {

		protected IConstraintElement constraintElement;

		protected List instances;

		public Quantity(IConstraintElement constraintElement, Allocation allocation) {
			this.instances = Collections.singletonList(allocation);
			this.constraintElement = constraintElement;
		}

		public Quantity(IConstraintElement constraintElement, List allocation) {
			this.instances = allocation;
			this.constraintElement = constraintElement;
		}

		public void accept(EObject semanticObj) {
			if (instances != null)
				for (Allocation a : instances)
					a.accept(semanticObj, constraintElement);
		}

		public List getAllocations() {
			return instances;
		}

		public IConstraintElement getConstraintElement() {
			return constraintElement;
		}

		@Override
		public String toString() {
			return toString("");
		}

		public String toString(String prefix) {
			if (instances == null)
				return "(null)";
			if (instances.isEmpty())
				return "(empty)";
			else if (!constraintElement.isMany() && instances.size() < 2) {
				return instances.get(0).toString(prefix);
			} else {
				StringBuilder buf = new StringBuilder();
				buf.append("[");
				for (Allocation a : instances) {
					buf.append("\n");
					buf.append(prefix + "  ");
					buf.append(a.toString(prefix + "  "));
				}
				buf.append("\n");
				buf.append(prefix);
				buf.append("]");
				return buf.toString();
			}
		}
	}

	protected abstract class SVFeature2Assignment extends Feature2Assignment {

		protected boolean optional;

		protected AllocationValue value;

		public SVFeature2Assignment(boolean optional, AllocationValue value) {
			super();
			this.optional = optional;
			this.value = value;
		}

	}

	protected class SVFeature2AssignmentAmbiguous extends SVFeature2Assignment {

		protected List assignments;

		protected Boolean[] enabled;

		public SVFeature2AssignmentAmbiguous(List assignments, boolean optional,
				AllocationValue value) {
			super(optional, value);
			this.assignments = assignments;
			this.enabled = new Boolean[assignments.get(0).getFeatureInfo().getAssignments().length];
			Arrays.fill(this.enabled, null);
		}

		@Override
		public IFeatureInfo getFeature() {
			return assignments.get(0).getFeatureInfo();
		}

		@Override
		public List getValuesFor(IConstraintElement assignment) {
			if (assignments.contains(assignment)) {
				Boolean en = enabled[assignment.getFeatureAssignmentID()];
				if (en == null && !isAmbiguous()) {
					for (IConstraintElement ass : assignments)
						if (enabled[ass.getFeatureAssignmentID()] == Boolean.TRUE)
							return Collections.emptyList();
					return Collections.singletonList(value);
				}
				if (Boolean.TRUE.equals(en))
					return Collections.singletonList(value);
			}
			return Collections.emptyList();
		}

		@Override
		public boolean isAmbiguous() {
			int undefined = 0;
			for (IConstraintElement ass : assignments)
				if (enabled[ass.getFeatureAssignmentID()] == null)
					undefined++;
			return undefined > 1;
		}

	}

	protected class SVFeature2AssignmentUnambiguous extends SVFeature2Assignment {

		protected IConstraintElement assignment;

		public SVFeature2AssignmentUnambiguous(IConstraintElement assignment, boolean optional, AllocationValue value) {
			super(optional, value);
			this.assignment = assignment;
		}

		@Override
		public IFeatureInfo getFeature() {
			return assignment.getFeatureInfo();
		}

		@Override
		public List getValuesFor(IConstraintElement assignment) {
			if (assignment == this.assignment)
				return Collections.singletonList(value);
			return Collections.emptyList();
		}

		@Override
		public boolean isAmbiguous() {
			return false;
		}

	}

	public final static int MAX = Integer.MAX_VALUE;

	protected final static int NO_ASSIGNMENT = -2;

	public final static int UNDEF = -1;

	protected final static int UNDEFINED_QUANTITY = -1;

	protected List constraintContexts;

	protected Map, IConstraint> constraints;

	@Inject
	protected ICrossReferenceSerializer crossRefSerializer;

	@Inject
	protected IEnumLiteralSerializer enumLiteralSerializer;

	@Inject
	protected IGrammarAccess grammarAccess;

	@Inject
	protected IGrammarConstraintProvider grammarConstraintProvider;

	@Inject
	protected IKeywordSerializer keywordSerializer;

	@Inject
	protected ITransientValueService transientValueService;

	@Inject
	protected IValueSerializer valueSerializer;

	protected void acceptAction(Action action, EObject semanticChild, ICompositeNode node) {
		if (sequenceAcceptor.enterAssignedAction(action, semanticChild, node)) {
			masterSequencer.createSequence(action, semanticChild);
			sequenceAcceptor.leaveAssignedAction(action, semanticChild);
		}
	}

	protected void acceptEObjectRuleCall(RuleCall ruleCall, EObject semanticChild, ICompositeNode node) {
		if (sequenceAcceptor.enterAssignedParserRuleCall(ruleCall, semanticChild, node)) {
			masterSequencer.createSequence(ruleCall.getRule(), semanticChild);
			sequenceAcceptor.leaveAssignedParserRuleCall(ruleCall, semanticChild);
		}
	}

	protected boolean acceptSemantic(EObject semanticObj, IConstraintElement constr, Object value, int index, INode node) {
		switch (constr.getType()) {
			case ASSIGNED_ACTION_CALL:
				acceptAction(constr.getAction(), (EObject) value, (ICompositeNode) node);
				return true;
			case ASSIGNED_PARSER_RULE_CALL:
				acceptEObjectRuleCall(constr.getRuleCall(), (EObject) value, (ICompositeNode) node);
				return true;
			case ASSIGNED_CROSSREF_DATATYPE_RULE_CALL:
				RuleCall datatypeRC = constr.getRuleCall();
				EObject value1 = (EObject) value;
				ICompositeNode node1 = (ICompositeNode) node;
				String token1 = crossRefSerializer.serializeCrossRef(semanticObj,
						GrammarUtil.containingCrossReference(datatypeRC), value1, node1, errorAcceptor);
				sequenceAcceptor.acceptAssignedCrossRefDatatype(datatypeRC, token1, value1, index, node1);
				return true;
			case ASSIGNED_CROSSREF_TERMINAL_RULE_CALL:
				RuleCall terminalRC = constr.getRuleCall();
				EObject value2 = (EObject) value;
				ILeafNode node2 = (ILeafNode) node;
				String token2 = crossRefSerializer.serializeCrossRef(semanticObj,
						GrammarUtil.containingCrossReference(terminalRC), value2, node2, errorAcceptor);
				sequenceAcceptor.acceptAssignedCrossRefTerminal(terminalRC, token2, value2, index, node2);
				return true;
			case ASSIGNED_CROSSREF_ENUM_RULE_CALL:
				RuleCall enumRC = constr.getRuleCall();
				ICompositeNode node3 = (ICompositeNode) node;
				EObject target3 = (EObject) value;
				String token3 = crossRefSerializer.serializeCrossRef(semanticObj,
						GrammarUtil.containingCrossReference(enumRC), target3, node3, errorAcceptor);
				sequenceAcceptor.acceptAssignedCrossRefEnum(enumRC, token3, target3, index, node3);
				return true;
			case ASSIGNED_CROSSREF_KEYWORD:
				Keyword kw0 = constr.getKeyword();
				ILeafNode node0 = (ILeafNode) node;
				EObject target0 = (EObject) value;
				String token0 = crossRefSerializer.serializeCrossRef(semanticObj,
						GrammarUtil.containingCrossReference(kw0), target0, node0, errorAcceptor);
				sequenceAcceptor.acceptAssignedCrossRefKeyword(kw0, token0, target0, index, node0);
				return true;
			case ASSIGNED_DATATYPE_RULE_CALL:
				RuleCall datatypeRC1 = constr.getRuleCall();
				ICompositeNode node4 = (ICompositeNode) node;
				String token4 = valueSerializer.serializeAssignedValue(semanticObj, datatypeRC1, value, node4,
						errorAcceptor);
				sequenceAcceptor.acceptAssignedDatatype(datatypeRC1, token4, value, index, node4);
				return true;
			case ASSIGNED_ENUM_RULE_CALL:
				RuleCall enumRC1 = constr.getRuleCall();
				ICompositeNode node5 = (ICompositeNode) node;
				String token5 = enumLiteralSerializer.serializeAssignedEnumLiteral(semanticObj, enumRC1, value, node5,
						errorAcceptor);
				sequenceAcceptor.acceptAssignedEnum(enumRC1, token5, value, index, node5);
				return true;
			case ASSIGNED_TERMINAL_RULE_CALL:
				RuleCall terminalRC1 = constr.getRuleCall();
				ILeafNode node6 = (ILeafNode) node;
				String token6 = valueSerializer.serializeAssignedValue(semanticObj, terminalRC1, value, node6,
						errorAcceptor);
				sequenceAcceptor.acceptAssignedTerminal(terminalRC1, token6, value, index, node6);
				return true;
			case ASSIGNED_KEYWORD:
				Keyword keyword = constr.getKeyword();
				ILeafNode node7 = (ILeafNode) node;
				String token7 = keywordSerializer.serializeAssignedKeyword(semanticObj, keyword, value, node7,
						errorAcceptor);
				sequenceAcceptor.acceptAssignedKeyword(keyword, token7, value, index, node7);
				return true;
			case ALTERNATIVE:
			case GROUP:
				return false;
		}
		return false;
	}

	//	protected void applydeterministicQuantities(IConstraint constraint, Feature2Assignment[] values) {
	//		boolean changed;
	//		do {
	//			changed = false;
	//			for (IConstraintElement assignment : constraint.getAssignments())
	//				if (values[assignment.getAssignmentID()] != null && values[assignment.getAssignmentID()].isAmbiguous()) {
	//					int min = getMin(values, assignment);
	//					int max = getMax(values, assignment);
	//					if (min == max && min != UNDEF) {
	//						values[assignment.getAssignmentID()].setQuantity(assignment, min);
	//						changed = true;
	//						//						System.out.println("Setting quantity of " + assignment + " to " + min);
	//					}
	//				}
	//		} while (changed);
	//	}

	protected boolean containsUnavailableFeature(Feature2Assignment[] values, IConstraintElement element,
			IConstraintElement excludeAssignment) {
		if (element.isOptional())
			return false;
		switch (element.getType()) {
			case GROUP:
				for (IConstraintElement a : element.getChildren())
					if (containsUnavailableFeature(values, a, excludeAssignment))
						return true;
				return false;
			case ALTERNATIVE:
				for (IConstraintElement a : element.getChildren())
					if (!containsUnavailableFeature(values, a, excludeAssignment))
						return false;
				return true;
			case ASSIGNED_ACTION_CALL:
			case ASSIGNED_CROSSREF_DATATYPE_RULE_CALL:
			case ASSIGNED_CROSSREF_ENUM_RULE_CALL:
			case ASSIGNED_CROSSREF_TERMINAL_RULE_CALL:
			case ASSIGNED_CROSSREF_KEYWORD:
			case ASSIGNED_DATATYPE_RULE_CALL:
			case ASSIGNED_ENUM_RULE_CALL:
			case ASSIGNED_KEYWORD:
			case ASSIGNED_PARSER_RULE_CALL:
			case ASSIGNED_TERMINAL_RULE_CALL:
				Feature2Assignment f2a = values[element.getAssignmentID()];
				if (f2a == null)
					return true;
				if (f2a.isAmbiguous())
					return false;
				if (f2a.getValuesFor(element).isEmpty())
					return true;
				return false;
		}
		return false;
	}

	public void createSequence(EObject context, EObject semanticObject) {
		initConstraints();
		IConstraint constraint = getConstraint(context, semanticObject.eClass());
		//		System.out.println("Constraint: " + constraint);
		if (constraint == null) {
			if (errorAcceptor != null)
				errorAcceptor.accept(diagnosticProvider.createInvalidContextOrTypeDiagnostic(semanticObject, context));
			return;
		}
		INodesForEObjectProvider nodes = nodeProvider.getNodesForSemanticObject(semanticObject, null);
		Feature2Assignment[] values = createValues(semanticObject, constraint, nodes);
		//		System.out.println("Values: " + f2aToStr(constraint.getBody(), values));
		//				System.out.println("Values (Disambiguated): " + f2aToStr(constraint.getBody(), values));
		if (constraint.getBody() != null) {
			Quantity quant = new Quantity(constraint.getBody(), createUnambiguousAllocation(constraint.getBody(),
					values));
			//		System.out.println("Quantity: " + quant + " EndQuantity");
			//		List result = Lists.newArrayList();
			quant.accept(semanticObject);
		}
		sequenceAcceptor.finish();
	}

	protected List createUnambiguousAllocation(IConstraintElement constraint,
			Feature2Assignment[] values) {
		switch (constraint.getType()) {
			case ALTERNATIVE:
				List result = Lists.newArrayList();
				for (IConstraintElement child : constraint.getChildren()) {
					List allocs = createUnambiguousAllocation(child, values);
					if (allocs == null)
						return null;
					if (child.isMany()) {
						Quantity q = new Quantity(child, allocs);
						result.add(new AlternativeAllocation(q));
					} else {
						for (Allocation a : allocs) {
							AlternativeAllocation alloc = new AlternativeAllocation(new Quantity(child, a));
							result.add(alloc);
						}
					}
				}
				return result;
			case GROUP:
				int min = 0;
				int max = Integer.MAX_VALUE;
				List>> children = Lists
						.newArrayListWithExpectedSize(constraint.getChildren().size());
				for (IConstraintElement child : constraint.getChildren()) {
					List allocs = createUnambiguousAllocation(child, values);
					if (allocs == null)
						return null;
					if (allocs.size() > 0)
						min = Math.max(min, child.isMany() ? 1 : allocs.size());
					if (!child.isOptional())
						max = Math.max(max, allocs.size());
					children.add(Tuples.> create(child, allocs));
				}
				if (max < min)
					throw new RuntimeException("err"); // TODO: handle this error
				List result2 = Lists.newArrayListWithExpectedSize(min);
				for (int i = 0; i < min; i++) {
					List ch = Lists.newArrayList();
					for (Pair> p : children) {
						if (i < p.getSecond().size()) {
							if (i == min - 1)
								ch.add(new Quantity(p.getFirst(), p.getSecond().subList(i, p.getSecond().size())));
							else
								ch.add(new Quantity(p.getFirst(), p.getSecond().get(i)));
						}
					}
					result2.add(new GroupAllocation(ch));
				}
				//				System.out.println(constraint + " => " + result2);
				return result2;
			case ASSIGNED_ACTION_CALL:
			case ASSIGNED_CROSSREF_DATATYPE_RULE_CALL:
			case ASSIGNED_CROSSREF_ENUM_RULE_CALL:
			case ASSIGNED_CROSSREF_TERMINAL_RULE_CALL:
			case ASSIGNED_CROSSREF_KEYWORD:
			case ASSIGNED_DATATYPE_RULE_CALL:
			case ASSIGNED_ENUM_RULE_CALL:
			case ASSIGNED_KEYWORD:
			case ASSIGNED_PARSER_RULE_CALL:
			case ASSIGNED_TERMINAL_RULE_CALL:
				Feature2Assignment f2a = values[constraint.getAssignmentID()];
				if (f2a == null)
					return Collections.emptyList();
				else if (!f2a.isAmbiguous()) {
					return f2a.getValuesFor(constraint);
					//					List r = f2a.getValuesFor(constraint);
					//					return r.isEmpty() ? null : r;
				} else
					return null;
		}
		return null;
	}

	protected Feature2Assignment[] createValues(EObject semanticObject, IConstraint constraint,
			INodesForEObjectProvider nodes) {
		Feature2Assignment[] result = new Feature2Assignment[constraint.getAssignments().length];
		for (IFeatureInfo feature : constraint.getSingleAssignementFeatures()) {
			if (feature.getFeature().isMany()) {
				List allocs = getNonTransientValuesForMVFeature(semanticObject, feature, nodes);
				if (!allocs.isEmpty()) {
					IConstraintElement ass = feature.getAssignments()[0];
					result[ass.getAssignmentID()] = new MVFeature2AssignmentUnambiguous(ass, allocs);
				}
			} else {
				ValueTransient trans = transientValueService.isValueTransient(semanticObject, feature.getFeature());
				if (trans != ValueTransient.YES) {
					Object value = semanticObject.eGet(feature.getFeature());
					INode node = nodes.getNodeForSingelValue(feature.getFeature(), value);
					if (trans != ValueTransient.PREFERABLY || node != null) {
						IConstraintElement ass = feature.getAssignments()[0];
						AllocationValue alloc = new AllocationValue(value, -1, trans == ValueTransient.PREFERABLY, node);
						result[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass,
								trans == ValueTransient.PREFERABLY, alloc);
					}
				}
			}
		}
		for (IFeatureInfo feature : constraint.getMultiAssignementFeatures()) {
			if (feature.getFeature().isMany()) {
				List allocs = getNonTransientValuesForMVFeature(semanticObject, feature, nodes);
				if (!allocs.isEmpty())
					createValues(semanticObject, feature, allocs, result);
			} else {
				ValueTransient trans = transientValueService.isValueTransient(semanticObject, feature.getFeature());
				if (trans != ValueTransient.YES) {
					Object value = semanticObject.eGet(feature.getFeature());
					INode node = nodes.getNodeForSingelValue(feature.getFeature(), value);
					AllocationValue alloc = new AllocationValue(value, -1, trans == ValueTransient.PREFERABLY, node);
					createValues(semanticObject, feature, trans == ValueTransient.PREFERABLY, alloc, result);
				}
			}
		}
		return result;
	}

	protected void createValues(EObject semanticObj, IFeatureInfo feature, boolean optional, AllocationValue value,
			Feature2Assignment[] target) {
		List remainingAssignments = Lists.newArrayList();
		for (IConstraintElement ass : feature.getAssignments())
			if (!isExcludedByDependees(ass, target))
				remainingAssignments.add(ass);
		if (!remainingAssignments.isEmpty() && feature.isContentValidationNeeded())
			remainingAssignments = findValidAssignments(semanticObj,
					remainingAssignments.toArray(new IConstraintElement[remainingAssignments.size()]), value);
		if (remainingAssignments.isEmpty())
			return; // TODO: handle this error, no valid assignment has been found for the value.
		else if (remainingAssignments.size() == 1) {
			IConstraintElement ass = remainingAssignments.get(0);
			target[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass, optional, value);
			return;
		}
		SVFeature2AssignmentAmbiguous f2a = new SVFeature2AssignmentAmbiguous(remainingAssignments, optional, value);
		for (IConstraintElement ass : remainingAssignments)
			target[ass.getAssignmentID()] = f2a;
	}

	protected void createValues(EObject semanticObj, IFeatureInfo feature, List values,
			Feature2Assignment[] target) {
		List remainingAssignments = Lists.newArrayList();
		for (IConstraintElement ass : feature.getAssignments())
			if (!isExcludedByDependees(ass, target))
				remainingAssignments.add(ass);
		if (feature.isContentValidationNeeded())
			remainingAssignments = findValidAssignments(semanticObj, remainingAssignments, values);
		if (remainingAssignments.size() == 0)
			throw new RuntimeException("no valid assignments"); // TODO: handle this better
		if (remainingAssignments.size() == 1) {
			IConstraintElement ass = remainingAssignments.get(0);
			target[ass.getAssignmentID()] = new MVFeature2AssignmentUnambiguous(ass, values);
			return;
		}
		List remainingValues = Lists.newArrayList(values);
		distributeValuesByQuantity(remainingAssignments, remainingValues, target);
		if (remainingAssignments.size() == 1) {
			IConstraintElement ass = remainingAssignments.get(0);
			target[ass.getAssignmentID()] = new MVFeature2AssignmentUnambiguous(ass, remainingValues);
			return;
		}
		MVFeature2AssignmentAmbiguous f2a = new MVFeature2AssignmentAmbiguous(remainingAssignments, remainingValues);
		for (IConstraintElement ass : remainingAssignments)
			target[ass.getAssignmentID()] = f2a;
	}

	protected void distributeValuesByQuantity(List assignments, List values,
			Feature2Assignment[] target) {
		while (true) {
			IConstraintElement ass = assignments.get(0);
			if (ass.isCardinalityOneAmongAssignments(assignments)) {
				target[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass, false, values.get(0));
				values.remove(0);
				assignments.remove(0);
			} else
				break;
		}
		for (int i = assignments.size() - 1; i >= 0 && !values.isEmpty(); i--) {
			IConstraintElement ass = assignments.get(i);
			if (ass != null && ass.isCardinalityOneAmongAssignments(assignments)) {
				target[ass.getAssignmentID()] = new SVFeature2AssignmentUnambiguous(ass, false, values.get(values
						.size() - 1));
				values.remove(values.size() - 1);
				assignments.remove(i);
			} else
				break;
		}
		//		for (int i = assignments.size() - 1; i >= 0; i--)
		//			if (assignments.get(i) == null)
		//				assignments.remove(i);
	}

	protected String f2aToStr(IConstraintElement ele, Feature2Assignment[] values) {
		if (ele == null)
			return "(null)";
		StringBuilder result = new StringBuilder();
		f2aToStr(ele, values, "", result);
		return result.toString();
	}

	protected void f2aToStr(IConstraintElement ele, Feature2Assignment[] values, String prefix, StringBuilder result) {
		result.append(prefix);
		if (ele.getChildren() != null) {
			result.append(ele.getType().name());
			result.append(ele.getCardinality());
			result.append(" {\n");
			for (IConstraintElement child : ele.getChildren())
				f2aToStr(child, values, prefix + "  ", result);
			result.append(prefix);
			result.append("}\n");
		} else if (ele.getAssignmentID() >= 0) {
			result.append(ele.toString());
			result.append(" => ");
			Feature2Assignment value = values[ele.getAssignmentID()];
			if (value != null) {
				if (value.isAmbiguous())
					result.append("ambiguous!");
				result.append(Joiner.on(", ").join(values[ele.getAssignmentID()].getValuesFor(ele)));
			}
			result.append("\n");
		}
	}

	@Inject
	protected IAssignmentFinder assignmentFinder;

	protected List findValidAssignments(EObject semanticObj, IConstraintElement[] assignments,
			AllocationValue value) {
		List assignedElements = Lists.newArrayList();
		for (IConstraintElement ass : assignments)
			assignedElements.add(ass.getGrammarElement());
		Set assignedElements2 = Sets.newHashSet(assignmentFinder.findAssignmentsByValue(semanticObj,
				assignedElements, value.getValue(), value.getNode()));
		List result = Lists.newArrayList();
		for (IConstraintElement ass : assignments)
			if (assignedElements2.contains(ass.getGrammarElement()))
				result.add(ass);
		return result;
	}

	protected List findValidAssignments(EObject semanticObj, List assignments,
			List values) {
		BitSet bs = new BitSet();
		IConstraintElement[] assignmentsAr = assignments.toArray(new IConstraintElement[assignments.size()]);
		for (AllocationValue value : values)
			for (IConstraintElement validAssignments : findValidAssignments(semanticObj, assignmentsAr, value))
				bs.set(validAssignments.getFeatureAssignmentID());
		List result = Lists.newArrayList();
		for (IConstraintElement ass : assignments)
			if (bs.get(ass.getFeatureAssignmentID()))
				result.add(ass);
		return result;
	}

	protected IConstraint getConstraint(EObject context, EClass type) {
		return constraints.get(Tuples.create(context, type));
	}

	protected List getNonTransientValuesForMVFeature(EObject semanticObject, IFeatureInfo feature,
			INodesForEObjectProvider nodes) {
		switch (transientValueService.isListTransient(semanticObject, feature.getFeature())) {
			case NO:
				List allocs1 = Lists.newArrayList();
				List values1 = (List) semanticObject.eGet(feature.getFeature());
				for (int i = 0; i < values1.size(); i++) {
					Object value = values1.get(i);
					INode node = nodes.getNodeForMultiValue(feature.getFeature(), i, i, value);
					allocs1.add(new AllocationValue(value, i, false, node));
				}
				return allocs1;
			case SOME:
				List allocs2 = Lists.newArrayList();
				List values2 = (List) semanticObject.eGet(feature.getFeature());
				for (int i = 0, j = 0; i < values2.size(); i++)
					if (!transientValueService.isValueInListTransient(semanticObject, i, feature.getFeature())) {
						Object value = values2.get(i);
						INode node = nodes.getNodeForMultiValue(feature.getFeature(), i, j++, value);
						allocs2.add(new AllocationValue(value, i, false, node));
					}
				return allocs2;
			case YES:
		}
		return Collections.emptyList();
	}

	protected void initConstraints() {
		if (constraintContexts == null) {
			constraints = Maps.newHashMap();
			constraintContexts = grammarConstraintProvider.getConstraints(grammarAccess.getGrammar());
			//			System.out.println(Joiner.on("\n").join(constraintContexts));
			for (IConstraintContext ctx : constraintContexts)
				for (IConstraint constraint : ctx.getConstraints())
					constraints.put(Tuples.create(ctx.getContext(), constraint.getType()), constraint);
		}
	}

	protected boolean isAmbiguous(Feature2Assignment[] allocations) {
		for (Feature2Assignment feat : allocations)
			if (feat.isAmbiguous())
				return true;
		return false;
	}

	protected boolean isExcludedByDependees(IConstraintElement assignments, Feature2Assignment[] target) {
		List> dependees = assignments.getDependingAssignment();
		if (dependees == null || dependees.isEmpty())
			return false;
		for (Pair e : dependees)
			switch (e.getSecond()) {
				case EXCLUDE_IF_SET:
					if (target[e.getFirst().getAssignmentID()] != null)
						return true;
					break;
				case SAME:
				case SAME_OR_LESS:
				case EXCLUDE_IF_UNSET:
					if (target[e.getFirst().getAssignmentID()] == null
							&& e.getFirst().getFeatureInfo().getAssignments().length == 1)
						return true;
					break;
				case MANDATORY_IF_SET:
				case SAME_OR_MORE:
			}
		return false;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy