org.eclipse.xtext.serializer.sequencer.ContextFinder.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.serializer.sequencer.ContextFinder.java

Source

/*******************************************************************************
 * 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 static org.eclipse.xtext.serializer.analysis.SerializationContext.*;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.serializer.ISerializationContext;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraint;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IConstraintElement;
import org.eclipse.xtext.serializer.analysis.IGrammarConstraintProvider.IFeatureInfo;
import org.eclipse.xtext.serializer.analysis.SerializationContext;
import org.eclipse.xtext.serializer.analysis.SerializationContextMap;
import org.eclipse.xtext.serializer.sequencer.ISemanticNodeProvider.INodesForEObjectProvider;
import org.eclipse.xtext.serializer.sequencer.ITransientValueService.ValueTransient;
import org.eclipse.xtext.xtext.RuleNames;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * @author Moritz Eysholdt - Initial contribution and API
 */
@Singleton
public class ContextFinder implements IContextFinder {

    @Inject
    protected IAssignmentFinder assignmentFinder;

    protected SerializationContextMap<IConstraint> constraints;

    @Inject
    protected IGrammarConstraintProvider grammarConstraintProvider;

    @Inject
    protected ISemanticNodeProvider nodesProvider;

    @Inject
    protected RuleNames ruleNames;

    @Inject
    protected ITransientValueService transientValues;

    @Inject
    protected TransientValueUtil transientValueUtil;

    protected Set<AbstractElement> findAssignedElements(EObject obj, EStructuralFeature feature,
            Multimap<AbstractElement, ISerializationContext> assignments) {
        if (feature.isMany()) {
            Set<AbstractElement> r = Sets.newLinkedHashSet();
            INodesForEObjectProvider nodes = nodesProvider.getNodesForSemanticObject(obj, null);
            switch (transientValues.isListTransient(obj, feature)) {
            case SOME:
                List<?> values1 = (List<?>) obj.eGet(feature);
                int j = 0;
                for (int i = 0; i < values1.size(); i++)
                    if (!transientValues.isValueInListTransient(obj, i, feature)) {
                        Object value = values1.get(i);
                        INode node = nodes.getNodeForMultiValue(feature, i, j, value);
                        r.addAll(assignmentFinder.findAssignmentsByValue(obj, assignments, value, node));
                        j++;
                    }
                return r;
            case NO:
                List<?> values2 = (List<?>) obj.eGet(feature);
                for (int i = 0; i < values2.size(); i++) {
                    Object value = values2.get(i);
                    INode node = nodes.getNodeForMultiValue(feature, i, i, value);
                    r.addAll(assignmentFinder.findAssignmentsByValue(obj, assignments, value, node));
                }
                return r;
            default:
                return Collections.emptySet();
            }
        } else {
            if (transientValues.isValueTransient(obj, feature) == ValueTransient.YES)
                return Collections.emptySet();
            Object value = obj.eGet(feature);
            INode node = nodesProvider.getNodesForSemanticObject(obj, null).getNodeForSingelValue(feature, value);
            return assignmentFinder.findAssignmentsByValue(obj, assignments, value, node);
        }
    }

    protected Multimap<AbstractElement, ISerializationContext> collectAssignments(
            Multimap<IConstraint, ISerializationContext> constraints, EStructuralFeature feature) {
        Multimap<AbstractElement, ISerializationContext> result = ArrayListMultimap.create();
        for (Entry<IConstraint, Collection<ISerializationContext>> e : constraints.asMap().entrySet()) {
            IConstraint constraint = e.getKey();
            Collection<ISerializationContext> contexts = e.getValue();
            IFeatureInfo featureInfo = constraint.getFeatures()[constraint.getType().getFeatureID(feature)];
            List<IConstraintElement> assignments = featureInfo.getAssignments();
            for (IConstraintElement assignment : assignments) {
                result.putAll(assignment.getGrammarElement(), contexts);
            }
        }
        return result;
    }

