Clover coverage report - Clover results for XOM 1.2d1
Coverage timestamp: Wed Feb 8 2006 08:31:33 EST
file stats: LOC: 1,091   Methods: 35
NCLOC: 457   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
Attribute.java 98.4% 86.4% 100% 89.4%
coverage coverage
 1    /* Copyright 2002-2006 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    package nu.xom;
 23   
 24    /**
 25    * <p>
 26    * This class represents an attribute such as
 27    * <code>type="empty"</code> or
 28    * <code>xlink:href="http://www.example.com"</code>.
 29    * </p>
 30    *
 31    * <p>
 32    * Attributes that declare namespaces such as
 33    * <code>xmlns="http://www.w3.org/TR/1999/xhtml"</code>
 34    * or <code>xmlns:xlink="http://www.w3.org/TR/1999/xlink"</code>
 35    * are stored separately on the elements where they
 36    * appear. They are never represented as <code>Attribute</code>
 37    * objects.
 38    * </p>
 39    *
 40    * @author Elliotte Rusty Harold
 41    * @version 1.2d1
 42    *
 43    */
 44    public class Attribute extends Node {
 45   
 46    private String localName;
 47    private String prefix;
 48    private String URI;
 49    private String value = "";
 50    private Type type;
 51   
 52   
 53    /**
 54    * <p>
 55    * Creates a new attribute in no namespace with the
 56    * specified name and value and undeclared type.
 57    * </p>
 58    *
 59    * @param localName the unprefixed attribute name
 60    * @param value the attribute value
 61    *
 62    * @throws IllegalNameException if the local name is not
 63    * a namespace well-formed, non-colonized name
 64    * @throws IllegalDataException if the value contains characters
 65    * which are not legal in XML such as vertical tab or a null.
 66    * Characters such as " and &amp; are legal, but will be
 67    * automatically escaped when the attribute is serialized.
 68    */
 69  1911533 public Attribute(String localName, String value) {
 70  1911533 this(localName, "", value, Type.UNDECLARED);
 71    }
 72   
 73   
 74    /**
 75    * <p>
 76    * Creates a new attribute in no namespace with the
 77    * specified name, value, and type.
 78    * </p>
 79    *
 80    * @param localName the unprefixed attribute name
 81    * @param value the attribute value
 82    * @param type the attribute type
 83    *
 84    * @throws IllegalNameException if the local name is
 85    * not a namespace well-formed non-colonized name
 86    * @throws IllegalDataException if the value contains
 87    * characters which are not legal in
 88    * XML such as vertical tab or a null. Note that
 89    * characters such as " and &amp; are legal,
 90    * but will be automatically escaped when the
 91    * attribute is serialized.
 92    */
 93  13 public Attribute(String localName, String value, Type type) {
 94  13 this(localName, "", value, type);
 95    }
 96   
 97   
 98    /**
 99    * <p>
 100    * Creates a new attribute in the specified namespace with the
 101    * specified name and value and undeclared type.
 102    * </p>
 103    *
 104    * @param name the prefixed attribute name
 105    * @param URI the namespace URI
 106    * @param value the attribute value
 107    *
 108    * @throws IllegalNameException if the name is not a namespace
 109    * well-formed name
 110    * @throws IllegalDataException if the value contains characters
 111    * which are not legal in XML such as vertical tab or a null.
 112    * Note that characters such as " and &amp; are legal, but will
 113    * be automatically escaped when the attribute is serialized.
 114    * @throws MalformedURIException if <code>URI</code> is not
 115    * an RFC 3986 URI reference
 116    * @throws NamespaceConflictException if there's no prefix,
 117    * but the URI is not the empty string, or the prefix is
 118    * <code>xml</code> and the URI is not
 119    * http://www.w3.org/XML/1998/namespace
 120    */
 121  2656 public Attribute(String name, String URI, String value) {
 122  2656 this(name, URI, value, Type.UNDECLARED);
 123    }
 124   
 125   
 126    /**
 127    * <p>
 128    * Creates a new attribute in the specified namespace with the
 129    * specified name, value, and type.
 130    * </p>
 131    *
 132    * @param name the prefixed attribute name
 133    * @param URI the namespace URI
 134    * @param value the attribute value
 135    * @param type the attribute type
 136    *
 137    * @throws IllegalNameException if the name is not a namespace
 138    * well-formed prefixed name
 139    * @throws IllegalDataException if the value contains
 140    * characters which are not legal in XML such as
 141    * vertical tab or a null. Note that characters such as
 142    * " and &amp; are legal, but will be automatically escaped
 143    * when the attribute is serialized.
 144    * @throws MalformedURIException if <code>URI</code> is not
 145    * an RFC 3986 absolute URI reference
 146    */
 147  1914830 public Attribute(
 148    String name, String URI, String value, Type type) {
 149   
 150  1914830 prefix = "";
 151  1914830 String localName = name;
 152  1914830 int prefixPosition = name.indexOf(':');
 153  1914830 if (prefixPosition > 0) {
 154  2697 prefix = name.substring(0, prefixPosition);
 155  2697 localName = name.substring(prefixPosition + 1);
 156    }
 157   
 158  1914830 try {
 159  1914830 _setLocalName(localName);
 160    }
 161    catch (IllegalNameException ex) {
 162  5 ex.setData(name);
 163  5 throw ex;
 164    }
 165  1914825 _setNamespace(prefix, URI);
 166  1914812 _setValue(value);
 167  1914807 if ("xml".equals(this.prefix) && "id".equals(this.localName)) {
 168  15 _setType(Attribute.Type.ID);
 169    }
 170    else {
 171  1914792 _setType(type);
 172    }
 173   
 174    }
 175   
 176   
 177    /**
 178    * <p>
 179    * Creates a copy of the specified attribute.
 180    * </p>
 181    *
 182    * @param attribute the attribute to copy
 183    *
 184    */
 185  294 public Attribute(Attribute attribute) {
 186   
 187    // These are all immutable types
 188  294 this.localName = attribute.localName;
 189  294 this.prefix = attribute.prefix;
 190  294 this.URI = attribute.URI;
 191  294 this.value = attribute.value;
 192  294 this.type = attribute.type;
 193   
 194    }
 195   
 196   
 197  2150292 private Attribute() {}
 198   
 199  2150292 static Attribute build(
 200    String qualifiedName, String URI, String value, Type type, String localName) {
 201   
 202  2150292 Attribute result = new Attribute();
 203  2150292 String prefix = "";
 204  2150292 int prefixPosition = qualifiedName.indexOf(':');
 205  2150292 if (prefixPosition >= 0) {
 206  1699 prefix = qualifiedName.substring(0, prefixPosition);
 207  1699 if ("xml:id".equals(qualifiedName)) {
 208  32 type = Attribute.Type.ID;
 209  32 value = normalize(value);
 210    // ???? should I only do this if validating?
 211    // Verifier.checkNCName(value);
 212    }
 213    }
 214   
 215  2150292 result.localName = localName;
 216  2150292 result.prefix = prefix;
 217  2150292 result.type = type;
 218  2150292 result.URI = URI;
 219  2150292 result.value = value;
 220   
 221  2150292 return result;
 222   
 223    }
 224   
 225   
 226  32 private static String normalize(String s) {
 227   
 228  32 int length = s.length();
 229  32 int pos = 0;
 230  10 while (pos < length && s.charAt(pos) == ' ') pos++;
 231  32 s = s.substring(pos);
 232  32 int end = s.length()-1;
 233  16 while (end > 0 && s.charAt(end) == ' ') end--;
 234  32 s = s.substring(0, end+1);
 235   
 236  32 length = s.length();
 237  32 StringBuffer sb = new StringBuffer(length);
 238  32 boolean wasSpace = false;
 239  32 for (int i = 0; i < length; i++) {
 240  102 char c = s.charAt(i);
 241  102 if (c == ' ') {
 242  5 if (wasSpace) continue;
 243  6 sb.append(' ');
 244  6 wasSpace = true;
 245    }
 246    else {
 247  91 sb.append(c);
 248  91 wasSpace = false;
 249    }
 250    }
 251  32 return sb.toString();
 252   
 253    }
 254   
 255   
 256    /**
 257    * <p>
 258    * Returns the DTD type of this attribute.
 259    * If this attribute does not have a type, then
 260    * <code>Type.UNDECLARED</code> is returned.
 261    * </p>
 262    *
 263    * @return the DTD type of this attribute
 264    */
 265  39941 public final Type getType() {
 266  39941 return type;
 267    }
 268   
 269   
 270    /**
 271    * <p>
 272    * Sets the type of this attribute to one of the ten
 273    * DTD types or <code>Type.UNDECLARED</code>.
 274    * </p>
 275    *
 276    * @param type the DTD type of this attribute
 277    * @throws NullPointerException if <code>type</code> is null
 278    * @throws IllegalDataException if this is an <code>xml:id</code>
 279    * attribute and the <code>type</code> is not ID
 280    */
 281  9 public void setType(Type type) {
 282   
 283  9 if (type == null) {
 284  1 throw new NullPointerException("Null attribute type");
 285    }
 286  8 if (isXMLID() && ! Type.ID.equals(type)) {
 287  1 throw new IllegalDataException(
 288    "Can't change type of xml:id attribute to " + type);
 289    }
 290  7 _setType(type);
 291   
 292    }
 293   
 294   
 295  11 private boolean isXMLID() {
 296  11 return "xml".equals(this.prefix) && "id".equals(this.localName);
 297    }
 298   
 299   
 300  1914814 private void _setType(Type type) {
 301  1914814 this.type = type;
 302    }
 303   
 304   
 305    /**
 306    * <p>
 307    * Returns the attribute value. If the attribute was
 308    * originally created by a parser, it will have been
 309    * normalized according to its type.
 310    * However, attributes created in memory are not normalized.
 311    * </p>
 312    *
 313    * @return the value of the attribute
 314    *
 315    */
 316  3650485 public final String getValue() {
 317  3650485 return value;
 318    }
 319   
 320   
 321    /**
 322    * <p>
 323    * Sets the attribute's value to the specified string,
 324    * replacing any previous value. The value is not normalized
 325    * automatically.
 326    * </p>
 327    *
 328    * @param value the value assigned to the attribute
 329    * @throws IllegalDataException if the value contains characters
 330    * which are not legal in XML such as vertical tab or a null.
 331    * Characters such as " and &amp; are legal, but will be
 332    * automatically escaped when the attribute is serialized.
 333    * @throws MalformedURIException if this is an
 334    * <code>xml:base</code> attribute, and the value is not a
 335    * legal IRI
 336    */
 337  79 public void setValue(String value) {
 338  79 _setValue(value);
 339    }
 340   
 341   
 342  1914891 private void _setValue(String value) {
 343   
 344  1914891 if ("xml".equals(this.prefix) && "id".equals(this.localName)) {
 345    // ???? do I really want to do this. XML ID test case
 346    // suggests not
 347  17 Verifier.checkNCName(value);
 348    }
 349    else {
 350  1914874 Verifier.checkPCDATA(value);
 351    }
 352  1914883 this.value = value;
 353   
 354    }
 355   
 356   
 357    /**
 358    * <p>
 359    * Returns the local name of this attribute,
 360    * not including the prefix.
 361    * </p>
 362    *
 363    * @return the attribute's local name
 364    */
 365  6052897 public final String getLocalName() {
 366  6052897 return localName;
 367    }
 368   
 369   
 370    /**
 371    * <p>
 372    * Sets the local name of the attribute.
 373    * </p>
 374    *
 375    * @param localName the new local name
 376    *
 377    * @throws IllegalNameException if <code>localName</code>
 378    * is not a namespace well-formed, non-colonized name
 379    *
 380    */
 381  5 public void setLocalName(String localName) {
 382   
 383  5 if ("id".equals(localName) &&
 384    "http://www.w3.org/XML/1998/namespace".equals(this.URI)) {
 385  2 Verifier.checkNCName(this.value);
 386    }
 387  4 _setLocalName(localName);
 388  3 if (isXMLID()) {
 389  1 this.setType(Attribute.Type.ID);
 390    }
 391   
 392    }
 393   
 394   
 395  1914834 private void _setLocalName(String localName) {
 396  1914834 Verifier.checkNCName(localName);
 397  1914831 if (localName.equals("xmlns")) {
 398  3 throw new IllegalNameException("The Attribute class is not"
 399    + " used for namespace declaration attributes.");
 400    }
 401  1914828 this.localName = localName;
 402    }
 403   
 404   
 405    /**
 406    * <p>
 407    * Returns the qualified name of this attribute,
 408    * including the prefix if this attribute is in a namespace.
 409    * </p>
 410    *
 411    * @return the attribute's qualified name
 412    */
 413  1900501 public final String getQualifiedName() {
 414  1898062 if (prefix.length() == 0) return localName;
 415  2439 else return prefix + ":" + localName;
 416    }
 417   
 418   
 419    /**
 420    * <p>
 421    * Returns the namespace URI of this attribute, or the empty string
 422    * if this attribute is not in a namespace.
 423    * </p>
 424    *
 425    * @return the attribute's namespace URI
 426    */
 427  5668721 public final String getNamespaceURI() {
 428  5668721 return URI;
 429    }
 430   
 431   
 432    /**
 433    * <p>
 434    * Returns the prefix of this attribute,
 435    * or the empty string if this attribute
 436    * is not in a namespace.
 437    * </p>
 438    *
 439    * @return the attribute's prefix
 440    */
 441  5820654 public final String getNamespacePrefix() {
 442  5820654 return prefix;
 443    }
 444   
 445   
 446    /**
 447    * <p>
 448    * Sets the attribute's namespace prefix and URI.
 449    * Because attributes must be prefixed in order to have a
 450    * namespace URI (and vice versa) this must be done
 451    * simultaneously.
 452    * </p>
 453    *
 454    * @param prefix the new namespace prefix
 455    * @param URI the new namespace URI
 456    *
 457    * @throws MalformedURIException if <code>URI</code> is
 458    * not an RFC 3986 URI reference
 459    * @throws IllegalNameException if
 460    * <ul>
 461    * <li>The prefix is <code>xmlns</code>.</li>
 462    * <li>The prefix is null or the empty string.</li>
 463    * <li>The URI is null or the empty string.</li>
 464    * </ul>
 465    * @throws NamespaceConflictException if
 466    * <ul>
 467    * <li>The prefix is <code>xml</code> and the namespace URI is
 468    * not <code>http://www.w3.org/XML/1998/namespace</code>.</li>
 469    * <li>The prefix conflicts with an existing declaration
 470    * on the attribute's parent element.</li>
 471    * </ul>
 472    */
 473  25 public void setNamespace(String prefix, String URI) {
 474   
 475  25 if ("xml".equals(prefix) && "id".equals(this.localName)) {
 476  3 Verifier.checkNCName(this.value);
 477  2 this.setType(Attribute.Type.ID);
 478    }
 479   
 480  24 _setNamespace(prefix, URI);
 481   
 482    }
 483   
 484   
 485  1914849 private void _setNamespace(String prefix, String URI) {
 486   
 487  1 if (URI == null) URI = "";
 488  1 if (prefix == null) prefix = "";
 489   
 490  1914849 if (prefix.equals("xmlns")) {
 491  3 throw new IllegalNameException(
 492    "Attribute objects are not used to represent "
 493    + " namespace declarations");
 494    }
 495  1914846 else if (prefix.equals("xml")
 496    && !(URI.equals("http://www.w3.org/XML/1998/namespace"))) {
 497  4 throw new NamespaceConflictException(
 498    "Wrong namespace URI for xml prefix: " + URI);
 499    }
 500  1914842 else if (URI.equals("http://www.w3.org/XML/1998/namespace")
 501    && !prefix.equals("xml")) {
 502  1 throw new NamespaceConflictException(
 503    "Wrong prefix for the XML namespace: " + prefix);
 504    }
 505  1914841 else if (prefix.length() == 0) {
 506  1912131 if (URI.length() == 0) {
 507  1912129 this.prefix = "";
 508  1912129 this.URI = "";
 509  1912129 return;
 510    }
 511    else {
 512  2 throw new NamespaceConflictException(
 513    "Unprefixed attribute " + this.localName
 514    + " cannot be in default namespace " + URI);
 515    }
 516    }
 517  2710 else if (URI.length() == 0) {
 518  1 throw new NamespaceConflictException(
 519    "Attribute prefixes must be declared.");
 520    }
 521   
 522  2709 ParentNode parent = this.getParent();
 523  2709 if (parent != null) {
 524    // test for namespace conflicts
 525  4 Element element = (Element) parent;
 526  4 String currentURI = element.getLocalNamespaceURI(prefix);
 527  4 if (currentURI != null && !currentURI.equals(URI)) {
 528  2 throw new NamespaceConflictException(
 529    "New prefix " + prefix
 530    + "conflicts with existing namespace declaration"
 531    );
 532    }
 533    }
 534   
 535   
 536  2707 Verifier.checkAbsoluteURIReference(URI);
 537  2697 Verifier.checkNCName(prefix);
 538   
 539  2697 this.URI = URI;
 540  2697 this.prefix = prefix;
 541   
 542    }
 543   
 544   
 545    /**
 546    * <p>
 547    * Throws <code>IndexOutOfBoundsException</code>
 548    * because attributes do not have children.
 549    * </p>
 550    *
 551    * @param position the child to return
 552    *
 553    * @return nothing. This method always throws an exception.
 554    *
 555    * @throws IndexOutOfBoundsException because attributes do
 556    * not have children
 557    */
 558  1 public final Node getChild(int position) {
 559  1 throw new IndexOutOfBoundsException(
 560    "Attributes do not have children"
 561    );
 562    }
 563   
 564   
 565    /**
 566    * <p>
 567    * Returns 0 because attributes do not have children.
 568    * </p>
 569    *
 570    * @return zero
 571    */
 572  1 public final int getChildCount() {
 573  1 return 0;
 574    }
 575   
 576   
 577    /**
 578    * <p>
 579    * Creates a deep copy of this attribute that
 580    * is not attached to an element.
 581    * </p>
 582    *
 583    * @return a copy of this attribute
 584    *
 585    */
 586  292 public Node copy() {
 587  292 return new Attribute(this);
 588    }
 589   
 590   
 591    /**
 592    * <p>
 593    * Returns a string representation of the attribute
 594    * that is a well-formed XML attribute.
 595    * </p>
 596    *
 597    * @return a string containing the XML form of this attribute
 598    */
 599  1943 public final String toXML() {
 600    // It's a common belief that methods like this one should be
 601    // implemented using StringBuffers rather than String
 602    // concatenation for maximum performance. However,
 603    // disassembling the code shows that today's compilers are
 604    // smart enough to figure this out for themselves. The compiled
 605    // version of this class only uses a single StringBuffer. No
 606    // benefit would be gained by making the code more opaque here.
 607  1943 return getQualifiedName() + "=\"" + escapeText(value) + "\"";
 608    }
 609   
 610   
 611    /**
 612    * <p>
 613    * Returns a string representation of the attribute suitable for
 614    * debugging and diagnosis. However, this is not necessarily
 615    * a well-formed XML attribute.
 616    * </p>
 617    *
 618    * @return a non-XML string representation of this attribute
 619    *
 620    * @see java.lang.Object#toString()
 621    */
 622  6 public final String toString() {
 623  6 return "[" + getClass().getName() + ": "
 624    + getQualifiedName() + "=\""
 625    + Text.escapeLineBreaksAndTruncate(getValue()) + "\"]";
 626    }
 627   
 628   
 629  1943 private static String escapeText(String s) {
 630   
 631  1943 int length = s.length();
 632    // Give the string buffer enough room for a couple of escaped characters
 633  1943 StringBuffer result = new StringBuffer(length+12);
 634  1943 for (int i = 0; i < length; i++) {
 635  14314 char c = s.charAt(i);
 636  14314 switch (c) {
 637  1 case '\t':
 638  1 result.append("&#x09;");
 639  1 break;
 640  2 case '\n':
 641  2 result.append("&#x0A;");
 642  2 break;
 643  0 case 11:
 644    // impossible
 645  0 break;
 646  0 case 12:
 647    // impossible
 648  0 break;
 649  2 case '\r':
 650  2 result.append("&#x0D;");
 651  2 break;
 652  0 case 14:
 653    // impossible
 654  0 break;
 655  0 case 15:
 656    // impossible
 657  0 break;
 658  0 case 16:
 659    // impossible
 660  0 break;
 661  0 case 17:
 662    // impossible
 663  0 break;
 664  0 case 18:
 665    // impossible
 666  0 break;
 667  0 case 19:
 668    // impossible
 669  0 break;
 670  0 case 20:
 671    // impossible
 672  0 break;
 673  0 case 21:
 674    // impossible
 675  0 break;
 676  0 case 22:
 677    // impossible
 678  0 break;
 679  0 case 23:
 680    // impossible
 681  0 break;
 682  0 case 24:
 683    // impossible
 684  0 break;
 685  0 case 25:
 686    // impossible
 687  0 break;
 688  0 case 26:
 689    // impossible
 690  0 break;
 691  0 case 27:
 692    // impossible
 693  0 break;
 694  0 case 28:
 695    // impossible
 696  0 break;
 697  0 case 29:
 698    // impossible
 699  0 break;
 700  0 case 30:
 701    // impossible
 702  0 break;
 703  0 case 31:
 704    // impossible
 705  0 break;
 706  14 case ' ':
 707  14 result.append(' ');
 708  14 break;
 709  1 case '!':
 710  1 result.append('!');
 711  1 break;
 712  2 case '"':
 713  2 result.append("&quot;");
 714  2 break;
 715  1 case '#':
 716  1 result.append('#');
 717  1 break;
 718  1 case '$':
 719  1 result.append('$');
 720  1 break;
 721  1 case '%':
 722  1 result.append('%');
 723  1 break;
 724  1 case '&':
 725  1 result.append("&amp;");
 726  1 break;
 727  3 case '\'':
 728  3 result.append('\'');
 729  3 break;
 730  1 case '(':
 731  1 result.append('(');
 732  1 break;
 733  1 case ')':
 734  1 result.append(')');
 735  1 break;
 736  1 case '*':
 737  1 result.append('*');
 738  1 break;
 739  1 case '+':
 740  1 result.append('+');
 741  1 break;
 742  1 case ',':
 743  1 result.append(',');
 744  1 break;
 745  1 case '-':
 746  1 result.append('-');
 747  1 break;
 748  97 case '.':
 749  97 result.append('.');
 750  97 break;
 751  1 case '/':
 752  1 result.append('/');
 753  1 break;
 754  1092 case '0':
 755  1092 result.append('0');
 756  1092 break;
 757  428 case '1':
 758  428 result.append('1');
 759  428 break;
 760  147 case '2':
 761  147 result.append('2');
 762  147 break;
 763  193 case '3':
 764  193 result.append('3');
 765  193 break;
 766  189 case '4':
 767  189 result.append('4');
 768  189 break;
 769  98 case '5':
 770  98 result.append('5');
 771  98 break;
 772  126 case '6':
 773  126 result.append('6');
 774  126 break;
 775  88 case '7':
 776  88 result.append('7');
 777  88 break;
 778  86 case '8':
 779  86 result.append('8');
 780  86 break;
 781  86 case '9':
 782  86 result.append('9');
 783  86 break;
 784  1 case ':':
 785  1 result.append(':');
 786  1 break;
 787  1 case ';':
 788  1 result.append(';');
 789  1 break;
 790  1 case '<':
 791  1 result.append("&lt;");
 792  1 break;
 793  1 case '=':
 794  1 result.append('=');
 795  1 break;
 796  1 case '>':
 797  1 result.append("&gt;");
 798  1 break;
 799  11643 default:
 800  11643 result.append(c);
 801    }
 802    }
 803  1943 return result.toString();
 804   
 805    }
 806   
 807   
 808  143 boolean isAttribute() {
 809  143 return true;
 810    }
 811   
 812   
 813    /**
 814    * <p>
 815    * Uses the type-safe enumeration
 816    * design pattern to represent attribute types,
 817    * as specified by XML DTDs.
 818    * </p>
 819    *
 820    * <p>
 821    * XOM enforces well-formedness, but it does not enforce
 822    * validity. Thus it is possible for a single element to have
 823    * multiple ID type attributes, or ID type attributes
 824    * on different elements to have the same value,
 825    * or NMTOKEN type attributes that don't contain legal
 826    * XML name tokens, and so forth.
 827    * </p>
 828    *
 829    * @author Elliotte Rusty Harold
 830    * @version 1.0
 831    *
 832    */
 833    public static final class Type {
 834   
 835    /**
 836    * <p>
 837    * The type of attributes declared to have type CDATA
 838    * in the DTD. The most general attribute type.
 839    * All well-formed attribute values are valid for
 840    * attributes of type CDATA.
 841    * </p>
 842    */
 843    public static final Type CDATA = new Type(1);
 844   
 845    /**
 846    * <p>
 847    * The type of attributes declared to have type ID
 848    * in the DTD. In order to be valid, an ID type attribute
 849    * must contain an XML name which is unique among other
 850    * ID type attributes in the document.
 851    * Furthermore, each element may contain no more than one
 852    * ID type attribute. However, XOM does not enforce
 853    * such validity constraints.
 854    * </p>
 855    */
 856    public static final Type ID = new Type(2);
 857   
 858    /**
 859    * <p>
 860    * The type of attributes declared to have type IDREF
 861    * in the DTD. In order to be valid, an IDREF type attribute
 862    * must contain an XML name which is also the value of
 863    * ID type attribute of some element in the document.
 864    * However, XOM does not enforce such validity constraints.
 865    * </p>
 866    *
 867    */
 868    public static final Type IDREF = new Type(3);
 869   
 870    /**
 871    * <p>
 872    * The type of attributes declared to have type IDREFS
 873    * in the DTD. In order to be valid, an IDREFS type attribute
 874    * must contain a white space separated list of
 875    * XML names, each of which is also the value of
 876    * ID type attribute of some element in the document.
 877    * However, XOM does not enforce such validity constraints.
 878    * </p>
 879    *
 880    */
 881    public static final Type IDREFS = new Type(4);
 882   
 883    /**
 884    * <p>
 885    * The type of attributes declared to have type NMTOKEN
 886    * in the DTD. In order to be valid, a NMTOKEN type
 887    * attribute must contain a single XML name token. However,
 888    * XOM does not enforce such validity constraints.
 889    * </p>
 890    *
 891    */
 892    public static final Type NMTOKEN = new Type(5);
 893   
 894    /**
 895    * <p>
 896    * The type of attributes declared to have type NMTOKENS
 897    * in the DTD. In order to be valid, a NMTOKENS type attribute
 898    * must contain a white space separated list of XML name
 899    * tokens. However, XOM does not enforce such validity
 900    * constraints.
 901    * </p>
 902    *
 903    */
 904    public static final Type NMTOKENS = new Type(6);
 905   
 906   
 907    /**
 908    * <p>
 909    * The type of attributes declared to have type NOTATION
 910    * in the DTD. In order to be valid, a NOTATION type
 911    * attribute must contain the name of a notation declared
 912    * in the DTD. However, XOM does not enforce such
 913    * validity constraints.
 914    * </p>
 915    *
 916    */
 917    public static final Type NOTATION = new Type(7);
 918   
 919    /**
 920    * <p>
 921    * The type of attributes declared to have type ENTITY
 922    * in the DTD. In order to be valid, a ENTITY type attribute
 923    * must contain the name of an unparsed entity declared in
 924    * the DTD. However, XOM does not enforce such
 925    * validity constraints.
 926    * </p>
 927    *
 928    */
 929    public static final Type ENTITY = new Type(8);
 930   
 931    /**
 932    * <p>
 933    * The type of attributes declared to have type ENTITIES
 934    * in the DTD. In order to be valid, an ENTITIES type
 935    * attribute must contain a white space separated list of
 936    * names of unparsed entities declared in the DTD.
 937    * However, XOM does not enforce such validity constraints.
 938    * </p>
 939    *
 940    */
 941    public static final Type ENTITIES = new Type(9);
 942   
 943    /**
 944    * <p>
 945    * The type of attributes declared by an enumeration
 946    * in the DTD. In order to be valid, a enumeration type
 947    * attribute must contain exactly one of the names given
 948    * in the enumeration in the DTD. However, XOM does not
 949    * enforce such validity constraints.
 950    * </p>
 951    *
 952    * <p>
 953    * Most parsers report attributes of type enumeration as
 954    * having type NMTOKEN. In this case, XOM will not
 955    * distinguish NMTOKEN and enumerated attributes.
 956    * </p>
 957    *
 958    */
 959    public static final Type ENUMERATION = new Type(10);
 960   
 961    /**
 962    * <p>
 963    * The type of attributes not declared in the DTD.
 964    * This type only appears in invalid documents.
 965    * This is the default type for all attributes in
 966    * documents without DTDs.
 967    * </p>
 968    *
 969    * <p>
 970    * Most parsers report attributes of undeclared
 971    * type as having type CDATA. In this case, XOM
 972    * will not distinguish CDATA and undeclared attributes.
 973    * </p>
 974    */
 975    public static final Type UNDECLARED = new Type(0);
 976   
 977   
 978    /**
 979    * <p>
 980    * Returns the string name of this type as might
 981    * be used in a DTD; for example, "ID", "CDATA", etc.
 982    * </p>
 983    *
 984    * @return an XML string representation of this type
 985    */
 986  51 public String getName() {
 987   
 988  51 switch (type) {
 989  4 case 0:
 990  4 return "UNDECLARED";
 991  6 case 1:
 992  6 return "CDATA";
 993  5 case 2:
 994  5 return "ID";
 995  5 case 3:
 996  5 return "IDREF";
 997  5 case 4:
 998  5 return "IDREFS";
 999  5 case 5:
 1000  5 return "NMTOKEN";
 1001  5 case 6:
 1002  5 return "NMTOKENS";
 1003  5 case 7:
 1004  5 return "NOTATION";
 1005  5 case 8:
 1006  5 return "ENTITY";
 1007  5 case 9:
 1008  5 return "ENTITIES";
 1009  1 case 10:
 1010  1 return "ENUMERATION";
 1011  0 default:
 1012  0 throw new RuntimeException(
 1013    "Bug in XOM: unexpected attribute type: " + type);
 1014    }
 1015   
 1016    }
 1017   
 1018   
 1019    private final int type;
 1020   
 1021  253 private Type(int type) {
 1022  253 this.type = type;
 1023    }
 1024   
 1025   
 1026    /**
 1027    * <p>
 1028    * Returns a unique identifier for this type.
 1029    * </p>
 1030    *
 1031    * @return a unique identifier for this type
 1032    *
 1033    * @see java.lang.Object#hashCode()
 1034    */
 1035  67642 public int hashCode() {
 1036  67642 return this.type;
 1037    }
 1038   
 1039   
 1040    /**
 1041    * <p>
 1042    * Tests for type equality. This is only necessary,
 1043    * to handle the case where two <code>Type</code> objects
 1044    * are loaded by different class loaders.
 1045    * </p>
 1046    *
 1047    * @param o the object compared for equality to this type
 1048    *
 1049    * @return true if and only if <code>o</code> represents
 1050    * the same type
 1051    *
 1052    * @see java.lang.Object#equals(Object)
 1053    */
 1054  51025 public boolean equals(Object o) {
 1055   
 1056  17201 if (o == this) return true;
 1057  1 if (o == null) return false;
 1058  33822 if (this.hashCode() != o.hashCode()) return false;
 1059  1 if (!o.getClass().getName().equals("nu.xom.Attribute.Type")) {
 1060  1 return false;
 1061    }
 1062  0 return true;
 1063   
 1064    }
 1065   
 1066   
 1067    /**
 1068    * <p>
 1069    * Returns a string representation of the type
 1070    * suitable for debugging and diagnosis.
 1071    * </p>
 1072    *
 1073    * @return a non-XML string representation of this type
 1074    *
 1075    * @see java.lang.Object#toString()
 1076    */
 1077  41 public String toString() {
 1078   
 1079  41 StringBuffer result
 1080    = new StringBuffer("[Attribute.Type: ");
 1081  41 result.append(getName());
 1082  41 result.append(']');
 1083  41 return result.toString();
 1084   
 1085    }
 1086   
 1087   
 1088    }
 1089   
 1090   
 1091    }