org.eclipse.xtext.validation.impl.AssignmentQuantityAllocator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.validation.impl.AssignmentQuantityAllocator.java

Source

/*******************************************************************************
 * 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.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.parsetree.reconstr.ITokenSerializer.IValueSerializer;
import org.eclipse.xtext.parsetree.reconstr.ITransientValueService;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.validation.IAssignmentQuantityAllocator;
import org.eclipse.xtext.validation.IAssignmentQuantityIntervalProvider;
import org.eclipse.xtext.validation.IConcreteSyntaxConstraintProvider.ConstraintType;
import org.eclipse.xtext.validation.IConcreteSyntaxConstraintProvider.ISyntaxConstraint;
import org.eclipse.xtext.validation.IConcreteSyntaxDiagnosticProvider;
import org.eclipse.xtext.validation.IConcreteSyntaxDiagnosticProvider.IConcreteSyntaxDiagnostic;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;

/**
 * @author Moritz Eysholdt - Initial contribution and API
 */
public class AssignmentQuantityAllocator implements IAssignmentQuantityAllocator {

    protected class Quantities implements IQuantities {
        protected Map<ISyntaxConstraint, Integer> assignmentQuants = Maps.newHashMap();
        protected EObject delegate;
        protected Map<EStructuralFeature, Integer> featureQuants = Maps.newHashMap();

        public Quantities(EObject delegate) {
            super();
            this.delegate = delegate;
        }

        @Override
        public Map<ISyntaxConstraint, Integer> getAssignmentQuantities() {
            return assignmentQuants;
        }

        @Override
        public Integer getAssignmentQuantity(ISyntaxConstraint assignement) {
            Integer i = assignmentQuants.get(assignement);
            return i == null || i < 0 ? -1 : i;
        }

        @Override
        public EObject getEObject() {
            return delegate;
        }

        @Override
        public Map<EStructuralFeature, Integer> getFeatureQuantities() {
            return featureQuants;
        }

        @Override
        public Integer getFeatureQuantity(EStructuralFeature feat) {
            Integer i = featureQuants.get(feat);
            return i == null || i < 0 ? -1 : i;
        }

        @Override
        public Map<EStructuralFeature, Collection<ISyntaxConstraint>> groupByFeature() {
            Multimap<EStructuralFeature, ISyntaxConstraint> map = HashMultimap.create();
            for (ISyntaxConstraint e : assignmentQuants.keySet())
                map.put(e.getAssignmentFeature(delegate.eClass()), e);
            return map.asMap();
        }

        public void setAssignmentQuantity(ISyntaxConstraint assignement, int quantity) {
            assignmentQuants.put(assignement, quantity);
        }

        public void setFeatureQuantity(EStructuralFeature feature, int quantity) {
            featureQuants.put(feature, quantity);
        }

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

        public String toString(Map<ISyntaxConstraint, Pair<Integer, Integer>> minmax) {
            Map<ISyntaxConstraint, String> postfix = Maps.newHashMap();
            for (Map.Entry<ISyntaxConstraint, Integer> e : assignmentQuants.entrySet()) {
                String s = ":" + e.getValue();
                if (minmax != null && minmax.containsKey(e.getKey())) {
                    Pair<Integer, Integer> p = minmax.get(e.getKey());
                    s += "<" + p.getFirst() + "," + (p.getSecond() == Integer.MAX_VALUE ? "*" : p.getSecond())
                            + ">";
                }
                postfix.put(e.getKey(), s);
            }
            Iterator<ISyntaxConstraint> i = assignmentQuants.keySet().iterator();
            if (!i.hasNext())
                return "";
            ISyntaxConstraint root = i.next();
            while (i.hasNext())
                root = root.findCommonContainer(i.next());
            return root.toString(postfix);
        }
    }

    @Inject
    protected IConcreteSyntaxDiagnosticProvider diagnosticProvider;

    @Inject
    protected IAssignmentQuantityIntervalProvider intervalProvider;

    @Inject
    protected ITransientValueService transSrvc;

    @Inject
    protected IValueSerializer valueSerializer;

    protected boolean allowTransient(EObject obj, EStructuralFeature feature,
            Collection<ISyntaxConstraint> constraint) {
        if (feature.getEType() instanceof EEnum)
            return true;
        Object value = obj.eGet(feature);
        List<RuleCall> ruleCalls = GrammarUtil.containedRuleCalls(constraint.iterator().next().getGrammarElement());
        if (ruleCalls.isEmpty())
            return false;
        return valueSerializer.isValid(obj, ruleCalls.get(0), value, null);

    }

