package org.eclipse.core.internal.content;
import java.io.InputStream;
import java.io.Reader;
import java.util.*;
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 class ContentTypeRegistryChangeListener implements IRegistryChangeListener {
@Override
public void registryChanged(IRegistryChangeEvent event) {
if (event.getExtensionDeltas(IContentConstants.RUNTIME_NAME, ContentTypeBuilder.PT_CONTENTTYPES).length == 0
&& event.getExtensionDeltas(IContentConstants.CONTENT_NAME,
ContentTypeBuilder.PT_CONTENTTYPES).length == 0)
return;
getInstance().invalidate();
}
}
private static IRegistryChangeListener runtimeExtensionListener = new ContentTypeRegistryChangeListener();
private static IRegistryChangeListener contentExtensionListener = new ContentTypeRegistryChangeListener();
private static volatile ContentTypeManager instance;
public static final int BLOCK_SIZE = 0x400;
public static final String CONTENT_TYPE_PREF_NODE = IContentConstants.RUNTIME_NAME + IPath.SEPARATOR + "content-types";
private static final String OPTION_DEBUG_CONTENT_TYPES = "org.eclipse.core.contenttype/debug";
static final boolean DEBUGGING = Activator.getDefault().getBooleanDebugOption(OPTION_DEBUG_CONTENT_TYPES, false);
private ContentTypeCatalog catalog;
private int catalogGeneration;
protected final ListenerList<IContentTypeChangeListener> contentTypeListeners = new ListenerList<>();
public static void addRegistryChangeListener(IExtensionRegistry registry) {
if (registry == null)
return;
registry.addRegistryChangeListener(runtimeExtensionListener, IContentConstants.RUNTIME_NAME);
registry.addRegistryChangeListener(contentExtensionListener, IContentConstants.CONTENT_NAME);
}
public static void shutdown() {
instance = null;
}
public static void removeRegistryChangeListener(IExtensionRegistry registry) {
if (registry == null)
return;
getInstance().invalidate();
registry.removeRegistryChangeListener(runtimeExtensionListener);
registry.removeRegistryChangeListener(contentExtensionListener);
}
public static ContentTypeManager getInstance() {
if (instance == null) {
synchronized (ContentTypeManager.class) {
if (instance == null) {
instance = new ContentTypeManager();
}
}
}
return instance;
}
static String getFileExtension(String fileName) {
int dotPosition = fileName.lastIndexOf('.');
return (dotPosition == -1 || dotPosition == fileName.length() - 1) ? "" : fileName.substring(dotPosition + 1);
}
protected static ILazySource readBuffer(InputStream contents) {
return new LazyInputStream(contents, BLOCK_SIZE);
}
protected static ILazySource readBuffer(Reader contents) {
return new LazyReader(contents, BLOCK_SIZE);
}
public ContentTypeManager() {
super(null, InstanceScope.INSTANCE);
}
protected ContentTypeBuilder createBuilder(ContentTypeCatalog newCatalog) {
return new ContentTypeBuilder(newCatalog);
}
@Override
public IContentType[] getAllContentTypes() {
ContentTypeCatalog currentCatalog = getCatalog();
IContentType[] types = currentCatalog.getAllContentTypes();
IContentType[] result = new IContentType[types.length];
int generation = currentCatalog.getGeneration();
for (int i = 0; i < result.length; i++)
result[i] = new ContentTypeHandler((ContentType) types[i], generation);
return result;
}
protected synchronized ContentTypeCatalog getCatalog() {
if (catalog != null)
return catalog;
ContentTypeCatalog newCatalog = new ContentTypeCatalog(this, catalogGeneration++);
ContentTypeBuilder builder = createBuilder(newCatalog);
try {
builder.buildCatalog(getContext());
catalog = newCatalog;
} catch (InvalidRegistryObjectException e) {
}
newCatalog.organize();
return newCatalog;
}
@Override
public IContentType getContentType(String contentTypeIdentifier) {
ContentTypeCatalog currentCatalog = getCatalog();
ContentType type = currentCatalog.getContentType(contentTypeIdentifier);
return type == null ? null : new ContentTypeHandler(type, currentCatalog.getGeneration());
}
@Override
public IContentTypeMatcher getMatcher(final ISelectionPolicy customPolicy, final IScopeContext context) {
return new ContentTypeMatcher(customPolicy, context == null ? getContext() : context);
}
IEclipsePreferences getPreferences() {
return getPreferences(getContext());
}
IEclipsePreferences getPreferences(IScopeContext context) {
return context.getNode(CONTENT_TYPE_PREF_NODE);
}
synchronized void invalidate() {
if (ContentTypeManager.DEBUGGING && catalog != null)
ContentMessages.message("Registry discarded");
catalog = null;
}
@Override
public void addContentTypeChangeListener(IContentTypeChangeListener listener) {
contentTypeListeners.add(listener);
}
@Override
public void removeContentTypeChangeListener(IContentTypeChangeListener listener) {
contentTypeListeners.remove(listener);
}
public void fireContentTypeChangeEvent(IContentType type) {
IContentType eventObject = type;
if (type instanceof ContentType) {
eventObject = new ContentTypeHandler((ContentType) type, ((ContentType) type).getCatalog().getGeneration());
} else {
eventObject = type;
}
for (final IContentTypeChangeListener listener : this.contentTypeListeners) {
final ContentTypeChangeEvent event = new ContentTypeChangeEvent(eventObject);
ISafeRunnable job = new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
}
@Override
public void run() throws Exception {
listener.contentTypeChanged(event);
}
};
SafeRunner.run(job);
}
}
@Override
public IContentDescription getSpecificDescription(BasicDescription description) {
return description;
}
@Override
public final void removeContentType(String contentTypeIdentifier) throws CoreException {
if (contentTypeIdentifier == null) {
return;
}
IContentType contentType = getContentType(contentTypeIdentifier);
if (contentType == null) {
return;
}
if (!contentType.isUserDefined()) {
throw new IllegalArgumentException("Can only delete content-types defined by users.");
}
getCatalog().removeContentType(contentType.getId());
List<String> userDefinedIds = new ArrayList<>(Arrays.asList(getUserDefinedContentTypeIds()));
userDefinedIds.remove(contentType.getId());
getContext().getNode(ContentType.PREF_USER_DEFINED).put(ContentType.PREF_USER_DEFINED,
userDefinedIds.stream().collect(Collectors.joining(ContentType.PREF_USER_DEFINED__SEPARATOR)));
try {
getContext().getNode(ContentType.PREF_USER_DEFINED).flush();
} catch (BackingStoreException e) {
String message = NLS.bind(ContentMessages.content_errorSavingSettings, contentType.getId());
IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, e);
throw new CoreException(status);
}
getCatalog().organize();
fireContentTypeChangeEvent(contentType);
}
@Override
public final IContentType addContentType(String id, String name, IContentType baseType) throws CoreException {
if (id == null) {
throw new IllegalArgumentException("Content-type 'id' mustn't be null");
}
if (id.contains(ContentType.PREF_USER_DEFINED__SEPARATOR)) {
throw new IllegalArgumentException(
"Content-Type id mustn't contain '" + ContentType.PREF_USER_DEFINED__SEPARATOR + '\'');
}
if (getContentType(id) != null) {
throw new IllegalArgumentException("Content-type '" + id + "' already exists.");
}
ContentType contentType = ContentType.createContentType(getCatalog(), id, name, (byte) 0, new String[0],
new String[0], new String[0], baseType != null ? baseType.getId() : null, null, null, null);
getCatalog().addContentType(contentType);
String currentUserDefined = getContext().getNode(ContentType.PREF_USER_DEFINED)
.get(ContentType.PREF_USER_DEFINED, ContentType.EMPTY_STRING);
if (!currentUserDefined.isEmpty()) {
currentUserDefined += ContentType.PREF_USER_DEFINED__SEPARATOR;
}
getContext().getNode(ContentType.PREF_USER_DEFINED).put(ContentType.PREF_USER_DEFINED, currentUserDefined + id);
contentType.setValidation(ContentType.STATUS_UNKNOWN);
IEclipsePreferences contextTypeNode = getContext().getNode(contentType.getId());
contextTypeNode.put(ContentType.PREF_USER_DEFINED__NAME, name);
if (baseType != null) {
contextTypeNode.put(ContentType.PREF_USER_DEFINED__BASE_TYPE_ID, baseType.getId());
}
try {
getContext().getNode(ContentType.PREF_USER_DEFINED).flush();
contextTypeNode.flush();
} catch (BackingStoreException e) {
String message = NLS.bind(ContentMessages.content_errorSavingSettings, id);
IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, e);
throw new CoreException(status);
}
getCatalog().organize();
fireContentTypeChangeEvent(contentType);
return contentType;
}
private String[] getUserDefinedContentTypeIds() {
return getUserDefinedContentTypeIds(getContext());
}
static String[] getUserDefinedContentTypeIds(IScopeContext context) {
String ids = context.getNode(ContentType.PREF_USER_DEFINED)
.get(ContentType.PREF_USER_DEFINED, ContentType.EMPTY_STRING);
if (ids.isEmpty()) {
return new String[0];
}
return ids.split(ContentType.PREF_USER_DEFINED__SEPARATOR);
}
}