summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Schmidt2012-05-21 12:43:37 (EDT)
committer Sebastian Schmidt2012-05-21 12:43:37 (EDT)
commite8a53673bf36d66d72bbc9498d6ca37feff37c68 (patch)
treed324b93c22bd8c95840a2b92dd172355ee22e66a
parent75f8056d89bcc5ae826bae5ca149f342c712cfa3 (diff)
downloadorg.eclipse.mylyn.context-e8a53673bf36d66d72bbc9498d6ca37feff37c68.zip
org.eclipse.mylyn.context-e8a53673bf36d66d72bbc9498d6ca37feff37c68.tar.gz
org.eclipse.mylyn.context-e8a53673bf36d66d72bbc9498d6ca37feff37c68.tar.bz2
allow third party contributions to the contextrefs/changes/52/6052/4
This change enables third party plugins to contribute content to Mylyn context. Third party plugins can register themselves as IContextContributor and will be asked for additional data before a save operation on a Mylyn context. Additionally, they are allowed to receive these additional data at any time a context is active. The IContextContributor registration should be enabled as an extension point in later versions. Bug: 378399 Change-Id: I704ede7baf022b7718cdb5e58a68405e14b985da
-rw-r--r--org.eclipse.mylyn.context.core/META-INF/MANIFEST.MF1
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/ContextCore.java7
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IContextContributor.java39
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IInteractionContextManager.java6
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/ContextCorePlugin.java17
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextExternalizer.java63
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextManager.java15
-rw-r--r--org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/LocalContextStore.java7
-rw-r--r--org.eclipse.mylyn.context.tests/META-INF/MANIFEST.MF5
-rw-r--r--org.eclipse.mylyn.context.tests/src/org/eclipse/mylyn/context/tests/ContextExternalizerTest.java58
10 files changed, 215 insertions, 3 deletions
diff --git a/org.eclipse.mylyn.context.core/META-INF/MANIFEST.MF b/org.eclipse.mylyn.context.core/META-INF/MANIFEST.MF
index 78ac595..2b90413 100644
--- a/org.eclipse.mylyn.context.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.context.core/META-INF/MANIFEST.MF
@@ -8,6 +8,7 @@ Bundle-Localization: plugin
Require-Bundle: org.eclipse.core.runtime,
org.eclipse.mylyn.commons.core;bundle-version="[3.8.0,4.0.0)",
org.eclipse.mylyn.monitor.core;bundle-version="[3.8.0,4.0.0)"
+Import-Package: org.apache.commons.io;version="[1.4.0,3.0.0)"
Bundle-ActivationPolicy: lazy
Bundle-Vendor: %Bundle-Vendor
Export-Package: org.eclipse.mylyn.context.core,
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/ContextCore.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/ContextCore.java
index 5a17594..2ae4613 100644
--- a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/ContextCore.java
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/ContextCore.java
@@ -11,6 +11,7 @@
package org.eclipse.mylyn.context.core;
+import java.util.List;
import java.util.Set;
import org.eclipse.mylyn.internal.context.core.ContextCorePlugin;
@@ -57,4 +58,10 @@ public final class ContextCore {
return ContextCorePlugin.getContextStore();
}
+ /**
+ * @since 3.9
+ */
+ public static List<IContextContributor> getContextContributor() {
+ return ContextCorePlugin.getDefault().getContextContributor();
+ }
}
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IContextContributor.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IContextContributor.java
new file mode 100644
index 0000000..34ed795
--- /dev/null
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IContextContributor.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2012 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:
+ * Sebastian Schmidt - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.mylyn.context.core;
+
+import java.io.InputStream;
+
+/**
+ * A ContextContributor may be used to put additional data to Mylyn context. As the ContextContributor is in charge of
+ * serialization and deserialization of provided data, the data type is not limited.
+ *
+ * @since 3.8
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ */
+public interface IContextContributor {
+
+ /**
+ * Provides data which should be added to the given context.
+ *
+ * @param context
+ * context that is going to be saved
+ * @return an InputStream with context related data or null
+ */
+ public InputStream getDataAsStream(IInteractionContext context);
+
+ /**
+ * @return an unique identifier for this ContextContributor
+ */
+ public String getIdentifier();
+}
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IInteractionContextManager.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IInteractionContextManager.java
index 574bae3..6ff14b2 100644
--- a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IInteractionContextManager.java
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/context/core/IInteractionContextManager.java
@@ -11,6 +11,7 @@
package org.eclipse.mylyn.context.core;
+import java.io.InputStream;
import java.util.Collection;
import java.util.Set;
@@ -72,4 +73,9 @@ public interface IInteractionContextManager {
* NOTE: If pausing ensure to restore to original state.
*/
public void setContextCapturePaused(boolean paused);
+
+ /**
+ * @since 3.9
+ */
+ public InputStream getAdditionalContextData(IInteractionContext context, String identifier);
} \ No newline at end of file
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/ContextCorePlugin.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/ContextCorePlugin.java
index e294991..fe7ecf8 100644
--- a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/ContextCorePlugin.java
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/ContextCorePlugin.java
@@ -19,6 +19,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.eclipse.core.runtime.IConfigurationElement;
@@ -33,6 +34,7 @@ import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.AbstractContextStructureBridge;
import org.eclipse.mylyn.context.core.ContextCore;
+import org.eclipse.mylyn.context.core.IContextContributor;
import org.eclipse.mylyn.context.core.IInteractionContextScaling;
import org.osgi.framework.BundleContext;
@@ -50,6 +52,8 @@ public class ContextCorePlugin extends Plugin {
private final Map<String, Set<String>> childContentTypeMap = new ConcurrentHashMap<String, Set<String>>();
+ private final List<IContextContributor> contextContributor = new CopyOnWriteArrayList<IContextContributor>();
+
// specifies that one content type should shadow another
// the <value> content type shadows the <key> content typee
private final Map<String, String> contentTypeToShadowMap = new ConcurrentHashMap<String, String>();
@@ -210,6 +214,19 @@ public class ContextCorePlugin extends Plugin {
return bridges;
}
+ public List<IContextContributor> getContextContributor() {
+ return contextContributor;
+ }
+
+ // TODO: add extension point to register context provider
+ public void addContextContributor(IContextContributor contributor) {
+ contextContributor.add(contributor);
+ }
+
+ public void removeContextContributor(IContextContributor contributor) {
+ contextContributor.remove(contributor);
+ }
+
/**
* Finds the shadowed content for the passed in base content
*
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextExternalizer.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextExternalizer.java
index 134bca6..c63b338 100644
--- a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextExternalizer.java
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextExternalizer.java
@@ -14,17 +14,24 @@ package org.eclipse.mylyn.internal.context.core;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Enumeration;
+import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
+import org.apache.commons.io.IOUtils;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.mylyn.commons.core.StatusHandler;
+import org.eclipse.mylyn.context.core.IContextContributor;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.context.core.IInteractionContextScaling;
@@ -144,6 +151,62 @@ public class InteractionContextExternalizer {
writer.writeContextToStream(context);
outputStream.flush();
outputStream.closeEntry();
+
+ addAdditionalInformation(context, outputStream);
+ }
+
+ private void addAdditionalInformation(final IInteractionContext context, final ZipOutputStream outputStream)
+ throws IOException {
+ for (final IContextContributor contributor : getContextContributor()) {
+ SafeRunner.run(new ISafeRunnable() {
+ public void handleException(Throwable e) {
+ StatusHandler.log(new Status(IStatus.WARNING, ContextCorePlugin.ID_PLUGIN,
+ "Context contribution failed: " //$NON-NLS-1$
+ + contributor.getClass(), e));
+ }
+
+ public void run() throws Exception {
+ InputStream additionalContextInformation = contributor.getDataAsStream(context);
+ if (additionalContextInformation != null) {
+ String encoded = URLEncoder.encode(contributor.getIdentifier(),
+ InteractionContextManager.CONTEXT_FILENAME_ENCODING);
+ ZipEntry zipEntry = new ZipEntry(encoded);
+ outputStream.putNextEntry(zipEntry);
+ IOUtils.copy(additionalContextInformation, outputStream);
+ outputStream.flush();
+ outputStream.closeEntry();
+ }
+ }
+ });
+ }
+ }
+
+ public InputStream getAdditionalInformation(File file, String contributorIdentifier) throws IOException {
+ if (!file.exists()) {
+ return null;
+ }
+ ZipFile zipFile = new ZipFile(file);
+ ZipEntry entry = findFileInZip(zipFile, contributorIdentifier);
+ if (entry == null) {
+ return null;
+ }
+
+ return zipFile.getInputStream(entry);
+ }
+
+ private ZipEntry findFileInZip(ZipFile zipFile, String identifier) throws UnsupportedEncodingException {
+ String encoded = URLEncoder.encode(identifier, InteractionContextManager.CONTEXT_FILENAME_ENCODING);
+ for (Enumeration<?> e = zipFile.entries(); e.hasMoreElements();) {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+ if (entry.getName().equals(encoded)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ private List<IContextContributor> getContextContributor() {
+ return ContextCorePlugin.getDefault().getContextContributor();
}
public IInteractionContext readContextFromXml(String handleIdentifier, File fromFile,
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextManager.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextManager.java
index a719733..1505bab 100644
--- a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextManager.java
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/InteractionContextManager.java
@@ -12,6 +12,8 @@
package org.eclipse.mylyn.internal.context.core;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -39,12 +41,12 @@ import org.eclipse.mylyn.commons.core.StatusHandler;
import org.eclipse.mylyn.context.core.AbstractContextListener;
import org.eclipse.mylyn.context.core.AbstractContextStructureBridge;
import org.eclipse.mylyn.context.core.ContextChangeEvent;
+import org.eclipse.mylyn.context.core.ContextChangeEvent.ContextChangeKind;
import org.eclipse.mylyn.context.core.ContextCore;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.context.core.IInteractionContextManager;
import org.eclipse.mylyn.context.core.IInteractionElement;
import org.eclipse.mylyn.context.core.IInteractionRelation;
-import org.eclipse.mylyn.context.core.ContextChangeEvent.ContextChangeKind;
import org.eclipse.mylyn.monitor.core.InteractionEvent;
import org.eclipse.mylyn.monitor.core.InteractionEvent.Kind;
@@ -420,6 +422,17 @@ public class InteractionContextManager implements IInteractionContextManager {
}
}
+ public InputStream getAdditionalContextData(IInteractionContext context, String contributorIdentifier) {
+ try {
+ return contextStore.getAdditionalContextInformation(context, contributorIdentifier);
+ } catch (IOException e) {
+ StatusHandler.log(new Status(IStatus.WARNING, ContextCorePlugin.ID_PLUGIN,
+ "Searching for additional context data failed" //$NON-NLS-1$
+ , e));
+ }
+ return null;
+ }
+
public void deactivateContext(String handleIdentifier) {
try {
System.setProperty(InteractionContextManager.PROPERTY_CONTEXT_ACTIVE, Boolean.FALSE.toString());
diff --git a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/LocalContextStore.java b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/LocalContextStore.java
index 4801769..e4c7bfa 100644
--- a/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/LocalContextStore.java
+++ b/org.eclipse.mylyn.context.core/src/org/eclipse/mylyn/internal/context/core/LocalContextStore.java
@@ -13,6 +13,7 @@ package org.eclipse.mylyn.internal.context.core;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
@@ -82,6 +83,12 @@ public class LocalContextStore implements IContextStore {
return loadContext(handleIdentifier, getFileForContext(handleIdentifier), commonContextScaling);
}
+ public InputStream getAdditionalContextInformation(IInteractionContext context, String identifier)
+ throws IOException {
+ File fileForContext = getFileForContext(context.getHandleIdentifier());
+ return externalizer.getAdditionalInformation(fileForContext, identifier);
+ }
+
public IInteractionContext importContext(String handleIdentifier, File fromFile) throws CoreException {
InteractionContext context;
String handleToImportFrom;
diff --git a/org.eclipse.mylyn.context.tests/META-INF/MANIFEST.MF b/org.eclipse.mylyn.context.tests/META-INF/MANIFEST.MF
index 3ef8ce2..0cae2cb 100644
--- a/org.eclipse.mylyn.context.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.mylyn.context.tests/META-INF/MANIFEST.MF
@@ -17,7 +17,10 @@ Require-Bundle: org.junit,
org.eclipse.mylyn.context.sdk.util,
org.eclipse.mylyn.context.ui,
org.eclipse.mylyn.monitor.core,
- org.eclipse.mylyn.monitor.ui
+ org.eclipse.mylyn.monitor.ui,
+ org.mockito;bundle-version="[1.8.4,2.0.0)",
+ org.objenesis;bundle-version="[1.0.0,2.0.0)",
+ org.hamcrest;bundle-version="[1.0.0,2.0.0)"
Export-Package: org.eclipse.mylyn.context.tests;x-internal:=true,
org.eclipse.mylyn.context.tests.support;x-internal:=true,
org.eclipse.mylyn.context.tests.support.search;x-internal:=true
diff --git a/org.eclipse.mylyn.context.tests/src/org/eclipse/mylyn/context/tests/ContextExternalizerTest.java b/org.eclipse.mylyn.context.tests/src/org/eclipse/mylyn/context/tests/ContextExternalizerTest.java
index 4d6edfd..e5eb933 100644
--- a/org.eclipse.mylyn.context.tests/src/org/eclipse/mylyn/context/tests/ContextExternalizerTest.java
+++ b/org.eclipse.mylyn.context.tests/src/org/eclipse/mylyn/context/tests/ContextExternalizerTest.java
@@ -11,18 +11,25 @@
package org.eclipse.mylyn.context.tests;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.mylyn.commons.sdk.util.CommonTestUtil;
import org.eclipse.mylyn.context.core.ContextCore;
+import org.eclipse.mylyn.context.core.IContextContributor;
import org.eclipse.mylyn.context.core.IInteractionContext;
import org.eclipse.mylyn.context.core.IInteractionContextScaling;
import org.eclipse.mylyn.context.core.IInteractionElement;
import org.eclipse.mylyn.context.core.IInteractionRelation;
-import org.eclipse.mylyn.context.sdk.util.AbstractContextTest;
import org.eclipse.mylyn.context.tests.support.DomContextReader;
import org.eclipse.mylyn.context.tests.support.DomContextWriter;
import org.eclipse.mylyn.internal.context.core.ContextCorePlugin;
@@ -30,6 +37,7 @@ import org.eclipse.mylyn.internal.context.core.InteractionContext;
import org.eclipse.mylyn.internal.context.core.InteractionContextExternalizer;
import org.eclipse.mylyn.internal.context.core.InteractionContextManager;
import org.eclipse.mylyn.internal.context.core.SaxContextReader;
+import org.eclipse.mylyn.monitor.core.InteractionEvent;
/**
* @author Mik Kersten
@@ -43,6 +51,8 @@ public class ContextExternalizerTest extends AbstractContextTest {
private IInteractionContextScaling scaling;
+ private File contextFile;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -53,6 +63,10 @@ public class ContextExternalizerTest extends AbstractContextTest {
@Override
protected void tearDown() throws Exception {
+ if (contextFile != null && contextFile.exists()) {
+ contextFile.delete();
+ }
+
super.tearDown();
}
@@ -297,4 +311,46 @@ public class ContextExternalizerTest extends AbstractContextTest {
assertNull(context);
}
+ public void testAddContextContributor() throws Exception {
+ InteractionContextExternalizer externalizer = new InteractionContextExternalizer();
+ ContextCorePlugin contextCorePlugin = ContextCorePlugin.getDefault();
+ IContextContributor contributor = mock(IContextContributor.class);
+ when(contributor.getDataAsStream(context)).thenReturn(null);
+
+ contextCorePlugin.addContextContributor(contributor);
+ assertEquals(1, contextCorePlugin.getContextContributor().size());
+ assertEquals(contributor, contextCorePlugin.getContextContributor().get(0));
+
+ externalizer.writeContext(context, mock(ZipOutputStream.class));
+ verify(contributor).getDataAsStream(context);
+
+ contextCorePlugin.removeContextContributor(contributor);
+ assertEquals(0, contextCorePlugin.getContextContributor().size());
+ }
+
+ public void testWriteAdditionalContextData() throws Exception {
+ InteractionContextExternalizer externalizer = new InteractionContextExternalizer();
+ IContextContributor contributor = mock(IContextContributor.class);
+ InteractionEvent event = mockNavigation("InteractionEvent");
+ context.parseEvent(event);
+
+ String testContributorId = "myContributor";
+ String testData = "important context information";
+ InputStream testStream = new ByteArrayInputStream(testData.getBytes());
+ when(contributor.getIdentifier()).thenReturn(testContributorId);
+ when(contributor.getDataAsStream(context)).thenReturn(testStream);
+ ContextCorePlugin.getDefault().addContextContributor(contributor);
+
+ contextFile = ContextCorePlugin.getContextStore().getFileForContext(context.getHandleIdentifier());
+
+ externalizer.writeContextToXml(context, contextFile);
+ InputStream resultStream = externalizer.getAdditionalInformation(contextFile, testContributorId);
+ assertNotNull(resultStream);
+ assertNull(externalizer.getAdditionalInformation(contextFile, "nonExistingContributor"));
+ assertEquals(testData, new Scanner(resultStream).useDelimiter("\\A").next());
+
+ resultStream = ContextCore.getContextManager().getAdditionalContextData(context, testContributorId);
+ assertNotNull(resultStream);
+ assertNull(externalizer.getAdditionalInformation(contextFile, "nonExistingContributor"));
+ }
}