Package org.eclipse.egit.internal.mylyn.ui

Source Code of org.eclipse.egit.internal.mylyn.ui.CommitHyperlinkDetector$CommitHyperlink

/*******************************************************************************
* Copyright (c) 2011 Benjamin Muskalla 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:
*     Benjamin Muskalla <benjamin.muskalla@tasktop.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.egit.internal.mylyn.ui;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.runtime.Assert;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.RepositoryCache;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.ui.internal.commit.CommitEditor;
import org.eclipse.egit.ui.internal.commit.RepositoryCommit;
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.Region;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Shell;

/**
* Detects Git commit ids in task descriptions and allows users to open them in
* the commit editor.
*/
public class CommitHyperlinkDetector extends AbstractHyperlinkDetector {

  private static final Pattern PATTERN_COMMIT_ID = Pattern
      .compile("(?<!\\w)([0-9a-f]{8}([0-9a-f]{32})?)"); //$NON-NLS-1$

  private static class CommitHyperlink implements IHyperlink {

    private IRegion region;

    private String objectId;

    private final Shell shell;

    public CommitHyperlink(IRegion region, String objectId, Shell shell) {
      this.shell = shell;
      Assert.isNotNull(objectId);
      Assert.isNotNull(region);
      Assert.isNotNull(shell);

      this.region = region;
      this.objectId = objectId;
    }

    public IRegion getHyperlinkRegion() {
      return region;
    }

    public String getTypeLabel() {
      return null;
    }

    public String getHyperlinkText() {
      return objectId;
    }

    public void open() {
      try {
        RepositoryCommit commit;
        commit = searchCommit();
        if (commit != null)
          CommitEditor.openQuiet(commit);
        else
          informCommitNotFound();
      } catch (IOException e) {
        // ignore
      }
    }

    private void informCommitNotFound() {
      MessageDialog
          .openWarning(
              shell,
              Messages.CommitHyperlinkDetector_CommitNotFound,
              NLS.bind(
                  Messages.CommitHyperlinkDetector_CommitNotFoundInRepositories,
                  objectId));
    }

    private RepositoryCommit searchCommit() throws IOException {
      RepositoryUtil repositoryUtil = Activator.getDefault()
          .getRepositoryUtil();
      List<String> configuredRepositories = repositoryUtil
          .getConfiguredRepositories();
      RepositoryCache repositoryCache = Activator.getDefault()
          .getRepositoryCache();
      for (String repoDir : configuredRepositories) {
        Repository repository = repositoryCache
            .lookupRepository(new File(repoDir));
        RevCommit commit = getCommit(repository);
        if (commit != null)
          return new RepositoryCommit(repository, commit);
      }

      return null;
    }

    private RevCommit getCommit(Repository repository) throws IOException {
      RevWalk revWalk = null;
      try {
        revWalk = new RevWalk(repository);
        return revWalk.parseCommit(ObjectId.fromString(objectId));
      } catch (MissingObjectException e) {
        // ignore
        return null;
      } catch (IncorrectObjectTypeException e) {
        // ignore
        return null;
      } finally {
        if (revWalk != null)
          revWalk.release();
      }

    }

    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder();
      builder.append("CommitHyperlink [region="); //$NON-NLS-1$
      builder.append(region);
      builder.append(", objectId="); //$NON-NLS-1$
      builder.append(objectId);
      builder.append("]"); //$NON-NLS-1$
      return builder.toString();
    }

  }

  /**
   * Detects and returns all available hyperlinks for the given
   * {@link TextViewer} which link to a Git commit.
   */
  public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
      final IRegion region, boolean canShowMultipleHyperlinks) {
    IDocument document = textViewer.getDocument();
    if (document == null || document.getLength() == 0) {
      return null;
    }

    String content;
    int contentOffset;
    int index;
    try {
      if (region.getLength() == 0) {
        // expand the region to include the whole line
        IRegion lineInfo = document.getLineInformationOfOffset(region
            .getOffset());
        int lineLength = lineInfo.getLength();
        int lineOffset = lineInfo.getOffset();
        int lineEnd = lineOffset + lineLength;
        int regionEnd = region.getOffset() + region.getLength();
        if (lineOffset < region.getOffset()) {
          int regionLength = Math.max(regionEnd, lineEnd)
              - lineOffset;
          contentOffset = lineOffset;
          content = document.get(lineOffset, regionLength);
          index = region.getOffset() - lineOffset;
        } else {
          // the line starts after region, may never happen
          int regionLength = Math.max(regionEnd, lineEnd)
              - region.getOffset();
          contentOffset = region.getOffset();
          content = document.get(contentOffset, regionLength);
          index = 0;
        }
      } else {
        content = document.get(region.getOffset(), region.getLength());
        contentOffset = region.getOffset();
        index = -1;
      }
    } catch (BadLocationException ex) {
      return null;
    }

    List<IHyperlink> hyperlinks = detectHyperlinks(textViewer, content,
        index, contentOffset);
    if (hyperlinks == null) {
      return null;
    }

    // filter hyperlinks that do not match original region
    if (region.getLength() == 0) {
      for (Iterator<IHyperlink> it = hyperlinks.iterator(); it.hasNext();) {
        IHyperlink hyperlink = it.next();
        IRegion hyperlinkRegion = hyperlink.getHyperlinkRegion();
        if (!isInRegion(region, hyperlinkRegion)) {
          it.remove();
        }
      }
    }
    if (hyperlinks.isEmpty()) {
      return null;
    }
    return hyperlinks.toArray(new IHyperlink[hyperlinks.size()]);
  }

  private List<IHyperlink> detectHyperlinks(ITextViewer textViewer,
      String content, int index, int contentOffset) {
    Shell shell = textViewer.getTextWidget().getShell();
    List<IHyperlink> links = null;
    Matcher matcher = PATTERN_COMMIT_ID.matcher(content);
    while (matcher.find()) {
      if (index != -1
          && (index < matcher.start() || index > matcher.end())) {
        continue;
      }
      if (links == null) {
        links = new ArrayList<IHyperlink>();
      }
      int start = matcher.start(1);
      Region region = new Region(contentOffset + start, matcher.end(1)
          - start);

      CommitHyperlink hyperlink = new CommitHyperlink(region,
          matcher.group(1), shell);
      links.add(hyperlink);
    }
    return links;
  }

  private boolean isInRegion(IRegion detectInRegion, IRegion hyperlinkRegion) {
    return detectInRegion.getOffset() >= hyperlinkRegion.getOffset()
        && detectInRegion.getOffset() <= hyperlinkRegion.getOffset()
            + hyperlinkRegion.getLength();
  }

}
TOP

Related Classes of org.eclipse.egit.internal.mylyn.ui.CommitHyperlinkDetector$CommitHyperlink

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.