This package contains the source code for the demo described below, as well as a build file to compile and run it.
2. How to use the XMI Documenter API This chapter will describe the main parts of the XMI Documenter API and show how they work together and are used in the demo provided with this article. The starting point for all XMI documentation editing is the class XMIDocumentFactory. This class provides two factory methods (both named createDocument, one takes a java.io.InputStream, the other one an org.w3c.dom.Document) for parsing an XMI input into an XMIDocument instance (see XMIDocumenterUIDemo#load()): FileInputStream xmiStream = new FileInputStream(selectedFile); try { // load the XMI document currentDocument = factory.createDocument(xmiStream); // update the tree treeModel.setRoot(new XMIDocumentNode(currentDocument)); treeModel.reload(); saveButton.setEnabled(true); } finally { xmiStream.close(); }An XMIDocument represents the structure of the parsed XMI file. The instance itself, and all of its members, does not contain all the information that was in the XMI file — it doesn't even contain enough information to reconstruct a valid XMI file from an XMIDocument instance. Instead the XMIDocument and all of its ModelElements are backed by a DOM tree of the parsed XMI file. The read and write of documentation for elements actually occurs by accessing the backing DOM nodes of the corresponding ModelElements. An XMIDocument instance is mainly a holder for all classes and interfaces defined in the XMI document. These classes and interfaces can be retrieved using the getClasses method. Subsets of all defined classes of the document can be retrieved using the getClasesForPackage or getClassesWithOperations methods. For details on the handling of interfaces see Chapter 3 of this article. An example of the retrieval of classes from an XMIDocument instance can be seen in XMIDocumenterUIDemo.XMIDocumentNode#XMIDocumentNode() — this method creates child nodes for all packages and classes defined in the XMIDocument instance: // get classes defined in document final ClassElement[] ownedElements = document.getClasses(); for (int i = 0; i < ownedElements.length; i++) { final ClassElement classElement = ownedElements[i]; final String packageName = classElement.getPackageName(); // find or create the package node containing the class node final PackageNode packageNode = findPackageNode(packageName); // create the class node and add it to the parent package node packageNode.addChildNode(new ModelElementTreeNode(classElement, packageNode)); }A ClassElement contains AttributeElements for all fields defined in the class and OperationElements for all methods defined in the class. OperationElements consist of ParameterElements, an optional ReturnValueElement and ExceptionElements. The whole documentation tree of an XMI document is described by these Elements. The UI editor itself can even abstract the differences between all of these instances and treat all of the instances as ModelElement instances. This way all of the instances can be by handled in a generic way — see XMIDocumenterUIDemo.ModelElementTreeNode which abstracts the editing and handling of ModelElements, e.g. in the constructor: ModelElementTreeNode(final ModelElement element, final TreeNode parent) { super(parent); this.element = element; final ModelElement[] ownedElements = element.getOwnedElements(); for (int i = 0; i < ownedElements.length; i++) { final ModelElement ownedElement = ownedElements[i]; getChildren().add(new ModelElementTreeNode(ownedElement, this)); } }All of the modification logic inside XMIDocumenterUIDemo also relies on the API declared by ModelElement. Basically the methods getName getDocumentationText and setDocumentationText are used to display and edit all of the elements and their documentation. Upon selection of a tree node that represents a ModelElement, any previously started editing of a ModelElement instance is ended by writing the current editor value back to the ModelElement instance — see XMIDocumenterUIDemo#endElementEditing(): if (currentlyEditedModelElement != null) { final String text = editingArea.getText(); if (!text.equals(currentlyEditedModelElement.getDocumentationText())) { currentlyEditedModelElement.setDocumentationText(text); } currentlyEditedModelElement = null; editingArea.setText(""); editingArea.setEnabled(false); }Afterwards the ModelElement represented by the selected node becomes the currently edited element and its current documentation text is written to the documentation editor pane — see XMIDocumenterUIDemo#editModelEelement(): private void editModelElement(final ModelElement modelElement, final ModelElementTreeNode modelElementTreeNode) { endElementEditing(); currentlyEditedTreeNode = modelElementTreeNode; currentlyEditedModelElement = modelElement; editingArea.setText(currentlyEditedModelElement.getDocumentationText()); editingArea.setEnabled(true); }The only point at which this generic handling of ModelElements is broken up is the XMIDocumenterUIDemo.XMIDocumenterTreeRenderer, that checks the type of all of the ModelElement instances using instanceof to be able to display the type of the ModelElement represented by a TreeNode. When editing of the XMI document has been finished, the document instance can be written into an java.io.OutputStream using XMIDocument#writeTo() — see XMIDocumenterUIDemo#save() for an example on this: final FileOutputStream out = new FileOutputStream(selectedFile); try { currentDocument.writeTo(out); treeModel.setRoot(new XMIDocumentNode(currentDocument)); treeModel.reload(); } finally { out.close(); }After describing all of the core elements of the XMI Documenter API, this article has hopefully helped illustrate how to load, modify and save XMI files using XMI Documenter component. 3. Implementation details of XMI documenter that are worth noting This chapter describes some of the XMI Documenter implementation details that are not suitable to be mentioned in the API documentation or CS of a component. The most interesting aspect of the XMI Documenter for designers who want to use this editor might be what is necessary to exist in the XMI document before staring editing of documentation. As the XMI Documenter does not provide any functionality for the creation of model elements, all elements that need to be documented must exist before loading the document. This basically means that all classes have to be declared, all methods and fields to be documented must be created inside Poseidon and all parameters need to be modeled. Special care must be taken of any exception documentation. In the XMI document created by Poseidon, there is no real structural model element that represents a throws declaration of a method. Instead, a method can have multiple throws-documentation elements (which in general are XML elements, that contain the all text after a throws tag). When documenting exceptions in methods, you must first create throws documentation for each of the exceptions that will be documented for the method, and the throws documentation must contain at least one word (which will be interpreted as the exception class name) or it will be ignored during XMI examination of the XMIDocumentFactory. So basically, before documenting, create all classes, fields and methods (including arguments, return value and throws documentation) as the XMI Documenter is unable to create model elements, it can only modify existing ones. The first thing that may seem odd is that interface and class elements found in the analyzed XMI file are both mapped to ClassElement instances and thus cannot be distinguished at runtime. The reason for this is that the parsing and handling of interfaces in the XMI file was not contained in the original design (neither designer nor design review board noticed that). When this was identified by a developer during dev phase, the PM decided that it was too late (i.e. the deadline was too near) to make any changes to the public API of the XMI documenter component — so the least intrusive fix was introduced — mapping interfaces to ClassElements also. Perhaps a 1.1 component will fix this issue. One more thing worth mentioning is that the XMI Documenter component does not have the scope of validating given XMI files. It may continue and work on an XMI file that is syntactically invalid but is valid enough in structure and content to be parsed ant modified by the component — so this basically means Garbage in-Garbage Out, i.e. the component will not always detect invalid XMI documents. The third detail that is worth noticing is that when the XMIDocument instance is constructed using the method XMIDocumentFactory#createDocument(org.w3c.dom.Document), the backing DOM used by the created XMIDocument instance is the one given as argument to that create method — so modifications to the XMI document result in modifications of the DOM document and vice versa. In the worst case that means that external modifications of the DOM document may break the integrity of the XMIDocument instance and the result of the file written out by XMIDocument#writeTo may be unpredictable. So keep that in mind when using that particular factory method. 4. How to setup and run the XMI Documenter UI demo The demo provided along with this article contains the class mentioned in the article and a build script to compile and run the editor. This demo can either be used as-is or as a starting point for your own modifications and improvements to the XMI editor. Most of this chapter describes the setup of dependencies of XMI Documenter. 4.1 TopCoder Software Components used
4.2 Third Party Components used by XMI Documenter
4.3 Dependencies Configuration Follow the instructions at https://jaxp.dev.java.net/Updating.html, section "Using JAXP with version 1.4 of the Java 2 platform" to configure JAXP 1.3 for use with Java 1.4. JAXP 1.3 is already included in Java 1.5, so nothing needs to be done for a Java 1.5 environment. In General the following steps are required:
To run the demo GUI simply call ant startdemo. The window shows three buttons, load can be used to load an XMI file (*.zuml files must be unzipped before editing), then while navigating through the tree you can view and edit the documentation of the selected node in the edit area on the right hand side. At any time the current state of the XMI document can be saved using the save button. The third button highlights tree nodes in red if they either contain no documentation or if they contain children with no documentation. Download Demo |
|