blob: 3b8995654f77fe4e971b23317616dab5f9d7467f [file] [log] [blame]
/**
* Copyright (c) 2011, 2015 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Florian Pirchner - Initial implementation
*/
package org.eclipse.osbp.xtext.builder.metadata.services.impl;
import static com.google.common.collect.Iterables.addAll;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.osbp.dsl.xtext.types.bundles.BundleSpace;
import org.eclipse.osbp.dsl.xtext.types.bundles.BundleSpaceTypeProvider;
import org.eclipse.osbp.runtime.common.types.IBundleSpace;
import org.eclipse.osbp.xtext.builder.metadata.services.IBuilderParticipant;
import org.eclipse.osbp.xtext.builder.metadata.services.IMetadataBuilderService;
import org.eclipse.osbp.xtext.builder.metadata.services.IUnitOfWork;
import org.eclipse.xtext.common.types.JvmDeclaredType;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.common.types.access.impl.IndexedJvmTypeAccess;
import org.eclipse.xtext.findReferences.IReferenceFinder;
import org.eclipse.xtext.findReferences.IReferenceFinder.Acceptor;
import org.eclipse.xtext.findReferences.ReferenceFinder;
import org.eclipse.xtext.findReferences.TargetURIs;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsData.ResourceSetAdapter;
import org.eclipse.xtext.validation.CheckMode;
import org.eclipse.xtext.validation.IResourceValidator;
import org.eclipse.xtext.validation.Issue;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
// TODO: Auto-generated Javadoc
/**
* The Class MetadataBuilder.
*
* @author riegel
*/
@SuppressWarnings("restriction")
@Component(immediate = true, service = {})
public class MetadataBuilder implements BundleListener {
/** The Constant LOGGER. */
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(MetadataBuilder.class);
// The service impl, which is being registered in the service registry
/** The service impl. */
private ServiceImpl serviceImpl;
/** The context. */
// Is used to sync calls to the bundle space
private ComponentContext context;
/** The resource set. */
private XtextResourceSet resourceSet;
/** The index. */
private ResourceDescriptionsData index;
/** The grammar registry. */
private IResourceServiceProvider.Registry grammarRegistry;
/** The injector. */
private Injector injector;
/** The reference finder. */
private ReferenceFinder referenceFinder;
/** The converter. */
private IQualifiedNameConverter converter;
/** The jvm type access. */
private IndexedJvmTypeAccess jvmTypeAccess;
/** The bundle space. */
private IBundleSpace bundleSpace;
/** The type provider. */
private BundleSpaceTypeProvider typeProvider;
/** The model providers. */
// participants
private Set<Bundle> modelProviders = Collections.synchronizedSet(new HashSet<Bundle>());
/** The participants. */
private List<IBuilderParticipant> participants = Collections.synchronizedList(new ArrayList<IBuilderParticipant>());
/** The injected participants. */
private Set<IBuilderParticipant> injectedParticipants = Collections
.synchronizedSet(new HashSet<IBuilderParticipant>());
/** The waiting for framework started event. */
// lifecycle
private AtomicBoolean waitingForFrameworkStartedEvent = new AtomicBoolean(true);
/** The resolved. */
private AtomicBoolean resolved = new AtomicBoolean(false);
/** The bundle space reg. */
// service registrations
private ServiceRegistration<IBundleSpace> bundleSpaceReg;
/** The service impl reg. */
private ServiceRegistration<IMetadataBuilderService> serviceImplReg;
/**
* Activate.
*
* @param context
* the context
*/
@Activate
public synchronized void activate(ComponentContext context) {
this.context = context;
this.serviceImpl = new ServiceImpl();
new ServiceActivatedTask().run();
}
/**
* Checks if is active.
*
* @return true, if is active
*/
private boolean isActive() {
return context != null;
}
/**
* {@inheritDoc}
*/
@Deactivate
public synchronized void deactivate() {
new ServiceDeactivatedTask().run();
this.context = null;
}
/**
* Returns the eObject for the given fully qualified name and type.
*
* @param fqn
* the fqn
* @param type
* the type
* @return the e object for FQN
*/
public EObject getEObjectForFQN(QualifiedName fqn, EClass type) {
EObject result = null;
ISelectable resourceDescriptions = ResourceDescriptionsData.ResourceSetAdapter
.findResourceDescriptionsData(resourceSet);
Iterable<IEObjectDescription> descriptions = resourceDescriptions.getExportedObjects(type, fqn, false);
for (IEObjectDescription desc : descriptions) {
result = desc.getEObjectOrProxy();
if (result.eIsProxy()) {
result = EcoreUtil.resolve(result, resourceSet);
}
break;
}
return result;
}
/**
* Returns true, if the bundle contains the header.
*
* @param bundle
* the bundle
* @param header
* the header
* @return true, if successful
*/
private boolean containsHeader(Bundle bundle, String header) {
Dictionary<String, String> headers = bundle.getHeaders();
Enumeration<String> keys = headers.keys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
if (key.equals(header)) {
return true;
}
}
return false;
}
/**
* Activates the given participant.
*
* @param participant
* the participant
*/
private void doNotifyParticipantActivate(IBuilderParticipant participant) {
if (isActive()) {
// Tell the participant to activate its state
participant.notifyLifecyle(
new IBuilderParticipant.LifecycleEvent(IBuilderParticipant.LifecycleEvent.ACTIVATED));
}
}
/**
* Do notify participant bundles scanned.
*
* @param participant
* the participant
*/
private void doNotifyParticipantBundlesScanned(IBuilderParticipant participant) {
if (isActive()) {
// Tell the participant to activate its state
participant.notifyLifecyle(
new IBuilderParticipant.LifecycleEvent(IBuilderParticipant.LifecycleEvent.BUNDLES_SCANNED));
}
}
/**
* Removes the models contained in the given bundle from the index.
*
* @param bundle
* the bundle
*/
private void removeFromIndex(Bundle bundle) {
if (!isActive()) {
return;
}
List<URL> urls = doFindModels(bundle);
if (urls.isEmpty()) {
return;
}
removeFromIndex(urls);
}
/**
* Removes the models contained in the given bundle from the index.
*
* @param participant
* the participant
*/
private void removeFromIndex(IBuilderParticipant participant) {
if (!resolved.get()) {
return;
}
List<URL> urls = doFindAllModelsToRemoveForParticipant(participant);
if (urls.isEmpty()) {
return;
}
removeFromIndex(urls);
}
/**
* Validate.
*
* @param resourceSet
* the resource set
* @return the list
*/
protected List<Issue> validate(ResourceSet resourceSet) {
if (resourceSet.getResources() == null) {
return Collections.emptyList();
}
List<Issue> issues = Lists.newArrayList();
List<Resource> resources = Lists.newArrayList(resourceSet.getResources());
for (Resource resource : resources) {
IResourceServiceProvider resourceServiceProvider = IResourceServiceProvider.Registry.INSTANCE
.getResourceServiceProvider(resource.getURI());
if (resourceServiceProvider != null) {
IResourceValidator resourceValidator = resourceServiceProvider.getResourceValidator();
List<Issue> result = resourceValidator.validate(resource, CheckMode.ALL, null);
addAll(issues, result);
}
}
return issues;
}
/**
* Looks up for all models available.
*
* @param suspect
* the suspect
* @return the list
*/
private List<URL> doFindModels(Bundle suspect) {
List<URL> result = new ArrayList<URL>();
// iterate all participants
synchronized (participants) {
for (IBuilderParticipant participant : participants) {
result.addAll(doFindModels(suspect, participant));
}
}
if (result.size() > 0) {
modelProviders.add(suspect);
}
return result;
}
/**
* Returns all models for the given bundle and participant.
*
* @param suspect
* the suspect
* @param participant
* the participant
* @return the list
*/
private List<URL> doFindModels(Bundle suspect, IBuilderParticipant participant) {
return participant.getModels(suspect);
}
/**
* Returns all models for the given participant. Therefore <b>only known
* model provider bundles</b> are used.
*
* @param participant
* the participant
* @return the list
*/
private List<URL> doFindAllModelsToRemoveForParticipant(IBuilderParticipant participant) {
List<URL> result = new ArrayList<URL>();
synchronized (modelProviders) {
for (Bundle bundle : modelProviders) {
result.addAll(doFindModels(bundle, participant));
}
}
return result;
}
/**
* Returns all models for the given participant. Therefore <b>ALL</b>
* bundles are used.
*
* @param participant
* the participant
* @return the list
*/
private List<URL> doFindAllModelsForNewParticipant(IBuilderParticipant participant) {
List<URL> result = new ArrayList<URL>();
for (Bundle bundle : context.getBundleContext().getBundles()) {
List<URL> temp = doFindModels(bundle, participant);
if (temp.size() > 0) {
doAddToBundleSpace(bundle);
modelProviders.add(bundle);
}
result.addAll(temp);
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.
* BundleEvent)
*/
@Override
public synchronized void bundleChanged(BundleEvent event) {
if (event.getType() == BundleEvent.STARTED) {
new BundleAddedTask(event.getBundle()).run();
} else if (event.getType() == BundleEvent.STOPPED) {
new BundleRemovedTask(event.getBundle()).run();
}
}
/**
* Called by OSGi-DS.
*
* @param participant
* the participant
*/
@Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE, policy = ReferencePolicy.DYNAMIC, unbind = "removeParticipant")
public synchronized void addParticipant(IBuilderParticipant participant) {
try {
new ParticipantAddedTask(participant).run();
} catch (Exception ex) {
LOGGER.error("{}", ex);
}
}
/**
* Resolves all models for all proper model bundles.
*/
private void doScanAllBundles() {
List<String> urls = new ArrayList<>();
for (Bundle bundle : context.getBundleContext().getBundles()) {
if (bundle.getState() <= 2) {
continue;
}
if (containsHeader(bundle, IMetadataBuilderService.LUN_RUNTIME_BUILDER_BUNDLE_SPACE)) {
doAddToBundleSpace(bundle);
}
for (URL url : doFindModels(bundle)) {
LOGGER.info("Adding model " + url.toString() + " to model index.");
if (filter(url)) {
continue;
}
urls.add(url.toString());
// adds the bundle to the bundleSpace if header is available
doAddToBundleSpace(bundle);
}
}
/*
* due to a bug in org.eclipse.bpmn2 the Bpmn2OppositeReferenceAdapter
* is an EContentAdapter and attached to every mode element in the
* resource set. Causing every resource to become loaded too early.
* Therefore we sort bpmn2 extensions to the end of the loading list.
*/
Collections.sort(urls, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
URI uri1 = URI.createURI(o1);
URI uri2 = URI.createURI(o2);
String e1 = uri1.fileExtension();
String e2 = uri2.fileExtension();
if (e2.equals("bpmn2")) {
return -1;
}
if (e1.equals("bpmn2")) {
return 1;
}
return e1.compareTo(e2);
}
});
for (String url : urls) {
addToIndex(URI.createURI(url), true);
}
LOGGER.info("Models indexed. In case of error, see messages before.");
}
/**
* Unloads all resources in the resource set.
*/
private void doUnloadAllResources() {
for (Resource resource : new ArrayList<>(resourceSet.getResources())) {
if (!resource.getURI().scheme().equals("java")) {
resource.unload();
}
}
LOGGER.info("Unloaded all resources.");
}
/**
* Filter.
*
* @param url
* the url
* @return true, if successful
*/
private boolean filter(URL url) {
if (url.toExternalForm().contains("META-INF/maven")) {
return true;
}
return false;
}
/**
* Resolves all models for the given participant. Therefore <b>all
* bundles</b> are used.
*
* @param participant
* the participant
*/
private void doScanAllBundles(IBuilderParticipant participant) {
for (URL url : doFindAllModelsForNewParticipant(participant)) {
if (filter(url)) {
continue;
}
LOGGER.info("Adding model " + url.toString() + " to model index.");
addToIndex(URI.createURI(url.toString()), true);
}
LOGGER.info("Models indexed. In case of error, see messages before.");
}
/**
* Do notify participants bundles scanned.
*/
private void doNotifyParticipantsBundlesScanned() {
synchronized (participants) {
for (IBuilderParticipant participant : participants) {
doNotifyParticipantBundlesScanned(participant);
}
}
}
/**
* Do notify participant initialize.
*
* @param participant
* the participant
*/
private void doNotifyParticipantInitialize(IBuilderParticipant participant) {
participant
.notifyLifecyle(new IBuilderParticipant.LifecycleEvent(IBuilderParticipant.LifecycleEvent.INITIALIZE));
}
/**
* Do inject participant.
*
* @param participant
* the participant
*/
private void doInjectParticipant(IBuilderParticipant participant) {
if (isActive() && !injectedParticipants.contains(participant)) {
injector.injectMembers(participant);
injectedParticipants.add(participant);
}
}
/**
* Handle task finish.
*
* @param task
* the task
*/
protected void handleTaskFinish(ITask task) {
if (task.getClass() == ServiceActivatedTask.class) {
new WaitForFrameworkTask().run();
} else if (task.getClass() == WaitForFrameworkTask.class) {
new InitialIndexingTask().run();
} else if (task.getClass() == InitialIndexingTask.class) {
registerOSGiService();
// nothing to do for now. All listeners are installed properly
}
}
/**
* Register the service impl as an OSGi service.
*/
private void registerOSGiService() {
serviceImplReg = context.getBundleContext().registerService(IMetadataBuilderService.class, serviceImpl, null);
}
/**
* Called by OSGi-DS.
*
* @param participant
* the participant
*/
public synchronized void removeParticipant(IBuilderParticipant participant) {
try {
new ParticipantRemovedTask(participant).run();
} catch (Exception ex) {
LOGGER.error("{}", ex);
}
}
/**
* Do deactivate participant.
*
* @param participant
* the participant
*/
private void doDeactivateParticipant(IBuilderParticipant participant) {
// remove all models for the given participant from the index
removeFromIndex(participant);
participants.remove(participant);
injectedParticipants.remove(participant);
// tell the participant to deactivate
participant
.notifyLifecyle(new IBuilderParticipant.LifecycleEvent(IBuilderParticipant.LifecycleEvent.DEACTIVATED));
}
/**
* Adds the bundle to the bundle space if the header is available.
*
* @param bundle
* the bundle
*/
private void doAddToBundleSpace(Bundle bundle) {
synchronized (bundleSpace) {
bundleSpace.add(bundle);
}
}
/**
* Removes the given bundle from the bundle space.
*
* @param bundle
* the bundle
*/
private void doRemoveFromBundleSpace(Bundle bundle) {
synchronized (bundleSpace) {
bundleSpace.remove(bundle);
}
}
/**
* Adds the to index.
*
* @param uri
* the uri
* @param load
* TODO
*/
protected void addToIndex(URI uri, boolean load) {
Resource resource = resourceSet.getResource(uri, load);
if (resource.isLoaded()) {
IResourceDescription.Manager manager = grammarRegistry.getResourceServiceProvider(uri)
.get(IResourceDescription.Manager.class);
try {
index.addDescription(uri, manager.getResourceDescription(resource));
} catch (Exception ex) {
LOGGER.error("Exception adding " + uri + " to index. See: {}", ex);
}
// unload the resource again
resource.unload();
}
}
/**
* Removes the given URLs from the index.
*
* @param urls
* the urls
*/
protected void removeFromIndex(List<URL> urls) {
if (!isActive()) {
return;
}
for (URL url : urls) {
if (filter(url)) {
continue;
}
LOGGER.info("Unregistered " + url.toString());
removeFromIndex(URI.createURI(url.toString()));
}
}
/**
* Removes the from index.
*
* @param uri
* the uri
*/
protected void removeFromIndex(URI uri) {
index.removeDescription(uri);
}
/**
* An internal task that processes different kinds of issues.
*/
interface ITask {
/**
* Run.
*/
void run();
}
/**
* This task handles waiting for the framework start. Only if the framework
* became started, the models may become indexed.
*/
private class WaitForFrameworkTask implements ITask, FrameworkListener {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
// get the state of the framework
Bundle framework = context.getBundleContext().getBundle(0);
if (framework.getState() == Bundle.ACTIVE) {
waitingForFrameworkStartedEvent.set(false);
}
if (waitingForFrameworkStartedEvent.get()) {
context.getBundleContext().addFrameworkListener(this);
} else {
notifyFrameworkStarted();
}
}
/*
* (non-Javadoc)
*
* @see org.osgi.framework.FrameworkListener#frameworkEvent(org.osgi.
* framework.FrameworkEvent)
*/
@Override
public void frameworkEvent(FrameworkEvent event) {
if (event.getType() == FrameworkEvent.STARTED) {
context.getBundleContext().removeFrameworkListener(this);
notifyFrameworkStarted();
}
}
/**
* Notify framework started.
*/
private void notifyFrameworkStarted() {
waitingForFrameworkStartedEvent.set(false);
handleTaskFinish(this);
}
}
/**
* This task will be called, if the service service was activated.
*/
private class ServiceActivatedTask implements ITask {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
doSetupService();
doInjectParticipants();
doInitializeParticipants();
doActivateParticipants();
handleTaskFinish(this);
}
/**
* Does the setup.
*/
protected void doSetupService() {
injector = Guice.createInjector(new MetadataBuilderModule(serviceImpl));
converter = injector.getInstance(IQualifiedNameConverter.class);
resourceSet = injector.getInstance(XtextResourceSet.class);
index = new ResourceDescriptionsData(new ArrayList<IResourceDescription>());
ResourceDescriptionsData.ResourceSetAdapter.installResourceDescriptionsData(resourceSet, index);
grammarRegistry = injector.getInstance(IResourceServiceProvider.Registry.class);
jvmTypeAccess = injector.getInstance(IndexedJvmTypeAccess.class);
bundleSpace = injector.getInstance(BundleSpace.class);
referenceFinder = injector.getInstance(ReferenceFinder.class);
// register the bundle space
bundleSpaceReg = context.getBundleContext().registerService(IBundleSpace.class, bundleSpace, null);
bundleSpace.add(context.getBundleContext().getBundle());
// Create the bundle space for class loading issues
typeProvider = new BundleSpaceTypeProvider(bundleSpace, resourceSet, jvmTypeAccess);
resourceSet.setClasspathURIContext(bundleSpace);
}
/**
* Do initialize participants.
*/
private void doInitializeParticipants() {
for (IBuilderParticipant participant : participants.toArray(new IBuilderParticipant[participants.size()])) {
doNotifyParticipantInitialize(participant);
}
}
/**
* Do inject participants.
*/
private void doInjectParticipants() {
for (IBuilderParticipant participant : participants) {
doInjectParticipant(participant);
}
}
/**
* Activate all participants.
*/
private void doActivateParticipants() {
for (IBuilderParticipant participant : participants.toArray(new IBuilderParticipant[participants.size()])) {
doNotifyParticipantActivate(participant);
}
}
}
/**
* The Class InitialIndexingTask.
*/
private class InitialIndexingTask implements ITask {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
doScanAllBundles();
doUnloadAllResources();
doNotifyParticipantsBundlesScanned();
startBundleTracking();
resolved.set(true);
handleTaskFinish(this);
}
/**
* Starts the tracking of bundles.
*/
private synchronized void startBundleTracking() {
context.getBundleContext().addBundleListener(MetadataBuilder.this);
}
}
/**
* The Class AddBundleToBundleSpaceTask.
*/
private class AddBundleToBundleSpaceTask implements ITask {
/** The bundle. */
private final Bundle bundle;
/**
* Instantiates a new adds the bundle to bundle space task.
*
* @param bundle
* the bundle
*/
public AddBundleToBundleSpaceTask(Bundle bundle) {
this.bundle = bundle;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
doAddToBundleSpace(bundle);
}
}
/**
* The Class RemoveBundleFromBundleSpaceTask.
*/
private class RemoveBundleFromBundleSpaceTask implements ITask {
/** The bundle. */
private final Bundle bundle;
/**
* Instantiates a new removes the bundle from bundle space task.
*
* @param bundle
* the bundle
*/
public RemoveBundleFromBundleSpaceTask(Bundle bundle) {
this.bundle = bundle;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
doRemoveFromBundleSpace(bundle);
}
}
/**
* The Class BundleAddedTask.
*/
private class BundleAddedTask implements ITask {
/** The bundle. */
private final Bundle bundle;
/**
* Instantiates a new bundle added task.
*
* @param bundle
* the bundle
*/
public BundleAddedTask(Bundle bundle) {
this.bundle = bundle;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
// started bundles need to expose the MANIFEST header
if (containsHeader(bundle, IMetadataBuilderService.LUN_RUNTIME_BUILDER_BUNDLE_SPACE)) {
doAddToBundleSpace(bundle);
}
// if the bundle was not scanned yet
if (!modelProviders.contains(bundle)) {
doIndexAddedBundle(bundle);
}
}
/**
* Indexes the models contained in the given bundle.
*
* @param bundle
* the bundle
*/
private synchronized void doIndexAddedBundle(Bundle bundle) {
List<URL> urls = doFindModels(bundle);
if (urls.isEmpty()) {
return;
}
for (URL url : urls) {
LOGGER.info("Added " + url.toString() + " to metadata cache.");
addToIndex(URI.createURI(url.toString()), true);
}
}
}
/**
* The Class BundleRemovedTask.
*/
private class BundleRemovedTask implements ITask {
/** The bundle. */
private final Bundle bundle;
/**
* Instantiates a new bundle removed task.
*
* @param bundle
* the bundle
*/
public BundleRemovedTask(Bundle bundle) {
this.bundle = bundle;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
if (!isActive()) {
return;
}
// remove the bundle from the bundle space
serviceImpl.removeFromBundleSpace(bundle);
// the bundle was already scanned
if (!modelProviders.contains(bundle)) {
return;
}
removeFromIndex(bundle);
modelProviders.remove(bundle);
}
}
/**
* The Class ServiceDeactivatedTask.
*/
private class ServiceDeactivatedTask implements ITask {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
if (bundleSpaceReg != null) {
bundleSpaceReg.unregister();
bundleSpaceReg = null;
}
if (serviceImplReg != null) {
serviceImplReg.unregister();
serviceImplReg = null;
}
context.getBundleContext().removeBundleListener(MetadataBuilder.this);
modelProviders.clear();
serviceImpl = null;
resourceSet = null;
index = null;
grammarRegistry = null;
resolved.set(false);
}
}
/**
* The Class ParticipantAddedTask.
*/
private class ParticipantAddedTask implements ITask {
/** The participant. */
private final IBuilderParticipant participant;
/**
* Instantiates a new participant added task.
*
* @param participant
* the participant
*/
public ParticipantAddedTask(IBuilderParticipant participant) {
this.participant = participant;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
if (!participants.contains(participant)) {
doInjectParticipant(participant);
participants.add(participant);
if (isActive()) {
// activate the participant
doNotifyParticipantInitialize(participant);
// activate the participant
doNotifyParticipantActivate(participant);
// scan all bundles to find proper models
doScanAllBundles(participant);
doNotifyParticipantBundlesScanned(participant);
}
}
}
}
/**
* The Class ParticipantRemovedTask.
*/
private class ParticipantRemovedTask implements ITask {
/** The participant. */
private final IBuilderParticipant participant;
/**
* Instantiates a new participant removed task.
*
* @param participant
* the participant
*/
public ParticipantRemovedTask(IBuilderParticipant participant) {
this.participant = participant;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.osbp.xtext.builder.metadata.services.impl.MetadataBuilder
* .ITask#run()
*/
@Override
public void run() {
doDeactivateParticipant(participant);
}
}
/**
* This service is immediatelly available for participants by @Inject
* IMetadataBuilderService. But it won't become registered as an OSGi
* service, before the models became resolved.
*/
public class ServiceImpl implements IMetadataBuilderService {
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#getAllDescriptions(org.eclipse.emf.ecore.
* EClass)
*/
@Override
public Iterable<IEObjectDescription> getAllDescriptions(EClass type) {
ISelectable resourceDescriptions = ResourceDescriptionsData.ResourceSetAdapter
.findResourceDescriptionsData(resourceSet);
Iterable<IEObjectDescription> descriptions = resourceDescriptions.getExportedObjectsByType(type);
return descriptions;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#addToBundleSpace(org.osgi.framework.Bundle)
*/
@Override
public synchronized void addToBundleSpace(Bundle bundle) {
new AddBundleToBundleSpaceTask(bundle).run();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#removeFromBundleSpace(org.osgi.framework.
* Bundle)
*/
@Override
public synchronized void removeFromBundleSpace(Bundle bundle) {
new RemoveBundleFromBundleSpaceTask(bundle).run();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#getAll(org.eclipse.emf.ecore.EClass)
*/
@Override
public Iterable<EObject> getAll(EClass eClass) {
return Iterables.transform(getAllDescriptions(eClass), new Function<IEObjectDescription, EObject>() {
@Override
public EObject apply(IEObjectDescription desc) {
EObject result = desc.getEObjectOrProxy();
// TODO goingEclipse - maybe we should remove
// resolving here. See IUnitOfWork.GetAllUnitOfWork
return resolve(result);
}
});
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#resolve(org.eclipse.emf.ecore.EObject)
*/
@Override
public EObject resolve(EObject proxy) {
// TODO goingEclipse - maybe we should remove
// resolving here. See IUnitOfWork.ResolveUnitOfWork
return proxy.eIsProxy() ? EcoreUtil.resolve(proxy, resourceSet) : proxy;
}
/**
* Returns the metadata model element for the given parameters. Or
* <code>null</code> if no model could be found.
*
* @param qualifiedName
* the qualified name
* @param type
* the type
* @return the metadata
*/
public EObject getMetadata(String qualifiedName, EClass type) {
return getEObjectForFQN(converter.toQualifiedName(qualifiedName), type);
}
/**
* Gets the bundle space.
*
* @return the bundleSpace
*/
public IBundleSpace getBundleSpace() {
return bundleSpace;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#execute(java.lang.Object,
* org.eclipse.osbp.xtext.builder.metadata.services.IUnitOfWork)
*/
@Override
public <T> void execute(T state, IUnitOfWork<T> unitOfWork) {
try {
unitOfWork.exec(this, state);
} catch (Exception e) {
LOGGER.error("{}", e);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#getResourceSet()
*/
@Override
public XtextResourceSet getResourceSet() {
return resourceSet;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.osbp.xtext.builder.metadata.services.
* IMetadataBuilderService#findSubTypes(org.eclipse.xtext.common.types.
* JvmDeclaredType)
*/
@Override
public Set<JvmDeclaredType> findSubTypes(JvmDeclaredType type) {
Set<JvmDeclaredType> result = new HashSet<>();
TargetURIs jvmTypeURIs = injector.getInstance(TargetURIs.class);
jvmTypeURIs.addURI(EcoreUtil.getURI(type));
Acceptor acceptor = new Acceptor() {
@Override
public void accept(EObject source, URI sourceURI, EReference eReference, int index,
EObject targetOrProxy, URI targetURI) {
if (eReference == TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE) {
if (sourceURI.fragment().endsWith("/@superTypes.0")) {
addToResult(sourceURI);
}
}
}
@Override
public void accept(IReferenceDescription description) {
if (description.getEReference() == TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE) {
if (description.getSourceEObjectUri().fragment().endsWith("/@superTypes.0")) {
addToResult(description.getSourceEObjectUri());
}
}
}
private void addToResult(URI sourceURI) {
URI uri = URI.createURI(sourceURI.toString().replace("/@superTypes.0", ""));
result.add((JvmDeclaredType) resourceSet.getEObject(uri, true));
}
};
referenceFinder.findAllReferences(jvmTypeURIs, new LocalResourceAccess(resourceSet),
new DescriptionsWrapper(ResourceSetAdapter.findResourceDescriptionsData(resourceSet)), acceptor,
null);
return result;
}
@Override
/**
* Gets the bundle space type provider.
*
* @return the type provider
*/
public BundleSpaceTypeProvider getTypeProvider() {
return typeProvider;
}
@Override
public void unloadResource(String extension) {
// selected filtered resources
List<Resource> filtered = new ArrayList<>();
List<Resource> resources = new ArrayList<>(getResourceSet().getResources());
for (Resource resource : resources) {
if(resource.getURI().lastSegment().contains(extension)) {
filtered.add(resource);
}
}
execute(null, new IUnitOfWork<Void>() {
@Override
public void exec(IMetadataBuilderService service, Void state)
throws Exception {
for (Resource resource : filtered) {
resource.unload();
}
}
});
}
}
/**
* The Class DescriptionsWrapper.
*/
public static class DescriptionsWrapper implements IResourceDescriptions {
/** The data. */
private final ResourceDescriptionsData data;
/**
* Instantiates a new descriptions wrapper.
*
* @param data
* the data
*/
public DescriptionsWrapper(ResourceDescriptionsData data) {
this.data = data;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.xtext.resource.ISelectable#isEmpty()
*/
@Override
public boolean isEmpty() {
return data.isEmpty();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.xtext.resource.ISelectable#getExportedObjects()
*/
@Override
public Iterable<IEObjectDescription> getExportedObjects() {
return data.getExportedObjects();
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.xtext.resource.ISelectable#getExportedObjects(org.eclipse
* .emf.ecore.EClass, org.eclipse.xtext.naming.QualifiedName, boolean)
*/
@Override
public Iterable<IEObjectDescription> getExportedObjects(EClass type, QualifiedName name, boolean ignoreCase) {
return data.getExportedObjects(type, name, ignoreCase);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.xtext.resource.ISelectable#getExportedObjectsByType(org.
* eclipse.emf.ecore.EClass)
*/
@Override
public Iterable<IEObjectDescription> getExportedObjectsByType(EClass type) {
return data.getExportedObjectsByType(type);
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.xtext.resource.ISelectable#getExportedObjectsByObject(org
* .eclipse.emf.ecore.EObject)
*/
@Override
public Iterable<IEObjectDescription> getExportedObjectsByObject(EObject object) {
return data.getExportedObjectsByObject(object);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.xtext.resource.IResourceDescriptions#
* getAllResourceDescriptions()
*/
@Override
public Iterable<IResourceDescription> getAllResourceDescriptions() {
return data.getAllResourceDescriptions();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.xtext.resource.IResourceDescriptions#
* getResourceDescription(org.eclipse.emf.common.util.URI)
*/
@Override
public IResourceDescription getResourceDescription(URI uri) {
return data.getResourceDescription(uri);
}
}
/**
* The Class LocalResourceAccess.
*/
public static class LocalResourceAccess implements IReferenceFinder.IResourceAccess {
/** The rs. */
private final ResourceSet rs;
/**
* Instantiates a new local resource access.
*
* @param rs
* the rs
*/
public LocalResourceAccess(ResourceSet rs) {
this.rs = rs;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.xtext.findReferences.IReferenceFinder.IResourceAccess#
* readOnly(org.eclipse.emf.common.util.URI,
* org.eclipse.xtext.util.concurrent.IUnitOfWork)
*/
@Override
public <R> R readOnly(URI resourceURI, org.eclipse.xtext.util.concurrent.IUnitOfWork<R, ResourceSet> work) {
try {
return work.exec(rs);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
}