Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2015-06-04 13:33:56 +0000
committerCamille Letavernier2015-06-05 08:25:19 +0000
commitd2dc2fd8777bbd50aa0ac10845b156d64be5695d (patch)
tree7c39ae39f95ff2040c17b9225bee3e2201485fa8 /plugins/infra
parentd5706b93beee4419dc3634b24b6e6300441c9659 (diff)
downloadorg.eclipse.papyrus-d2dc2fd8777bbd50aa0ac10845b156d64be5695d.tar.gz
org.eclipse.papyrus-d2dc2fd8777bbd50aa0ac10845b156d64be5695d.tar.xz
org.eclipse.papyrus-d2dc2fd8777bbd50aa0ac10845b156d64be5695d.zip
Bug 468030: [Papyrus Core] Papyrus DI Model should support the notion of Language
https://bugs.eclipse.org/bugs/show_bug.cgi?id=468030 Implement a Language Service for configuration of the Papyrus ModelSet to support custom implementations of UML and other modeling languages. Includes: * language service providing languages before the ModelSet loads any resources * hooks for languages to configure and unconfigure a ModelSet * implementation of a language provider that maps applied profiles to languages * addition of a profile index service that provides the URIs of profiles applied to model resources without having to load them in a resource set * a stub of a UML-RT language with placeholder for configuration of the ModelSet * a standard UML language * implementation of the profile index service using an enhanced DecoratorModelIndex that now also index the normal profile applications in user models Papyrus Service Registry changes: * fix the explicit starting of lazy services * new AbstractServiceUtils API for requesting optional or defaultable services Also includes the very barest of JUnit tests covering: * the new profile index service * using the language service to query content-type-based languages * UML languages: UML models have the UML language, itself, and proper matching and installation of profile languages Change-Id: I9d5175cfbefbe40864f04ea4215e18556e3739df Reviewed-on: https://git.eclipse.org/r/49152 Tested-by: Hudson CI Reviewed-by: Camille Letavernier <camille.letavernier@cea.fr>
Diffstat (limited to 'plugins/infra')
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF1
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml10
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd219
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/ModelSetServiceFactory.java17
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java203
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java260
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java110
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguage.java83
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java53
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java64
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Language.java101
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Version.java221
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java39
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/BadStateException.java5
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceException.java37
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceMultiException.java20
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServicesRegistry.java28
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/internal/LazyStartupEntry.java5
-rw-r--r--plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java32
19 files changed, 1482 insertions, 26 deletions
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF
index 850c6ec0719..44505d47c1a 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF
@@ -8,6 +8,7 @@ Export-Package: org.eclipse.papyrus.infra.core,
org.eclipse.papyrus.infra.core.extension,
org.eclipse.papyrus.infra.core.extension.commands,
org.eclipse.papyrus.infra.core.extension.diagrameditor,
+ org.eclipse.papyrus.infra.core.language,
org.eclipse.papyrus.infra.core.lifecycleevents,
org.eclipse.papyrus.infra.core.listenerservice,
org.eclipse.papyrus.infra.core.markers,
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml
index 1ec4a12a9ec..7a1ae21bd95 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml
@@ -8,6 +8,7 @@
<extension-point id="model" name="plugin.xml.ModelName" schema="schema/model.exsd"/>
<extension-point id="transactionalEditingDomainProvider" name="transactionalEditingDomainProvider" schema="schema/transactionalEditingDomainProvider.exsd"/>
<extension-point id="sashModelProvider" name="Sash Model Providers" schema="schema/sashModelProvider.exsd"/>
+ <extension-point id="language" name="Modeling Language" schema="schema/language.exsd"/>
<extension
point="org.eclipse.ui.menus">
@@ -394,5 +395,14 @@
type="org.eclipse.papyrus.infra.core.services.IService">
</adapter>
</factory>
+ </extension>
+ <extension
+ point="org.eclipse.papyrus.infra.core.service">
+ <service
+ classname="org.eclipse.papyrus.infra.core.internal.language.LanguageService"
+ id="org.eclipse.papyrus.infra.core.language.ILanguageService"
+ priority="10"
+ startKind="startup">
+ </service>
</extension>
</plugin>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd b/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd
new file mode 100644
index 00000000000..516be1d8310
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd
@@ -0,0 +1,219 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.papyrus.infra.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.papyrus.infra.core" id="language" name="Modeling Language"/>
+ </appInfo>
+ <documentation>
+ Registration of Modeling Languages in the Papyrus Language Service.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="provider" minOccurs="1" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="provider">
+ <annotation>
+ <documentation>
+ Registration of a Papryus modeling language provider.
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="class" minOccurs="0" maxOccurs="1"/>
+ <choice minOccurs="0" maxOccurs="unbounded">
+ <element ref="content-type"/>
+ <element ref="language"/>
+ </choice>
+ </sequence>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+ The name of a language provider class. If not specified, then nested elements are required to specify a generic provider that matches URIs to languages.
+The class may also be specified by the nested &lt;tt&gt;&amp;lt;class&amp;gt;&lt;/tt&gt; element, especially if it is parameterized.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.papyrus.infra.core.language.ILanguageProvider"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="content-type">
+ <annotation>
+ <documentation>
+ Matches languages by resource content type.
+Ignored if the &lt;tt&gt;class&lt;/tt&gt; attribute of the &lt;tt&gt;&amp;lt;provider&amp;gt;&lt;/tt&gt; element is specified.
+Required if the &lt;tt&gt;&amp;lt;language&amp;gt;&lt;/tt&gt; element is specified.
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ References a resource content type for which to provider a language.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="identifier" basedOn="org.eclipse.core.contenttype.contentTypes/content-type/@id"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="language">
+ <annotation>
+ <documentation>
+ Specifies a language matched by resource content type.
+Ignored if the &lt;tt&gt;class&lt;/tt&gt; attribute or &lt;tt&gt;&amp;lt;implementation&amp;gt;&lt;/tt&gt; element of the &lt;tt&gt;&amp;lt;provider&amp;gt;&lt;/tt&gt; element is specified.
+Required if the &lt;tt&gt;&amp;lt;contentType&amp;gt;&lt;/tt&gt; element is specified.
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+ The name of a language implementation class. If not specified, then a default implementation is supplied based on the other attributes that simply does no ModelSet configuration.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.papyrus.infra.core.language.ILanguage"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+ An unique identifier for the language.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="version" type="string" use="required">
+ <annotation>
+ <documentation>
+ The version number of the language.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ The localized user-presentable name of the language.
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="class">
+ <annotation>
+ <documentation>
+ Specification of a parameterized executable extension. If specified, then the &lt;tt&gt;class&lt;/tt&gt; attribute is ignored.
+ </documentation>
+ </annotation>
+ <complexType>
+ <sequence>
+ <element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string">
+ <annotation>
+ <documentation>
+ The name of a language provider class. Parameters may be specified to configure the instance as appropriate to its API specification.
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java" basedOn=":org.eclipse.papyrus.infra.core.language.ILanguageProvider"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <annotation>
+ <documentation>
+ A name/value pair to configure an implementation of the language provider.
+ </documentation>
+ </annotation>
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ An unique parameter name, as specified by the API of the implementation class.
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="value" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 1.1.0
+ </documentation>
+ </annotation>
+
+
+
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2015 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
+which accompanies this distribution, and is available at
+http://www.eclipse.org/legal/epl-v10.html
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/ModelSetServiceFactory.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/ModelSetServiceFactory.java
index ae76099d3b4..185172c1f33 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/ModelSetServiceFactory.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/editor/ModelSetServiceFactory.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011, 2014 CEA LIST and others.
+ * Copyright (c) 2011, 2015 CEA LIST, Christian W. Damus, and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -10,11 +10,13 @@
* Contributors:
* CEA LIST - Initial API and implementation
* Christian W. Damus (CEA) - bug 431953 (pre-requisite refactoring of ModelSet service start-up)
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.editor;
import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.papyrus.infra.core.language.ILanguageService;
import org.eclipse.papyrus.infra.core.resource.ModelSet;
import org.eclipse.papyrus.infra.core.services.IServiceFactory;
import org.eclipse.papyrus.infra.core.services.ModelSetServiceAdapter;
@@ -46,15 +48,14 @@ public class ModelSetServiceFactory implements IServiceFactory {
this.serviceRegistry = servicesRegistry;
}
- /**
- *
- * @see org.eclipse.papyrus.infra.core.services.IService#startService()
- *
- * @throws ServiceException
- */
@Override
public void startService() throws ServiceException {
- // Pass
+ // If the language service is available (optional dependency), the ModelSet will want to use it, so start it
+ try {
+ serviceRegistry.startServicesByClassKeys(ILanguageService.class);
+ } catch (ServiceException e) {
+ // It's okay: the language service is optional
+ }
}
/**
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
new file mode 100644
index 00000000000..11f745b52d1
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/DefaultLanguageProvider.java
@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.internal.language;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+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.IContentType;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.ContentHandler;
+import org.eclipse.emf.ecore.resource.URIConverter;
+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 com.google.common.collect.Lists;
+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 final Set<IContentType> contentTypes = Sets.newHashSet();
+
+ private final List<ILanguage> languages = Lists.newArrayList();
+
+ private List<LanguageProxy> languageProxies;
+
+ public DefaultLanguageProvider() {
+ super();
+ }
+
+ @Override
+ public synchronized Iterable<ILanguage> getLanguages(ILanguageService languageService, URI modelURI, boolean uriHasFileExtension) {
+ Set<URI> allURIs;
+
+ try {
+ allURIs = resolveURIs(modelURI, uriHasFileExtension);
+ } catch (CoreException e) {
+ Activator.log.log(e.getStatus());
+
+ // Can't match our content types against anything
+ return Collections.emptyList();
+ }
+
+ if (!matchContentType(allURIs)) {
+ 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) {
+ Activator.log.warn("No such content-type in language provider extension: " + id); //$NON-NLS-1$
+ } else {
+ contentTypes.add(contentType);
+ }
+ }
+
+ synchronized void addLanguage(ILanguage language) {
+ this.languages.add(language);
+ }
+
+ synchronized void addLanguage(IConfigurationElement config, String attribute) {
+ if (languageProxies == null) {
+ languageProxies = Lists.newArrayList();
+ }
+
+ languageProxies.add(new LanguageProxy(config, attribute));
+ }
+
+ private void resolveProxies() {
+ if (languageProxies != null) {
+ try {
+ for (LanguageProxy next : languageProxies) {
+ ILanguage resolved = next.resolve();
+ if (resolved != null) {
+ addLanguage(resolved);
+ }
+ }
+ } finally {
+ languageProxies = null;
+ }
+ }
+ }
+
+ private Set<URI> resolveURIs(URI modelURI, boolean uriHasFileExtension) throws CoreException {
+ // If given an extension, just take that
+ if (uriHasFileExtension) {
+ return Collections.singleton(modelURI);
+ }
+
+ Set<URI> result = Sets.newHashSet();
+
+ if (modelURI.isPlatformResource()) {
+ String baseName = modelURI.lastSegment();
+ IPath path = new Path(modelURI.trimSegments(1).toPlatformString(true));
+ IContainer container = (path.segmentCount() > 1) ? ResourcesPlugin.getWorkspace().getRoot().getFolder(path) : ResourcesPlugin.getWorkspace().getRoot().getProject(path.lastSegment());
+ for (IResource next : container.members()) {
+ if (next.getType() == IResource.FILE) {
+ IPath nextPath = next.getFullPath();
+ String name = nextPath.removeFileExtension().lastSegment();
+ if (name.equals(baseName)) {
+ result.add(URI.createPlatformResourceURI(nextPath.toString(), true));
+ }
+ }
+ }
+ } else if (modelURI.isFile()) {
+ String baseName = modelURI.lastSegment();
+ File directory = new File(modelURI.trimSegments(1).toFileString());
+ for (File next : directory.listFiles()) {
+ if (next.isFile()) {
+ IPath nextPath = new Path(directory.getPath()).append(next.getName());
+ String name = nextPath.removeFileExtension().lastSegment();
+ if (name.equals(baseName)) {
+ result.add(URI.createPlatformResourceURI(nextPath.toString(), true));
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private boolean matchContentType(Set<URI> uris) {
+ boolean result = false;
+
+ out: for (URI next : uris) {
+ try {
+ Map<String, ?> desc = URIConverter.INSTANCE.contentDescription(next, null);
+ String contentTypeID = (String) desc.get(ContentHandler.CONTENT_TYPE_PROPERTY);
+ if (contentTypeID != null) {
+ IContentType contentType = Platform.getContentTypeManager().getContentType(contentTypeID);
+ if (contentType != null) {
+ for (IContentType type : contentTypes) {
+ if (contentType.isKindOf(type)) {
+ result = true;
+ break out;
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ // Not our content type, I guess
+ }
+ }
+
+ return result;
+ }
+
+ //
+ // Nested types
+ //
+
+ private static class LanguageProxy {
+ private final IConfigurationElement config;
+ private final String attribute;
+
+ LanguageProxy(IConfigurationElement config, String attribute) {
+ super();
+
+ this.config = config;
+ this.attribute = attribute;
+ }
+
+ ILanguage resolve() {
+ try {
+ return (ILanguage) config.createExecutableExtension(attribute);
+ } catch (Exception e) {
+ Activator.log.error("Failed to instantiate language in provider extension from contributor " + config.getContributor().getName(), e); //$NON-NLS-1$
+ return null;
+ }
+ }
+ }
+}
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
new file mode 100644
index 00000000000..ce61df60eb6
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageProviderRegistry.java
@@ -0,0 +1,260 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.internal.language;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.plugin.RegistryReader;
+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.language.Language;
+import org.eclipse.papyrus.infra.core.language.Version;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+
+/**
+ * A registry of contributed {@link ILanguageProvider} implementations plugged in on my extension point.
+ */
+public class LanguageProviderRegistry implements ILanguageProvider.Registry {
+
+ private static final String EXT_POINT = "language"; //$NON-NLS-1$
+
+ private static final ILanguageProvider NULL_PROVIDER = new ILanguageProvider() {
+
+ @Override
+ public Iterable<ILanguage> getLanguages(ILanguageService languageService, URI modelURI, boolean uriHasFileExtension) {
+ return Collections.emptyList();
+ }
+ };
+
+ private final List<ILanguageProvider> providers = Lists.newArrayListWithExpectedSize(2);
+
+ private boolean needPrune;
+
+ public LanguageProviderRegistry() {
+ super();
+
+ new MyRegistryReader().readRegistry();
+ }
+
+ /**
+ * Prune out any null providers (failed to initialize) and replace
+ * descriptors that have been instantiated by their instances, to avoid
+ * delegation.
+ */
+ private void prune() {
+ if (needPrune) {
+ needPrune = false;
+ for (ListIterator<ILanguageProvider> iter = providers.listIterator(); iter.hasNext();) {
+
+ ILanguageProvider next = iter.next();
+ if (next == NULL_PROVIDER) {
+ iter.remove(); // Don't need this, any more
+ } else if (next instanceof MyRegistryReader.Descriptor) {
+ MyRegistryReader.Descriptor desc = (MyRegistryReader.Descriptor) next;
+ if (desc.instance != null) {
+ iter.set(desc.instance);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public Collection<ILanguageProvider> getProviders() {
+ prune();
+ return Collections.unmodifiableCollection(providers);
+ }
+
+ private void removeProvider(String className) {
+ synchronized (providers) {
+ for (Iterator<ILanguageProvider> iter = providers.iterator(); iter.hasNext();) {
+
+ ILanguageProvider next = iter.next();
+ if (next instanceof MyRegistryReader.Descriptor) {
+ MyRegistryReader.Descriptor desc = (MyRegistryReader.Descriptor) next;
+ if (className.equals(desc.getClassName())) {
+ iter.remove();
+ break;
+ }
+ } else if (className.equals(next.getClass().getName())) {
+ iter.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ private class MyRegistryReader extends RegistryReader {
+
+ private static final String A_CLASS = "class"; //$NON-NLS-1$
+
+ private static final String E_PROVIDER = "provider"; //$NON-NLS-1$
+
+ private static final String E_CONTENT_TYPE = "content-type"; //$NON-NLS-1$
+ private static final String E_LANGUAGE = "language"; //$NON-NLS-1$
+ private static final String A_ID = "id"; //$NON-NLS-1$
+ private static final String A_VERSION = "version"; //$NON-NLS-1$
+ private static final String A_NAME = "name"; //$NON-NLS-1$
+
+ private Descriptor currentDescriptor;
+
+ MyRegistryReader() {
+ super(Platform.getExtensionRegistry(), Activator.PLUGIN_ID, EXT_POINT);
+ }
+
+ @Override
+ protected boolean readElement(IConfigurationElement element, boolean add) {
+ return add ? handleAdd(element) : handleRemove(element);
+ }
+
+ private boolean handleAdd(IConfigurationElement element) {
+ boolean result = true;
+
+ if (E_PROVIDER.equals(element.getName())) {
+ currentDescriptor = new Descriptor(element, A_CLASS);
+ providers.add(currentDescriptor);
+ }
+
+ return result;
+ }
+
+ private boolean handleRemove(IConfigurationElement element) {
+ boolean result = true;
+
+ if (E_PROVIDER.equals(element.getName())) {
+ String className = getClassName(element, A_CLASS);
+ if (className != null) {
+ removeProvider(className);
+ }
+ }
+
+ return result;
+ }
+
+ String getClassName(IConfigurationElement provider, String attributeName) {
+ String result = provider.getAttribute(attributeName);
+ if (result == null) {
+ IConfigurationElement[] classes = provider.getChildren(attributeName);
+ if (classes.length > 0) { // Assume a single occurrence
+ result = classes[0].getAttribute("class"); //$NON-NLS-1$ It's always 'class' attribute of a nested element
+ }
+ }
+ return result;
+ }
+
+ private class Descriptor extends PluginClassDescriptor implements ILanguageProvider {
+
+ private ILanguageProvider instance;
+
+ Descriptor(IConfigurationElement element, String attributeName) {
+ super(element, attributeName);
+ }
+
+ String getClassName() {
+ return MyRegistryReader.this.getClassName(element, attributeName);
+ }
+
+ ILanguageProvider getInstance() {
+ if (instance == null) {
+ try {
+ String className = getClassName();
+ if (className == null) {
+ // It's the default implementation
+ instance = createDefaultProvider();
+ } else {
+ instance = (ILanguageProvider) createInstance();
+ }
+ } catch (Exception e) {
+ Activator.log.error("Failed to instantiate language provider extension.", e);
+ instance = NULL_PROVIDER;
+ }
+
+ needPrune = true;
+ }
+
+ return instance;
+ }
+
+ @Override
+ public Iterable<ILanguage> getLanguages(ILanguageService languageService, URI modelURI, boolean uriHasFileExtension) {
+ return getInstance().getLanguages(languageService, modelURI, uriHasFileExtension);
+ }
+
+ ILanguageProvider createDefaultProvider() throws CoreException {
+ DefaultLanguageProvider result = new DefaultLanguageProvider();
+
+ for (IConfigurationElement next : element.getChildren()) {
+ if (E_CONTENT_TYPE.equals(next.getName())) {
+ addContentType(result, next);
+ } else if (E_LANGUAGE.equals(next.getName())) {
+ addLanguage(result, next);
+ }
+ }
+
+ return result;
+ }
+
+ private void addContentType(DefaultLanguageProvider provider, IConfigurationElement contentType) {
+ String id = contentType.getAttribute(A_ID);
+ if (Strings.isNullOrEmpty(id)) {
+ logMissingAttribute(contentType, A_ID);
+ } else {
+ provider.addContentType(id);
+ }
+ }
+
+ private void addLanguage(DefaultLanguageProvider provider, IConfigurationElement language) throws CoreException {
+ String className = language.getAttribute(A_CLASS);
+ if (Strings.isNullOrEmpty(className)) {
+ className = Language.class.getName();
+ }
+
+ // Maybe the plug-in specifies the default Language class explicitly
+ if (!className.equals(Language.class.getName())) {
+ provider.addLanguage(language, A_CLASS);
+ } else {
+ String id = language.getAttribute(A_ID);
+ if (Strings.isNullOrEmpty(id)) {
+ logMissingAttribute(language, A_ID);
+ }
+ String version = language.getAttribute(A_VERSION);
+ if (Strings.isNullOrEmpty(version)) {
+ logMissingAttribute(language, A_VERSION);
+ }
+ String name = language.getAttribute(A_NAME);
+ if (Strings.isNullOrEmpty(name)) {
+ logMissingAttribute(language, A_NAME);
+ }
+ provider.addLanguage(new Language(id, new Version(version), name));
+ }
+ }
+ }
+ }
+}
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
new file mode 100644
index 00000000000..c8f91883549
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/language/LanguageService.java
@@ -0,0 +1,110 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.internal.language;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.eclipse.core.runtime.PlatformObject;
+import org.eclipse.emf.common.util.URI;
+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.services.IService;
+import org.eclipse.papyrus.infra.core.services.ServiceException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+/**
+ * The implementation of the Papyrus Modeling Language Service.
+ */
+public class LanguageService extends PlatformObject implements ILanguageService, IService {
+
+ private final CopyOnWriteArrayList<ILanguageProvider> languageProviders = Lists.newCopyOnWriteArrayList();
+
+ private ServicesRegistry registry;
+
+ public LanguageService() {
+ super();
+ }
+
+ @Override
+ public Set<ILanguage> getLanguages(URI modelURI, boolean uriHasFileExtension) {
+ Set<ILanguage> result = Sets.newHashSet();
+
+ for (ILanguageProvider next : languageProviders) {
+ Iterables.addAll(result, next.getLanguages(this, modelURI, uriHasFileExtension));
+ }
+
+ return result;
+ }
+
+ @Override
+ public void addLanguageProvider(ILanguageProvider provider) {
+ languageProviders.addIfAbsent(provider);
+ }
+
+ @Override
+ public void removeLanguageProvider(ILanguageProvider provider) {
+ if (provider != null) {
+ languageProviders.remove(provider);
+ }
+ }
+
+ //
+ // Adaptable API
+ //
+
+
+ /**
+ * Adaptation to other services in the registry takes precedence over registered adapter factories.
+ */
+ @Override
+ public <T> T getAdapter(Class<T> adapter) {
+ T result;
+
+ if (adapter.isInstance(registry)) {
+ result = adapter.cast(registry);
+ } else {
+ result = (registry == null) ? null : ServiceUtils.getInstance().getService(adapter, registry, null);
+ }
+
+ return (result != null) ? result : super.getAdapter(adapter);
+ }
+
+ //
+ // Service lifecycle API
+ //
+
+ @Override
+ public void init(ServicesRegistry servicesRegistry) throws ServiceException {
+ this.registry = servicesRegistry;
+ }
+
+ @Override
+ public void startService() throws ServiceException {
+ languageProviders.addAllAbsent(ILanguageProvider.Registry.INSTANCE.getProviders());
+ }
+
+ @Override
+ public void disposeService() throws ServiceException {
+ languageProviders.clear();
+ registry = null;
+ }
+
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguage.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguage.java
new file mode 100644
index 00000000000..86ce58cfa22
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguage.java
@@ -0,0 +1,83 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.language;
+
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+
+/**
+ * <p>
+ * Protocol for the definition of a <em>Modeling Language</em> as it is instantiated
+ * in a Papyrus model. Although one may distinguish between primary or core languages
+ * such as UML, Ecore, MOF, etc. as implemented by OMG metamodels and derivative
+ * languages such as UML profiles, specializations, or package merges, this API does
+ * not make any such distinction.
+ * </p>
+ * <p>
+ * Any number of languages may be instantiated in a Papyrus model, but as long as the
+ * {@linkplain ILanguageService Language Service} is present, there is always at least
+ * one language.
+ * </p>
+ */
+public interface ILanguage {
+
+ /**
+ * Obtains an unique identifier of the language, irrespective of version.
+ *
+ * @return
+ */
+ String getID();
+
+ /**
+ * Obtains the version of the language. All versions of a language have the same {@linkplain #getID() identifier}.
+ *
+ * @return the version. Never {@code null}
+ */
+ Version getVersion();
+
+ /**
+ * Obtains the user-presentable (translated for the current locale) name of the language, not including any {@linkplain #getVersion() version number}.
+ *
+ * @return my name. Never {@code null} or empty
+ */
+ String getName();
+
+ /**
+ * Installs me on a model-set. This performs whatever registrations, listener attachments, etc. that may be necessary
+ * for the proper functioning of my language in the Papyrus context.
+ *
+ * @param modelSet
+ * a model-set to configure
+ */
+ void install(ModelSet modelSet);
+
+ /**
+ * Uninstalls me from a model-set. This undoes whatever the {@linkplain #install(ModelSet) installation} did.
+ *
+ * @param modelSet
+ * a model-set to unconfigure
+ */
+ void uninstall(ModelSet modelSet);
+
+ /**
+ * Two languages are equal if they have the same {@linkplain #getID() identifier} and {@linkplain #getVersion() version}.
+ */
+ @Override
+ boolean equals(Object obj);
+
+ /**
+ * Languages are hashed by {@linkplain #getID() identifier} and {@linkplain #getVersion() version}.
+ */
+ @Override
+ int hashCode();
+}
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
new file mode 100644
index 00000000000..f1e2b3eacdd
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageProvider.java
@@ -0,0 +1,53 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.language;
+
+import java.util.Collection;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.papyrus.infra.core.internal.language.LanguageProviderRegistry;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+
+/**
+ * A provider of {@link ILanguage}s to the {@linkplain ILanguageService service}.
+ * Providers may be registered dynamically using the {@linkplain ILanguageService#addLanguageProvider(ILanguageProvider) service API}
+ * or may be registered statically on the <tt>org.eclipse.papyrus.infra.core.language</tt> extension point.
+ */
+public interface ILanguageProvider {
+ /**
+ * Queries the languages that are instantiated in the specified model resource.
+ *
+ * @param languageService
+ * the language service that is requesting languages
+ * @param modelURI
+ * the URI of a model resource for which the service wants to determine languages
+ * @param uriHasFileExtension
+ * whether the {@code modelURI} has a file extension. For example, if the {@link ModelSet}
+ * is requesting languages, then the URI typically does not have an extension because a model-set comprising several
+ * resources that all have the same base file name
+ *
+ * @return the languages instantiated in the specified resource. May be empty if this provider does not recognize any languages in the resource
+ */
+ Iterable<ILanguage> getLanguages(ILanguageService languageService, URI modelURI, boolean uriHasFileExtension);
+
+ //
+ // Nested types
+ //
+
+ interface Registry {
+ Registry INSTANCE = new LanguageProviderRegistry();
+
+ Collection<ILanguageProvider> getProviders();
+ }
+}
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
new file mode 100644
index 00000000000..fbf34748f84
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/ILanguageService.java
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.language;
+
+import java.util.Set;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+
+/**
+ * <p>
+ * The Language Service provides information the {@linkplain ILanguage Modeling Languages} that are
+ * instantiated in a Papyrus model.
+ * </p>
+ * <p>
+ * In my capacity as an {@link IAdaptable}, I provide other services from the registry in which I
+ * am provided as adapters, in addition to whatever other adapters may be contributed for me.
+ * </p>
+ *
+ * @see ILanguage
+ */
+public interface ILanguageService extends IAdaptable {
+ /**
+ * Queries the languages that are instantiated in the specified model resource.
+ *
+ * @param modelURI
+ * the URI of a model resource, such as might be used to load a {@link ModelSet}
+ * @param uriHasFileExtension
+ * whether the {@code modelURI} has a file extension. For example, if the {@link ModelSet}
+ * is requesting languages, then the URI typically does not have an extension because a model-set comprising several
+ * resources that all have the same base file name
+ *
+ * @return the languages instantiated in the specified resource
+ */
+ Set<ILanguage> getLanguages(URI modelURI, boolean uriHasFileExtension);
+
+ /**
+ * Registers a language {@code provider}. Has no effect if the given {@code provider} is already registered.
+ *
+ * @param provider
+ * a language provider to add (may not be {@code null})
+ */
+ void addLanguageProvider(ILanguageProvider provider);
+
+ /**
+ * Unregisters a language {@code provider}. Has no effect if the given {@code provider} is not currently registered.
+ *
+ * @param provider
+ * a language provider to remove (may be {@code null}, which has no effect)
+ */
+ void removeLanguageProvider(ILanguageProvider provider);
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Language.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Language.java
new file mode 100644
index 00000000000..280ba37eaee
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Language.java
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.language;
+
+import org.eclipse.papyrus.infra.core.resource.ModelSet;
+
+/**
+ * Default implementation of a language. This implementation does nothing when {@linkplain #install(ModelSet) installed} on a model-set.
+ */
+public class Language implements ILanguage {
+ private final String id;
+ private final Version version;
+ private final String name;
+
+ public Language(String id, Version version, String name) {
+ super();
+
+ this.id = id;
+ this.version = version;
+ this.name = name;
+ }
+
+ @Override
+ public String getID() {
+ return id;
+ }
+
+ @Override
+ public Version getVersion() {
+ return version;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public void install(ModelSet modelSet) {
+ // Pass
+ }
+
+ @Override
+ public void uninstall(ModelSet modelSet) {
+ // Pass
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((id == null) ? 0 : id.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof ILanguage)) {
+ return false;
+ }
+ ILanguage other = (ILanguage) obj;
+ if (id == null) {
+ if (other.getID() != null) {
+ return false;
+ }
+ } else if (!id.equals(other.getID())) {
+ return false;
+ }
+ if (version == null) {
+ if (other.getVersion() != null) {
+ return false;
+ }
+ } else if (!version.equals(other.getVersion())) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s version %s", getName(), getVersion()); //$NON-NLS-1$
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Version.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Version.java
new file mode 100644
index 00000000000..9c232e30512
--- /dev/null
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/language/Version.java
@@ -0,0 +1,221 @@
+/*****************************************************************************
+ * Copyright (c) 2015 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
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.core.language;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.google.common.base.Strings;
+
+/**
+ * A {@linkplain ILanguage language} version.
+ */
+public final class Version implements Comparable<Version> {
+ private final Pattern VERSION_PATTERN = Pattern.compile("^(-?\\d+)(\\.\\d+)?(\\.\\d+)?(\\.[A-Za-z0-9_+\\-]+)?$"); //$NON-NLS-1$
+
+ public static final Version ZERO = new Version(0, 0, 0, null);
+ public static final Version ONE = new Version(1, 0, 0, null);
+
+ private final int major;
+ private final int minor;
+ private final int service;
+ private final String qualifier;
+
+ /**
+ * Initializes me.
+ *
+ * @param major
+ * my major increment. Others are zero
+ */
+ public Version(int major) {
+ this(major, 0, 0, null);
+ }
+
+ /**
+ * Initializes me.
+ *
+ * @param major
+ * my major increment
+ * @param minor
+ * my minor increment. Must be a whole number
+ *
+ * @throws IllegalArgumentException
+ * if the {@code minor} segment is negative
+ */
+ public Version(int major, int minor) {
+ this(major, minor, 0, null);
+ }
+
+ /**
+ * Initializes me.
+ *
+ * @param major
+ * my major increment
+ * @param minor
+ * my minor increment. Must be a whole number
+ * @param service
+ * my service increment. Must be a whole number
+ *
+ * @throws IllegalArgumentException
+ * if any {@code minor}/{@code service} segment is negative
+ */
+ public Version(int major, int minor, int service) {
+ this(major, minor, service, null);
+ }
+
+ /**
+ * Initializes me.
+ *
+ * @param major
+ * my major increment
+ * @param minor
+ * my minor increment. Must be a whole number
+ * @param service
+ * my service increment. Must be a whole number
+ * @param qualifier
+ * my qualifier (may be {@code null} if not needed, but not empty)
+ *
+ * @throws IllegalArgumentException
+ * if any {@code minor}/{@code service} segment is negative or if the qualifier is the empty string
+ */
+ public Version(int major, int minor, int service, String qualifier) {
+ super();
+
+ if (minor < 0) {
+ throw new IllegalArgumentException("Negative minor segment"); //$NON-NLS-1$
+ }
+ if (service < 0) {
+ throw new IllegalArgumentException("Negative service segment"); //$NON-NLS-1$
+ }
+ if ("".equals(qualifier)) { //$NON-NLS-1$
+ throw new IllegalArgumentException("Empty qualifier; should be null"); //$NON-NLS-1$
+ }
+
+ this.major = major;
+ this.minor = minor;
+ this.service = service;
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * Initializes me from my string representation.
+ *
+ * @param versionSpec
+ * my string representation
+ *
+ * @throws IllegalArgumentException
+ * if the string representation has errors in any segment
+ */
+ public Version(String versionSpec) {
+ super();
+
+ Matcher m = VERSION_PATTERN.matcher(versionSpec);
+ if (!m.matches()) {
+ throw new IllegalArgumentException("Invalid version specification: " + versionSpec); //$NON-NLS-1$
+ }
+
+ this.major = Integer.parseInt(m.group(1));
+ this.minor = m.group(2) == null ? 0 : Integer.parseInt(m.group(2).substring(1));
+ this.service = m.group(3) == null ? 0 : Integer.parseInt(m.group(3).substring(1));
+ this.qualifier = m.group(3) == null ? null : m.group(3).substring(1);
+ }
+
+ public int major() {
+ return major;
+ }
+
+ public int minor() {
+ return minor;
+ }
+
+ public int service() {
+ return service;
+ }
+
+ public String qualifier() {
+ return qualifier;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + major;
+ result = prime * result + minor;
+ result = prime * result + ((qualifier == null) ? 0 : qualifier.hashCode());
+ result = prime * result + service;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return (qualifier == null)
+ ? String.format("%s.%s.%s", major, minor, service) //$NON-NLS-1$
+ : String.format("%s.%s.%s.%s", major, minor, service, qualifier); //$NON-NLS-1$
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Version)) {
+ return false;
+ }
+ Version other = (Version) obj;
+ if (major != other.major) {
+ return false;
+ }
+ if (minor != other.minor) {
+ return false;
+ }
+ if (qualifier == null) {
+ if (other.qualifier != null) {
+ return false;
+ }
+ } else if (!qualifier.equals(other.qualifier)) {
+ return false;
+ }
+ if (service != other.service) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int compareTo(Version o) {
+ int result = o.major - this.major;
+ if (result == 0) {
+ result = o.minor - this.minor;
+ if (result == 0) {
+ result = o.service - this.service;
+ if (result == 0) {
+ result = Strings.nullToEmpty(o.qualifier).compareTo(Strings.nullToEmpty(this.qualifier));
+ }
+ }
+ }
+ return result;
+ }
+
+ public static org.osgi.framework.Version toOSGI(Version version) {
+ return new org.osgi.framework.Version(version.major, version.minor, version.service, version.qualifier);
+ }
+
+ public static Version fromOSGI(org.osgi.framework.Version version) {
+ return new Version(version.getMajor(), version.getMinor(), version.getMicro(), Strings.emptyToNull(version.getQualifier()));
+ }
+}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java
index a69a10ddc4a..50d98d220ea 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/resource/ModelSet.java
@@ -20,6 +20,7 @@
* Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Bug 436952
* Gabriel Pascual (ALL4TEC) gabriel.pascual@all4tec.net - Bug 436998
* Christian W. Damus - bug 436998
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.resource;
@@ -61,10 +62,15 @@ import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.emf.transaction.impl.EditingDomainManager;
import org.eclipse.papyrus.infra.core.Activator;
+import org.eclipse.papyrus.infra.core.editor.ModelSetServiceFactory;
+import org.eclipse.papyrus.infra.core.language.ILanguage;
+import org.eclipse.papyrus.infra.core.language.ILanguageService;
import org.eclipse.papyrus.infra.core.resource.additional.AdditionalResourcesModel;
+import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
import org.eclipse.papyrus.infra.tools.util.PlatformHelper;
import com.google.common.base.Optional;
+import com.google.common.collect.Lists;
/**
* This class is used to manage a set of {@link IModel}.
@@ -128,7 +134,7 @@ public class ModelSet extends ResourceSetImpl {
/** map of resource loaded in the resource set, with resource as the key and a boolean indicating if the resource is loaded or not has the valuer */
protected Map<Resource, Boolean> resourcesToLoadState = new HashMap<Resource, Boolean>();
-
+ private List<ILanguage> languages;
/**
*
@@ -565,6 +571,8 @@ public class ModelSet extends ResourceSetImpl {
// Get the file name, without extension.
uriWithoutExtension = uri.trimFileExtension();
+ installLanguages();
+
ModelMultiException exceptions = null;
List<IModel> orderedModelsForLoading = getOrderedModelsForLoading();
@@ -995,6 +1003,8 @@ public class ModelSet extends ResourceSetImpl {
iter.remove();
}
+ uninstallLanguages();
+
// Clear the package registry (it may contain dynamic profile EPackages that we don't
// want to leak in BasicExtendedMetaData instances attached to static EPackages)
// Works around EMF bug 433108
@@ -1258,5 +1268,32 @@ public class ModelSet extends ResourceSetImpl {
return null;
}
+ protected void installLanguages() {
+ ILanguageService languageService = ServiceUtils.getInstance().getService(ILanguageService.class, ModelSetServiceFactory.getServiceRegistry(this), null);
+ if (languageService != null) {
+ languages = Lists.newArrayList(languageService.getLanguages(getURIWithoutExtension(), false));
+ for (ILanguage next : languages) {
+ try {
+ next.install(this);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in installation of language " + next, e); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ protected void uninstallLanguages() {
+ if (languages != null) {
+ for (ILanguage next : Lists.reverse(languages)) {
+ try {
+ next.uninstall(this);
+ } catch (Exception e) {
+ Activator.log.error("Uncaught exception in uninstallation of language " + next, e); //$NON-NLS-1$
+ }
+ }
+
+ languages.clear();
+ }
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/BadStateException.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/BadStateException.java
index 1a7aa023923..fbcfe0b4402 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/BadStateException.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/BadStateException.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011, 2014 LIFL and others.
+ * Copyright (c) 2011, 2015 LIFL, Christian W. Damus, and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,6 +9,7 @@
*
* Contributors:
* LIFL - Initial API and implementation
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services;
@@ -41,7 +42,7 @@ public class BadStateException extends ServiceException {
* @param serviceDescriptor
*/
public BadStateException(String text, ServiceState state, ServiceDescriptor descriptor) {
- super(text + " (Service= '" + descriptor.getKey() + ", state= " + state + ")");
+ super(text + " (Service= '" + descriptor.getKey() + "', state= " + state + ")");
}
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceException.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceException.java
index f8c749a8f13..1ec8d111ec0 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceException.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceException.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011, 2014 LIFL and others.
+ * Copyright (c) 2011, 2015 LIFL, Christian W. Damus, and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,6 +9,7 @@
*
* Contributors:
* LIFL - Initial API and implementation
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services;
@@ -30,7 +31,7 @@ public class ServiceException extends Exception {
* Constructor.
*/
public ServiceException() {
- // TODO Auto-generated constructor stub
+ super();
}
/**
@@ -40,7 +41,6 @@ public class ServiceException extends Exception {
*/
public ServiceException(String message) {
super(message);
- // TODO Auto-generated constructor stub
}
/**
@@ -50,7 +50,6 @@ public class ServiceException extends Exception {
*/
public ServiceException(Throwable cause) {
super(cause);
- // TODO Auto-generated constructor stub
}
/**
@@ -61,7 +60,35 @@ public class ServiceException extends Exception {
*/
public ServiceException(String message, Throwable cause) {
super(message, cause);
- // TODO Auto-generated constructor stub
}
+ public ServiceException chain(Throwable next) {
+ return (next == null) ? this : new ServiceMultiException().chain(next);
+ }
+
+ public ServiceException chain(String identifier, Throwable next) {
+ return (next == null) ? this : new ServiceMultiException().chain(identifier, next);
+ }
+
+ public static ServiceException chain(ServiceException serviceException, Throwable next) {
+ if (serviceException != null) {
+ return serviceException.chain(next);
+ } else if (next instanceof ServiceException) {
+ return (ServiceException) next;
+ } else if (next != null) {
+ return new ServiceMultiException().chain(next);
+ } else {
+ return serviceException;
+ }
+ }
+
+ public static ServiceException chain(ServiceException serviceException, String identifier, Throwable next) {
+ if (serviceException != null) {
+ return serviceException.chain(identifier, next);
+ } else if (next != null) {
+ return new ServiceMultiException().chain(identifier, next);
+ } else {
+ return serviceException;
+ }
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceMultiException.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceMultiException.java
index 3484f6d98d5..3a6f7ae2369 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceMultiException.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServiceMultiException.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011, 2014 LIFL and others.
+ * Copyright (c) 2011, 2015 LIFL, Christian W. Damus, and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,6 +9,7 @@
*
* Contributors:
* LIFL - Initial API and implementation
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services;
@@ -149,4 +150,21 @@ public class ServiceMultiException extends ServiceException {
serviceIdentifiers.addAll(serviceIdentifiers);
}
+ @Override
+ public ServiceException chain(Throwable next) {
+ if (next instanceof ServiceMultiException) {
+ addAll((ServiceMultiException) next);
+ } else if (next != null) {
+ addException(next);
+ }
+ return this;
+ }
+
+ @Override
+ public ServiceException chain(String identifier, Throwable next) {
+ if (next != null) {
+ addException(identifier, next);
+ }
+ return this;
+ }
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServicesRegistry.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServicesRegistry.java
index 695d89c579a..13601f8708c 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServicesRegistry.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/ServicesRegistry.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2011, 2014 LIFL, CEA, and others.
+ * Copyright (c) 2011, 2015 LIFL, CEA, 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
@@ -9,6 +9,7 @@
* Contributors:
* LIFL - Initial API and implementation
* Christian W. Damus (CEA) - bug 431953 (fix start-up of selective services to require only their dependencies)
+ * Christian W. Damus - bug 468030
*/
package org.eclipse.papyrus.infra.core.services;
@@ -628,14 +629,13 @@ public class ServicesRegistry {
}
// Detect cycles
checkCycle(roots, map);
- // Retain only services with startupkind == START and state ==
- // REGISTERED
- roots = retainsToStartServices(roots);
+ // Retain only services with state == REGISTERED
+ roots = retainUnstartedServices(roots);
//
List<ServiceStartupEntry> toStart = buildTopologicalListOfServicesToStart(roots, map);
// Remove already started services
- toStart = retainsToStartServices(toStart);
+ toStart = retainUnstartedServices(toStart);
// if( log.isLoggable(Level.FINE))
// {
@@ -769,6 +769,24 @@ public class ServicesRegistry {
}
/**
+ * Retains the services that are not yet started. Retains only services with state == REGISTERED
+ *
+ * @param services
+ * Collection to filter
+ * @return a new Collection containing the registered services
+ */
+ private List<ServiceStartupEntry> retainUnstartedServices(Collection<ServiceStartupEntry> services) {
+ List<ServiceStartupEntry> result = new ArrayList<ServiceStartupEntry>(services.size());
+ for (ServiceStartupEntry service : services) {
+ if (service.getState() == ServiceState.registered) {
+ result.add(service);
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Check for cycles. Throws an exception if a cycle is discovered. Each root
* is checked to see if it contains a cycle.
*
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/internal/LazyStartupEntry.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/internal/LazyStartupEntry.java
index a8df83e7159..708cd8b5dc8 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/internal/LazyStartupEntry.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/services/internal/LazyStartupEntry.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) CEA LIST.
+ * Copyright (c) CEA LIST, Christian W. Damus, and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,10 +9,12 @@
*
* Contributors:
* Cedric Dumoulin Cedric.dumoulin@lifl.fr - Initial API and implementation
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.infra.core.services.internal;
+import org.eclipse.papyrus.infra.core.Activator;
import org.eclipse.papyrus.infra.core.services.IService;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServiceState;
@@ -57,6 +59,7 @@ public class LazyStartupEntry extends ServiceStartupEntry {
serviceEntry.startService();
} catch (Exception e) {
// There was an error. The service is in error
+ Activator.log.error("Failed to start lazy service: " + getDescriptor().getKey(), e); //$NON-NLS-1$
serviceEntry = new ErrorServiceTypeEntry(serviceEntry.getDescriptor());
}
}
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java
index 6390ec7d546..2ce85f89fc6 100644
--- a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java
+++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/utils/AbstractServiceUtils.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2010 LIFL & CEA LIST.
+ * Copyright (c) 2010, 2015 LIFL & CEA LIST, Christian W. Damus, and others.
*
*
* All rights reserved. This program and the accompanying materials
@@ -9,6 +9,7 @@
*
* Contributors:
* Cedric Dumoulin (LIFL) cedric.dumoulin@lifl.fr - Initial API and implementation
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
@@ -143,7 +144,7 @@ public abstract class AbstractServiceUtils<T> {
* @param from
* The context from which the service should be retrieved
* @return
- * The implementation of the requested service
+ * The implementation of the requested service
* @throws ServiceException
* If an error occurs (e.g. cannot find the ServicesRegistry or the Service)
*
@@ -160,7 +161,7 @@ public abstract class AbstractServiceUtils<T> {
* @param from
* The context from which the service should be retrieved
* @return
- * The implementation of the requested service
+ * The implementation of the requested service
* @throws ServiceException
* If an error occurs (e.g. cannot find the ServicesRegistry or the Service)
*
@@ -168,4 +169,29 @@ public abstract class AbstractServiceUtils<T> {
public Object getService(Object service, T from) throws ServiceException {
return getServiceRegistry(from).getService(service);
}
+
+ /**
+ * Returns an implementation of the requested <em>optional</em> service, from the specified context, if it is available.
+ *
+ * @param service
+ * The service for which an implementation is requested
+ * @param from
+ * The context from which the service should be retrieved
+ * @param defaultImpl
+ * A default implementation of the requested service API to return if none is available in the registry
+ * or if the registered implementation could not be properly initialized. May be {@code null} if the
+ * service is <em>optional</em>
+ *
+ * @return
+ * The implementation of the requested service, or the {@code defaultImpl}
+ */
+ public <S> S getService(Class<S> service, T from, S defaultImpl) {
+ try {
+ // Don't even attempt to get a registry from a null context
+ return (from == null) ? defaultImpl : getServiceRegistry(from).getService(service);
+ } catch (ServiceException e) {
+ // That's OK. It's optional and we have a default
+ return defaultImpl;
+ }
+ }
}

Back to the top