Clover coverage report - QedeqKernelSe Coverage Report
Coverage timestamp: Sa Dez 22 2007 01:35:21 CET
file stats: LOC: 450   Methods: 24
NCLOC: 250   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
SimpleXPath.java 60,7% 70,4% 79,2% 68%
coverage coverage
 1    /* $Id: SimpleXPath.java,v 1.15 2007/12/21 23:33:48 m31 Exp $
 2    *
 3    * This file is part of the project "Hilbert II" - http://www.qedeq.org
 4    *
 5    * Copyright 2000-2007, 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.kernel.common.SourcePosition;
 25    import org.qedeq.kernel.utility.EqualsUtility;
 26   
 27   
 28    /**
 29    * Simple XPath like description of a location in an XML file.
 30    *
 31    * @version $Revision: 1.15 $
 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  33023 public SimpleXPath(final String xpath) {
 71  33023 elements = new ArrayList();
 72  33023 numbers = new ArrayList();
 73  33023 attribute = null;
 74  33023 init(xpath);
 75    }
 76   
 77    /**
 78    * Empty constructor.
 79    */
 80  98847 public SimpleXPath() {
 81  98847 elements = new ArrayList();
 82  98847 numbers = new ArrayList();
 83  98847 attribute = null;
 84    }
 85   
 86    /**
 87    * Copy constructor.
 88    *
 89    * @param original XPath to copy.
 90    */
 91  329854 public SimpleXPath(final SimpleXPath original) {
 92  329854 elements = new ArrayList();
 93  329854 numbers = new ArrayList();
 94  329854 attribute = null;
 95  329854 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  362877 private void init(final String xpath) {
 107  362877 if (xpath == null) {
 108  0 throw new NullPointerException();
 109    }
 110  362877 if (xpath.length() <= 0) {
 111  1 throw new RuntimeException("XPath must not be empty");
 112    }
 113  362876 if (xpath.charAt(0) != '/') {
 114  2 throw new RuntimeException("XPath must start with '/': " + xpath);
 115    }
 116  362874 if (xpath.indexOf("//") >= 0) {
 117  1 throw new RuntimeException("empty tag not permitted: " + xpath);
 118    }
 119  362873 if (xpath.endsWith("/")) {
 120  1 throw new RuntimeException("XPath must not end with '/': " + xpath);
 121    }
 122  362872 final StringTokenizer tokenizer = new StringTokenizer(xpath, "/");
 123  362872 while (tokenizer.hasMoreTokens()) {
 124  1735947 String token = tokenizer.nextToken();
 125  1735947 if (!tokenizer.hasMoreTokens() && token.indexOf('@') >= 0) {
 126  3362 attribute = token.substring(token.indexOf('@') + 1);
 127  3362 if (attribute.length() <= 0) {
 128  1 throw new RuntimeException("empty attribute not permitted: " + xpath);
 129    }
 130  3361 token = token.substring(0, token.indexOf('@'));
 131    }
 132  1735946 if (token.indexOf('[') < 0) {
 133  1142296 elements.add(token);
 134  1142296 numbers.add(new Integer(1));
 135    } else {
 136  593650 final StringTokenizer getnu = new StringTokenizer(token, "[]");
 137  593650 elements.add(getnu.nextToken());
 138  593650 final Integer i;
 139  593650 try {
 140  593650 i = new Integer(getnu.nextToken());
 141    } catch (RuntimeException e) {
 142  0 throw new RuntimeException("not an integer: " + xpath, e);
 143    }
 144  593650 if (i.intValue() <= 0) {
 145  1 throw new RuntimeException("integer must be greater zero: " + xpath);
 146    }
 147  593649 numbers.add(i);
 148    }
 149    }
 150    }
 151   
 152    /**
 153    * Get number of collected exceptions.
 154    *
 155    * @return Number of collected exceptions.
 156    */
 157  274312971 public final int size() {
 158  274312971 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  130694990 public final String getElementName(final int i) {
 168  130694990 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  87635088 public final int getElementOccurrence(final int i) {
 178  87635088 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 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  129532547 public final void addElement(final String elementName, final int occurrence) {
 199  129532547 attribute = null;
 200  129532547 elements.add(elementName);
 201  129532547 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 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 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  129243105 public void deleteLastElement() {
 235  129243105 int size = elements.size();
 236  129243105 if (size > 0) {
 237  129243105 elements.remove(size - 1);
 238  129243105 numbers.remove(size - 1);
 239  129243105 attribute = null;
 240    }
 241    }
 242   
 243    /**
 244    * Set attribute.
 245    *
 246    * @param attribute Attribute, maybe <code>null</code>.
 247    */
 248  722647 public final void setAttribute(final String attribute) {
 249  722647 this.attribute = attribute;
 250    }
 251   
 252    /**
 253    * Get attribute.
 254    *
 255    * @return Attribute, maybe <code>null</code>.
 256    */
 257  481749 public final String getAttribute() {
 258  481749 return attribute;
 259    }
 260   
 261    /**
 262    * Set starting location of XPath.
 263    *
 264    * @param position Starting point of this XPath.
 265    */
 266  36118 public final void setStartLocation(final SourcePosition position) {
 267  36118 start = position;
 268    }
 269   
 270    /**
 271    * Get start location.
 272    *
 273    * @return File position.
 274    */
 275  35331 public final SourcePosition getStartLocation() {
 276  35331 return start;
 277    }
 278   
 279    /**
 280    * Set ending location of XPath.
 281    *
 282    * @param position Ending point of this XPath.
 283    */
 284  32950 public final void setEndLocation(final SourcePosition position) {
 285  32950 end = position;
 286    }
 287   
 288    /**
 289    * Get end location.
 290    *
 291    * @return File position.
 292    */
 293  32966 public final SourcePosition getEndLocation() {
 294  32966 return end;
 295    }
 296   
 297  53 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  19 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 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  128677004 public final boolean matchesElements(final SimpleXPath current,
 356    final SimpleXPath currentSummary) {
 357  128677004 final int size = current.size();
 358  128677004 if (size != size()) {
 359  114177532 return false;
 360    }
 361  14499472 if (size != currentSummary.size()) {
 362  0 throw new IllegalArgumentException("summary size doesn't match");
 363    }
 364   
 365  14499472 for (int i = 0; i < size; i++) {
 366  42880430 if ("*".equals(getElementName(i))) {
 367  0 if (getElementOccurrence(i) != currentSummary.getElementOccurrence(i)) {
 368  0 return false;
 369    }
 370  0 continue;
 371    }
 372  42880430 if (!EqualsUtility.equals(current.getElementName(i), getElementName(i))) {
 373  431332 return false;
 374    }
 375  42449098 if (current.getElementOccurrence(i) != getElementOccurrence(i)) {
 376  14002240 return false;
 377    }
 378    }
 379  65900 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 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  405794 public final String toString() {
 420  405794 final StringBuffer buffer = new StringBuffer();
 421  405794 for (int i = 0; i < size(); i++) {
 422  2053414 buffer.append("/");
 423  2053414 buffer.append(getElementName(i));
 424  2053414 if (getElementOccurrence(i) != 1) {
 425  683206 buffer.append("[");
 426  683206 buffer.append(getElementOccurrence(i));
 427  683206 buffer.append("]");
 428    }
 429    }
 430  405794 if (getAttribute() != null) {
 431  6506 buffer.append("@");
 432  6506 buffer.append(getAttribute());
 433    }
 434  405794 return buffer.toString();
 435    }
 436   
 437  46 public final int hashCode() {
 438  46 int code = 0;
 439  46 if (attribute != null) {
 440  14 code ^= attribute.hashCode();
 441    }
 442  46 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    }