1 /* Copyright 2002-2004 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@ibiblio.org. 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 package nu.xom;
24 
25 /**
26  * <p>
27  *  This class represents an XML processing instruction.
28  *  Each processing instruction has two key properties:
29  * </p>
30  *
31  * <ul>
32  *   <li>The target, a non-colonized name</li>
33  *   <li>The data, a string which does not contain the two character
34  *       sequence <code>?&gt;</code>. The syntax of the data
35  *       depends completely on the processing instruction.
36  *       Other than forbidding <code>?&gt;</code>, XML defines
37  *       no rules for processing instruction data.
38  *   </li>
39  * </ul>
40  *
41  * @author Elliotte Rusty Harold
42  * @version 1.0
43  *
44  */
45 public class ProcessingInstruction extends Node {
46 
47     private String target;
48     private String data;
49 
50 
51     /**
52      * <p>
53      * Create a processing instruction with a certain target and data.
54      * </p>
55      *
56      * @param target the target of the processing instruction
57      * @param data the processing instruction data
58      *
59      * @throws IllegalTargetException if the target is not a
60      *    non-colonized name or is the string "xml" in any case
61      * @throws IllegalDataException if data contains "?>" or any
62      *    other illegal characters
63      */
ProcessingInstruction(String target, String data)64     public ProcessingInstruction(String target, String data) {
65         _setTarget(target);
66         _setValue(data);
67     }
68 
69 
70     /**
71      * <p>
72      * Create a copy of a processing instruction.
73      * </p>
74      *
75      * @param instruction the processing instruction to copy
76      *
77      */
ProcessingInstruction(ProcessingInstruction instruction)78     public ProcessingInstruction(ProcessingInstruction instruction) {
79         this.target = instruction.target;
80         this.data = instruction.data;
81     }
82 
83 
ProcessingInstruction()84     private ProcessingInstruction() {}
85 
build(String target, String data)86     static ProcessingInstruction build(String target, String data) {
87         ProcessingInstruction result = new ProcessingInstruction();
88         result.target = target;
89         result.data = data;
90         return result;
91     }
92 
93 
94     /**
95      * <p>
96      * Returns the processing instruction target.
97      * </p>
98      *
99      * @return the target
100      */
getTarget()101     public final String getTarget() {
102         return target;
103     }
104 
105 
106     /**
107      * <p>
108      * Sets the target.
109      * </p>
110      *
111      * @param target the new target
112      *
113      * @throws IllegalTargetException if the proposed target
114      *     is not an XML 1.0 non-colonized name or is the string
115      *     "xml" in any case
116      */
setTarget(String target)117     public void setTarget(String target) {
118         _setTarget(target);
119     }
120 
121 
_setTarget(String target)122     private void _setTarget(String target) {
123 
124         try {
125             Verifier.checkNCName(target);
126         }
127         catch (IllegalNameException ex) {
128             IllegalTargetException tex = new IllegalTargetException(ex.getMessage());
129             tex.setData(target);
130             throw tex;
131         }
132 
133         if (target.equalsIgnoreCase("xml")) {
134             IllegalTargetException tex = new IllegalTargetException(
135               target + " is not a legal processing instruction target."
136             );
137             tex.setData(target);
138             throw tex;
139         }
140 
141         this.target = target;
142 
143     }
144 
145 
146     /**
147      * <p>
148      * Sets the data.
149      * </p>
150      *
151      * @param data the data to set
152      *
153      * @throws IllegalDataException if <code>data</code> is null
154      *      or otherwise not legal XML processing instruction data
155      */
setValue(String data)156     public void setValue(String data) {
157         _setValue(data);
158     }
159 
160 
_setValue(String data)161     private void _setValue(String data) {
162 
163         Verifier.checkPCDATA(data);
164         if (data.length() != 0) {
165             if (data.indexOf("?>") >= 0) {
166                 IllegalDataException ex = new IllegalDataException(
167                   "Processing instruction data must not contain \"?>\""
168                 );
169                 ex.setData(data);
170                 throw ex;
171             }
172             if (data.indexOf('\r') >= 0) {
173                 IllegalDataException ex = new IllegalDataException(
174                   "Processing instruction data cannot contain carriage returns"
175                 );
176                 ex.setData(data);
177                 throw ex;
178             }
179 
180             char first = data.charAt(0);
181             if (first == ' ' || first == '\n' || first == '\t') {
182                 IllegalDataException ex =  new IllegalDataException(
183                   "Processing instruction data cannot contain " +
184                   "leading white space"
185                 );
186                 ex.setData(data);
187                 throw ex;
188             }
189         }
190         this.data = data;
191 
192     }
193 
194 
195     /**
196      * <p>
197      * Returns the processing instruction data.
198      * </p>
199      *
200      * @return the data of the processing instruction
201      *
202      */
getValue()203     public final String getValue() {
204         return data;
205     }
206 
207 
208     /**
209      * <p>
210      * Throws <code>IndexOutOfBoundsException</code> because
211      * processing instructions do not have children.
212      * </p>
213      *
214      * @return never returns because processing instructions do not
215      *     have children; always throws an exception.
216      *
217      * @param position the index of the child node to return
218      *
219      * @throws IndexOutOfBoundsException because processing
220      *     instructions do not have children
221      */
getChild(int position)222     public final Node getChild(int position) {
223         throw new IndexOutOfBoundsException(
224           "LeafNodes do not have children");
225     }
226 
227 
228     /**
229      * <p>
230      * Returns 0 because processing instructions do not have children.
231      * </p>
232      *
233      * @return zero
234      */
getChildCount()235     public final int getChildCount() {
236         return 0;
237     }
238 
239 
240     /**
241      * <p>
242      * Returns the actual XML form of this processing instruction,
243      * such as might be copied and pasted from the original document.
244      * </p>
245      *
246      * @return an XML representation of this processing instruction
247      *         as a <code>String</code>
248      */
toXML()249     public final String toXML() {
250 
251         StringBuffer result = new StringBuffer("<?");
252         result.append(target);
253         if (data.length() > 0) {
254             result.append(' ');
255             result.append(data);
256         }
257         result.append("?>");
258         return result.toString();
259 
260     }
261 
262 
263     /**
264      * <p>
265      * Returns a deep copy of this processing instruction with no
266      * parent, that can be added to this document or a different
267      * one.
268      * </p>
269      *
270      * @return a copy of this <code>ProcessingInstruction</code>
271      *         with no parent
272      */
copy()273     public Node copy() {
274         return new ProcessingInstruction(target, data);
275     }
276 
277 
isProcessingInstruction()278     boolean isProcessingInstruction() {
279         return true;
280     }
281 
282 
283     /**
284      * <p>
285      * Returns a <code>String</code> representation
286      * of this processing instruction suitable for
287      * debugging and diagnosis. This is <em>not</em>
288      * the XML representation of this processing instruction.
289      * </p>
290      *
291      * @return a non-XML string representation of this
292      *      <code>ProcessingInstruction</code>
293      */
toString()294     public final String toString() {
295         return "[" + getClass().getName() + ": target=\""
296          + target + "\"; data=\""
297          + Text.escapeLineBreaksAndTruncate(data) +"\"]";
298     }
299 
300 
301 }
302