Clover Coverage Report
Coverage timestamp: Fri May 24 2013 13:47:27 UTC
../../../../../img/srcFileCovDistChart8.png 62% of files have more coverage
130   392   60   6.5
76   223   0.46   20
20     3  
1    
 
  SimpleXPath       Line # 30 130 60 72.6% 0.7256637
 
  (84)
 
1    /* This file is part of the project "Hilbert II" - http://www.qedeq.org
2    *
3    * Copyright 2000-2013, Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10    * This program is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    * GNU General Public License for more details.
14    */
15   
16    package org.qedeq.kernel.xml.tracker;
17   
18    import java.util.ArrayList;
19    import java.util.List;
20    import java.util.StringTokenizer;
21   
22    import org.qedeq.base.utility.EqualsUtility;
23   
24   
25    /**
26    * Simple XPath like description of a location in an XML file.
27    *
28    * @author Michael Meyling
29    */
 
30    public class SimpleXPath {
31   
32    /** List with element names. */
33    private final List elements;
34   
35    /** List with element occurrence numbers. */
36    private final List numbers;
37   
38    /** Attribute of element. */
39    private String attribute;
40   
41    /**
42    * Constructor with simple XPath string as parameter.
43    * This is not the standard XPath definition but it is similar to a subset of
44    * the abbreviation XPath notation.
45    * <p>
46    * <code>/element1/element2[3]@attribute</code> is an example for such
47    * a notation. This selects from the first occurrence of <code>element1</code>
48    * and from the third occurrence of subnode <code>element2</code> the attribute
49    * <code>attribute</code>. The attribute is optional. It is always exactly one node or
50    * the attribute of one node specified.
51    * <p>
52    * The general syntax could be described as follows:
53    * {"/"<em>element</em>"["<em>index</em>"]"}+
54    * ["@"<em>attribute</em>]
55    *
56    *
57    * @param xpath String with the syntax as described above. If the syntax is violated
58    * RuntimeExceptions may occur.
59    */
 
60  96 toggle public SimpleXPath(final String xpath) {
61  96 elements = new ArrayList();
62  96 numbers = new ArrayList();
63  96 attribute = null;
64  96 init(xpath);
65    }
66   
67    /**
68    * Empty constructor.
69    */
 
70  200291 toggle public SimpleXPath() {
71  200291 elements = new ArrayList();
72  200291 numbers = new ArrayList();
73  200291 attribute = null;
74    }
75   
76    /**
77    * Copy constructor.
78    *
79    * @param original XPath to copy.
80    */
 
81  816265 toggle public SimpleXPath(final SimpleXPath original) {
82  816265 elements = new ArrayList();
83  816265 numbers = new ArrayList();
84  816265 attribute = null;
85  816265 init(original.toString());
86    }
87   
88    /**
89    * Initialize all object attributes according to XPath parameter.
90    *
91    * @see SimpleXPath#SimpleXPath(String)
92    *
93    * @param xpath String with the syntax as described above. If the syntax is violated
94    * RuntimeExceptions may occur.
95    */
 
96  816361 toggle private void init(final String xpath) {
97  816361 if (xpath == null) {
98  0 throw new NullPointerException();
99    }
100  816361 if (xpath.length() <= 0) {
101  2 throw new RuntimeException("XPath must not be empty");
102    }
103  816359 if (xpath.charAt(0) != '/') {
104  3 throw new RuntimeException("XPath must start with '/': " + xpath);
105    }
106  816356 if (xpath.indexOf("//") >= 0) {
107  1 throw new RuntimeException("empty tag not permitted: " + xpath);
108    }
109  816355 if (xpath.endsWith("/")) {
110  1 throw new RuntimeException("XPath must not end with '/': " + xpath);
111    }
112  816354 final StringTokenizer tokenizer = new StringTokenizer(xpath, "/");
113  5006799 while (tokenizer.hasMoreTokens()) {
114  4190447 String token = tokenizer.nextToken();
115  4190447 if (!tokenizer.hasMoreTokens() && token.indexOf('@') >= 0) {
116  462 attribute = token.substring(token.indexOf('@') + 1);
117  462 if (attribute.length() <= 0) {
118  1 throw new RuntimeException("empty attribute not permitted: " + xpath);
119    }
120  461 token = token.substring(0, token.indexOf('@'));
121    }
122  4190446 if (token.indexOf('[') < 0) {
123  2769646 elements.add(token);
124  2769646 numbers.add(new Integer(1));
125    } else {
126  1420800 final StringTokenizer getnu = new StringTokenizer(token, "[]");
127  1420800 elements.add(getnu.nextToken());
128  1420800 final Integer i;
129  1420800 try {
130  1420800 i = new Integer(getnu.nextToken());
131    } catch (RuntimeException e) {
132  0 throw new RuntimeException("not an integer: " + xpath, e);
133    }
134  1420800 if (i.intValue() <= 0) {
135  1 throw new RuntimeException("integer must be greater zero: " + xpath);
136    }
137  1420799 numbers.add(i);
138    }
139    }
140    }
141   
142    /**
143    * Get number of collected exceptions.
144    *
145    * @return Number of collected exceptions.
146    */
 
147  454865651 toggle public final int size() {
148  454865651 return elements.size();
149    }
150   
151    /**
152    * Get <code>i</code>-th Element name.
153    *
154    * @param i Starts with 0 and must be smaller than {@link #size()}.
155    * @return Wanted element name.
156    */
 
157  262484181 toggle public final String getElementName(final int i) {
158  262484181 return (String) elements.get(i);
159    }
160   
161    /**
162    * Get <code>i</code>-th occurrence number.
163    *
164    * @param i Starts with 0 and must be smaller than {@link #size()}.
165    * @return Wanted element occurrence number.
166    */
 
167  177176163 toggle public final int getElementOccurrence(final int i) {
168  177176163 return ((Integer) numbers.get(i)).intValue();
169    }
170   
171    /**
172    * Add new element to end of XPath.
173    *
174    * @param elementName element to add.
175    */
 
176  0 toggle public final void addElement(final String elementName) {
177  0 attribute = null;
178  0 elements.add(elementName);
179  0 numbers.add(new Integer(1));
180    }
181   
182    /**
183    * Add new element to end of XPath.
184    *
185    * @param elementName element to add.
186    * @param occurrence Occurrence number of element. Starts with 1.
187    */
 
188  215157702 toggle public final void addElement(final String elementName, final int occurrence) {
189  215157702 attribute = null;
190  215157702 elements.add(elementName);
191  215157702 numbers.add(new Integer(occurrence));
192    }
193   
194    /**
195    * Get last XPath element name.
196    *
197    * @return Last element name. Could be <code>null</code> if no elements exist.
198    */
 
199  0 toggle public final String getLastElement() {
200  0 int size = elements.size();
201  0 if (size <= 0) {
202  0 return null;
203    }
204  0 return (String) elements.get(size - 1);
205    }
206   
207    /**
208    * Get XPath element name before last.
209    *
210    * @return Before last element name. Could be <code>null</code> if no more than one element
211    * exist.
212    */
 
213  0 toggle public final String getBeforeLastElement() {
214  0 int size = elements.size();
215  0 if (size <= 1) {
216  0 return null;
217    }
218  0 return (String) elements.get(size - 2);
219    }
220   
221    /**
222    * Delete last XPath element if any.
223    */
 
224  213052541 toggle public void deleteLastElement() {
225  213052541 int size = elements.size();
226  213052541 if (size > 0) {
227  213052541 elements.remove(size - 1);
228  213052541 numbers.remove(size - 1);
229  213052541 attribute = null;
230    }
231    }
232   
233    /**
234    * Set attribute.
235    *
236    * @param attribute Attribute, maybe <code>null</code>.
237    */
 
238  1665552 toggle public final void setAttribute(final String attribute) {
239  1665552 this.attribute = attribute;
240    }
241   
242    /**
243    * Get attribute.
244    *
245    * @return Attribute, maybe <code>null</code>.
246    */
 
247  1022175 toggle public final String getAttribute() {
248  1022175 return attribute;
249    }
250   
251   
 
252  53 toggle public final boolean equals(final Object obj) {
253  53 if (!(obj instanceof SimpleXPath)) {
254  0 return false;
255    }
256  53 final SimpleXPath other = (SimpleXPath) obj;
257  53 if (!EqualsUtility.equals(this.getAttribute(), other.getAttribute())) {
258  19 return false;
259    }
260  34 return equalsElements(other);
261    }
262   
263    /**
264    * Are the elements and occurrences of this and another element equal? No special treatment
265    * of "*" elements.
266    *
267    * @param other Compare with this object.
268    * @return Are the elements of this and the parameter object equal?
269    */
 
270  34 toggle public final boolean equalsElements(final SimpleXPath other) {
271  34 final int size = this.size();
272  34 if (size != other.size()) {
273  15 return false;
274    }
275   
276  62 for (int i = 0; i < size; i++) {
277  51 if (!EqualsUtility.equals(this.getElementName(i), other.getElementName(i))) {
278  7 return false;
279    }
280  44 if (getElementOccurrence(i) != other.getElementOccurrence(i)) {
281  1 return false;
282    }
283    }
284  11 return true;
285    }
286   
287    /**
288    * Match the elements and occurrences of this finder object and current elements?
289    * This object may contain "*" elements.
290    *
291    * @param current Compare with this current elements. These elements should not
292    * contain "*" elements.
293    * @param currentSummary Contains only "*" elements. This parameter must be identify the same
294    * XPath as <code>current</code>
295    * @return Match the elements of this finder object and the parameter objects?
296    */
 
297  212534247 toggle public final boolean matchesElements(final SimpleXPath current,
298    final SimpleXPath currentSummary) {
299  212534247 final int size = current.size();
300  212534247 if (size != size()) {
301  188512452 return false;
302    }
303  24021795 if (size != currentSummary.size()) {
304  0 throw new IllegalArgumentException("summary size doesn't match");
305    }
306   
307  85992925 for (int i = 0; i < size; i++) {
308  85863954 if ("*".equals(getElementName(i))) {
309  0 if (getElementOccurrence(i) != currentSummary.getElementOccurrence(i)) {
310  0 return false;
311    }
312  0 continue;
313    }
314  85863954 if (!EqualsUtility.equals(current.getElementName(i), getElementName(i))) {
315  537706 return false;
316    }
317  85326248 if (current.getElementOccurrence(i) != getElementOccurrence(i)) {
318  23355118 return false;
319    }
320    }
321  128971 return true;
322    }
323   
324    /**
325    * Match the elements and occurrences of this finder object and current elements?
326    * This object may contain "*" elements. Checks only to current.size().
327    *
328    * @param current Compare with this current elements. These elements should not
329    * contain "*" elements.
330    * @param currentSummary Contains only "*" elements. This parameter must be identify the same
331    * XPath as <code>current</code>
332    * @return Match the elements of this finder object and the parameter objects?
333    */
 
334  0 toggle public final boolean matchesElementsBegining(final SimpleXPath current,
335    final SimpleXPath currentSummary) {
336  0 final int size = current.size();
337  0 if (size > size()) {
338  0 return false;
339    }
340  0 if (size != currentSummary.size()) {
341  0 throw new IllegalArgumentException("summary size doesn't match");
342    }
343   
344  0 for (int i = 0; i < size; i++) {
345  0 if ("*".equals(getElementName(i))) {
346  0 if (getElementOccurrence(i) != currentSummary.getElementOccurrence(i)) {
347  0 return false;
348    }
349  0 continue;
350    }
351  0 if (!EqualsUtility.equals(current.getElementName(i), getElementName(i))) {
352  0 return false;
353    }
354  0 if (current.getElementOccurrence(i) != getElementOccurrence(i)) {
355  0 return false;
356    }
357    }
358  0 return true;
359    }
360   
 
361  883046 toggle public final String toString() {
362  883046 final StringBuffer buffer = new StringBuffer();
363  5775079 for (int i = 0; i < size(); i++) {
364  4892033 buffer.append("/");
365  4892033 buffer.append(getElementName(i));
366  4892033 if (getElementOccurrence(i) != 1) {
367  1631362 buffer.append("[");
368  1631362 buffer.append(getElementOccurrence(i));
369  1631362 buffer.append("]");
370    }
371    }
372  883046 if (getAttribute() != null) {
373  4993 buffer.append("@");
374  4993 buffer.append(getAttribute());
375    }
376  883046 return buffer.toString();
377    }
378   
 
379  46 toggle public final int hashCode() {
380  46 int code = 0;
381  46 if (attribute != null) {
382  14 code ^= attribute.hashCode();
383    }
384  210 for (int i = 0; i < size(); i++) {
385  164 code ^= i + 1;
386  164 code ^= getElementName(i).hashCode();
387  164 code ^= getElementOccurrence(i);
388    }
389  46 return code;
390    }
391   
392    }