org.eclipse.sirius.business.internal.contribution.IncrementalModelContributor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.business.internal.contribution.IncrementalModelContributor.java

Source

/*******************************************************************************
 * Copyright (c) 2011 THALES GLOBAL SERVICES.
 * 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
 *
 * Contributors:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.business.internal.contribution;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
import org.eclipse.sirius.business.api.query.ViewpointQuery;
import org.eclipse.sirius.description.contribution.Contribution;
import org.eclipse.sirius.description.contribution.FeatureContribution;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.ext.emf.AllContents;
import org.eclipse.sirius.viewpoint.description.Viewpoint;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * Similar to a {@link ModelContributor}, but successive operations with
 * different inputs incrementally modify the result of the first one.
 * 
 * @author pierre-charles.david@obeo.fr
 */
public class IncrementalModelContributor extends ModelContributor {
    /**
     * The resulting model. <code>null</code> initially, before the first
     * contribution application.
     */
    private EObject model;

    private Map<EObject, Object> modelIds;

    private Function<EObject, Object> idFunction;

    private Map<EObject, Object> contributions;

    private Copier currentCopier;

    private Map<Viewpoint, String> viewpointUris;

    private List<EStructuralFeature> targetsWithEOpposites;

    /**
     * Create a new {@link IncrementalModelContributor}.
     * 
     * @param contributionFinder
     *            the function to use to find all the potentially applicable
     *            contributions from a source model.
     * @param resolver
     *            the resolver to use on the contributions to locate their
     *            actual source and target elements.
     * @param idFunction
     *            the function used to obtain logical identifiers for model
     *            elements, required to perform the incremental update.
     * 
     */
    public IncrementalModelContributor(Function<Iterable<EObject>, Iterable<Contribution>> contributionFinder,
            ReferenceResolver resolver, Function<EObject, Object> idFunction) {
        super(contributionFinder, resolver);
        this.idFunction = Preconditions.checkNotNull(idFunction);
    }

    /**
     * Applies all the applicable contributions found in the sources (including
     * the target model itself) to the target model.
     * 
     * @param targetModel
     *            the model to which the contributions should be applied.
     * @param contributionSources
     *            source models, in addition to the target model itself, which
     *            may provide contributions.
     * @return the modified target model, with the contributions applied.
     */
    public EObject apply(EObject targetModel, Collection<? extends EObject> contributionSources) {
        Collection<EObject> referenceInputs = Lists.newArrayList(contributionSources);
        currentCopier = new EcoreUtil.Copier();
        List<EObject> inputs = Lists.newArrayList(currentCopier.copyAll(referenceInputs));
        currentCopier.copyReferences();

        Map<EObject, Object> inputIds = Maps.newHashMap();
        for (EObject root : referenceInputs) {
            for (EObject obj : AllContents.of(root, true)) {
                inputIds.put(currentCopier.get(obj), idFunction.apply(obj));
            }
        }

        viewpointUris = Maps.newHashMap();
        for (Viewpoint originalVP : Iterables.filter(currentCopier.keySet(), Viewpoint.class)) {
            Option<URI> uri = new ViewpointQuery(originalVP).getViewpointURI();
            if (uri.some()) {
                viewpointUris.put((Viewpoint) currentCopier.get(originalVP), uri.get().toString());
            }
        }

        EObject result = super.apply(currentCopier.get(targetModel), inputs);
        postProcess(result);
        contributions = Maps.newHashMap(Maps.filterKeys(inputIds, Predicates.in(additions)));

        if (model == null) {
            model = result;
            modelIds = Maps.newHashMap(Maps.filterKeys(inputIds, new Predicate<EObject>() {
                public boolean apply(EObject input) {
                    return input == model || EcoreUtil.isAncestor(model, input);
                }
            }));
        } else {
            Function<EObject, Object> f = update(result, inputIds);
            Map<EObject, Object> newIds = Maps.newHashMap();
            for (EObject obj : AllContents.of(model, true)) {
                newIds.put(obj, f.apply(obj));
            }
            modelIds = newIds;
        }
        return model;
    }

    /**
     * Allow sub-classes to adjust the resulting model right after the
     * contributions have been applied.
     * 
     * @param result
     *            the result model.
     */
    protected void postProcess(EObject result) {
        // Nothing by default.
    }

    /**
     * Returns the identifier associated with obj, which must be an element of
     * the result model.
     * 
     * @param obj
     *            an element of the result model.
     * @return the identifier of obj.
     */
    public Object getIdentifier(EObject obj) {
        return modelIds.get(obj);
    }

    /**
     * Reset the state of the contributor.
     * 
     * @param newModel
     *            the model.
     * @param ids
     *            the identifiers of every elements of the model.
     */
    public void resetState(EObject newModel, Map<EObject, ? extends Object> ids) {
        this.model = newModel;
        this.modelIds = Maps.newHashMap(ids);
    }

    public Map<EObject, Object> getContributionPoints() {
        return ImmutableMap.copyOf(contributions);
    }

    /**
     * Returns the logical Sirius URI of a copied Sirius instance. The URI
     * can not be retrieved from the copy as it is not inside a resource.
     * 
     * @param copiedSirius
     *            a copied instance of Sirius
     * @return the logical URI of the Sirius, as retrived from the original
     *         instance which lives in a VSM.
     */
    protected String getSiriusURI(Viewpoint copiedSirius) {
        return viewpointUris.get(copiedSirius);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected List<Contribution> findAllContributions(EObject targetModel, Iterable<EObject> sources) {
        /*
         * Return the versions of the Contribution objects from the copied
         * sources, not the original (as those would point to original elements
         * as their source and targets).
         */
        List<Contribution> rawResult = super.findAllContributions(targetModel, sources);
        List<Contribution> result = Lists.newArrayListWithCapacity(rawResult.size());
        for (Contribution contribution : rawResult) {
            if (currentCopier.containsKey(contribution)) {
                EObject copy = currentCopier.get(contribution);
                if (copy instanceof Contribution) {
                    result.add((Contribution) copy);
                }
            } else {
                result.add(contribution);
            }
        }
        targetsWithEOpposites = Lists.newArrayList();
        for (Contribution contribution : result) {
            for (FeatureContribution fc : contribution.getFeatureMask()) {
                if (fc.getTargetFeature() instanceof EReference
                        && ((EReference) fc.getTargetFeature()).getEOpposite() != null) {
                    targetsWithEOpposites.add(((EReference) fc.getTargetFeature()).getEOpposite());
                }
            }
        }
        return result;
    }

    private Function<EObject, Object> update(EObject result, final Map<EObject, Object> inputIds) {
        Function<EObject, Object> idFunction2 = new Function<EObject, Object>() {
            public Object apply(EObject from) {
                if (modelIds.containsKey(from)) {
                    return modelIds.get(from);
                } else {
                    return inputIds.get(from);
                }
            }
        };
        Matcher matcher = new IdentifierBasedMatcher(idFunction2);
        Updater updater = new Updater(matcher, model, result);
        updater.setFeaturesToIgnore(targetsWithEOpposites);
        updater.update();
        return idFunction2;
    }
}