Source Code Cross Referenced for HTMLSyntaxSupport.java in  » IDE-Netbeans » html » org » netbeans » editor » ext » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Netbeans » html » org.netbeans.editor.ext.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003:         *
004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005:         *
006:         * The contents of this file are subject to the terms of either the GNU
007:         * General Public License Version 2 only ("GPL") or the Common
008:         * Development and Distribution License("CDDL") (collectively, the
009:         * "License"). You may not use this file except in compliance with the
010:         * License. You can obtain a copy of the License at
011:         * http://www.netbeans.org/cddl-gplv2.html
012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013:         * specific language governing permissions and limitations under the
014:         * License.  When distributing the software, include this License Header
015:         * Notice in each file and include the License file at
016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
017:         * particular file as subject to the "Classpath" exception as provided
018:         * by Sun in the GPL Version 2 section of the License file that
019:         * accompanied this code. If applicable, add the following below the
020:         * License Header, with the fields enclosed by brackets [] replaced by
021:         * your own identifying information:
022:         * "Portions Copyrighted [year] [name of copyright owner]"
023:         *
024:         * Contributor(s):
025:         *
026:         * The Original Software is NetBeans. The Initial Developer of the Original
027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028:         * Microsystems, Inc. All Rights Reserved.
029:         *
030:         * If you wish your version of this file to be governed by only the CDDL
031:         * or only the GPL Version 2, indicate your decision by adding
032:         * "[Contributor] elects to include this software in this distribution
033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
034:         * single choice of license, a recipient has the option to distribute
035:         * your version of this file under either the CDDL, the GPL Version 2 or
036:         * to extend the choice of license to its licensees as provided above.
037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
038:         * Version 2 license, then the option applies only if the new code is
039:         * made subject to such option by the copyright holder.
040:         */
041:        package org.netbeans.editor.ext.html;
042:
043:        import javax.swing.text.Document;
044:        import java.util.*;
045:        import javax.swing.text.BadLocationException;
046:        import javax.swing.text.JTextComponent;
047:        import org.netbeans.api.html.lexer.HTMLTokenId;
048:        import org.netbeans.api.lexer.Token;
049:        import org.netbeans.api.lexer.TokenHierarchy;
050:        import org.netbeans.api.lexer.TokenSequence;
051:        import org.netbeans.editor.BaseDocument;
052:        import org.netbeans.editor.ext.ExtSyntaxSupport;
053:        import org.netbeans.editor.ext.html.dtd.DTD;
054:        import org.netbeans.editor.ext.html.dtd.DTD.Element;
055:        import org.netbeans.editor.ext.html.dtd.InvalidateEvent;
056:        import org.netbeans.editor.ext.html.dtd.InvalidateListener;
057:        import org.netbeans.spi.editor.completion.CompletionItem;
058:
059:        /**
060:         * Support methods for HTML document syntax analyzes
061:         *
062:         * @author Petr Nejedly
063:         * @author Marek Fukala
064:         */
065:        public class HTMLSyntaxSupport extends ExtSyntaxSupport implements 
066:                InvalidateListener {
067:            private static final String FALLBACK_DOCTYPE = "-//W3C//DTD HTML 4.01 Transitional//EN"; // NOI18N
068:
069:            private DTD dtd;
070:            private String docType;
071:
072:            public static synchronized HTMLSyntaxSupport get(Document doc) {
073:                HTMLSyntaxSupport sup = (HTMLSyntaxSupport) doc
074:                        .getProperty(HTMLSyntaxSupport.class);
075:                if (sup == null) {
076:                    sup = new HTMLSyntaxSupport((BaseDocument) doc);
077:                    doc.putProperty(HTMLSyntaxSupport.class, sup);
078:                }
079:                return sup;
080:            }
081:
082:            /** Creates new HTMLSyntaxSupport */
083:            public HTMLSyntaxSupport(BaseDocument doc) {
084:                super (doc);
085:            }
086:
087:            /** Reset our cached DTD if no longer valid.
088:             */
089:            public void dtdInvalidated(InvalidateEvent evt) {
090:                if (dtd != null && evt.isInvalidatedIdentifier(docType)) {
091:                    dtd = null;
092:                }
093:            }
094:
095:            public DTD getDTD() {
096:                String type = getDocType();
097:                if (type == null)
098:                    type = FALLBACK_DOCTYPE;
099:
100:                if (dtd != null && type == docType)
101:                    return dtd;
102:
103:                docType = type;
104:                dtd = org.netbeans.editor.ext.html.dtd.Registry.getDTD(docType,
105:                        null);
106:
107:                if (dtd == null) {
108:                    //use default for unknown doctypes
109:                    dtd = org.netbeans.editor.ext.html.dtd.Registry.getDTD(
110:                            FALLBACK_DOCTYPE, null);
111:                }
112:                return dtd;
113:            }
114:
115:            protected String getDocType() {
116:                try {
117:                    SyntaxElement elem = getElementChain(0);
118:
119:                    if (elem == null)
120:                        return null; // empty document
121:
122:                    int type = elem.getType();
123:
124:                    while (type != SyntaxElement.TYPE_DECLARATION
125:                            && type != SyntaxElement.TYPE_TAG) {
126:                        elem = elem.getNext();
127:                        if (elem == null)
128:                            break;
129:                        type = elem.getType();
130:                    }
131:
132:                    if (type == SyntaxElement.TYPE_DECLARATION)
133:                        return ((SyntaxElement.Declaration) elem)
134:                                .getPublicIdentifier();
135:
136:                    return null;
137:                } catch (BadLocationException e) {
138:                    return null;
139:                }
140:            }
141:
142:            /** Find matching tags with the current position.
143:             * @param offset position of the starting tag
144:             * @param simple whether the search should skip comment and possibly other areas.
145:             *  This can be useful when the speed is critical, because the simple
146:             *  search is faster.
147:             * @return array of integers containing starting and ending position
148:             *  of the block in the document. Null is returned if there's
149:             *  no matching block.
150:             */
151:            @Override
152:            public int[] findMatchingBlock(int offset, boolean simpleSearch)
153:                    throws BadLocationException {
154:                BaseDocument document = getDocument();
155:                document.readLock();
156:                try {
157:                    TokenHierarchy hi = TokenHierarchy.get(document);
158:                    TokenSequence ts = tokenSequence(hi, offset);
159:                    if (ts == null) {
160:                        //no suitable token sequence found
161:                        return null;
162:                    }
163:
164:                    ts.move(offset);
165:                    if (!ts.moveNext() && !ts.movePrevious()) {
166:                        return null; //no token found
167:                    }
168:
169:                    Token token = ts.token();
170:
171:                    // if the carret is after HTML tag ( after char '>' ), ship inside the tag
172:                    if (token.id() == HTMLTokenId.TAG_CLOSE_SYMBOL) {
173:                        ts.moveIndex(ts.index() - 2);
174:                        if (ts.moveNext()) {
175:                            token = ts.token();
176:                        }
177:                    }
178:
179:                    boolean isInside = false; // flag, whether the carret is somewhere in a HTML tag
180:                    if (isTagButNotSymbol(token)) {
181:                        isInside = true; // the carret is somewhere in '<htmltag' or '</htmltag'
182:                    } else {
183:                        if (ts.moveNext()) {
184:                            token = ts.token();
185:                            if (token.id() == HTMLTokenId.TAG_OPEN_SYMBOL) {
186:                                //we are on opening symbol < or </
187:                                //so go to the next token which should be a TAG
188:                                //if the token is null or nor TAG there is nothing to match
189:                                if ((token.id() == HTMLTokenId.TAG_CLOSE)
190:                                        || (token.id() == HTMLTokenId.TAG_OPEN)) {
191:                                    isInside = true; // we found a tag
192:                                } else {
193:                                    return null;
194:                                }
195:                            } else {
196:                                //we are on closing symbol > or />
197:                                // find out whether the carret is inside an HTML tag
198:                                //try to find the beginning of the tag.
199:                                boolean found = false;
200:                                while (!isTagButNotSymbol(token)
201:                                        && token.id() != HTMLTokenId.TAG_CLOSE_SYMBOL
202:                                        && ts.movePrevious()) {
203:                                    token = ts.token();
204:                                }
205:
206:                                if (ts.index() != -1
207:                                        && isTagButNotSymbol(token)) {
208:                                    isInside = true;
209:                                }
210:                            }
211:                        } else {
212:                            return null; //no token
213:                        }
214:                    }
215:
216:                    if (ts.index() != -1 && isTagButNotSymbol(token)
217:                            && isInside) {
218:                        int start; // possition where the matched tag starts
219:                        int end; // possition where the matched tag ends
220:                        int poss = -1; // how many the same tags is inside the mathed tag
221:
222:                        String tag = token.text().toString().toLowerCase()
223:                                .trim();
224:                        //test whether we are in a close tag
225:                        if (token.id() == HTMLTokenId.TAG_CLOSE) {
226:                            //we are in a close tag
227:                            do {
228:                                token = ts.token();
229:                                if (isTagButNotSymbol(token)) {
230:                                    String tagName = token.text().toString()
231:                                            .toLowerCase().trim();
232:                                    if (tagName.equals(tag)
233:                                            && (token.id() == HTMLTokenId.TAG_OPEN)
234:                                            && !isSingletonTag(ts)) {
235:                                        //it's an open tag
236:                                        if (poss == 0) {
237:                                            //get offset of previous token: < or </
238:                                            ts.movePrevious();
239:                                            start = ts.token().offset(hi);
240:                                            ts.moveIndex(ts.index() + 2);
241:                                            ts.moveNext();
242:                                            Token tok = ts.token();
243:                                            end = tok.offset(hi)
244:                                                    + (tok.id() == HTMLTokenId.TAG_CLOSE_SYMBOL ? tok
245:                                                            .text().length()
246:                                                            : 0);
247:                                            return new int[] { start, end };
248:                                        } else {
249:                                            poss--;
250:                                        }
251:                                    } else {
252:                                        //test whether the tag is a close tag for the 'tag' tagname
253:                                        if ((tagName.indexOf(tag) > -1)
254:                                                && !isSingletonTag(ts)) {
255:                                            poss++;
256:                                        }
257:                                    }
258:                                }
259:                            } while (ts.movePrevious());
260:
261:                        } else {
262:                            //we are in an open tag
263:                            if (tag.charAt(0) == '>')
264:                                return null;
265:
266:                            //We need to find out whether the open tag is a singleton tag or not.
267:                            //In the first case no matching is needed
268:                            if (isSingletonTag(ts))
269:                                return null;
270:
271:                            do {
272:                                token = ts.token();
273:                                if (isTagButNotSymbol(token)) {
274:                                    String tagName = token.text().toString()
275:                                            .toLowerCase().trim();
276:                                    if (tagName.equals(tag)
277:                                            && token.id() == HTMLTokenId.TAG_CLOSE) {
278:                                        if (poss == 0) {
279:                                            //get offset of previous token: < or </
280:                                            end = token.offset(hi)
281:                                                    + token.text().length() + 1;
282:                                            ts.movePrevious();
283:                                            start = ts.token().offset(hi);
284:
285:                                            do {
286:                                                token = ts.token();
287:                                            } while (ts.moveNext()
288:                                                    && token.id() != HTMLTokenId.TAG_CLOSE_SYMBOL);
289:
290:                                            if (ts.index() != -1) {
291:                                                end = token.offset(hi)
292:                                                        + token.text().length();
293:                                            }
294:                                            return new int[] { start, end };
295:                                        } else
296:                                            poss--;
297:                                    } else {
298:                                        if (tagName.equals(tag)
299:                                                && !isSingletonTag(ts)) {
300:                                            poss++;
301:                                        }
302:                                    }
303:                                }
304:                            } while (ts.moveNext());
305:                        }
306:                    }
307:
308:                    ts.move(offset); //reset the token sequence to the original position
309:                    if (!(ts.moveNext() || ts.movePrevious())) {
310:                        return null; //no token
311:                    }
312:                    token = ts.token();
313:
314:                    //match html comments
315:                    if (ts.index() != -1
316:                            && token.id() == HTMLTokenId.BLOCK_COMMENT) {
317:                        String tokenImage = token.text().toString();
318:                        if (tokenImage.startsWith("<!--")
319:                                && (offset < (token.offset(hi))
320:                                        + "<!--".length())) { //NOI18N
321:                            //start html token - we need to find the end token of the html comment
322:                            do {
323:                                token = ts.token();
324:                                tokenImage = token.text().toString();
325:                                if ((token.id() == HTMLTokenId.BLOCK_COMMENT)) {
326:                                    if (tokenImage.endsWith("-->")) {//NOI18N
327:                                        //found end token
328:                                        int end = token.offset(hi)
329:                                                + tokenImage.length();
330:                                        int start = end - "-->".length(); //NOI18N
331:                                        return new int[] { start, end };
332:                                    }
333:                                } else
334:                                    break;
335:                            } while (ts.moveNext());
336:                        }
337:
338:                        if (tokenImage.endsWith("-->")
339:                                && (offset >= (token.offset(hi))
340:                                        + tokenImage.length() - "-->".length())) { //NOI18N
341:                            //end html token - we need to find the start token of the html comment
342:                            do {
343:                                token = ts.token();
344:                                if ((token.id() == HTMLTokenId.BLOCK_COMMENT)) {
345:                                    if (token.text().toString().startsWith(
346:                                            "<!--")) { //NOI18N
347:                                        //found end token
348:                                        int start = token.offset(hi);
349:                                        int end = start + "<!--".length(); //NOI18N
350:                                        return new int[] { start, end };
351:                                    }
352:                                } else
353:                                    break;
354:
355:                            } while (ts.movePrevious());
356:                        }
357:                    } //eof match html comments
358:
359:                } finally {
360:                    document.readUnlock();
361:                }
362:                return null;
363:            }
364:
365:            /** Finds out whether the given {@link TokenSequence}'s actual token is a part of a singleton tag (e.g. <div style=""/>).
366:             * @ts TokenSequence positioned on a token within a tag
367:             * @return true is the token is a part of singleton tag
368:             */
369:            public boolean isSingletonTag(TokenSequence ts) {
370:                int tsIndex = ts.index(); //backup ts state
371:                if (tsIndex != -1) { //test if we are on a token
372:                    try {
373:                        do {
374:                            Token ti = ts.token();
375:                            if (ti.id() == HTMLTokenId.TAG_CLOSE_SYMBOL) {
376:                                if ("/>".equals(ti.text().toString())) { // NOI18N
377:                                    //it is a singleton tag => do not match
378:                                    return true;
379:                                }
380:                                if (">".equals(ti.text().toString())) {
381:                                    break; // NOI18N
382:                                }
383:                            }
384:                            //break the loop on TEXT or on another open tag symbol
385:                            //(just to prevent long loop in case the tag is not closed)
386:                            if ((ti.id() == HTMLTokenId.TEXT)
387:                                    || (ti.id() == HTMLTokenId.TAG_OPEN_SYMBOL)) {
388:                                break;
389:                            }
390:                        } while (ts.moveNext());
391:                    } finally {
392:                        ts.moveIndex(tsIndex); //backup the TokenSequence position
393:                        ts.moveNext();
394:                    }
395:                } else {
396:                    //ts is rewinded out of tokens
397:                }
398:                return false;
399:            }
400:
401:            /** The way how to get previous SyntaxElement in document. It is not intended
402:             * for direct usage, and thus is not public. Usually, it is called from
403:             * SyntaxElement's method getPrevious()
404:             */
405:            public SyntaxElement getPreviousElement(int offset)
406:                    throws BadLocationException {
407:                return offset == 0 ? null : getElementChain(offset - 1);
408:            }
409:
410:            /** Returns SyntaxElement instance for block of tokens, which is either
411:             * surrounding given offset, or is just after the offset.
412:             *
413:             * @param offset offset in document where to search for SyntaxElement
414:             * @return SyntaxElement surrounding or laying after the offset
415:             * or <CODE>null</CODE> if there is no element there (end of document)
416:             */
417:            public SyntaxElement getElementChain(int offset)
418:                    throws BadLocationException {
419:                getDocument().readLock();
420:                try {
421:                    TokenHierarchy hi = TokenHierarchy.get(getDocument());
422:                    TokenSequence ts = tokenSequence(hi, offset);
423:                    if (ts == null) {
424:                        //we are out of html - go back and try to find an html element
425:                        TokenSequence tseq = hi.tokenSequence();
426:                        tseq.move(offset);
427:                        if (!tseq.movePrevious() && !tseq.moveNext()) {
428:                            //no token on the position
429:                            return null;
430:                        }
431:                        int nonHtmlBlockStart = 0;
432:                        //go back until we find an html code
433:                        while (tseq.movePrevious()) {
434:                            //XXX - just one level embedding
435:                            TokenSequence htmlTS = tseq.embedded(HTMLTokenId
436:                                    .language());
437:                            if (htmlTS != null) {
438:                                if (htmlTS.moveNext() || htmlTS.movePrevious()) {
439:                                    //found html piece
440:                                    nonHtmlBlockStart = htmlTS.offset()
441:                                            + htmlTS.token().length();
442:                                    break;
443:                                }
444:                            }
445:                        }
446:                        return getNextElement(nonHtmlBlockStart);
447:                    }
448:
449:                    //html token found
450:                    ts.move(offset);
451:                    if (!ts.moveNext() && !ts.movePrevious())
452:                        return null; //no token found
453:
454:                    Token item = ts.token();
455:
456:                    int beginning = ts.offset();
457:
458:                    if (item.id() == HTMLTokenId.CHARACTER) {
459:                        do {
460:                            item = ts.token();
461:                            beginning = ts.offset();
462:                        } while (item.id() == HTMLTokenId.CHARACTER
463:                                && ts.movePrevious());
464:
465:                        // now item is either HTMLSyntax.VALUE or we're in text, or at BOF
466:                        if (item.id() != HTMLTokenId.VALUE
467:                                && item.id() != HTMLTokenId.TEXT) {
468:                            return getNextElement(beginning);
469:                        } // else ( for VALUE or TEXT ) fall through
470:                    }
471:
472:                    if (item.id() == HTMLTokenId.WS
473:                            || item.id() == HTMLTokenId.ARGUMENT
474:                            || // these are possible only in Tags
475:                            item.id() == HTMLTokenId.OPERATOR
476:                            || item.id() == HTMLTokenId.VALUE) { // so find boundary
477:                        while (ts.movePrevious() && !isTag(item = ts.token()))
478:                            ;
479:                        return getNextElement(item.offset(hi)); // TAGC
480:                    }
481:
482:                    if (item.id() == HTMLTokenId.TEXT) {
483:                        do {
484:                            beginning = ts.offset();
485:                        } while (ts.movePrevious()
486:                                && (ts.token().id() == HTMLTokenId.TEXT || ts
487:                                        .token().id() == HTMLTokenId.CHARACTER));
488:
489:                        return getNextElement(beginning); // from start of Commment
490:                    }
491:
492:                    if (item.id() == HTMLTokenId.SCRIPT) {
493:                        //we have just one big token for script
494:                        return getNextElement(ts.token().offset(hi));
495:                    }
496:
497:                    if (item.id() == HTMLTokenId.STYLE) {
498:                        //we have just one big token for script
499:                        return getNextElement(ts.token().offset(hi));
500:                    }
501:
502:                    if (isTag(item)) {
503:                        if (item.id() == HTMLTokenId.TAG_OPEN
504:                                || item.id() == HTMLTokenId.TAG_OPEN_SYMBOL)
505:                            return getNextElement(item.offset(hi)); // TAGO/ETAGO // NOI18N
506:                        else {
507:                            do {
508:                                if (!ts.movePrevious()) {
509:                                    return getNextElement(item.offset(hi));
510:                                }
511:                                item = ts.token();
512:                            } while (item.id() != HTMLTokenId.TAG_OPEN_SYMBOL);
513:
514:                            return getNextElement(item.offset(hi)); // TAGC
515:                        }
516:                    }
517:
518:                    if (item.id() == HTMLTokenId.ERROR)
519:                        return new SyntaxElement(this , item.offset(hi),
520:                                getTokenEnd(hi, item), SyntaxElement.TYPE_ERROR);
521:
522:                    if (item.id() == HTMLTokenId.BLOCK_COMMENT) {
523:                        while (item.id() == HTMLTokenId.BLOCK_COMMENT
524:                                && !item.text().toString().startsWith("<!--")
525:                                && ts.movePrevious()) { // NOI18N
526:                            item = ts.token();
527:                        }
528:                        return getNextElement(item.offset(hi)); // from start of Commment
529:                    }
530:
531:                    if (item.id() == HTMLTokenId.DECLARATION
532:                            || item.id() == HTMLTokenId.SGML_COMMENT) {
533:                        while ((item.id() != HTMLTokenId.DECLARATION || !item
534:                                .text().toString().startsWith("<!"))
535:                                && ts.movePrevious()) { // NOI18N
536:                            item = ts.token();
537:                        }
538:                        return getNextElement(item.offset(hi)); // from start of Commment
539:                    }
540:                } finally {
541:                    getDocument().readUnlock();
542:                }
543:                return null;
544:            }
545:
546:            public SyntaxElement getNextElement(int offset)
547:                    throws javax.swing.text.BadLocationException {
548:                getDocument().readLock();
549:                try {
550:                    TokenHierarchy hi = TokenHierarchy.get(getDocument());
551:                    TokenSequence ts = tokenSequence(hi, offset);
552:                    if (ts == null) {
553:                        //we are out of html - go back and try to find an html element
554:                        TokenSequence tseq = hi.tokenSequence();
555:                        tseq.move(offset);
556:                        if (!tseq.movePrevious() && !tseq.moveNext()) {
557:                            //no token on the position
558:                            return null;
559:                        }
560:                        int nonHtmlBlockEnd = getDocument().getLength();
561:                        //find end of the non-html block
562:                        while (tseq.moveNext()) {
563:                            //XXX - just one level embedding
564:                            TokenSequence htmlTS = tseq.embedded(HTMLTokenId
565:                                    .language());
566:                            if (htmlTS != null) {
567:                                //found html piece
568:                                if (!htmlTS.moveNext()) {
569:                                    return null; //no token in the TS!?!?!
570:                                }
571:                                nonHtmlBlockEnd = htmlTS.offset();
572:                                break;
573:                            }
574:                        }
575:                        if (offset == nonHtmlBlockEnd) {
576:                            return null;
577:                        }
578:
579:                        return new SyntaxElement(this , offset, nonHtmlBlockEnd,
580:                                SyntaxElement.TYPE_UNKNOWN);
581:                    }
582:
583:                    ts.move(offset);
584:                    if (!ts.moveNext())
585:                        return null;
586:                    org.netbeans.api.lexer.Token item = ts.token();
587:                    int lastOffset = getTokenEnd(hi, item);
588:
589:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.BLOCK_COMMENT) {
590:                        do {
591:                            lastOffset = getTokenEnd(hi, ts.token());
592:                        } while (ts.token().id() == org.netbeans.api.html.lexer.HTMLTokenId.BLOCK_COMMENT
593:                                && ts.moveNext());
594:                        return new SyntaxElement(this , offset, lastOffset,
595:                                SyntaxElement.TYPE_COMMENT);
596:                    }
597:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.DECLARATION) {
598:                        java.lang.StringBuffer sb = new java.lang.StringBuffer(
599:                                item.text());
600:
601:                        while (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.DECLARATION
602:                                || item.id() == org.netbeans.api.html.lexer.HTMLTokenId.SGML_COMMENT) {
603:                            lastOffset = getTokenEnd(hi, item);
604:                            if (!ts.moveNext()) {
605:                                break;
606:                            }
607:                            item = ts.token();
608:                            if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.DECLARATION)
609:                                sb.append(item.text().toString());
610:                        }
611:                        java.lang.String image = sb.toString();
612:
613:                        if (!image.startsWith("<!DOCTYPE"))
614:                            return new org.netbeans.editor.ext.html.SyntaxElement.Declaration(
615:                                    this , offset, lastOffset, null, null, null);
616:                        image = image.substring(9).trim();
617:                        int index = image.indexOf(' ');
618:
619:                        if (index < 0)
620:                            return new org.netbeans.editor.ext.html.SyntaxElement.Declaration(
621:                                    this , offset, lastOffset, null, null, null);
622:                        java.lang.String rootElem = image.substring(0, index);
623:
624:                        image = image.substring(index).trim();
625:                        if (image.startsWith("PUBLIC")) {
626:                            image = image.substring(6).trim();
627:                            sb = new java.lang.StringBuffer(image);
628:                            java.lang.String pi = getQuotedString(sb);
629:
630:                            if (pi != null) {
631:                                java.lang.String si = getQuotedString(sb);
632:
633:                                return new org.netbeans.editor.ext.html.SyntaxElement.Declaration(
634:                                        this , offset, lastOffset, rootElem, pi,
635:                                        si);
636:                            }
637:                        } else if (image.startsWith("SYSTEM")) {
638:                            image = image.substring(6).trim();
639:                            sb = new java.lang.StringBuffer(image);
640:                            java.lang.String si = getQuotedString(sb);
641:
642:                            if (si != null) {
643:                                return new org.netbeans.editor.ext.html.SyntaxElement.Declaration(
644:                                        this , offset, lastOffset, rootElem,
645:                                        null, si);
646:                            }
647:                        }
648:                        return new org.netbeans.editor.ext.html.SyntaxElement.Declaration(
649:                                this , offset, lastOffset, null, null, null);
650:                    }
651:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.ERROR)
652:                        return new SyntaxElement(this , item.offset(hi),
653:                                lastOffset, SyntaxElement.TYPE_ERROR);
654:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TEXT
655:                            || item.id() == org.netbeans.api.html.lexer.HTMLTokenId.CHARACTER) {
656:                        do {
657:                            lastOffset = getTokenEnd(hi, item);
658:                            item = ts.token();
659:                        } while (ts.moveNext()
660:                                && (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TEXT || item
661:                                        .id() == org.netbeans.api.html.lexer.HTMLTokenId.CHARACTER));
662:                        return new SyntaxElement(this , offset, lastOffset,
663:                                SyntaxElement.TYPE_TEXT);
664:                    }
665:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.SCRIPT) {
666:                        return new SyntaxElement(this , offset, getTokenEnd(hi,
667:                                item), SyntaxElement.TYPE_SCRIPT);
668:                    }
669:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.STYLE) {
670:                        return new SyntaxElement(this , offset, getTokenEnd(hi,
671:                                item), SyntaxElement.TYPE_STYLE);
672:                    }
673:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_CLOSE
674:                            || (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_OPEN_SYMBOL && item
675:                                    .text().toString().equals("</"))) {
676:                        java.lang.String name = item.text().toString();
677:
678:                        if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_OPEN_SYMBOL) {
679:                            ts.moveNext();
680:                            name = ts.token().text().toString();
681:                        }
682:                        ts.moveNext();
683:                        item = ts.token();
684:                        do {
685:                            item = ts.token();
686:                            lastOffset = getTokenEnd(hi, item);
687:                        } while (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.WS
688:                                && ts.moveNext());
689:                        if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_CLOSE_SYMBOL) {
690:                            return new org.netbeans.editor.ext.html.SyntaxElement.Named(
691:                                    this , offset, getTokenEnd(hi, item),
692:                                    SyntaxElement.TYPE_ENDTAG, name);
693:                        } else {
694:                            return new org.netbeans.editor.ext.html.SyntaxElement.Named(
695:                                    this , offset, lastOffset,
696:                                    SyntaxElement.TYPE_ENDTAG, name);
697:                        }
698:                    }
699:                    if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_OPEN
700:                            || (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_OPEN_SYMBOL && !item
701:                                    .text().toString().equals("</"))) {
702:                        java.lang.String name = item.text().toString();
703:                        ArrayList<SyntaxElement.TagAttribute> attrs = new ArrayList<SyntaxElement.TagAttribute>();
704:
705:                        if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_OPEN_SYMBOL) {
706:                            ts.moveNext();
707:                            name = ts.token().text().toString();
708:                        }
709:                        ts.moveNext();
710:                        item = ts.token();
711:
712:                        //find tag attributes
713:                        Token attrNameToken = null;
714:                        do {
715:                            item = ts.token();
716:                            if (item.id() == HTMLTokenId.ARGUMENT) {
717:                                //attribute name
718:                                attrNameToken = item;
719:                            } else if (item.id() == HTMLTokenId.VALUE
720:                                    && attrNameToken != null) {
721:                                //found attribute value after attribute name
722:
723:                                //there may be entity reference inside the attribute value
724:                                //e.g. onclick="alert('hello&nbsp;world');" which divides the value
725:                                //into more html tokens
726:                                StringBuffer value = new StringBuffer();
727:                                Token t = null;
728:                                do {
729:                                    t = ts.token();
730:                                    value.append(t.text().toString());
731:                                } while (ts.moveNext()
732:                                        && (ts.token().id() == HTMLTokenId.VALUE || ts
733:                                                .token().id() == HTMLTokenId.CHARACTER));
734:
735:                                SyntaxElement.TagAttribute tagAttr = new SyntaxElement.TagAttribute(
736:                                        attrNameToken.text().toString(), value
737:                                                .toString(), attrNameToken
738:                                                .offset(hi), item.offset(hi));
739:                                attrs.add(tagAttr);
740:                                attrNameToken = null;
741:                            }
742:                            lastOffset = getTokenEnd(hi, item);
743:                        } while ((item.id() == org.netbeans.api.html.lexer.HTMLTokenId.WS
744:                                || item.id() == org.netbeans.api.html.lexer.HTMLTokenId.ARGUMENT
745:                                || item.id() == org.netbeans.api.html.lexer.HTMLTokenId.OPERATOR
746:                                || item.id() == org.netbeans.api.html.lexer.HTMLTokenId.VALUE || item
747:                                .id() == org.netbeans.api.html.lexer.HTMLTokenId.CHARACTER)
748:                                && ts.moveNext());
749:
750:                        if (item.id() == org.netbeans.api.html.lexer.HTMLTokenId.TAG_CLOSE_SYMBOL) {
751:                            return new org.netbeans.editor.ext.html.SyntaxElement.Tag(
752:                                    this , offset, getTokenEnd(hi, item), name,
753:                                    attrs, item.text().toString().equals("/>"));
754:                        } else {
755:                            return new org.netbeans.editor.ext.html.SyntaxElement.Tag(
756:                                    this , offset, lastOffset, name, attrs);
757:                        }
758:                    }
759:
760:                } finally {
761:                    getDocument().readUnlock();
762:                }
763:                return null;
764:            }
765:
766:            public static boolean isTag(Token t) {
767:                return ((t.id() == HTMLTokenId.TAG_OPEN)
768:                        || (t.id() == HTMLTokenId.TAG_CLOSE)
769:                        || (t.id() == HTMLTokenId.TAG_OPEN_SYMBOL) || (t.id() == HTMLTokenId.TAG_CLOSE_SYMBOL));
770:            }
771:
772:            public static boolean isTagButNotSymbol(Token t) {
773:                return ((t.id() == HTMLTokenId.TAG_OPEN) || (t.id() == HTMLTokenId.TAG_CLOSE));
774:            }
775:
776:            private static int getTokenEnd(TokenHierarchy thi, Token item) {
777:                return item.offset(thi) + item.text().length();
778:            }
779:
780:            /**
781:             * Beware, changes data
782:             */
783:            private static String getQuotedString(StringBuffer data) {
784:                int startIndex = 0;
785:                if (data == null || data.length() == 0)
786:                    return null;
787:                while (data.charAt(startIndex) == ' ')
788:                    startIndex++;
789:
790:                char stopMark = data.charAt(startIndex++);
791:                if (stopMark == '"' || stopMark == '\'') {
792:                    for (int index = startIndex; index < data.length(); index++)
793:                        if (data.charAt(index) == stopMark) {
794:                            String quoted = data.substring(startIndex, index);
795:                            data.delete(0, index + 1);
796:                            return quoted;
797:                        }
798:                }
799:
800:                return null;
801:            }
802:
803:            private static TokenSequence tokenSequence(TokenHierarchy hi,
804:                    int offset) {
805:                TokenSequence ts = hi.tokenSequence(HTMLTokenId.language());
806:                if (ts == null) {
807:                    //HTML language is not top level one
808:                    ts = hi.tokenSequence();
809:                    ts.move(offset);
810:                    if (!ts.moveNext() && !ts.movePrevious()) {
811:                        return null; //no token found
812:                    } else {
813:                        ts = ts.embedded(HTMLTokenId.language());
814:                    }
815:                }
816:                return ts;
817:            }
818:
819:            public List getPossibleEndTags(int offset, String prefix)
820:                    throws BadLocationException {
821:                prefix = prefix.toUpperCase();
822:                int prefixLen = prefix.length();
823:                SyntaxElement elem = getElementChain(offset);
824:                Stack stack = new Stack();
825:                List result = new ArrayList();
826:                Set found = new HashSet();
827:                DTD dtd = getDTD();
828:
829:                if (elem == null) {
830:                    if (offset > 0) {
831:                        elem = getElementChain(offset - 1);
832:                        if (elem == null)
833:                            return result;
834:                    } else
835:                        return result;
836:                }
837:
838:                int itemsCount = 0;
839:                for (elem = elem.getPrevious(); elem != null; elem = elem
840:                        .getPrevious()) {
841:                    if (elem.getType() == SyntaxElement.TYPE_ENDTAG) { // NOI18N
842:                        DTD.Element tag = dtd
843:                                .getElement(((SyntaxElement.Named) elem)
844:                                        .getName().toUpperCase());
845:                        if (tag != null && !tag.isEmpty())
846:                            stack.push(((SyntaxElement.Named) elem).getName()
847:                                    .toUpperCase());
848:                    } else if (elem.getType() == SyntaxElement.TYPE_TAG) { //now </ and > are returned as SyntaxElement.TAG so I need to filter them  NOI18N
849:                        DTD.Element tag = dtd
850:                                .getElement(((SyntaxElement.Tag) elem)
851:                                        .getName().toUpperCase());
852:
853:                        if (tag == null)
854:                            continue; // Unknown tag - ignore
855:                        if (tag.isEmpty())
856:                            continue; // ignore empty Tags - they are like start and imediate end
857:
858:                        String name = tag.getName();
859:
860:                        if (stack.empty()) { // empty stack - we are on the same tree deepnes - can close this tag
861:                            if (name.startsWith(prefix)
862:                                    && !found.contains(name)) { // add only new items
863:                                found.add(name);
864:                                result.add(new HTMLCompletionQuery.EndTagItem(
865:                                        name, offset - 2 - prefixLen,
866:                                        prefixLen + 2, name, itemsCount));
867:                            }
868:                            if (!tag.hasOptionalEnd())
869:                                break; // If this tag have required EndTag, we can't go higher until completing this tag
870:                        } else { // not empty - we match content of stack
871:                            if (stack.peek().equals(name)) { // match - close this branch of document tree
872:                                stack.pop();
873:                            } else if (!tag.hasOptionalEnd())
874:                                break; // we reached error in document structure, give up
875:                        }
876:                    }
877:                }
878:
879:                return result;
880:            }
881:
882:            public List getAutocompletedEndTag(int offset) {
883:                List l = new ArrayList();
884:                try {
885:                    SyntaxElement elem = getElementChain(offset - 1);
886:                    if (elem != null
887:                            && elem.getType() == SyntaxElement.TYPE_TAG) {
888:                        String tagName = ((SyntaxElement.Named) elem).getName();
889:                        //check if the tag has required endtag
890:                        Element dtdElem = getDTD().getElement(
891:                                tagName.toUpperCase());
892:                        if (dtdElem == null || !dtdElem.isEmpty()) {
893:                            CompletionItem eti = new HTMLCompletionQuery.AutocompleteEndTagItem(
894:                                    tagName, offset);
895:                            l.add(eti);
896:                        }
897:                    }
898:                } catch (BadLocationException e) {
899:                    //just ignore
900:                }
901:                return l;
902:            }
903:
904:            public int checkCompletion(JTextComponent target, String typedText,
905:                    boolean visible) {
906:                int retVal = COMPLETION_CANCEL;
907:                int dotPos = target.getCaret().getDot();
908:                BaseDocument doc = (BaseDocument) target.getDocument();
909:                switch (typedText.charAt(typedText.length() - 1)) {
910:                case '/':
911:                    if (dotPos >= 2) { // last char before inserted slash
912:                        try {
913:                            String txtBeforeSpace = doc.getText(dotPos - 2, 2);
914:                            if (txtBeforeSpace.equals("</")) // NOI18N
915:                                return COMPLETION_POPUP;
916:                        } catch (BadLocationException e) {
917:                        }
918:                    }
919:                    break;
920:                case ' ':
921:                    doc.readLock();
922:                    try {
923:                        TokenHierarchy hi = TokenHierarchy.get(doc);
924:                        TokenSequence ts = tokenSequence(hi, dotPos - 1);
925:                        if (ts == null) {
926:                            //no suitable token sequence found
927:                            return COMPLETION_POST_REFRESH;
928:                        }
929:
930:                        ts.move(dotPos - 1);
931:                        if (ts.moveNext() || ts.movePrevious()) {
932:                            if (ts.token().id() == HTMLTokenId.WS) {
933:                                return COMPLETION_POPUP;
934:                            }
935:                        }
936:                    } finally {
937:                        doc.readUnlock();
938:                    }
939:                    break;
940:                case '<':
941:                case '&':
942:                    return COMPLETION_POPUP;
943:                case ';':
944:                    return COMPLETION_HIDE;
945:                case '>':
946:                    try {
947:                        //check if the cursor is behind an open tag
948:                        SyntaxElement se = getElementChain(dotPos - 1);
949:                        if (se != null
950:                                && se.getType() == SyntaxElement.TYPE_TAG) {
951:                            return COMPLETION_POPUP;
952:                        }
953:                    } catch (BadLocationException e) {
954:                        //do nothing
955:                    }
956:                    return COMPLETION_HIDE;
957:
958:                }
959:                return COMPLETION_POST_REFRESH;
960:
961:            }
962:
963:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.