Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-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
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.classpath2
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.settings/org.eclipse.jdt.core.prefs6
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/META-INF/MANIFEST.MF2
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/plugin.xml10
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/helper/DecoratorModelUtils.java35
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/DecoratorModelIndex.java147
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/AbstractUMLIndexHandler.java314
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ModelIndexHandler.java124
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndex.java66
-rw-r--r--plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndexHandler.java275
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF1
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/plugin.xml13
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/IProfileIndex.java59
-rw-r--r--plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/ProfileLanguageProvider.java186
33 files changed, 2447 insertions, 301 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;
+ }
+ }
}
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.classpath b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.classpath
index 0c22b5d7e6d..6a42377b56a 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.classpath
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.classpath
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="src" path="src-gen"/>
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.settings/org.eclipse.jdt.core.prefs b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.settings/org.eclipse.jdt.core.prefs
index 94d61f00da6..f08be2b06c4 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.settings/org.eclipse.jdt.core.prefs
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/.settings/org.eclipse.jdt.core.prefs
@@ -1,10 +1,10 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.compiler.source=1.7
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/META-INF/MANIFEST.MF b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/META-INF/MANIFEST.MF
index 021d637083c..6c1f1462a0e 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/META-INF/MANIFEST.MF
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/META-INF/MANIFEST.MF
@@ -24,7 +24,7 @@ Require-Bundle: org.eclipse.core.runtime,
org.eclipse.core.expressions;bundle-version="3.4.600",
org.eclipse.papyrus.infra.emf.readonly;bundle-version="1.1.0",
org.eclipse.papyrus.infra.tools;bundle-version="1.1.0"
-Bundle-RequiredExecutionEnvironment: JavaSE-1.6
+Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy;exclude:=org.eclipse.papyrus.uml.decoratormodel.internal.expressions
Export-Package: org.eclipse.papyrus.uml.decoratormodel,
org.eclipse.papyrus.uml.decoratormodel.helper,
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/plugin.xml b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/plugin.xml
index a4b704e740d..52618d1c682 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/plugin.xml
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/plugin.xml
@@ -80,5 +80,15 @@
class="org.eclipse.papyrus.uml.decoratormodel.profileExternalization.ProfileExternalizationPackage"
genModel="model/ProfileExternalization.profile.genmodel"/>
</extension>
+ <extension
+ point="org.eclipse.papyrus.infra.core.service">
+ <service
+ classname="org.eclipse.papyrus.uml.decoratormodel.internal.resource.index.ProfileIndex"
+ id="org.eclipse.papyrus.uml.tools.profile.index.IProfileIndex"
+ priority="10"
+ startKind="lazy"
+ description="The profile application index for workspace resources">
+ </service>
+ </extension>
</plugin>
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/helper/DecoratorModelUtils.java b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/helper/DecoratorModelUtils.java
index 6b0e3cc04d4..c558fee75a2 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/helper/DecoratorModelUtils.java
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/helper/DecoratorModelUtils.java
@@ -11,6 +11,7 @@
* Christian W. Damus - bug 399859
* Christian W. Damus - bug 458197
* Christian W. Damus - bug 459613
+ * Christian W. Damus - bug 468030
*
*****************************************************************************/
package org.eclipse.papyrus.uml.decoratormodel.helper;
@@ -778,6 +779,32 @@ public class DecoratorModelUtils {
}
/**
+ * Queries whether a given {@code file} is a decorator model resource.
+ * <p>
+ * This method does <em>not</em> access the decorator model index.
+ *
+ * @param file
+ * a workspace file
+ *
+ * @return whether the fileexists and is a decorator model
+ */
+ public static boolean isDecoratorModel(IFile file) {
+ boolean result = false;
+
+ if (file.isAccessible()) {
+ try {
+ IContentDescription desc = file.exists() ? file.getContentDescription() : null;
+ result = (desc != null) && (desc.getContentType() != null) && desc.getContentType().isKindOf(DECORATOR_MODEL_CONTENT_TYPE);
+ } catch (CoreException e) {
+ // Couldn't determine content description? Then it cannot be our type
+ Activator.getDefault().getLog().log(e.getStatus());
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Whether the resource indicated by the given URI is a decorator model resource.
* <p>
* This method does <em>not</em> access the decorator model index.
@@ -794,13 +821,7 @@ public class DecoratorModelUtils {
// Let the workspace's content-type manager handle it (which can cache the information)
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(uri.toPlatformString(true)));
- try {
- IContentDescription desc = file.exists() ? file.getContentDescription() : null;
- result = (desc != null) && (desc.getContentType() != null) && desc.getContentType().isKindOf(DECORATOR_MODEL_CONTENT_TYPE);
- } catch (CoreException e) {
- // Couldn't determine content description? Then it cannot be our type
- Activator.getDefault().getLog().log(e.getStatus());
- }
+ result = isDecoratorModel(file);
} else {
// Work it out the hard way
InputStream input = null;
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/DecoratorModelIndex.java b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/DecoratorModelIndex.java
index e8c69c076f7..b4de5bc665b 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/DecoratorModelIndex.java
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/DecoratorModelIndex.java
@@ -13,8 +13,8 @@
package org.eclipse.papyrus.uml.decoratormodel.internal.resource;
-import java.io.IOException;
import java.io.InputStream;
+import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -37,8 +37,12 @@ import org.eclipse.papyrus.infra.emf.resource.index.WorkspaceModelIndexEvent;
import org.eclipse.papyrus.uml.decoratormodel.Activator;
import org.eclipse.papyrus.uml.decoratormodel.helper.DecoratorModelUtils;
import org.eclipse.papyrus.uml.decoratormodel.internal.messages.Messages;
+import org.eclipse.papyrus.uml.decoratormodel.internal.resource.index.ModelIndexHandler;
import org.eclipse.papyrus.uml.decoratormodel.internal.resource.index.ProfileIndexHandler;
import org.eclipse.uml2.common.util.CacheAdapter;
+import org.eclipse.uml2.uml.Profile;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.xml.sax.helpers.DefaultHandler;
import com.google.common.base.Function;
import com.google.common.collect.HashMultimap;
@@ -48,6 +52,7 @@ import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -68,6 +73,8 @@ public class DecoratorModelIndex {
private final Map<URI, Map<URI, Map<URI, URI>>> decoratorModelToPackageToProfileApplications = Maps.newHashMap();
private final Map<URI, String> decoratorModelNames = Maps.newHashMap();
+ private final Map<URI, SetMultimap<URI, URI>> userModelToResourceToAppliedProfiles = Maps.newHashMap();
+
private final WorkspaceModelIndex<DecoratorModelIndex> index;
private final CopyOnWriteArrayList<IDecoratorModelIndexListener> listeners = Lists.newCopyOnWriteArrayList();
@@ -78,7 +85,7 @@ public class DecoratorModelIndex {
private DecoratorModelIndex() {
super();
- index = new WorkspaceModelIndex<DecoratorModelIndex>("decoratorModels", DecoratorModelUtils.DECORATOR_MODEL_CONTENT_TYPE.getId(), indexer(), MAX_INDEX_JOBS); //$NON-NLS-1$
+ index = new WorkspaceModelIndex<DecoratorModelIndex>("papyrusUMLProfiles", UMLPackage.eCONTENT_TYPE, indexer(), MAX_INDEX_JOBS); //$NON-NLS-1$
index.addListener(new WorkspaceModelIndexAdapter() {
@Override
protected void indexAboutToCalculateOrRecalculate(WorkspaceModelIndexEvent event) {
@@ -466,42 +473,73 @@ public class DecoratorModelIndex {
};
}
- <V> ListenableFuture<V> afterIndex(Callable<V> callable) {
- return index.afterIndex(callable);
+ /**
+ * Asynchronously queries the set of URIs of {@link Profile}s applied internally and by decorators to {@link Package}s
+ * within the specified user model.
+ *
+ * @param userModelURI
+ * the URI of a user model
+ * @return a future result of the set of URIs of the profile elements applied to it
+ */
+ public ListenableFuture<Set<URI>> getAllProfilesAppliedToPackagesAsync(URI userModelURI) {
+ return afterIndex(getAllProfilesAppliedToPackagesCallable(userModelURI));
}
- private void index(IFile file) {
- final URI decoratorURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
+ /**
+ * Queries the set of URIs of {@link Profile}s applied internally and by decorators to {@link Package}s
+ * within the specified user model.
+ *
+ * @param userModelURI
+ * the URI of a user model
+ * @return the set of URIs of the profile elements applied to it
+ */
+ public Set<URI> getAllProfilesAppliedToPackages(URI userModelURI) throws CoreException {
+ return sync(afterIndex(getAllProfilesAppliedToPackagesCallable(userModelURI)));
+ }
- ProfileIndexHandler handler = new ProfileIndexHandler(decoratorURI);
+ Callable<Set<URI>> getAllProfilesAppliedToPackagesCallable(final URI userModelURI) {
+ return new SyncCallable<Set<URI>>() {
+ @Override
+ protected Set<URI> doCall() {
+ SetMultimap<URI, URI> resourceToAppliedProfiles = userModelToResourceToAppliedProfiles.get(userModelURI);
+ return (resourceToAppliedProfiles == null) ? Collections.<URI> emptySet() : ImmutableSet.copyOf(resourceToAppliedProfiles.values());
+ }
+ };
+ }
- InputStream input = null;
+ <V> ListenableFuture<V> afterIndex(Callable<V> callable) {
+ return index.afterIndex(callable);
+ }
- try {
+ private void runIndexHandler(IFile file, URI resourceURI, DefaultHandler handler) {
+ try (InputStream input = file.getContents()) {
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setValidating(false);
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
- input = file.getContents();
-
- parser.parse(input, handler, decoratorURI.toString());
+ parser.parse(input, handler, resourceURI.toString());
} catch (Exception e) {
// We intentionally bomb out early with an exception
- } finally {
- if (input != null) {
- try {
- input.close();
- } catch (IOException e) {
- Activator.log.error("Could not close file after indexing.", e); //$NON-NLS-1$
- }
- }
}
+ }
+
+ private void indexDecoratorModel(IFile file) {
+ final URI decoratorURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
+
+ ProfileIndexHandler handler = new ProfileIndexHandler(decoratorURI);
+
+ runIndexHandler(file, decoratorURI, handler);
synchronized (sync) {
// first, remove all links to the decorator model
for (URI next : decoratorToModels.get(decoratorURI)) {
modelToDecorators.remove(next, decoratorURI);
+
+ SetMultimap<URI, URI> decoratorMap = userModelToResourceToAppliedProfiles.get(next);
+ if (decoratorMap != null) {
+ decoratorMap.removeAll(decoratorURI);
+ }
}
// update the forward mapping
@@ -530,16 +568,37 @@ public class DecoratorModelIndex {
// and the externalization name index
decoratorModelNames.put(decoratorURI, handler.getExternalizationName());
+
+ // and the applied profiles by resource (external and internal)
+ Set<URI> userModelsProcessed = Sets.newHashSet();
+ for (Map.Entry<URI, Map<URI, URI>> next : handler.getPackageToProfileApplications().entrySet()) {
+ URI userModelURI = next.getKey().trimFragment();
+ if (userModelsProcessed.add(userModelURI)) {
+ SetMultimap<URI, URI> resourceToAppliedProfiles = userModelToResourceToAppliedProfiles.get(userModelURI);
+ if (resourceToAppliedProfiles == null) {
+ resourceToAppliedProfiles = HashMultimap.create();
+ userModelToResourceToAppliedProfiles.put(userModelURI, resourceToAppliedProfiles);
+ }
+ for (URI profileURI : next.getValue().keySet()) {
+ resourceToAppliedProfiles.put(decoratorURI, profileURI);
+ }
+ }
+ }
}
}
- private void unindex(IFile file) {
+ private void unindexDecoratorModel(IFile file) {
final URI decoratorURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
synchronized (sync) {
// first, remove all links to the decorator model
for (URI next : decoratorToModels.get(decoratorURI)) {
modelToDecorators.remove(next, decoratorURI);
+
+ SetMultimap<URI, URI> resourceToAppliedProfiles = userModelToResourceToAppliedProfiles.get(next);
+ if (resourceToAppliedProfiles != null) {
+ resourceToAppliedProfiles.removeAll(decoratorURI);
+ }
}
// remove the forward mapping
@@ -561,17 +620,59 @@ public class DecoratorModelIndex {
}
}
+ private void indexUserModel(IFile file) {
+ final URI userModelURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
+
+ ModelIndexHandler handler = new ModelIndexHandler(userModelURI);
+
+ runIndexHandler(file, userModelURI, handler);
+
+ synchronized (sync) {
+ // remove all internal profile applications of this resource
+ SetMultimap<URI, URI> resourceToAppliedProfiles = userModelToResourceToAppliedProfiles.get(userModelURI);
+ if (resourceToAppliedProfiles != null) {
+ resourceToAppliedProfiles.removeAll(userModelURI);
+ }
+
+ // then add the applied profiles by resource (external and internal)
+ for (Map<URI, URI> next : handler.getProfileApplicationsByPackage().values()) {
+ if (resourceToAppliedProfiles == null) {
+ resourceToAppliedProfiles = HashMultimap.create();
+ userModelToResourceToAppliedProfiles.put(userModelURI, resourceToAppliedProfiles);
+ }
+ resourceToAppliedProfiles.putAll(userModelURI, next.keySet()); // This resource applies the profiles, itself
+ }
+ }
+ }
+
+ private void unindexUserModel(IFile file) {
+ final URI userModelURI = URI.createPlatformResourceURI(file.getFullPath().toString(), true);
+
+ synchronized (sync) {
+ // remove all internal profile applications of this resource
+ SetMultimap<URI, URI> resourceToAppliedProfiles = userModelToResourceToAppliedProfiles.get(userModelURI);
+ if (resourceToAppliedProfiles != null) {
+ resourceToAppliedProfiles.removeAll(userModelURI);
+ }
+ }
+ }
+
private IndexHandler<DecoratorModelIndex> indexer() {
return new IndexHandler<DecoratorModelIndex>() {
@Override
public DecoratorModelIndex index(IFile file) {
- DecoratorModelIndex.this.index(file);
+ if (DecoratorModelUtils.isDecoratorModel(file)) {
+ DecoratorModelIndex.this.indexDecoratorModel(file);
+ } else {
+ DecoratorModelIndex.this.indexUserModel(file);
+ }
return DecoratorModelIndex.this;
}
@Override
public void unindex(IFile file) {
- DecoratorModelIndex.this.unindex(file);
+ DecoratorModelIndex.this.unindexDecoratorModel(file);
+ DecoratorModelIndex.this.unindexUserModel(file);
}
};
}
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/AbstractUMLIndexHandler.java b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/AbstractUMLIndexHandler.java
new file mode 100644
index 00000000000..1eb4b23d987
--- /dev/null
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/AbstractUMLIndexHandler.java
@@ -0,0 +1,314 @@
+/*****************************************************************************
+ * Copyright (c) 2014, 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.uml.decoratormodel.internal.resource.index;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.xmi.XMIResource;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.eclipse.uml2.uml.util.UMLUtil;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+/**
+ * Framework for SAX parsing handlers for indexing UML resources.
+ */
+public abstract class AbstractUMLIndexHandler extends DefaultHandler {
+ protected final URI fileURI;
+ private final Map<URI, Map<URI, URI>> packageToProfileApplications = Maps.newHashMap();
+
+ private String umlNamespace;
+ private String umlPrefix;
+ private String xmiType;
+ private String xmiID;
+
+ private Set<String> packageTypes;
+ private String eAnnotations;
+ private String references;
+
+ private UMLElement top;
+ private int ignore;
+
+ protected UMLElement currentPackage;
+
+ private Await await = new Await();
+
+ /**
+ * Initializes me.
+ *
+ * @param fileURI
+ * the URI of the UML file that I am parsing
+ */
+ public AbstractUMLIndexHandler(final URI fileURI) {
+ this.fileURI = fileURI;
+ }
+
+ public URI getFileURI() {
+ return fileURI;
+ }
+
+ public Map<URI, Map<URI, URI>> getProfileApplicationsByPackage() {
+ return packageToProfileApplications;
+ }
+
+ @Override
+ public void startPrefixMapping(String prefix, String uri) throws SAXException {
+ if (uri.startsWith(XMIResource.XMI_NAMESPACE_PREFIX) || uri.startsWith(XMIResource.XMI_2_4_NAMESPACE_PREFIX)) {
+ xmiType = qname(prefix, "type"); //$NON-NLS-1$
+ xmiID = qname(prefix, "id"); //$NON-NLS-1$
+ } else if (EPackage.Registry.INSTANCE.getEPackage(uri) == UMLPackage.eINSTANCE) {
+ umlNamespace = uri;
+ umlPrefix = prefix;
+
+ initializeUMLElementNames();
+ }
+ }
+
+ protected void initializeUMLElementNames() {
+ packageTypes = ImmutableSet.of(umlElement("Package"), umlElement("Model"), umlElement("Profile")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+
+ eAnnotations = "eAnnotations"; //$NON-NLS-1$
+ references = "references"; //$NON-NLS-1$
+ }
+
+ protected final String umlElement(String name) {
+ return qname(umlPrefix, name);
+ }
+
+ protected final String qname(String prefix, String name) {
+ StringBuilder buf = new StringBuilder(prefix.length() + name.length() + 1);
+ return buf.append(prefix).append(':').append(name).toString();
+ }
+
+ @Override
+ public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+ if (umlNamespace.equals(uri) || (top != null)) {
+ // skip over annotations
+ if (!ignore(qName, attributes)) {
+ push(qName, attributes);
+ handleUMLElement(top, attributes);
+ }
+ }
+ }
+
+ protected boolean ignore(String qName, Attributes attributes) {
+ boolean result = false;
+
+ if (attributes != null) { // Starting an element
+ result = (ignore > 0) || (eAnnotations.equals(qName) && !UMLUtil.UML2_UML_PACKAGE_2_0_NS_URI.equals(attributes.getValue("source"))); //$NON-NLS-1$
+ if (result) {
+ ignore++;
+ }
+ } else { // Ending an element
+ result = (ignore > 0);
+ if (result) {
+ ignore--;
+ }
+ }
+
+ return result;
+ }
+
+ protected final void push(String qName, Attributes attributes) {
+ top = new UMLElement(qName, attributes);
+ }
+
+ protected final UMLElement pop() {
+ UMLElement result = top;
+ if (top != null) {
+ top = top.parent;
+ }
+ return result;
+ }
+
+ protected void handleUMLElement(UMLElement element, Attributes attributes) throws SAXException {
+ if (element.isPackage() && (element.getHREF() == null)) {
+ // An href is a reference to a package, not a package in our element hierarchy
+ currentPackage = element;
+ enterPackage(currentPackage, attributes);
+ }
+
+ if (!doHandleUMLElement(element, attributes)) {
+ if (element.isA(UMLUtil.UML2_UML_PACKAGE_2_0_NS_URI)) {
+ // It's the applied profile definition annotation
+ await.push(references);
+ } else if (await.isAwaiting(element)) {
+ if (element.isRole(references)) {
+ handleAnnotationReferences(element);
+ } else {
+ handleAwaitedElement(element);
+ }
+
+ await.pop();
+ }
+ }
+ }
+
+ protected void enterPackage(UMLElement package_, Attributes attributes) {
+ // Pass
+ }
+
+ protected void exitPackage(UMLElement package_) {
+ // Pass
+ }
+
+ protected abstract boolean doHandleUMLElement(UMLElement element, Attributes attributes);
+
+ protected final void await(String elementName) {
+ await.push(elementName);
+ }
+
+ protected void handleAnnotationReferences(UMLElement references) {
+ // Pass
+ }
+
+ protected abstract void handleAwaitedElement(UMLElement element);
+
+ protected abstract void summarize();
+
+ @Override
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (!ignore(qName, null)) {
+ if (await.stopAt(pop())) {
+ await.pop();
+ }
+
+ if (top != null) {
+ UMLElement newPackage = top.nearestPackage();
+ if ((newPackage != currentPackage) && (currentPackage != null)) {
+ exitPackage(currentPackage);
+ }
+ currentPackage = newPackage;
+ }
+
+ if (top == null) {
+ // We're done with UML content
+ summarize();
+ throw new OperationCanceledException();
+ }
+ }
+ }
+
+ //
+ // Nested types
+ //
+
+ protected final class UMLElement {
+ final UMLElement parent;
+
+ final String role;
+ final String type;
+ final String id;
+ final String href;
+
+ UMLElement(String qName, Attributes attributes) {
+ parent = top;
+
+ String type;
+ if (qName.equals(eAnnotations)) {
+ // "type" of an annotation is its source
+ type = attributes.getValue("source"); //$NON-NLS-1$
+ } else {
+ type = attributes.getValue(xmiType);
+ if (Strings.isNullOrEmpty(type)) {
+ type = qName;
+ }
+ }
+
+ this.role = qName;
+ this.type = type;
+ this.id = attributes.getValue(xmiID);
+ this.href = attributes.getValue("href"); //$NON-NLS-1$
+ }
+
+ boolean isPackage() {
+ return packageTypes.contains(type);
+ }
+
+ boolean isRole(String roleName) {
+ return roleName.equals(role);
+ }
+
+ boolean isA(String xmiType) {
+ return xmiType.equals(type);
+ }
+
+ URI getHREF() {
+ return Strings.isNullOrEmpty(href) ? null : URI.createURI(href).resolve(fileURI);
+ }
+
+ UMLElement nearestPackage() {
+ for (UMLElement next = this; next != null; next = next.parent) {
+ if (next.isPackage()) {
+ return next;
+ }
+ }
+ return null;
+ }
+ }
+
+ private final class Await {
+ final Await parent = await;
+
+ final String awaiting;
+ final UMLElement limit;
+
+ Await() {
+ this(null);
+ }
+
+ private Await(String awaiting) {
+ this.awaiting = awaiting;
+ this.limit = top;
+ }
+
+ boolean isRoot() {
+ return parent == null;
+ }
+
+ boolean isAwaiting(UMLElement element) {
+ return !isRoot() && element.isRole(awaiting);
+ }
+
+ boolean stopAt(UMLElement element) {
+ return !isRoot() && (limit == element);
+ }
+
+ Await push(String elementName) {
+ Await result = new Await(elementName);
+ await = result;
+ return result;
+ }
+
+ void pop() {
+ if (!isRoot()) {
+ await = parent;
+ }
+ }
+ }
+
+ protected static final class URIPair {
+ public URI first;
+ public URI second;
+ }
+}
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ModelIndexHandler.java b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ModelIndexHandler.java
new file mode 100644
index 00000000000..3fa5f3a2947
--- /dev/null
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ModelIndexHandler.java
@@ -0,0 +1,124 @@
+/*****************************************************************************
+ * 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.uml.decoratormodel.internal.resource.index;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.uml2.uml.Package;
+import org.eclipse.uml2.uml.Profile;
+import org.xml.sax.Attributes;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+
+/**
+ * SAX parsing handler for indexing a UML resource. It produces one output:
+ * <ul>
+ * <li>{@link #getProfileApplicationsByPackage()}: profile applications by {@link Package}, as mappings of {@link Profile}&nbsp;==>&nbsp;{@link EPackage} object URIs (with fragments)</li>
+ * </ul>
+ */
+public class ModelIndexHandler extends AbstractUMLIndexHandler {
+ private final Map<URI, Map<URI, URI>> packageToProfileApplications = Maps.newHashMap();
+ private final Multimap<String, URIPair> packageProfileApplications = ArrayListMultimap.create();
+
+ private String profileApplication;
+ private String appliedProfile;
+
+ private URIPair currentProfileApplication;
+
+ /**
+ * Initializes me.
+ *
+ * @param fileURI
+ * the URI of the UML file that I am parsing
+ */
+ public ModelIndexHandler(final URI fileURI) {
+ super(fileURI);
+ }
+
+ @Override
+ public URI getFileURI() {
+ return fileURI;
+ }
+
+ @Override
+ public Map<URI, Map<URI, URI>> getProfileApplicationsByPackage() {
+ return packageToProfileApplications;
+ }
+
+ @Override
+ protected void initializeUMLElementNames() {
+ super.initializeUMLElementNames();
+
+ profileApplication = "profileApplication"; //$NON-NLS-1$
+ appliedProfile = "appliedProfile"; //$NON-NLS-1$
+ }
+
+ @Override
+ protected boolean doHandleUMLElement(UMLElement element, Attributes attributes) {
+ boolean result = false;
+
+ if (element.isRole(profileApplication)) {
+ currentProfileApplication = new URIPair();
+ packageProfileApplications.put(currentPackage.id, currentProfileApplication);
+ await(appliedProfile);
+ result = true;
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void handleAwaitedElement(UMLElement element) {
+ if (element.isRole(appliedProfile)) {
+ URI href = element.getHREF();
+ if (href != null) {
+ currentProfileApplication.first = href;
+ }
+ }
+ }
+
+ @Override
+ protected void handleAnnotationReferences(UMLElement references) {
+ URI href = references.getHREF();
+ if (href != null) {
+ currentProfileApplication.second = href;
+ }
+ }
+
+ @Override
+ protected void summarize() {
+ for (String packageID : packageProfileApplications.keySet()) {
+ URI applyingPackageURI = fileURI.appendFragment(packageID);
+ Collection<URIPair> profileApplications = packageProfileApplications.get(packageID);
+ if (!profileApplications.isEmpty()) {
+ Map<URI, URI> map = packageToProfileApplications.get(applyingPackageURI);
+ if (map == null) {
+ map = Maps.newHashMap();
+ packageToProfileApplications.put(applyingPackageURI, map);
+ }
+ for (URIPair profileApplication : profileApplications) {
+ // If we can't determine the Ecore definition, the profile is not properly applied
+ if (profileApplication.second != null) {
+ map.put(profileApplication.first, profileApplication.second);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndex.java b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndex.java
new file mode 100644
index 00000000000..df36a707cee
--- /dev/null
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndex.java
@@ -0,0 +1,66 @@
+/*****************************************************************************
+ * 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.uml.decoratormodel.internal.resource.index;
+
+import java.util.Set;
+
+import org.eclipse.emf.common.util.URI;
+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.uml.decoratormodel.internal.resource.DecoratorModelIndex;
+import org.eclipse.papyrus.uml.tools.profile.index.IProfileIndex;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * Implementation of the profile index provider service API that uses our workspace model index.
+ */
+public class ProfileIndex implements IProfileIndex, IService {
+
+ public ProfileIndex() {
+ super();
+ }
+
+ @Override
+ public boolean indexes(URI umlResource) {
+ return umlResource.isPlatformResource();
+ }
+
+ @Override
+ public ListenableFuture<Set<URI>> getAppliedProfiles(URI umlResource) {
+ return DecoratorModelIndex.getInstance().getAllProfilesAppliedToPackagesAsync(umlResource);
+ }
+
+ //
+ // Service lifecycle API
+ //
+
+ @Override
+ public void init(ServicesRegistry servicesRegistry) throws ServiceException {
+ // Pass
+ }
+
+ @Override
+ public void startService() throws ServiceException {
+ // Ensure that the index is alive and kicking
+ DecoratorModelIndex.getInstance();
+ }
+
+ @Override
+ public void disposeService() throws ServiceException {
+ // Pass
+ }
+
+}
diff --git a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndexHandler.java b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndexHandler.java
index b234efe9904..f5b7e20387f 100644
--- a/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndexHandler.java
+++ b/plugins/uml/decoratormodel/org.eclipse.papyrus.uml.decoratormodel/src/org/eclipse/papyrus/uml/decoratormodel/internal/resource/index/ProfileIndexHandler.java
@@ -17,19 +17,10 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
-import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.xmi.XMIResource;
-import org.eclipse.uml2.uml.UMLPackage;
-import org.eclipse.uml2.uml.util.UMLUtil;
import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-import com.google.common.base.Strings;
import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
@@ -41,35 +32,20 @@ import com.google.common.collect.Sets;
* <li>{@link #getAppliedProfileURIs()}: a mapping of applying package to applied profile, as object URIs (with fragments)</li>
* </ul>
*/
-public class ProfileIndexHandler extends DefaultHandler {
- private final URI fileURI;
+public class ProfileIndexHandler extends AbstractUMLIndexHandler {
private final Set<URI> referencedModelURIs = Sets.newHashSet();
private final Map<URI, Map<URI, URI>> packageToProfileApplications = Maps.newHashMap();
private String externalizationName;
- private String umlNamespace;
- private String umlPrefix;
- private String xmiType;
- private String xmiID;
- private Set<String> packageTypes;
private String dependencyType;
private String client;
private String profileApplication;
private String appliedProfile;
- private String eAnnotations;
- private String references;
-
- private UMLElement top;
- private int ignore;
-
- private UMLElement currentPackage;
private Map<String, URI> packageClients = Maps.newHashMap();
private URIPair currentProfileApplication;
private Multimap<String, URIPair> packageProfileApplications = ArrayListMultimap.create();
- private Await await = new Await();
-
/**
* Initializes me.
*
@@ -77,11 +53,7 @@ public class ProfileIndexHandler extends DefaultHandler {
* the URI of the profile-application file that I am parsing
*/
public ProfileIndexHandler(final URI fileURI) {
- this.fileURI = fileURI;
- }
-
- public URI getFileURI() {
- return fileURI;
+ super(fileURI);
}
public Set<URI> getReferencedModelURIs() {
@@ -97,108 +69,48 @@ public class ProfileIndexHandler extends DefaultHandler {
}
@Override
- public void startPrefixMapping(String prefix, String uri) throws SAXException {
- if (uri.startsWith(XMIResource.XMI_NAMESPACE_PREFIX) || uri.startsWith(XMIResource.XMI_2_4_NAMESPACE_PREFIX)) {
- xmiType = qname(prefix, "type"); //$NON-NLS-1$
- xmiID = qname(prefix, "id"); //$NON-NLS-1$
- } else if (EPackage.Registry.INSTANCE.getEPackage(uri) == UMLPackage.eINSTANCE) {
- umlNamespace = uri;
- umlPrefix = prefix;
-
- packageTypes = ImmutableSet.of(umlElement("Package"), umlElement("Model"), umlElement("Profile")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- dependencyType = umlElement("Dependency"); //$NON-NLS-1$
- client = "client"; //$NON-NLS-1$
+ protected void initializeUMLElementNames() {
+ super.initializeUMLElementNames();
- profileApplication = "profileApplication"; //$NON-NLS-1$
- appliedProfile = "appliedProfile"; //$NON-NLS-1$
+ dependencyType = umlElement("Dependency"); //$NON-NLS-1$
+ client = "client"; //$NON-NLS-1$
- eAnnotations = "eAnnotations"; //$NON-NLS-1$
- references = "references"; //$NON-NLS-1$
- }
- }
-
- protected final String umlElement(String name) {
- return qname(umlPrefix, name);
- }
-
- protected final String qname(String prefix, String name) {
- StringBuilder buf = new StringBuilder(prefix.length() + name.length() + 1);
- return buf.append(prefix).append(':').append(name).toString();
+ profileApplication = "profileApplication"; //$NON-NLS-1$
+ appliedProfile = "appliedProfile"; //$NON-NLS-1$
}
@Override
- public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
- if (umlNamespace.equals(uri) || (top != null)) {
- // skip over annotations
- if (!ignore(qName, attributes)) {
- push(qName, attributes);
- handleUMLElement(top, attributes);
- }
+ protected void enterPackage(UMLElement package_, Attributes attributes) {
+ if (package_.parent == null) {
+ externalizationName = attributes.getValue("name"); //$NON-NLS-1$
}
}
- boolean ignore(String qName, Attributes attributes) {
+ @Override
+ protected boolean doHandleUMLElement(UMLElement element, Attributes attributes) {
boolean result = false;
- if (attributes != null) { // Starting an element
- result = (ignore > 0) || (eAnnotations.equals(qName) && !UMLUtil.UML2_UML_PACKAGE_2_0_NS_URI.equals(attributes.getValue("source"))); //$NON-NLS-1$
- if (result) {
- ignore++;
- }
- } else { // Ending an element
- result = (ignore > 0);
- if (result) {
- ignore--;
- }
- }
-
- return result;
- }
-
- protected final void push(String qName, Attributes attributes) {
- top = new UMLElement(qName, attributes);
- }
-
- protected final UMLElement pop() {
- UMLElement result = top;
- if (top != null) {
- top = top.parent;
- }
- return result;
- }
-
- protected void handleUMLElement(UMLElement element, Attributes attributes) throws SAXException {
- if (element.isPackage() && (element.getHREF() == null)) {
- // An href is a reference to a package, not a package in our element hierarchy
- currentPackage = element;
-
- // if it's the top package, get its name
- if (currentPackage.parent == null) {
- externalizationName = attributes.getValue("name"); //$NON-NLS-1$
- }
- }
-
if (element.isA(dependencyType)) {
// It's a dependency. We want its client
- await.push(client);
+ await(client);
+ result = true;
} else if (element.isRole(profileApplication)) {
currentProfileApplication = new URIPair();
packageProfileApplications.put(currentPackage.id, currentProfileApplication);
- await.push(appliedProfile);
- } else if (element.isA(UMLUtil.UML2_UML_PACKAGE_2_0_NS_URI)) {
- // It's the applied profile definition annotation
- await.push(references);
- } else if (await.isAwaiting(element)) {
- if (element.isRole(client) && element.isPackage()) {
- // Got a package dependency client
- handleDependencyClient(element);
- } else if (element.isRole(appliedProfile)) {
- handleAppliedProfile(element);
- } else if (element.isRole(references)) {
- handleReferences(element);
- }
+ await(appliedProfile);
+ result = true;
+ }
+
+ return result;
+ }
- await.pop();
+ @Override
+ protected void handleAwaitedElement(UMLElement element) {
+ if (element.isRole(client) && element.isPackage()) {
+ // Got a package dependency client
+ handleDependencyClient(element);
+ } else if (element.isRole(appliedProfile)) {
+ handleAppliedProfile(element);
}
}
@@ -210,7 +122,8 @@ public class ProfileIndexHandler extends DefaultHandler {
}
}
- protected void handleReferences(UMLElement references) {
+ @Override
+ protected void handleAnnotationReferences(UMLElement references) {
URI href = references.getHREF();
if (href != null) {
currentProfileApplication.second = href;
@@ -224,6 +137,7 @@ public class ProfileIndexHandler extends DefaultHandler {
}
}
+ @Override
protected void summarize() {
for (String packageID : packageProfileApplications.keySet()) {
URI applyingPackageURI = packageClients.get(packageID);
@@ -236,132 +150,13 @@ public class ProfileIndexHandler extends DefaultHandler {
packageToProfileApplications.put(applyingPackageURI, map);
}
for (URIPair profileApplication : profileApplications) {
- map.put(profileApplication.first, profileApplication.second);
+ // If we can't determine the Ecore definition, the profile is not properly applied
+ if (profileApplication.second != null) {
+ map.put(profileApplication.first, profileApplication.second);
+ }
}
}
}
}
}
-
- @Override
- public void endElement(String uri, String localName, String qName) throws SAXException {
- if (!ignore(qName, null)) {
- if (await.stopAt(pop())) {
- await.pop();
- }
-
- if (top != null) {
- currentPackage = top.nearestPackage();
- }
-
- if (top == null) {
- // We're done with UML content
- summarize();
- throw new OperationCanceledException();
- }
- }
- }
-
- //
- // Nested types
- //
-
- class UMLElement {
- final UMLElement parent;
-
- final String role;
- final String type;
- final String id;
- final String href;
-
- UMLElement(String qName, Attributes attributes) {
- parent = top;
-
- String type;
- if (qName.equals(eAnnotations)) {
- // "type" of an annotation is its source
- type = attributes.getValue("source"); //$NON-NLS-1$
- } else {
- type = attributes.getValue(xmiType);
- if (Strings.isNullOrEmpty(type)) {
- type = qName;
- }
- }
-
- this.role = qName;
- this.type = type;
- this.id = attributes.getValue(xmiID);
- this.href = attributes.getValue("href"); //$NON-NLS-1$
- }
-
- boolean isPackage() {
- return packageTypes.contains(type);
- }
-
- boolean isRole(String roleName) {
- return roleName.equals(role);
- }
-
- boolean isA(String xmiType) {
- return xmiType.equals(type);
- }
-
- URI getHREF() {
- return Strings.isNullOrEmpty(href) ? null : URI.createURI(href).resolve(fileURI);
- }
-
- UMLElement nearestPackage() {
- for (UMLElement next = this; next != null; next = next.parent) {
- if (next.isPackage()) {
- return next;
- }
- }
- return null;
- }
- }
-
- class Await {
- final Await parent = await;
-
- final String awaiting;
- final UMLElement limit;
-
- Await() {
- this(null);
- }
-
- private Await(String awaiting) {
- this.awaiting = awaiting;
- this.limit = top;
- }
-
- boolean isRoot() {
- return parent == null;
- }
-
- boolean isAwaiting(UMLElement element) {
- return !isRoot() && element.isRole(awaiting);
- }
-
- boolean stopAt(UMLElement element) {
- return !isRoot() && (limit == element);
- }
-
- Await push(String elementName) {
- Await result = new Await(elementName);
- await = result;
- return result;
- }
-
- void pop() {
- if (!isRoot()) {
- await = parent;
- }
- }
- }
-
- private static final class URIPair {
- URI first;
- URI second;
- }
}
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF
index 4ad466a277f..4b8669b29ce 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF
@@ -11,6 +11,7 @@ Export-Package: org.eclipse.papyrus.uml.tools,
org.eclipse.papyrus.uml.tools.model,
org.eclipse.papyrus.uml.tools.namereferences,
org.eclipse.papyrus.uml.tools.profile.definition,
+ org.eclipse.papyrus.uml.tools.profile.index,
org.eclipse.papyrus.uml.tools.providers,
org.eclipse.papyrus.uml.tools.service,
org.eclipse.papyrus.uml.tools.util
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/plugin.xml b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/plugin.xml
index 43ddfb3b9e6..06e8166d2f8 100644
--- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/plugin.xml
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/plugin.xml
@@ -134,4 +134,17 @@
</dependsOn>
</service>
</extension>
+ <extension
+ point="org.eclipse.papyrus.infra.core.language">
+ <provider>
+ <content-type
+ id="org.eclipse.uml2.uml">
+ </content-type>
+ <language
+ id="org.eclipse.papyrus.uml.language"
+ version="2.5"
+ name="UML">
+ </language>
+ </provider>
+ </extension>
</plugin>
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/IProfileIndex.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/IProfileIndex.java
new file mode 100644
index 00000000000..4bd4cb0af00
--- /dev/null
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/IProfileIndex.java
@@ -0,0 +1,59 @@
+/*****************************************************************************
+ * 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.uml.tools.profile.index;
+
+import java.util.Set;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.uml2.uml.Profile;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * <p>
+ * An index service that provides the profiles applied to resources in the workspace.
+ * It is expected that the index can provide the URIs of {@link Profile}s that are applied
+ * to some {@link org.eclipse.uml2.uml.Package Package} stored in a resource without the
+ * resource being loaded in the context of any particular resource set. The mechanism
+ * by which this is accomplished is not specified.
+ * </p>
+ * <p>
+ * A suitable implementation of this interface should be registered in the Papyrus Service Registry.
+ * </p>
+ */
+public interface IProfileIndex {
+ /**
+ * Queries whether the index covers the specified URI. For example, the index may cover
+ * only the workspace and not remote (e.g., HTTP) or filesystem (outside of the workspace)
+ * resources.
+ *
+ * @param umlResource
+ * the URI of a UML resource
+ *
+ * @return whether the index covers it
+ */
+ boolean indexes(URI umlResource);
+
+ /**
+ * Asynchronously obtains the profiles applied to packages in the specified UML resource.
+ *
+ * @param umlResource
+ * a resource storing UML model content
+ *
+ * @return a future set of URIs of the {@link Profile}s applied to packages within the UML resource.
+ * These are the URIs of the profile elements, themselves, not of the containing resource, because
+ * there may be any number of distinct profiles in a resource
+ */
+ ListenableFuture<Set<URI>> getAppliedProfiles(URI umlResource);
+}
diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/ProfileLanguageProvider.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/ProfileLanguageProvider.java
new file mode 100644
index 00000000000..5439ab70927
--- /dev/null
+++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/profile/index/ProfileLanguageProvider.java
@@ -0,0 +1,186 @@
+/*****************************************************************************
+ * 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.uml.tools.profile.index;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+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.BadStateException;
+import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
+import org.eclipse.papyrus.uml.tools.Activator;
+import org.eclipse.papyrus.uml.tools.model.UmlModel;
+import org.eclipse.uml2.uml.Profile;
+import org.osgi.framework.Bundle;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
+/**
+ * <p>
+ * A language provider based on UML profiles. It uses the {@link IProfileIndex} to efficiently
+ * determine the profiles applied to UML resources and return languages based on the applied
+ * profiles that it finds. It must be configured using the parameterized
+ * <tt>{@literal <implementation>}</tt> element of the language extension point. Parameters
+ * are of the form:
+ * </p>
+ * <table>
+ * <tr>
+ * <th>Name</th>
+ * <th>Value</th>
+ * <th>Comments</th>
+ * </tr>
+ * <tr valign="top">
+ * <td><tt>profile.<i>N</i></tt></td>
+ * <td>URI of a {@link Profile} element in a profile resource</td>
+ * <td>where <i>N</i> is an unique index 1, 2, etc. distinguishing the profile parameters</td>
+ * </tr>
+ * <tr valign="top">
+ * <td><tt>language.<i>N</i></tt></td>
+ * <td>name of class implementing the {@link ILanguage} interface on the declaring plug-in's classpath</td>
+ * <td>where <i>N</i> matches the corresponding profile parameter</td>
+ * </tr>
+ * </table>
+ */
+public class ProfileLanguageProvider implements ILanguageProvider, IExecutableExtension {
+ private static final Pattern NAME_PATTERN = Pattern.compile("(?:(profile)|language)\\.(\\d+)"); //$NON-NLS-1$
+
+ private IConfigurationElement config;
+ private Map<URI, String> languageClasses;
+
+ public ProfileLanguageProvider() {
+ super();
+ }
+
+ @Override
+ public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
+ this.config = config;
+
+ if (!(data instanceof Map<?, ?>)) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Initialization parameter 'data' is not a Map in language provider from contributor " + config.getContributor().getName()));
+ }
+
+ // Gather configuration parameters
+ @SuppressWarnings("unchecked")
+ Map<String, String> map = (Map<String, String>) data;
+
+ Map<String, URI> profileURIs = Maps.newHashMap();
+ Map<String, String> languages = Maps.newHashMap();
+ for (Map.Entry<String, String> next : map.entrySet()) {
+ Matcher m = NAME_PATTERN.matcher(next.getKey());
+ if (!m.find()) {
+ Activator.log.warn(String.format("Invalid profile language provider parameter name %s from contributor %s", next.getKey(), config.getContributor().getName())); //$NON-NLS-1$
+ } else {
+ if (m.group(1) != null) {
+ // it's a profile
+ profileURIs.put(m.group(2), URI.createURI(next.getValue(), true));
+ } else {
+ // it's a language
+ languages.put(m.group(2), next.getValue());
+ }
+ }
+ }
+
+ // Assemble the configuration parameters
+ languageClasses = Maps.newHashMap();
+ for (Map.Entry<String, URI> next : profileURIs.entrySet()) {
+ String languageClass = languages.remove(next.getKey());
+ if (languageClass == null) {
+ Activator.log.warn(String.format("Missing language class for profile %s from contributor %s", next.getValue(), config.getContributor().getName())); //$NON-NLS-1$
+ } else {
+ languageClasses.put(next.getValue(), languageClass);
+ }
+ }
+
+ // Any left over?
+ for (String next : languages.values()) {
+ Activator.log.warn(String.format("Missing profile URI for language class %s from contributor %s", next, config.getContributor().getName())); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public Iterable<ILanguage> getLanguages(ILanguageService languageService, URI modelURI, boolean uriHasFileExtension) {
+ Set<ILanguage> result = Sets.newHashSet();
+
+ if (!uriHasFileExtension) {
+ modelURI = modelURI.appendFileExtension(UmlModel.UML_FILE_EXTENSION);
+ }
+
+ try {
+ IProfileIndex index = null;
+ ServicesRegistry registry = languageService.getAdapter(ServicesRegistry.class);
+ if (registry != null) {
+ try {
+ index = registry.getService(IProfileIndex.class);
+ } catch (BadStateException e) {
+ // The ModelSet is started before the rest of the registry, and it doesn't know about the profile
+ // index service to start it. So, we must start the profile service explicitly
+ registry.startServicesByClassKeys(IProfileIndex.class);
+ index = registry.getService(IProfileIndex.class);
+ }
+ }
+
+ if (index != null) {
+ Set<URI> profiles = index.getAppliedProfiles(modelURI).get(10L, TimeUnit.MINUTES);
+ for (URI next : profiles) {
+ ILanguage language = getLanguage(next);
+ if (language != null) {
+ result.add(language);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Activator.log.error(String.format("Failed to access profile index for resource %s", modelURI), e); //$NON-NLS-1$
+ }
+
+ return result;
+ }
+
+ protected synchronized ILanguage getLanguage(URI profileURI) {
+ ILanguage result = null;
+
+ String className = languageClasses.get(profileURI);
+ if (className != null) {
+ Bundle bundle = Platform.getBundle(config.getContributor().getName());
+ if (bundle != null) {
+ try {
+ Class<?> class_ = bundle.loadClass(className);
+ if ((class_ == null) || !ILanguage.class.isAssignableFrom(class_)) {
+ languageClasses.remove(profileURI); // Don't try this again
+ Activator.log.error(String.format("Not a language class for profile %s in contributor %s: %s", profileURI, config.getContributor().getName(), className), null); //$NON-NLS-1$
+ } else {
+ result = class_.asSubclass(ILanguage.class).newInstance();
+ }
+ } catch (Exception e) {
+ languageClasses.remove(profileURI); // Don't try this again
+ Activator.log.error(String.format("Failed to instantiate language class %s for profile %s in contributor %s", className, profileURI, config.getContributor().getName()), e); //$NON-NLS-1$
+ }
+ }
+ }
+
+ return result;
+ }
+}

Back to the top