diff options
author | Christian W. Damus | 2015-06-04 13:33:56 +0000 |
---|---|---|
committer | Camille Letavernier | 2015-06-05 08:25:19 +0000 |
commit | d2dc2fd8777bbd50aa0ac10845b156d64be5695d (patch) | |
tree | 7c39ae39f95ff2040c17b9225bee3e2201485fa8 /plugins/infra | |
parent | d5706b93beee4419dc3634b24b6e6300441c9659 (diff) | |
download | org.eclipse.papyrus-d2dc2fd8777bbd50aa0ac10845b156d64be5695d.tar.gz org.eclipse.papyrus-d2dc2fd8777bbd50aa0ac10845b156d64be5695d.tar.xz org.eclipse.papyrus-d2dc2fd8777bbd50aa0ac10845b156d64be5695d.zip |
Bug 468030: [Papyrus Core] Papyrus DI Model should support the notion of Language
https://bugs.eclipse.org/bugs/show_bug.cgi?id=468030
Implement a Language Service for configuration of the Papyrus ModelSet to
support custom implementations of UML and other modeling languages.
Includes:
* language service providing languages before the ModelSet loads any resources
* hooks for languages to configure and unconfigure a ModelSet
* implementation of a language provider that maps applied profiles to languages
* addition of a profile index service that provides the URIs of profiles applied
to model resources without having to load them in a resource set
* a stub of a UML-RT language with placeholder for configuration of the ModelSet
* a standard UML language
* implementation of the profile index service using an enhanced DecoratorModelIndex
that now also index the normal profile applications in user models
Papyrus Service Registry changes:
* fix the explicit starting of lazy services
* new AbstractServiceUtils API for requesting optional or defaultable services
Also includes the very barest of JUnit tests covering:
* the new profile index service
* using the language service to query content-type-based languages
* UML languages: UML models have the UML language, itself, and proper
matching and installation of profile languages
Change-Id: I9d5175cfbefbe40864f04ea4215e18556e3739df
Reviewed-on: https://git.eclipse.org/r/49152
Tested-by: Hudson CI
Reviewed-by: Camille Letavernier <camille.letavernier@cea.fr>
Diffstat (limited to 'plugins/infra')
19 files changed, 1482 insertions, 26 deletions
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF index 850c6ec0719..44505d47c1a 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Export-Package: org.eclipse.papyrus.infra.core, org.eclipse.papyrus.infra.core.extension, org.eclipse.papyrus.infra.core.extension.commands, org.eclipse.papyrus.infra.core.extension.diagrameditor, + org.eclipse.papyrus.infra.core.language, org.eclipse.papyrus.infra.core.lifecycleevents, org.eclipse.papyrus.infra.core.listenerservice, org.eclipse.papyrus.infra.core.markers, diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml index 1ec4a12a9ec..7a1ae21bd95 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/plugin.xml @@ -8,6 +8,7 @@ <extension-point id="model" name="plugin.xml.ModelName" schema="schema/model.exsd"/>
<extension-point id="transactionalEditingDomainProvider" name="transactionalEditingDomainProvider" schema="schema/transactionalEditingDomainProvider.exsd"/>
<extension-point id="sashModelProvider" name="Sash Model Providers" schema="schema/sashModelProvider.exsd"/>
+ <extension-point id="language" name="Modeling Language" schema="schema/language.exsd"/>
<extension
point="org.eclipse.ui.menus">
@@ -394,5 +395,14 @@ type="org.eclipse.papyrus.infra.core.services.IService">
</adapter>
</factory>
+ </extension>
+ <extension
+ point="org.eclipse.papyrus.infra.core.service">
+ <service
+ classname="org.eclipse.papyrus.infra.core.internal.language.LanguageService"
+ id="org.eclipse.papyrus.infra.core.language.ILanguageService"
+ priority="10"
+ startKind="startup">
+ </service>
</extension> </plugin>
diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd b/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd new file mode 100644 index 00000000000..516be1d8310 --- /dev/null +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/schema/language.exsd @@ -0,0 +1,219 @@ +<?xml version='1.0' encoding='UTF-8'?> +<!-- Schema file written by PDE --> +<schema targetNamespace="org.eclipse.papyrus.infra.core" xmlns="http://www.w3.org/2001/XMLSchema"> +<annotation> + <appInfo> + <meta.schema plugin="org.eclipse.papyrus.infra.core" id="language" name="Modeling Language"/> + </appInfo> + <documentation> + Registration of Modeling Languages in the Papyrus Language Service. + </documentation> + </annotation> + + <element name="extension"> + <annotation> + <appInfo> + <meta.element /> + </appInfo> + </annotation> + <complexType> + <sequence> + <element ref="provider" minOccurs="1" maxOccurs="unbounded"/> + </sequence> + <attribute name="point" type="string" use="required"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="id" type="string"> + <annotation> + <documentation> + + </documentation> + </annotation> + </attribute> + <attribute name="name" type="string"> + <annotation> + <documentation> + + </documentation> + <appInfo> + <meta.attribute translatable="true"/> + </appInfo> + </annotation> + </attribute> + </complexType> + </element> + + <element name="provider"> + <annotation> + <documentation> + Registration of a Papryus modeling language provider. + </documentation> + </annotation> + <complexType> + <sequence> + <element ref="class" minOccurs="0" maxOccurs="1"/> + <choice minOccurs="0" maxOccurs="unbounded"> + <element ref="content-type"/> + <element ref="language"/> + </choice> + </sequence> + <attribute name="class" type="string"> + <annotation> + <documentation> + The name of a language provider class. If not specified, then nested elements are required to specify a generic provider that matches URIs to languages. +The class may also be specified by the nested <tt>&lt;class&gt;</tt> 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 <tt>class</tt> attribute of the <tt>&lt;provider&gt;</tt> element is specified. +Required if the <tt>&lt;language&gt;</tt> 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 <tt>class</tt> attribute or <tt>&lt;implementation&gt;</tt> element of the <tt>&lt;provider&gt;</tt> element is specified. +Required if the <tt>&lt;contentType&gt;</tt> 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 <tt>class</tt> 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;
+ }
+ }
}
|