From 2c56cb95293d0eae3f83cfdaaa44d89b5a5ada4f Mon Sep 17 00:00:00 2001 From: spingel Date: Wed, 26 May 2010 03:38:11 +0000 Subject: REOPENED - bug 304910: [api] recognize comment#number link on bug editor without bug number too https://bugs.eclipse.org/bugs/show_bug.cgi?id=304910 --- .../mylyn/bugzilla/tests/AllBugzillaTests.java | 2 + .../tests/ui/BugzillaHyperlinkDetectorTest.java | 153 +++++++++++++++++++++ .../ui/BugzillaTaskHyperlinkDetectorTest.java | 13 +- .../bugzilla/ui/TaskAttachmentHyperlink.java | 53 +++++++ .../bugzilla/ui/tasklist/BugzillaConnectorUi.java | 118 ++++------------ .../org/eclipse/mylyn/tasks/ui/TaskHyperlink.java | 62 ++++++++- 6 files changed, 308 insertions(+), 93 deletions(-) create mode 100644 org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaHyperlinkDetectorTest.java diff --git a/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/AllBugzillaTests.java b/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/AllBugzillaTests.java index f51fe9fc7..68b10d10e 100644 --- a/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/AllBugzillaTests.java +++ b/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/AllBugzillaTests.java @@ -17,6 +17,7 @@ import junit.framework.TestSuite; import org.eclipse.mylyn.bugzilla.tests.core.BugzillaPriorityTest; import org.eclipse.mylyn.bugzilla.tests.core.BugzillaXMLRPCTest; import org.eclipse.mylyn.bugzilla.tests.support.BugzillaFixture; +import org.eclipse.mylyn.bugzilla.tests.ui.BugzillaHyperlinkDetectorTest; import org.eclipse.mylyn.bugzilla.tests.ui.BugzillaRepositorySettingsPageTest; import org.eclipse.mylyn.bugzilla.tests.ui.BugzillaSearchPageTest; import org.eclipse.mylyn.bugzilla.tests.ui.BugzillaTaskHyperlinkDetectorTest; @@ -40,6 +41,7 @@ public class AllBugzillaTests { suite.addTestSuite(BugzillaSearchPageTest.class); suite.addTestSuite(BugzillaDateTimeTests.class); suite.addTestSuite(BugzillaTaskHyperlinkDetectorTest.class); + suite.addTestSuite(BugzillaHyperlinkDetectorTest.class); // Each of these tests gets executed against every repo in BugzillaFixture.ALL // unless otherwise excluded diff --git a/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaHyperlinkDetectorTest.java b/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaHyperlinkDetectorTest.java new file mode 100644 index 000000000..809ccc289 --- /dev/null +++ b/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaHyperlinkDetectorTest.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies 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: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.bugzilla.tests.ui; + +import junit.framework.TestCase; + +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.mylyn.internal.bugzilla.core.BugzillaCorePlugin; +import org.eclipse.mylyn.internal.bugzilla.ui.TaskAttachmentHyperlink; +import org.eclipse.mylyn.internal.bugzilla.ui.tasklist.BugzillaConnectorUi; +import org.eclipse.mylyn.internal.tasks.core.TaskTask; +import org.eclipse.mylyn.tasks.core.TaskRepository; +import org.eclipse.mylyn.tasks.core.data.TaskAttribute; +import org.eclipse.mylyn.tasks.ui.TaskHyperlink; + +/** + * @author Steffen Pingel + */ +public class BugzillaHyperlinkDetectorTest extends TestCase { + + private BugzillaConnectorUi connector; + + private TaskRepository repository; + + private TaskTask task; + + private TaskAttachmentHyperlink alink(int offset, int length, String attachmentId) { + TaskAttachmentHyperlink link = new TaskAttachmentHyperlink(new Region(offset, length), repository, attachmentId); + return link; + } + + private void assertHyperlinks(String string, IHyperlink... expected) { + IHyperlink[] links = connector.findHyperlinks(repository, task, string, -1, 0); + if (expected.length == 0) { + assertNull(links); + return; + } + assertNotNull("Expected hyperlinks in " + string, links); + assertEquals(expected.length, links.length); + for (int i = 0; i < links.length; i++) { + assertEquals(expected[i], links[i]); + } + } + + private TaskHyperlink link(int offset, int length, String taskId) { + return link(offset, length, taskId, null); + } + + private TaskHyperlink link(int offset, int length, String taskId, String commentId) { + TaskHyperlink link = new TaskHyperlink(new Region(offset, length), repository, taskId); + if (commentId != null) { + link.setSelection(TaskAttribute.PREFIX_COMMENT + commentId); + } + return link; + } + + @Override + protected void setUp() throws Exception { + repository = new TaskRepository(BugzillaCorePlugin.CONNECTOR_KIND, "http://localhost"); + task = new TaskTask(BugzillaCorePlugin.CONNECTOR_KIND, "http://localhost", "123"); + connector = new BugzillaConnectorUi(); + } + + public void testFindHyperlinksAttachment() { + assertHyperlinks("attachment 123", alink(0, 14, "123")); + assertHyperlinks("attachment 123", alink(0, 15, "123")); + assertHyperlinks("attachment # 123", alink(0, 17, "123")); + assertHyperlinks("attachment#1", alink(0, 12, "1")); + assertHyperlinks("attachment (id=123)", alink(0, 19, "123")); + assertHyperlinks("Created attachment 123", alink(0, 22, "123")); + assertHyperlinks("Created an attachment 123", alink(0, 25, "123")); + assertHyperlinks("Created an attachment (id=123)", alink(0, 30, "123")); + } + + public void testFindHyperlinksBug() { + assertHyperlinks("bug123", link(0, 6, "123")); + assertHyperlinks("bug 123", link(0, 7, "123")); + assertHyperlinks("bug 123", link(0, 8, "123")); + assertHyperlinks("bug#123", link(0, 7, "123")); + assertHyperlinks("bug # 123", link(0, 11, "123")); + } + + public void testFindHyperlinksTask() { + assertHyperlinks("task123", link(0, 7, "123")); + } + + public void testFindHyperlinksDuplicateOf() { + assertHyperlinks("duplicate of 123", link(0, 15, "123")); + } + + public void testFindHyperlinksBugComment() { + assertHyperlinks("bug 123 comment 12", link(0, 18, "123", "12")); + assertHyperlinks("bug#123 comment 12", link(0, 18, "123", "12")); + assertHyperlinks("bug 123 comment#12", link(0, 18, "123", "12")); + assertHyperlinks("bug#123 comment#12", link(0, 18, "123", "12")); + assertHyperlinks("bug 123 comment# 12", link(0, 22, "123", "12")); + assertHyperlinks("bug456comment#1", link(0, 15, "456", "1")); + } + + public void testFindHyperlinksBugNoComment() { + assertHyperlinks("bug 123#c1", link(0, 7, "123")); + assertHyperlinks("bug 123#1", link(0, 7, "123")); + assertHyperlinks("bug#123#c1", link(0, 7, "123")); + assertHyperlinks("bug#123#1", link(0, 7, "123")); + } + + public void testFindHyperlinksComment() { + assertHyperlinks("comment#12", link(0, 10, "123", "12")); + assertHyperlinks("comment #12", link(0, 12, "123", "12")); + assertHyperlinks("comment 1", link(0, 9, "123", "1")); + } + + public void testFindHyperlinksInline() { + assertHyperlinks("abc bug 123 def", link(4, 7, "123")); + } + + public void testFindHyperlinksMultiple() { + assertHyperlinks("bug 456#comment#12", link(0, 7, "456"), link(8, 10, "123", "12")); + assertHyperlinks("bug 123 bug 456", link(0, 7, "123"), link(20, 7, "456")); + } + + public void testFindHyperlinksNoAttachment() { + assertHyperlinks("attachment"); + assertHyperlinks("attachmen 123"); + assertHyperlinks("attachment id"); + assertHyperlinks("attachment id"); + assertHyperlinks("Create attachment 123"); + } + + public void testFindHyperlinksNoBug() { + assertHyperlinks("bu 123"); + assertHyperlinks("bu# 123"); + assertHyperlinks("bug"); + assertHyperlinks("bugcomment"); + assertHyperlinks("bug#comment"); + } + + public void testFindHyperlinksNoComment() { + assertHyperlinks("c 12"); + assertHyperlinks("#c12"); + assertHyperlinks("comment"); + } +} diff --git a/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaTaskHyperlinkDetectorTest.java b/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaTaskHyperlinkDetectorTest.java index f28a29c62..86f8ce0fd 100644 --- a/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaTaskHyperlinkDetectorTest.java +++ b/org.eclipse.mylyn.bugzilla.tests/src/org/eclipse/mylyn/bugzilla/tests/ui/BugzillaTaskHyperlinkDetectorTest.java @@ -339,7 +339,7 @@ public class BugzillaTaskHyperlinkDetectorTest extends TestCase { assertEquals("123", taskLink.getTaskId()); assertEquals(testString.indexOf(format), taskLink.getHyperlinkRegion().getOffset()); Object comment = taskLink.getSelection(); - assertNotNull(comment); + assertNotNull(format, comment); assertEquals(TaskAttribute.PREFIX_COMMENT + "44556677", comment); } for (String format : commentFormats) { @@ -657,4 +657,15 @@ public class BugzillaTaskHyperlinkDetectorTest extends TestCase { assertEquals(1, links.length); assertEquals(testString.indexOf(ATTACHMENT_NUMBER), links[0].getHyperlinkRegion().getOffset()); } + + public void testCommentLotsOfWhitespace() { + String testString = "bug 123 d bug 245 comment 1"; + viewer.setDocument(new Document(testString)); + Region region = new Region(0, testString.length()); + IHyperlink[] links = detector.detectHyperlinks(viewer, region, false); + assertNotNull(links); + assertEquals(1, links.length); + assertEquals(testString.indexOf(ATTACHMENT_NUMBER), links[0].getHyperlinkRegion().getOffset()); + } + } diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/TaskAttachmentHyperlink.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/TaskAttachmentHyperlink.java index e29c57715..b91c90678 100644 --- a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/TaskAttachmentHyperlink.java +++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/TaskAttachmentHyperlink.java @@ -55,4 +55,57 @@ public final class TaskAttachmentHyperlink implements IHyperlink { String url = repository.getUrl() + IBugzillaConstants.URL_GET_ATTACHMENT_SUFFIX + attachmentId; TasksUiUtil.openUrl(url); } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((attachmentId == null) ? 0 : attachmentId.hashCode()); + result = prime * result + ((region == null) ? 0 : region.hashCode()); + result = prime * result + ((repository == null) ? 0 : repository.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TaskAttachmentHyperlink other = (TaskAttachmentHyperlink) obj; + if (attachmentId == null) { + if (other.attachmentId != null) { + return false; + } + } else if (!attachmentId.equals(other.attachmentId)) { + return false; + } + if (region == null) { + if (other.region != null) { + return false; + } + } else if (!region.equals(other.region)) { + return false; + } + if (repository == null) { + if (other.repository != null) { + return false; + } + } else if (!repository.equals(other.repository)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "TaskAttachmentHyperlink [attachmentId=" + attachmentId + ", region=" + region + ", repository=" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + repository + "]"; //$NON-NLS-1$ + } + } diff --git a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/tasklist/BugzillaConnectorUi.java b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/tasklist/BugzillaConnectorUi.java index fe575da9d..85f2ff077 100644 --- a/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/tasklist/BugzillaConnectorUi.java +++ b/org.eclipse.mylyn.bugzilla.ui/src/org/eclipse/mylyn/internal/bugzilla/ui/tasklist/BugzillaConnectorUi.java @@ -20,7 +20,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.hyperlink.IHyperlink; import org.eclipse.jface.viewers.IStructuredSelection; @@ -54,19 +53,17 @@ import org.eclipse.mylyn.tasks.ui.wizards.RepositoryQueryWizard; */ public class BugzillaConnectorUi extends AbstractRepositoryConnectorUi { - private static final int GET_TASK_NUM_GROUP = 8; + private static final String BUG = "(?:duplicate of|bug|task)\\s*#?\\s*(\\d+)"; //$NON-NLS-1$ - private static final int GET_COMMENT_NUM_GROUP = 13; + private static final String COMMENT = "comment\\s*#?\\s*(\\d+)"; //$NON-NLS-1$ - private static final int GET_ATTACHMENT_NUM_GROUP = 3; + private static final String REGEXP_BUG = "(?:\\W||^)(" + BUG + "(?:\\s*" + COMMENT + ")?)|(" + COMMENT + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - private static final String regexp_bug = "(((duplicate of|((\\W||^)+(bug|task)))( ?#? ?)(\\d+))?((\\W||\\s)*(comment|c)??(\\s*#\\s*)(\\d+))?)"; //$NON-NLS-1$ + private static final String REGEXP_ATTACHMENT = "(?:Created (?:an )?)?attachment\\s*#?\\s*(?:\\(id=)?(\\d+)\\)?"; //$NON-NLS-1$ - private static final String regexp_attachment = "Created (an )?attachment\\s*(\\(id=)?(\\d+)"; //$NON-NLS-1$ + private static final Pattern PATTERN_BUG = Pattern.compile(REGEXP_BUG, Pattern.CASE_INSENSITIVE); - private static final Pattern PATTERN_BUG = Pattern.compile(regexp_bug, Pattern.CASE_INSENSITIVE); - - private static final Pattern PATTERN_ATTACHMENT = Pattern.compile(regexp_attachment, Pattern.CASE_INSENSITIVE); + private static final Pattern PATTERN_ATTACHMENT = Pattern.compile(REGEXP_ATTACHMENT, Pattern.CASE_INSENSITIVE); @Override public String getAccountCreationUrl(TaskRepository taskRepository) { @@ -189,7 +186,21 @@ public class BugzillaConnectorUi extends AbstractRepositoryConnectorUi { Matcher mb = PATTERN_BUG.matcher(text); while (mb.find()) { if (index == -1 || (index >= mb.start() && index <= mb.end())) { - IHyperlink link = extractHyperlinkBug(repository, task, textOffset, mb); + TaskHyperlink link = null; + if (mb.group(1) != null) { + // bug comment + Region region = new Region(textOffset + mb.start(1), mb.end(1) - mb.start(1)); + link = new TaskHyperlink(region, repository, mb.group(2)); + if (mb.group(3) != null) { + link.setSelection(TaskAttribute.PREFIX_COMMENT + mb.group(3)); + } + } else if (task != null && mb.group(4) != null) { + // comment + Region region = new Region(textOffset + mb.start(4), mb.end(4) - mb.start(4)); + link = new TaskHyperlink(region, repository, task.getTaskId()); + link.setSelection(TaskAttribute.PREFIX_COMMENT + mb.group(5)); + } + if (link != null) { if (hyperlinksFound == null) { hyperlinksFound = new ArrayList(); @@ -201,92 +212,17 @@ public class BugzillaConnectorUi extends AbstractRepositoryConnectorUi { Matcher ma = PATTERN_ATTACHMENT.matcher(text); while (ma.find()) { if (index == -1 || (index >= ma.start() && index <= ma.end())) { - IHyperlink link = extractHyperlinkAttachment(repository, textOffset, ma); - if (link != null) { - if (hyperlinksFound == null) { - hyperlinksFound = new ArrayList(); - } - hyperlinksFound.add(link); + // attachment + Region region = new Region(textOffset + ma.start(), ma.end() - ma.start()); + TaskAttachmentHyperlink link = new TaskAttachmentHyperlink(region, repository, ma.group(1)); + if (hyperlinksFound == null) { + hyperlinksFound = new ArrayList(); } + hyperlinksFound.add(link); } } return (hyperlinksFound != null) ? hyperlinksFound.toArray(new IHyperlink[0]) : null; } - private static IHyperlink extractHyperlinkBug(TaskRepository repository, ITask task, int regionOffset, Matcher m) { - - int start = m.start(); - if (m.group().startsWith("duplicate")) { //$NON-NLS-1$ - start = m.start() + m.group().indexOf(m.group(GET_TASK_NUM_GROUP)); - } else { - start = m.start(); - for (int index = 0; index < m.group().length() && !Character.isLetter(m.group().charAt(index)); index++, start++) { - } - } - - int end = m.end(); - - if (end == -1) { - end = m.group().length(); - } - - start += regionOffset; - end += regionOffset; - String bugId = m.group(GET_TASK_NUM_GROUP); - if (bugId == null) { - String commentId = m.group(GET_COMMENT_NUM_GROUP); - IRegion sregion = new Region(start, end - start); - if (commentId != null) { - String taskID; - if (task != null) { - taskID = task.getTaskId(); - } else { - taskID = ""; //$NON-NLS-1$ - } - TaskHyperlink taskHyperLink = new TaskHyperlink(sregion, repository, taskID); - if (commentId != null) { - taskHyperLink.setSelection(TaskAttribute.PREFIX_COMMENT + commentId); - } - return taskHyperLink; - - } - } else { - bugId.trim(); - IRegion sregion = new Region(start, end - start); - TaskHyperlink taskHyperLink = new TaskHyperlink(sregion, repository, bugId); - String commentId = m.group(GET_COMMENT_NUM_GROUP); - if (commentId != null) { - taskHyperLink.setSelection(TaskAttribute.PREFIX_COMMENT + commentId); - } - return taskHyperLink; - } - return null; - } - - private static IHyperlink extractHyperlinkAttachment(TaskRepository repository, int regionOffset, Matcher m) { - - int start = -1; - start = m.start(); - for (int index = 0; index < m.group().length() && !Character.isLetter(m.group().charAt(index)); index++, start++) { - } - - int end = m.end(); - - if (end == -1) { - end = m.group().length(); - } - - start += regionOffset; - end += regionOffset; - - String attachmentId = m.group(GET_ATTACHMENT_NUM_GROUP); - if (attachmentId != null) { - start = start + m.group().indexOf(m.group(GET_ATTACHMENT_NUM_GROUP)); - - IRegion sregion = new Region(start, end - start); - return new TaskAttachmentHyperlink(sregion, repository, attachmentId); - } - return null; - } } diff --git a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskHyperlink.java b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskHyperlink.java index 4aaa0d54c..0272c4571 100644 --- a/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskHyperlink.java +++ b/org.eclipse.mylyn.tasks.ui/src/org/eclipse/mylyn/tasks/ui/TaskHyperlink.java @@ -38,7 +38,7 @@ public final class TaskHyperlink implements IHyperlink { private final String taskId; - Object selection; + private Object selection; public TaskHyperlink(IRegion region, TaskRepository repository, String taskId) { this.region = region; @@ -114,4 +114,64 @@ public final class TaskHyperlink implements IHyperlink { this.selection = selection; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((region == null) ? 0 : region.hashCode()); + result = prime * result + ((repository == null) ? 0 : repository.hashCode()); + result = prime * result + ((selection == null) ? 0 : selection.hashCode()); + result = prime * result + ((taskId == null) ? 0 : taskId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TaskHyperlink other = (TaskHyperlink) obj; + if (region == null) { + if (other.region != null) { + return false; + } + } else if (!region.equals(other.region)) { + return false; + } + if (repository == null) { + if (other.repository != null) { + return false; + } + } else if (!repository.equals(other.repository)) { + return false; + } + if (selection == null) { + if (other.selection != null) { + return false; + } + } else if (!selection.equals(other.selection)) { + return false; + } + if (taskId == null) { + if (other.taskId != null) { + return false; + } + } else if (!taskId.equals(other.taskId)) { + return false; + } + return true; + } + + @Override + public String toString() { + return "TaskHyperlink [region=" + region + ", repository=" + repository + ", selection=" + selection //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + ", taskId=" + taskId + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } -- cgit v1.2.3