msi.gama.lang.gaml.resource.GamlResource.java Source code

Java tutorial

Introduction

Here is the source code for msi.gama.lang.gaml.resource.GamlResource.java

Source

/*********************************************************************************************
 *
 * 'GamlResource.java, in plugin msi.gama.lang.gaml, is part of the source code of the GAMA modeling and simulation
 * platform. (c) 2007-2016 UMI 209 UMMISCO IRD/UPMC & Partners
 *
 * Visit https://github.com/gama-platform/gama for license information and developers contact.
 * 
 *
 **********************************************************************************************/
package msi.gama.lang.gaml.resource;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.diagnostics.Severity;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.util.OnChangeEvictingCache;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import org.eclipse.xtext.validation.EObjectDiagnosticImpl;

import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimaps;

import gnu.trove.map.hash.THashMap;
import msi.gama.common.interfaces.IGamlIssue;
import msi.gama.lang.gaml.gaml.GamlPackage;
import msi.gama.lang.gaml.gaml.Model;
import msi.gama.lang.gaml.indexer.GamlResourceIndexer;
import msi.gama.runtime.IExecutionContext;
import msi.gama.util.GAML;
import msi.gaml.compilation.GamlCompilationError;
import msi.gaml.compilation.ast.ISyntacticElement;
import msi.gaml.descriptions.ModelDescription;
import msi.gaml.descriptions.ValidationContext;
import msi.gaml.factories.ModelFactory;

/*
 *
 * The class GamlResource.
 *
 * @author drogoul
 *
 * @since 24 avr. 2012
 */
public class GamlResource extends LazyLinkingResource {

    private static boolean MEMOIZE_DESCRIPTION = false;
    ModelDescription description;
    ISyntacticElement element;

    public ValidationContext getValidationContext() {
        return GamlResourceServices.getValidationContext(this);
    }

    public boolean hasSemanticErrors() {
        return getValidationContext().hasErrors();
    }

    @Override
    public String getEncoding() {
        return "UTF-8";
    }

    @Override
    public String toString() {
        return "GamlResource[" + getURI().lastSegment() + "]";
    }

    public void updateWith(final ModelDescription model, final boolean newState) {
        GamlResourceServices.updateState(getURI(), model, newState,
                GamlResourceServices.getValidationContext(this));
    }

    public ISyntacticElement getSyntacticContents() {
        if (element == null)
            setElement(GamlResourceServices.buildSyntacticContents(this));
        return element;
    }

    private final static Function<GamlResource, ISyntacticElement> TO_SYNTACTIC_CONTENTS = input -> {
        input.getResourceSet().getResource(input.getURI(), true);
        return input.getSyntacticContents();
    };

    private ModelDescription buildModelDescription(final LinkedHashMultimap<String, GamlResource> resources) {

        // Initializations
        GAML.getExpressionFactory().resetParser();
        final ModelFactory f = GAML.getModelFactory();
        final String modelPath = GamlResourceServices.getModelPathOf(this);
        final String projectPath = GamlResourceServices.getProjectPathOf(this);
        final boolean isEdited = GamlResourceServices.isEdited(this);
        final ValidationContext context = GamlResourceServices.getValidationContext(this);
        // If the resources imported are null, no need to go through their
        // validation
        if (resources == null) {
            final List<ISyntacticElement> self = Collections.singletonList(getSyntacticContents());
            return f.createModelDescription(projectPath, modelPath, self, context, isEdited, null);
        }
        // If there are no micro-models
        final Set<String> keySet = resources.keySet();
        if (keySet.size() == 1 && keySet.contains(null)) {
            final Iterable<ISyntacticElement> selfAndImports = Iterables.concat(
                    Collections.singleton(getSyntacticContents()),
                    Multimaps.transformValues(resources, TO_SYNTACTIC_CONTENTS).get(null));
            return f.createModelDescription(projectPath, modelPath, selfAndImports, context, isEdited, null);
        }
        final ListMultimap<String, ISyntacticElement> models = ArrayListMultimap.create();
        models.put(null, getSyntacticContents());
        models.putAll(Multimaps.transformValues(resources, TO_SYNTACTIC_CONTENTS));
        final List<ISyntacticElement> ownImports = models.removeAll(null);
        final Map<String, ModelDescription> compiledMicroModels = new THashMap<String, ModelDescription>();
        for (final String aliasName : models.keySet()) {
            final ModelDescription mic = GAML.getModelFactory().createModelDescription(projectPath, modelPath,
                    models.get(aliasName), context, isEdited, null);
            mic.setAlias(aliasName);
            compiledMicroModels.put(aliasName, mic);
        }
        return f.createModelDescription(projectPath, modelPath, ownImports, context, isEdited, compiledMicroModels);
    }

