org.eclipse.sirius.common.acceleo.mtl.ide.AcceleoCompletionService.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.sirius.common.acceleo.mtl.ide.AcceleoCompletionService.java

Source

/*******************************************************************************
 * Copyright (c) 2011, 2015 THALES GLOBAL SERVICES 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
 *
 * Contributors:
 *    Obeo - initial API and implementation
 *******************************************************************************/
package org.eclipse.sirius.common.acceleo.mtl.ide;

import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.eclipse.acceleo.internal.ide.ui.editors.template.AcceleoCompletionProcessor;
import org.eclipse.acceleo.internal.ide.ui.editors.template.AcceleoCompletionProposal;
import org.eclipse.acceleo.internal.ide.ui.editors.template.AcceleoSourceContent;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.sirius.common.acceleo.mtl.business.internal.interpreter.DynamicAcceleoModule;
import org.eclipse.sirius.common.tools.api.contentassist.ContentProposal;
import org.eclipse.sirius.common.tools.api.contentassist.ContentProposalBuilder;
import org.eclipse.sirius.common.tools.api.contentassist.ContentProposalWithReplacement.ImageKind;

/**
 * Service to provide completion for the Acceleo MTL interpreter.
 * 
 * @author mporhel
 */
public class AcceleoCompletionService {

    private final Collection<URI> dependencies;

    private final Set<String> ignoreList;

    private final ResourceSet resourceSet;

    private final String moduleContent;

    /**
     * Constructor.
     * 
     * @param moduleContent
     *            the dynamic module.
     * @param module
     *            The dynamic acceleo module in which our dependencies have been
     *            compiled.
     * @param dependencies
     *            the declated imported mtl files
     */
    public AcceleoCompletionService(String moduleContent, DynamicAcceleoModule module,
            Collection<URI> dependencies) {
        this.moduleContent = moduleContent;
        this.ignoreList = module.getGeneratedQueryNames();
        this.resourceSet = module.getResourceSet();
        this.dependencies = dependencies;
    }

    /**
     * Compute a list of proposals for the given context.
     * 
     * @param contextText
     *            the text to complete.
     * @param cursorPosition
     *            the current cursor position.
     * @param moduleElementName
     *            the name of the edited module element.
     * @return a list of {@link ContentProposal}
     */
    public synchronized List<ContentProposal> getProposals(String contextText, int cursorPosition,
            String moduleElementName) {
        List<ContentProposal> proposals = Lists.newArrayList();

        AcceleoSourceContent src = new AcceleoSourceContent() {
            {
                /*
                 * We compiled in-memory and thus our dependencies may not be
                 * all accessible from disc. Use the existing resource set
                 * instead of trying to re-load/re-compile everything.
                 */
                this.syntaxHelpResourceSet = new ResourceSetImpl();
                this.syntaxHelpResourceSet.setResourceFactoryRegistry(resourceSet.getResourceFactoryRegistry());
                for (Resource res : resourceSet.getResources()) {
                    final Resource copy = this.syntaxHelpResourceSet.createResource(res.getURI());
                    for (EObject root : res.getContents()) {
                        copy.getContents().add(EcoreUtil.copy(root));
                    }
                    this.syntaxHelpResourceSet.getResources().add(copy);
                }
            }

            @Override
            public List<URI> getAccessibleOutputFiles() {
                return getDeclaredImports();
            }
        };
        src.init(new StringBuffer(moduleContent));

        String expression = contextText.substring(1);
        int position = -1;
        // Add a trailing parenthesis in case we have more than 10 queries.
        final int queryIndex = moduleContent.indexOf(moduleElementName + '(');

        if (queryIndex > 0) {
            position = moduleContent.indexOf(expression, queryIndex);
        }

        if (position > 0) {
            // remove one : we have a leading [ in the text, but not in the
            // module
            position += cursorPosition - 1;

            AcceleoCompletionProcessor processor = new AcceleoCompletionProcessor(src);
            ICompletionProposal[] props = processor.computeCompletionProposals(null, position);

            for (ICompletionProposal prop : props) {
                String displayString = prop.getDisplayString();
                if (!(displayString.contains("(") //$NON-NLS-1$
                        && ignoreList.contains(displayString.substring(0, displayString.indexOf("("))))) { //$NON-NLS-1$
                    if (prop instanceof AcceleoCompletionProposal) {
                        AcceleoCompletionProposal acceleoCompletionProposal = (AcceleoCompletionProposal) prop;

                        String proposalText = getCleanedProposalText(
                                acceleoCompletionProposal.getReplacementString());
                        String information = getCleanedAdditionalText(prop.getAdditionalProposalInfo());

                        final int offset = acceleoCompletionProposal.getReplacementOffset() - position
                                + cursorPosition;
                        final int length = acceleoCompletionProposal.getReplacementLength();

                        ContentProposal proposal = ContentProposalBuilder
                                .proposal(proposalText, prop.getDisplayString(), information)
                                .withReplacement(offset, length)
                                .withImage(acceleoCompletionProposal.getImage(), ImageKind.SWT_IMAGE).build();

                        proposals.add(proposal);
                    } else {
                        IDocument doc = new Document(moduleContent);
                        String end = doc.get().substring(position, moduleContent.indexOf(']', position) + 1);
                        prop.apply(doc);

                        final String completedText = doc.get();
                        final String proposalText = this.getCleanedProposalText(
                                completedText.substring(position, completedText.indexOf(end, position)));
                        final String information = getCleanedAdditionalText(prop.getAdditionalProposalInfo());

                        ContentProposal proposal = ContentProposalBuilder
                                .proposal(proposalText, displayString, information)
                                .withReplacement(cursorPosition, 0).withImage(prop.getImage(), ImageKind.SWT_IMAGE)
                                .build();

                        proposals.add(proposal);
                    }
                }
            }
        }
        return proposals;
    }

    /**
     * Remove the "${variable}" parts in the completion. There are needed in the
     * context of a full editor, but here they just add noise.
     */
    private String getCleanedProposalText(String s) {
        return s.replaceAll("\\$\\{[^\\}]+\\}", ""); //$NON-NLS-1$ //$NON-NLS-2$
    }

    /**
     * Remove the HTML-like tags from the tooltip text, as they not supported
     * with the current tooltips/completion code.
     */
    private String getCleanedAdditionalText(String s) {
        return s.replaceAll("<br/>", "\n").replaceAll("<[^>]+>", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
    }

    private List<URI> getDeclaredImports() {
        List<URI> accessibleOutputFiles = Lists.newArrayList();
        if (dependencies != null) {
            Iterables.addAll(accessibleOutputFiles, Iterables.filter(dependencies, Predicates.notNull()));
        }
        return accessibleOutputFiles;
    }
}