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);
      }
    };
  }
 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 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;
  }
  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"
                          : XmlUtils.getTextValue(groupId); // $NON-NLS-1$
                  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;
  }
  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;
  }