org.eclipse.m2e.editor.xml.PomHyperlinkDetector.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.m2e.editor.xml.PomHyperlinkDetector.java

Source

/*******************************************************************************
 * Copyright (c) 2008-2010 Sonatype, Inc.
 * 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:
 *      Sonatype, Inc. - initial API and implementation
 *******************************************************************************/

package org.eclipse.m2e.editor.xml;

import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.ARTIFACT_ID;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.DEPENDENCY;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.EXTENSION;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.GROUP_ID;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.NAME;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PARENT;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PLUGIN;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.PROPERTIES;
import static org.eclipse.m2e.core.ui.internal.editing.PomEdits.VERSION;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.source.Annotation;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.ui.StructuredTextEditor;

import org.apache.maven.model.Build;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.DependencyManagement;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.Model;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginManagement;
import org.apache.maven.project.MavenProject;

import org.eclipse.m2e.core.internal.IMavenConstants;
import org.eclipse.m2e.core.ui.internal.actions.OpenPomAction;
import org.eclipse.m2e.core.ui.internal.actions.OpenPomAction.MavenPathStorageEditorInput;
import org.eclipse.m2e.editor.xml.internal.Messages;
import org.eclipse.m2e.editor.xml.internal.NodeOperation;
import org.eclipse.m2e.editor.xml.internal.XmlUtils;

/**
 * @author Eugene Kuleshov
 * @author Milos Kleint
 */
public class PomHyperlinkDetector implements IHyperlinkDetector {
    private static final Logger log = LoggerFactory.getLogger(PomHyperlinkDetector.class);

    public IHyperlink[] detectHyperlinks(final ITextViewer textViewer, final IRegion region,
            boolean canShowMultipleHyperlinks) {
        if (region == null || textViewer == null) {
            return null;
        }

        IDocument document = textViewer.getDocument();
        if (document == null) {
            return null;
        }

        IRegion lineInfo;
        String line;
        try {
            lineInfo = document.getLineInformationOfOffset(region.getOffset());
            line = document.get(lineInfo.getOffset(), lineInfo.getLength());
        } catch (BadLocationException ex) {
            return null;
        }

        if (line.length() == 0) {
            return null;
        }
        final List<IHyperlink> hyperlinks = new ArrayList<IHyperlink>();
        final int offset = region.getOffset();

        XmlUtils.performOnCurrentElement(document, offset, new NodeOperation<Node>() {
            public void process(Node node, IStructuredDocument structured) {
                if (textViewer instanceof ISourceViewer) {
                    IHyperlink[] links = openExternalMarkerDefinition((ISourceViewer) textViewer, offset);
                    if (links.length > 0) {
                        hyperlinks.addAll(Arrays.asList(links));
                    }
                }
                //check if we have a property expression at cursor
                IHyperlink link = openPropertyDefinition(node, textViewer, offset);
                if (link != null) {
                    hyperlinks.add(link);
                }
                //now check if the dependency/plugin has a version element or not, if not, try searching for it in DM/PM of effective pom
                link = openDPManagement(node, textViewer, offset);
                if (link != null) {
                    hyperlinks.add(link);
                }
                //check if <module> text is selected.
                link = openModule(node, textViewer, offset);
                if (link != null) {
                    hyperlinks.add(link);
                }
                link = openPOMbyID(node, textViewer, offset);
                if (link != null) {
                    hyperlinks.add(link);
                }
            }
        });

        if (hyperlinks.size() > 0) {
            return hyperlinks.toArray(new IHyperlink[0]);
        }
        return null;
    }

