diff options
author | Christian W. Damus | 2016-01-28 15:06:14 +0000 |
---|---|---|
committer | Gerrit Code Review @ Eclipse.org | 2016-01-29 16:52:21 +0000 |
commit | 8d95235d0b3d9c15ff4c824c8bda35ff63528813 (patch) | |
tree | 0143d78f33b5df0b4517e511105c29e8a40161fe /plugins/infra/core/org.eclipse.papyrus.infra.core/src/org | |
parent | 0e9c200139b99bb0825d52ff87ce962571c5bc6e (diff) | |
download | org.eclipse.papyrus-8d95235d0b3d9c15ff4c824c8bda35ff63528813.tar.gz org.eclipse.papyrus-8d95235d0b3d9c15ff4c824c8bda35ff63528813.tar.xz org.eclipse.papyrus-8d95235d0b3d9c15ff4c824c8bda35ff63528813.zip |
Bug 486834: Language service doesn't understand new resources
https://bugs.eclipse.org/bugs/show_bug.cgi?id=486834
A resource that is new (such as in the New Model Wizard) cannot provide
a content-type via its persisted state because that doesn't yet exist,
so it is necessary to infer (as much as possible) a content-type from
the in-memory state. This also introduces a cache by schema that can
help to avoid the cost of accessing persisted content redundantly for
the purpose of descibing the content.
Diffstat (limited to 'plugins/infra/core/org.eclipse.papyrus.infra.core/src/org')
5 files changed, 142 insertions, 4 deletions
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java index 11f745b52d1..6f40db2c71b 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java @@ -15,10 +15,14 @@ package org.eclipse.papyrus.infra.core.internal.language; import java.io.File; import java.io.IOException; +import java.io.StringReader; +import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IResource; @@ -28,14 +32,20 @@ import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.resource.ContentHandler; +import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.URIConverter; +import org.eclipse.emf.ecore.xmi.XMIResource; import org.eclipse.papyrus.infra.core.Activator; import org.eclipse.papyrus.infra.core.language.ILanguage; import org.eclipse.papyrus.infra.core.language.ILanguageProvider; import org.eclipse.papyrus.infra.core.language.ILanguageService; +import org.eclipse.papyrus.infra.core.resource.ModelSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -44,6 +54,10 @@ import com.google.common.collect.Sets; * A default language provider for extensions that simply declare content-type matches for languages. */ public class DefaultLanguageProvider implements ILanguageProvider { + private static Map<EPackage, IContentType> CONTENT_TYPES = new WeakHashMap<>(); + + private static IContentType NULL_CONTENT_TYPE = Platform.getContentTypeManager().getContentType("org.eclipse.emf.ecore.xmi"); //$NON-NLS-1$ + private final Set<IContentType> contentTypes = Sets.newHashSet(); private final List<ILanguage> languages = Lists.newArrayList(); @@ -76,6 +90,17 @@ public class DefaultLanguageProvider implements ILanguageProvider { } } + @Override + public synchronized Iterable<ILanguage> getLanguages(ILanguageService languageService, ModelSet modelSet) { + if (!matchContentType(modelSet.getResources())) { + return Collections.emptyList(); + } else { + // If any of my content-types matches, then all of my languages are present + resolveProxies(); + return Collections.unmodifiableList(languages); + } + } + void addContentType(String id) { IContentType contentType = Platform.getContentTypeManager().getContentType(id); if (contentType == null) { @@ -176,6 +201,73 @@ public class DefaultLanguageProvider implements ILanguageProvider { return result; } + private boolean matchContentType(Collection<? extends Resource> resources) { + boolean result = false; + Set<EPackage> packagesSeen = new HashSet<>(); + + out: for (Resource next : resources) { + String filename = next.getURI().lastSegment(); + + // TODO: Is it really sufficient to check only the roots? Other content could be contained + for (EObject root : next.getContents()) { + EPackage ePackage = root.eClass().getEPackage(); + if (packagesSeen.add(ePackage)) { + IContentType contentType = getContentType(ePackage, filename); + if (contentType != null) { + for (IContentType type : contentTypes) { + if (contentType.isKindOf(type)) { + result = true; + break out; + } + } + } + } + } + } + + return result; + } + + /** + * We don't actually have a mapping in EMF between packages and content-types, + * so we infer it by feeding the platform's content-type system some fake inputs, + * relaying on the standard EMF pattern of using XML namespace declarations to + * determine content-type. + * + * @param ePackage + * the package to infer the content-type + * @param filename + * used as a hint to the platform's content-type manager + * + * @return the content-type or a placeholder for unknown content (never {@code null}) + */ + private IContentType getContentType(EPackage ePackage, String filename) { + return CONTENT_TYPES.computeIfAbsent(ePackage, package_ -> { + IContentType contentType = null; + + StringReader xmiString = createStringReader(ePackage); + try { + IContentDescription desc = Platform.getContentTypeManager().getDescriptionFor(xmiString, filename, null); + contentType = (desc == null) ? null : desc.getContentType(); + } catch (IOException e) { + // Not our content type, I guess + } + + if (contentType == null) { + contentType = NULL_CONTENT_TYPE; + } + + return contentType; + }); + } + + private StringReader createStringReader(EPackage ePackage) { + String xmi = String.format( + "<?xml version=\"1.0\" encoding=\"UTF-8\"?><xmi:XMI xmlns:xmi=\"%s\" xmlns:content=\"%s\"/>", //$NON-NLS-1$ + XMIResource.XMI_2_1_URI, ePackage.getNsURI()); + return new StringReader(xmi); + } + // // Nested types // diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java index ce61df60eb6..db81831c5dc 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java @@ -30,6 +30,7 @@ import org.eclipse.papyrus.infra.core.language.ILanguageProvider; import org.eclipse.papyrus.infra.core.language.ILanguageService; import org.eclipse.papyrus.infra.core.language.Language; import org.eclipse.papyrus.infra.core.language.Version; +import org.eclipse.papyrus.infra.core.resource.ModelSet; import com.google.common.base.Strings; import com.google.common.collect.Lists; @@ -207,6 +208,11 @@ public class LanguageProviderRegistry implements ILanguageProvider.Registry { return getInstance().getLanguages(languageService, modelURI, uriHasFileExtension); } + @Override + public java.lang.Iterable<ILanguage> getLanguages(ILanguageService languageService, ModelSet modelSet) { + return getInstance().getLanguages(languageService, modelSet); + } + ILanguageProvider createDefaultProvider() throws CoreException { DefaultLanguageProvider result = new DefaultLanguageProvider(); diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java index 0fca9314071..631995734d1 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2015 Christian W. Damus and others. + * Copyright (c) 2015, 2016 Christian W. Damus and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -24,6 +24,7 @@ import org.eclipse.papyrus.infra.core.language.ILanguageChangeListener; import org.eclipse.papyrus.infra.core.language.ILanguageProvider; import org.eclipse.papyrus.infra.core.language.ILanguageService; import org.eclipse.papyrus.infra.core.language.LanguageChangeEvent; +import org.eclipse.papyrus.infra.core.resource.ModelSet; import org.eclipse.papyrus.infra.core.services.IService; import org.eclipse.papyrus.infra.core.services.ServiceException; import org.eclipse.papyrus.infra.core.services.ServicesRegistry; @@ -60,6 +61,17 @@ public class LanguageService extends PlatformObject implements ILanguageService, } @Override + public Set<ILanguage> getLanguages(ModelSet modelSet) { + Set<ILanguage> result = Sets.newHashSet(); + + for (ILanguageProvider next : languageProviders) { + Iterables.addAll(result, next.getLanguages(this, modelSet)); + } + + return result; + } + + @Override public void addLanguageChangeListener(ILanguageChangeListener listener) { languageListeners.addIfAbsent(listener); } diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java index f1e2b3eacdd..76388ec7953 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2015 Christian W. Damus and others. + * Copyright (c) 2015, 2016 Christian W. Damus and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -41,6 +41,21 @@ public interface ILanguageProvider { */ Iterable<ILanguage> getLanguages(ILanguageService languageService, URI modelURI, boolean uriHasFileExtension); + /** + * Queries the languages that are instantiated in the specified model-set. This has the + * advantage over the {@linkplain #getLanguages(ILanguageService, URI, boolean) URI-based API} of being able + * to inspect resources that don't yet exist in the persistent store or are different from + * the latest persisted state. + * + * @param modelSet + * a {@link ModelSet} in which to find the instantiated languages + * + * @return the languages instantiated in the specified resource + */ + default Iterable<ILanguage> getLanguages(ILanguageService languageService, ModelSet modelSet) { + return getLanguages(languageService, modelSet.getURIWithoutExtension(), false); + } + // // Nested types // diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java index 7fa56614980..9ccc1effb22 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2015 Christian W. Damus and others. + * Copyright (c) 2015, 2016 Christian W. Damus and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -55,6 +55,19 @@ public interface ILanguageService extends IAdaptable, ILanguageChangeListener { Set<ILanguage> getLanguages(URI modelURI, boolean uriHasFileExtension); /** + * Queries the languages that are instantiated in the specified model-set. This has the + * advantage over the {@linkplain #getLanguages(URI, boolean) URI-based API} of being able + * to inspect resources that don't yet exist in the persistent store or are different from + * the latest persisted state. + * + * @param modelSet + * a {@link ModelSet} in which to find the instantiated languages + * + * @return the languages instantiated in the specified resource + */ + Set<ILanguage> getLanguages(ModelSet modelSet); + + /** * Adds a listener for language change notifications. Has no effect if the {@code listener} is already added. * * @param listener @@ -105,7 +118,7 @@ public interface ILanguageService extends IAdaptable, ILanguageChangeListener { // No language service? No language models result = Collections.emptyList(); } else { - result = service.getLanguages(modelSet.getURIWithoutExtension(), false).stream() + result = service.getLanguages(modelSet).stream() .map(l -> l.getModel(modelSet)) .filter(Objects::nonNull) .distinct() // Only unique models |