%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.commons.configuration.ConfigurationKey$KeyIterator |
|
|
1 | /* |
|
2 | * Copyright 2004-2005 The Apache Software Foundation. |
|
3 | * |
|
4 | * Licensed under the Apache License, Version 2.0 (the "License") |
|
5 | * you may not use this file except in compliance with the License. |
|
6 | * You may obtain a copy of the License at |
|
7 | * |
|
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
|
9 | * |
|
10 | * Unless required by applicable law or agreed to in writing, software |
|
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
|
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 | * See the License for the specific language governing permissions and |
|
14 | * limitations under the License. |
|
15 | */ |
|
16 | ||
17 | package org.apache.commons.configuration; |
|
18 | ||
19 | import java.io.Serializable; |
|
20 | import java.util.Iterator; |
|
21 | import java.util.NoSuchElementException; |
|
22 | ||
23 | /** |
|
24 | * <p>A simple class that supports creation of and iteration on complex |
|
25 | * configuration keys.</p> |
|
26 | * |
|
27 | * <p>For key creation the class works similar to a StringBuffer: There are |
|
28 | * several <code>appendXXXX()</code> methods with which single parts |
|
29 | * of a key can be constructed. All these methods return a reference to the |
|
30 | * actual object so they can be written in a chain. When using this methods |
|
31 | * the exact syntax for keys need not be known.</p> |
|
32 | * |
|
33 | * <p>This class also defines a specialized iterator for configuration keys. |
|
34 | * With such an iterator a key can be tokenized into its single parts. For |
|
35 | * each part it can be checked whether it has an associated index.</p> |
|
36 | * |
|
37 | * @author <a href="mailto:oliver.heger@t-online.de">Oliver Heger</a> |
|
38 | * @version $Id: ConfigurationKey.java 295090 2005-10-05 19:36:15Z oheger $ |
|
39 | */ |
|
40 | public class ConfigurationKey implements Serializable |
|
41 | { |
|
42 | /** Constant for a property delimiter.*/ |
|
43 | public static final char PROPERTY_DELIMITER = '.'; |
|
44 | ||
45 | /** Constant for an escaped delimiter. */ |
|
46 | public static final String ESCAPED_DELIMITER = |
|
47 | String.valueOf(PROPERTY_DELIMITER) + String.valueOf(PROPERTY_DELIMITER); |
|
48 | ||
49 | /** Constant for an attribute start marker.*/ |
|
50 | private static final String ATTRIBUTE_START = "[@"; |
|
51 | ||
52 | /** Constant for an attribute end marker.*/ |
|
53 | private static final String ATTRIBUTE_END = "]"; |
|
54 | ||
55 | /** Constant for an index start marker.*/ |
|
56 | private static final char INDEX_START = '('; |
|
57 | ||
58 | /** Constant for an index end marker.*/ |
|
59 | private static final char INDEX_END = ')'; |
|
60 | ||
61 | /** Constant for the initial StringBuffer size.*/ |
|
62 | private static final int INITIAL_SIZE = 32; |
|
63 | ||
64 | /** Holds a buffer with the so far created key.*/ |
|
65 | private StringBuffer keyBuffer; |
|
66 | ||
67 | /** |
|
68 | * Creates a new, empty instance of <code>ConfigurationKey</code>. |
|
69 | */ |
|
70 | public ConfigurationKey() |
|
71 | { |
|
72 | keyBuffer = new StringBuffer(INITIAL_SIZE); |
|
73 | } |
|
74 | ||
75 | /** |
|
76 | * Creates a new instance of <code>ConfigurationKey</code> and |
|
77 | * initializes it with the given key. |
|
78 | * |
|
79 | * @param key the key as a string |
|
80 | */ |
|
81 | public ConfigurationKey(String key) |
|
82 | { |
|
83 | keyBuffer = new StringBuffer(key); |
|
84 | removeTrailingDelimiter(); |
|
85 | } |
|
86 | ||
87 | /** |
|
88 | * Appends the name of a property to this key. If necessary, a |
|
89 | * property delimiter will be added. |
|
90 | * |
|
91 | * @param property the name of the property to be added |
|
92 | * @return a reference to this object |
|
93 | */ |
|
94 | public ConfigurationKey append(String property) |
|
95 | { |
|
96 | if (keyBuffer.length() > 0 && !hasDelimiter() && !isAttributeKey(property)) |
|
97 | { |
|
98 | keyBuffer.append(PROPERTY_DELIMITER); |
|
99 | } |
|
100 | ||
101 | keyBuffer.append(property); |
|
102 | removeTrailingDelimiter(); |
|
103 | return this; |
|
104 | } |
|
105 | ||
106 | /** |
|
107 | * Appends an index to this configuration key. |
|
108 | * |
|
109 | * @param index the index to be appended |
|
110 | * @return a reference to this object |
|
111 | */ |
|
112 | public ConfigurationKey appendIndex(int index) |
|
113 | { |
|
114 | keyBuffer.append(INDEX_START).append(index); |
|
115 | keyBuffer.append(INDEX_END); |
|
116 | return this; |
|
117 | } |
|
118 | ||
119 | /** |
|
120 | * Appends an attribute to this configuration key. |
|
121 | * |
|
122 | * @param attr the name of the attribute to be appended |
|
123 | * @return a reference to this object |
|
124 | */ |
|
125 | public ConfigurationKey appendAttribute(String attr) |
|
126 | { |
|
127 | keyBuffer.append(constructAttributeKey(attr)); |
|
128 | return this; |
|
129 | } |
|
130 | ||
131 | /** |
|
132 | * Checks if this key is an attribute key. |
|
133 | * |
|
134 | * @return a flag if this key is an attribute key |
|
135 | */ |
|
136 | public boolean isAttributeKey() |
|
137 | { |
|
138 | return isAttributeKey(keyBuffer.toString()); |
|
139 | } |
|
140 | ||
141 | /** |
|
142 | * Checks if the passed in key is an attribute key. Such attribute keys |
|
143 | * start and end with certain marker strings. In some cases they must be |
|
144 | * treated slightly different. |
|
145 | * |
|
146 | * @param key the key (part) to be checked |
|
147 | * @return a flag if this key is an attribute key |
|
148 | */ |
|
149 | public static boolean isAttributeKey(String key) |
|
150 | { |
|
151 | return key != null |
|
152 | && key.startsWith(ATTRIBUTE_START) |
|
153 | && key.endsWith(ATTRIBUTE_END); |
|
154 | } |
|
155 | ||
156 | /** |
|
157 | * Decorates the given key so that it represents an attribute. Adds |
|
158 | * special start and end markers. |
|
159 | * |
|
160 | * @param key the key to be decorated |
|
161 | * @return the decorated attribute key |
|
162 | */ |
|
163 | public static String constructAttributeKey(String key) |
|
164 | { |
|
165 | StringBuffer buf = new StringBuffer(); |
|
166 | buf.append(ATTRIBUTE_START).append(key).append(ATTRIBUTE_END); |
|
167 | return buf.toString(); |
|
168 | } |
|
169 | ||
170 | /** |
|
171 | * Extracts the name of the attribute from the given attribute key. |
|
172 | * This method removes the attribute markers - if any - from the |
|
173 | * specified key. |
|
174 | * |
|
175 | * @param key the attribute key |
|
176 | * @return the name of the corresponding attribute |
|
177 | */ |
|
178 | public static String attributeName(String key) |
|
179 | { |
|
180 | return isAttributeKey(key) ? removeAttributeMarkers(key) : key; |
|
181 | } |
|
182 | ||
183 | /** |
|
184 | * Helper method for removing attribute markers from a key. |
|
185 | * |
|
186 | * @param key the key |
|
187 | * @return the key with removed attribute markers |
|
188 | */ |
|
189 | static String removeAttributeMarkers(String key) |
|
190 | { |
|
191 | return key.substring(ATTRIBUTE_START.length(), key.length() - ATTRIBUTE_END.length()); |
|
192 | } |
|
193 | ||
194 | /** |
|
195 | * Helper method that checks if the actual buffer ends with a property |
|
196 | * delimiter. |
|
197 | * |
|
198 | * @return a flag if there is a trailing delimiter |
|
199 | */ |
|
200 | private boolean hasDelimiter() |
|
201 | { |
|
202 | int count = 0; |
|
203 | for (int idx = keyBuffer.length() - 1; idx >= 0 |
|
204 | && keyBuffer.charAt(idx) == PROPERTY_DELIMITER; idx--) |
|
205 | { |
|
206 | count++; |
|
207 | } |
|
208 | return count % 2 == 1; |
|
209 | } |
|
210 | ||
211 | /** |
|
212 | * Removes a trailing delimiter if there is any. |
|
213 | */ |
|
214 | private void removeTrailingDelimiter() |
|
215 | { |
|
216 | while (hasDelimiter()) |
|
217 | { |
|
218 | keyBuffer.deleteCharAt(keyBuffer.length() - 1); |
|
219 | } |
|
220 | } |
|
221 | ||
222 | /** |
|
223 | * Returns a string representation of this object. This is the |
|
224 | * configuration key as a plain string. |
|
225 | * |
|
226 | * @return a string for this object |
|
227 | */ |
|
228 | public String toString() |
|
229 | { |
|
230 | return keyBuffer.toString(); |
|
231 | } |
|
232 | ||
233 | /** |
|
234 | * Returns an iterator for iterating over the single components of |
|
235 | * this configuration key. |
|
236 | * |
|
237 | * @return an iterator for this key |
|
238 | */ |
|
239 | public KeyIterator iterator() |
|
240 | { |
|
241 | return new KeyIterator(); |
|
242 | } |
|
243 | ||
244 | /** |
|
245 | * Returns the actual length of this configuration key. |
|
246 | * |
|
247 | * @return the length of this key |
|
248 | */ |
|
249 | public int length() |
|
250 | { |
|
251 | return keyBuffer.length(); |
|
252 | } |
|
253 | ||
254 | /** |
|
255 | * Sets the new length of this configuration key. With this method it is |
|
256 | * possible to truncate the key, e.g. to return to a state prior calling |
|
257 | * some <code>append()</code> methods. The semantic is the same as |
|
258 | * the <code>setLength()</code> method of <code>StringBuffer</code>. |
|
259 | * |
|
260 | * @param len the new length of the key |
|
261 | */ |
|
262 | public void setLength(int len) |
|
263 | { |
|
264 | keyBuffer.setLength(len); |
|
265 | } |
|
266 | ||
267 | /** |
|
268 | * Checks if two <code>ConfigurationKey</code> objects are equal. The |
|
269 | * method can be called with strings or other objects, too. |
|
270 | * |
|
271 | * @param c the object to compare |
|
272 | * @return a flag if both objects are equal |
|
273 | */ |
|
274 | public boolean equals(Object c) |
|
275 | { |
|
276 | if (c == null) |
|
277 | { |
|
278 | return false; |
|
279 | } |
|
280 | ||
281 | return keyBuffer.toString().equals(c.toString()); |
|
282 | } |
|
283 | ||
284 | /** |
|
285 | * Returns the hash code for this object. |
|
286 | * |
|
287 | * @return the hash code |
|
288 | */ |
|
289 | public int hashCode() |
|
290 | { |
|
291 | return String.valueOf(keyBuffer).hashCode(); |
|
292 | } |
|
293 | ||
294 | /** |
|
295 | * Returns a configuration key object that is initialized with the part |
|
296 | * of the key that is common to this key and the passed in key. |
|
297 | * |
|
298 | * @param other the other key |
|
299 | * @return a key object with the common key part |
|
300 | */ |
|
301 | public ConfigurationKey commonKey(ConfigurationKey other) |
|
302 | { |
|
303 | if (other == null) |
|
304 | { |
|
305 | throw new IllegalArgumentException("Other key must no be null!"); |
|
306 | } |
|
307 | ||
308 | ConfigurationKey result = new ConfigurationKey(); |
|
309 | KeyIterator it1 = iterator(); |
|
310 | KeyIterator it2 = other.iterator(); |
|
311 | ||
312 | while (it1.hasNext() && it2.hasNext() && partsEqual(it1, it2)) |
|
313 | { |
|
314 | if (it1.isAttribute()) |
|
315 | { |
|
316 | result.appendAttribute(it1.currentKey()); |
|
317 | } |
|
318 | else |
|
319 | { |
|
320 | result.append(it1.currentKey()); |
|
321 | if (it1.hasIndex) |
|
322 | { |
|
323 | result.appendIndex(it1.getIndex()); |
|
324 | } |
|
325 | } |
|
326 | } |
|
327 | ||
328 | return result; |
|
329 | } |
|
330 | ||
331 | /** |
|
332 | * Returns the "difference key" to a given key. This value |
|
333 | * is the part of the passed in key that differs from this key. There is |
|
334 | * the following relation: |
|
335 | * <code>other = key.commonKey(other) + key.differenceKey(other)</code> |
|
336 | * for an arbitrary configuration key <code>key</code>. |
|
337 | * |
|
338 | * @param other the key for which the difference is to be calculated |
|
339 | * @return the difference key |
|
340 | */ |
|
341 | public ConfigurationKey differenceKey(ConfigurationKey other) |
|
342 | { |
|
343 | ConfigurationKey common = commonKey(other); |
|
344 | ConfigurationKey result = new ConfigurationKey(); |
|
345 | ||
346 | if (common.length() < other.length()) |
|
347 | { |
|
348 | String k = other.toString().substring(common.length()); |
|
349 | // skip trailing delimiters |
|
350 | int i = 0; |
|
351 | while (i < k.length() && k.charAt(i) == PROPERTY_DELIMITER) |
|
352 | { |
|
353 | i++; |
|
354 | } |
|
355 | ||
356 | if (i < k.length()) |
|
357 | { |
|
358 | result.append(k.substring(i)); |
|
359 | } |
|
360 | } |
|
361 | ||
362 | return result; |
|
363 | } |
|
364 | ||
365 | /** |
|
366 | * Helper method for comparing two key parts. |
|
367 | * |
|
368 | * @param it1 the iterator with the first part |
|
369 | * @param it2 the iterator with the second part |
|
370 | * @return a flag if both parts are equal |
|
371 | */ |
|
372 | private static boolean partsEqual(KeyIterator it1, KeyIterator it2) |
|
373 | { |
|
374 | return it1.nextKey().equals(it2.nextKey()) |
|
375 | && it1.getIndex() == it2.getIndex() |
|
376 | && it1.isAttribute() == it2.isAttribute(); |
|
377 | } |
|
378 | ||
379 | /** |
|
380 | * A specialized iterator class for tokenizing a configuration key. |
|
381 | * This class implements the normal iterator interface. In addition it |
|
382 | * provides some specific methods for configuration keys. |
|
383 | */ |
|
384 | 7077 | public class KeyIterator implements Iterator, Cloneable |
385 | { |
|
386 | /** Stores the current key name.*/ |
|
387 | private String current; |
|
388 | ||
389 | /** Stores the start index of the actual token.*/ |
|
390 | private int startIndex; |
|
391 | ||
392 | /** Stores the end index of the actual token.*/ |
|
393 | private int endIndex; |
|
394 | ||
395 | /** Stores the index of the actual property if there is one.*/ |
|
396 | private int indexValue; |
|
397 | ||
398 | /** Stores a flag if the actual property has an index.*/ |
|
399 | private boolean hasIndex; |
|
400 | ||
401 | /** Stores a flag if the actual property is an attribute.*/ |
|
402 | private boolean attribute; |
|
403 | ||
404 | /** |
|
405 | * Helper method for determining the next indices. |
|
406 | * |
|
407 | * @return the next key part |
|
408 | */ |
|
409 | private String findNextIndices() |
|
410 | { |
|
411 | 15519 | startIndex = endIndex; |
412 | // skip empty names |
|
413 | 15519 | while (startIndex < keyBuffer.length() |
414 | 23649 | && keyBuffer.charAt(startIndex) == PROPERTY_DELIMITER) |
415 | { |
|
416 | 8130 | startIndex++; |
417 | } |
|
418 | ||
419 | // Key ends with a delimiter? |
|
420 | 15519 | if (startIndex >= keyBuffer.length()) |
421 | { |
|
422 | 0 | endIndex = keyBuffer.length(); |
423 | 0 | startIndex = endIndex - 1; |
424 | 0 | return keyBuffer.substring(startIndex, endIndex); |
425 | } |
|
426 | else |
|
427 | { |
|
428 | 15519 | return nextKeyPart(); |
429 | } |
|
430 | } |
|
431 | ||
432 | /** |
|
433 | * Helper method for extracting the next key part. Takes escaping of |
|
434 | * delimiter characters into account. |
|
435 | * |
|
436 | * @return the next key part |
|
437 | */ |
|
438 | private String nextKeyPart() |
|
439 | { |
|
440 | 15519 | StringBuffer key = new StringBuffer(INITIAL_SIZE); |
441 | 15519 | int idx = startIndex; |
442 | 15519 | int endIdx = keyBuffer.toString().indexOf(ATTRIBUTE_START, |
443 | startIndex); |
|
444 | 15519 | if (endIdx < 0 || endIdx == startIndex) |
445 | { |
|
446 | 13956 | endIdx = keyBuffer.length(); |
447 | } |
|
448 | 15519 | boolean found = false; |
449 | ||
450 | 127191 | while (!found && idx < endIdx) |
451 | { |
|
452 | 96153 | char c = keyBuffer.class="keyword">charAt(idx); |
453 | 96153 | if (c == PROPERTY_DELIMITER) |
454 | { |
|
455 | // a duplicated delimiter means escaping |
|
456 | 7464 | if (idx == endIdx - 1 |
457 | || keyBuffer.charAt(idx + 1) != PROPERTY_DELIMITER) |
|
458 | { |
|
459 | 7284 | found = true; |
460 | } |
|
461 | else |
|
462 | { |
|
463 | 180 | idx++; |
464 | } |
|
465 | } |
|
466 | 96153 | if (!found) |
467 | { |
|
468 | 88869 | key.append(c); |
469 | 88869 | idx++; |
470 | } |
|
471 | } |
|
472 | ||
473 | 15519 | endIndex = idx; |
474 | 15519 | return key.toString(); |
475 | } |
|
476 | ||
477 | /** |
|
478 | * Returns the next key part of this configuration key. This is a short |
|
479 | * form of <code>nextKey(false)</code>. |
|
480 | * |
|
481 | * @return the next key part |
|
482 | */ |
|
483 | public String nextKey() |
|
484 | { |
|
485 | 4857 | return nextKey(false); |
486 | } |
|
487 | ||
488 | /** |
|
489 | * Returns the next key part of this configuration key. The boolean |
|
490 | * parameter indicates wheter a decorated key should be returned. This |
|
491 | * affects only attribute keys: if the parameter is <b>false</b>, the |
|
492 | * attribute markers are stripped from the key; if it is <b>true</b>, |
|
493 | * they remain. |
|
494 | * |
|
495 | * @param decorated a flag if the decorated key is to be returned |
|
496 | * @return the next key part |
|
497 | */ |
|
498 | public String nextKey(boolean decorated) |
|
499 | { |
|
500 | 15522 | if (!hasNext()) |
501 | { |
|
502 | 3 | throw new NoSuchElementException("No more key parts!"); |
503 | } |
|
504 | ||
505 | 15519 | hasIndex = false; |
506 | 15519 | indexValue = -1; |
507 | 15519 | String key = findNextIndices(); |
508 | ||
509 | 15519 | attribute = checkAttribute(key); |
510 | 15519 | if (!attribute) |
511 | { |
|
512 | 14127 | hasIndex = checkIndex(key); |
513 | 14127 | if (!hasIndex) |
514 | { |
|
515 | 13440 | current = key; |
516 | } |
|
517 | } |
|
518 | ||
519 | 15519 | return currentKey(decorated); |
520 | } |
|
521 | ||
522 | /** |
|
523 | * Helper method for checking if the passed key is an attribute. |
|
524 | * If this is the case, the internal fields will be set. |
|
525 | * |
|
526 | * @param key the key to be checked |
|
527 | * @return a flag if the key is an attribute |
|
528 | */ |
|
529 | private boolean checkAttribute(String key) |
|
530 | { |
|
531 | 15519 | if (isAttributeKey(key)) |
532 | { |
|
533 | 1392 | current = removeAttributeMarkers(key); |
534 | 1392 | return true; |
535 | } |
|
536 | else |
|
537 | { |
|
538 | 14127 | return false; |
539 | } |
|
540 | } |
|
541 | ||
542 | /** |
|
543 | * Helper method for checking if the passed key contains an index. |
|
544 | * If this is the case, internal fields will be set. |
|
545 | * |
|
546 | * @param key the key to be checked |
|
547 | * @return a flag if an index is defined |
|
548 | */ |
|
549 | private boolean checkIndex(String key) |
|
550 | { |
|
551 | 14127 | boolean result = false; |
552 | ||
553 | 14127 | int idx = key.lastIndexOf(INDEX_START); |
554 | 14127 | if (idx > 0) |
555 | { |
|
556 | 690 | int endidx = key.indexOf(INDEX_END, idx); |
557 | ||
558 | 690 | if (endidx > idx + 1) |
559 | { |
|
560 | 687 | indexValue = Integer.parseInt(key.substring(idx + 1, endidx)); |
561 | 687 | current = key.substring(0, idx); |
562 | 687 | result = true; |
563 | } |
|
564 | } |
|
565 | ||
566 | 14127 | return result; |
567 | } |
|
568 | ||
569 | /** |
|
570 | * Checks if there is a next element. |
|
571 | * |
|
572 | * @return a flag if there is a next element |
|
573 | */ |
|
574 | public boolean hasNext() |
|
575 | { |
|
576 | 37881 | return endIndex < keyBuffer.length(); |
577 | } |
|
578 | ||
579 | /** |
|
580 | * Returns the next object in the iteration. |
|
581 | * |
|
582 | * @return the next object |
|
583 | */ |
|
584 | public Object next() |
|
585 | { |
|
586 | 114 | return nextKey(); |
587 | } |
|
588 | ||
589 | /** |
|
590 | * Removes the current object in the iteration. This method is not |
|
591 | * supported by this iterator type, so an exception is thrown. |
|
592 | */ |
|
593 | public void remove() |
|
594 | { |
|
595 | 3 | throw new UnsupportedOperationException("Remove not supported!"); |
596 | } |
|
597 | ||
598 | /** |
|
599 | * Returns the current key of the iteration (without skipping to the |
|
600 | * next element). This is the same key the previous <code>next()</code> |
|
601 | * call had returned. (Short form of <code>currentKey(false)</code>. |
|
602 | * |
|
603 | * @return the current key |
|
604 | */ |
|
605 | public String currentKey() |
|
606 | { |
|
607 | 915 | return currentKey(false); |
608 | } |
|
609 | ||
610 | /** |
|
611 | * Returns the current key of the iteration (without skipping to the |
|
612 | * next element). The boolean parameter indicates wheter a decorated |
|
613 | * key should be returned. This affects only attribute keys: if the |
|
614 | * parameter is <b>false</b>, the attribute markers are stripped from |
|
615 | * the key; if it is <b>true</b>, they remain. |
|
616 | * |
|
617 | * @param decorated a flag if the decorated key is to be returned |
|
618 | * @return the current key |
|
619 | */ |
|
620 | public String currentKey(boolean decorated) |
|
621 | { |
|
622 | 17655 | return (decorated && isAttribute()) ? constructAttributeKey(current) : current; |
623 | } |
|
624 | ||
625 | /** |
|
626 | * Returns a flag if the current key is an attribute. This method can |
|
627 | * be called after <code>next()</code>. |
|
628 | * |
|
629 | * @return a flag if the current key is an attribute |
|
630 | */ |
|
631 | public boolean isAttribute() |
|
632 | { |
|
633 | 14238 | return attribute; |
634 | } |
|
635 | ||
636 | /** |
|
637 | * Returns the index value of the current key. If the current key does |
|
638 | * not have an index, return value is -1. This method can be called |
|
639 | * after <code>next()</code>. |
|
640 | * |
|
641 | * @return the index value of the current key |
|
642 | */ |
|
643 | public int getIndex() |
|
644 | { |
|
645 | 3237 | return indexValue; |
646 | } |
|
647 | ||
648 | /** |
|
649 | * Returns a flag if the current key has an associated index. |
|
650 | * This method can be called after <code>next()</code>. |
|
651 | * |
|
652 | * @return a flag if the current key has an index |
|
653 | */ |
|
654 | public boolean hasIndex() |
|
655 | { |
|
656 | 10476 | return hasIndex; |
657 | } |
|
658 | ||
659 | /** |
|
660 | * Creates a clone of this object. |
|
661 | * |
|
662 | * @return a clone of this object |
|
663 | */ |
|
664 | public Object clone() |
|
665 | { |
|
666 | try |
|
667 | { |
|
668 | 12669 | return super.clone(); |
669 | } |
|
670 | catch (CloneNotSupportedException cex) |
|
671 | { |
|
672 | // should not happen |
|
673 | 0 | return null; |
674 | } |
|
675 | } |
|
676 | } |
|
677 | } |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |