~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to xml/text-edit/src/org/netbeans/modules/xml/text/syntax/dom/Tag.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
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]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
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.
 
29
 *
 
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.
 
40
 */
 
41
 
 
42
package org.netbeans.modules.xml.text.syntax.dom;
 
43
 
 
44
import java.util.*;
 
45
import javax.swing.text.BadLocationException;
 
46
 
 
47
import org.w3c.dom.*;
 
48
import org.netbeans.modules.xml.text.syntax.*;
 
49
import org.netbeans.modules.xml.spi.dom.*;
 
50
import org.netbeans.editor.*;
 
51
 
 
52
/**
 
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
 
58
 * purposes.
 
59
 */
 
60
public abstract class Tag extends SyntaxNode implements Element, XMLTokenIDs {
 
61
    
 
62
    protected NamedNodeMap domAttributes;
 
63
    
 
64
    protected String name;
 
65
    
 
66
    public Tag(XMLSyntaxSupport support, TokenItem from, int to, String name, Collection attribs) {
 
67
        super( support, from,to );
 
68
        this.name = name;
 
69
    }
 
70
    
 
71
    public final short getNodeType() {
 
72
        return Node.ELEMENT_NODE;
 
73
    }
 
74
    
 
75
    public final String getNodeName() {
 
76
        return getTagName();
 
77
    }
 
78
    
 
79
    public final String getTagName() {
 
80
        return name;
 
81
    }
 
82
    
 
83
    /**
 
84
     * Create properly bound attributes and cache results.
 
85
     * Parse attributes from first token.
 
86
     */
 
87
    public synchronized NamedNodeMap getAttributes() {
 
88
        
 
89
        // cached results not implemented
 
90
        if (domAttributes != null) return domAttributes;
 
91
        
 
92
        Map map = new HashMap(3);
 
93
        
 
94
        SCAN_LOOP:
 
95
            for (TokenItem next = first().getNext(); next != null; next = next.getNext()) {
 
96
                TokenID id = next.getTokenID();
 
97
                String name;
 
98
                String value;
 
99
                if (id == ARGUMENT) {
 
100
                    TokenItem attributeStart = next;
 
101
                    name = next.getImage();
 
102
                    while (next.getTokenID() != VALUE) {
 
103
                        next = next.getNext();
 
104
                        if (next == null) break SCAN_LOOP;
 
105
                    }
 
106
                    
 
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();
 
113
                        }
 
114
                    }
 
115
                    
 
116
                    if (next == null) break SCAN_LOOP;
 
117
                    value = next.getImage();
 
118
                    
 
119
                    Object key = NamedNodeMapImpl.createKey(name);
 
120
                    map.put(key, new AttrImpl(support, attributeStart, this));
 
121
                    
 
122
                    next = Util.skipAttributeValue(next, test);
 
123
                    if (next == null) break SCAN_LOOP;
 
124
                } else if (id == WS) {
 
125
                    // just skip
 
126
                } else {
 
127
                    break; // end of element markup
 
128
                }
 
129
            }
 
130
            
 
131
            // domAttributes = new NamedNodeMapImpl(map);
 
132
            return new NamedNodeMapImpl(map);
 
133
    }
 
134
    
 
135
    public String getAttribute(String name) {
 
136
        Attr attribute = getAttributeNode(name);
 
137
        if (attribute == null) return null;
 
138
        return attribute.getValue();
 
139
    }
 
140
    
 
141
    public final void setAttribute(String name, String value) {
 
142
        NamedNodeMap attributes = getAttributes();
 
143
        Node attr = attributes.getNamedItem(name);
 
144
        if (attr != null) {
 
145
            attr.setNodeValue(value);
 
146
        } else {
 
147
            String stringToInsert = " " + name + "=" + '"' + value + '"';
 
148
            
 
149
            // Get the document and lock it
 
150
            BaseDocument doc = (BaseDocument)support.getDocument();
 
151
            doc.atomicLock();
 
152
            
 
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;
 
156
            
 
157
            SCAN_LOOP:
 
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;
 
164
                        }
 
165
                        
 
166
                        if (next == null) break SCAN_LOOP;
 
167
                        
 
168
                        String image = next.getImage();
 
169
                        char test = image.charAt(0);
 
170
                        
 
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();
 
175
                                break SCAN_LOOP;
 
176
                            }
 
177
                            next = next.getNext();
 
178
                            if (next == null) break SCAN_LOOP;
 
179
                            
 
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/>
 
189
                                    insertStart--;
 
190
                                }
 
191
                            }
 
192
                        }
 
193
                        
 
194
                        if (next == null) break SCAN_LOOP;
 
195
                    } else if (id == WS) {
 
196
                        // just skip
 
197
                    } else {
 
198
                        break; // end of element markup
 
199
                    }
 
200
                }
 
201
                
 
202
                // Update the document
 
203
                try {
 
204
                    doc.insertString(insertStart, stringToInsert, null);
 
205
                    doc.invalidateSyntaxMarks();
 
206
                } catch( BadLocationException e ) {
 
207
                    throw new DOMException(DOMException.INVALID_STATE_ERR , e.getMessage());
 
208
                } finally {
 
209
                    doc.atomicUnlock();
 
210
                }
 
211
        }
 
212
        
 
213
        // Update this object's member variables
 
214
//        retokenizeObject();
 
