| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
| <html> |
| <head> |
| <title>Support Logical Resources</title> |
| </head> |
| <body> |
| <table border="0" cellspacing="5" cellpadding="2" width="100%"> |
| <tbody> |
| <tr> |
| <td align="left" valign="top" bgcolor="#0080c0"> <b><font |
| color="#ffffff" face="Arial,Helvetica"> Eclipse 3.1 - Support Logical |
| Resources - Adaptables</font></b> </td> |
| </tr> |
| </tbody> |
| </table> |
| <h1>Solution 1: Support Logical Resources - Adaptables<br> |
| </h1> |
| This document outlines a possible solution to the adaptables problem |
| outlined in the <a href="logical-physical-public.html"><strong>Support |
| Logical Resources</strong></a> plan item problem description document. |
| Interested parties should review |
| this document and verify that their use cases are reflected in the |
| requirements then later that the solution provided satisfies their |
| needs. Feedback is strongly encouraged and may be provided on the |
| platform-team-rev mailing list or in the <a |
| href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=37723">bug report</a> |
| for this plan item<span style="font-weight: bold;"></span> |
| <h3>IlModelElement</h3> |
| <p>This solution involves adding an interface to the Resources plugin |
| that maps logical elements to physical ones. The interface is purposely |
| simple with logical model manipulations omitted. A client can't use |
| this interface to display logical models or gain any interesting |
| additional knowledge about it.</p> |
| <p>Current plugins that add object contributions to <code>IResource</code> |
| (CVS, Search, Compare) would have to also add them to <code>IModelElement</code> |
| and honour the bindings provided by the logical resource. Moreover, the |
| plugins that adapted to <code>IResource</code> to these object |
| contributions shown in the context menu should no longer adapt the <code>IResource</code> |
| and instead implement <code>IModelElement</code> (this could be |
| simplified by adding a tag to the objectConribution XML that indicates |
| that it can be used with <code>IResource</code> or <code>IModelElement</code>).</p> |
| <blockquote> |
| <pre>/**<br> * This interface defines an element of an application that models the<br> * data/processes of a particular problem domain. This purpose of this interface<br> * is to support the transformation of the application model into its underlying<br> * file system resources for the purposes of reconciling the model with versions<br> * of the model stored in a repository. Hence, this interface provides the<br> * bridge between a logical element and the physical resource(s) into which it<br> * is stored but does not provide more comprehensive model access or<br> * manipulations.<br> * <br> * @see IResource<br> */<br>public interface IModelElement {<br> <br> /**<br> * Returns the project which contains this model element. This is a resource<br> * handle operation; neither the element nor the resulting project need<br> * exist. Model elements may not span multiple projects. In other words, all<br> * physical resources that constitute a logical resource are located in the<br> * same project.<br> * <br> * @return the project handle<br> */<br> public IProject getProject();<br> <br> /**<br> * Returns one or more traversals that can be used to access all the<br> * physical resources that constitute the logical resource. A traversal is<br> * simply a set of resources and the depth to which they are to be<br> * traversed. This method returns an array of traversals in order to provide<br> * flexibility in describing the traversals that constitute a model element.<br> * A depth is included to allow the clients of this interface (most likely<br> * repository providers) an opportunity to optimize the operation and also<br> * ensure that resources that were or have become members of the model<br> * element are included in the operation.<br> * <br> * Implementors of this interface should ensure, as much as possible, that<br> * all resources that are or may be members of the model element are<br> * included. For instance, a model element should return the same list of<br> * resources regardless of the existance of the files on the file system.<br> * For example, if a logical resource called "form" maps to "/p1/form.xml"<br> * and "/p1/form.java" then whether form.xml or form.java existed, they<br> * should be returned by this method.<br> * <br> * In some cases, it may not be possible for a model element to know all the<br> * resources that may consitite the element without accessing the state of<br> * the model element in another location (e.g. a repository). This method is<br> * provided with a context which, when provided, gives access to<br> * the members of correcponding remote containers and the contenst of<br> * corresponding remote files. This gives the model element the opportunity<br> * to deduce what additional resources should be included in the traversal.<br> * <br> * @param context gives access to the state of<br> * remote resources that correspond to local resources for the<br> * purpose of determining traversals that adequately cover the<br> * model element resources given the state of the model element<br> * in another location. A null may be provided, in<br> * which case the implementor can assume that only the local<br> * resources are of interest to the client.<br> * @param monitor a progress monitor<br> * @return a set of traversals that cover the resources that constitute the<br> * model element<br> */<br> public ITraversal[] getTraversals(IModelContext context,<br> IProgressMonitor monitor) throws CoreException;<br>}</pre> |
| </blockquote> |
| <h3>ITraversal</h3> |
| <p>A logical model element can be made up of one or more traversals |
| that define the physical resources that make up the model element. |
| Traversals are used to allow the clients of the traversal to optimize |
| their operations based on the depth of the resources being traversed.</p> |
| <blockquote> |
| <pre>/** <br> * A model element traversal is simply a set of resources and<br> * the depth to which each are to be traversed. A set of traversals<br> * is used to describe the resources that constitute a model element.<br> *<br> * @see org.eclipse.core.resources.IResource<br> * @see org.eclipse.core.resources.model.IModelElement<br> */<br>public interface ITraversal {<br> <br> /**<br> * Returns the file system resource(s) for this traversal. <br> * The returned resources must be contained within the same project <br> * and need not exist in the local file system.<br> * <br> * The traversal of the returned resources should be done considering<br> * the flag returned by getDepth. If a resource returned by a <br> * traversal is a file, it should always be visited. If a resource of a traversal<br> * is a folder then files contained in the folder can only be visited if the folder is<br> * IResource.DEPTH_ONE or IResource.DEPTH_INFINITE. <br> * Child folders should only be visited if the depth is IResource.DEPTH_INFINITE.<br> */<br> public IResource[] getResources();<br> <br> /**<br> * Return the depth to which the resources should be traversed.<br> * @return the depth to which the physical resources are to be traversed<br> * (one of IResource.DEPTH_ZERO, IResource.DEPTH_ONE or IResource.DEPTH_INFINITE)<br> */<br> public int getDepth();<br>}</pre> |
| </blockquote> |
| <h3>IModelContext</h3> |
| <p>One of the advantages of a logical model traversal API is that it |
| allows plug-ins to implement any operations they desire |
| in terms of logical resources (e.g. CVS update, CVS commit, CVS tag, |
| dirty |
| decoration, etc.). However, the context of the operation may imply that |
| resources that don't exist locally should be included in the operation. |
| This is not a problem for some logical models. For instance, a java |
| package is a container visited to a depth of one. Also, the plugin |
| example knows all the files that vould potentially be involved, even if |
| they don't exist locally. Given this, a repository provider can easily |
| determine that outgoing deletions should be included when committing or |
| that incoming additions should be included when updating.</p> |
| <p>However, properly determining the set of traversals that adequately |
| cover a model element gets more complicated if the resources that |
| constitute the element depend of the contents of a manifest file (or |
| some other similar mechanism) and may change over time. In this case, |
| it is possible that the model element could access the contents of the |
| manifest file as contained in the remote location. Given this ability, |
| the model element provider is provided with enough context about the |
| operation to the logical model element so a set of traversal can be |
| returned that adequately cover all resources that constitute the model |
| element. </p> |
| <p>This has led to the definition of the following <code>IModelContext</code> |
| interface. </p> |
| <blockquote> |
| <pre>/**<br> * A model context provides a model element with a view of the remote state<br> * of a local resource as it relates to a repository operation that is in<br> * progress. A repository provider can pass an instance of this interface to a<br> * model element when obtaining a set of traversals for a model element. This<br> * allows the model element to query the remote state of a resource in order to<br> * determine if there are resources that exist remotely but do not exist locally<br> * that should be included in the traversal.<br> */<br>public interface IModelContext {<br> <br> /**<br> * Return whether the contents of the corresponding remote differs from the<br> * content of the local file. This can be used by clients to determine if<br> * they nee to fetch the remote contents in order to determine if the<br> * resources that constitute the model element are different in another<br> * location.<br> * <br> * @param file the local file<br> * @param monitor a progress monitor<br> * @return whether the contents of the corresponding remote differs from the<br> * content of the local file<br> * @throws CoreException if this method fails. Reasons include:<br> * - The corresponding remote resource does not exist (status<br> * code will be IResourceStatus.REMOTE_DOES_NOT_EXIST).<br> * - The corresponding remote resource is not a container<br> * (status code will be IResourceStatus.REMOTE_WRONG_TYPE).<br> * - The server communications failed (status code will be<br> * IResourceStatus.REMOTE_COMMUNICATION_FAILURE).<br> */<br> public boolean contentDiffers(IFile file, IProgressMonitor monitor)<br> throws CoreException;<br> <br> /**<br> * Returns an instance of IStorage in order to allow the<br> * caller to access the contents of the remote that corresponds to the given<br> * local resource. The provided local file handle need not exist locally. A<br> * exception is thrown if the corresponding remote resource does not exist<br> * or is not a file.<br> * <br> * This method may be long running as a server may need to be contacted to<br> * obtain the contents of the file.<br> * <br> * @param file the local file<br> * @param monitor a progress monitor<br> * @return a storage that provides access to the contents of the local<br> * resource's corresponding remote resource<br> * @throws CoreException if this method fails. Reasons include:<br> * - The corresponding remote resource does not exist (status<br> * code will beIResourceStatus.REMOTE_DOES_NOT_EXIST).<br> * - The corresponding remote resource is not a file (status<br> * code will be IResourceStatus.REMOTE_WRONG_TYPE).<br> * - The server communications failed (status code will be<br> * IResourceStatus.REMOTE_COMMUNICATION_FAILURE).<br> */<br> public IStorage fetchContents(IFile file, IProgressMonitor monitor)<br> throws CoreException;<br><br> /**<br> * Returns the list of member resources whose corresponding remote resources<br> * are members of the corresponding remote resource of the given local<br> * container. The container need not exist locally and the result may<br> * include entries that do not exist locally and may not include all local<br> * children. An empty list is returned if the remote resource which<br> * corresponds to the container is empty. An exception is thrown if the<br> * corresponding remote does not exist or is not capable of having members.<br> * <br> * This method may be long running as a server may need to be contacted to<br> * obtain the members of the containers corresponding remote resource.<br> * <br> * @param container the local container<br> * @param monitor a progress monitor<br> * @return a list of member resources whose corresponding remote resources<br> * are members of the remote counterpart of the given container<br> * @throws CoreException if this method fails. Reasons include: <br> * - The corresponding remote resource does not exist (status<br> * code will be IResourceStatus.REMOTE_DOES_NOT_EXIST).<br> * - The corresponding remote resource is not a container<br> * (status code will be IResourceStatus.REMOTE_WRONG_TYPE).<br> * - The server communications failed (status code will be<br> * IResourceStatus.REMOTE_COMMUNICATION_FAILURE).<br> */<br> public IResource[] fetchMembers(IContainer container,<br> IProgressMonitor monitor) throws CoreException;<br>}</pre> |
| </blockquote> |
| <p>Although this can be thought of as a repository provider API, it |
| bypasses the complexity by allowing the repository provider to |
| implement the interface in a maner specific to the operation being |
| performed. For instance, for a commit, the repository provider would |
| provide the contents of files that define the base of a manifest file. |
| In other words, the contents of the file would match the contents |
| before the user modified the model element. This would allow for the |
| inclusion of any outgoing deletions. For an update, the repository |
| provider would provide access to the contents of the file in the |
| repository.</p> |
| <h2>How this will affect existing plug-ins</h2> |
| There are two types of plug-ins this will affect, those that adapt |
| elements in the workbench to IResource to get menu contributions and |
| the plug-in that contributes the contributions. Here is the list of |
| extension points with "adaptable" attributes:<br> |
| <br> |
| org.eclipse.ui.decorators<br> |
| org.eclipse.ui.popupMenus<br> |
| org.eclipse.ui.propertyPages<br> |
| <br> |
| All extension points document that "adaptable=true" means the object |
| must be adaptable to IResource. And here is the list of references to |
| these extension points in the SDK:<br> |
| <br> |
| org.eclipse.ui.decorators<br> |
| org.eclipse.pde.ui<br> |
| objectClass=IProject<br> |
| org.eclipse.team.cvs.ui<br> |
| objectClass=IResource<br> |
| org.eclipse.ui.ide<br> |
| objectClass=IResource<br> |
| <br> |
| org.eclipse.ui.propertyPages<br> |
| org.eclipse.team.cvs.ui<br> |
| objectClass=IFile<br> |
| objectClass=IFolder<br> |
| objectClass=IProject<br> |
| org.eclipse.ui.externaltools<br> |
| objectClass=IProject<br> |
| org.eclipse.ui.ide<br> |
| objectClass=IProject<br> |
| objectClass=IResource<br> |
| org.eclipse.update.ui<br> |
| <br> |
| org.eclipse.ui.popupMenus: objectContribution<br> |
| org.eclipse.compare<br> |
| objectClass=IFile<br> |
| objectClass=IContainer<br> |
| objectClass=IResource<br> |
| org.eclipse.pde.ui<br> |
| objectClass=IProject<br> |
| org.eclipse.team.cvs.ui<br> |
| objectClass=IFile<br> |
| objectClass=IResource<br> |
| objectClass=IProject<br> |
| objectClass=IContainer<br> |
| objectClass=IFolder<br> |
| org.eclipse.team.ui<br> |
| objectClass=IResource<br> |
| objectClass=IProject<br> |
| org.eclipse.ui.editors <span |
| style="font-weight: bold;"><br> |
| <br> |
| </span>For the plug-ins contributing extensions to adaptable extension |
| points they will have to make two changes to support the new model |
| element APIs:<br> |
| <ol> |
| <li>Update their plugin.xml files and add IModelElement in the |
| objectClass of their adaptable contributions.</li> |
| <li>Update their actions to work on ITraversals instead of IResource |
| and respect the depth constraints provided in the traversals.</li> |
| <li>(Optional) provide a IModelContext if they manage remote resources</li> |
| </ol> |
| And for plugi-ins that define UI elements with adaptable elements to |
| IResource, they will have to change the IAdaptable#getAdapter() methods |
| to return IModelElement instead of IResource. Here are some examples:<br> |
| <p>For some model elements, simple traversals can be used to describe |
| the element. For instance, here is an example traversal that represents |
| a Java package.</p> |
| <blockquote> |
| <pre>/**<br> * An example traversal for a Java package<br> */<br>public class JavaPackageTraversal implements ITraversal {<br> IContainer javaPackage;<br> public JavaPackageTraversal(IContainer javaPackage) {<br> this.javaPackage = javaPackage;<br> }<br> public IResource[] getResources() {<br> return new IResource[] { javaPackage };<br> }<br> public int getDepth() {<br> return IResource.DEPTH_ONE;<br> }<br>}</pre> |
| </blockquote> |
| <p>The next example class shows a traversal for a fixed set of files. |
| The chosen example is for the data files that define the properties of |
| a plugin in Eclipse. The same files are always returned even if they |
| don't exist locally. It is up to the client of the interface in this |
| case to decide how to handle the files that don't exist locally. If the |
| client is a repository and the operation is an update to fetch the |
| latest contents from the server, then the repository provider would |
| fetch the files from the server if they now esist there or ignore them |
| if they don't exist either locally or remotely.</p> |
| <blockquote> |
| <pre>/**<br> * An example of a fixed traversal that always includes<br> * a certain set of files even if they don't exist <br> * locally<br> */<br>public class PluginDataTraversal implements ITraversal {<br> IProject pluginProject;<br> public PluginDataTraversal(IProject project) {<br> this.pluginProject = project;<br> }<br> public IResource[] getResources() {<br> return new IResource[] {<br> pluginProject.getFile(new Path("plugin.xml")),<br> pluginProject.getFile(new Path("plugin.properties")),<br> pluginProject.getFile(new Path("build.properties")),<br> pluginProject.getFile(new Path("META-INF/MANIFEST.MF"))<br> };<br> }<br> public int getDepth() {<br> return IResource.DEPTH_ZERO;<br> }<br>}</pre> |
| </blockquote> |
| <p> Assuming that the plugin data consists of the above mentioned files |
| and the contents of the schema directory, the model element class for a |
| plugin's data model could be the following:</p> |
| <blockquote> |
| <pre>/**<br> * A plugin model element<br> */<br>public class PluginModel implements IModelElement {<br> IProject pluginProject;<br> public IProject getProject() {<br> return pluginProject;<br> }<br> public ITraversal[] getTraversals(ITraversalContext context,<br> IProgressMonitor monitor) throws CoreException {<br> return new ITraversal[] {<br> new PluginDataTraversal(pluginProject),<br> new ITraversal() {<br> public IResource[] getResources() {<br> return new IResource[] {<br> pluginProject.getFolder("schema")<br> };<br> }<br> // Traverse the folder deeply to include all children<br> public int getDepth() {<br> return IResource.DEPTH_INFINITE;<br> }<br> }<br> };<br> }<br>}</pre> |
| </blockquote> |
| <span style="font-weight: bold;"></span> |
| <h2>Eclipse Platform Issues</h2> |
| <p>The following issues would need to be addressed for this solutions</p> |
| <ol> |
| <li>ObjectContributions have to be duplicated in the plugins that |
| would like to provide both contributions for <code>IResource</code> |
| and <code>ILogicalResource</code>. </li> |
| <ul> |
| <li>This could be simplified by adding support to tag an |
| objectContribution for use with both interfaces<br> |
| <br> |
| </li> |
| </ul> |
| <li>(Bug 53217) Logical classes (e.g. java model, UML) have to |
| implement the |
| interface instead of adapting to it. This is a limitation of the |
| current objectContribution story in the workbench. </li> |
| <ul> |
| <li>Again, this could be circumvented with support in the UI that |
| does the same magic for <code>ILogicalResource</code> that is done for |
| <code>IResource</code></li> |
| <br> |
| </ul> |
| <li>Plugins that add object contributions will have to modify their |
| actions to handle logical resources. There should be some standard UI |
| components for showing the mappings. For instance you could show the |
| logical model at the top and in a details part the physical |
| files/folders that will be affected by the operation.</li> |
| <li>Would it be possible to add a workbench adapter to specific |
| implementations IModelElement in order to get an image and label for a |
| model element?<br> |
| </li> |
| </ol> |
| </body> |
| </html> |