Skip to content

Commit

Permalink
Initial support for export contributions
Browse files Browse the repository at this point in the history
Generalize existing IAdditionalModelInferrer pattern to support
extending multiple services. For now we add services required for export
grouped together (fingerprint computer, resource description strategy,
qualified name provider) in IAdditionalExport.

We keep IAdditionalModelInferrer as is, but do not register it directly
anymore in an extension point. Instead in extension point we register
ILanguageContribution where we can register extensions for multiple
services. Check extension will then also come into this service. Later
we might need a similar thing for UI services when we are to extend
content assist.

Both *InferrersService and *ExportService get are language specific and
can be injected by languages that support extensions. These services
have default implementations (DefaultAdditionalExportService,
DefaultAdditionalInferrersService) which rely on global
ILanguageContributionService. ILanguageContribution service is the one
that may have different implementations for the standalone builder and
for Eclipse OSGi use case.

Issue dsldevkit#46
  • Loading branch information
Roman Mitin committed Feb 5, 2018
1 parent d95db3f commit 114bb41
Show file tree
Hide file tree
Showing 12 changed files with 401 additions and 9 deletions.
1 change: 1 addition & 0 deletions com.avaloq.tools.ddk.xtext/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Require-Bundle: com.google.guava;bundle-version="[10.0.0,19.0.0)",
org.eclipse.xtext.util
Export-Package: com.avaloq.tools.ddk.caching,
com.avaloq.tools.ddk.xtext.build,
com.avaloq.tools.ddk.xtext.contribution,
com.avaloq.tools.ddk.xtext.documentation,
com.avaloq.tools.ddk.xtext.formatting,
com.avaloq.tools.ddk.xtext.formatting.locators,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Evolution AG 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:
* Avaloq Evolution AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.xtext.contribution;

import com.avaloq.tools.ddk.xtext.modelinference.IAdditionalModelInferrer;
import com.avaloq.tools.ddk.xtext.resource.IAdditionalExport;