    static ManagedArtifactRegion findManagedArtifactRegion(Node current, ITextViewer textViewer, int offset) {
        while (current != null && !(current instanceof Element)) {
            current = current.getParentNode();
        }
        if (current != null) {
            Node artNode = null;
            Node groupNode = null;
            if (ARTIFACT_ID.equals(current.getNodeName())) { //$NON-NLS-1$
                artNode = current;
            }
            if (GROUP_ID.equals(current.getNodeName())) { //$NON-NLS-1$
                groupNode = current;
            }
            //only on artifactid and groupid elements..
            if (artNode == null && groupNode == null) {
                return null;
            }
            Node root = current.getParentNode();
            boolean isDependency = false;
            boolean isPlugin = false;
            if (root != null) {
                String name = root.getNodeName();
                if (DEPENDENCY.equals(name)) { //$NON-NLS-1$
                    isDependency = true;
                }
                if (PLUGIN.equals(name)) { //$NON-NLS-1$
                    isPlugin = true;
                }
            } else {
                return null;
            }
            if (!isDependency && !isPlugin) {
                //some kind of other identifier
                return null;
            }
            //now see if version is missing
            NodeList childs = root.getChildNodes();
            for (int i = 0; i < childs.getLength(); i++) {
                Node child = childs.item(i);
                if (child instanceof Element) {
                    Element el = (Element) child;
                    if (VERSION.equals(el.getNodeName())) { //$NON-NLS-1$
                        return null;
                    }
                    if (artNode == null && ARTIFACT_ID.equals(el.getNodeName())) { //$NON-NLS-1$
                        artNode = el;
                    }
                    if (groupNode == null && GROUP_ID.equals(el.getNodeName())) { //$NON-NLS-1$
                        groupNode = el;
                    }
                }
            }
            if (groupNode != null && artNode != null) {
                assert groupNode instanceof IndexedRegion;
                assert artNode instanceof IndexedRegion;

                IndexedRegion groupReg = (IndexedRegion) groupNode;
                IndexedRegion artReg = (IndexedRegion) artNode;
                int startOffset = Math.min(groupReg.getStartOffset(), artReg.getStartOffset());
                int length = Math.max(groupReg.getEndOffset(), artReg.getEndOffset()) - startOffset;
                String groupId = XmlUtils.getTextValue(groupNode);
                String artifactId = XmlUtils.getTextValue(artNode);
                final MavenProject prj = XmlUtils.extractMavenProject(textViewer);
                if (prj != null) {
                    //now we can create the region I guess, 
                    return new ManagedArtifactRegion(startOffset, length, groupId, artifactId, isDependency,
                            isPlugin, prj);
                }
            }
        }
        return null;
    }

