001// Attributes2Impl.java - extended AttributesImpl
002// http://www.saxproject.org
003// Public Domain: no warranty.
004// $Id: Attributes2Impl.java,v 1.1 2004/12/23 22:38:42 mark Exp $
005
006package org.xml.sax.ext;
007
008import org.xml.sax.Attributes;
009import org.xml.sax.helpers.AttributesImpl;
010
011
012/**
013 * SAX2 extension helper for additional Attributes information,
014 * implementing the {@link Attributes2} interface.
015 *
016 * <blockquote>
017 * <em>This module, both source code and documentation, is in the
018 * Public Domain, and comes with <strong>NO WARRANTY</strong>.</em>
019 * </blockquote>
020 *
021 * <p>This is not part of core-only SAX2 distributions.</p>
022 *
023 * <p>The <em>specified</em> flag for each attribute will always
024 * be true, unless it has been set to false in the copy constructor
025 * or using {@link #setSpecified}.
026 * Similarly, the <em>declared</em> flag for each attribute will
027 * always be false, except for defaulted attributes (<em>specified</em>
028 * is false), non-CDATA attributes, or when it is set to true using
029 * {@link #setDeclared}.
030 * If you change an attribute's type by hand, you may need to modify
031 * its <em>declared</em> flag to match. 
032 * </p>
033 *
034 * @since SAX 2.0 (extensions 1.1 alpha)
035 * @author David Brownell
036 * @version TBS
037 */
038public class Attributes2Impl extends AttributesImpl implements Attributes2
039{
040    private boolean     declared [];
041    private boolean     specified [];
042
043
044    /**
045     * Construct a new, empty Attributes2Impl object.
046     */
047    public Attributes2Impl () { }
048
049
050    /**
051     * Copy an existing Attributes or Attributes2 object.
052     * If the object implements Attributes2, values of the
053     * <em>specified</em> and <em>declared</em> flags for each
054     * attribute are copied.
055     * Otherwise the flag values are defaulted to assume no DTD was used,
056     * unless there is evidence to the contrary (such as attributes with
057     * type other than CDATA, which must have been <em>declared</em>).
058     *
059     * <p>This constructor is especially useful inside a
060     * {@link org.xml.sax.ContentHandler#startElement startElement} event.</p>
061     *
062     * @param atts The existing Attributes object.
063     */
064    public Attributes2Impl (Attributes atts)
065    {
066        super (atts);
067    }
068
069
070    ////////////////////////////////////////////////////////////////////
071    // Implementation of Attributes2
072    ////////////////////////////////////////////////////////////////////
073
074
075    /**
076     * Returns the current value of the attribute's "declared" flag.
077     */
078    // javadoc mostly from interface
079    public boolean isDeclared (int index)
080    {
081        if (index < 0 || index >= getLength ())
082            throw new ArrayIndexOutOfBoundsException (
083                "No attribute at index: " + index);
084        return declared [index];
085    }
086
087
088    /**
089     * Returns the current value of the attribute's "declared" flag.
090     */
091    // javadoc mostly from interface
092    public boolean isDeclared (String uri, String localName)
093    {
094        int index = getIndex (uri, localName);
095
096        if (index < 0)
097            throw new IllegalArgumentException (
098                "No such attribute: local=" + localName
099                + ", namespace=" + uri);
100        return declared [index];
101    }
102
103
104    /**
105     * Returns the current value of the attribute's "declared" flag.
106     */
107    // javadoc mostly from interface
108    public boolean isDeclared (String qName)
109    {
110        int index = getIndex (qName);
111
112        if (index < 0)
113            throw new IllegalArgumentException (
114                "No such attribute: " + qName);
115        return declared [index];
116    }
117
118
119    /**
120     * Returns the current value of an attribute's "specified" flag.
121     *
122     * @param index The attribute index (zero-based).
123     * @return current flag value
124     * @exception java.lang.ArrayIndexOutOfBoundsException When the
125     *            supplied index does not identify an attribute.
126     */
127    public boolean isSpecified (int index)
128    {
129        if (index < 0 || index >= getLength ())
130            throw new ArrayIndexOutOfBoundsException (
131                "No attribute at index: " + index);
132        return specified [index];
133    }
134
135
136    /**
137     * Returns the current value of an attribute's "specified" flag.
138     *
139     * @param uri The Namespace URI, or the empty string if
140     *        the name has no Namespace URI.
141     * @param localName The attribute's local name.
142     * @return current flag value
143     * @exception java.lang.IllegalArgumentException When the
144     *            supplied names do not identify an attribute.
145     */
146    public boolean isSpecified (String uri, String localName)
147    {
148        int index = getIndex (uri, localName);
149
150        if (index < 0)
151            throw new IllegalArgumentException (
152                "No such attribute: local=" + localName
153                + ", namespace=" + uri);
154        return specified [index];
155    }
156
157
158    /**
159     * Returns the current value of an attribute's "specified" flag.
160     *
161     * @param qName The XML qualified (prefixed) name.
162     * @return current flag value
163     * @exception java.lang.IllegalArgumentException When the
164     *            supplied name does not identify an attribute.
165     */
166    public boolean isSpecified (String qName)
167    {
168        int index = getIndex (qName);
169
170        if (index < 0)
171            throw new IllegalArgumentException (
172                "No such attribute: " + qName);
173        return specified [index];
174    }
175
176
177    ////////////////////////////////////////////////////////////////////
178    // Manipulators
179    ////////////////////////////////////////////////////////////////////
180
181
182    /**
183     * Copy an entire Attributes object.  The "specified" flags are
184     * assigned as true, and "declared" flags as false (except when
185     * an attribute's type is not CDATA),
186     * unless the object is an Attributes2 object.
187     * In that case those flag values are all copied.
188     *
189     * @see AttributesImpl#setAttributes
190     */
191    public void setAttributes (Attributes atts)
192    {
193        int length = atts.getLength ();
194
195        super.setAttributes (atts);
196        declared = new boolean [length];
197        specified = new boolean [length];
198
199        if (atts instanceof Attributes2) {
200            Attributes2 a2 = (Attributes2) atts;
201            for (int i = 0; i < length; i++) {
202                declared [i] = a2.isDeclared (i);
203                specified [i] = a2.isSpecified (i);
204            }
205        } else {
206            for (int i = 0; i < length; i++) {
207                declared [i] = !"CDATA".equals (atts.getType (i));
208                specified [i] = true;
209            }
210        }
211    }
212
213
214    /**
215     * Add an attribute to the end of the list, setting its
216     * "specified" flag to true.  To set that flag's value
217     * to false, use {@link #setSpecified}.
218     *
219     * <p>Unless the attribute <em>type</em> is CDATA, this attribute
220     * is marked as being declared in the DTD.  To set that flag's value
221     * to true for CDATA attributes, use {@link #setDeclared}.
222     *
223     * @see AttributesImpl#addAttribute
224     */
225    public void addAttribute (String uri, String localName, String qName,
226                              String type, String value)
227    {
228        super.addAttribute (uri, localName, qName, type, value);
229
230        int length = getLength ();
231
232        if (length < specified.length) {
233            boolean     newFlags [];
234
235            newFlags = new boolean [length];
236            System.arraycopy (declared, 0, newFlags, 0, declared.length);
237            declared = newFlags;
238
239            newFlags = new boolean [length];
240            System.arraycopy (specified, 0, newFlags, 0, specified.length);
241            specified = newFlags;
242        }
243
244        specified [length - 1] = true;
245        declared [length - 1] = !"CDATA".equals (type);
246    }
247
248
249    // javadoc entirely from superclass
250    public void removeAttribute (int index)
251    {
252        int origMax = getLength () - 1;
253
254        super.removeAttribute (index);
255        if (index != origMax) {
256            System.arraycopy (declared, index + 1, declared, index,
257                    origMax - index);
258            System.arraycopy (specified, index + 1, specified, index,
259                    origMax - index);
260        }
261    }
262
263
264    /**
265     * Assign a value to the "declared" flag of a specific attribute.
266     * This is normally needed only for attributes of type CDATA,
267     * including attributes whose type is changed to or from CDATA.
268     *
269     * @param index The index of the attribute (zero-based).
270     * @param value The desired flag value.
271     * @exception java.lang.ArrayIndexOutOfBoundsException When the
272     *            supplied index does not identify an attribute.
273     * @see #setType
274     */
275    public void setDeclared (int index, boolean value)
276    {
277        if (index < 0 || index >= getLength ())
278            throw new ArrayIndexOutOfBoundsException (
279                "No attribute at index: " + index);
280        declared [index] = value;
281    }
282
283
284    /**
285     * Assign a value to the "specified" flag of a specific attribute.
286     * This is the only way this flag can be cleared, except clearing
287     * by initialization with the copy constructor.
288     *
289     * @param index The index of the attribute (zero-based).
290     * @param value The desired flag value.
291     * @exception java.lang.ArrayIndexOutOfBoundsException When the
292     *            supplied index does not identify an attribute.
293     */
294    public void setSpecified (int index, boolean value)
295    {
296        if (index < 0 || index >= getLength ())
297            throw new ArrayIndexOutOfBoundsException (
298                "No attribute at index: " + index);
299        specified [index] = value;
300    }
301}