    protected void collectAssignments(ISyntaxConstraint rule, EObject obj, ISyntaxConstraint ele,
            Multimap<EStructuralFeature, ISyntaxConstraint> assignments, List<IConcreteSyntaxDiagnostic> acceptor) {
        if (ele.getSemanticTypesToCheck() != null && !ele.getSemanticTypesToCheck().contains(obj.eClass()))
            return;
        if (ele.getType() == ConstraintType.ASSIGNMENT) {
            EStructuralFeature f = obj.eClass()
                    .getEStructuralFeature(((Assignment) ele.getGrammarElement()).getFeature());
            if (f == null)
                acceptor.add(diagnosticProvider.createFeatureMissingDiagnostic(rule, obj, ele,
                        Collections.<ISyntaxConstraint>emptySet()));
            else
                assignments.put(f, ele);
        }
        for (ISyntaxConstraint e : ele.getContents())
            collectAssignments(rule, obj, e, assignments, acceptor);
    }

    protected Quantities createQuantities(EObject obj) {
        return new Quantities(obj);
    }

    @Override
    public IQuantities getAssignmentQuantities(EObject obj, ISyntaxConstraint rule,
            List<IConcreteSyntaxDiagnostic> acceptor) {
        Multimap<EStructuralFeature, ISyntaxConstraint> assignments = HashMultimap.create();
        collectAssignments(rule, obj, rule, assignments, acceptor);
        //      Map<EStructuralFeature, Integer> quantities = Maps.newHashMap();
        Quantities quants = createQuantities(obj);
        for (EStructuralFeature f : obj.eClass().getEAllStructuralFeatures()) {
            int quantity = getFeatureQuantity(obj, f);
            if (quantity > 0 && !assignments.containsKey(f))
                acceptor.add(diagnosticProvider.createAssignmentMissingDiagnostic(rule, obj, f,
                        Collections.<ISyntaxConstraint>emptySet()));
            else
                quants.setFeatureQuantity(f, quantity);
        }
        Multimap<EStructuralFeature, ISyntaxConstraint> multipleAssignments = HashMultimap.create();
        Multimap<EStructuralFeature, ISyntaxConstraint> allowTransients = HashMultimap.create();
        for (Map.Entry<EStructuralFeature, Integer> f : quants.getFeatureQuantities().entrySet()) {
            Collection<ISyntaxConstraint> ass = assignments.get(f.getKey());
            if (ass.isEmpty())
                continue;
            boolean allowTransient = f.getKey() instanceof EAttribute && !f.getKey().isMany() && f.getValue() == 0
                    && allowTransient(obj, f.getKey(), ass);
            boolean multiNeeded = ass.size() > 1 && f.getValue() != 0;
            if (allowTransient)
                allowTransients.putAll(f.getKey(), ass);
            if (multiNeeded)
                multipleAssignments.putAll(f.getKey(), ass);
            if (!allowTransient && !multiNeeded)
                for (ISyntaxConstraint a : ass)
                    quants.setAssignmentQuantity(a, f.getValue());
        }
        if (multipleAssignments.isEmpty() && allowTransients.isEmpty())
            return quants;
        for (Map.Entry<EStructuralFeature, Collection<ISyntaxConstraint>> e : allowTransients.asMap().entrySet()) {
            int min = 0;
            for (ISyntaxConstraint x : e.getValue())
                min += intervalProvider.getMin(quants, x, Sets.<ISyntaxConstraint>newHashSet());
            int val = min > 0 ? 1 : 0;
            quants.setFeatureQuantity(e.getKey(), val);
            if (e.getValue().size() == 1)
                quants.setAssignmentQuantity(e.getValue().iterator().next(), val);
        }
        //      System.out.println("AllowTransientsQuantities: " + quants.toString());
        if (multipleAssignments.isEmpty())
            return quants;
        return null;
        // TODO: implement an algorithm to handle multipleAssignments. For details, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=310454 
    }

    @Override
    public int getFeatureQuantity(EObject obj, EStructuralFeature feat) {
        if (feat.isMany()) {
            int count = 0, max = ((List<?>) obj.eGet(feat)).size();
            if (transSrvc.isCheckElementsIndividually(obj, feat)) {
                for (int i = 0; i < max; i++)
                    if (!transSrvc.isTransient(obj, feat, i))
                        count++;
                return count;
            }
            return transSrvc.isTransient(obj, feat, 0) ? 0 : max;
        }
        return transSrvc.isTransient(obj, feat, 0) ? 0 : 1;
    }

}