Bug 500892 - API to create/remove content-types in user-space Change-Id: I925cdee45d97d7d657e0d25c80eb69c43e9c9d71 Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF index 74b6163..65c5da8 100644 --- a/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.core.contenttype/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.contenttype; singleton:=true -Bundle-Version: 3.5.100.qualifier +Bundle-Version: 3.6.0.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Require-Bundle: org.eclipse.equinox.preferences;bundle-version="[3.2.0,4.0.0)",
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java index 98057c3..23adf17 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentType.java
@@ -10,7 +10,6 @@ *******************************************************************************/ package org.eclipse.core.internal.content; -import org.eclipse.core.runtime.QualifiedName; import java.io.*; import java.util.*; import org.eclipse.core.internal.runtime.RuntimeLog; @@ -52,6 +51,10 @@ public final static String PREF_DEFAULT_CHARSET = "charset"; //$NON-NLS-1$ public final static String PREF_FILE_EXTENSIONS = "file-extensions"; //$NON-NLS-1$ public final static String PREF_FILE_NAMES = "file-names"; //$NON-NLS-1$ + /** + * @since 3.6 + */ + public static final String PREF_USER_DEFINED = "userDefined"; //$NON-NLS-1$ final static byte PRIORITY_HIGH = 1; final static byte PRIORITY_LOW = -1; final static byte PRIORITY_NORMAL = 0; @@ -73,6 +76,7 @@ String id; private ContentTypeManager manager; private String name; + private boolean userDefined; private byte priority; private ContentType target; private String userCharset; @@ -609,4 +613,9 @@ this.baseType = baseType; } + @Override + public boolean isUserDefined() { + return this.userDefined; + } + }
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java index f4793fa..3b455c3 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeBuilder.java
@@ -10,15 +10,12 @@ *******************************************************************************/ package org.eclipse.core.internal.content; -import org.eclipse.core.runtime.QualifiedName; - import java.util.*; import org.eclipse.core.internal.runtime.RuntimeLog; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.content.IContentType; -import org.eclipse.core.runtime.preferences.IEclipsePreferences; -import org.eclipse.core.runtime.preferences.IPreferenceNodeVisitor; +import org.eclipse.core.runtime.preferences.*; import org.eclipse.osgi.util.NLS; import org.osgi.service.prefs.BackingStoreException; @@ -85,7 +82,7 @@ /** * Builds all content types found in the extension registry. */ - public void buildCatalog() { + public void buildCatalog(IScopeContext context) { IConfigurationElement[] allContentTypeCEs = getConfigurationElements(); for (int i = 0; i < allContentTypeCEs.length; i++) if (allContentTypeCEs[i].getName().equals("content-type")) //$NON-NLS-1$
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java index 1153d7b..26aa2f4 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeCatalog.java
@@ -10,10 +10,6 @@ *******************************************************************************/ package org.eclipse.core.internal.content; -import java.util.Iterator; - -import java.util.Set; -import org.eclipse.core.runtime.content.IContentType; import java.io.*; import java.util.*; import org.eclipse.core.runtime.*; @@ -607,4 +603,12 @@ } return destination; } + + void removeContentType(IContentType contentType) throws CoreException { + if (contentType.getSettings(getManager().getContext()).isUserDefined()) { + throw new IllegalArgumentException("content type must be user-defined."); //$NON-NLS-1$ + } + contentTypes.remove(contentType.getId()); + } + }
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeHandler.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeHandler.java index dcf3926..aa9e5cf 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeHandler.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeHandler.java
@@ -202,4 +202,13 @@ return id; } + @Override + public boolean isUserDefined() { + ContentType target = getTarget(); + if (target != null) { + return target.isUserDefined(); + } + return false; + } + }
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java index 8f67515..fee8a47 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeManager.java
@@ -13,9 +13,14 @@ import java.io.InputStream; import java.io.Reader; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.content.*; import org.eclipse.core.runtime.preferences.*; +import org.eclipse.osgi.util.NLS; +import org.osgi.service.prefs.BackingStoreException; public class ContentTypeManager extends ContentTypeMatcher implements IContentTypeManager { private static ContentTypeManager instance; @@ -140,7 +145,7 @@ // build catalog by parsing the extension registry ContentTypeBuilder builder = createBuilder(newCatalog); try { - builder.buildCatalog(); + builder.buildCatalog(getContext()); // only remember catalog if building it was successful catalog = newCatalog; } catch (InvalidRegistryObjectException e) { @@ -213,4 +218,60 @@ // this is the platform content type manager, no specificities return description; } + + @Override + public void removeContentType(IContentType contentType) throws CoreException { + if (contentType.getSettings(getContext()).isUserDefined()) { + throw new IllegalArgumentException("content type must be user-defined."); //$NON-NLS-1$ + } + if (!contentType.isUserDefined()) { + throw new IllegalArgumentException("Can only delete content-types defined by users."); //$NON-NLS-1$ + } + getCatalog().removeContentType(contentType); + // remove preferences for this content type + String currentUserDefined = getContext().getNode(ContentType.PREF_USER_DEFINED) + .get(ContentType.PREF_USER_DEFINED, "");//$NON-NLS-1$ + List<String> userDefinedIds = Arrays.asList(currentUserDefined.split(",")); //$NON-NLS-1$ + userDefinedIds.remove(contentType.getId()); + getContext().getNode(ContentType.PREF_USER_DEFINED).put(ContentType.PREF_USER_DEFINED, + userDefinedIds.stream().collect(Collectors.joining(","))); //$NON-NLS-1$ + try { + getContext().getNode(ContentType.PREF_USER_DEFINED).flush(); + } catch (BackingStoreException bse) { + String message = NLS.bind(ContentMessages.content_errorSavingSettings, contentType.getId()); + IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, bse); + throw new CoreException(status); + } + } + + @Override + public IContentType addContentType(String id, String name, IContentType baseType) throws CoreException { + if (id == null) { + throw new IllegalArgumentException("content-type 'id' mustn't be null");//$NON-NLS-1$ + } + if (id.contains(",")) { //$NON-NLS-1$ + throw new IllegalAccessError("Content-Type id mustn't contain ','"); //$NON-NLS-1$ + } + if (getContentType(id) != null) { + throw new IllegalArgumentException("content-type '" + id + "' already exists.");//$NON-NLS-1$ //$NON-NLS-2$ + } + ContentType contentType = ContentType.createContentType(getCatalog(), id, name, (byte) 0, new String[0], + new String[0], baseType.getId(), null, null, null); + getCatalog().addContentType(contentType); + // add preferences for this content type + String currentUserDefined = getContext().getNode(ContentType.PREF_USER_DEFINED) + .get(ContentType.PREF_USER_DEFINED, "");//$NON-NLS-1$ + if (currentUserDefined.length() > 0) { + currentUserDefined += ",";//$NON-NLS-1$ + } + getContext().getNode(ContentType.PREF_USER_DEFINED).put(ContentType.PREF_USER_DEFINED, currentUserDefined + id); + try { + getContext().getNode(ContentType.PREF_USER_DEFINED).flush(); + } catch (BackingStoreException bse) { + String message = NLS.bind(ContentMessages.content_errorSavingSettings, id); + IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, bse); + throw new CoreException(status); + } + return contentType; + } }
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeSettings.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeSettings.java index 71387ee..60512f4 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeSettings.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/internal/content/ContentTypeSettings.java
@@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.core.internal.content; +import java.util.Arrays; import java.util.List; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.content.IContentDescription; @@ -161,4 +162,12 @@ } } + @Override + public boolean isUserDefined() { + return Arrays.asList( + context.getNode(ContentType.PREF_USER_DEFINED).get(ContentType.PREF_USER_DEFINED, "") //$NON-NLS-1$ + .split(",")) //$NON-NLS-1$ + .contains(contentType.getId()); + } + }
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeManager.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeManager.java index 1c9fb05..6bf307e 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeManager.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeManager.java
@@ -11,6 +11,7 @@ package org.eclipse.core.runtime.content; import java.util.EventObject; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.preferences.IScopeContext; /** @@ -22,6 +23,7 @@ * * @see org.eclipse.core.runtime.content.IContentTypeMatcher * @since 3.0 + * @noimplement This interface is not intended to be implemented by clients. */ public interface IContentTypeManager extends IContentTypeMatcher { @@ -215,4 +217,32 @@ * @see IContentTypeManager.IContentTypeChangeListener */ public void removeContentTypeChangeListener(IContentTypeChangeListener listener); + + /** + * Removes the content-type from internal registry. + * + * @param id + * the non-null content-type id + * @param name + * the non-null user readable name + * @param baseType + * parent base type. May be null. + * @return the newly created and registered content-type + * @throws IllegalArgumentException + * if one of the non-null arguments is null, or id is already + * used by another content-type + * + * TODO move to a IContentTypeManagerExtension interface + */ + public IContentType addContentType(String id, String name, IContentType baseType) throws CoreException; + + /** + * Remove a content-type from underlying registry. + * + * @param contentType + * the content-type to remove. + * + * TODO move to a IContentTypeManagerExtension interface + */ + public void removeContentType(IContentType contentType) throws CoreException; }
diff --git a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeSettings.java b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeSettings.java index 17f9035..25ee261 100644 --- a/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeSettings.java +++ b/bundles/org.eclipse.core.contenttype/src/org/eclipse/core/runtime/content/IContentTypeSettings.java
@@ -22,6 +22,7 @@ * @see IContentType * @see IContentType#getSettings(IScopeContext) * @since 3.1 + * @noimplement This interface is not intended to be implemented by clients. */ public interface IContentTypeSettings { /** @@ -115,4 +116,10 @@ * </ul> */ public void setDefaultCharset(String userCharset) throws CoreException; + + /** + * @return whether the content-type was defined by user + * @since 3.6 + */ + public boolean isUserDefined(); }