Coverage Report - org.argouml.persistence.PrivateHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
PrivateHandler
0%
0/75
0%
0/64
5.111
PrivateHandler$NameVal
0%
0/6
N/A
5.111
 
 1  
 /* $Id: PrivateHandler.java 17832 2010-01-12 19:02:29Z linus $
 2  
  *****************************************************************************
 3  
  * Copyright (c) 2009 Contributors - see below
 4  
  * All rights reserved. This program and the accompanying materials
 5  
  * are made available under the terms of the Eclipse Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/epl-v10.html
 8  
  *
 9  
  * Contributors:
 10  
  *    tfmorris
 11  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 1996-2009 The Regents of the University of California. All
 17  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 18  
 // software and its documentation without fee, and without a written
 19  
 // agreement is hereby granted, provided that the above copyright notice
 20  
 // and this paragraph appear in all copies.  This software program and
 21  
 // documentation are copyrighted by The Regents of the University of
 22  
 // California. The software program and documentation are supplied "AS
 23  
 // IS", without any accompanying services from The Regents. The Regents
 24  
 // does not warrant that the operation of the program will be
 25  
 // uninterrupted or error-free. The end-user understands that the program
 26  
 // was developed for research purposes and is advised not to rely
 27  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 28  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 29  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 30  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 31  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 32  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 33  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 34  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 35  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 36  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 37  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 38  
 package org.argouml.persistence;
 39  
 
 40  
 import java.util.StringTokenizer;
 41  
 
 42  
 import org.apache.log4j.Logger;
 43  
 import org.argouml.uml.diagram.ui.PathItemPlacement;
 44  
 import org.argouml.util.IItemUID;
 45  
 import org.argouml.util.ItemUID;
 46  
 import org.tigris.gef.base.PathItemPlacementStrategy;
 47  
 import org.tigris.gef.persistence.pgml.Container;
 48  
 import org.tigris.gef.persistence.pgml.FigEdgeHandler;
 49  
 import org.tigris.gef.persistence.pgml.FigGroupHandler;
 50  
 import org.tigris.gef.persistence.pgml.PGMLHandler;
 51  
 import org.tigris.gef.presentation.Fig;
 52  
 import org.tigris.gef.presentation.FigEdge;
 53  
 import org.xml.sax.Attributes;
 54  
 import org.xml.sax.SAXException;
 55  
 
 56  
 /**
 57  
  * Will set the ItemUID for objects represented by
 58  
  * PGML elements that contain private elements that have
 59  
  * ItemUID assignments in them.<p>
 60  
  *
 61  
  * Currently, there are three possibilities: ArgoDiagram,
 62  
  * FigNode, FigEdge
 63  
  */
 64  
 class PrivateHandler
 65  
     extends org.tigris.gef.persistence.pgml.PrivateHandler {
 66  
 
 67  
     private Container container;
 68  
 
 69  
     /**
 70  
      * Logger.
 71  
      */
 72  0
     private static final Logger LOG = Logger.getLogger(PrivateHandler.class);
 73  
 
 74  
     /**
 75  
      * The constructor.
 76  
      *
 77  
      * @param parser
 78  
      * @param cont
 79  
      */
 80  
     public PrivateHandler(PGMLStackParser parser, Container cont) {
 81  0
         super(parser, cont);
 82  0
         container = cont;
 83  0
     }
 84  
 
 85  
     /**
 86  
      * If the containing object is a type for which the private element
 87  
      * might contain an ItemUID, extract the ItemUID if it exists and assign it
 88  
      * to the object.
 89  
      * 
 90  
      * @param contents
 91  
      * @exception SAXException
 92  
      */
 93  
     public void gotElement(String contents)
 94  
         throws SAXException {
 95  
 
 96  0
         if (container instanceof PGMLHandler) {
 97  0
             Object o = getPGMLStackParser().getDiagram();
 98  0
             if (o instanceof IItemUID) {
 99  0
                 ItemUID uid = getItemUID(contents);
 100  0
                 if (uid != null) {
 101  0
                     ((IItemUID) o).setItemUID(uid);
 102  
                 }
 103  
             }
 104  
             // No other uses of string in PGMLHandler
 105  0
             return;
 106  
         }
 107  
 
 108  0
         if (container instanceof FigGroupHandler) {
 109  0
             Object o = ((FigGroupHandler) container).getFigGroup();
 110  0
             if (o instanceof IItemUID) {
 111  0
                 ItemUID uid = getItemUID(contents);
 112  0
                 if (uid != null) {
 113  0
                     ((IItemUID) o).setItemUID(uid);
 114  
                 }
 115  
             }
 116  
         }
 117  
 
 118  0
         if (container instanceof FigEdgeHandler) {
 119  0
             Object o = ((FigEdgeHandler) container).getFigEdge();
 120  0
             if (o instanceof IItemUID) {
 121  0
                 ItemUID uid = getItemUID(contents);
 122  0
                 if (uid != null) {
 123  0
                     ((IItemUID) o).setItemUID(uid);
 124  
                 }
 125  
             }
 126  
         }
 127  
 
 128  
         // Handle other uses of <private> contents
 129  0
         super.gotElement(contents);
 130  0
     }
 131  
     
 132  
     /**
 133  
      * Process starting elements within the private tag.
 134  
      * This method handles all attributes within tags within private methods.
 135  
      * The only specific tags we handle here at the moment are pathitems.
 136  
      * 
 137  
      * The strategy for handling pathitems is as follows:
 138  
      * <ul>
 139  
      *  <li>Data is saved for each path item using one <argouml:pathitem ... /> 
 140  
      *      tag per path item.
 141  
      *  <li>The code that defines what is stored is in 
 142  
      *      org.argouml.persistence.PGML.tee
 143  
      *  <li>Each <argouml:pathitem> tag stores 
 144  
      *  <ul>
 145  
      *    <li>The class name of the PathItemPlacementStrategy
 146  
      *    <li>The class name of the fig which it places.
 147  
      *    <li>The href of the model element which owns the fig being placed.
 148  
      *    <li>The angle of the placement vector (PathItemPlacement.angle)
 149  
      *    <li>The distance along the displacement vector to place the fig
 150  
      *        (PathItemPlacement.vectorOffset).
 151  
      *  </ul>
 152  
      *  </li>
 153  
      *  <li>No specific data is stored to match pathitem tags to the
 154  
      *      diagram figs which they control.
 155  
      *  <li>The matching during file load depends entirely on
 156  
      *      there being a unique figclassname and ownerhref combination
 157  
      *      for each pathitem on the diagram.  For example, For a 
 158  
      *      FigAssociation, the main label is a FigTextGroup, and it's
 159  
      *      owner is assigned to the Association.  This combination is
 160  
      *      unique, and is used to match the parsed pathitem data back 
 161  
      *      to the instantiated PathItemPlacement.
 162  
      *      Another example is the source multiplicity, which is a
 163  
      *      FigMultiplicity, and it's owner is assigned to the
 164  
      *      source model element.
 165  
      *      In each case, the combination is unique, so there is only
 166  
      *      one pathitem that matches when rebuilding the diagram.
 167  
      *  </ul>
 168  
      * 
 169  
      * @param uri
 170  
      * @param localname
 171  
      * @param qname
 172  
      * @param attributes
 173  
      * @throws SAXException
 174  
      * @see org.tigris.gef.persistence.pgml.BaseHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
 175  
      */
 176  
     public void startElement(String uri, String localname, String qname,
 177  
             Attributes attributes) throws SAXException {
 178  0
         if ("argouml:pathitem".equals(qname)
 179  
                 && container instanceof FigEdgeHandler) {
 180  0
             String classname = attributes.getValue("classname");
 181  0
             String figclassname = 
 182  
                 attributes.getValue("figclassname");
 183  0
             String ownerhref = attributes.getValue("ownerhref");
 184  0
             String angle = attributes.getValue("angle");
 185  0
             String offset = attributes.getValue("offset");
 186  0
             if ( classname != null
 187  
                     && figclassname != null
 188  
                     && ownerhref != null
 189  
                     && angle != null
 190  
                     && offset != null ) {
 191  
                 // Method 2: (assign data immediately, see end of file).
 192  
                 // TODO: if we ever want to extend PathItemPlacement,
 193  
                 // we should modify this, so that we also recognise any 
 194  
                 // subclass of PathItemPlacement.
 195  
                 // Is the class name a PathItemPlacment?
 196  
                 // TODO: Use class reference to make this dependency obvious
 197  0
                 if ("org.argouml.uml.diagram.ui.PathItemPlacement".equals(
 198  
                         classname)) {
 199  0
                     PathItemPlacementStrategy pips 
 200  
                         = getPips(figclassname, ownerhref);
 201  
                     // Sanity check - the returned path item placement 
 202  
                     // strategy should match the one in the UML.
 203  
                     // If it doesn't, it could be that the UML was 
 204  
                     // created with an older argo version, and the new
 205  
                     // argo version use a different placement strategy.
 206  
                     // If they don't match, just use the default.
 207  0
                     if (pips != null 
 208  
                             && classname.equals(pips.getClass().getName())) {
 209  
                         // Now we're into processing each specific path 
 210  
                         // item strategy.
 211  
                         // At the moment, we only know PathItemPlacement
 212  0
                         if (pips instanceof PathItemPlacement) {
 213  0
                             PathItemPlacement pip = 
 214  
                                 (PathItemPlacement) pips;
 215  0
                             pip.setDisplacementVector(
 216  
                                     Double.parseDouble(angle),
 217  
                                     Integer.parseInt(offset));
 218  0
                         }
 219  
                         // Continue (future PathItemPlacementStrategy impl) 
 220  
                         //else if (...) {
 221  
                         //}
 222  
                     }
 223  
                     // If the PathItemPlacement was unknown, leave the
 224  
                     // diagram with the default settings.
 225  
                     else {
 226  0
                         LOG.warn("PGML stored pathitem class name does "
 227  
                                 + "not match the class name on the "
 228  
                                 + "diagram. Label position will revert "
 229  
                                 + "to defaults.");
 230  
                     }
 231  0
                 }
 232  
             }
 233  
             // If any of the values are null, ignore the element.
 234  
             else {
 235  0
                 LOG.warn("Could not find all attributes for <" 
 236  
                         + qname + "> tag, ignoring.");
 237  
                 //System.out.println("Error - one of these is null:" 
 238  
                 //        + "classname=" + classname
 239  
                 //        + " figclassname=" + figclassname
 240  
                 //        + " ownerhref=" + ownerhref
 241  
                 //        + " angle=" + angle
 242  
                 //        + " offset=" + offset);
 243  
             }
 244  
         }
 245  0
         super.startElement(uri, localname, qname, attributes);
 246  0
     }
 247  
 
 248  
     /**
 249  
      * Finds the path item placement strategy for a sub Fig, by its class name,
 250  
      * and it's owner href.
 251  
      * @param figclassname The class name of the fig being placed.
 252  
      * @param ownerhref The href of the owner of the fig being placed.
 253  
      * @return The path item placement strategy.
 254  
      */
 255  
     private PathItemPlacementStrategy getPips(String figclassname, 
 256  
             String ownerhref) {
 257  0
         if (container instanceof FigEdgeHandler) {
 258  0
             FigEdge fe = ((FigEdgeHandler) container).getFigEdge();
 259  0
             Object owner = getPGMLStackParser().findOwner(ownerhref);
 260  
 
 261  0
             for (Object o : fe.getPathItemFigs()) {
 262  0
                 Fig f = (Fig) o;
 263  
                 // For a match to be found, it has to have the same 
 264  
                 // owner, and the same long class name.
 265  0
                 if (owner.equals(f.getOwner())
 266  
                         && figclassname.equals(f.getClass().getName())) {
 267  
                     //System.out.println("MATCHED! " + figclassname);
 268  0
                     return fe.getPathItemPlacementStrategy(f);
 269  
                 }
 270  0
             }
 271  
         }
 272  0
         LOG.warn("Could not load path item for fig '" + figclassname 
 273  
                 + "', using default placement.");
 274  0
         return null;
 275  
     }
 276  
     
 277  
     /**
 278  
      * Determine if the string contains an ItemUID.
 279  
      *
 280  
      * @return a newly created ItemUID (or <code>null</code>).
 281  
      */
 282  
     private ItemUID getItemUID(String privateContents) {
 283  0
         StringTokenizer st = new StringTokenizer(privateContents, "\n");
 284  
 
 285  0
         while (st.hasMoreElements()) {
 286  0
             String str = st.nextToken();
 287  0
             NameVal nval = splitNameVal(str);
 288  
 
 289  0
             if (nval != null) {
 290  0
                 if (LOG.isDebugEnabled()) {
 291  0
                     LOG.debug("Private Element: \"" + nval.getName()
 292  
                               + "\" \"" + nval.getValue() + "\"");
 293  
                 }
 294  0
                 if ("ItemUID".equals(nval.getName())
 295  
                     && nval.getValue().length() > 0) {
 296  0
                     return new ItemUID(nval.getValue());
 297  
                 }
 298  
             }
 299  0
         }
 300  0
         return null;
 301  
     }
 302  
 
 303  
     /**
 304  
      * Utility class to pair a name and a value String together.
 305  
      */
 306  
     static class NameVal {
 307  
         private String name;
 308  
         private String value;
 309  
 
 310  
         /**
 311  
          * The constructor.
 312  
          *
 313  
          * @param n the name
 314  
          * @param v the value
 315  
          */
 316  0
         NameVal(String n, String v) {
 317  0
             name = n.trim();
 318  0
             value = v.trim();
 319  0
         }
 320  
 
 321  
         /**
 322  
          * @return returns the name
 323  
          */
 324  
         String getName() {
 325  0
             return name;
 326  
         }
 327  
 
 328  
         /**
 329  
          * @return returns the value
 330  
          */
 331  
         String getValue() {
 332  0
             return value;
 333  
         }
 334  
     }
 335  
 
 336  
     /**
 337  
      * Splits a name value pair into a NameVal instance. A name value pair is
 338  
      * a String on the form &lt; name = ["] value ["] &gt;.
 339  
      *
 340  
      * @param str A String with a name value pair.
 341  
      * @return A NameVal, or null if they could not be split.
 342  
      */
 343  
     protected NameVal splitNameVal(String str) {
 344  0
         NameVal rv = null;
 345  
         int lqpos, rqpos;
 346  0
         int eqpos = str.indexOf('=');
 347  
 
 348  0
         if (eqpos < 0) {
 349  0
             return null;
 350  
         }
 351  
 
 352  0
         lqpos = str.indexOf('"', eqpos);
 353  0
         rqpos = str.lastIndexOf('"');
 354  
 
 355  0
         if (lqpos < 0 || rqpos <= lqpos) {
 356  0
             return null;
 357  
         }
 358  
 
 359  0
         rv =
 360  
             new NameVal(str.substring(0, eqpos),
 361  
                 str.substring(lqpos + 1, rqpos));
 362  
 
 363  0
         return rv;
 364  
     }
 365  
 }
 366  
 
 367  
 // An alternative implementation of the parsing of pathitems is to collect 
 368  
 // everything at the start, then iterate through it all at the end.
 369  
 // The code below does this - it works, but it is currently not used,
 370  
 // since it is a unnecessarily complicated.
 371  
 // There are probably better ways to implement this than using an
 372  
 // ArrayList of Hashtables.
 373  
 // see option 1 in 
 374  
 // http://argouml.tigris.org/issues/show_bug.cgi?id=1048#desc66
 375  
 //
 376  
 
 377  
 ///**
 378  
 // * A list of the path item attributes for this container.
 379  
 // * The list is populated during parsing, them processed at endElement() 
 380  
 // */
 381  
 //private List<Hashtable<String, String>> pathItemAttrs = 
 382  
 //    new ArrayList<Hashtable<String, String>>();
 383  
 
 384  
 // This code has to go within the startElement block after the strings
 385  
 // have been matched.
 386  
 
 387  
 //// Method 1:
 388  
 //// (collect data and assign later in endElement() method).
 389  
 //Hashtable<String, String> ht = 
 390  
 //    new Hashtable<String, String>(); 
 391  
 //ht.put("classname", classname);
 392  
 //ht.put("figclassname", figclassname);
 393  
 //ht.put("ownerhref", ownerhref);
 394  
 //ht.put("angle", angle);
 395  
 //ht.put("offset", offset);
 396  
 //pathItemAttrs.add(ht);
 397  
 
 398  
 //public void endElement(String uri, String localname, String qname)
 399  
 //throws SAXException {
 400  
 ////System.out.print("Got endElement: " 
 401  
 ////        + "uri='" + uri + "'\n" 
 402  
 ////        + "localname='" + localname + "'\n" 
 403  
 ////        + "qname='" + qname + "'\n" 
 404  
 ////);
 405  
 //// If we collected any path items for a FigEdgeModelElement,
 406  
 //// process them now, and assign their values to real Figs on the diag.
 407  
 //if (!(pathItemAttrs.isEmpty())) {
 408  
 //    for (Hashtable<String, String> attrs : pathItemAttrs) {
 409  
 //        // Is the class name a PathItemPlacment?
 410  
 //        // TODO: if we ever want to extend PathItemPlacement,
 411  
 //        // we should modify this, so that we also recognise any 
 412  
 //        // subclass of PathItemPlacement.
 413  
 //        if ("org.argouml.uml.diagram.ui.PathItemPlacement".
 414  
 //                equals(attrs.get("classname"))) {
 415  
 //            //System.out.println("figclassname=" + attrs.get("figclassname"));
 416  
 //            
 417  
 //            PathItemPlacementStrategy pips 
 418  
 //                = getPips(attrs.get("figclassname"), 
 419  
 //                        attrs.get("ownerhref"));
 420  
 //            // Sanity check - the returned path item placement straty
 421  
 //            // should match the one in the uml.
 422  
 //            if (pips.getClass().getName().equals(attrs.get("classname"))) {
 423  
 //                // Now we're into processing each specific path item 
 424  
 //                // strategy.
 425  
 //                // At the moment, we only know about PathItemPlacement
 426  
 //                if (pips instanceof PathItemPlacement) {
 427  
 //                    PathItemPlacement pip = (PathItemPlacement) pips;
 428  
 //                    pip.setDisplacementVector(
 429  
 //                            Double.parseDouble(attrs.get("angle")),
 430  
 //                            Integer.parseInt(attrs.get("offset")));
 431  
 //                }
 432  
 //                // Continue (future PathItemPlacementStrategy impl) 
 433  
 //                //else if (...) {
 434  
 //                //    
 435  
 //                //}
 436  
 //                    
 437  
 //            }
 438  
 //            else {
 439  
 //                LOG.warn("PGML stored pathitem class name does not "
 440  
 //                        + "match the class name on the diagram."
 441  
 //                        + "Label position will revert to defaults.");
 442  
 //            }
 443  
 //        }
 444  
 //    }
 445  
 //}
 446  
 //    
 447  
 //super.endElement(uri, localname, qname);
 448  
 //}