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