summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Schmidt2012-05-21 12:43:37 -0400
committerSebastian Schmidt2012-05-21 12:43:37 -0400
commite8a53673bf36d66d72bbc9498d6ca37feff37c68 (patch)
treed324b93c22bd8c95840a2b92dd172355ee22e66a
parent75f8056d89bcc5ae826bae5ca149f342c712cfa3 (diff)
downloadorg.eclipse.mylyn.context-e8a53673bf36d66d72bbc9498d6ca37feff37c68.zip
org.eclipse.mylyn.context-e8a53673bf36d66d72bbc9498d6ca37feff37c68.tar.gz
org.eclipse.mylyn.context-e8a53673bf36d66d72bbc9498d6ca37feff37c68.tar.xz
allow third party contributions to the context
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"));
+ }
}