blob: 345874dba646a8cd81fafc6fd99b66042171fe58 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2005 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jem.internal.beaninfo.adapters;
/*
* $RCSfile: BeaninfoNature.java,v $
* $Revision: 1.37 $ $Date: 2005/09/14 23:30:38 $
*/
import java.io.*;
import java.text.MessageFormat;
import java.util.*;
import java.util.logging.Level;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.jdt.core.*;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.eclipse.jem.internal.beaninfo.core.*;
import org.eclipse.jem.internal.java.beaninfo.IIntrospectionAdapter;
import org.eclipse.jem.internal.java.init.JavaInit;
import org.eclipse.jem.internal.plugin.JavaEMFNature;
import org.eclipse.jem.internal.proxy.core.*;
import org.eclipse.jem.java.adapters.JavaXMIFactory;
import org.eclipse.jem.util.emf.workbench.ProjectResourceSet;
import org.eclipse.jem.util.emf.workbench.ResourceHandler;
/**
* The beaninfo nature. It is created for a project and holds the
* necessary info for beaninfo to be performed on a project.
*/
public class BeaninfoNature implements IProjectNature {
public static final String NATURE_ID = BeaninfoPlugin.PI_BEANINFO_PLUGINID + ".BeanInfoNature"; //$NON-NLS-1$
public static final String P_BEANINFO_SEARCH_PATH = ".beaninfoConfig"; //$NON-NLS-1$
public static final QualifiedName CONFIG_INFO_SESSION_KEY = new QualifiedName(BeaninfoPlugin.PI_BEANINFO_PLUGINID, "CONFIG_INFO"); //$NON-NLS-1$
public static final QualifiedName BEANINFO_CONTRIBUTORS_SESSION_KEY = new QualifiedName(BeaninfoPlugin.PI_BEANINFO_PLUGINID, "BEANINFO_CONTRIBUTORS"); //$NON-NLS-1$
private ProxyFactoryRegistry.IRegistryListener registryListener = new ProxyFactoryRegistry.IRegistryListener() {
/**
* @see org.eclipse.jem.internal.proxy.core.ProxyFactoryRegistry.IRegistryListener#registryTerminated(ProxyFactoryRegistry)
*/
public void registryTerminated(ProxyFactoryRegistry registry) {
markAllStale();
};
};
/**
* Get the runtime nature for the project, create it if necessary.
*/
public static BeaninfoNature getRuntime(IProject project) throws CoreException {
JavaEMFNature.createRuntime(project); // Must force JAVAEMFNature creation first before we try to get ours. There is a chicken/egg problem if we let our nature try to get JavaEMFNature during setProject.
if (project.hasNature(NATURE_ID))
return (BeaninfoNature) project.getNature(NATURE_ID);
else
return createRuntime(project);
}
/**
* Return whether this project has a BeanInfo runtime turned on.
*
* @param project
* @return <code>true</code> if it has the a BeanInfo runtime.
* @throws CoreException
*
* @since 1.0.0
*/
public static boolean hasRuntime(IProject project) throws CoreException {
return project.hasNature(NATURE_ID);
}
/**
* Test if this is a valid project for a Beaninfo Nature. It must be
* a JavaProject.
*/
public static boolean isValidProject(IProject project) {
try {
return project.hasNature(JavaCore.NATURE_ID);
} catch (CoreException e) {
return false;
}
}
/**
* Create the runtime.
*/
private static BeaninfoNature createRuntime(IProject project) throws CoreException {
if (!isValidProject(project))
throw new CoreException(
new Status(
IStatus.ERROR,
BeaninfoPlugin.PI_BEANINFO_PLUGINID,
0,
MessageFormat.format(
BeanInfoAdapterMessages.INTROSPECT_FAILED_EXC_,
new Object[] { project.getName(), BeanInfoAdapterMessages.BeaninfoNature_InvalidProject}),
null));
addNatureToProject(project, NATURE_ID);
return (BeaninfoNature) project.getNature(NATURE_ID);
}
private static void addNatureToProject(IProject proj, String natureId) throws CoreException {
IProjectDescription description = proj.getDescription();
String[] prevNatures = description.getNatureIds();
String[] newNatures = new String[prevNatures.length + 1];
System.arraycopy(prevNatures, 0, newNatures, 0, prevNatures.length);
newNatures[prevNatures.length] = natureId;
description.setNatureIds(newNatures);
proj.setDescription(description, null);
}
private IProject fProject;
protected ProxyFactoryRegistry fRegistry;
protected ResourceSet javaRSet;
protected BeaninfoModelSynchronizer fSynchronizer;
protected static BeaninfoJavaReflectionKeyExtension fReflectionKeyExtension;
/**
* Configures the project with this nature.
* This is called by <code>IProject.getNature</code> and should not
* be called directly by clients.
* The nature extension id is added to the list of natures on the project by
* <code>IProject.getNature</code>, and need not be added here.
*
* @exception CoreException if this method fails.
*/
public void configure() throws CoreException {
}
/**
* Removes this nature from the project, performing any required deconfiguration.
* This is called by <code>IProject.removeNature</code> and should not
* be called directly by clients.
* The nature id is removed from the list of natures on the project by
* <code>IProject.removeNature</code>, and need not be removed here.
*
* @exception CoreException if this method fails.
*/
public void deconfigure() throws CoreException {
removeSharedProperty(P_BEANINFO_SEARCH_PATH, null);
cleanup(true, true);
}
/**
* Shutdown the nature. Called by BeanInfoPlugin to tell the nature that the plugin is being shutdown.
* It needs to cleanup.
* TODO <package-protected> because only BeanInfoPlugin should call it. (public for now but when we make
* BeanInfoNature an API it will be moved into the same package as BeanInfoPlugin).
*
* @since 1.0.0
*/
public void shutdown() {
cleanup(true, false);
}
/**
* Return a new ResourceSet that is linked correctly to this Beaninfo Nature.
* <p>
* This links up a ResourceSet so that it will work correctly with this nature.
* It makes sure that going through the ResourceSet that any "java:/..."
* classes can be found and it makes sure that any new classes are placed into the
* nature's resource set and not resource set doing the calling.
* <p>
* This should be used any time a resource set is needed that is not the
* project wide resource set associated with beaninfos, but will reference
* Java Model classes or instantiate.
* <p>
* An additional change is made too. The ResourceFactoryRegistry's extensionToResourceFactory map is modified
* to have an "java"->XMIResourceFactory entry added to it if EMF Examples is loaded. EMF Examples add
* the "java" extension and sets it to their own special JavaResourceFactory.
* If EMF Examples is not loaded, then it falls back to the default "*" mapping, which is to XMIResourceFactory.
* This normally causes problems for many
* customers. If users of this resource set really want the EMF examples entry instead, after they retrieve the
* new resource set they can do this:
* <p>
* <pre><code>
* rset = beaninfoNature.newResourceSet();
* rset.getResourceFactoryRegistry().getExtensionToFactoryMap().remove("java");
* </code></pre>
*
* @return a ResourceSet that is specially connected to the JEM java model.
*
* @since 1.0.0
*/
public ProjectResourceSet newResourceSet() {
SpecialResourceSet rset = new SpecialResourceSet();
rset.add(new ResourceHandler() {
public EObject getEObjectFailed(ResourceSet originatingResourceSet, URI uri, boolean loadOnDemand) {
return null; // We don't have any special things we can do in this case.
}
public Resource getResource(ResourceSet originatingResourceSet, URI uri) {
// Always try to get it out of the nature's resource set because it may of been loaded there either as
// the "java:..." type or it could of been an override extra file (such as an override EMF package, for
// example jcf has a side package containing the definition of the new attribute type. That file
// will also be loaded into this resourceset. So to find it we need to go in here and try.
//
// However, if not found we won't go and try to load the resource. That could load in the wrong place.
// Kludge: Because of a bug (feature :-)) in XMLHandler.getPackageFromURI(), it doesn't use getResource(...,true) and it tries instead
// to use uri inputstream to load the package when not found. This bypasses our special create resource and so
// packages are not automatically created. So we need to do load on demand here instead if it is a java protocol.
// EMF will not be fixing this. It is working as designed.
return getResourceSet().getResource(uri, JavaXMIFactory.SCHEME.equals(uri.scheme()));
}
public Resource createResource(ResourceSet originatingResourceSet, URI uri) {
// This is the one. It has got here because it couldn't find a resource already loaded.
// If it is a "java:/..." protocol resource, then we want to make sure it is loaded at the BeaninfoNature context
// instead of the lower one.
if (JavaXMIFactory.SCHEME.equals(uri.scheme()))
return getResourceSet().getResource(uri, true);
else
return null;
}
});
// [71473] Restore "*.java" to be an XMIResource. If EMF Examples are loaded they overload this and load their special resource for "*.java" which we don't want.
// If some user really wants that, they grab the resource resource set and remove our override.
if (Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().containsKey("java")) { //$NON-NLS-1$
// Need to add an override to go to XMI instead.
rset.getResourceFactoryRegistry().getExtensionToFactoryMap().put("java", new XMIResourceFactoryImpl()); //$NON-NLS-1$
}
return rset;
}
/**
* Clean up, this means either the project is being closed, deleted, or it means that
* the nature is being removed from the project. Either way that means to
* terminate the VM and remove what we added to the context if the flag says clear it.
* <p>
* This should be called ONLY when this instance of the nature is no longer needed. It
* will be recreated for any new uses. That is because we will be removing ourselves
* from the list of active natures in the BeanInfoPlugin.
* <p>
* <b>Note:</b> This will be called from the BeanInfoCacheController. It knows when the project is
* being closed or deleted.
*
* @param clearResults clear the results such that any JEM model objects have no BeanInfo
* adapters attached to them. This allows BeanInfo to be GC'd without being hung onto.
*
* @param deregister Deregister from the BeanInfoPlugin. Normally this will always be true, but it
* will be called with false when BeanInfoPlugin is calling back to shutdown.
*/
public void cleanup(boolean clearResults, boolean deregister) {
if (deregister)
BeaninfoPlugin.getPlugin().removeBeanInfoNature(this);
fSynchronizer.stopSynchronizer(clearResults);
Init.cleanup(javaRSet, clearResults);
if (fRegistry != null)
fRegistry.terminateRegistry(true);
javaRSet = null;
fRegistry = null;
fProject = null;
fSynchronizer = null;
}
/**
* Returns the project to which this project nature applies.
*
* @return the project handle
*/
public IProject getProject() {
return fProject;
}
/**
* Sets the project to which this nature applies.
* Used when instantiating this project nature runtime.
* This is called by <code>IProject.addNature</code>
* and should not be called directly by clients.
*
* @param project the project to which this nature applies
*/
public void setProject(IProject project) {
// BeanInfoCacheController.INSTANCE.getClass(); // Instantiates the controller if not already started.
fProject = project;
BeaninfoPlugin.getPlugin().addBeanInfoNature(this);
try {
// The nature has been started for this project, need to setup the introspection process now.
JavaEMFNature javaNature = JavaEMFNature.createRuntime(fProject);
JavaInit.init();
if (fReflectionKeyExtension == null) {
// Register the reflection key extension.
fReflectionKeyExtension = new BeaninfoJavaReflectionKeyExtension();
JavaXMIFactory.INSTANCE.registerReflectionKeyExtension(fReflectionKeyExtension);
}
javaRSet = javaNature.getResourceSet();
Init.initialize(javaRSet, new IBeaninfoSupplier() {
public ProxyFactoryRegistry getRegistry() {
return BeaninfoNature.this.getRegistry();
}
public boolean isRegistryCreated() {
return BeaninfoNature.this.isRegistryCreated();
}
public void closeRegistry() {
BeaninfoNature.this.closeRegistry();
}
public IProject getProject() {
return BeaninfoNature.this.getProject();
}
public ProjectResourceSet getNewResourceSet() {
return BeaninfoNature.this.newResourceSet();
}
public ResourceSet getProjectResourceSet() {
return getResourceSet();
}
});
fSynchronizer =
new BeaninfoModelSynchronizer(
(BeaninfoAdapterFactory) EcoreUtil.getAdapterFactory(javaRSet.getAdapterFactories(), IIntrospectionAdapter.ADAPTER_KEY),
JavaCore.create(javaNature.getProject()));
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e.getStatus());
}
}
/**
* Close the registry. It needs to be recycled because a class has changed
* and now the new class needs to be accessed.
*/
protected void closeRegistry() {
ProxyFactoryRegistry reg = null;
synchronized (this) {
reg = fRegistry;
fRegistry = null;
try {
// Wipe out the Session properties so that they are recomputed.
getProject().setSessionProperty(CONFIG_INFO_SESSION_KEY, null);
getProject().setSessionProperty(BEANINFO_CONTRIBUTORS_SESSION_KEY, null);
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.INFO);
}
}
if (reg != null) {
reg.removeRegistryListener(registryListener);
reg.terminateRegistry();
}
}
private static final String PI_CLASS = "class"; //$NON-NLS-1$
/**
* Using the given configuration info, compute the BeanInfo config info needed. This sets the
* session properties BEANINFO_CONTRIBUTORS_SESSION_KEY and CONFIG_INFO_SESSION_KEY.
*
* @param info
* @throws CoreException
*
* @since 1.1.0
*/
public static void computeBeanInfoConfigInfo(IConfigurationContributionInfo info) throws CoreException {
// First time for this nature, or first time after registry reset. Need to compute the info.
// It is possible for this to be called BEFORE the first usage of BeanInfo. The editor usually
// brings up the editor's registry before it gets anything from BeanInfo.
List contributorsList = new ArrayList(10);
if (!info.getContainerIds().isEmpty()) {
// Run through all of the visible container ids that are applicable and get BeanInfo contributors.
Iterator containerIdItr = info.getContainerIds().entrySet().iterator();
while (containerIdItr.hasNext()) {
Map.Entry entry = (Map.Entry) containerIdItr.next();
if (((Boolean) entry.getValue()).booleanValue()) {
IConfigurationElement[] contributors = BeaninfoPlugin.getPlugin().getContainerIdContributors(
(String) entry.getKey());
if (contributors != null) {
for (int i = 0; i < contributors.length; i++) {
try {
Object contributor = contributors[i].createExecutableExtension(PI_CLASS);
if (contributor instanceof IBeanInfoContributor)
contributorsList.add(contributor);
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.WARNING);
}
}
}
}
}
}
if (!info.getPluginIds().isEmpty()) {
// Run through all of the visible plugin ids that are applicable and get BeanInfo contributors.
Iterator pluginIdItr = info.getPluginIds().entrySet().iterator();
while (pluginIdItr.hasNext()) {
Map.Entry entry = (Map.Entry) pluginIdItr.next();
if (((Boolean) entry.getValue()).booleanValue()) {
IConfigurationElement[] contributors = BeaninfoPlugin.getPlugin().getPluginContributors(
(String) entry.getKey());
if (contributors != null) {
for (int i = 0; i < contributors.length; i++) {
try {
Object contributor = contributors[i].createExecutableExtension(PI_CLASS);
if (contributor instanceof IBeanInfoContributor)
contributorsList.add(contributor);
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.WARNING);
}
}
}
}
}
}
// Save it for all beaninfo processing (and configuration processing if they implement proxy configuration contributor).
IBeanInfoContributor[] explicitContributors = (IBeanInfoContributor[]) contributorsList.toArray(new IBeanInfoContributor[contributorsList.size()]);
info.getJavaProject().getProject().setSessionProperty(BEANINFO_CONTRIBUTORS_SESSION_KEY, explicitContributors);
// Save it for override processing. That happens over and over later after all config processing is done.
// Do it last so that if there is a race condition, since this property is a flag to indicate we have data,
// we need to make sure the Beaninfo data is already set at the point we set this.
// We could actually set it twice because of this, but it is the same data, so, so what.
info.getJavaProject().getProject().setSessionProperty(CONFIG_INFO_SESSION_KEY, info);
}
/**
* Get registry, creating it if necessary.
* @return the registry.
*
* @since 1.0.0
*/
public ProxyFactoryRegistry getRegistry() {
synchronized (this) {
if (fRegistry != null)
return fRegistry;
}
// Now need to start the appropriate job. In another class so that it can handle dynamically checking if
// UI is available to even do this (it maybe not in a UI mode, so then just do it.
CreateRegistryJobHandler.createRegistry(this);
return fRegistry;
}
/*
* This is <package-protected> so that only the appropriate create job in this
* package can call it. This is because this must be controlled to only be
* done when build not in progress and serial access.
*/
void createRegistry(IProgressMonitor pm) {
pm.beginTask(BeanInfoAdapterMessages.UICreateRegistryJobHandler_StartBeaninfoRegistry, 100);
if (isRegistryCreated()) {
pm.done();
return; // It had already been created. Could of been because threads were racing to do the creation, and one got there first.
}
try {
ConfigurationContributor configurationContributor = (ConfigurationContributor) getConfigurationContributor();
configurationContributor.setNature(this);
ProxyFactoryRegistry registry = ProxyLaunchSupport.startImplementation(fProject, "Beaninfo", //$NON-NLS-1$
new IConfigurationContributor[] { configurationContributor}, false, new SubProgressMonitor(pm, 100));
registry.addRegistryListener(registryListener);
synchronized(this) {
fRegistry = registry;
}
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e.getStatus());
} finally {
pm.done();
}
}
public synchronized boolean isRegistryCreated() {
return fRegistry != null;
}
/**
* Check to see if the nature is still valid. If the project has been
* renamed, the nature is still around, but the project has been closed.
* So the nature is now invalid.
*
* @return Is this a valid nature. I.e. is the project still open.
*/
public boolean isValidNature() {
return fProject != null;
}
/**
* Set the search path onto the registry.
*/
protected void setProxySearchPath(ProxyFactoryRegistry registry, List searchPaths) {
if (searchPaths != null) {
String[] stringSearchPath = (String[]) searchPaths.toArray(new String[searchPaths.size()]);
Utilities.setBeanInfoSearchPath(registry, stringSearchPath);
} else
Utilities.setBeanInfoSearchPath(registry, null);
}
private static final String ENCODING = "UTF-8"; //$NON-NLS-1$
static final String sBeaninfos = "beaninfos"; // Root element name //$NON-NLS-1$
/**
* Get the persistent search path. It is copy.
*/
public BeaninfosDoc getSearchPath() {
BeaninfosDoc bdoc = null;
try {
InputStream property = getSharedProperty(P_BEANINFO_SEARCH_PATH);
if (property != null) {
try {
// Need to reconstruct from the XML format.
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new InputStreamReader(property, ENCODING)));
Element root = doc.getDocumentElement();
if (root != null && root.getNodeName().equalsIgnoreCase(sBeaninfos)) {
bdoc = BeaninfosDoc.readEntry(new DOMReader(), root, getProject());
}
} finally {
try {
property.close();
} catch (IOException e) {
}
}
}
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e.getStatus());
} catch (Exception e) {
BeaninfoPlugin.getPlugin().getLogger().log(new Status(IStatus.WARNING, BeaninfoPlugin.PI_BEANINFO_PLUGINID, 0, "", e)); //$NON-NLS-1$
}
return bdoc;
}
/**
* Set the persistent search path. No progress monitor.
*/
public void setSearchPath(BeaninfosDoc searchPath) throws CoreException {
setSearchPath(searchPath, null);
}
/**
* Set the persistent search path with a progress monitor
*/
public void setSearchPath(BeaninfosDoc searchPath, IProgressMonitor monitor) throws CoreException {
String property = null;
if (searchPath != null && searchPath.getSearchpath().length > 0) {
try {
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = doc.createElement(sBeaninfos); // Create Root Element
IBeaninfosDocEntry[] entries = searchPath.getSearchpath();
for (int i = 0; i < entries.length; i++)
root.appendChild(entries[i].writeEntry(doc, getProject())); // Add to the search path
doc.appendChild(root); // Add Root to Document
StringWriter strWriter = new StringWriter();
Result result = new StreamResult(strWriter);
Source source = new DOMSource(doc);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$
transformer.setOutputProperty(OutputKeys.METHOD, "xml"); //$NON-NLS-1$
transformer.transform(source, result);
property = strWriter.toString();
} catch (TransformerConfigurationException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.WARNING);
} catch (TransformerException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.WARNING);
} catch (ParserConfigurationException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.WARNING);
} catch (FactoryConfigurationError e) {
BeaninfoPlugin.getPlugin().getLogger().log(e, Level.WARNING);
}
}
if (property != null) {
// If it hasn't changed, don't write it back out. This is so that if the file hasn't
// been checked out and it is the same, we don't want to bother the user. This is because
// we don't know if the user had simply browsed the search path or had actually changed and
// set it back to what it was. In either of those cases it would be a bother to ask the
// user to checkout the file.
InputStream is = getSharedProperty(P_BEANINFO_SEARCH_PATH);
if (is != null) {
try {
try {
InputStreamReader reader = new InputStreamReader(is, ENCODING);
char[] chars = new char[1000];
StringBuffer oldProperty = new StringBuffer(1000);
int read = reader.read(chars);
while (read != -1) {
oldProperty.append(chars, 0, read);
read = reader.read(chars);
}
if (oldProperty.toString().equals(property))
return;
} catch (IOException e) {
} // Didn't change.
} finally {
try {
is.close();
} catch (IOException e) {
}
}
}
setSharedProperty(P_BEANINFO_SEARCH_PATH, property, monitor);
} else
removeSharedProperty(P_BEANINFO_SEARCH_PATH, monitor);
}
/**
* Return the resource set for all java packages in this nature.
*/
public ResourceSet getResourceSet() {
return javaRSet;
}
protected void markAllStale() {
// Mark all stale so that the registry will be recycled.
if (fRegistry != null) {
// We have a registry running, we need to indicate recycle is needed.
fSynchronizer.getAdapterFactory().markAllStale();
// Mark all stale. Next time we need anything it will be recycled.
}
}
/**
* Compute the file name to use for a given shared property
*/
protected String computeSharedPropertyFileName(QualifiedName qName) {
return qName.getLocalName();
}
/**
* Retrieve a shared property on a project. If the property is not defined, answers null.
* Note that it is orthogonal to IResource persistent properties, and client code has to decide
* which form of storage to use appropriately. Shared properties produce real resource files which
* can be shared through a VCM onto a server. Persistent properties are not shareable.
*
*/
protected InputStream getSharedProperty(String propertyFileName) throws CoreException {
IFile rscFile = getProject().getFile(propertyFileName);
if (rscFile.exists())
return rscFile.getContents(true);
else
return null;
}
/**
* Record a shared persistent property onto a project.
* Note that it is orthogonal to IResource persistent properties, and client code has to decide
* which form of storage to use appropriately. Shared properties produce real resource files which
* can be shared through a VCM onto a server. Persistent properties are not shareable.
*
* shared properties end up in resource files, and thus cannot be modified during
* delta notifications (a CoreException would then be thrown).
*
*/
protected void setSharedProperty(String propertyName, String value, IProgressMonitor monitor) throws CoreException {
try {
IFile rscFile = getProject().getFile(propertyName);
InputStream input = new ByteArrayInputStream(value.getBytes(ENCODING));
// update the resource content
if (rscFile.exists()) {
rscFile.setContents(input, true, false, null);
} else {
rscFile.create(input, true, monitor);
}
} catch (UnsupportedEncodingException e) {
}
}
/**
* Remove a shared persistent property onto a project.
* Note that it is orthogonal to IResource persistent properties, and client code has to decide
* which form of storage to use appropriately. Shared properties produce real resource files which
* can be shared through a VCM onto a server. Persistent properties are not shareable.
*
* shared properties end up in resource files, and thus cannot be modified during
* delta notifications (a CoreException would then be thrown).
*
*/
protected void removeSharedProperty(String propertyName, IProgressMonitor monitor) throws CoreException {
IFile rscFile = getProject().getFile(propertyName);
rscFile.delete(true, true, monitor);
}
/**
* Return a configuration contributor that sets up a vm to allow
* introspection. This will make sure the appropriate paths
* are in the classpath to allow access to the beaninfos, and
* it will setup the beaninfo search path for this project.
*/
public IConfigurationContributor getConfigurationContributor() {
return new ConfigurationContributor(getSearchPath());
}
private static class ConfigurationContributor extends ConfigurationContributorAdapter {
private BeaninfosDoc doc;
List computedSearchPath;
// The nature. If the nature is not set then this contributor is one
// used by some other later proxy registry to get the beaninfo classes into their paths. In that case
// we can expect the config info to be in the session variable for our use. Otherwise we will need to
// add it here. Also don't set searchpath stuff if not nature because only the beaninfo one will do introspection.
private BeaninfoNature nature;
private IConfigurationContributionInfo info;
private IBeanInfoContributor[] explicitContributors;
public ConfigurationContributor(BeaninfosDoc doc) {
this.doc = doc;
}
/*
* Set that this is the nature contributor. Not null, means that this is the contributor being
* used to setup the registry for the project's beaninfo nature. null (default) means that this
* is one created to add to some editor's registry.
*
* Note: This MUST be set before initialize is called or it will not work correctly. If not set, it
* will be considered not for BeanInfo nature directly.
*/
public void setNature(BeaninfoNature nature) {
this.nature = nature;
if (nature != null)
computedSearchPath = new ArrayList(3); // We will be gathering this info.
}
/* (non-Javadoc)
* @see org.eclipse.jem.internal.proxy.core.IConfigurationContributor#initialize(org.eclipse.jem.internal.proxy.core.IConfigurationContributionInfo)
*/
public void initialize(IConfigurationContributionInfo info) {
this.info = info;
try {
if (info.getJavaProject().getProject().getSessionProperty(CONFIG_INFO_SESSION_KEY) == null) {
// TODO For now we will rebuild for each time we open a registry, but it actually is only needed if a classpath
// changes for some reason. At that point we can get it out of here.
computeBeanInfoConfigInfo(info);
}
explicitContributors = (IBeanInfoContributor[]) info.getJavaProject().getProject().getSessionProperty(BEANINFO_CONTRIBUTORS_SESSION_KEY);
} catch (CoreException e) {
BeaninfoPlugin.getPlugin().getLogger().log(e);
}
}
public void contributeClasspaths(final IConfigurationContributionController controller) throws CoreException {
// Contribute for this project
contributeClasspathsForProject(controller, info.getJavaProject().getProject(), doc, true);
if (!info.getProjectPaths().isEmpty()) {
// Run through all of the visible projects and contribute the classpaths (which come from the BeanInfo docs, if they have any).
IWorkspaceRoot root = info.getJavaProject().getProject().getWorkspace().getRoot();
Iterator projIter = info.getProjectPaths().entrySet().iterator();
while (projIter.hasNext()) {
Map.Entry entry = (Map.Entry) projIter.next();
if (((Boolean) entry.getValue()).booleanValue()) {
IResource res = root.findMember((IPath) entry.getKey());
if (res instanceof IProject && ((IProject) res).isOpen() && BeaninfoNature.hasRuntime((IProject) res))
contributeClasspathsForProject(controller, (IProject) res, BeaninfoNature.getRuntime((IProject) res)
.getSearchPath(), false);
}
}
}
if (!info.getContainerIds().isEmpty()) {
// Run through all of the visible container ids that are applicable.
Iterator containerIdItr = info.getContainerIds().entrySet().iterator();
while (containerIdItr.hasNext()) {
Map.Entry entry = (Map.Entry) containerIdItr.next();
if (((Boolean) entry.getValue()).booleanValue()) {
processBeaninfoEntries(BeaninfoPlugin.getPlugin().getContainerIdBeanInfos((String) entry.getKey()),
controller, info.getJavaProject());
}
}
}
if (!info.getPluginIds().isEmpty()) {
// Run through all of the visible plugin ids that are applicable.
Iterator pluginIdItr = info.getPluginIds().entrySet().iterator();
while (pluginIdItr.hasNext()) {
Map.Entry entry = (Map.Entry) pluginIdItr.next();
if (((Boolean) entry.getValue()).booleanValue()) {
processBeaninfoEntries(BeaninfoPlugin.getPlugin().getPluginBeanInfos((String) entry.getKey()), controller, info.getJavaProject());
}
}
}
if (!info.getContainers().isEmpty()) {
// Run through all of the visible containers that implement IBeanInfoContributor and ask them for the contributions.
Iterator containerItr = info.getContainers().entrySet().iterator();
while (containerItr.hasNext()) {
Map.Entry entry = (Map.Entry) containerItr.next();
if (((Boolean) entry.getValue()).booleanValue()) {
if (entry.getKey() instanceof IBeanInfoContributor)
processBeaninfoEntries(((IBeanInfoContributor) entry.getKey()).getBeanInfoEntryContributions(info),
controller, info.getJavaProject());
}
}
}
// And finally run through the explicit contributors.
for (int i = 0; i < explicitContributors.length; i++) {
final IBeanInfoContributor contributor = explicitContributors[i];
processBeaninfoEntries(contributor.getBeanInfoEntryContributions(info), controller, info.getJavaProject());
if (contributor instanceof IConfigurationContributor) {
Platform.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
// do nothing. by default platform logs.
}
public void run() throws Exception {;
if (contributor instanceof IConfigurationContributor)
((IConfigurationContributor) contributor).contributeClasspaths(controller);
}
});
}
}
// Add the common and beaninfovm.jar and any nls to the end of the classpath.
controller.contributeClasspath(BeaninfoPlugin.getPlugin().getBundle(), "beaninfocommon.jar", IConfigurationContributionController.APPEND_USER_CLASSPATH, true); //$NON-NLS-1$
// Add the beaninfovm.jar and any nls to the end of the classpath.
controller.contributeClasspath(BeaninfoPlugin.getPlugin().getBundle(), "vm/beaninfovm.jar", IConfigurationContributionController.APPEND_USER_CLASSPATH, true); //$NON-NLS-1$
}
private IClasspathEntry get(IClasspathEntry[] array, SearchpathEntry se) {
for (int i = 0; i < array.length; i++) {
if (array[i].getEntryKind() == se.getKind() && array[i].getPath().equals(se.getPath()))
return array[i];
}
return null;
}
private static final IBeaninfosDocEntry[] EMPTY_ENTRIES = new IBeaninfosDocEntry[0];
/*
* Contribute classpaths for the specified project. If doc is passed in, then this is the top level and
* all should be added. If no doc, then this is pre-req'd project, and then we will handle exported entries only.
*/
protected void contributeClasspathsForProject(
IConfigurationContributionController controller,
IProject project,
BeaninfosDoc doc,
boolean toplevelProject)
throws CoreException {
IJavaProject jProject = JavaCore.create(project);
IClasspathEntry[] rawPath = jProject.getRawClasspath();
// Search path of this project
IBeaninfosDocEntry[] entries = (doc != null) ? doc.getSearchpath() : EMPTY_ENTRIES;
for (int i = 0; i < entries.length; i++) {
IBeaninfosDocEntry entry = entries[i];
if (entry instanceof BeaninfoEntry) {
BeaninfoEntry be = (BeaninfoEntry) entry;
if (toplevelProject || be.isExported()) {
// First project or this is an exported beaninfo, so we process it.
processBeaninfoEntry(be, controller, jProject);
}
} else if (nature != null){
// Just a search path entry. There is no beaninfo jar to pick up.
// We have a nature, so we process search path.
SearchpathEntry se = (SearchpathEntry) entry;
if (!toplevelProject) {
// We are in a nested project, find the raw classpath entry to see
// if this entry is exported. Only do it if exported. (Note: exported is only used on non-source. Source are always exported).
IClasspathEntry cpe = get(rawPath, se);
if (cpe == null || (cpe.getEntryKind() != IClasspathEntry.CPE_SOURCE && !cpe.isExported())) {
continue; // Not exist or not exported, so we don't want it here either.
}
}
String pkg = se.getPackage();
if (pkg != null) {
// Explicit search path
if (!computedSearchPath.contains(pkg))
computedSearchPath.add(pkg);
} else {
// We no longer allow this, but just to be on safe side we test for it.
}
}
}
}
protected void processBeaninfoEntries(
BeaninfoEntry[] entries,
IConfigurationContributionController controller,
IJavaProject javaProject)
throws CoreException {
if (entries != null) {
for (int i = 0; i < entries.length; i++)
processBeaninfoEntry(entries[i], controller, javaProject);
}
}
protected void processBeaninfoEntry(
BeaninfoEntry entry,
IConfigurationContributionController controller,
IJavaProject javaProject)
throws CoreException {
Object[] cps = entry.getClasspath(javaProject);
for (int j = 0; j < cps.length; j++) {
Object cp = cps[j];
if (cp instanceof IProject)
controller.contributeProject((IProject) cp);
else if (cp instanceof String)
controller.contributeClasspath(ProxyLaunchSupport.convertStringPathToURL((String) cp), IConfigurationContributionController.APPEND_USER_CLASSPATH);
else if (cp instanceof IPath) {
IPath path = (IPath) cp;
Bundle bundle = Platform.getBundle(path.segment(0));
if (bundle != null)
controller.contributeClasspath(bundle, path.removeFirstSegments(1), IConfigurationContributionController.APPEND_USER_CLASSPATH, true);
}
}
if (nature != null) {
// Now add in the package names.
SearchpathEntry[] sees = entry.getSearchPaths();
for (int j = 0; j < sees.length; j++) {
SearchpathEntry searchpathEntry = sees[j];
if (!computedSearchPath.contains(searchpathEntry.getPackage()))
computedSearchPath.add(searchpathEntry.getPackage());
}
}
}
public void contributeToConfiguration(final ILaunchConfigurationWorkingCopy config) {
for (int i = 0; i < explicitContributors.length; i++) {
final int ii = i;
Platform.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
// do nothing. by default platform logs.
}
public void run() throws Exception {
IBeanInfoContributor contributor = explicitContributors[ii];
if (contributor instanceof IConfigurationContributor)
((IConfigurationContributor) contributor).contributeToConfiguration(config);
}
});
}
}
public void contributeToRegistry(final ProxyFactoryRegistry registry) {
if (nature != null)
nature.setProxySearchPath(registry, computedSearchPath);
for (int i = 0; i < explicitContributors.length; i++) {
final int ii = i;
Platform.run(new ISafeRunnable() {
public void handleException(Throwable exception) {
// do nothing. by default platform logs.
}
public void run() throws Exception {
IBeanInfoContributor contributor = explicitContributors[ii];
if (contributor instanceof IConfigurationContributor)
((IConfigurationContributor) contributor).contributeToRegistry(registry);
}
});
}
}
}
}