KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jdom > ProcessingInstruction


1 /*--
2
3  $Id: ProcessingInstruction.java,v 1.46 2004/02/27 11:32:57 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.util.*;
60
61 /**
62  * An XML processing instruction. Methods allow the user to obtain the target of
63  * the PI as well as its data. The data can always be accessed as a String or,
64  * if the data appears akin to an attribute list, can be retrieved as name/value
65  * pairs.
66  *
67  * @version $Revision: 1.46 $, $Date: 2004/02/27 11:32:57 $
68  * @author Brett McLaughlin
69  * @author Jason Hunter
70  * @author Steven Gould
71  */

72
73 public class ProcessingInstruction extends Content {
74
75     private static final String JavaDoc CVS_ID =
76       "@(#) $RCSfile: ProcessingInstruction.java,v $ $Revision: 1.46 $ $Date: 2004/02/27 11:32:57 $ $Name: $";
77
78     /** The target of the PI */
79     protected String JavaDoc target;
80
81     /** The data for the PI as a String */
82     protected String JavaDoc rawData;
83
84     /** The data for the PI in name/value pairs */
85     protected Map mapData;
86
87     /**
88      * Default, no-args constructor for implementations
89      * to use if needed.
90      */

91     protected ProcessingInstruction() { }
92
93     /**
94      * This will create a new <code>ProcessingInstruction</code>
95      * with the specified target and data.
96      *
97      * @param target <code>String</code> target of PI.
98      * @param data <code>Map</code> data for PI, in
99      * name/value pairs
100      * @throws IllegalTargetException if the given target is illegal
101      * as a processing instruction name.
102      */

103     public ProcessingInstruction(String JavaDoc target, Map data) {
104         setTarget(target);
105         setData(data);
106     }
107
108     /**
109      * This will create a new <code>ProcessingInstruction</code>
110      * with the specified target and data.
111      *
112      * @param target <code>String</code> target of PI.
113      * @param data <code>String</code> data for PI.
114      * @throws IllegalTargetException if the given target is illegal
115      * as a processing instruction name.
116      */

117     public ProcessingInstruction(String JavaDoc target, String JavaDoc data) {
118         setTarget(target);
119         setData(data);
120     }
121
122     /**
123      * This will set the target for the PI.
124      *
125      * @param newTarget <code>String</code> new target of PI.
126      * @return <code>ProcessingInstruction</code> - this PI modified.
127      */

128     public ProcessingInstruction setTarget(String JavaDoc newTarget) {
129         String JavaDoc reason;
130         if ((reason = Verifier.checkProcessingInstructionTarget(newTarget))
131                                     != null) {
132             throw new IllegalTargetException(newTarget, reason);
133         }
134
135         target = newTarget;
136         return this;
137     }
138
139     /**
140      * Returns the XPath 1.0 string value of this element, which is the
141      * data of this PI.
142      *
143      * @return the data of this PI
144      */

145     public String JavaDoc getValue() {
146         return rawData;
147     }
148
149
150     /**
151      * This will retrieve the target of the PI.
152      *
153      * @return <code>String</code> - target of PI.
154      */

155     public String JavaDoc getTarget() {
156         return target;
157     }
158
159     /**
160      * This will return the raw data from all instructions.
161      *
162      * @return <code>String</code> - data of PI.
163      */

164     public String JavaDoc getData() {
165         return rawData;
166     }
167
168     /**
169      * This will return a <code>List</code> containing the names of the
170      * "attribute" style pieces of name/value pairs in this PI's data.
171      *
172      * @return <code>List</code> - the <code>List</code> containing the
173      * "attribute" names.
174      */

175     public List getPseudoAttributeNames() {
176       Set mapDataSet = mapData.entrySet();
177       List nameList = new ArrayList();
178       for (Iterator i = mapDataSet.iterator(); i.hasNext();) {
179          String JavaDoc wholeSet = (i.next()).toString();
180          String JavaDoc attrName = wholeSet.substring(0,(wholeSet.indexOf("=")));
181          nameList.add(attrName);
182       }
183       return nameList;
184     }
185
186     /**
187      * This will set the raw data for the PI.
188      *
189      * @param data <code>String</code> data of PI.
190      * @return <code>ProcessingInstruction</code> - this PI modified.
191      */

192     public ProcessingInstruction setData(String JavaDoc data) {
193         String JavaDoc reason = Verifier.checkProcessingInstructionData(data);
194         if (reason != null) {
195             throw new IllegalDataException(data, reason);
196         }
197
198         this.rawData = data;
199         this.mapData = parseData(data);
200         return this;
201     }
202
203     /**
204      * This will set the name/value pairs within the passed
205      * <code>Map</code> as the pairs for the data of
206      * this PI. The keys should be the pair name
207      * and the values should be the pair values.
208      *
209      * @param data new map data to use
210      * @return <code>ProcessingInstruction</code> - modified PI.
211      */

212     public ProcessingInstruction setData(Map data) {
213         String JavaDoc temp = toString(data);
214
215         String JavaDoc reason = Verifier.checkProcessingInstructionData(temp);
216         if (reason != null) {
217             throw new IllegalDataException(temp, reason);
218         }
219
220         this.rawData = temp;
221         this.mapData = data;
222         return this;
223     }
224
225
226     /**
227      * This will return the value for a specific
228      * name/value pair on the PI. If no such pair is
229      * found for this PI, null is returned.
230      *
231      * @param name <code>String</code> name of name/value pair
232      * to lookup value for.
233      * @return <code>String</code> - value of name/value pair.
234      */

235     public String JavaDoc getPseudoAttributeValue(String JavaDoc name) {
236         return (String JavaDoc)mapData.get(name);
237     }
238
239     /**
240      * This will set a pseudo attribute with the given name and value.
241      * If the PI data is not already in a pseudo-attribute format, this will
242      * replace the existing data.
243      *
244      * @param name <code>String</code> name of pair.
245      * @param value <code>String</code> value for pair.
246      * @return <code>ProcessingInstruction</code> this PI modified.
247      */

248     public ProcessingInstruction setPseudoAttribute(String JavaDoc name, String JavaDoc value) {
249         String JavaDoc reason = Verifier.checkProcessingInstructionData(name);
250         if (reason != null) {
251             throw new IllegalDataException(name, reason);
252         }
253
254         reason = Verifier.checkProcessingInstructionData(value);
255         if (reason != null) {
256             throw new IllegalDataException(value, reason);
257         }
258
259         this.mapData.put(name, value);
260         this.rawData = toString(mapData);
261         return this;
262     }
263
264
265     /**
266      * This will remove the pseudo attribute with the specified name.
267      *
268      * @param name name of pseudo attribute to remove
269      * @return <code>boolean</code> - whether the requested
270      * instruction was removed.
271      */

272     public boolean removePseudoAttribute(String JavaDoc name) {
273         if ((mapData.remove(name)) != null) {
274             rawData = toString(mapData);
275             return true;
276         }
277
278         return false;
279     }
280
281     /**
282      * This will convert the Map to a string representation.
283      *
284      * @param mapData <code>Map</code> PI data to convert
285      * @return a string representation of the Map as appropriate for a PI
286      */

287     private String JavaDoc toString(Map mapData) {
288         StringBuffer JavaDoc rawData = new StringBuffer JavaDoc();
289
290         Iterator i = mapData.keySet().iterator();
291         while (i.hasNext()) {
292             String JavaDoc name = (String JavaDoc)i.next();
293             String JavaDoc value = (String JavaDoc)mapData.get(name);
294             rawData.append(name)
295                    .append("=\"")
296                    .append(value)
297                    .append("\" ");
298         }
299         // Remove last space, if we did any appending
300
if (rawData.length() > 0) {
301             rawData.setLength(rawData.length() - 1);
302         }
303
304         return rawData.toString();
305     }
306
307     /**
308      * This will parse and load the instructions for the PI.
309      * This is separated to allow it to occur once and then be reused.
310      */

311     private Map parseData(String JavaDoc rawData) {
312         // The parsing here is done largely "by hand" which means the code
313
// gets a little tricky/messy. The following conditions should
314
// now be handled correctly:
315
// <?pi HREF="http://hi/a=b"?> Reads OK
316
// <?pi href = 'http://hi/a=b' ?> Reads OK
317
// <?pi href\t = \t'http://hi/a=b'?> Reads OK
318
// <?pi href = "http://hi/a=b"?> Reads OK
319
// <?pi?> Empty Map
320
// <?pi id=22?> Empty Map
321
// <?pi id='22?> Empty Map
322

323         Map data = new HashMap();
324
325         // System.out.println("rawData: " + rawData);
326

327         // The inputData variable holds the part of rawData left to parse
328
String JavaDoc inputData = rawData.trim();
329
330         // Iterate through the remaining inputData string
331
while (!inputData.trim().equals("")) {
332             //System.out.println("parseData() looking at: " + inputData);
333

334             // Search for "name =", "name=" or "name1 name2..."
335
String JavaDoc name = "";
336             String JavaDoc value = "";
337             int startName = 0;
338             char previousChar = inputData.charAt(startName);
339             int pos = 1;
340             for (; pos<inputData.length(); pos++) {
341                 char currentChar = inputData.charAt(pos);
342                 if (currentChar == '=') {
343                     name = inputData.substring(startName, pos).trim();
344                     // Get the boundaries on the quoted string
345
// We use boundaries so we know where to start next
346
int[] bounds = extractQuotedString(
347                                      inputData.substring(pos+1));
348                     // A null value means a parse error and we return empty!
349
if (bounds == null) {
350                         return new HashMap();
351                     }
352                     value = inputData.substring(bounds[0]+pos+1,
353                                                 bounds[1]+pos+1);
354                     pos += bounds[1] + 1; // skip past value
355
break;
356                 }
357                 else if (Character.isWhitespace(previousChar)
358                           && !Character.isWhitespace(currentChar)) {
359                     startName = pos;
360                 }
361
362                 previousChar = currentChar;
363             }
364
365             // Remove the first pos characters; they have been processed
366
inputData = inputData.substring(pos);
367
368             // System.out.println("Extracted (name, value) pair: ("
369
// + name + ", '" + value+"')");
370

371             // If both a name and a value have been found, then add
372
// them to the data Map
373
if (name.length() > 0 && value != null) {
374                 //if (data.containsKey(name)) {
375
// A repeat, that's a parse error, so return a null map
376
//return new HashMap();
377
//}
378
//else {
379
data.put(name, value);
380                 //}
381
}
382         }
383
384         return data;
385     }
386
387     /**
388      * This is a helper routine, only used by parseData, to extract a
389      * quoted String from the input parameter, rawData. A quoted string
390      * can use either single or double quotes, but they must match up.
391      * A singly quoted string can contain an unbalanced amount of double
392      * quotes, or vice versa. For example, the String "JDOM's the best"
393      * is legal as is 'JDOM"s the best'.
394      *
395      * @param rawData the input string from which a quoted string is to
396      * be extracted.
397      * @return the first quoted string encountered in the input data. If
398      * no quoted string is found, then the empty string, "", is
399      * returned.
400      * @see #parseData
401      */

402     private static int[] extractQuotedString(String JavaDoc rawData) {
403         // Remembers whether we're actually in a quoted string yet
404
boolean inQuotes = false;
405
406         // Remembers which type of quoted string we're in
407
char quoteChar = '"';
408
409         // Stores the position of the first character inside
410
// the quoted string (i.e. the start of the return string)
411
int start = 0;
412
413         // Iterate through the input string looking for the start
414
// and end of the quoted string
415
for (int pos=0; pos < rawData.length(); pos++) {
416             char currentChar = rawData.charAt(pos);
417             if (currentChar=='"' || currentChar=='\'') {
418                 if (!inQuotes) {
419                     // We're entering a quoted string
420
quoteChar = currentChar;
421                     inQuotes = true;
422                     start = pos+1;
423                 }
424                 else if (quoteChar == currentChar) {
425                     // We're leaving a quoted string
426
inQuotes = false;
427                     return new int[] { start, pos };
428                 }
429                 // Otherwise we've encountered a quote
430
// inside a quote, so just continue
431
}
432         }
433
434         return null;
435     }
436
437     /**
438      * This returns a <code>String</code> representation of the
439      * <code>ProcessingInstruction</code>, suitable for debugging. If the XML
440      * representation of the <code>ProcessingInstruction</code> is desired,
441      * {@link org.jdom.output.XMLOutputter#outputString(ProcessingInstruction)}
442      * should be used.
443      *
444      * @return <code>String</code> - information about the
445      * <code>ProcessingInstruction</code>
446      */

447     public String JavaDoc toString() {
448         return new StringBuffer JavaDoc()
449             .append("[ProcessingInstruction: ")
450             .append(new org.jdom.output.XMLOutputter().outputString(this))
451             .append("]")
452             .toString();
453     }
454
455     /**
456      * This will return a clone of this <code>ProcessingInstruction</code>.
457      *
458      * @return <code>Object</code> - clone of this
459      * <code>ProcessingInstruction</code>.
460      */

461     public Object JavaDoc clone() {
462         ProcessingInstruction pi = (ProcessingInstruction) super.clone();
463
464         // target and rawdata are immutable and references copied by
465
// Object.clone()
466

467         // Create a new Map object for the clone (since Map isn't Cloneable)
468
if (mapData != null) {
469             pi.mapData = parseData(rawData);
470         }
471         return pi;
472     }
473 }
474
Popular Tags