Java tutorial
/******************************************************************************* * Copyright (c) 2011, 2012 GitHub Inc. 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: * Kevin Sawicki (GitHub Inc.) - initial API and implementation *******************************************************************************/ package org.eclipse.egit.ui.internal.commit; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.egit.ui.Activator; import org.eclipse.egit.ui.UIUtils; import org.eclipse.egit.ui.internal.GitLabelProvider; import org.eclipse.egit.ui.internal.UIIcons; import org.eclipse.egit.ui.internal.UIText; import org.eclipse.egit.ui.internal.dialogs.SpellcheckableMessageArea; import org.eclipse.egit.ui.internal.history.CommitFileDiffViewer; import org.eclipse.egit.ui.internal.history.FileDiff; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.viewers.ArrayContentProvider; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.revwalk.RevWalkUtils; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CLabel; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.PartInitException; import org.eclipse.ui.forms.IFormColors; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.forms.editor.FormPage; import org.eclipse.ui.forms.events.ExpansionAdapter; import org.eclipse.ui.forms.events.ExpansionEvent; import org.eclipse.ui.forms.events.HyperlinkAdapter; import org.eclipse.ui.forms.events.HyperlinkEvent; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.Hyperlink; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.ui.part.ShowInContext; /** * Commit editor page class displaying author, committer, parent commits, * message, and file information in form sections. */ public class CommitEditorPage extends FormPage implements ISchedulingRule { private static final String SIGNED_OFF_BY = "Signed-off-by: {0} <{1}>"; //$NON-NLS-1$ /** * Abbreviated length of parent id links displayed */ public static final int PARENT_LENGTH = 20; private LocalResourceManager resources = new LocalResourceManager(JFaceResources.getResources()); private Composite tagLabelArea; private Section branchSection; private TableViewer branchViewer; private Section diffSection; private CommitFileDiffViewer diffViewer; /** * Create commit editor page * * @param editor */ public CommitEditorPage(FormEditor editor) { this(editor, "commitPage", UIText.CommitEditorPage_Title); //$NON-NLS-1$ } /** * Create commit editor page * * @param editor * @param id * @param title */ public CommitEditorPage(FormEditor editor, String id, String title) { super(editor, id, title); } private void hookExpansionGrabbing(final Section section) { section.addExpansionListener(new ExpansionAdapter() { public void expansionStateChanged(ExpansionEvent e) { ((GridData) section.getLayoutData()).grabExcessVerticalSpace = e.getState(); getManagedForm().getForm().getBody().layout(true, true); } }); } private Image getImage(ImageDescriptor descriptor) { return (Image) this.resources.get(descriptor); } private Section createSection(Composite parent, FormToolkit toolkit, int span) { Section section = toolkit.createSection(parent, ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED); GridDataFactory.fillDefaults().span(span, 1).grab(true, true).applyTo(section); return section; } private Composite createSectionClient(Section parent, FormToolkit toolkit) { Composite client = toolkit.createComposite(parent); GridLayoutFactory.fillDefaults().extendedMargins(2, 2, 2, 2).applyTo(client); return client; } private boolean isSignedOffBy(PersonIdent person) { RevCommit commit = getCommit().getRevCommit(); return commit.getFullMessage().indexOf(getSignedOffByLine(person)) != -1; } private String getSignedOffByLine(PersonIdent person) { return MessageFormat.format(SIGNED_OFF_BY, person.getName(), person.getEmailAddress()); } private String replaceSignedOffByLine(String message, PersonIdent person) { Pattern pattern = Pattern.compile("^\\s*" + Pattern.quote(getSignedOffByLine(person)) //$NON-NLS-1$ + "\\s*$", Pattern.MULTILINE); //$NON-NLS-1$ return pattern.matcher(message).replaceAll(""); //$NON-NLS-1$ } private Composite createUserArea(Composite parent, FormToolkit toolkit, PersonIdent person, boolean author) { Composite userArea = toolkit.createComposite(parent); GridLayoutFactory.fillDefaults().spacing(2, 2).numColumns(3).applyTo(userArea); Label userLabel = toolkit.createLabel(userArea, null); userLabel.setImage(getImage(author ? UIIcons.ELCL16_AUTHOR : UIIcons.ELCL16_COMMITTER)); if (author) userLabel.setToolTipText(UIText.CommitEditorPage_TooltipAuthor); else userLabel.setToolTipText(UIText.CommitEditorPage_TooltipCommitter); boolean signedOff = isSignedOffBy(person); Text userText = new Text(userArea, SWT.FLAT | SWT.READ_ONLY); userText.setText(MessageFormat.format( author ? UIText.CommitEditorPage_LabelAuthor : UIText.CommitEditorPage_LabelCommitter, person.getName(), person.getEmailAddress(), person.getWhen())); toolkit.adapt(userText, false, false); userText.setData(FormToolkit.KEY_DRAW_BORDER, Boolean.FALSE); GridDataFactory.fillDefaults().span(signedOff ? 1 : 2, 1).applyTo(userText); if (signedOff) { Label signedOffLabel = toolkit.createLabel(userArea, null); signedOffLabel.setImage(getImage(UIIcons.SIGNED_OFF)); if (author) signedOffLabel.setToolTipText(UIText.CommitEditorPage_TooltipSignedOffByAuthor); else signedOffLabel.setToolTipText(UIText.CommitEditorPage_TooltipSignedOffByCommitter); } return userArea; } private void updateSectionClient(Section section, Composite client, FormToolkit toolkit) { hookExpansionGrabbing(section); toolkit.paintBordersFor(client); section.setClient(client); } private void createHeaderArea(Composite parent, FormToolkit toolkit, int span) { RevCommit commit = getCommit().getRevCommit(); Composite top = toolkit.createComposite(parent); GridDataFactory.fillDefaults().grab(true, false).span(span, 1).applyTo(top); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(top); Composite userArea = toolkit.createComposite(top); GridLayoutFactory.fillDefaults().spacing(2, 2).numColumns(1).applyTo(userArea); GridDataFactory.fillDefaults().grab(true, false).applyTo(userArea); PersonIdent author = commit.getAuthorIdent(); if (author != null) createUserArea(userArea, toolkit, author, true); PersonIdent committer = commit.getCommitterIdent(); if (committer != null && !committer.equals(author)) createUserArea(userArea, toolkit, committer, false); int count = commit.getParentCount(); if (count > 0) { Composite parents = toolkit.createComposite(top); GridLayoutFactory.fillDefaults().spacing(2, 2).numColumns(2).applyTo(parents); GridDataFactory.fillDefaults().grab(false, false).applyTo(parents); for (int i = 0; i < count; i++) { final RevCommit parentCommit = commit.getParent(i); toolkit.createLabel(parents, UIText.CommitEditorPage_LabelParent) .setForeground(toolkit.getColors().getColor(IFormColors.TB_TOGGLE)); final Hyperlink link = toolkit.createHyperlink(parents, parentCommit.abbreviate(PARENT_LENGTH).name(), SWT.NONE); link.addHyperlinkListener(new HyperlinkAdapter() { public void linkActivated(HyperlinkEvent e) { try { CommitEditor.open(new RepositoryCommit(getCommit().getRepository(), parentCommit)); if ((e.getStateMask() & SWT.MOD1) != 0) getEditor().close(false); } catch (PartInitException e1) { Activator.logError("Error opening commit editor", e1);//$NON-NLS-1$ } } }); } } createTagsArea(userArea, toolkit, 2); } private List<Ref> getTags() { Repository repository = getCommit().getRepository(); List<Ref> tags = new ArrayList<Ref>(repository.getTags().values()); Collections.sort(tags, new Comparator<Ref>() { public int compare(Ref r1, Ref r2) { return Repository.shortenRefName(r1.getName()) .compareToIgnoreCase(Repository.shortenRefName(r2.getName())); } }); return tags; } private void createTagsArea(Composite parent, FormToolkit toolkit, int span) { Composite tagArea = toolkit.createComposite(parent); GridLayoutFactory.fillDefaults().numColumns(2).equalWidth(false).applyTo(tagArea); GridDataFactory.fillDefaults().span(span, 1).grab(true, false).applyTo(tagArea); toolkit.createLabel(tagArea, UIText.CommitEditorPage_LabelTags) .setForeground(toolkit.getColors().getColor(IFormColors.TB_TOGGLE)); tagLabelArea = toolkit.createComposite(tagArea); GridDataFactory.fillDefaults().grab(true, true).applyTo(tagLabelArea); GridLayoutFactory.fillDefaults().spacing(1, 1).applyTo(tagLabelArea); } private void fillDiffs(FileDiff[] diffs) { diffViewer.setInput(diffs); diffSection .setText(MessageFormat.format(UIText.CommitEditorPage_SectionFiles, Integer.valueOf(diffs.length))); } private void fillTags(FormToolkit toolkit, List<Ref> tags) { for (Control child : tagLabelArea.getChildren()) child.dispose(); GridLayoutFactory.fillDefaults().spacing(1, 1).numColumns(tags.size()).applyTo(tagLabelArea); for (Ref tag : tags) { ObjectId id = tag.getPeeledObjectId(); boolean annotated = id != null; if (id == null) id = tag.getObjectId(); CLabel tagLabel = new CLabel(tagLabelArea, SWT.NONE); toolkit.adapt(tagLabel, false, false); if (annotated) tagLabel.setImage(getImage(UIIcons.TAG_ANNOTATED)); else tagLabel.setImage(getImage(UIIcons.TAG)); tagLabel.setText(Repository.shortenRefName(tag.getName())); } } private void createMessageArea(Composite parent, FormToolkit toolkit, int span) { Section messageSection = createSection(parent, toolkit, span); Composite messageArea = createSectionClient(messageSection, toolkit); messageSection.setText(UIText.CommitEditorPage_SectionMessage); RevCommit commit = getCommit().getRevCommit(); String message = commit.getFullMessage(); PersonIdent author = commit.getAuthorIdent(); if (author != null) message = replaceSignedOffByLine(message, author); PersonIdent committer = commit.getCommitterIdent(); if (committer != null) message = replaceSignedOffByLine(message, committer); SpellcheckableMessageArea textContent = new SpellcheckableMessageArea(messageArea, message, true, toolkit.getBorderStyle()) { @Override protected IAdaptable getDefaultTarget() { return new PlatformObject() { public Object getAdapter(Class adapter) { return Platform.getAdapterManager().getAdapter(getEditorInput(), adapter); } }; } protected void createMarginPainter() { // Disabled intentionally } }; if ((toolkit.getBorderStyle() & SWT.BORDER) == 0) textContent.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 80).grab(true, true).applyTo(textContent); updateSectionClient(messageSection, messageArea, toolkit); } private void createBranchesArea(Composite parent, FormToolkit toolkit, int span) { branchSection = createSection(parent, toolkit, span); branchSection.setText(UIText.CommitEditorPage_SectionBranchesEmpty); Composite branchesArea = createSectionClient(branchSection, toolkit); branchViewer = new TableViewer(toolkit.createTable(branchesArea, SWT.V_SCROLL | SWT.H_SCROLL)); GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 50).applyTo(branchViewer.getControl()); branchViewer.setSorter(new ViewerSorter()); branchViewer.setLabelProvider(new GitLabelProvider() { public String getText(Object element) { return Repository.shortenRefName(super.getText(element)); } }); branchViewer.setContentProvider(ArrayContentProvider.getInstance()); branchViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); updateSectionClient(branchSection, branchesArea, toolkit); } private void fillBranches(List<Ref> result) { branchViewer.setInput(result); branchSection.setText( MessageFormat.format(UIText.CommitEditorPage_SectionBranches, Integer.valueOf(result.size()))); } private void createFilesArea(Composite parent, FormToolkit toolkit, int span) { diffSection = createSection(parent, toolkit, span); diffSection.setText(UIText.CommitEditorPage_SectionFilesEmpty); Composite filesArea = createSectionClient(diffSection, toolkit); diffViewer = new CommitFileDiffViewer(filesArea, getSite(), SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | toolkit.getBorderStyle()); diffViewer.getTable().setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TREE_BORDER); GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 80).applyTo(diffViewer.getControl()); diffViewer.setContentProvider(ArrayContentProvider.getInstance()); diffViewer.setTreeWalk(getCommit().getRepository(), null); updateSectionClient(diffSection, filesArea, toolkit); } private RepositoryCommit getCommit() { return (RepositoryCommit) getEditor().getAdapter(RepositoryCommit.class); } /** * @see org.eclipse.ui.forms.editor.FormPage#createFormContent(org.eclipse.ui.forms.IManagedForm) */ protected void createFormContent(IManagedForm managedForm) { Composite body = managedForm.getForm().getBody(); body.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { resources.dispose(); } }); FillLayout bodyLayout = new FillLayout(); bodyLayout.marginHeight = 5; bodyLayout.marginWidth = 5; body.setLayout(bodyLayout); FormToolkit toolkit = managedForm.getToolkit(); Composite displayArea = toolkit.createComposite(body); GridLayoutFactory.fillDefaults().numColumns(2).applyTo(displayArea); createHeaderArea(displayArea, toolkit, 2); createMessageArea(displayArea, toolkit, 2); createFilesArea(displayArea, toolkit, 1); createBranchesArea(displayArea, toolkit, 1); loadSections(); } private List<Ref> loadTags() { RepositoryCommit repoCommit = getCommit(); RevCommit commit = repoCommit.getRevCommit(); Repository repository = repoCommit.getRepository(); List<Ref> tags = new ArrayList<Ref>(); for (Ref tag : getTags()) { tag = repository.peel(tag); ObjectId id = tag.getPeeledObjectId(); if (id == null) id = tag.getObjectId(); if (!commit.equals(id)) continue; tags.add(tag); } return tags; } private List<Ref> loadBranches() { Repository repository = getCommit().getRepository(); RevCommit commit = getCommit().getRevCommit(); RevWalk revWalk = new RevWalk(repository); try { Map<String, Ref> refsMap = new HashMap<String, Ref>(); refsMap.putAll(repository.getRefDatabase().getRefs(Constants.R_HEADS)); refsMap.putAll(repository.getRefDatabase().getRefs(Constants.R_REMOTES)); return RevWalkUtils.findBranchesReachableFrom(commit, revWalk, refsMap.values()); } catch (IOException e) { Activator.handleError(e.getMessage(), e, false); return Collections.emptyList(); } } private void loadSections() { RepositoryCommit commit = getCommit(); Job refreshJob = new Job( MessageFormat.format(UIText.CommitEditorPage_JobName, commit.getRevCommit().name())) { @Override protected IStatus run(IProgressMonitor monitor) { final List<Ref> tags = loadTags(); final List<Ref> branches = loadBranches(); final FileDiff[] diffs = getCommit().getDiffs(); final ScrolledForm form = getManagedForm().getForm(); if (UIUtils.isUsable(form)) form.getDisplay().syncExec(new Runnable() { public void run() { if (!UIUtils.isUsable(form)) return; fillTags(getManagedForm().getToolkit(), tags); fillDiffs(diffs); fillBranches(branches); form.layout(true, true); } }); return Status.OK_STATUS; } }; refreshJob.setRule(this); refreshJob.schedule(); } /** * Refresh the editor page */ public void refresh() { loadSections(); } public boolean contains(ISchedulingRule rule) { return rule == this; } public boolean isConflicting(ISchedulingRule rule) { return rule == this; } ShowInContext getShowInContext() { if (diffViewer != null && diffViewer.getControl().isFocusControl()) return diffViewer.getShowInContext(); else return null; } }