| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
| <html> |
| <head> |
| <title>Support Logical Resources: Resource Mappings</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 |
| - Resource Mappings</font></b></td> |
| </tr> |
| </tbody> |
| </table> |
| <h1>Support Logical Resources - Resource Mappings</h1> |
| This document outlines the chosen 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. Feedback is strongly encouraged and may |
| be provided on the platform-team-dev mailing list or in the <a |
| href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=37723">bug report</a> for |
| this plan item. The API described in this document is not yet final and may undergo |
| some revision before the end of the Eclipse 3.1 development cycle. |
| <h3>The Basic Resource Mapping API</h3> |
| <p>This solution involves adding API to the Resources plugin that maps logical |
| models elements to workspace (i.e. file system) resources. The API 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. It's purpose is simply to map one or more model elements to workspace resources. |
| </p> |
| <p>The API consists of the following classes: </p> |
| <ul> |
| <li><strong>ResourceMapping</strong>: The Class to which logical model elements |
| adapt to indicate that the model corresponds to a set of resources in the |
| workspace. The complete <code>ResourceMapping</code> class can be viewed <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceMapping.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">here</a>. |
| The methods of interest are: |
| <ul> |
| <li><code>Object getModelObject()</code>: The model object from which the |
| mapping was derived (or adapted).</li> |
| <li><code>ResourceTraversal[] getTraversals(ResourceMappingContext, IProgressMonitor)</code>: |
| The resource traversal that cover the resources that constitute the model |
| object.</li> |
| </ul> |
| </li> |
| <li><strong>ResourceTraversal</strong>: A <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceTraversal.java?rev=HEAD&content-type=text/vnd.viewcvs-markup"><code>ResourceTraversal</code></a> |
| contains a set of resources and a depth flag that indicates the depth to which |
| the resources in the traversal are associated with the originating model object. |
| Resource traversals are provided to a client by a resource mapping in order |
| to describe the contents of a model in such a way that the client (e.g. a |
| repository provider) can perform its operations in as efficient a means as |
| possible. Methods of interest are: |
| <ul> |
| <li><code>getResources()</code></li> |
| <li><code>getDepth()</code></li> |
| </ul> |
| </li> |
| <li><strong>ResourceMappingContext</strong>: a context that is provided to the |
| resource mapping by the client when obtaining traversals. This context allows |
| the logical model to determine what the remote state of the model is so that |
| the proper resources can be covered by the resource traversals returned by |
| the resource mapping. The use of the<code> <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceMappingContext.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">ResourceMappingContext</a></code> |
| and<code> <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/RemoteResourceMappingContext.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">RemoteResourceMappingContext</a></code> |
| is a bit more complicated and is described <a href="#ResourceMappingContext">later</a>.</li> |
| </ul> |
| <p>There are two types of plugins that should be interested in resource mappings. |
| Those who provide a model that consists of, or is persisted in, resources in |
| the workspace and those that want to perform operations on resources. The following |
| two sections describe how to associate a resource mapping with a model object |
| and how to contribute menus to objects that adapt to resource mappings.</p> |
| <h4>Adapting a Model to a ResourceMapping</h4> |
| <p>Plugins that adapted their model objects to <code>IResource</code> in order |
| to get resource specific actions shown in the context menu can now adapt to |
| <code>ResourceMapping</code> if a richer description of how the object adapts |
| to resources is beneficial. However, they are not required to do so if there |
| is no benefit. For instance a Java compilation unit (i.e. *.java file shown |
| in a JDT view) that now currently adapts to <code>IFile</code> need not adapt |
| to <code>ResourceMapping</code> since nothing is gained. However, a Java package |
| should adapt to <code>ResourceMapping</code> in order to indicate that the package |
| is only the files in the corresponding folder and not the subfolders.</p> |
| <p>The preferred way to adapt model elements to a resource mapping is to use an |
| adapter factory. The following is the XML markup for contributing an adapter |
| factory in a plugin manifest. </p> |
| <pre style="background-color: rgb(204, 204, 255);"> <extension<br> point="org.eclipse.core.runtime.adapters"><br> <factory<br> class="org.eclipse.example.library.logical.AdapterFactory"<br> adaptableType="org.eclipse.example.library.Book"><br> <adapter type="org.eclipse.core.resources.mapping.ResourceMapping"/><br> </factory><br> <factory<br> class="org.eclipse.example.library.logical.AdapterFactory"<br> adaptableType="org.eclipse.example.library.Library"><br> <adapter type="org.eclipse.core.resources.mapping.ResourceMapping"/><br> </factory><br> ...<br> </extension></pre> |
| <p>The adapter factory implementation would look something like this:</p> |
| <pre style="background-color: rgb(204, 204, 255);">public class AdapterFactory implements IAdapterFactory { |
| public Object getAdapter(Object adaptableObject, Class adapterType) { |
| if((adaptableObject instanceof EObject) && adapterType == ResourceMapping.class) { |
| return new EMFResourceMapping((EObject)adaptableObject); |
| } |
| return null; |
| } |
| |
| public Class[] getAdapterList() { |
| return new Class[] {ResourceMapping.class}; |
| } |
| }</pre> |
| <p>For popup menu contributions, it is not required that the model objects implement |
| the <code>IAdaptable</code> interface. However, if they do, they must ensure |
| that the Platform adapter manager is consulted. This can be done by either subclassing |
| <code>PlatformObject</code> or by using the following line of code:</p> |
| <blockquote> |
| <p><code>Platform.getAdapterManager().getAdapter(Object, Class)</code></p> |
| </blockquote> |
| <p>The above is the preferable approach. However, the model object can implement |
| the IAdaptable interface and provide a <code>getAdapter(Class)</code> implementation |
| that creates returns an instance of <code>ResourceMapping</code> explicitly |
| when asked for one. This is a more straightforward approach but the least desirable |
| as the model must have explicit knowledge of the adaptation to resources.</p> |
| <p>In some cases, the provider of a logical model may not want their model to |
| adapt to <code>IResource</code> in every context or may want the object to adapt |
| differently for object contributions than other contexts, The workbench UI provides |
| a special intermediate adapter API, <code>IContributorResourceAdapter</code>, |
| for this purpose. When objects are being adapted to <code>IResource</code> in |
| the context of object contributions, the workbench first tries to adapt the |
| resource to <code>IContributorResourceAdapter</code> before trying to adapt |
| to <code>IResource</code> directly. A new sub-interface of this interface, <code>IContributorResourceAdapter2</code>, |
| has been added which provides the same capability for <code>ResourceMapping</code>. |
| The only difference is that the model provider should register a factory for<code> |
| IContributorResourceAdapter</code> since the Workbench does an <em>instanceof</em> |
| check to see if the contributed adapter is also an instance of <code>IContributorResourceAdapter2</code>.</p> |
| <p>The implementation of the <code>ResourceMapping</code> subclass for a Java |
| package would look something like this.</p> |
| <pre style="background-color: rgb(204, 204, 255);">public class JavaPackageResourceMapping extends ResourceMapping { |
| IPackageFragment package; |
| ... |
| public getModelObject() { |
| return package; |
| } |
| public ResourceTraversals[] getTraversals( |
| ResourceMappingContext context, |
| IProgressMonitor monitor) { |
| return new ResourceTraversal[] { |
| new ResourceTraversal( |
| new IResource[] { package.getCorrespondingResource() }, |
| IResource.DEPTH_ONE, IResource.NONE) |
| } |
| } |
| }</pre> |
| <p>This is a fairly straightforward mapping so the implementation is not complex. |
| The complexity of the resource mapping implementation will, of course, vary |
| from model to model. The <code>EMFResourceMapping</code> example used above |
| is implemented in the example and it a much more complicated implementation.</p> |
| <h4>Contributing Menus to Resource Mappings</h4> |
| <p>Plug-ins that contribute extensions to adaptable extension points will have |
| to make two changes to support the new <code>ResourceMapping</code> APIs:</p> |
| <ol> |
| <li>Update their plugin.xml files to change the objectClass of their adaptable |
| contributions to <code>ResourceMapping</code> (for those for which this is |
| appropriate).</li> |
| <li>Update their actions to work on <code>ResourceMapping</code> instead of |
| <code>IResource</code> and respect the depth constraints provided in the traversals.</li> |
| <li>(Optional) provide a <code>ResourceMappingContext</code> if they manage |
| remote resources (more on this <a href="#ResourceMappingContext">below</a>)</li> |
| </ol> |
| <p>First of all, plugins that add object contributions to <code>IResource</code> |
| (CVS, Search, Compare) can now add them to <code>ResourceMapping</code> instead, |
| if the action can apply to multiple resources. Here is an XML snippet that contributes |
| a menu action to objects that adapt to resource mappings:</p> |
| <pre style="background-color: rgb(204, 204, 255);"> <extension<br> point="org.eclipse.ui.popupMenus"><br> <objectContribution<br> adaptable="true"<br> objectClass="org.eclipse.core.resources.mapping.ResourceMapping"<br> id="org.eclipse.example.library.ResourceMappingContributions"><br> <action<br> label="Show Resource Mappings"<br> class="org.eclipse.example.library.contributions.ShowResourceMappingsAction"<br> menubarPath="additions"<br> id="org.eclipse.example.library.showMappings"/> <br> </objectContribution><br> </extension></pre> |
| <p>Contributions to <code>ResourceMapping</code> will automatically apply to objects |
| that adapt to <code>IResource</code>. This transitive association is handled |
| by the Workbench.</p> |
| <p>Filtering of the contributions to resource mappings can be done using action |
| filters or expressions. An expression for filtering by project persistent property |
| has been added to allow repository providers to have their menus appear on projects |
| that are mapped to their repositories.</p> |
| <pre style="background-color: rgb(204, 204, 255);"> <extension<br> point="org.eclipse.ui.popupMenus"><br> <objectContribution<br> objectClass="org.eclipse.core.resources.mapping.ResourceMapping"<br> adaptable="true"<br> id="org.eclipse.team.ccvs.ui.ResourceMapperContributions"><br> <enablement><br> <adapt type="org.eclipse.core.resources.mapping.ResourceMapping"><br> <test |
| property="org.eclipse.ui.ide.projectPersistentProperty" |
| args="org.eclipse.team.core.repository=org.eclipse.team.cvs.core.cvsnature" /><br> </adapt><br> </enablement><br> <action<br> label="%UpdateAction.label"<br> definitionId="org.eclipse.team.cvs.ui.update"<br> class="org.eclipse.team.internal.ccvs.ui.actions.UpdateAction"<br> tooltip="%UpdateAction.tooltip"<br> menubarPath="team.main/group2"<br> id="org.eclipse.team.cvs.ui.update"><br> </action> |
| ...<br> </objectContribution><br> </extension></pre> |
| <p>Actions that have been contributed to the <code>ResourceMapping</code> class |
| will be given a selection that contains one or more <code>ResourceMappings</code>. |
| It is the actions responsibility to translate the resource mapping into a set |
| of resources to be operated on. This can be done by calling <code>getTraversals</code> |
| to get the traversals of the mapping. Traversals are used to allow the clients |
| of the traversal to optimize their operations based on the depth of the resources |
| being traversed. A client may traverse the resource manually or may use the |
| resource and the depth as input into an operation that the action delegates |
| to do the work. As an example, if the user performs a CVS update on a java package |
| and the java package resource mapping maps to a folder of depth one, CVS would |
| issue an appropriate command ("cvs update -l" for those who are curious) |
| which would perform a shallow update on the folder the package represents.</p> |
| <h3><a name="ResourceMappingContext"></a>Resource Mapping Context</h3> |
| <p>One of the advantages of a Resource Mapping API is that it allows plug-ins |
| to implement any operations they desire in terms of resource mappings (e.g. |
| CVS update, CVS commit, CVS tag, dirty decoration, etc.). However, the API that |
| has been introduced so far deals only with the local state of the model. When |
| working with a model that may be shared between developers, you end up in a |
| situation where the remote state of the model (i.e. the state of the model that |
| another user has checked-in to the repository) may differ from the state in |
| the workspace. If you performed a CVS update, you would want the local state |
| of the model to match the remote state even if it meant that additional files |
| needed to be included or some files needed to be removed.</p> |
| <p>This is not an issue for some logical models. For instance, a java package |
| is a container visited to a depth of one, regardless of the remote state of |
| the model. 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. However, the resources that constitute some logical |
| models may change over time. For instance, the resources that constitute a model |
| element may depend of the contents of a manifest file (or some other similar |
| mechanism). In order for the resource mapping to return the proper traversal, |
| it must access the remote contents of the manifest file (if it differs from |
| the local contents) in order to see if there are additional resources that need |
| to be included. These additional resources may not exist in the workspace but |
| the repository provider would know how to make sure they did when the selected |
| action was performed.</p> |
| <p>In order to support these more complex models, a <code>RemoteResourceMappingContext</code> |
| can be passed to the <code>ResourceMapping#getTraversals</code> method. When |
| a context is provided, the mapping can use it to ensure that all the necessary |
| resources are included in the traversal. If a context is not provided, the mapping |
| can assume that only the local state is of interest. </p> |
| <h4>When does a ResourceMapping need to worry about the RemoteResourceMappingContext?</h4> |
| <p>A <code>ResourceMapping</code> need only worry about a context supplied to |
| the <code>getTraversals</code> method in cases were the resources that make |
| up a model change over time and the relationship between the model and resources |
| cannot be described by a simple traversal that is guaranteed to encompass those |
| resources (and only those resources) that constitute the model. For example, |
| although the resources of a Java package may change over time, the package can |
| be described as a folder of depth of one so a resource mapping for java packages |
| would not ned to make use of the resource mapping context. </p> |
| <p>As a more complicated example, consider an HTML file that contains several |
| images. Let's make the assumption that any images references from an HTML file |
| are part of the model of that file. When updating the local contents of the |
| HTML file from a repository, the user would expect that any new images would |
| be included. The <code>getTraversals</code> method for a <code>ResourceMapping</code> |
| for the HTML file model would look something like this:</p> |
| <pre style="background-color: rgb(204, 204, 255);">public class HTMLResourceMapping extends ResourceMapping { |
| private HTMLFile htmlFile; |
| getTraversals(ResourceMappingContext context, IPorgressMonitor monitor) { |
| IResource[] resources = htmlFile.getResources(); |
| if (context instanceof RemoteResourceMappingContext) { |
| // Look for any additional resources on the server |
| RemoteResourceMappingContext remoteContext = (RemoteResourceMappingContext)context; |
| IFile file = htmlFile.getFile(); |
| if (remoteContext.contentDiffers(file, monitor) { |
| IStorage storage = remoteContext.fetchContents(file, monitor); |
| IResource[] additionalResources = getReferences(storage.getContents()); |
| resources = combine(resources, additionalResources); |
| |
| } |
| } |
| return new ResourceTraversal(resources, IResource.DEPTH_ZERO); |
| |
| }<br>}</pre> |
| <p>Notice that there are two sets of resources included in the model: those derived |
| from the local contents of the HTML file in the workspace and the contents of |
| the file remotely. In either of these two sets, there may be resources that |
| do not exist in the workspace. For instance, the local HTML file may contain |
| a relative link to an image that does not exist in the workspace. This resource |
| should be included so that it will be fetched if it exists remotely. As for |
| the remote file, it may contain a new copy that references additional images |
| that should be fetched when the new remote contents are downloaded.</p> |
| <h4>When does a client need to provide a RemoteResourceMappingContext?</h4> |
| <p>Any client that is providing the ability of sharing workspace resources through |
| a repository and is supporting ResourceMappings should provide an appropriate |
| context for determining the relevant remote state of the model resources. The |
| context provides three basic queries:</p> |
| <ul> |
| <li>Does the local contents differ from the remote contents</li> |
| <li>What are the remote contents of a file</li> |
| <li>What are the remote members of a folder</li> |
| </ul> |
| <p>The answer to the first question above depends on the type of operation that |
| is being performed:</p> |
| <ul> |
| <li>When checking in a resource, the context would consider that a file's contents |
| differ if the local contents have been modified since the last check-in.</li> |
| <li>When updating a resource, the context would consider that a file's contents |
| differ if the remote contents have been modified since the users last update.</li> |
| <li>When replacing a resource, the context would consider that a file's contents |
| differ if either the the local or remote contents have changed since the last |
| synchronization.</li> |
| </ul> |
| <p>The Eclipse Team API includes a <code>Subcriber</code> class that defines an |
| API for providing the synchronization state between the local workspace and |
| a remote server. A <code><a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.core.resources/src/org/eclipse/core/internal/resources/mapping/ResourceTraversal.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">SubscriberResourceMappingContext</a></code> |
| has been created that uses a <code>Subscriber</code> to access the necessary |
| remote state. Clients that have a <code>Subscriber</code> do not need to do |
| any additional work to get a resource mapping context.</p> |
| <h2>EMF Library Example</h2> |
| <p>This example, available <a href="../../../plugins/library.zip">here</a>, is |
| built on a model that consists of libraries, books and writers. The code is |
| generated by EMF. It requires Eclipse 3.1 M5 and EMF SDK 2.0.1.</p> |
| <p> The model is persisted in several types of files:</p> |
| <ul> |
| <li>*.library files which contain the name of a library and one or more book |
| copy records which reference a book and indicate the number of copies of the |
| book the libraries contain.</li> |
| <li>*.books files which contain one or more books. Each book has several properties |
| (name, number of pages, etc.) as well as a reference to the book's author.</li> |
| <li>*.writers files which contain one or more writers. Each writer has properties |
| (e.g. name) as well as references to one or more books that have been authored |
| by the writer.</li> |
| </ul> |
| <p>The following screen shot shows an example Library.</p> |
| <p><img src="libraryView.png" width="306" height="229"></p> |
| <p>Here is the containment relationship of these model elements</p> |
| <ul> |
| <li>My.writers: contains "Bob"</li> |
| <li>My.books: contains "The Life of Bob"</li> |
| <li>mylib.library: contains copies of the "Life of Bob"</li> |
| </ul> |
| <p>For the purposes of this example, model containment is determined by following |
| all the references from the file containing the selected model element to all |
| other files. For instance, the resources associated with the writer "Bob" |
| would be My.writers, since Bob is contained in that file, and My.books since |
| Bob wrote a book in that book catalog. The resources associated with the Main |
| Library would be mylib.library, My.books, since the library contains a book |
| from that catalog, and My.writers, since the it contains Bob, the author of |
| that book.</p> |
| <p>There are three files of interest in the example code, all of them in the org.eclipse.team.examples.library.adapt |
| plugin.</p> |
| <ul> |
| <li><em>org.eclipse.team.examples.library.adapt/plugin.xml</em>: contains the |
| adapter factory registration and an object contribution to <code>ResourceMapping</code>.</li> |
| <li><em>org.eclipse.team.examples.library.adapt/src/org/eclipse/team/examples/library/adapt/EMFResourceMapping</em>: |
| The resource mapping from the Library model to resources. This is basically |
| a worst-case mapping that uses EMF to traverse the model for file references. |
| Read the class javadoc for more information.</li> |
| <li><em>org.eclipse.team.examples.library.adapt/src/org/eclipse/team/examples/library/adapt/ShowResourceMappingsAction</em>: |
| The action that shows the resource mapping. At the time of writing it uses |
| internal Team UI classes but the intention is to make these classes (or at |
| least the functionality they provide) API in 3.1.</li> |
| </ul> |
| <p>There is also a read-me file (<em>org.eclipse.team.examples.library.adapt/readme.html</em>) |
| that contains instructions on how to use the example. </p> |
| <h2>Conclusion</h2> |
| <h3>How this will affect existing plug-ins</h3> |
| <p>Existing plugins do not need to change at all unless they want to take advantage |
| of the new API. For plugins that provide logical models, it may be worthwhile |
| for them to adapt their model objects to <code>ResourceMappings</code>. Of course, |
| this is only a benefit when combined with a repository provider implementation |
| that contributes at last some of it's actions to resource mappings.</p> |
| <span style="font-weight: bold;"></span> |
| <h3>Open Issues</h3> |
| <p>The following issues would need to be addressed for this solutions.</p> |
| <h4>UI Support</h4> |
| <p>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. |
| There is currently a <code>MappingSelectionDialog</code> in the Team UI plugin |
| but it is not yet API. The goal is to evolve this class to the point where it |
| can be made API.</p> |
| <h4><strong>Incomplete Implementations</strong></h4> |
| <p>Full support for resource mappings depends on these factors:</p> |
| <ol> |
| <li>The model provides a resource mapping for its model elements</li> |
| <li>Repository providers (or other tools) become resource mapping aware.</li> |
| <li>Repository providers (or other tools) provide a resource mapping context |
| for those operations that warrant it.</li> |
| <li>Model that may have resources added or removed at the root level (i.e. the |
| resources returned by the <code>ResourceTraversal#getResources()</code> method) |
| make use of the context to determine any additional resources that should |
| be included in the mapping.</li> |
| </ol> |
| <p>Without the first point, nothing can be done so this is really the main requirement. |
| Assuming we have a model that provides a resource mapping, we end up with the |
| following possible scenarios:</p> |
| <p><strong>A repository provider is not mapping aware</strong>: In this case, |
| the best that can be done is for the model (i.e. the model's view) to provide |
| a Show In>Navigator command which will allow the user to perform Team operations |
| from there. Of course, this is only an issue for model elements that do not |
| map to a single resource. Although this is a bit cumbersome, it will work in |
| many situations. It will not work well for those models that may have added |
| or removed resources at the root level.</p> |
| <p><strong>A repository provider does not provide a resource mapping context or |
| a model does not make use of the context</strong>: For some repository providers, |
| it may be difficult to provide a resource mapping context. It is also possible |
| that a model may not make use of the context to determine which resource should |
| be included in the mapping. In either case, this is only an issue for models |
| that may add or remove resources at the root level. In reality, the number of |
| these cases may be small but the effects will be noticeable since operations |
| may exclude resources. These cases can be handled by falling back to operations |
| on the resources themselves.<br> |
| </p> |
| </body> |
| </html> |