platform-vcm-home/docs/online/team3.1/logical-physical-mappings.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (view) (download) (as text)

1 : mvalenta 1.1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 :     <html>
3 :     <head>
4 :     <title>Support Logical Resources: Resource Mappings</title>
5 :     </head>
6 :     <body>
7 :     <table border="0" cellspacing="5" cellpadding="2" width="100%">
8 :     <tbody>
9 :     <tr>
10 :     <td align="left" valign="top" bgcolor="#0080c0"> <b><font
11 :     color="#ffffff" face="Arial,Helvetica"> Eclipse 3.1 - Support Logical Resources
12 :     - Resource Mappings</font></b></td>
13 :     </tr>
14 :     </tbody>
15 :     </table>
16 :     <h1>Support Logical Resources - Resource Mappings</h1>
17 :     This document outlines the chosen solution to the adaptables problem outlined
18 :     in the <a href="logical-physical-public.html"><strong>Support Logical Resources</strong></a>
19 :     plan item problem description document. Feedback is strongly encouraged and may
20 :     be provided on the platform-team-dev mailing list or in the <a
21 :     href="http://bugs.eclipse.org/bugs/show_bug.cgi?id=37723">bug report</a> for
22 :     this plan item. The API described in this document is not yet final and may undergo
23 :     some revision before the end of the Eclipse 3.1 development cycle.
24 :     <h3>The Basic Resource Mapping API</h3>
25 :     <p>This solution involves adding API to the Resources plugin that maps logical
26 : mvalenta 1.2 models elements to workspace (i.e. file system) resources. The API is purposely
27 :     simple with logical model manipulations omitted. A client can't use this interface
28 :     to display logical models or gain any interesting additional knowledge about
29 :     it. It's purpose is simply to map one or more model elements to workspace resources.
30 :     </p>
31 :     <p>The API consists of the following classes: </p>
32 : mvalenta 1.1 <ul>
33 : mvalenta 1.2 <li><strong>ResourceMapping</strong>: The Class to which logical model elements
34 : mvalenta 1.1 adapt to indicate that the model corresponds to a set of resources in the
35 :     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/resources/mapping/ResourceMapping.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">here</a>.
36 :     The methods of interest are:
37 :     <ul>
38 :     <li><code>Object getModelObject()</code>: The model object from which the
39 :     mapping was derived (or adapted).</li>
40 :     <li><code>ResourceTraversal[] getTraversals(ResourceMappingContext, IProgressMonitor)</code>:
41 :     The resource traversal that cover the resources that constitute the model
42 :     object.</li>
43 :     </ul>
44 :     </li>
45 :     <li><strong>ResourceTraversal</strong>: A <a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.core.resources/src/org/eclipse/core/resources/mapping/ResourceTraversal.java?rev=HEAD&content-type=text/vnd.viewcvs-markup"><code>ResourceTraversal</code></a>
46 : mvalenta 1.4 contains a set of resources and a depth flag that indicates the depth to which
47 :     the resources in the traversal are associated with the originating model object.
48 :     Resource traversals are provided to a client by a resource mapping in order
49 :     to describe the contents of a model in such a way that the client (e.g a repository
50 :     provider) can perform its operations in as efficient a means as possible.
51 :     Methods of interest are:
52 : mvalenta 1.2 <ul>
53 :     <li><code>getResources()</code></li>
54 :     <li><code>getDepth()</code></li>
55 :     </ul>
56 :     </li>
57 : mvalenta 1.1 <li><strong>ResourceMappingContext</strong>: a context that is provided to the
58 : mvalenta 1.2 resource mapping by the client when obtaining traversals. This context allows
59 :     the logical model to determine what the remote state of the model is so that
60 :     the proper resources can be covered by the resource traversals returned by
61 :     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/resources/mapping/ResourceMappingContext.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">ResourceMappingContext</a></code>
62 : mvalenta 1.1 is a bit more complicated and is described <a href="#ResourceMappingContext">later</a>.</li>
63 :     </ul>
64 :     <p>There are two types of plugins that should be interested in resource mappings.
65 : mvalenta 1.2 Those who provide a model that consists of, or is persisted in, resources in
66 :     the workspace and those that want to perform operations on resources. The following
67 : mvalenta 1.1 two sections describe how to associate a resource mapping with a model object
68 :     and how to contribute menus to objects that adapt to resource mappings.</p>
69 :     <h4>Adapting a Model to a ResourceMapping</h4>
70 :     <p>Plugins that adapted their model objects to <code>IResource</code> in order
71 : mvalenta 1.2 to get resource specific actions shown in the context menu can now adapt to
72 :     <code>ResourceMapping</code> if a richer description of how the object adapts
73 :     to resources is benefitial. However, they are not required to do so if there
74 : mvalenta 1.4 is no benefit. For instance a Java compilation unit (i.e. *.java file shown
75 :     in a JDT view) that now currently adapts to <code>IFile</code> need not adapt
76 :     to <code>ResourceMapping</code> since nothing is gained. However, a Java package
77 :     should adapt to <code>ResourceMapping</code> in order to indicate that the package
78 :     is only the files in the corresponding folder and not the subfolders.</p>
79 : mvalenta 1.2 <p>The preferred way to adapt model elements to a resource mapping is to use an
80 :     adapter factory. The following is the XML markup for contributing an adapter
81 :     factory in a plugin manifest. </p>
82 : mvalenta 1.1 <pre style="background-color: rgb(204, 204, 255);"> &lt;extension<br> point=&quot;org.eclipse.core.runtime.adapters&quot;&gt;<br> &lt;factory<br> class=&quot;org.eclipse.example.library.logical.AdapterFactory&quot;<br> adaptableType=&quot;org.eclipse.example.library.Book&quot;&gt;<br> &lt;adapter type=&quot;org.eclipse.core.resources.mapping.ResourceMapping&quot;/&gt;<br> &lt;/factory&gt;<br> &lt;factory<br> class=&quot;org.eclipse.example.library.logical.AdapterFactory&quot;<br> adaptableType=&quot;org.eclipse.example.library.Library&quot;&gt;<br> &lt;adapter type=&quot;org.eclipse.core.resources.mapping.ResourceMapping&quot;/&gt;<br> &lt;/factory&gt;<br> ...<br> &lt;/extension&gt;</pre>
83 : mvalenta 1.2 <p>The adapter factory implementation would look something like this:</p>
84 :     <pre style="background-color: rgb(204, 204, 255);">public class AdapterFactory implements IAdapterFactory {
85 :     public Object getAdapter(Object adaptableObject, Class adapterType) {
86 :     if((adaptableObject instanceof EObject) && adapterType == ResourceMapping.class) {
87 :     return new EMFResourceMapping((EObject)adaptableObject);
88 :     }
89 :     return null;
90 :     }
91 :    
92 :     public Class[] getAdapterList() {
93 :     return new Class[] {ResourceMapping.class};
94 :     }
95 :     }</pre>
96 : mvalenta 1.1 <p>For popup menu contribuions, it is not required that the model objects implement
97 : mvalenta 1.4 the <code>IAdaptable</code> interface. However, if they do, they must ensure
98 : mvalenta 1.1 that the Platform adapter manager is consulted. This can be done by either subclassing
99 :     <code>PlatformObject</code> or by using the following line of code:</p>
100 :     <blockquote>
101 :     <p><code>Platform.getAdapterManager().getAdapter(Object, Class)</code></p>
102 :     </blockquote>
103 :     <p>The above is the preferrable approach. However, the model object can implement
104 :     the IAdaptable interface and provide a <code>getAdapter(Class)</code> implementation
105 :     that creates returns an instance of <code>ResourceMapping</code> explicitly
106 :     when asked for one. This is a more straightforward approach but the least desirable
107 :     as the model must have explicit knowledge of the adaptation to resources.</p>
108 : mvalenta 1.3 <p>The implementation of the <code>ResourceMapping</code> subclass for a Java
109 :     package would look something like this.</p>
110 :     <pre style="background-color: rgb(204, 204, 255);">public class JavaPackageResourceMapping extends ResourceMapping {
111 :     IPackageFragment package;
112 :     ...
113 :     public getModelObject() {
114 :     return package;
115 :     }
116 :     public ResourceTraversals[] getTraversals(
117 :     ResourceMappingContext context,
118 :     IProgressMonitor monitor) {
119 :     return new ResourceTraversal[] {
120 :     new ResourceTraversal(
121 :     new IResource[] { package.getCorrespondingResource() },
122 :     IResource.DEPTH_ONE, IResource.NONE)
123 :     }
124 :     }
125 :     }</pre>
126 :     <p>This is a fairly straightforward mapping so the implementaion is not complex.
127 :     The complexity of the resource mapping implementation will, of course, vary
128 :     from model to model. The <code>EMFResourceMapping</code> example used above
129 :     is implemented in the example and it a much more complicated implementation.</p>
130 : mvalenta 1.1 <h4>Contributing Menus to Resource Mappings</h4>
131 :     <p>Plug-ins that contribute extensions to adaptable extension points will have
132 :     to make two changes to support the new <code>ResourceMapping</code> APIs:</p>
133 :     <ol>
134 : mvalenta 1.4 <li>Update their plugin.xml files to change the objectClass of their adaptable
135 : mvalenta 1.1 contributions to <code>ResourceMapping</code> (for those for which this is
136 :     appropriate).</li>
137 :     <li>Update their actions to work on <code>ResourceMapping</code> instead of
138 : mvalenta 1.4 <code>IResource</code> and respect the depth constraints provided in the traversals.</li>
139 : mvalenta 1.1 <li>(Optional) provide a <code>ResourceMappingContext</code> if they manage
140 :     remote resources (more on this <a href="#ResourceMappingContext">below</a>)</li>
141 :     </ol>
142 :     <p>First of all, plugins that add object contributions to <code>IResource</code>
143 :     (CVS, Search, Compare) can now add them to <code>ResourceMapping</code> instead,
144 : mvalenta 1.4 if the action can apply to multiple resources. Here is an XML snipet that contributes
145 :     a menu action to objects that adapt to resource mappings:</p>
146 : mvalenta 1.1 <pre style="background-color: rgb(204, 204, 255);"> &lt;extension<br> point=&quot;org.eclipse.ui.popupMenus&quot;&gt;<br> &lt;objectContribution<br> adaptable=&quot;true&quot;<br> objectClass=&quot;org.eclipse.core.resources.mapping.ResourceMapping&quot;<br> id=&quot;org.eclipse.example.library.ResourceMappingContributions&quot;&gt;<br> &lt;action<br> label=&quot;Show Resource Mappings&quot;<br> class=&quot;org.eclipse.example.library.contributions.ShowResourceMappingsAction&quot;<br> menubarPath=&quot;additions&quot;<br> id=&quot;org.eclipse.example.library.showMappings&quot;/&gt; <br> &lt;/objectContribution&gt;<br> &lt;/extension&gt;</pre>
147 :     <p>Contributions to <code>ResourceMapping</code> will automatically apply to objects
148 :     that adapt to <code>IResource</code>. This transitive association is handled
149 :     by the Workbench.</p>
150 :     <p>Filtering of the contributions to resource mappings can be done using action
151 : mvalenta 1.4 filters or expressions. An expression for filtering by project persistant property
152 :     has been added to allow repository providers to have their menus appear on projects
153 :     that are mapped to their repositories.</p>
154 : mvalenta 1.1 <pre style="background-color: rgb(204, 204, 255);"> &lt;extension<br> point=&quot;org.eclipse.ui.popupMenus&quot;&gt;<br> &lt;objectContribution<br> objectClass=&quot;org.eclipse.core.resources.mapping.ResourceMapping&quot;<br> adaptable=&quot;true&quot;<br> id=&quot;org.eclipse.team.ccvs.ui.ResourceMapperContributions&quot;&gt;<br> &lt;enablement&gt;<br> &lt;adapt type=&quot;org.eclipse.core.resources.mapping.ResourceMapping&quot;&gt;<br> &lt;test
155 :     property=&quot;org.eclipse.ui.ide.projectPersistentProperty&quot;
156 :     args=&quot;org.eclipse.team.core.repository=org.eclipse.team.cvs.core.cvsnature&quot; /&gt;<br> &lt;/adapt&gt;<br> &lt;/enablement&gt;<br> &lt;action<br> label=&quot;%UpdateAction.label&quot;<br> definitionId=&quot;org.eclipse.team.cvs.ui.update&quot;<br> class=&quot;org.eclipse.team.internal.ccvs.ui.actions.UpdateAction&quot;<br> tooltip=&quot;%UpdateAction.tooltip&quot;<br> menubarPath=&quot;team.main/group2&quot;<br> id=&quot;org.eclipse.team.cvs.ui.update&quot;&gt;<br> &lt;/action&gt;
157 :     ...<br> &lt;/objectContribution&gt;<br> &lt;/extension&gt;</pre>
158 :     <p>Actions that have been contributed to the <code>ResourceMapping</code> class
159 :     will be given a selection that contains one or more <code>ResourceMappings</code>.
160 :     It is the actions responsibility to translate the resource mapping into a set
161 :     of resources to be operated on. This can be done by calling <code>getTraversals</code>
162 :     to get the traversals of the mapping. Traversals are used to allow the clients
163 :     of the traversal to optimize their operations based on the depth of the resources
164 :     being traversed. A client may traverse the resource manually or may use the
165 :     resource and the depth as input into an operation that the action delegates
166 :     to do the work. As an example, if the user performs a CVS update on a java package
167 :     and the java package resource mapping maps to a folder of depth one, CVS would
168 :     issue an appropriat command (&quot;cvs update -l&quot; for those who are curious)
169 :     which would perform a shallow update on the folder the pakcage represents.</p>
170 :     <h3><a name="ResourceMappingContext"></a>Resource Mapping Context</h3>
171 :     <p>One of the advantages of a Resouce Mapping API is that it allows plug-ins to
172 :     implement any operations they desire in terms of resource mappings (e.g. CVS
173 :     update, CVS commit, CVS tag, dirty decoration, etc.). However, the API that
174 :     has been introduced so far deals only with the local state of the model. When
175 :     working with a model that may be shared between developers, you end up in a
176 :     situation where the remote state of the model (i.e. the state of the model that
177 :     another user has checked-in to the repository) may differ from the state in
178 :     the workspace. If you performed a CVS update, you would want the local state
179 :     of the model to match the remote state even if it meant that additional files
180 :     needed to be included or some files needed to be removed.</p>
181 :     <p>This is not an issue for some logical models. For instance, a java package
182 :     is a container visited to a depth of one, regardless of the remote state of
183 :     the model. Given this, a repository provider can easily determine that outgoing
184 :     deletions should be included when committing or that incoming additions should
185 :     be included when updating. However, the resources that constitute some logical
186 :     models may change over time. For instance, the resources that constitute a model
187 :     element may depend of the contents of a manifest file (or some other similar
188 : mvalenta 1.4 mechanism). In order for the resource mapping to return the proper traversal,
189 : mvalenta 1.1 it must access the remote contents of the manifest file (if it differes from
190 :     the local contents) in order to see if there are additional resources that need
191 :     to be included. These additional resources may not exist in the workspace but
192 :     the repository provider would know how to make sure they did when the selected
193 :     action was performed.</p>
194 :     <p>In order to support these more complex models, a <code>ResourceMappingContext</code>
195 :     can be passed to the <code>ResourceMapping#getTraversals</code> method. When
196 :     a context is provided, the mapping can use it to ensure that all the necessary
197 :     resources are included in the traversal. If a context is not provided, the mapping
198 :     can assume that only the local state is of interest. </p>
199 :     <h4>When does a ResourceMapping need to worry about the ResourceMappingContext?</h4>
200 :     <p>A <code>ResourceMapping</code> need only worry about a context supplied to
201 :     the <code>getTraversals</code> method in cases were the resources that make
202 :     up a model change over time and the relationship between the model and resources
203 :     cannot be described by a simple traversal that is gauranteed to encompass those
204 :     resources (and only those resources) that consitute the model. For example,
205 :     although the resources of a Java package may change over time, the package can
206 : mvalenta 1.3 be described as a folder of depth of one so a resource mapping for java packages
207 : mvalenta 1.1 would not ned to make use of the resource mapping context. </p>
208 :     <p>As a more complicated example, consider an HTML file that contains several
209 : mvalenta 1.4 images. Let's make the assumption that any images references from an HTML file
210 :     are part of the model of that file. When updating the local contents of the
211 :     HTML file from a repository, the user would expect that any new images would
212 :     be included. The <code>getTraversals</code> method for a <code>ResourceMapping</code>
213 :     for the HTML file model would look something like this:</p>
214 : mvalenta 1.2 <pre style="background-color: rgb(204, 204, 255);">public class HTMLResourceMapping extends ResourceMapping {
215 : mvalenta 1.1 private HTMLFile htmlFile;
216 :     getTraversals(ResourceMappingContext context, IPorgressMonitor monitor) {
217 : mvalenta 1.4 IResource[] resources = htmlFile.getResources();
218 : mvalenta 1.1 if (context != null) {
219 :     // Look for any additional resources on the server
220 :     IFile file = htmlFile.getFile();
221 :     if (context.contentDiffers(file, monitor) {
222 :     IStorage storage = context.fetchContents(file, monitor);
223 :     IResource[] additionalResources = getReferences(storage.getContents());
224 :     resources = combine(resources, additionalResources);
225 :    
226 :     }
227 :     }
228 :     return new ResourceTraversal(resources, IResource.DEPTH_ZERO);
229 :    
230 :     }<br>}</pre>
231 : mvalenta 1.6 <p>Notice that there are two sets of resources included in the model: those derived
232 :     from the local contents of the HTML file in the worspace and the contents of
233 :     the file remotely. In either of these two sets, there may be resources that
234 :     do not exist in the workspace. For instance, the local HTML file may contain
235 :     a relative link to an image that does not exist in the workspace. This resource
236 :     should be included so that it will be fetched if it exists remotely. As for
237 :     the remote file, it may contain a new copy that references additional images
238 :     that should be fetched when the new remote contents are downloaded.</p>
239 : mvalenta 1.1 <h4>When does a client need to provide a ResourceMappingContext?</h4>
240 :     <p>Any client that is providing the ability of sharing workspace resources through
241 :     a repository and is supporting ResourceMappings should provide an appropriate
242 : mvalenta 1.4 context for determining the relevant remote state of the model resources. The
243 : mvalenta 1.1 context provides three basic quieries:</p>
244 :     <ul>
245 :     <li>Does the local contents differ from the remote contents</li>
246 :     <li>What are the remote contents of a file</li>
247 :     <li>What are the remote memebers of a folder</li>
248 :     </ul>
249 :     <p>The answer to the first question above depends on the type of operation that
250 :     is being performed:</p>
251 :     <ul>
252 :     <li>When checking in a resource, the context would consider that a file's contents
253 :     differ if the local contents have been modified since the last check-in.</li>
254 :     <li>When updating a resource, the context would consider that a file's contents
255 :     differ if the remote contents have been modified since the users last update.</li>
256 :     <li>When replacing a resource, the context would consider that a file's contents
257 :     differ if either the the local or remote contents have changed since the last
258 :     synchronization.</li>
259 :     </ul>
260 :     <p>The Eclipse Team API includes a <code>Subcriber</code> class that defines an
261 :     API for providing the synchronization state between the local workspace and
262 : mvalenta 1.6 a remote server. A <code><a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.team.core/src/org/eclipse/team/core/subscribers/SubscriberResourceMappingContext.java?rev=HEAD&content-type=text/vnd.viewcvs-markup">SubscriberResourceMappingContext</a></code>
263 :     has been created that uses a <code>Subscriber</code> to access the necessary
264 :     remote state. Clients that have a <code>Subscriber</code> do not need to do
265 :     any additional work to get a resource mapping context.</p>
266 : mvalenta 1.5 <h2>EMF Library Example</h2>
267 :     <p>This example, available <a href="../../../plugins/library.zip">here</a>, is
268 :     built on a model that consists of libraries, books and writers. The code is
269 :     generated by EMF. It requires Eclipse 3.1 M5 and EMF SDK 2.0.1.</p>
270 :     <p> The model is persisted in several types of files:</p>
271 :     <ul>
272 :     <li>*.library files which contain the name of a library and one ofr more book
273 :     copy records which reference a bokk and indicate the number of copies of the
274 :     book the libraries contain.</li>
275 :     <li>*.books files which contain one or more books. Each book has serveral properties
276 :     (name, number of pages, etc.) as well as a reference to the book's author.</li>
277 :     <li>*.writers files which contain one or more writers. Each writer has properties
278 :     (e.g. name) as weel as references to one or more books that have been authored
279 :     by the writer.</li>
280 :     </ul>
281 :     <p>The following screenshot shows an example Library.</p>
282 :     <p><img src="libraryView.png" width="306" height="229"></p>
283 :     <p>Here is the containment relationship of these model elements</p>
284 :     <ul>
285 :     <li>My.writers: contains &quot;Bob&quot;</li>
286 :     <li>My.books: contains &quot;The Life of Bob&quot;</li>
287 :     <li>mylib.library: contains copeis of the &quot;Life of Bob&quot;</li>
288 :     </ul>
289 :     <p>For the purposes of this example, model containment is determined by following
290 :     all the references from the file containing the selected model element to all
291 :     other files. For instance, the resources associated with the writer &quot;Bob&quot;
292 :     would be My.writers, since Bob is contained in that file, and My.books since
293 :     Bob wrote a book in that book catalog. The resources associated with the Main
294 :     Library would be mylib.library, My.books, since the library contains a book
295 :     from that catalog, and My.writers, since the it contains Bob, the auther of
296 :     that book.</p>
297 :     <p>There are three files of interest in the example code, all of them in the org.eclipse.team.examples.library.adapt
298 :     plugin.</p>
299 :     <ul>
300 :     <li><em>org.eclipse.team.examples.library.adapt/plugin.xml</em>: containes the
301 :     adapter factory registration and an object contribution to <code>ResourceMapping</code>.</li>
302 :     <li><em>org.eclipse.team.examples.library.adapt/src/org/eclipse/team/examples/library/adapt/EMFResourceMapping</em>:
303 :     The resource mapping from the Library model to resources. This is basically
304 :     a worst-case mapping that uses EMF to traverse the modle for file references.
305 :     Read the class javadoc for more information.</li>
306 :     <li><em>org.eclipse.team.examples.library.adapt/src/org/eclipse/team/examples/library/adapt/ShowResourceMappingsAction</em>:
307 :     The action that shows the resource mapping. At the time of writting it uses
308 :     internal Team UI classes but the intention is to make these classes (or at
309 :     least the funtionality they provide) API in 3.1.</li>
310 :     </ul>
311 :     <p>There is also a read-me file (<em>org.eclipse.team.examples.library.adapt/readme.html</em>)
312 :     that contains instructions on how to use the example. </p>
313 :     <h2>Conclusion</h2>
314 :     <h3>How this will affect existing plug-ins</h3>
315 : mvalenta 1.1 <p>Existing plugins do not need to change at all unless they want to take advantage
316 : mvalenta 1.5 of the new API. For plugins that provide logical models, it may be worthwhile
317 : mvalenta 1.1 for them to adapt their model objects to <code>ResourceMappings</code>. Of course,
318 :     this is only a benefit when combined with a repository provider implementation
319 :     that contributes at last some of it's actions to resource mappings.</p>
320 :     <span style="font-weight: bold;"></span>
321 : mvalenta 1.5 <h3>Open Issues</h3>
322 : mvalenta 1.1 <p>The following issues would need to be addressed for this solutions.</p>
323 :     <ul>
324 :     <li> Plugins that add object contributions will have to modify their actions
325 :     to handle logical resources. There should be some standard UI components for
326 :     showing the mappings. For instance you could show the logical model at the
327 :     top and in a details part the physical files/folders that will be affected
328 :     by the operation.</li>
329 :     <li>Can the user benefit from resource mappings in cases where the repository
330 : mvalenta 1.3 provider isn't mapping aware?</li>
331 :     <li>What about reposiory providers that cannot provide a remote mapping context?</li>
332 : mvalenta 1.6 <li>What should a model provider do if they cannot reliably determine the remote
333 : mvalenta 1.5 model state?<br>
334 : mvalenta 1.1 </li>
335 :     </ul>
336 :     </body>
337 :     </html>