Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 333   Methods: 6
NCLOC: 230   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
XPointer.java 100% 100% 83.3% 99.6%
coverage coverage
 1    /* Copyright 2002-2005 Elliotte Rusty Harold
 2   
 3    This library is free software; you can redistribute it and/or modify
 4    it under the terms of version 2.1 of the GNU Lesser General Public
 5    License as published by the Free Software Foundation.
 6   
 7    This library is distributed in the hope that it will be useful,
 8    but WITHOUT ANY WARRANTY; without even the implied warranty of
 9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 10    GNU Lesser General Public License for more details.
 11   
 12    You should have received a copy of the GNU Lesser General Public
 13    License along with this library; if not, write to the
 14    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 15    Boston, MA 02111-1307 USA
 16   
 17    You can contact Elliotte Rusty Harold by sending e-mail to
 18    elharo@metalab.unc.edu. Please include the word "XOM" in the
 19    subject line. The XOM home page is located at http://www.xom.nu/
 20    */
 21   
 22   
 23   
 24    package nu.xom.xinclude;
 25   
 26    import java.util.ArrayList;
 27    import java.util.List;
 28   
 29    import nu.xom.Attribute;
 30    import nu.xom.Document;
 31    import nu.xom.Element;
 32    import nu.xom.IllegalNameException;
 33    import nu.xom.Node;
 34    import nu.xom.Nodes;
 35    import nu.xom.ParentNode;
 36    import nu.xom.XMLException;
 37   
 38    /**
 39    *
 40    * <p>
 41    * Right now this is just for XInclude, and hence is non-public.
 42    * Once it's more baked it will probably become public and move
 43    * to a package of its own.
 44    * </p>
 45    *
 46    * @author Elliotte Rusty Harold
 47    * @version 1.1b6
 48    *
 49    */
 50    class XPointer {
 51   
 52   
 53    // prevent instantiation
 54  0 private XPointer() {}
 55   
 56   
 57  88 static Nodes query(Document doc, String xptr)
 58    throws XPointerSyntaxException, XPointerResourceException {
 59   
 60  88 Nodes result = new Nodes();
 61  88 boolean found = false;
 62   
 63  88 try { // Is this a shorthand XPointer?
 64    // Need to include a URI in case this is a colonized scheme name
 65  88 new Element(xptr, "http://www.example.com");
 66  35 Element identified = findByID(doc.getRootElement(), xptr);
 67  35 if (identified != null) {
 68  34 result.append(identified);
 69  34 return result;
 70    }
 71    }
 72    catch (IllegalNameException ex) {
 73    // not a bare name; try element() scheme
 74  53 List elementSchemeData = findElementSchemeData(xptr);
 75  47 if (elementSchemeData.size() == 0) {
 76    // This may be a legal XPointer, but it doesn't
 77    // have an element() scheme so we can't handle it.
 78  4 throw new XPointerSyntaxException(
 79    "No supported XPointer schemes found"
 80    );
 81    }
 82   
 83  43 for (int i = 0; i < elementSchemeData.size(); i++) {
 84  50 String currentData = (String) (elementSchemeData.get(i));
 85  50 int[] keys = new int[0];
 86  50 ParentNode current = doc;
 87  50 if (currentData.indexOf('/') == -1) {
 88    // raw id in element like element(f2)
 89  5 try {
 90  5 new Element(currentData);
 91    }
 92    catch (IllegalNameException inex) {
 93    // not a bare name; and doesn't contain a /
 94    // This doesn't adhere to the element scheme.
 95    // Therefore, according to the XPointer element
 96    // scheme spec, " if scheme data in a pointer
 97    // part with the element() scheme does not
 98    // conform to the syntax defined in this
 99    // section the pointer part does not identify
 100    // a subresource."
 101  1 continue;
 102    }
 103  4 Element identified = findByID(
 104    doc.getRootElement(), currentData);
 105  4 if (identified != null) {
 106  2 if (!found) result.append(identified);
 107  3 found = true;
 108    }
 109    }
 110  45 else if (!currentData.startsWith("/")) {
 111  4 String id = currentData.substring(
 112    0, currentData.indexOf('/'));
 113    // Check to make sure this is a legal
 114    // XML name/ID value
 115  4 try {
 116  4 new Element(id);
 117    }
 118    catch (XMLException inex) {
 119    // doesn't adhere to the element scheme spec;
 120    // Therefore this pointer part does not identify
 121    // a subresource (See 2nd paragraph of section
 122    // 3 of http://www.w3.org/TR/xptr-element/ )
 123    // This is not a resource error unless no
 124    // XPointer part identifies a subresource.
 125  1 continue;
 126    }
 127  3 current = findByID(doc.getRootElement(), id);
 128  3 keys = split(currentData.substring(
 129    currentData.indexOf('/')));
 130   
 131  2 if (current == null) continue;
 132    }
 133    else {
 134  41 keys = split(currentData);
 135    }
 136   
 137  43 for (int j = 0; j < keys.length; j++) {
 138  76 current = findNthChildElement(current, keys[j]);
 139  8 if (current == null) break;
 140    }
 141   
 142  43 if (current != doc && current != null) {
 143  30 if (!found) result.append(current);
 144  31 found = true;
 145    }
 146   
 147    }
 148   
 149    }
 150   
 151  32 if (found) return result;
 152    else {
 153    // If we get here and still haven't been able to match an
 154    // element, the XPointer has failed.
 155  9 throw new XPointerResourceException(
 156    "XPointer " + xptr
 157    + " did not locate any nodes in the document "
 158    + doc.getBaseURI()
 159    );
 160    }
 161   
 162    }
 163   
 164   
 165  76 private static Element findNthChildElement(
 166    ParentNode parent, int position) {
 167    // watch out for 1-based indexing of tumblers
 168  76 int elementCount = 1;
 169  76 for (int i = 0; i < parent.getChildCount(); i++) {
 170  165 Node child = parent.getChild(i);
 171  165 if (child instanceof Element) {
 172  68 if (elementCount == position) return (Element) child;
 173  19 elementCount++;
 174    }
 175    }
 176  8 return null;
 177    }
 178   
 179   
 180  44 private static int[] split(String tumbler)
 181    throws XPointerSyntaxException {
 182   
 183  44 int numberOfParts = 0;
 184  44 for (int i = 0; i < tumbler.length(); i++) {
 185  89 if (tumbler.charAt(i) == '/') numberOfParts++;
 186    }
 187   
 188  44 int[] result = new int[numberOfParts];
 189  44 int index = 0;
 190  44 StringBuffer part = new StringBuffer(3);
 191  44 try {
 192  44 for (int i = 1; i < tumbler.length(); i++) {
 193  139 if (tumbler.charAt(i) == '/') {
 194  44 result[index] = Integer.parseInt(part.toString());
 195  43 index++;
 196  43 part = new StringBuffer(3);
 197    }
 198    else {
 199  95 part.append(tumbler.charAt(i));
 200    }
 201    }
 202  43 result[result.length-1] = Integer.parseInt(part.toString());
 203    }
 204    catch (NumberFormatException ex) {
 205  3 XPointerSyntaxException ex2
 206    = new XPointerSyntaxException(tumbler
 207    + " is not syntactically correct", ex);
 208  3 throw ex2;
 209    }
 210   
 211  41 return result;
 212    }
 213   
 214  65 private static List findElementSchemeData(String xpointer)
 215    throws XPointerSyntaxException {
 216   
 217  65 List result = new ArrayList(1);
 218   
 219  65 StringBuffer xptr = new StringBuffer(xpointer.trim());
 220  65 StringBuffer scheme = new StringBuffer();
 221  65 int i = 0;
 222  65 while (i < xptr.length()) {
 223  514 char c = xptr.charAt(i);
 224  62 if (c == '(') break;
 225  452 else scheme.append(c);
 226  452 i++;
 227    }
 228   
 229    // need to verify that scheme is a QName
 230  65 try {
 231    // ugly hack because Verifier isn't public
 232  65 new Element(scheme.toString(), "http://www.example.com/");
 233    }
 234    catch (IllegalNameException ex) {
 235  3 throw new XPointerSyntaxException(ex.getMessage());
 236    }
 237   
 238  62 int open = 1; // parentheses count
 239  62 i++;
 240  62 StringBuffer schemeData = new StringBuffer();
 241  62 try {
 242  62 while (open > 0) {
 243  341 char c = xptr.charAt(i);
 244  339 if (c == '^') {
 245  3 c = xptr.charAt(i+1);
 246  3 schemeData.append(c);
 247  3 if (c != '^' && c != '(' && c != ')') {
 248  1 throw new XPointerSyntaxException(
 249    "Illegal XPointer escape sequence"
 250    );
 251    }
 252  2 i++;
 253    }
 254  336 else if (c == '(') {
 255  2 schemeData.append(c);
 256  2 open++;
 257    }
 258  334 else if (c == ')') {
 259  61 open--;
 260  2 if (open > 0) schemeData.append(c);
 261    }
 262    else {
 263  273 schemeData.append(c);
 264    }
 265  338 i++;
 266    }
 267    }
 268    catch (StringIndexOutOfBoundsException ex) {
 269  2 throw new XPointerSyntaxException("Unbalanced parentheses");
 270    }
 271   
 272  59 if (scheme.toString().equals("element")) {
 273  52 result.add(schemeData.toString());
 274    }
 275   
 276  59 if (i + 1 < xptr.length()) {
 277  12 result.addAll(findElementSchemeData(xptr.substring(i)));
 278    }
 279   
 280  57 return result;
 281    }
 282   
 283   
 284  42 static Element findByID(Element element, String id) {
 285   
 286  42 Node current = element;
 287  42 boolean end = false;
 288  42 int index = -1;
 289  42 while (true) {
 290   
 291  452 if (current instanceof Element) {
 292  249 Element currentElement = (Element) current;
 293  249 for (int i = 0; i < currentElement.getAttributeCount(); i++) {
 294  232 Attribute att = currentElement.getAttribute(i);
 295  232 if (att.getType() == Attribute.Type.ID) {
 296  146 if (att.getValue().trim().equals(id)) {
 297  38 return currentElement;
 298    }
 299    }
 300    }
 301    }
 302   
 303  414 if (!end && current.getChildCount() > 0) {
 304  126 current = current.getChild(0);
 305  126 index = 0;
 306    }
 307    else {
 308  288 if (end) {
 309  4 if (current == element) break;
 310    }
 311  284 end = false;
 312  284 ParentNode parent = current.getParent();
 313  284 if (parent.getChildCount() - 1 == index) {
 314  72 current = parent;
 315  72 if (current != element) {
 316  68 parent = current.getParent();
 317  68 index = parent.indexOf(current);
 318    }
 319  72 end = true;
 320    }
 321    else {
 322  212 index++;
 323  212 current = parent.getChild(index);
 324    }
 325    }
 326    }
 327   
 328  4 return null;
 329   
 330    }
 331   
 332   
 333    }