    public void invalidate(final GamlResource r, final String s) {
        GamlCompilationError error = null;
        if (GamlResourceIndexer.equals(r.getURI(), getURI())) {
            error = new GamlCompilationError(s, IGamlIssue.GENERAL, r.getContents().get(0), false, false);
        } else {
            error = new GamlCompilationError(s, IGamlIssue.GENERAL, r.getURI(), false, false);
        }
        getValidationContext().add(error);
        updateWith(null, true);
    }

    public ModelDescription buildCompleteDescription() {
        if (MEMOIZE_DESCRIPTION && description != null)
            return description;
        final LinkedHashMultimap<String, GamlResource> imports = GamlResourceIndexer.validateImportsOf(this);
        if (hasErrors() || hasSemanticErrors()) {
            setDescription(null);
            return null;
        }
        final ModelDescription model = buildModelDescription(imports);
        // If, for whatever reason, the description is null, we stop the
        // semantic validation
        if (model == null) {
            invalidate(this, "Impossible to validate " + URI.decode(getURI().lastSegment()) + " (check the logs)");
        }
        setDescription(model);
        return model;
    }

    /**
     * Validates the resource by compiling its contents into a ModelDescription and
     * discarding this ModelDescription afterwards
     * 
     * @note The errors will be available as part of the ValidationContext, which
     *       can later be retrieved from the resource, and which contains semantic
     *       errors (as opposed to the ones obtained via resource.getErrors(), which
     *       are syntactic errors), This collector can be probed for compilation
     *       errors via its hasErrors(), hasInternalErrors(), hasImportedErrors()
     *       methods
     *
     */
    public void validate() {
        final ModelDescription model = buildCompleteDescription();
        if (model == null) {
            updateWith(null, true);
            return;
        }

        // We then validate it and get rid of the description. The
        // documentation is produced only if the resource is
        // marked as 'edited'
        final boolean edited = GamlResourceServices.isEdited(this.getURI());
        try {
            model.validate(edited);
            updateWith(model, true);
        } finally {
            if (!MEMOIZE_DESCRIPTION)
                if (edited) {
                    GamlResourceServices.getResourceDocumenter().addCleanupTask(model);
                } else {
                    model.dispose();
                }
        }
    }

    @Override
    protected void updateInternalState(final IParseResult oldParseResult, final IParseResult newParseResult) {
        super.updateInternalState(oldParseResult, newParseResult);
        setElement(null);
        setDescription(null);
    }

    @Override
    protected void clearInternalState() {
        super.clearInternalState();
        setElement(null);
        setDescription(null);
    }

    @Override
    protected void doUnload() {
        super.doUnload();
        setElement(null);
        setDescription(null);
    }

    private void setDescription(final ModelDescription model) {
        if (!MEMOIZE_DESCRIPTION)
            return;
        if (model == description)
            return;
        if (description != null)
            description.dispose();
        description = model;
    }

    private void setElement(final ISyntacticElement model) {
        if (model == element)
            return;
        if (element != null)
            element.dispose();
        element = model;
    }

    /**
     * In the case of synthetic resources, pass the URI they depend on
     * 
     * @throws IOException
     */
    public void loadSynthetic(final InputStream is, final IExecutionContext additionalLinkingContext)
            throws IOException {
        final OnChangeEvictingCache r = getCache();
        r.getOrCreate(this).set("linking", additionalLinkingContext);
        getCache().execWithoutCacheClear(this, new IUnitOfWork.Void<GamlResource>() {

            @Override
            public void process(final GamlResource state) throws Exception {
                state.load(is, null);
                EcoreUtil.resolveAll(GamlResource.this);
            }
        });
        r.getOrCreate(this).set("linking", null);

    }

    @Override
    public OnChangeEvictingCache getCache() {
        return (OnChangeEvictingCache) super.getCache();
    }

    @Override
    protected void doLinking() {
        // If the imports are not correctly updated, we cannot proceed
        final EObject faulty = GamlResourceIndexer.updateImports(this);
        if (faulty != null) {
            final EAttribute attribute = getContents().get(0) instanceof Model
                    ? GamlPackage.Literals.IMPORT__IMPORT_URI
                    : GamlPackage.Literals.HEADLESS_EXPERIMENT__IMPORT_URI;
            getErrors().add(new EObjectDiagnosticImpl(Severity.ERROR, IGamlIssue.IMPORT_ERROR,
                    "Impossible to locate import", faulty, attribute, -1, null));
            return;
        }
        super.doLinking();
    }

    public boolean hasErrors() {
        return !getErrors().isEmpty() || getParseResult().hasSyntaxErrors();
    }

}