blob: d786754603e5666d614144bbdddbdc4ae18c739c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2012 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.common;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.acceleo.common.internal.utils.AcceleoDynamicMetamodelResourceSetImpl;
import org.eclipse.acceleo.common.internal.utils.AcceleoLibrariesEclipseUtil;
import org.eclipse.acceleo.common.internal.utils.AcceleoPackageRegistry;
import org.eclipse.acceleo.common.internal.utils.AcceleoServicesEclipseUtil;
import org.eclipse.acceleo.common.internal.utils.workspace.AcceleoModelManager;
import org.eclipse.acceleo.common.internal.utils.workspace.AcceleoWorkspaceUtil;
import org.eclipse.acceleo.common.library.connector.ILibrary;
import org.eclipse.acceleo.common.preference.AcceleoPreferences;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IRegistryEventListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.URI;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
public class AcceleoCommonPlugin extends Plugin {
/** Name of the extension point to parse for other libraries. */
public static final String LIBRARIES_EXTENSION_POINT = "org.eclipse.acceleo.common.libraries"; //$NON-NLS-1$
/** Name of the extension point to parse for other languages queries. */
public static final String LIBRARY_CONNECTORS_EXTENSION_POINT = "org.eclipse.acceleo.common.library.connectors"; //$NON-NLS-1$
/** The plug-in ID. */
public static final String PLUGIN_ID = "org.eclipse.acceleo.common"; //$NON-NLS-1$
/**
* Name of the extension point to parse for service classes.
*
* @deprecated this extension point has been deleted
*/
@Deprecated
public static final String SERVICES_EXTENSION_POINT = "org.eclipse.acceleo.common.java.services"; //$NON-NLS-1$
/** Exact name of the "class" tag of the extension point. */
private static final String CLASS_TAG_NAME = "class"; //$NON-NLS-1$
/** Exact name of the "fileExtension" tag of the extension point. */
private static final String FILE_EXTENSION_TAG_NAME = "fileExtension"; //$NON-NLS-1$
/** Exact name of the "file" tag of the extension point. */
private static final String FILE_TAG_NAME = "file"; //$NON-NLS-1$
/** This plug-in's shared instance. */
private static AcceleoCommonPlugin plugin;
/** Keeps a reference to this bundle's context. */
private BundleContext context;
/** The registry listener that will be used to listen to Acceleo library connector changes. */
private final AcceleoLibraryConnectorsRegistryListener librariesConnectorListener = new AcceleoLibraryConnectorsRegistryListener();
/** The registry listener that will be used to listen to Acceleo libraries changes. */
private final AcceleoLibrariesRegistryListener librariesListener = new AcceleoLibrariesRegistryListener();
/** Listener tracking changes with the workspace ecore files. */
private final WorkspaceEcoreListener workspaceEcoreListener = new WorkspaceEcoreListener();
/**
* Default constructor for the plugin.
*/
public AcceleoCommonPlugin() {
plugin = this;
}
/**
* Returns the shared instance.
*
* @return the shared instance
*/
public static AcceleoCommonPlugin getDefault() {
return plugin;
}
/**
* Trace an Exception in the error log.
*
* @param e
* Exception to log.
* @param blocker
* <code>True</code> if the exception must be logged as error, <code>False</code> to log it as
* a warning.
*/
public static void log(Exception e, boolean blocker) {
if (e == null) {
throw new NullPointerException(AcceleoCommonMessages
.getString("AcceleoCommonPlugin.LogNullException")); //$NON-NLS-1$
}
if (getDefault() == null) {
// We are out of eclipse. Prints the stack trace on standard error.
// CHECKSTYLE:OFF
e.printStackTrace();
// CHECKSTYLE:ON
} else if (e instanceof CoreException) {
log(((CoreException)e).getStatus());
} else if (e instanceof NullPointerException) {
int severity = IStatus.WARNING;
if (blocker) {
severity = IStatus.ERROR;
}
log(new Status(severity, PLUGIN_ID, severity, AcceleoCommonMessages
.getString("AcceleoCommonPlugin.ElementNotFound"), e)); //$NON-NLS-1$
} else {
int severity = IStatus.WARNING;
if (blocker) {
severity = IStatus.ERROR;
}
log(new Status(severity, PLUGIN_ID, severity, e.getMessage(), e));
}
}
/**
* Puts the given status in the error log view.
*
* @param status
* Error Status.
*/
public static void log(IStatus status) {
// Eclipse platform displays NullPointer on standard error instead of throwing it.
// We'll handle this by throwing it ourselves.
if (status == null) {
throw new NullPointerException(AcceleoCommonMessages
.getString("AcceleoCommonPlugin.LogNullStatus")); //$NON-NLS-1$
}
if (getDefault() != null) {
getDefault().getLog().log(status);
} else {
// We are out of eclipse. Prints the message on standard error.
// CHECKSTYLE:OFF
System.err.println(status.getMessage());
status.getException().printStackTrace();
// CHECKSTYLE:ON
}
}
/**
* Puts the given message in the error log view, as error or warning.
*
* @param message
* The message to put in the error log view.
* @param blocker
* <code>True</code> if the message must be logged as error, <code>False</code> to log it as a
* warning.
*/
public static void log(String message, boolean blocker) {
if (getDefault() == null) {
// We are out of eclipse. Prints the message on standard error.
// CHECKSTYLE:OFF
System.err.println(message);
// CHECKSTYLE:ON
} else {
int severity = IStatus.WARNING;
if (blocker) {
severity = IStatus.ERROR;
}
String errorMessage = message;
if (errorMessage == null || "".equals(errorMessage)) { //$NON-NLS-1$
errorMessage = AcceleoCommonMessages.getString("AcceleoCommonPlugin.UnexpectedException"); //$NON-NLS-1$
}
log(new Status(severity, PLUGIN_ID, errorMessage));
}
}
/**
* Traces an exception in the error log with the given log message.
* <p>
* This is a convenience method fully equivalent to using
* <code>log(new Status(int, PLUGIN_ID, message, cause)</code>.
* </p>
*
* @param message
* The message that is to be displayed in the error log view.
* @param cause
* Exception that is to be logged.
* @param blocker
* <code>True</code> if the exception must be logged as error, <code>False</code> to log it as
* a warning.
* @since 0.8
*/
public static void log(String message, Exception cause, boolean blocker) {
final int severity;
if (blocker) {
severity = IStatus.ERROR;
} else {
severity = IStatus.WARNING;
}
log(new Status(severity, PLUGIN_ID, message, cause));
}
/**
* Returns this bundle's context.
*
* @return This bundle's context.
*/
public BundleContext getContext() {
return context;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.Plugin#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(final BundleContext bundleContext) throws Exception {
super.start(bundleContext);
AcceleoWorkspaceUtil.INSTANCE.initialize();
Bundle pdeCoreBundle = Platform.getBundle("org.eclipse.pde.core"); //$NON-NLS-1$
if (pdeCoreBundle != null) {
ResourcesPlugin.getWorkspace().addResourceChangeListener(
workspaceEcoreListener,
IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE
| IResourceChangeEvent.POST_CHANGE);
}
ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceVisitor() {
public boolean visit(IResource resource) throws CoreException {
if (resource instanceof IFile) {
if (resource instanceof IFile
&& ((IFile)resource).getFileExtension() != null
&& ((IFile)resource).getFileExtension().equals(
IAcceleoConstants.ECORE_FILE_EXTENSION)) {
URI uri = URI.createPlatformResourceURI(resource.getFullPath().toString(), true);
AcceleoPackageRegistry.INSTANCE.registerEcorePackages(uri.toString(),
AcceleoDynamicMetamodelResourceSetImpl.DYNAMIC_METAMODEL_RESOURCE_SET);
}
}
return true;
}
});
context = bundleContext;
final IExtensionRegistry registry = Platform.getExtensionRegistry();
registry.addListener(librariesConnectorListener, LIBRARY_CONNECTORS_EXTENSION_POINT);
registry.addListener(librariesListener, LIBRARIES_EXTENSION_POINT);
parseInitialContributions();
AcceleoModelManager.getManager().startup();
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(final BundleContext bundleContext) throws Exception {
try {
final IExtensionRegistry registry = Platform.getExtensionRegistry();
registry.removeListener(librariesConnectorListener);
registry.removeListener(librariesListener);
Bundle pdeCoreBundle = Platform.getBundle("org.eclipse.pde.core"); //$NON-NLS-1$
if (pdeCoreBundle != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(workspaceEcoreListener);
}
AcceleoServicesEclipseUtil.clearRegistry();
AcceleoLibraryConnectorsRegistry.INSTANCE.clearRegistry();
AcceleoLibrariesEclipseUtil.clearRegistry();
AcceleoWorkspaceUtil.INSTANCE.dispose();
AcceleoPreferences.save();
plugin = null;
context = null;
AcceleoModelManager.getManager().shutdown();
} finally {
super.stop(bundleContext);
}
}
/**
* Though we have listeners on the provided extension points, there could have been contributions before
* this plugin got started and listeners installed. This will parse them.
*/
@SuppressWarnings("unchecked")
private void parseInitialContributions() {
final IExtensionRegistry registry = Platform.getExtensionRegistry();
for (IExtension extension : registry.getExtensionPoint(LIBRARY_CONNECTORS_EXTENSION_POINT)
.getExtensions()) {
for (IConfigurationElement service : extension.getConfigurationElements()) {
try {
AcceleoLibraryConnectorsRegistry.INSTANCE.addLibraryConnector((Class<ILibrary>)service
.createExecutableExtension(CLASS_TAG_NAME).getClass(), service
.getAttribute(FILE_EXTENSION_TAG_NAME));
} catch (CoreException e) {
log(e, false);
}
}
}
for (IExtension extension : registry.getExtensionPoint(LIBRARIES_EXTENSION_POINT).getExtensions()) {
for (IConfigurationElement library : extension.getConfigurationElements()) {
String pathToFile = library.getAttribute(FILE_TAG_NAME);
Class<ILibrary> libClass = AcceleoLibraryConnectorsRegistry.INSTANCE
.getConnectorForResource(pathToFile);
if (libClass != null) {
try {
ILibrary lib = libClass.newInstance();
lib.setURI(URI.createFileURI(ResourcesPlugin.getWorkspace().getRoot().getProject(
extension.getContributor().getName()).getFile(pathToFile).getLocation()
.toString()));
AcceleoLibrariesEclipseUtil.addLibrary(lib);
} catch (InstantiationException e) {
log(e, true);
} catch (IllegalAccessException e) {
log(e, true);
}
} else {
log(AcceleoCommonMessages.getString("AcceleoCommonPlugin.MissingHandle", pathToFile), //$NON-NLS-1$
false);
}
}
}
}
/**
* This will allow us to be aware of changes of extension against the Acceleo library connector extension
* point.
*/
final class AcceleoLibrariesRegistryListener implements IRegistryEventListener {
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtension[])
*/
public void added(IExtension[] extensions) {
for (IExtension extension : extensions) {
for (IConfigurationElement service : extension.getConfigurationElements()) {
String pathToFile = service.getAttribute(FILE_TAG_NAME);
Class<ILibrary> libClass = AcceleoLibraryConnectorsRegistry.INSTANCE
.getConnectorForResource(pathToFile);
if (libClass != null) {
try {
ILibrary lib = libClass.newInstance();
lib.setURI(URI.createFileURI(ResourcesPlugin.getWorkspace().getRoot().getProject(
extension.getContributor().getName()).getFile(pathToFile).getLocation()
.toString()));
AcceleoLibrariesEclipseUtil.addLibrary(lib);
} catch (InstantiationException e) {
log(e, true);
} catch (IllegalAccessException e) {
log(e, true);
}
} else {
log(AcceleoCommonMessages.getString("AcceleoCommonPlugin.MissingHandle", pathToFile), //$NON-NLS-1$
false);
}
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtensionPoint[])
*/
public void added(IExtensionPoint[] extensionPoints) {
// no need to listen to this
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtension[])
*/
public void removed(IExtension[] extensions) {
for (IExtension extension : extensions) {
for (IConfigurationElement service : extension.getConfigurationElements()) {
AcceleoLibrariesEclipseUtil.removeLibrary(service.getAttribute(FILE_TAG_NAME));
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtensionPoint[])
*/
public void removed(IExtensionPoint[] extensionPoints) {
// no need to listen to this event
}
}
/**
* This will allow us to be aware of changes of extension against the Acceleo library connector extension
* point.
*/
final class AcceleoLibraryConnectorsRegistryListener implements IRegistryEventListener {
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtension[])
*/
@SuppressWarnings("unchecked")
public void added(IExtension[] extensions) {
for (IExtension extension : extensions) {
for (IConfigurationElement service : extension.getConfigurationElements()) {
try {
AcceleoLibraryConnectorsRegistry.INSTANCE
.addLibraryConnector((Class<ILibrary>)service.createExecutableExtension(
CLASS_TAG_NAME).getClass(), service
.getAttribute(FILE_EXTENSION_TAG_NAME));
} catch (CoreException e) {
log(e, false);
}
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtensionPoint[])
*/
public void added(IExtensionPoint[] extensionPoints) {
// no need to listen to this
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtension[])
*/
public void removed(IExtension[] extensions) {
for (IExtension extension : extensions) {
for (IConfigurationElement service : extension.getConfigurationElements()) {
AcceleoLibraryConnectorsRegistry.INSTANCE.removeLibraryConnector(service
.getAttribute(CLASS_TAG_NAME));
}
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtensionPoint[])
*/
public void removed(IExtensionPoint[] extensionPoints) {
// no need to listen to this event
}
}
/**
* Allows us to react to changes in the dynamic ecore models.
*
* @author <a href="mailto:laurent.goubet@obeo.fr">Laurent Goubet</a>
*/
private class WorkspaceEcoreListener implements IResourceChangeListener {
/**
* Default constructor.
*/
WorkspaceEcoreListener() {
// Increases visibility
}
/**
* {@inheritDoc}
*
* @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
*/
public void resourceChanged(IResourceChangeEvent event) {
switch (event.getType()) {
/*
* Project closing and deletion must trigger the removal of its models from the dynamic registry.
* Model deletion must trigger its removal from the dynamic registry. Model change must trigger
* its removal and re-adding in the registry. Model creation must trigger its addition in the
* registry.
*/
case IResourceChangeEvent.PRE_CLOSE:
case IResourceChangeEvent.PRE_DELETE:
if (event.getResource() instanceof IProject) {
try {
List<IFile> ecoreFiles = members((IContainer)event.getResource(),
IAcceleoConstants.ECORE_FILE_EXTENSION);
if (!ecoreFiles.isEmpty()) {
for (IFile ecoreFile : ecoreFiles) {
AcceleoPackageRegistry.INSTANCE.unregisterEcorePackages(ecoreFile
.getFullPath().toString());
}
}
} catch (CoreException e) {
AcceleoCommonPlugin.log(e, false);
}
}
break;
case IResourceChangeEvent.POST_CHANGE:
IResource resource = null;
if (event.getResource() != null) {
resource = event.getResource();
} else if (getResources(event.getDelta()).size() > 0) {
List<IResource> resources = getResources(event.getDelta());
resource = resources.get(0);
}
if (resource != null && resource.isAccessible() && resource.getFileExtension() != null
&& resource.getFileExtension().endsWith(IAcceleoConstants.ECORE_FILE_EXTENSION)) {
URI uri = URI.createPlatformResourceURI(resource.getFullPath().toString(), true);
AcceleoPackageRegistry.INSTANCE.registerEcorePackages(uri.toString(),
AcceleoDynamicMetamodelResourceSetImpl.DYNAMIC_METAMODEL_RESOURCE_SET);
}
break;
default:
// no default action
}
}
/**
* Computes the resources available in a resource delta.
*
* @param delta
* the resource delta.
* @return The lsit of resources in a resource delta.
*/
private List<IResource> getResources(IResourceDelta delta) {
List<IResource> resources = new ArrayList<IResource>();
IResourceDelta[] affectedChildren = delta.getAffectedChildren();
for (IResourceDelta iResourceDelta : affectedChildren) {
IResource resource = iResourceDelta.getResource();
if (resource instanceof IFile
&& ((IFile)resource).getFileExtension() != null
&& ((IFile)resource).getFileExtension()
.equals(IAcceleoConstants.ECORE_FILE_EXTENSION)) {
resources.add(resource);
}
resources.addAll(getResources(iResourceDelta));
}
return resources;
}
/**
* Returns a list of existing member files (that validate the file extension) in this resource.
*
* @param container
* The container to browse for files with the given extension.
* @param extension
* The file extension to browse for.
* @return The List of files of the given extension contained by <code>container</code>.
* @throws CoreException
* Thrown if we couldn't retrieve the children of <code>container</code>.
*/
private List<IFile> members(IContainer container, String extension) throws CoreException {
List<IFile> output = new ArrayList<IFile>();
if (container != null && container.isAccessible()) {
IResource[] children = container.members();
if (children != null) {
for (int i = 0; i < children.length; ++i) {
IResource resource = children[i];
if (resource instanceof IFile
&& extension.equals(((IFile)resource).getFileExtension())) {
output.add((IFile)resource);
} else if (resource instanceof IContainer) {
output.addAll(members((IContainer)resource, extension));
}
}
}
}
return output;
}
}
}