de.se_rwth.langeditor.texteditor.hyperlinks.HyperlinkDetectorImpl.java Source code

Java tutorial

Introduction

Here is the source code for de.se_rwth.langeditor.texteditor.hyperlinks.HyperlinkDetectorImpl.java

Source

/*******************************************************************************
 * MontiCore Language Workbench
 * Copyright (c) 2015, 2016, MontiCore, All rights reserved.
 *  
 * This project is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this project. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package de.se_rwth.langeditor.texteditor.hyperlinks;

import java.util.Collections;
import java.util.Optional;
import java.util.function.Supplier;

import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.Trees;
import org.eclipse.core.resources.IStorage;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.ITextEditor;

import com.google.common.collect.Lists;
import com.google.inject.Inject;

import de.monticore.ast.ASTNode;
import de.se_rwth.langeditor.injection.TextEditorScoped;
import de.se_rwth.langeditor.language.Language;
import de.se_rwth.langeditor.modelstates.Nodes;
import de.se_rwth.langeditor.modelstates.ModelState;
import de.se_rwth.langeditor.modelstates.ObservableModelStates;
import de.se_rwth.langeditor.util.Misc;
import de.se_rwth.langeditor.util.antlr.ParseTrees;

@TextEditorScoped
public final class HyperlinkDetectorImpl extends AbstractHyperlinkDetector {

    private final Language language;

    private final Nodes nodes;

    private final ObservableModelStates observableModelStates;

    private final Supplier<Optional<ModelState>> currentModelState;

    @Inject
    HyperlinkDetectorImpl(Language language, Nodes nodes, IStorage storage,
            ObservableModelStates observableModelStates) {
        this.language = language;
        this.nodes = nodes;
        this.observableModelStates = observableModelStates;
        this.currentModelState = () -> observableModelStates.findModelState(storage);
    }

    @Override
    public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region,
            boolean canShowMultipleHyperlinks) {
        Optional<ASTNode> enclosingASTNode = getEnclosingASTNode(region);
        Optional<Supplier<Optional<ASTNode>>> resolver = enclosingASTNode.flatMap(language::createResolver);
        if (enclosingASTNode.isPresent() && resolver.isPresent()) {
            IHyperlink hyperlink = new ParseTreeHyperlinkAdapter(
                    enclosingASTNode.flatMap(nodes::getParseTree).get(), resolver.get());
            return new IHyperlink[] { hyperlink };
        }
        return null;
    }

    private Optional<ASTNode> getEnclosingASTNode(IRegion region) {
        return currentModelState.get()
                .flatMap(currentModelState -> ParseTrees
                        .getTerminalBySourceCharIndex(currentModelState.getRootContext(), region.getOffset()))
                .map(Trees::getAncestors).map(ancestors -> Lists.reverse(ancestors)).orElse(Collections.emptyList())
                .stream().filter(ParseTree.class::isInstance).map(ParseTree.class::cast).map(Nodes::getAstNode)
                .filter(Optional::isPresent).map(Optional::get).findFirst();
    }

    private class ParseTreeHyperlinkAdapter implements IHyperlink {

        private final ParseTree parseTree;

        private final Supplier<Optional<ASTNode>> resolver;

        private ParseTreeHyperlinkAdapter(ParseTree parseTree, Supplier<Optional<ASTNode>> resolver) {
            this.parseTree = parseTree;
            this.resolver = resolver;
        }

        @Override
        public IRegion getHyperlinkRegion() {
            Interval interval = ParseTrees.tokenInterval(parseTree);
            return new Region(interval.a, interval.length() - 1);
        }

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

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

        @Override
        public void open() {
            resolver.get().flatMap(nodes::getParseTree).ifPresent(resolved -> {
                findStorage(resolved).ifPresent(storage -> openEditor(resolved, storage));
            });
        }

        private Optional<IStorage> findStorage(ParseTree parseTree) {
            return observableModelStates.getModelStates().stream().filter(modelState -> Trees
                    .descendants(modelState.getRootContext()).stream().anyMatch(parseTree::equals)).findFirst()
                    .map(ModelState::getStorage);
        }
    }

    private void openEditor(ParseTree parseTree, IStorage storage) {
        try {
            IEditorDescriptor editorDescriptor = IDE.getEditorDescriptor(storage.getName());
            ITextEditor textEditor = (ITextEditor) IDE.openEditor(
                    PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(),
                    Misc.getEditorInput(storage), editorDescriptor.getId());
            int startIndex = ParseTrees.getFirstToken(parseTree).get().getStartIndex();
            textEditor.selectAndReveal(startIndex, 0);
        } catch (PartInitException e) {
            throw new RuntimeException(e);
        }
    }
}