001: /* Copyright 2002-2004 Elliotte Rusty Harold
002:
003: This library is free software; you can redistribute it and/or modify
004: it under the terms of version 2.1 of the GNU Lesser General Public
005: License as published by the Free Software Foundation.
006:
007: This library is distributed in the hope that it will be useful,
008: but WITHOUT ANY WARRANTY; without even the implied warranty of
009: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010: GNU Lesser General Public License for more details.
011:
012: You should have received a copy of the GNU Lesser General Public
013: License along with this library; if not, write to the
014: Free Software Foundation, Inc., 59 Temple Place, Suite 330,
015: Boston, MA 02111-1307 USA
016:
017: You can contact Elliotte Rusty Harold by sending e-mail to
018: elharo@metalab.unc.edu. Please include the word "XOM" in the
019: subject line. The XOM home page is located at http://www.xom.nu/
020: */
021:
022: package nu.xom;
023:
024: /**
025: * <p>
026: * This class represents an XML processing instruction.
027: * Each processing instruction has two key properties:
028: * </p>
029: *
030: * <ul>
031: * <li>The target, a non-colonized name</li>
032: * <li>The data, a string which does not contain the two character
033: * sequence <code>?></code>. The syntax of the data
034: * depends completely on the processing instruction.
035: * Other than forbidding <code>?></code>, XML defines
036: * no rules for processing instruction data.
037: * </li>
038: * </ul>
039: *
040: * @author Elliotte Rusty Harold
041: * @version 1.0
042: *
043: */
044: public class ProcessingInstruction extends Node {
045:
046: private String target;
047: private String data;
048:
049: /**
050: * <p>
051: * Create a processing instruction with a certain target and data.
052: * </p>
053: *
054: * @param target the target of the processing instruction
055: * @param data the processing instruction data
056: *
057: * @throws IllegalTargetException if the target is not a
058: * non-colonized name or is the string "xml" in any case
059: * @throws IllegalDataException if data contains "?>" or any
060: * other illegal characters
061: */
062: public ProcessingInstruction(String target, String data) {
063: _setTarget(target);
064: _setValue(data);
065: }
066:
067: /**
068: * <p>
069: * Create a copy of a processing instruction.
070: * </p>
071: *
072: * @param instruction the processing instruction to copy
073: *
074: */
075: public ProcessingInstruction(ProcessingInstruction instruction) {
076: this .target = instruction.target;
077: this .data = instruction.data;
078: }
079:
080: private ProcessingInstruction() {
081: }
082:
083: static ProcessingInstruction build(String target, String data) {
084: ProcessingInstruction result = new ProcessingInstruction();
085: result.target = target;
086: result.data = data;
087: return result;
088: }
089:
090: /**
091: * <p>
092: * Returns the processing instruction target.
093: * </p>
094: *
095: * @return the target
096: */
097: public final String getTarget() {
098: return target;
099: }
100:
101: /**
102: * <p>
103: * Sets the target.
104: * </p>
105: *
106: * @param target the new target
107: *
108: * @throws IllegalTargetException if the proposed target
109: * is not an XML 1.0 non-colonized name or is the string
110: * "xml" in any case
111: */
112: public void setTarget(String target) {
113: _setTarget(target);
114: }
115:
116: private void _setTarget(String target) {
117:
118: try {
119: Verifier.checkNCName(target);
120: } catch (IllegalNameException ex) {
121: IllegalTargetException tex = new IllegalTargetException(ex
122: .getMessage());
123: tex.setData(target);
124: throw tex;
125: }
126:
127: if (target.equalsIgnoreCase("xml")) {
128: IllegalTargetException tex = new IllegalTargetException(
129: target
130: + " is not a legal processing instruction target.");
131: tex.setData(target);
132: throw tex;
133: }
134:
135: this .target = target;
136:
137: }
138:
139: /**
140: * <p>
141: * Sets the data.
142: * </p>
143: *
144: * @param data the data to set
145: *
146: * @throws IllegalDataException if <code>data</code> is null
147: * or otherwise not legal XML processing instruction data
148: */
149: public void setValue(String data) {
150: _setValue(data);
151: }
152:
153: private void _setValue(String data) {
154:
155: Verifier.checkPCDATA(data);
156: if (data.length() != 0) {
157: if (data.indexOf("?>") >= 0) {
158: IllegalDataException ex = new IllegalDataException(
159: "Processing instruction data must not contain \"?>\"");
160: ex.setData(data);
161: throw ex;
162: }
163: if (data.indexOf('\r') >= 0) {
164: IllegalDataException ex = new IllegalDataException(
165: "Processing instruction data cannot contain carriage returns");
166: ex.setData(data);
167: throw ex;
168: }
169:
170: char first = data.charAt(0);
171: if (first == ' ' || first == '\n' || first == '\t') {
172: IllegalDataException ex = new IllegalDataException(
173: "Processing instruction data cannot contain "
174: + "leading white space");
175: ex.setData(data);
176: throw ex;
177: }
178: }
179: this .data = data;
180:
181: }
182:
183: /**
184: * <p>
185: * Returns the processing instruction data.
186: * </p>
187: *
188: * @return the data of the processing instruction
189: *
190: */
191: public final String getValue() {
192: return data;
193: }
194:
195: /**
196: * <p>
197: * Throws <code>IndexOutOfBoundsException</code> because
198: * processing instructions do not have children.
199: * </p>
200: *
201: * @return never returns because processing instructions do not
202: * have children; always throws an exception.
203: *
204: * @param position the index of the child node to return
205: *
206: * @throws IndexOutOfBoundsException because processing
207: * instructions do not have children
208: */
209: public final Node getChild(int position) {
210: throw new IndexOutOfBoundsException(
211: "LeafNodes do not have children");
212: }
213:
214: /**
215: * <p>
216: * Returns 0 because processing instructions do not have children.
217: * </p>
218: *
219: * @return zero
220: */
221: public final int getChildCount() {
222: return 0;
223: }
224:
225: /**
226: * <p>
227: * Returns the actual XML form of this processing instruction,
228: * such as might be copied and pasted from the original document.
229: * </p>
230: *
231: * @return an XML representation of this processing instruction
232: * as a <code>String</code>
233: */
234: public final String toXML() {
235:
236: StringBuffer result = new StringBuffer("<?");
237: result.append(target);
238: if (data.length() > 0) {
239: result.append(' ');
240: result.append(data);
241: }
242: result.append("?>");
243: return result.toString();
244:
245: }
246:
247: /**
248: * <p>
249: * Returns a deep copy of this processing instruction with no
250: * parent, that can be added to this document or a different
251: * one.
252: * </p>
253: *
254: * @return a copy of this <code>ProcessingInstruction</code>
255: * with no parent
256: */
257: public Node copy() {
258: return new ProcessingInstruction(target, data);
259: }
260:
261: boolean isProcessingInstruction() {
262: return true;
263: }
264:
265: /**
266: * <p>
267: * Returns a <code>String</code> representation
268: * of this processing instruction suitable for
269: * debugging and diagnosis. This is <em>not</em>
270: * the XML representation of this processing instruction.
271: * </p>
272: *
273: * @return a non-XML string representation of this
274: * <code>ProcessingInstruction</code>
275: */
276: public final String toString() {
277: return "[" + getClass().getName() + ": target=\"" + target
278: + "\"; data=\""
279: + Text.escapeLineBreaksAndTruncate(data) + "\"]";
280: }
281:
282: }
|