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>?></code>. The syntax of the data 35 * depends completely on the processing instruction. 36 * Other than forbidding <code>?></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