2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.xml.text.syntax.dom;
45
import javax.swing.text.BadLocationException;
48
import org.netbeans.modules.xml.text.syntax.*;
49
import org.netbeans.modules.xml.spi.dom.*;
50
import org.netbeans.editor.*;
53
* Represents tag syntax element. It also represent DOM <code>Element</code>.
54
* This duality means that one document element is represented by
55
* two DOM <code>Element</code> instances - one for start tag and one for
56
* end tag. This is hidden during document traversal but never relay on
57
* <code>equals</code>. The <code>equals</code> is used for syntax element
60
public abstract class Tag extends SyntaxNode implements Element, XMLTokenIDs {
62
protected NamedNodeMap domAttributes;
64
protected String name;
66
public Tag(XMLSyntaxSupport support, TokenItem from, int to, String name, Collection attribs) {
67
super( support, from,to );
71
public final short getNodeType() {
72
return Node.ELEMENT_NODE;
75
public final String getNodeName() {
79
public final String getTagName() {
84
* Create properly bound attributes and cache results.
85
* Parse attributes from first token.
87
public synchronized NamedNodeMap getAttributes() {
89
// cached results not implemented
90
if (domAttributes != null) return domAttributes;
92
Map map = new HashMap(3);
95
for (TokenItem next = first().getNext(); next != null; next = next.getNext()) {
96
TokenID id = next.getTokenID();
100
TokenItem attributeStart = next;
101
name = next.getImage();
102
while (next.getTokenID() != VALUE) {
103
next = next.getNext();
104
if (next == null) break SCAN_LOOP;
107
// fuzziness to relax minor tokenization changes
108
String image = next.getImage();
109
char test = image.charAt(0);
110
if (image.length() == 1) {
111
if (test == '"' || test == '\'') {
112
next = next.getNext();
116
if (next == null) break SCAN_LOOP;
117
value = next.getImage();
119
Object key = NamedNodeMapImpl.createKey(name);
120
map.put(key, new AttrImpl(support, attributeStart, this));
122
next = Util.skipAttributeValue(next, test);
123
if (next == null) break SCAN_LOOP;
124
} else if (id == WS) {
127
break; // end of element markup
131
// domAttributes = new NamedNodeMapImpl(map);
132
return new NamedNodeMapImpl(map);
135
public String getAttribute(String name) {
136
Attr attribute = getAttributeNode(name);
137
if (attribute == null) return null;
138
return attribute.getValue();
141
public final void setAttribute(String name, String value) {
142
NamedNodeMap attributes = getAttributes();
143
Node attr = attributes.getNamedItem(name);
145
attr.setNodeValue(value);
147
String stringToInsert = " " + name + "=" + '"' + value + '"';
149
// Get the document and lock it
150
BaseDocument doc = (BaseDocument)support.getDocument();
153
// An attribute with the name was not found for the element
154
// Let's add it to the end
155
int insertStart = offset + length - 1;
158
for (TokenItem next = first().getNext(); next != null; next = next.getNext()) {
159
TokenID id = next.getTokenID();
160
if (id == ARGUMENT) {
161
while (next.getTokenID() != VALUE) {
162
next = next.getNext();
163
if (next == null) break SCAN_LOOP;
166
if (next == null) break SCAN_LOOP;
168
String image = next.getImage();
169
char test = image.charAt(0);
171
while (next.getTokenID() == VALUE || next.getTokenID() == CHARACTER) {
172
String actualValue = Util.actualAttributeValue(image);
173
if (!actualValue.equals(image)) {
174
insertStart = next.getOffset() + actualValue.length();
177
next = next.getNext();
178
if (next == null) break SCAN_LOOP;
180
// Check if this is the last token in the element and set the
181
// insertStart if it is
182
image = next.getImage();
183
insertStart = next.getOffset();
184
if (image.length() > 0 && image.charAt(image.length() - 1) == '>') {
185
// The element is closing
186
insertStart += image.length() - 1;
187
if (image.length() > 1 && image.charAt(image.length() - 2) == '/') {
188
// We have a closed element at the form <blu/>
194
if (next == null) break SCAN_LOOP;
195
} else if (id == WS) {
198
break; // end of element markup
202
// Update the document
204
doc.insertString(insertStart, stringToInsert, null);
205
doc.invalidateSyntaxMarks();
206
} catch( BadLocationException e ) {
207
throw new DOMException(DOMException.INVALID_STATE_ERR , e.getMessage());
213
// Update this object's member variables
214
// retokenizeObject();
217
public final void removeAttribute(String name) {
218
throw new ROException();
221
public Attr getAttributeNode(String name) {
222
NamedNodeMap map = getAttributes();
223
Node node = map.getNamedItem(name);
227
public final Attr setAttributeNode(Attr attribute) {
228
throw new ROException();
231
public final Attr removeAttributeNode(Attr attribute) {
232
throw new ROException();
235
public NodeList getElementsByTagName(String name) {
236
throw new ROException();
240
* Returns previous sibling by locating pairing start tag
241
* and asking it for previous non-start tag SyntaxNode.
243
public Node getPreviousSibling() {
244
SyntaxNode prev = getStartTag();
245
if (prev == null) return null;
246
prev = findPrevious(prev);
247
if (prev instanceof StartTag) {
255
* Returns next sibling by locating pairing end tag
256
* and asking it for next non-end tag SyntaxNode.
258
public Node getNextSibling() {
259
SyntaxNode next = getEndTag();
260
if (next == null) return null;
261
next = findNext(next);
262
if (next instanceof EndTag) {
269
public Node getFirstChild() {
270
NodeList list = getChildNodes();
271
if (list.getLength() == 0) return null;
272
return getChildNodes().item(0);
275
public Node getLastChild() {
276
NodeList list = getChildNodes();
277
if (list.getLength() == 0) return null;
278
return list.item(list.getLength());
281
protected abstract Tag getStartTag();
283
protected abstract Tag getEndTag();
285
// public boolean equals(Object obj) {
286
// if ((obj instanceof Tag) == false) return false;
291
// unsupported DOM level 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293
public String getAttributeNS(String namespaceURI, String localName) {
294
throw new UOException();
297
public void setAttributeNS(String namespaceURI, String qualifiedName, String value) {
298
throw new UOException();
301
public void removeAttributeNS(String namespaceURI, String localName) {
302
throw new UOException();
305
public Attr getAttributeNodeNS(String namespaceURI, String localName) {
306
throw new UOException();
309
public Attr setAttributeNodeNS(Attr newAttr) {
310
throw new UOException();
313
public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
314
throw new UOException();
317
public boolean hasAttribute(String name) {
318
throw new UOException();
321
public boolean hasAttributeNS(String namespaceURI, String localName) {
322
throw new UOException();
325
// public void retokenizeObject() {
326
// // Update this object's member variables
328
// first = support.getTokenChain(offset, support.getDocument().getLength());
329
// } catch (BadLocationException e) {
330
// throw new DOMException(DOMException.INVALID_STATE_ERR , e.getMessage());
335
* We guarantee DOM Node equality by using Java Object's equals.
336
* It's potentionally dangerous as it mixes StartTags and EndTags.
337
* Never put this object into vector of so until you assumes DOM Node
340
* I would appreciate a methos at DOM that would define
341
* Node equals not Objevt's equals.
343
public final boolean equals(Object obj) {
344
if (obj == this) return true;
345
if (obj instanceof Tag) {
347
Tag t1 = tag.getStartTag();
348
Tag t2 = getStartTag();
349
if (t1 == null || t2 == null) return false;
350
return t1.superEquals(t2);
355
private boolean superEquals(Tag tag) {
356
return super.equals(tag);
360
* The same as for equals it's DOM node hashcode.
362
public final int hashCode() {
363
Tag tag = getStartTag();
364
if (tag == null || tag == this) {
365
return super.hashCode();
367
return tag.hashCode();