/**
* Default implementation providing no contribution.
*/
public abstract class AbstractLanguageContribution implements ILanguageContribution {

@Override
public IAdditionalModelInferrer getAdditionalModelInferrer() {
return null;
}

@Override
public IAdditionalExport getAdditionalExport() {
return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Evolution AG 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:
* Avaloq Evolution AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.xtext.contribution;

import com.avaloq.tools.ddk.xtext.modelinference.IAdditionalModelInferrer;
import com.avaloq.tools.ddk.xtext.resource.IAdditionalExport;


/**
* Extends the specified language.
*/
public interface ILanguageContribution {

/**
* Gets the full qualified name of the language supported.
*
* @return the language name
*/
String getTargetLanguageName();

/**
* Returns the additional model inferrer for this contribution, or {@code null} if no additional inference is necessary.
*
* @return a model inferrer, may be {@code null}
*/
IAdditionalModelInferrer getAdditionalModelInferrer();

/**
* Gets the contributions to export.
*
* @return the additional export
*/
IAdditionalExport getAdditionalExport();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Evolution AG 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:
* Avaloq Evolution AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.xtext.contribution;

import java.util.Collection;


/**
* Global service that manages contributions to all languages.
* <p>
* This service is implemented as a global singleton, one instance for all DSLs.
* </p>
*/
public interface ILanguageContributionService {

/**
* Gets all contributions for the given language.
*
* @param languageName
* the language name
* @return the collection of contributions, may be empty, never {@code null}
*/
Collection<ILanguageContribution> getContributions(String languageName);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Evolution AG 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:
* Avaloq Evolution AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.xtext.modelinference;

import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.util.IAcceptor;

import com.avaloq.tools.ddk.xtext.contribution.ILanguageContribution;
import com.avaloq.tools.ddk.xtext.contribution.ILanguageContributionService;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;


/**
* Default implementation relying on {@link ILanguageContribution}.
*/
public class DefaultAdditionalInferrersService implements IAdditionalInferrersService {

@Inject
@Named(Constants.LANGUAGE_NAME)
private String languageName;

@Inject
private ILanguageContributionService contributionService;

@Inject
private Injector injector;

private List<IAdditionalModelInferrer> inferrers;

@Override
public void inferTargetModel(final EObject sourceModelElement, final IAcceptor<EObject> acceptor, final boolean isPrelinkingPhase) {
if (inferrers == null) {
List<IAdditionalModelInferrer> unsortedInferrers = Lists.newArrayList();
for (ILanguageContribution contribution : contributionService.getContributions(languageName)) {
IAdditionalModelInferrer inferrer = contribution.getAdditionalModelInferrer();
if (inferrer != null) {
injector.injectMembers(inferrer);
unsortedInferrers.add(inferrer);
}
}
// We order the inferrers found by their class name to keep a strict ordering
inferrers = unsortedInferrers.stream().sorted().collect(Collectors.toList());
}
for (IAdditionalModelInferrer inferrer : inferrers) {
inferrer.inferTargetModel(sourceModelElement, acceptor, isPrelinkingPhase);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
@Singleton
public class ExtendableInferredModelAssociator extends InferredModelAssociator {

@Inject(optional = true)
@Inject
private IAdditionalInferrersService additionalInferrers;

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@

package com.avaloq.tools.ddk.xtext.modelinference;

import com.google.inject.ImplementedBy;


/**
* This service allows additional inference for a given DSL to be executed outside of its plugin.
*/
@ImplementedBy(DefaultAdditionalInferrersService.class)
public interface IAdditionalInferrersService extends IModelInferrer {

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@
*/
public interface IAdditionalModelInferrer extends IModelInferrer, Comparable<IAdditionalModelInferrer> {

/**
* Gets the full qualified name of the language supported.
*
* @return the language name
*/
String getTargetLanguageName();

/**
* Compares this {@link IAdditionalModelInferrer} with the specified Compares this object with the specified object for order.
* The name of the class implementing this interface is used for the comparison.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ protected CharSequence computeSelectorFragmentSegment(final EObject obj, final E
return result;
}

/**
* Allows override for delegation to extensions.
*
* @param object
* object to compute fragment for
* @return fragment, never {@code null}
*/
protected CharSequence getFragmentSegmentFallback(final EObject object) {
return shortFragmentProvider.getFragmentSegment(object);
}

/**
* {@inheritDoc}
* <p>
Expand All @@ -100,7 +111,7 @@ protected CharSequence computeSelectorFragmentSegment(final EObject obj, final E
// TODO DSL-348: change generator for fragment providers to implement getFragmentSegment instead of getFragment
@Override
public CharSequence getFragmentSegment(final EObject object) {
return shortFragmentProvider.getFragmentSegment(object);
return getFragmentSegmentFallback(object);
}

/** {@inheritDoc} */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*******************************************************************************
* Copyright (c) 2016 Avaloq Evolution AG 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:
* Avaloq Evolution AG - initial API and implementation
*******************************************************************************/

package com.avaloq.tools.ddk.xtext.resource;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.Constants;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.util.IAcceptor;

import com.avaloq.tools.ddk.xtext.contribution.ILanguageContribution;
import com.avaloq.tools.ddk.xtext.contribution.ILanguageContributionService;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.google.inject.name.Named;


/**
* Default implementation relying on {@link ILanguageContribution}.
*/
@Singleton
public class DefaultAdditionalExportService implements IAdditionalExportService {

@Inject
@Named(Constants.LANGUAGE_NAME)
private String languageName;

@Inject
private ILanguageContributionService contributionService;

@Inject
private Injector injector;

private List<IAdditionalExport> exportContributions;

@Override
public QualifiedName qualifiedName(final EObject object) {
return returnFirst(c -> c.qualifiedName(object));
}

@Override
public String computeFingerprint(final EObject object) {
return returnFirst(c -> c.computeFingerprint(object));
}

@Override
public CharSequence getFragmentSegment(final EObject object) {
return returnFirst(c -> c.getFragmentSegment(object));
}

@Override
public Set<EClass> getExportedEClasses(final Resource resource) {
ensureInitilaized();
return exportContributions.stream().flatMap(c -> c.getExportedEClasses(resource).stream()).collect(Collectors.toSet());
}

@Override
public boolean doCreateEObjectDescriptions(final EObject object, final IAcceptor<IEObjectDescription> acceptor) {
boolean traverse = false;
for (IAdditionalExport contribution : exportContributions) {
traverse |= contribution.doCreateEObjectDescriptions(object, acceptor);
}
return traverse;
}

private <T> T returnFirst(final Function<IAdditionalExport, T> mapper) {
ensureInitilaized();
return exportContributions.stream().map(mapper).filter(Objects::nonNull).findFirst().orElse(null);
}

private void ensureInitilaized() {
if (exportContributions == null) {
exportContributions = Lists.newArrayList();
for (ILanguageContribution contribution : contributionService.getContributions(languageName)) {
IAdditionalExport export = contribution.getAdditionalExport();
if (export != null) {
injector.injectMembers(export);
exportContributions.add(export);
}
}
}
}

}
Loading

0 comments on commit 114bb41

Please sign in to comment.