Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
PrivateHandler |
|
| 5.111111111111111;5.111 | ||||
PrivateHandler$NameVal |
|
| 5.111111111111111;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 < name = ["] value ["] >. | |
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 | //} |