215
    }
 
216
    
 
217
    public final void removeAttribute(String name) {
 
218
        throw new ROException();
 
219
    }
 
220
    
 
221
    public Attr getAttributeNode(String name) {
 
222
        NamedNodeMap map = getAttributes();
 
223
        Node node = map.getNamedItem(name);
 
224
        return (Attr) node;
 
225
    }
 
226
    
 
227
    public final Attr setAttributeNode(Attr attribute) {
 
228
        throw new ROException();
 
229
    }
 
230
    
 
231
    public final Attr removeAttributeNode(Attr attribute) {
 
232
        throw new ROException();
 
233
    }
 
234
    
 
235
    public NodeList getElementsByTagName(String name) {
 
236
        throw new ROException();
 
237
    }
 
238
    
 
239
    /**
 
240
     * Returns previous sibling by locating pairing start tag
 
241
     * and asking it for previous non-start tag SyntaxNode.
 
242
     */
 
243
    public Node getPreviousSibling() {
 
244
        SyntaxNode prev = getStartTag();
 
245
        if (prev == null) return null;
 
246
        prev = findPrevious(prev);
 
247
        if (prev instanceof StartTag) {
 
248
            return null;
 
249
        } else {
 
250
            return prev;
 
251
        }
 
252
    }
 
253
    
 
254
    /**
 
255
     * Returns next sibling by locating pairing end tag
 
256
     * and asking it for next non-end tag SyntaxNode.
 
257
     */
 
258
    public Node getNextSibling() {
 
259
        SyntaxNode next = getEndTag();
 
260
        if (next == null) return null;
 
261
        next = findNext(next);
 
262
        if (next instanceof EndTag) {
 
263
            return null;
 
264
        } else {
 
265
            return next;
 
266
        }
 
267
    }
 
268
    
 
269
    public Node getFirstChild() {
 
270
        NodeList list = getChildNodes();
 
271
        if (list.getLength() == 0) return null;
 
272
        return getChildNodes().item(0);
 
273
    }
 
274
    
 
275
    public Node getLastChild() {
 
276
        NodeList list = getChildNodes();
 
277
        if (list.getLength() == 0) return null;
 
278
        return list.item(list.getLength());
 
279
    }
 
280
    
 
281
    protected abstract Tag getStartTag();
 
282
    
 
283
    protected abstract Tag getEndTag();
 
284
    
 
285
    //    public boolean equals(Object obj) {
 
286
    //        if ((obj instanceof Tag) == false) return false;
 
287
    //        return false;
 
288
    //    }
 
289
    
 
290
    
 
291
    // unsupported DOM level 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
292
    
 
293
    public String getAttributeNS(String namespaceURI, String localName) {
 
294
        throw new UOException();
 
295
    }
 
296
    
 
297
    public void setAttributeNS(String namespaceURI, String qualifiedName, String value) {
 
298
        throw new UOException();
 
299
    }
 
300
    
 
301
    public void removeAttributeNS(String namespaceURI, String localName) {
 
302
        throw new UOException();
 
303
    }
 
304
    
 
305
    public Attr getAttributeNodeNS(String namespaceURI, String localName) {
 
306
        throw new UOException();
 
307
    }
 
308
    
 
309
    public Attr setAttributeNodeNS(Attr newAttr) {
 
310
        throw new UOException();
 
311
    }
 
312
    
 
313
    public NodeList getElementsByTagNameNS(String namespaceURI, String localName) {
 
314
        throw new UOException();
 
315
    }
 
316
    
 
317
    public boolean hasAttribute(String name) {
 
318
        throw new UOException();
 
319
    }
 
320
    
 
321
    public boolean hasAttributeNS(String namespaceURI, String localName) {
 
322
        throw new UOException();
 
323
    }
 
324
    
 
325
//    public void retokenizeObject() {
 
326
//        // Update this object's member variables
 
327
//        try {
 
328
//            first = support.getTokenChain(offset, support.getDocument().getLength());
 
329
//        } catch (BadLocationException e) {
 
330
//            throw new DOMException(DOMException.INVALID_STATE_ERR , e.getMessage());
 
331
//        }
 
332
//    }
 
333
 
 
334
    /**
 
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
 
338
     * equality.
 
339
     * <p>
 
340
     * I would appreciate a methos at DOM that would define
 
341
     * Node equals not Objevt's equals.
 
342
     */
 
343
    public final boolean equals(Object obj) {
 
344
        if (obj == this) return true;
 
345
        if (obj instanceof Tag) {
 
346
            Tag tag = (Tag) obj;
 
347
            Tag t1 = tag.getStartTag();
 
348
            Tag t2 = getStartTag();
 
349
            if (t1 == null || t2 == null) return false;
 
350
            return t1.superEquals(t2);
 
351
        }
 
352
        return false;
 
353
    }
 
354
 
 
355
    private boolean superEquals(Tag tag) {
 
356
        return super.equals(tag);
 
357
    }
 
358
 
 
359
    /**
 
360
     * The same as for equals it's DOM node hashcode.
 
361
     */
 
362
    public final int hashCode() {
 
363
        Tag tag = getStartTag();
 
364
        if (tag == null || tag == this) {
 
365
            return super.hashCode();
 
366
        } else {
 
367
            return tag.hashCode();
 
368
        }
 
369
    }
 
370
}
 
371