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;
 }
  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;
  }