Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ItemUID |
|
| 7.714285714285714;7.714 |
1 | /* $Id: ItemUID.java 17887 2010-01-12 21:17:18Z 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 | * bobtarling | |
11 | ***************************************************************************** | |
12 | * | |
13 | * Some portions of this file was previously release using the BSD License: | |
14 | */ | |
15 | ||
16 | // Copyright (c) 2002-2008 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 | ||
39 | package org.argouml.util; | |
40 | ||
41 | import java.lang.reflect.InvocationTargetException; | |
42 | import java.lang.reflect.Method; | |
43 | ||
44 | import org.apache.log4j.Logger; | |
45 | import org.argouml.model.Model; | |
46 | ||
47 | /** | |
48 | * An instance of this class is supposed to be attached to an instance | |
49 | * of another class to uniquely identify it. It is intended that such | |
50 | * a tagging should be persistent over saving and loading, if applicable.<p> | |
51 | * | |
52 | * The class also harbors the | |
53 | * {@link #getIDOfObject getIDOfObject(Object, boolean)} which provides | |
54 | * a way to get the ItemUID of any object with a method | |
55 | * <code>ItemUID getItemUID()</code> | |
56 | * and creating new ItemUIDs for any object with a method | |
57 | * <code>setItemUID(ItemUID)</code> | |
58 | * using reflection in java.<p> | |
59 | * | |
60 | * A class intended to be tagged must at least provide a | |
61 | * <code>ItemUID getItemUID()</code> | |
62 | * method. It may also provide a | |
63 | * <code>void setItemUID(ItemUID id)</code> | |
64 | * such that getItemUID() will return id if a call returns successfully, | |
65 | * and which is stored persistently should the tagged object be stored. | |
66 | * This allows this class to automatically tag an object when necessary, | |
67 | * but it is allowed to tag classes by other means and only provide the | |
68 | * getItemUID() call.<p> | |
69 | * | |
70 | * A critical requirement for this class is that the cognitive component | |
71 | * is supposed to work with general objects. This class is a wrapper around | |
72 | * places where the component needs persistent identities of objects, since | |
73 | * I have said that some features cannot be implemented without that, such as | |
74 | * ResolvedCritic, and so far noone has shown me wrong (though I wouldn't | |
75 | * mind). It is for this reason that some perhaps ugly looking exceptions in | |
76 | * this code must be considered perfectly normal conditions. Failure of some | |
77 | * object to work with tagging must be handled by the cognitive component | |
78 | * programmer and it is (see eg ResolvedCritic).<p> | |
79 | * | |
80 | * A possible future change would be to allow tag handlers to be registered | |
81 | * with this class to handle other preexisting tagging mechanisms, which | |
82 | * could be used to remove the dependancy to the model component here, which | |
83 | * I find a bit unaesthetic. So far, not enough to write it (though it is not | |
84 | * much work). | |
85 | * | |
86 | * @author Michael Stockman | |
87 | */ | |
88 | public class ItemUID { | |
89 | /** | |
90 | * The logger. | |
91 | */ | |
92 | 900 | private static final Logger LOG = Logger.getLogger(ItemUID.class); |
93 | ||
94 | /** | |
95 | * Keeps a reference to the Class object of this class. | |
96 | */ | |
97 | 900 | private static final Class MYCLASS = (new ItemUID()).getClass(); |
98 | ||
99 | /** | |
100 | * This actual ID of this instance. | |
101 | */ | |
102 | private String id; | |
103 | ||
104 | /** | |
105 | * Constructs a new ItemUID and creates a new ID for it. | |
106 | */ | |
107 | 900 | public ItemUID() { |
108 | 900 | id = generateID(); |
109 | 900 | } |
110 | ||
111 | /** | |
112 | * Constructs a new ItemUID and uses the String param as the ID. | |
113 | * Mainly intended to be used during loading of saved objects. | |
114 | * | |
115 | * @param param The ID to used for the new instance. | |
116 | * @see #toString() | |
117 | */ | |
118 | 0 | public ItemUID(String param) { |
119 | 0 | id = param; |
120 | 0 | } |
121 | ||
122 | /** | |
123 | * Returns the ID of this ItemUID as a String. If everything works all | |
124 | * such Strings will be unique. It is possible to created a new | |
125 | * identical ItemUID using this String. | |
126 | * | |
127 | * @return The ID as a String. | |
128 | * @see #ItemUID(String) | |
129 | */ | |
130 | public String toString() { | |
131 | 0 | return id; |
132 | } | |
133 | ||
134 | /** | |
135 | * Generates a new unique ID and returns it as a String. The contents | |
136 | * of the String is supposed to be unique with respect to all Strings | |
137 | * generated by other instances of this class. | |
138 | * TODO: We should consider using java.util.UUID for this instead. | |
139 | * | |
140 | * @return A String with unique content. | |
141 | */ | |
142 | public static String generateID() { | |
143 | 900 | return (new java.rmi.server.UID()).toString(); |
144 | } | |
145 | ||
146 | /** | |
147 | * Obtains the ID of an object and returns it as a String. If | |
148 | * canCreate is true it will try to create a new ID for the object | |
149 | * if it has none. | |
150 | * | |
151 | * @param obj the Object to get the ID of. | |
152 | * @param canCreate If an ID can be created, should object not have one. | |
153 | * @return The ID of the object, or null. | |
154 | */ | |
155 | public static String getIDOfObject(Object obj, boolean canCreate) { | |
156 | 1967 | String s = readObjectID(obj); |
157 | ||
158 | 1967 | if (s == null && canCreate) { |
159 | 0 | s = createObjectID(obj); |
160 | } | |
161 | ||
162 | 1967 | return s; |
163 | } | |
164 | ||
165 | /** | |
166 | * Tries to read the ID of the object. It uses the reflective | |
167 | * properties of java to access a method named getItemUID of the | |
168 | * object which is expected to return an ItemUID. | |
169 | * | |
170 | * @param obj The object whose ID to read. | |
171 | * @return The ID of the object, or null. | |
172 | */ | |
173 | protected static String readObjectID(Object obj) { | |
174 | 1967 | if (Model.getFacade().isAUMLElement(obj)) { |
175 | 1967 | return Model.getFacade().getUUID(obj); |
176 | } | |
177 | ||
178 | 0 | if (obj instanceof IItemUID) { |
179 | 0 | final ItemUID itemUid = ((IItemUID) obj).getItemUID(); |
180 | 0 | return (itemUid == null ? null : itemUid.toString()); |
181 | } | |
182 | Object rv; | |
183 | try { | |
184 | // TODO: We shouldn't need this reflection any more once we have | |
185 | // convinced ourselves that everything with a getItemUID method | |
186 | // is implementing IItemUID | |
187 | 0 | Method m = obj.getClass().getMethod("getItemUID", (Class[]) null); |
188 | 0 | rv = m.invoke(obj, (Object[]) null); |
189 | 0 | } catch (NoSuchMethodException nsme) { |
190 | // Apparently this object had no getItemUID | |
191 | try { | |
192 | // This is needed for a CommentEdge ... | |
193 | // TODO: Why doesn't CommentEdge implement IItemUID and be | |
194 | // handled with the mechanism above. | |
195 | 0 | Method m = obj.getClass().getMethod("getUUID", (Class[]) null); |
196 | 0 | rv = m.invoke(obj, (Object[]) null); |
197 | 0 | return (String) rv; |
198 | 0 | } catch (NoSuchMethodException nsme2) { |
199 | // Apparently this object had no getUUID | |
200 | 0 | return null; |
201 | 0 | } catch (IllegalArgumentException iare) { |
202 | 0 | LOG.error("getUUID for " + obj.getClass() |
203 | + " takes strange parameter: ", | |
204 | iare); | |
205 | 0 | return null; |
206 | 0 | } catch (IllegalAccessException iace) { |
207 | // Apparently it had a getItemUID, | |
208 | // but we're not allowed to call it | |
209 | 0 | return null; |
210 | 0 | } catch (InvocationTargetException tie) { |
211 | 0 | LOG.error("getUUID for " + obj.getClass() + " threw: ", |
212 | tie); | |
213 | 0 | return null; |
214 | } | |
215 | 0 | } catch (SecurityException se) { |
216 | // Apparently it had a getItemUID, | |
217 | // but we're not allowed to call it | |
218 | 0 | return null; |
219 | 0 | } catch (InvocationTargetException tie) { |
220 | 0 | LOG.error("getItemUID for " + obj.getClass() + " threw: ", |
221 | tie); | |
222 | 0 | return null; |
223 | 0 | } catch (IllegalAccessException iace) { |
224 | // Apparently it had a getItemUID, | |
225 | // but we're not allowed to call it | |
226 | 0 | return null; |
227 | 0 | } catch (IllegalArgumentException iare) { |
228 | 0 | LOG.error("getItemUID for " + obj.getClass() |
229 | + " takes strange parameter: ", | |
230 | iare); | |
231 | 0 | return null; |
232 | 0 | } catch (ExceptionInInitializerError eiie) { |
233 | 0 | LOG.error("getItemUID for " + obj.getClass() |
234 | + " exception: ", | |
235 | eiie); | |
236 | 0 | return null; |
237 | 0 | } |
238 | ||
239 | 0 | if (rv == null) { |
240 | 0 | return null; |
241 | } | |
242 | ||
243 | 0 | if (!(rv instanceof ItemUID)) { |
244 | 0 | LOG.error("getItemUID for " + obj.getClass() |
245 | + " returns strange value: " + rv.getClass()); | |
246 | 0 | return null; |
247 | } | |
248 | ||
249 | 0 | return rv.toString(); |
250 | } | |
251 | ||
252 | /** | |
253 | * Tries to create a new ID for the object. It uses the reflective | |
254 | * properties of java to access a method named setItemUID(ItemUID). | |
255 | * If that method exist and doesn't throw when called, then the call | |
256 | * is assumed to have been successful and the object is responsible | |
257 | * for remembering the ID. | |
258 | * | |
259 | * @param obj The object to assign a new ID. | |
260 | * @return The new ID of the object, or null. | |
261 | */ | |
262 | protected static String createObjectID(Object obj) { | |
263 | 0 | if (Model.getFacade().isAUMLElement(obj)) { |
264 | 0 | return null; |
265 | } | |
266 | ||
267 | 0 | if (obj instanceof IItemUID) { |
268 | 0 | ItemUID uid = new ItemUID(); |
269 | 0 | ((IItemUID) obj).setItemUID(uid); |
270 | 0 | return uid.toString(); |
271 | } | |
272 | ||
273 | 0 | Class[] params = new Class[1]; |
274 | Object[] mparam; | |
275 | 0 | params[0] = MYCLASS; |
276 | try { | |
277 | // TODO: We shouldn't need this reflection any more once we have | |
278 | // convinced ourselves that everything with a setItemUID method | |
279 | // is implementing IItemUID | |
280 | 0 | Method m = obj.getClass().getMethod("setItemUID", params); |
281 | 0 | mparam = new Object[1]; |
282 | 0 | mparam[0] = new ItemUID(); |
283 | 0 | m.invoke(obj, mparam); |
284 | 0 | } catch (NoSuchMethodException nsme) { |
285 | // Apparently this object had no setItemUID | |
286 | 0 | return null; |
287 | 0 | } catch (SecurityException se) { |
288 | // Apparently it had a setItemUID, | |
289 | // but we're not allowed to call it | |
290 | 0 | return null; |
291 | 0 | } catch (InvocationTargetException tie) { |
292 | 0 | LOG.error("setItemUID for " + obj.getClass() + " threw", |
293 | tie); | |
294 | 0 | return null; |
295 | 0 | } catch (IllegalAccessException iace) { |
296 | // Apparently it had a setItemUID, | |
297 | // but we're not allowed to call it | |
298 | 0 | return null; |
299 | 0 | } catch (IllegalArgumentException iare) { |
300 | 0 | LOG.error("setItemUID for " + obj.getClass() |
301 | + " takes strange parameter", | |
302 | iare); | |
303 | 0 | return null; |
304 | 0 | } catch (ExceptionInInitializerError eiie) { |
305 | 0 | LOG.error("setItemUID for " + obj.getClass() + " threw", |
306 | eiie); | |
307 | 0 | return null; |
308 | 0 | } |
309 | ||
310 | 0 | return mparam[0].toString(); |
311 | } | |
312 | } | |
313 |