Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/jdom/Attribute.java


1   /*--
2   
3    $Id: Attribute.java,v 1.52 2004/03/01 23:58:28 jhunter Exp $
4   
5    Copyright (C) 2000-2004 Jason Hunter & Brett McLaughlin.
6    All rights reserved.
7   
8    Redistribution and use in source and binary forms, with or without
9    modification, are permitted provided that the following conditions
10   are met:
11  
12   1. Redistributions of source code must retain the above copyright
13      notice, this list of conditions, and the following disclaimer.
14  
15   2. Redistributions in binary form must reproduce the above copyright
16      notice, this list of conditions, and the disclaimer that follows
17      these conditions in the documentation and/or other materials
18      provided with the distribution.
19  
20   3. The name "JDOM" must not be used to endorse or promote products
21      derived from this software without prior written permission.  For
22      written permission, please contact <request_AT_jdom_DOT_org>.
23  
24   4. Products derived from this software may not be called "JDOM", nor
25      may "JDOM" appear in their name, without prior written permission
26      from the JDOM Project Management <request_AT_jdom_DOT_org>.
27  
28   In addition, we request (but do not require) that you include in the
29   end-user documentation provided with the redistribution and/or in the
30   software itself an acknowledgement equivalent to the following:
31       "This product includes software developed by the
32        JDOM Project (http://www.jdom.org/)."
33   Alternatively, the acknowledgment may be graphical using the logos
34   available at http://www.jdom.org/images/logos.
35  
36   THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39   DISCLAIMED.  IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
40   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47   SUCH DAMAGE.
48  
49   This software consists of voluntary contributions made by many
50   individuals on behalf of the JDOM Project and was originally
51   created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
52   Brett McLaughlin <brett_AT_jdom_DOT_org>.  For more information
53   on the JDOM Project, please see <http://www.jdom.org/>.
54  
55   */
56  
57  package org.jdom;
58  
59  import java.io.*;
60  
61  /**
62   * An XML attribute. Methods allow the user to obtain the value of the attribute
63   * as well as namespace and type information.
64   *
65   * @version $Revision: 1.52 $, $Date: 2004/03/01 23:58:28 $
66   * @author  Brett McLaughlin
67   * @author  Jason Hunter
68   * @author  Elliotte Rusty Harold
69   * @author  Wesley Biggs
70   */
71  public class Attribute implements Serializable, Cloneable {
72  
73      private static final String CVS_ID =
74        "@(#) $RCSfile: Attribute.java,v $ $Revision: 1.52 $ $Date: 2004/03/01 23:58:28 $ $Name: jdom_1_0 $";
75  
76      /**
77       * Attribute type: the attribute has not been declared or type
78       * is unknown.
79       *
80       * @see #getAttributeType
81       */
82      public final static int UNDECLARED_TYPE = 0;
83  
84      /**
85       * Attribute type: the attribute value is a string.
86       *
87       * @see #getAttributeType
88       */
89      public final static int CDATA_TYPE = 1;
90  
91      /**
92       * Attribute type: the attribute value is a unique identifier.
93       *
94       * @see #getAttributeType
95       */
96      public final static int ID_TYPE = 2;
97  
98      /**
99       * Attribute type: the attribute value is a reference to a
100      * unique identifier.
101      *
102      * @see #getAttributeType
103      */
104     public final static int IDREF_TYPE = 3;
105 
106     /**
107      * Attribute type: the attribute value is a list of references to
108      * unique identifiers.
109      *
110      * @see #getAttributeType
111      */
112     public final static int IDREFS_TYPE = 4;
113 
114     /**
115      * Attribute type: the attribute value is the name of an entity.
116      *
117      * @see #getAttributeType
118      */
119     public final static int ENTITY_TYPE = 5;
120 
121     /**
122      * <p>
123      * Attribute type: the attribute value is a list of entity names.
124      * </p>
125      *
126      * @see #getAttributeType
127      */
128     public final static int ENTITIES_TYPE = 6;
129 
130     /**
131      * Attribute type: the attribute value is a name token.
132      * <p>
133      * According to SAX 2.0 specification, attributes of enumerated
134      * types should be reported as "NMTOKEN" by SAX parsers.  But the
135      * major parsers (Xerces and Crimson) provide specific values
136      * that permit to recognize them as {@link #ENUMERATED_TYPE}.
137      *
138      * @see #getAttributeType
139      */
140     public final static int NMTOKEN_TYPE = 7;
141 
142     /**
143      * Attribute type: the attribute value is a list of name tokens.
144      *
145      * @see #getAttributeType
146      */
147     public final static int NMTOKENS_TYPE = 8;
148 
149     /**
150      * Attribute type: the attribute value is the name of a notation.
151      *
152      * @see #getAttributeType
153      */
154     public final static int NOTATION_TYPE = 9;
155 
156     /**
157      * Attribute type: the attribute value is a name token from an
158      * enumeration.
159      *
160      * @see #getAttributeType
161      */
162     public final static int ENUMERATED_TYPE = 10;
163 
164     // Keep the old constant names for one beta cycle to help migration
165 
166 
167 
168     /** The local name of the <code>Attribute</code> */
169     protected String name;
170 
171     /** The <code>{@link Namespace}</code> of the <code>Attribute</code> */
172     protected transient Namespace namespace;
173 
174     /** The value of the <code>Attribute</code> */
175     protected String value;
176 
177     /** The type of the <code>Attribute</code> */
178     protected int type = UNDECLARED_TYPE;
179 
180     /** Parent element, or null if none */
181     protected Object parent;
182 
183     /**
184      * Default, no-args constructor for implementations to use if needed.
185      */
186     protected Attribute() {}
187 
188     /**
189      * This will create a new <code>Attribute</code> with the
190      * specified (local) name and value, and in the provided
191      * <code>{@link Namespace}</code>.
192      *
193      * @param name <code>String</code> name of <code>Attribute</code>.
194      * @param value <code>String</code> value for new attribute.
195      * @param namespace <code>Namespace</code> namespace for new attribute.
196      * @throws IllegalNameException if the given name is illegal as an
197      *         attribute name or if if the new namespace is the default
198      *         namespace. Attributes cannot be in a default namespace.
199      * @throws IllegalDataException if the given attribute value is
200      *         illegal character data (as determined by
201      *         {@link org.jdom.Verifier#checkCharacterData}).
202      */
203     public Attribute(String name, String value, Namespace namespace) {
204         setName(name);
205         setValue(value);
206         setNamespace(namespace);
207     }
208 
209     /**
210      * This will create a new <code>Attribute</code> with the
211      * specified (local) name, value, and type, and in the provided
212      * <code>{@link Namespace}</code>.
213      *
214      * @param name <code>String</code> name of <code>Attribute</code>.
215      * @param value <code>String</code> value for new attribute.
216      * @param type <code>int</code> type for new attribute.
217      * @param namespace <code>Namespace</code> namespace for new attribute.
218      * @throws IllegalNameException if the given name is illegal as an
219      *         attribute name or if if the new namespace is the default
220      *         namespace. Attributes cannot be in a default namespace.
221      * @throws IllegalDataException if the given attribute value is
222      *         illegal character data (as determined by
223      *         {@link org.jdom.Verifier#checkCharacterData}) or
224      *         if the given attribute type is not one of the
225      *         supported types.
226      */
227     public Attribute(String name, String value, int type, Namespace namespace) {
228         setName(name);
229         setValue(value);
230         setAttributeType(type);
231         setNamespace(namespace);
232     }
233 
234     /**
235      * This will create a new <code>Attribute</code> with the
236      * specified (local) name and value, and does not place
237      * the attribute in a <code>{@link Namespace}</code>.
238      * <p>
239      * <b>Note</b>: This actually explicitly puts the
240      * <code>Attribute</code> in the "empty" <code>Namespace</code>
241      * (<code>{@link Namespace#NO_NAMESPACE}</code>).
242      *
243      * @param name <code>String</code> name of <code>Attribute</code>.
244      * @param value <code>String</code> value for new attribute.
245      * @throws IllegalNameException if the given name is illegal as an
246      *         attribute name.
247      * @throws IllegalDataException if the given attribute value is
248      *         illegal character data (as determined by
249      *         {@link org.jdom.Verifier#checkCharacterData}).
250      */
251     public Attribute(String name, String value) {
252         this(name, value, UNDECLARED_TYPE, Namespace.NO_NAMESPACE);
253     }
254 
255     /**
256      * This will create a new <code>Attribute</code> with the
257      * specified (local) name, value and type, and does not place
258      * the attribute in a <code>{@link Namespace}</code>.
259      * <p>
260      * <b>Note</b>: This actually explicitly puts the
261      * <code>Attribute</code> in the "empty" <code>Namespace</code>
262      * (<code>{@link Namespace#NO_NAMESPACE}</code>).
263      *
264      * @param name <code>String</code> name of <code>Attribute</code>.
265      * @param value <code>String</code> value for new attribute.
266      * @param type <code>int</code> type for new attribute.
267      * @throws IllegalNameException if the given name is illegal as an
268      *         attribute name.
269      * @throws IllegalDataException if the given attribute value is
270      *         illegal character data (as determined by
271      *         {@link org.jdom.Verifier#checkCharacterData}) or
272      *         if the given attribute type is not one of the
273      *         supported types.
274      */
275     public Attribute(String name, String value, int type) {
276         this(name, value, type, Namespace.NO_NAMESPACE);
277     }
278 
279     /**
280      * This will return the parent of this <code>Attribute</code>.
281      * If there is no parent, then this returns <code>null</code>.
282      *
283      * @return parent of this <code>Attribute</code>
284      */
285     public Element getParent() {
286         return (Element) parent;
287     }
288 
289     /**
290      * This retrieves the owning <code>{@link Document}</code> for
291      * this Attribute, or null if not a currently a member of a
292      * <code>{@link Document}</code>.
293      *
294      * @return <code>Document</code> owning this Attribute, or null.
295      */
296     public Document getDocument() {
297         if (parent != null) {
298             return ((Element)parent).getDocument();
299         }
300         return null;
301     }
302 
303     /**
304      * This will set the parent of this <code>Attribute</code>.
305      *
306      * @param parent <code>Element</code> to be new parent.
307      * @return this <code>Attribute</code> modified.
308      */
309     protected Attribute setParent(Element parent) {
310         this.parent = parent;
311         return this;
312     }
313 
314     /**
315      * This detaches the <code>Attribute</code> from its parent, or does
316      * nothing if the <code>Attribute</code> has no parent.
317      *
318      * @return <code>Attribute</code> - this <code>Attribute</code> modified.
319      */
320     public Attribute detach() {
321         Element p = getParent();
322         if (p != null) {
323             p.removeAttribute(this.getName(), this.getNamespace());
324         }
325         return this;
326     }
327 
328     /**
329      * This will retrieve the local name of the
330      * <code>Attribute</code>. For any XML attribute
331      * which appears as
332      * <code>[namespacePrefix]:[attributeName]</code>,
333      * the local name of the attribute would be
334      * <code>[attributeName]</code>. When the attribute
335      * has no namespace, the local name is simply the attribute
336      * name.
337      * <p>
338      * To obtain the namespace prefix for this
339      * attribute, the
340      * <code>{@link #getNamespacePrefix()}</code>
341      * method should be used.
342      *
343      * @return <code>String</code> - name of this attribute,
344      *                               without any namespace prefix.
345      */
346     public String getName() {
347         return name;
348     }
349 
350     /**
351      * This sets the local name of the <code>Attribute</code>.
352      *
353      * @param name the new local name to set
354      * @return <code>Attribute</code> - the attribute modified.
355      * @throws IllegalNameException if the given name is illegal as an
356      *         attribute name.
357      */
358     public Attribute setName(String name) {
359         String reason;
360         if ((reason = Verifier.checkAttributeName(name)) != null) {
361             throw new IllegalNameException(name, "attribute", reason);
362         }
363         this.name = name;
364         return this;
365     }
366 
367     /**
368      * This will retrieve the qualified name of the <code>Attribute</code>.
369      * For any XML attribute whose name is
370      * <code>[namespacePrefix]:[elementName]</code>,
371      * the qualified name of the attribute would be
372      * everything (both namespace prefix and
373      * element name). When the attribute has no
374      * namespace, the qualified name is simply the attribute's
375      * local name.
376      * <p>
377      * To obtain the local name of the attribute, the
378      * <code>{@link #getName()}</code> method should be used.
379      * <p>
380      * To obtain the namespace prefix for this attribute,
381      * the <code>{@link #getNamespacePrefix()}</code>
382      * method should be used.
383      *
384      * @return <code>String</code> - full name for this element.
385      */
386     public String getQualifiedName() {
387         // Note: Any changes here should be reflected in
388         // XMLOutputter.printQualifiedName()
389         String prefix = namespace.getPrefix();
390         if ((prefix != null) && (!prefix.equals(""))) {
391             return new StringBuffer(prefix)
392                 .append(':')
393                 .append(getName())
394                 .toString();
395         } else {
396             return getName();
397         }
398     }
399 
400     /**
401      * This will retrieve the namespace prefix of the
402      * <code>Attribute</code>. For any XML attribute
403      * which appears as
404      * <code>[namespacePrefix]:[attributeName]</code>,
405      * the namespace prefix of the attribute would be
406      * <code>[namespacePrefix]</code>. When the attribute
407      * has no namespace, an empty <code>String</code> is returned.
408      *
409      * @return <code>String</code> - namespace prefix of this
410      *                               attribute.
411      */
412     public String getNamespacePrefix() {
413         return namespace.getPrefix();
414     }
415 
416     /**
417      * This returns the URI mapped to this <code>Attribute</code>'s
418      * prefix. If no mapping is found, an empty <code>String</code> is
419      * returned.
420      *
421      * @return <code>String</code> - namespace URI for this <code>Attribute</code>.
422      */
423     public String getNamespaceURI() {
424         return namespace.getURI();
425     }
426 
427     /**
428      * This will return this <code>Attribute</code>'s
429      * <code>{@link Namespace}</code>.
430      *
431      * @return <code>Namespace</code> - Namespace object for this <code>Attribute</code>
432      */
433     public Namespace getNamespace() {
434         return namespace;
435     }
436 
437     /**
438      * This sets this <code>Attribute</code>'s <code>{@link Namespace}</code>.
439      * If the provided namespace is null, the attribute will have no namespace.
440      * The namespace must have a prefix.
441      *
442      * @param namespace the new namespace
443      * @return <code>Element</code> - the element modified.
444      * @throws IllegalNameException if the new namespace is the default
445      *         namespace. Attributes cannot be in a default namespace.
446      */
447     public Attribute setNamespace(Namespace namespace) {
448         if (namespace == null) {
449             namespace = Namespace.NO_NAMESPACE;
450         }
451 
452         // Verify the attribute isn't trying to be in a default namespace
453         // Attributes can't be in a default namespace
454         if (namespace != Namespace.NO_NAMESPACE &&
455             namespace.getPrefix().equals("")) {
456             throw new IllegalNameException("", "attribute namespace",
457                 "An attribute namespace without a prefix can only be the " +
458                 "NO_NAMESPACE namespace");
459         }
460         this.namespace = namespace;
461         return this;
462     }
463     /**
464      * This will return the actual textual value of this
465      * <code>Attribute</code>.  This will include all text
466      * within the quotation marks.
467      *
468      * @return <code>String</code> - value for this attribute.
469      */
470     public String getValue() {
471         return value;
472     }
473 
474     /**
475      * This will set the value of the <code>Attribute</code>.
476      *
477      * @param value <code>String</code> value for the attribute.
478      * @return <code>Attribute</code> - this Attribute modified.
479      * @throws IllegalDataException if the given attribute value is
480      *         illegal character data (as determined by
481      *         {@link org.jdom.Verifier#checkCharacterData}).
482      */
483     public Attribute setValue(String value) {
484         String reason = null;
485         if ((reason = Verifier.checkCharacterData(value)) != null) {
486             throw new IllegalDataException(value, "attribute", reason);
487         }
488         this.value = value;
489         return this;
490     }
491 
492     /**
493      * This will return the actual declared type of this
494      * <code>Attribute</code>.
495      *
496      * @return <code>int</code> - type for this attribute.
497      */
498     public int getAttributeType() {
499         return type;
500     }
501 
502     /**
503      * This will set the type of the <code>Attribute</code>.
504      *
505      * @param type <code>int</code> type for the attribute.
506      * @return <code>Attribute</code> - this Attribute modified.
507      * @throws IllegalDataException if the given attribute type is
508      *         not one of the supported types.
509      */
510     public Attribute setAttributeType(int type) {
511         if ((type < UNDECLARED_TYPE) || (type > ENUMERATED_TYPE)) {
512             throw new IllegalDataException(String.valueOf(type),
513                                         "attribute", "Illegal attribute type");
514         }
515         this.type = type;
516         return this;
517     }
518 
519     /**
520      * This returns a <code>String</code> representation of the
521      * <code>Attribute</code>, suitable for debugging.
522      *
523      * @return <code>String</code> - information about the
524      *         <code>Attribute</code>
525      */
526     public String toString() {
527         return new StringBuffer()
528             .append("[Attribute: ")
529             .append(getQualifiedName())
530             .append("=\"")
531             .append(value)
532             .append("\"")
533             .append("]")
534             .toString();
535     }
536 
537     /**
538      * This tests for equality of this <code>Attribute</code> to the supplied
539      * <code>Object</code>.
540      *
541      * @param ob <code>Object</code> to compare to.
542      * @return <code>boolean</code> - whether the <code>Attribute</code> is
543      *         equal to the supplied <code>Object</code>.
544      */
545     public final boolean equals(Object ob) {
546         return (ob == this);
547     }
548 
549     /**
550      * This returns the hash code for this <code>Attribute</code>.
551      *
552      * @return <code>int</code> - hash code.
553      */
554     public final int hashCode() {
555         return super.hashCode();
556     }
557 
558     /**
559      * This will return a clone of this <code>Attribute</code>.
560      *
561      * @return <code>Object</code> - clone of this <code>Attribute</code>.
562      */
563     public Object clone() {
564         Attribute attribute = null;
565 
566         try {
567             attribute = (Attribute) super.clone();
568         } catch(CloneNotSupportedException ce) {
569             // Won't happen
570         }
571 
572         // Name, namespace, and value are references to imutable objects
573         // and are copied by super.clone() (aka Object.clone())
574 
575         // super.clone() copies reference to set parent to null
576         attribute.parent = null;
577         return attribute;
578     }
579 
580     /////////////////////////////////////////////////////////////////
581     // Convenience Methods below here
582     /////////////////////////////////////////////////////////////////
583 
584     /**
585      * This gets the value of the attribute, in
586      * <code>int</code> form, and if no conversion
587      * can occur, throws a
588      * <code>{@link DataConversionException}</code>
589      *
590      * @return <code>int</code> value of attribute.
591      * @throws DataConversionException when conversion fails.
592      */
593     public int getIntValue() throws DataConversionException {
594         try {
595             return Integer.parseInt(value.trim());
596         } catch (NumberFormatException e) {
597             throw new DataConversionException(name, "int");
598         }
599     }
600 
601     /**
602      * This gets the value of the attribute, in
603      * <code>long</code> form, and if no conversion
604      * can occur, throws a
605      * <code>{@link DataConversionException}</code>
606      *
607      * @return <code>long</code> value of attribute.
608      * @throws DataConversionException when conversion fails.
609      */
610     public long getLongValue() throws DataConversionException {
611         try {
612             return Long.parseLong(value.trim());
613         } catch (NumberFormatException e) {
614             throw new DataConversionException(name, "long");
615         }
616     }
617 
618     /**
619      * This gets the value of the attribute, in
620      * <code>float</code> form, and if no conversion
621      * can occur, throws a
622      * <code>{@link DataConversionException}</code>
623      *
624      * @return <code>float</code> value of attribute.
625      * @throws DataConversionException when conversion fails.
626      */
627     public float getFloatValue() throws DataConversionException {
628         try {
629             // Avoid Float.parseFloat() to support JDK 1.1
630             return Float.valueOf(value.trim()).floatValue();
631         } catch (NumberFormatException e) {
632             throw new DataConversionException(name, "float");
633         }
634     }
635 
636     /**
637      * This gets the value of the attribute, in
638      * <code>double</code> form, and if no conversion
639      * can occur, throws a
640      * <code>{@link DataConversionException}</code>
641      *
642      * @return <code>double</code> value of attribute.
643      * @throws DataConversionException when conversion fails.
644      */
645     public double getDoubleValue() throws DataConversionException {
646         try {
647             // Avoid Double.parseDouble() to support JDK 1.1
648             return Double.valueOf(value.trim()).doubleValue();
649         } catch (NumberFormatException e) {
650             throw new DataConversionException(name, "double");
651         }
652     }
653 
654     /**
655      * This gets the effective boolean value of the attribute, or throws a
656      * <code>{@link DataConversionException}</code> if a conversion can't be
657      * performed.  True values are: "true", "on", "1", and "yes".  False
658      * values are: "false", "off", "0", and "no".  Values are trimmed before
659      * comparison.  Values other than those listed here throw the exception.
660      *
661      * @return <code>boolean</code> value of attribute.
662      * @throws DataConversionException when conversion fails.
663      */
664     public boolean getBooleanValue() throws DataConversionException {
665         String valueTrim = value.trim();
666         if ((valueTrim.equalsIgnoreCase("true")) ||
667             (valueTrim.equalsIgnoreCase("on")) ||
668             (valueTrim.equalsIgnoreCase("1")) ||
669             (valueTrim.equalsIgnoreCase("yes"))) {
670             return true;
671         } else if ((valueTrim.equalsIgnoreCase("false")) ||
672                    (valueTrim.equalsIgnoreCase("off")) ||
673                    (valueTrim.equalsIgnoreCase("0")) ||
674                    (valueTrim.equalsIgnoreCase("no"))) {
675             return false;
676         } else {
677             throw new DataConversionException(name, "boolean");
678         }
679     }
680 
681     // Support a custom Namespace serialization so no two namespace
682     // object instances may exist for the same prefix/uri pair
683     private void writeObject(ObjectOutputStream out) throws IOException {
684 
685         out.defaultWriteObject();
686 
687         // We use writeObject() and not writeUTF() to minimize space
688         // This allows for writing pointers to already written strings
689         out.writeObject(namespace.getPrefix());
690         out.writeObject(namespace.getURI());
691     }
692 
693     private void readObject(ObjectInputStream in)
694         throws IOException, ClassNotFoundException {
695 
696         in.defaultReadObject();
697 
698         namespace = Namespace.getNamespace(
699             (String)in.readObject(), (String)in.readObject());
700     }
701 }