    @Override
    public Set<ISerializationContext> findByContents(EObject semanticObject,
            Iterable<ISerializationContext> contextCandidates) {
        if (semanticObject == null)
            throw new NullPointerException();

        initConstraints();

        Multimap<IConstraint, ISerializationContext> constraints;
        if (contextCandidates != null)
            constraints = getConstraints(semanticObject, contextCandidates);
        else
            constraints = getConstraints(semanticObject);

        if (constraints.size() < 2)
            return Sets.newLinkedHashSet(constraints.values());

        for (IConstraint cand : Lists.newArrayList(constraints.keySet()))
            if (!isValidValueQuantity(cand, semanticObject))
                constraints.removeAll(cand);

        if (constraints.size() < 2)
            return Sets.newLinkedHashSet(constraints.values());

        LinkedHashSet<ISerializationContext> result = Sets.newLinkedHashSet(constraints.values());
        for (EStructuralFeature feat : semanticObject.eClass().getEAllStructuralFeatures()) {
            if (transientValueUtil.isTransient(semanticObject, feat) != ValueTransient.NO)
                continue;
            if (feat.isMany() && ((List<?>) semanticObject.eGet(feat)).isEmpty())
                continue;
            Multimap<AbstractElement, ISerializationContext> assignments = collectAssignments(constraints, feat);
            Set<AbstractElement> assignedElements = findAssignedElements(semanticObject, feat, assignments);
            Set<ISerializationContext> keep = Sets.newHashSet();
            for (AbstractElement ele : assignedElements)
                keep.addAll(assignments.get(ele));
            result.retainAll(keep);
        }
        return result;
    }

    @Override
    public Set<ISerializationContext> findByContentsAndContainer(EObject semanticObject,
            Iterable<ISerializationContext> contextCandidates) {
        initConstraints();
        contextCandidates = findContextsByContainer(semanticObject, contextCandidates);
        if (contextCandidates != null && Iterables.size(contextCandidates) < 2)
            return Sets.newLinkedHashSet(contextCandidates);
        return findByContents(semanticObject, contextCandidates);
    }

    protected List<ISerializationContext> createContextsForFeatures(Collection<ISerializationContext> containers,
            IFeatureInfo feature, EObject sem) {
        List<ISerializationContext> result = Lists.newArrayList();
        for (IConstraintElement assignment : feature.getAssignments()) {
            for (ISerializationContext container : containers) {
                result.add(SerializationContext.forChild(container, assignment.getGrammarElement(), sem));
            }
        }
        return result;
    }

    protected Iterable<ISerializationContext> findContextsByContainer(EObject sem,
            Iterable<ISerializationContext> contextCandidates) {
        if (sem.eResource() != null && sem.eResource().getContents().contains(sem))
            return Collections.singleton(getRootContext(sem));
        EReference ref = sem.eContainmentFeature();
        if (ref == null || (contextCandidates != null && Iterables.size(contextCandidates) < 2))
            return contextCandidates;
        Multimap<IConstraint, ISerializationContext> containerConstraints = getConstraints(sem.eContainer());
        int refID = sem.eContainer().eClass().getFeatureID(ref);
        Set<ISerializationContext> childContexts = Sets.newLinkedHashSet();
        for (Entry<IConstraint, Collection<ISerializationContext>> e : Lists
                .newArrayList(containerConstraints.asMap().entrySet())) {
            IConstraint constraint = e.getKey();
            Collection<ISerializationContext> contexts = e.getValue();
            if (constraint.getFeatures()[refID] == null)
                containerConstraints.removeAll(constraint);
            else {
                childContexts.addAll(createContextsForFeatures(contexts, constraint.getFeatures()[refID], sem));
            }
        }
        Set<ISerializationContext> result;
        if (contextCandidates != null) {
            result = Sets.newLinkedHashSet(contextCandidates);
            result.retainAll(childContexts);
        } else
            result = childContexts;
        if (result.size() < 2)
            return result;
        Iterable<ISerializationContext> filteredContexts = findContextsByContainer(sem.eContainer(),
                containerConstraints.values());
        childContexts = Sets.newLinkedHashSet();
        for (Entry<IConstraint, Collection<ISerializationContext>> e : Lists
                .newArrayList(containerConstraints.asMap().entrySet()))
            if (intersect(filteredContexts, e.getValue()))
                childContexts.addAll(createContextsForFeatures(e.getValue(), e.getKey().getFeatures()[refID], sem));
        result.retainAll(childContexts);
        return result;
    }

