View Javadoc
1   /*
2    * Copyright (c) 2002-2026 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * https://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package org.htmlunit.html;
16  
17  import java.io.PrintWriter;
18  
19  import org.htmlunit.SgmlPage;
20  import org.w3c.dom.DOMException;
21  import org.w3c.dom.ProcessingInstruction;
22  
23  /**
24   * Wrapper for the DOM node ProcessingInstruction.
25   * <p>
26   * <em>Note:</em> There is an architectural difference between the Java DOM layer and the JavaScript host layer:
27   * This Java class extends {@link DomNode} directly rather than a character data class, whereas the corresponding
28   * JavaScript host class inherits from {@link org.htmlunit.javascript.host.dom.CharacterData} to match browser behavior.
29   * </p>
30   *
31   * @author Ahmed Ashour
32   * @author Ronald Brill
33   */
34  public class DomProcessingInstruction extends DomNode implements ProcessingInstruction {
35  
36      private final String target_;
37      private String data_;
38  
39      /**
40       * Creates a new instance.
41       *
42       * @param page the Page that contains this element
43       * @param target the target
44       * @param data the data
45       */
46      public DomProcessingInstruction(final SgmlPage page, final String target, final String data) {
47          super(page);
48          target_ = target;
49          setData(data);
50      }
51  
52      /**
53       * {@inheritDoc}
54       * @return the node type constant, in this case {@link org.w3c.dom.Node#PROCESSING_INSTRUCTION_NODE}
55       */
56      @Override
57      public short getNodeType() {
58          return org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE;
59      }
60  
61      /**
62       * {@inheritDoc}
63       */
64      @Override
65      public String getNodeName() {
66          return target_;
67      }
68  
69      /**
70       * {@inheritDoc}
71       */
72      @Override
73      public String getTarget() {
74          return getNodeName();
75      }
76  
77      /**
78       * {@inheritDoc}
79       */
80      @Override
81      public String getData() {
82          return getNodeValue();
83      }
84  
85      /**
86       * {@inheritDoc}
87       */
88      @Override
89      public void setData(final String data) throws DOMException {
90          setNodeValue(data);
91      }
92  
93      /**
94       * Returns the number of characters in the character data.
95       * @return the number of characters
96       */
97      public int getLength() {
98          return data_ == null ? 0 : data_.length();
99      }
100 
101     /**
102      * Appends a string to character data.
103      * @param newData the string to be appended to the character data
104      */
105     public void appendData(final String newData) {
106         if (newData != null) {
107             setData(data_ == null ? newData : data_ + newData);
108         }
109     }
110 
111     /**
112      * Deletes characters from character data.
113      * @param offset the position of the first character to be deleted (can't be
114      *        less than zero)
115      * @param count the number of characters to be deleted, if less than zero
116      *        leaves the first offset chars
117      */
118     public void deleteData(final int offset, final int count) {
119         if (offset < 0) {
120             throw new IllegalArgumentException("Provided offset: " + offset + " is less than zero.");
121         }
122         if (data_ == null) {
123             return;
124         }
125 
126         final String data = data_.substring(0, Math.min(offset, data_.length()));
127         if (count >= 0) {
128             final int fromLeft = offset + count;
129             if (fromLeft < data_.length()) {
130                 setData(data + data_.substring(fromLeft));
131                 return;
132             }
133         }
134         setData(data);
135     }
136 
137     /**
138      * Inserts a string into character data.
139      * @param offset the position within the first character at which the string is to be inserted
140      * @param arg the string to insert
141      */
142     public void insertData(final int offset, final String arg) {
143         if (data_ == null) {
144             setData(arg);
145             return;
146         }
147 
148         setData(new StringBuilder(data_).insert(offset, arg).toString());
149     }
150 
151     /**
152      * Replaces characters of character data with a string.
153      * @param offset the position within the first character at which the string is to be replaced
154      * @param count the number of characters to be replaced
155      * @param arg the string that replaces the count characters beginning at the character at offset
156      */
157     public void replaceData(final int offset, final int count, final String arg) {
158         if (offset < 0 || count < 0) {
159             throw new IllegalArgumentException("offset: " + offset + " count: " + count);
160         }
161         if (data_ == null) {
162             setData(arg);
163             return;
164         }
165 
166         final int end = Math.min(offset + count, data_.length());
167         final StringBuilder sb = new StringBuilder(data_);
168         sb.replace(offset, end, arg != null ? arg : "");
169         setData(sb.toString());
170     }
171 
172     /**
173      * Extracts a substring from character data.
174      * @param offset the position of the first character to be extracted
175      * @param count the number of characters to be extracted
176      * @return a string that consists of the count characters of the character data starting
177      *         from the character at position offset
178      */
179     public String substringData(final int offset, final int count) {
180         if (data_ == null) {
181             throw new IllegalArgumentException("offset: " + offset + " count: " + count);
182         }
183         final int length = data_.length();
184         if (count < 0 || offset < 0 || offset > length - 1) {
185             throw new IllegalArgumentException("offset: " + offset + " count: " + count);
186         }
187 
188         final int tailIndex = Math.min(offset + count, length);
189         return data_.substring(offset, tailIndex);
190     }
191 
192     /**
193      * {@inheritDoc}
194      */
195     @Override
196     public void setNodeValue(final String value) {
197         data_ = value;
198     }
199 
200     /**
201      * {@inheritDoc}
202      */
203     @Override
204     public String getNodeValue() {
205         return data_;
206     }
207 
208     /**
209      * {@inheritDoc}
210      */
211     @Override
212     public void setTextContent(final String textContent) {
213         setNodeValue(textContent);
214     }
215 
216     /**
217      * {@inheritDoc}
218      */
219     @Override
220     protected boolean printXml(final String indent, final boolean indentBefore, final PrintWriter printWriter) {
221         printWriter.print("<?");
222         printWriter.print(getTarget());
223         printWriter.print(" ");
224         printWriter.print(getData());
225         printWriter.print("?>");
226         return true;
227     }
228 
229     /**
230      * {@inheritDoc}
231      */
232     @Override
233     public void setPrefix(final String prefix) {
234         // Empty.
235     }
236 }