alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

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( "