Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2016-01-28 15:06:14 +0000
committerGerrit Code Review @ Eclipse.org2016-01-29 16:52:21 +0000
commit8d95235d0b3d9c15ff4c824c8bda35ff63528813 (patch)
tree0143d78f33b5df0b4517e511105c29e8a40161fe
parent0e9c200139b99bb0825d52ff87ce962571c5bc6e (diff)
downloadorg.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.
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java92
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java6
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java14
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java17
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java17
-rw-r--r--tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/language/LanguageServiceTest.java67
6 files changed, 208 insertions, 5 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
diff --git a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/language/LanguageServiceTest.java b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/language/LanguageServiceTest.java
index d332faeb09f..3530a06d78e 100644
--- a/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/language/LanguageServiceTest.java
+++ b/tests/junit/plugins/core/org.eclipse.papyrus.infra.core.tests/test/org/eclipse/papyrus/infra/core/language/LanguageServiceTest.java
@@ -13,6 +13,7 @@
package org.eclipse.papyrus.infra.core.language;
+import static java.util.stream.StreamSupport.stream;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -20,17 +21,34 @@ import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import java.util.Collection;
-
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.papyrus.infra.core.Activator;
import org.eclipse.papyrus.infra.core.internal.language.LanguageModelRegistry;
+import org.eclipse.papyrus.infra.core.resource.IEMFModel;
import org.eclipse.papyrus.infra.core.resource.IModel;
+import org.eclipse.papyrus.infra.core.resource.ModelMultiException;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+import org.eclipse.papyrus.infra.core.services.ExtensionServicesRegistry;
import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServiceMultiException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.core.utils.TransactionHelper;
import org.eclipse.papyrus.junit.utils.resources.EcoreModel;
+import org.eclipse.papyrus.junit.utils.rules.HouseKeeper;
import org.eclipse.papyrus.junit.utils.rules.ModelSetFixture;
import org.eclipse.papyrus.junit.utils.rules.PluginResource;
import org.eclipse.papyrus.junit.utils.rules.ServiceRegistryModelSetFixture;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
/**
@@ -51,6 +69,9 @@ public class LanguageServiceTest {
private static IModel ecoreModel;
+ @Rule
+ public final HouseKeeper houseKeeper = new HouseKeeper();
+
public LanguageServiceTest() {
super();
}
@@ -106,6 +127,50 @@ public class LanguageServiceTest {
assertThat(models, hasItem(ecoreModel));
}
+ @Test
+ public void contentBasedLanguagesInNewModel() throws Exception {
+ ServicesRegistry services = houseKeeper.cleanUpLater(new ExtensionServicesRegistry(Activator.PLUGIN_ID), reg -> {
+ try {
+ reg.disposeRegistry();
+ } catch (ServiceMultiException e) {
+ // We expect these in the tests
+ }
+ });
+
+ try {
+ services.startRegistry();
+ } catch (ServiceMultiException e) {
+ // These are normal
+ }
+
+ ModelSet modelSet = services.getService(ModelSet.class);
+
+ IEMFModel ecore = new EcoreModel();
+ modelSet.registerModel(ecore);
+
+ URI uri = URI.createURI("platform:/resource/test/bogus.ecore", true);
+ EPackage ePackage = EcoreFactory.eINSTANCE.createEPackage();
+ Resource resource = modelSet.createResource(uri);
+
+ TransactionHelper.run(modelSet.getTransactionalEditingDomain(), () -> {
+ resource.getContents().add(ePackage);
+ });
+
+ try {
+ modelSet.loadModels(uri);
+ } catch (ModelMultiException e) {
+ // We expect this
+ }
+
+ List<EObject> semanticRoots = ILanguageService.getLanguageModels(modelSet).stream()
+ .filter(IEMFModel.class::isInstance)
+ .map(IEMFModel.class::cast)
+ .flatMap(m -> stream(m.getRootElements().spliterator(), false))
+ .collect(Collectors.toList());
+
+ assertThat(semanticRoots, hasItem(ePackage));
+ }
+
//
// Test framework
//

Back to the top