    public static IHyperlink createHyperlink(final ManagedArtifactRegion region) {
        return new IHyperlink() {
            public IRegion getHyperlinkRegion() {
                return region;
            }

            public String getHyperlinkText() {
                return NLS.bind(Messages.PomHyperlinkDetector_link_managed,
                        "" + region.groupId + ":" + region.artifactId);
            }

            public String getTypeLabel() {
                return "pom-dependency-plugin-management"; //$NON-NLS-1$
            }

            public void open() {
                //see if we can find the plugin in plugin management of resolved project.
                MavenProject mavprj = region.project;
                if (mavprj != null) {
                    InputLocation openLocation = findLocationForManagedArtifact(region, mavprj);
                    if (openLocation != null) {
                        File file = XmlUtils.fileForInputLocation(openLocation, mavprj);
                        if (file != null) {
                            IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI());
                            openXmlEditor(fileStore, openLocation.getLineNumber(), openLocation.getColumnNumber(),
                                    openLocation.getSource().getModelId());
                        }
                    }
                }
            }
        };

    }

    private IHyperlink openDPManagement(Node current, ITextViewer textViewer, int offset) {
        final ManagedArtifactRegion region = findManagedArtifactRegion(current, textViewer, offset);
        if (region != null) {
            return createHyperlink(region);
        }
        return null;
    }

    static InputLocation findLocationForManagedArtifact(final ManagedArtifactRegion region, MavenProject mavprj) {
        Model mdl = mavprj.getModel();
        InputLocation openLocation = null;
        if (region.isDependency) {
            DependencyManagement dm = mdl.getDependencyManagement();
            if (dm != null) {
                List<Dependency> list = dm.getDependencies();
                String id = region.groupId + ":" + region.artifactId + ":"; //$NON-NLS-1$ //$NON-NLS-2$
                if (list != null) {
                    for (Dependency dep : list) {
                        if (dep.getManagementKey().startsWith(id)) {
                            InputLocation location = dep.getLocation(ARTIFACT_ID); //$NON-NLS-1$
                            //when would this be null?
                            if (location != null) {
                                openLocation = location;
                                break;
                            }
                        }
                    }
                }
            }
        }
        if (region.isPlugin) {
            Build build = mdl.getBuild();
            if (build != null) {
                PluginManagement pm = build.getPluginManagement();
                if (pm != null) {
                    List<Plugin> list = pm.getPlugins();
                    String id = Plugin.constructKey(region.groupId, region.artifactId);
                    if (list != null) {
                        for (Plugin plg : list) {
                            if (id.equals(plg.getKey())) {
                                InputLocation location = plg.getLocation(ARTIFACT_ID); //$NON-NLS-1$
                                //when would this be null?
                                if (location != null) {
                                    openLocation = location;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return openLocation;
    }

    static ExpressionRegion findExpressionRegion(Node current, ITextViewer viewer, int offset) {
        if (current != null && current instanceof Text) {
            Text node = (Text) current;
            String value = node.getNodeValue();
            if (value != null) {
                assert node instanceof IndexedRegion;
                IndexedRegion reg = (IndexedRegion) node;
                int index = offset - reg.getStartOffset();
                String before = value.substring(0, Math.min(index + 1, value.length()));
                String after = value.substring(Math.min(index + 1, value.length()));
                int start = before.lastIndexOf("${"); //$NON-NLS-1$
                if (before.lastIndexOf("}") > start) {//$NON-NLS-1$
                    //we might be in between two expressions..
                    start = -1;
                }
                int end = after.indexOf("}"); //$NON-NLS-1$
                if (after.indexOf("${") != -1 && after.indexOf("${") < end) {//$NON-NLS-1$
                    //we might be in between two expressions..
                    end = -1;
                }
                if (start > -1 && end > -1) {
                    final int startOffset = reg.getStartOffset() + start;
                    final String expr = before.substring(start) + after.substring(0, end + 1);
                    final int length = expr.length();
                    final String prop = before.substring(start + 2) + after.substring(0, end);
                    // there are often properties that start with project. eg. project.build.sourceEncoding          
                    //          if (prop.startsWith("project.") || prop.startsWith("pom.")) { //$NON-NLS-1$ //$NON-NLS-2$
                    //            return null; //ignore these, not in properties section.
                    //          }
                    MavenProject prj = XmlUtils.extractMavenProject(viewer);
                    if (prj != null) {
                        return new ExpressionRegion(startOffset, length, prop, prj);
                    }
                }
            }
        }
        return null;
    }

    public static IHyperlink createHyperlink(final ExpressionRegion region) {
        return new IHyperlink() {
            public IRegion getHyperlinkRegion() {
                return region;
            }

            public String getHyperlinkText() {
                return NLS.bind(Messages.PomHyperlinkDetector_open_property, region.property);
            }

            public String getTypeLabel() {
                return "pom-property-expression"; //$NON-NLS-1$
            }

            public void open() {
                //see if we can find the plugin in plugin management of resolved project.
                MavenProject mavprj = region.project;
                if (mavprj != null) {
                    //TODO get rid of InputLocation here and use the dom tree to find the property
                    Model mdl = mavprj.getModel();
                    InputLocation location = null;
                    if (mdl.getProperties().containsKey(region.property)) {
                        location = mdl.getLocation(PROPERTIES).getLocation(region.property);
                    } else if (region.property != null && region.property.startsWith("project.")) {//$NON-NLS-1$
                        if ("project.version".equals(region.property)) {
                            location = mdl.getLocation(VERSION);
                        } else if ("project.name".equals(region.property)) {
                            location = mdl.getLocation(NAME);
                        }
                    }
                    if (location != null) {
                        File file = XmlUtils.fileForInputLocation(location, mavprj);
                        if (file != null) {
                            IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI());
                            openXmlEditor(fileStore, location.getLineNumber(), location.getColumnNumber(),
                                    location.getSource().getModelId());
                        }
                    }
                }
            }
        };
    }

    public static boolean canCreateHyperLink(final ExpressionRegion region) {
        if ("project.version".equals(region.property) || "project.name".equals(region.property)) {
            return true;
        }
        return region.project != null && region.project.getModel().getProperties().containsKey(region.property);
    }

    //only create the hyperlink when the origin location for jumping is present.
    //in some cases (managed version comes from imported dependencies) we don't have the location and have nowhere to jump)
    public static boolean canCreateHyperLink(final ManagedArtifactRegion region) {
        return region.project != null
                && PomHyperlinkDetector.findLocationForManagedArtifact(region, region.project) != null;
    }

    static IHyperlink[] openExternalMarkerDefinition(ISourceViewer sourceViewer, int offset) {
        List<IHyperlink> toRet = new ArrayList<IHyperlink>();
        MarkerRegion[] regions = findMarkerRegions(sourceViewer, offset);
        for (MarkerRegion reg : regions) {
            if (reg.isDefinedInParent()) {
                toRet.add(createHyperlink(reg));
            }
        }
        return toRet.toArray(new IHyperlink[0]);
    }

    static MarkerRegion[] findMarkerRegions(ISourceViewer sourceViewer, int offset) {
        List<MarkerRegion> toRet = new ArrayList<MarkerRegion>();
        IAnnotationModel model = sourceViewer.getAnnotationModel();
        if (model != null) { //eg. in tests
            @SuppressWarnings("unchecked")
            Iterator<Annotation> it = model.getAnnotationIterator();
            while (it.hasNext()) {
                Annotation ann = it.next();
                if (ann instanceof MarkerAnnotation) {
                    Position pos = sourceViewer.getAnnotationModel().getPosition(ann);
                    if (pos.includes(offset)) {
                        toRet.add(new MarkerRegion(pos.getOffset(), pos.getLength(), (MarkerAnnotation) ann));
                    }
                }
            }
        }
        return toRet.toArray(new MarkerRegion[0]);
    }

    public static IHyperlink createHyperlink(final MarkerRegion mark) {
        return new IHyperlink() {

            public IRegion getHyperlinkRegion() {
                return new Region(mark.getOffset(), mark.getLength());
            }

            public String getTypeLabel() {
                return "marker-error-defined-in-parent"; //$NON-NLS-1$;
            }

            public String getHyperlinkText() {
                return NLS.bind("Open definition in parent for {0}", mark.getAnnotation().getText()); //TODO if there are multiple markers in one spot, how to differentiate better..
            }

            public void open() {
                IMarker marker = mark.getAnnotation().getMarker();
                String loc = marker.getAttribute(IMavenConstants.MARKER_CAUSE_RESOURCE_PATH, null);
                if (loc != null) {
                    IFileStore fileStore = EFS.getLocalFileSystem().getStore(new Path(loc));
                    int row = marker.getAttribute(IMavenConstants.MARKER_CAUSE_LINE_NUMBER, 0);
                    int column = marker.getAttribute(IMavenConstants.MARKER_CAUSE_COLUMN_START, 0);
                    String name = marker.getAttribute(IMavenConstants.MARKER_CAUSE_RESOURCE_ID, null);
                    //          String hint = marker.getAttribute(IMavenConstants.MARKER_ATTR_EDITOR_HINT, null);
                    //          if (IMavenConstants.EDITOR_HINT_NOT_COVERED_MOJO_EXECUTION.equals(hint)) {
                    //          }
                    openXmlEditor(fileStore, row, column, name);
                }
            }
        };
    }

    private IHyperlink openPropertyDefinition(Node current, ITextViewer viewer, int offset) {
        final ExpressionRegion region = findExpressionRegion(current, viewer, offset);
        if (region != null && canCreateHyperLink(region)) {
            return createHyperlink(region);
        }
        return null;
    }

    private IHyperlink openModule(Node current, ITextViewer textViewer, int offset) {
        while (current != null && !(current instanceof Element)) {
            current = current.getParentNode();
        }
        if (current == null) {
            return null;
        }
        String pathUp = XmlUtils.pathUp(current, 2);
        if (!"modules/module".equals(pathUp)) { //$NON-NLS-1$
            //just in case we are in some random plugin configuration snippet..
            return null;
        }

        ITextFileBuffer buf = FileBuffers.getTextFileBufferManager().getTextFileBuffer(textViewer.getDocument());
        if (buf == null) {
            //for repository based poms..
            return null;
        }
        IFileStore folder = buf.getFileStore().getParent();

        String path = XmlUtils.getTextValue(current);
        final String fPath = path;
        //construct IPath for the child pom file, handle relative paths..
        while (folder != null && path.startsWith("../")) { //$NON-NLS-1$
            folder = folder.getParent();
            path = path.substring("../".length());//$NON-NLS-1$
        }
        if (folder == null) {
            return null;
        }
        IFileStore modulePom = folder.getChild(path);
        if (!modulePom.getName().endsWith("xml")) { //$NON-NLS-1$
            modulePom = modulePom.getChild("pom.xml");//$NON-NLS-1$
        }
        final IFileStore fileStore = modulePom;
        if (!fileStore.fetchInfo().exists()) {
            return null;
        }
        assert current instanceof IndexedRegion;
        final IndexedRegion region = (IndexedRegion) current;

        return new IHyperlink() {
            public IRegion getHyperlinkRegion() {
                return new Region(region.getStartOffset(), region.getEndOffset() - region.getStartOffset());
            }

            public String getHyperlinkText() {
                return NLS.bind(Messages.PomHyperlinkDetector_open_module, fPath);
            }

            public String getTypeLabel() {
                return "pom-module"; //$NON-NLS-1$
            }

            public void open() {
                openXmlEditor(fileStore);
            }
        };
    }

    private IHyperlink openPOMbyID(Node current, final ITextViewer viewer, int offset) {
        while (current != null && !(current instanceof Element)) {
            current = current.getParentNode();
        }
        if (current == null) {
            return null;
        }
        current = current.getParentNode();
        if (current == null || !(current instanceof Element)) {
            return null;
        }
        Element parent = (Element) current;
        String parentName = parent.getNodeName();
        if (DEPENDENCY.equals(parentName) || PARENT.equals(parentName) || PLUGIN.equals(parentName)
                || "reportPlugin".equals(parentName) || EXTENSION.equals(parentName)) {
            final Node groupId = XmlUtils.findChild(parent, GROUP_ID);
            final Node artifactId = XmlUtils.findChild(parent, ARTIFACT_ID);
            final Node version = XmlUtils.findChild(parent, VERSION);
            final MavenProject prj = XmlUtils.extractMavenProject(viewer);

            IHyperlink pomHyperlink = new IHyperlink() {
                public IRegion getHyperlinkRegion() {
                    //the goal here is to have the groupid/artifactid/version combo underscored by the link.
                    //that will prevent underscoring big portions (like plugin config) underscored and
                    // will also handle cases like dependencies within plugins.
                    int max = groupId != null ? ((IndexedRegion) groupId).getEndOffset() : Integer.MIN_VALUE;
                    int min = groupId != null ? ((IndexedRegion) groupId).getStartOffset() : Integer.MAX_VALUE;
                    max = Math.max(max,
                            artifactId != null ? ((IndexedRegion) artifactId).getEndOffset() : Integer.MIN_VALUE);
                    min = Math.min(min,
                            artifactId != null ? ((IndexedRegion) artifactId).getStartOffset() : Integer.MAX_VALUE);
                    max = Math.max(max,
                            version != null ? ((IndexedRegion) version).getEndOffset() : Integer.MIN_VALUE);
                    min = Math.min(min,
                            version != null ? ((IndexedRegion) version).getStartOffset() : Integer.MAX_VALUE);
                    return new Region(min, max - min);
                }

                public String getHyperlinkText() {
                    return NLS.bind(Messages.PomHyperlinkDetector_hyperlink_pattern, XmlUtils.getTextValue(groupId),
                            XmlUtils.getTextValue(artifactId));
                }

                public String getTypeLabel() {
                    return "pom"; //$NON-NLS-1$
                }

                public void open() {
                    new Job(Messages.PomHyperlinkDetector_job_name) {
                        protected IStatus run(IProgressMonitor monitor) {
                            // TODO resolve groupId if groupId==null
                            String gridString = groupId == null ? "org.apache.maven.plugins" //$NON-NLS-1$
                                    : XmlUtils.getTextValue(groupId);
                            String artidString = artifactId == null ? null : XmlUtils.getTextValue(artifactId);
                            String versionString = version == null ? null : XmlUtils.getTextValue(version);
                            if (prj != null && gridString != null && artidString != null
                                    && (versionString == null || versionString.contains("${"))) { //$NON-NLS-1$
                                try {
                                    //TODO how do we decide here if the hyperlink is a dependency or a plugin
                                    // hyperlink??
                                    versionString = PomTemplateContext.extractVersion(prj, null, versionString,
                                            gridString, artidString,
                                            PomTemplateContext.EXTRACT_STRATEGY_DEPENDENCY);

                                } catch (CoreException e) {
                                    versionString = null;
                                }
                            }
                            if (versionString == null) {
                                return Status.OK_STATUS;
                            }
                            OpenPomAction.openEditor(gridString, artidString, versionString, monitor);
                            // TODO: it's preferable to open the xml page, but this code will blink and open overview first and later switch. looks bad            
                            //            Display.getDefault().syncExec(new Runnable() {
                            //              public void run() {
                            //                selectEditorPage(page);
                            //              }
                            //            });
                            return Status.OK_STATUS;
                        }
                    }.schedule();
                }

            };
            return pomHyperlink;
        }
        return null;
    }

    private void openXmlEditor(final IFileStore fileStore) {
        openXmlEditor(fileStore, -1, -1, fileStore.getName());
    }

    private static void openXmlEditor(final IFileStore fileStore, int line, int column, String name) {
        assert fileStore != null;
        IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
        if (window != null) {
            IWorkbenchPage page = window.getActivePage();
            if (page != null) {
                try {
                    if (!fileStore.getName().endsWith(".pom")) { //.pom means stuff from local repository?
                        IEditorPart part = IDE.openEditorOnFileStore(page, fileStore);
                        reveal(selectEditorPage(part), line, column);
                    } else {
                        //we need special EditorInput for stuff from repository
                        name = name + ".pom"; //$NON-NLS-1$
                        File file = new File(fileStore.toURI());
                        try {
                            IEditorInput input = new MavenPathStorageEditorInput(name, name, file.getAbsolutePath(),
                                    readStream(new FileInputStream(file)));
                            IEditorPart part = OpenPomAction.openEditor(input, name);
                            reveal(selectEditorPage(part), line, column);
                        } catch (IOException e) {
                            log.error("failed opening editor", e);
                        }
                    }
                } catch (PartInitException e) {
                    MessageDialog.openInformation(Display.getDefault().getActiveShell(), //
                            Messages.PomHyperlinkDetector_error_title,
                            NLS.bind(Messages.PomHyperlinkDetector_error_message, fileStore, e.toString()));

                }
            }
        }
    }

    private static StructuredTextEditor selectEditorPage(IEditorPart part) {
        if (part == null) {
            return null;
        }
        if (part instanceof FormEditor) {
            FormEditor ed = (FormEditor) part;
            ed.setActivePage(null); //null means source, always or just in the case of MavenPomEditor?
            if (ed.getActiveEditor() instanceof StructuredTextEditor) {
                return (StructuredTextEditor) ed.getActiveEditor();
            }
        }
        return null;
    }

    private static void reveal(StructuredTextEditor structured, int line, int column) {
        if (structured == null || line < 0 || column < 0) {
            return;
        }
        IDocument doc = structured.getTextViewer().getDocument();
        if (doc instanceof IStructuredDocument) {
            IStructuredDocument document = (IStructuredDocument) doc;
            try {
                int offset = document.getLineOffset(line - 1);
                structured.selectAndReveal(offset + column - 1, 0);
            } catch (BadLocationException e) {
                log.error("failed selecting part of editor", e);
            }
        }
    }

    /**
     * duplicate of OpenPomAction method
     * 
     * @param is
     * @return
     * @throws IOException
     */
    private static byte[] readStream(InputStream is) throws IOException {
        byte[] b = new byte[is.available()];
        int len = 0;
        while (true) {
            int n = is.read(b, len, b.length - len);
            if (n == -1) {
                if (len < b.length) {
                    byte[] c = new byte[len];
                    System.arraycopy(b, 0, c, 0, len);
                    b = c;
                }
                return b;
            }
            len += n;
            if (len == b.length) {
                byte[] c = new byte[b.length + 1000];
                System.arraycopy(b, 0, c, 0, len);
                b = c;
            }
        }
    }

    public static class ExpressionRegion implements IRegion {

        final String property;

        private int length;

        private int offset;

        final MavenProject project;

        public ExpressionRegion(int startOffset, int length, String prop, MavenProject project) {
            this.offset = startOffset;
            this.length = length;
            this.property = prop;
            this.project = project;
            assert project != null;
        }

        public int getLength() {
            return length;
        }

        public int getOffset() {
            return offset;
        }
    }

    public static class ManagedArtifactRegion implements IRegion {

        private int length;

        private int offset;

        final MavenProject project;

        final String groupId;

        final String artifactId;

        final boolean isPlugin;

        final boolean isDependency;

        public ManagedArtifactRegion(int startOffset, int length, String groupId, String artifactId,
                boolean isDependency, boolean isPlugin, MavenProject project) {
            this.offset = startOffset;
            this.length = length;
            this.project = project;
            assert project != null;
            this.artifactId = artifactId;
            this.groupId = groupId;
            this.isDependency = isDependency;
            this.isPlugin = isPlugin;
        }

        public int getLength() {
            return length;
        }

        public int getOffset() {
            return offset;
        }
    }

    public static class MarkerRegion implements IRegion {

        private final MarkerAnnotation ann;

        final int offset;

        final int length;

        public MarkerRegion(int offset, int length, MarkerAnnotation applicable) {
            this.offset = offset;
            this.length = length;
            this.ann = applicable;
        }

        public int getLength() {
            return length;
        }

        public int getOffset() {
            return offset;
        }

        public MarkerAnnotation getAnnotation() {
            return ann;
        }

        public boolean isDefinedInParent() {
            IMarker mark = ann.getMarker();
            String isElsewhere = mark.getAttribute(IMavenConstants.MARKER_CAUSE_RESOURCE_PATH, null);
            if (isElsewhere != null) {
                return true;
            }
            return false;
        }

    }

}