|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor.ext.html;
import java.util.*;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import org.netbeans.editor.*;
import org.netbeans.editor.ext.*;
import org.netbeans.editor.ext.html.dtd.*;
/**
*
* @author Petr Nejedly
* @version 0.9
*/
public class HTMLSyntaxSupport extends ExtSyntaxSupport implements InvalidateListener {
private static final String FALLBACK_DOCTYPE =
"-//W3C//DTD HTML 4.01 Transitional//EN"; // NOI18N
private DTD dtd;
private String docType;
/** Creates new HTMLSyntaxSupport */
public HTMLSyntaxSupport( BaseDocument doc ) {
super(doc);
}
/** Reset our cached DTD if no longer valid.
*/
public void dtdInvalidated(InvalidateEvent evt) {
if( dtd != null && evt.isInvalidatedIdentifier( docType ) ) {
dtd = null;
}
}
public DTD getDTD() {
String type = getDocType();
if( type == null ) type = FALLBACK_DOCTYPE;
if( dtd != null && type == docType ) return dtd;
docType = type;
dtd = org.netbeans.editor.ext.html.dtd.Registry.getDTD( docType, null );
return dtd;
}
protected String getDocType() {
try {
SyntaxElement elem = getElementChain( 0 );
if( elem == null ) return null; // empty document
int type = elem.getType();
while( type != SyntaxElement.TYPE_DECLARATION
&& type != SyntaxElement.TYPE_TAG ) {
elem = elem.getNext();
if( elem == null ) break;
type = elem.getType();
}
if( type == SyntaxElement.TYPE_DECLARATION )
return ((SyntaxElement.Declaration)elem).getPublicIdentifier();
return null;
} catch( BadLocationException e ) {
return null;
}
}
/** Find matching tags with the current position.
* @param offset position of the starting tag
* @param simple whether the search should skip comment and possibly other areas.
* This can be useful when the speed is critical, because the simple
* search is faster.
* @return array of integers containing starting and ending position
* of the block in the document. Null is returned if there's
* no matching block.
*/
public int[] findMatchingBlock(int offset, boolean simpleSearch)
throws BadLocationException {
// TODO - replanning to the other thread. Now it's in awt thread
TokenItem token = getTokenChain(offset, offset+1);
if (token.getTokenID().getNumericID() == HTMLTokenContext.TAG_ID){
if (token.getImage().charAt(0) == '>')
return null;
int start; // possition where the matched tag starts
int end; // possition where the matched tag ends
int poss = -1; // how many the same tags is inside the mathed tag
if ((token.getImage().length() > 1) && token.getImage().charAt(1) == '/'){
String tag = token.getImage().substring(2).trim().toLowerCase();
while ( token != null){
if (token.getTokenID().getNumericID() == HTMLTokenContext.TAG_ID) {
if (token.getImage().substring(1).trim().toLowerCase().equals(tag)){
if (poss == 0){
start = token.getOffset();
end = token.getOffset()+token.getImage().length()+1;
token = token.getNext();
while (token != null && token.getTokenID().getNumericID() != HTMLTokenContext.TAG_ID){
token = token.getNext();
}
if (token != null)
end = token.getOffset()+1;
return new int[] {start, end};
}
else{
poss--;
}
}
else {
if (token.getImage().toLowerCase().indexOf(tag) > -1){
poss++;
}
}
}
token = token.getPrevious();
}
}
else{
if (token.getImage().charAt(0) == '>')
return null;
String tag = token.getImage().substring(1).toLowerCase();
while ( token != null){
if (token.getTokenID().getNumericID() == HTMLTokenContext.TAG_ID && token.getImage().length()>1) {
if (token.getImage().substring(2).trim().toLowerCase().equals(tag)){
if (poss == 0) {
start = token.getOffset();
end = token.getOffset()+token.getImage().length()+1;
token = token.getNext();
while (token != null && token.getTokenID().getNumericID() != HTMLTokenContext.TAG_ID){
token = token.getNext();
}
if (token != null)
end = token.getOffset()+1;
return new int[] {start, end};
}
else
poss--;
}
else{
if (token.getImage().substring(1).toLowerCase().equals(tag))
poss++;
}
}
token = token.getNext();
}
}
}
return null;
}
private final int getTokenEnd( TokenItem item ) {
return item.getOffset() + item.getImage().length();
}
/** Returns SyntaxElement instance for block of tokens, which is either
* surrounding given offset, or is just after the offset.
* @param offset offset in document where to search for SyntaxElement
* @return SyntaxElement surrounding or laying after the offset
* or null if there is no element there (end of document)
*/
public SyntaxElement getElementChain( int offset ) throws BadLocationException {
TokenItem first = getTokenChain( offset, Math.min( offset + 10, getDocument().getLength() ) );
TokenItem item = first;
while (item != null && !item.getTokenContextPath().contains(HTMLTokenContext.contextPath) ){
item = item.getPrevious();
}
if( item == null ) return null; // on End of document
TokenID id = item.getTokenID();
int beginning = item.getOffset();
if( id == HTMLTokenContext.CHARACTER ) {
while( id != null && id == HTMLTokenContext.CHARACTER ) {
beginning = item.getOffset();
item = item.getPrevious();
id = item == null ? null : item.getTokenID();
}
// now item is either HTMLSyntax.VALUE or we're in text, or at BOF
if( id != HTMLTokenContext.VALUE && id != HTMLTokenContext.TEXT ) {
return getNextElement( beginning );
} // else ( for VALUE or TEXT ) fall through
}
if( id == HTMLTokenContext.WS || id == HTMLTokenContext.ARGUMENT || // these are possible only in Tags
id == HTMLTokenContext.OPERATOR || id == HTMLTokenContext.VALUE ) { // so find boundary
do {
item = item.getPrevious(); // Can't get null here, there IS TAG before WS|ARGUMENT|OPERATOR|VALUE
id = item.getTokenID();
} while( id != HTMLTokenContext.TAG );
return getNextElement( item.getOffset() ); // TAGC
}
if( id == HTMLTokenContext.TEXT ) {
while( id != null && (id == HTMLTokenContext.TEXT || id == HTMLTokenContext.CHARACTER) ) {
beginning = item.getOffset();
item = item.getPrevious();
id = item == null ? null : item.getTokenID();
}
return getNextElement( beginning ); // from start of Commment
}
if( id == HTMLTokenContext.TAG ) {
if( item.getImage().startsWith( "<" ) ) return getNextElement( item.getOffset() ); // TAGO/ETAGO // NOI18N
else {
do {
item = item.getPrevious();
id = item.getTokenID();
} while( id != HTMLTokenContext.TAG );
return getNextElement( item.getOffset() ); // TAGC
}
}
if( id == HTMLTokenContext.ERROR )
return new SyntaxElement( this, item.getOffset(), getTokenEnd( item ), SyntaxElement.TYPE_ERROR );
if( id == HTMLTokenContext.BLOCK_COMMENT ) {
while( id == HTMLTokenContext.BLOCK_COMMENT && !item.getImage().startsWith( " |