    @Override
    @Deprecated
    public Iterable<EObject> findContextsByContents(EObject semanticObject, Iterable<EObject> contextCandidates) {
        List<ISerializationContext> candidates = fromEObjects(contextCandidates, semanticObject);
        return fromIContexts(findByContents(semanticObject, candidates));
    }

    @Override
    @Deprecated
    public Iterable<EObject> findContextsByContentsAndContainer(EObject semanticObject,
            Iterable<EObject> contextCandidates) {
        List<ISerializationContext> candidates = fromEObjects(contextCandidates, semanticObject);
        return fromIContexts(findByContentsAndContainer(semanticObject, candidates));
    }

    protected Multimap<IConstraint, ISerializationContext> getConstraints(EObject sem) {
        EClass type = sem == null ? null : sem.eClass();
        Multimap<IConstraint, ISerializationContext> result = ArrayListMultimap.create();
        for (SerializationContextMap.Entry<IConstraint> e : constraints.values()) {
            IConstraint constraint = e.getValue();
            for (ISerializationContext context : e.getContexts()) {
                if (constraint.getType() == type) {
                    result.put(constraint, context);
                }
            }
        }
        return result;
    }

    protected Multimap<IConstraint, ISerializationContext> getConstraints(EObject sem,
            Iterable<ISerializationContext> contextCandidates) {
        EClass type = sem == null ? null : sem.eClass();
        Multimap<IConstraint, ISerializationContext> result = ArrayListMultimap.create();
        for (ISerializationContext ctx : contextCandidates) {
            IConstraint constraint = constraints.get(ctx);
            if (constraint != null && constraint.getType() == type) {
                result.put(constraint, ctx);
            }
        }
        return result;
    }

    protected ISerializationContext getRootContext(EObject sem) {
        for (AbstractRule rule : ruleNames.getAllRules())
            if (GrammarUtil.isEObjectRule(rule))
                return SerializationContext.fromEObject(rule, sem);
        throw new RuntimeException("There is no parser rule in the grammar.");
    }

    public void initConstraints() {
        if (constraints == null) {
            constraints = grammarConstraintProvider.getConstraints(ruleNames.getContextGrammar());
        }
    }

    protected boolean intersect(Iterable<ISerializationContext> it1, Iterable<ISerializationContext> it2) {
        for (ISerializationContext i1 : it1)
            for (ISerializationContext i2 : it2)
                if (i1.equals(i2))
                    return true;
        return false;
    }

    protected boolean isValidValueQuantity(IConstraint constraint, EObject semanicObj) {
        if (constraint == null)
            return false;
        for (int featureID = 0; featureID < semanicObj.eClass().getFeatureCount(); featureID++) {
            IFeatureInfo featureInfo = constraint.getFeatures()[featureID];
            EStructuralFeature feature = semanicObj.eClass().getEStructuralFeature(featureID);
            if (feature.isMany()) {
                int count = transientValueUtil.countNonTransientListValues(semanicObj, feature);
                if (count > featureInfo.getUpperBound())
                    return false;
                if (count < featureInfo.getLowerBound())
                    return false;
            } else {
                ValueTransient valueTransient = transientValues.isValueTransient(semanicObj, feature);
                switch (valueTransient) {
                case NO:
                    if (featureInfo == null)
                        return false;
                    if (featureInfo.getUpperBound() <= 0)
                        return false;
                    break;
                case YES:
                    if (featureInfo == null)
                        break;
                    if (featureInfo.getLowerBound() > 0)
                        return false;
                    break;
                case PREFERABLY:
                    break;
                }
            }
        }
        return true;
    }

}