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-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.web.core.syntax;

import java.io.File;
import java.util.*;
import java.io.IOException;
import java.net.URL;
import javax.swing.text.BadLocationException;
import javax.swing.text.EditorKit;
import javax.swing.JEditorPane;
import javax.swing.text.JTextComponent;

import javax.servlet.jsp.tagext.*;

import org.openide.filesystems.FileObject;
import org.openide.ErrorManager;
import org.openide.loaders.DataObject;

import org.netbeans.modules.web.jsps.parserapi.JspParserAPI;
import org.netbeans.modules.web.jsps.parserapi.PageInfo;

import org.netbeans.editor.*;
import org.netbeans.editor.ext.ExtSyntaxSupport;
import org.netbeans.editor.ext.html.HTMLTokenContext;
import org.netbeans.editor.ext.java.JavaSyntaxSupport;
import org.netbeans.editor.ext.java.JavaTokenContext;
import org.netbeans.modules.editor.NbEditorUtilities;
import java.util.*;
import org.openide.filesystems.FileUtil;
import org.openide.modules.InstalledFileLocator;

/**
 *
 * @author  Petr Jiricka, Petr Nejedly
 */
public class JspSyntaxSupport extends ExtSyntaxSupport {

    /** ErrorManager shared by whole module (package) for logging */
    static final ErrorManager err = 
        ErrorManager.getDefault().getInstance("org.netbeans.modules.web.jspsyntax"); // NOI18N

    /* Constants for various contexts in the text from the point of
    view of JSP completion.*/
    
    /** Completion context for JSP tags (standard or custom) */
    public static final int TAG_COMPLETION_CONTEXT = 1;
    /** Completion context for JSP end tags (standard or custom) */
    public static final int ENDTAG_COMPLETION_CONTEXT = 2;
    /** Completion context for JSP directives */
    public static final int DIRECTIVE_COMPLETION_CONTEXT = 3;
    /** Completion context for JSP comments */
    public static final int COMMENT_COMPLETION_CONTEXT = 4;
    /** Completion context for other JSP text - such as body of custom tags 
     * with TAG_DEPENDENT body content. */
    public static final int TEXT_COMPLETION_CONTEXT = 5;
    /** Completion context for the content language */
    public static final int CONTENTL_COMPLETION_CONTEXT = 6;
    /** Completion context for the scripting language */
    public static final int SCRIPTINGL_COMPLETION_CONTEXT = 7;
    /** Completion context for error */
    public static final int ERROR_COMPLETION_CONTEXT = 8;
    
    private static final String STANDARD_JSP_PREFIX = "jsp";    // NOI18N
    /** Data for completion: TreeMap for standard JSP tags 
    * (tag name, array of attributes). */
    private static TagInfo[] standardJspTagDatas;
    
    private static TagInfo[] standardTagTagDatas;
    /** Data for completion, when the jsp page is in XML syntax
     **/
    private static TagInfo[] xmlJspTagDatas;
    
    /** Data for completion, when the tag file is in XML syntax
     **/
    private static TagInfo[] xmlTagFileTagDatas;
    
    /** Data for completion: TreeMap for JSP directives
    * (directive name, array of attributes). */
    private static TagInfo[] directiveJspData;
    private static TagInfo[] directiveTagFileData;
    
    /** Mapping the URI of tag library -> URL where the help files are. 
     */
    private static HashMap helpMap = null;
    
    private static final TokenID[] JSP_BRACKET_SKIP_TOKENS = new TokenID[] {
                JavaTokenContext.LINE_COMMENT,
                JavaTokenContext.BLOCK_COMMENT,
                JavaTokenContext.CHAR_LITERAL,
                JavaTokenContext.STRING_LITERAL,
                JspTagTokenContext.ATTR_VALUE,
                JspTagTokenContext.COMMENT
            };

    protected FileObject fobj;
    
    /** Content language SyntaxSupport cached for getContentLanguageSyntaxSupport */
    private ExtSyntaxSupport contentLanguageSyntaxSupport = null;
    
    /** Special bracket finder is used when caret is in JSP context */
    private boolean useCustomBracketFinder = true;
    
    private boolean isXmlSyntax = false;

    /** Creates new HTMLSyntaxSupport */
    
    public JspSyntaxSupport(BaseDocument doc, boolean isXml) {
        super(doc);
        fobj = null;
        if (doc != null){
            DataObject dobj = NbEditorUtilities.getDataObject(doc);
            fobj = (dobj != null) ? NbEditorUtilities.getDataObject(doc).getPrimaryFile(): null;
        }
        
        isXmlSyntax = isXml;
    }
    
    public JspSyntaxSupport(BaseDocument doc) {
        this(doc, false);
    }
    
    
    
    public boolean isXmlSyntax(){
        return isXmlSyntax;
    }
    
    protected JspParserAPI.ParseResult getParseResult() {
        JspParserAPI.ParseResult result = JspUtils.getCachedParseResult(getDocument(), fobj, true, false);
        if (result == null) {
            result = JspUtils.getCachedParseResult(getDocument(), fobj, false, false);
        }
        return result;
    }
    
    /** Returns a map of prefix -> URI that maps tag libraries on prefixes.
     * For the XML syntax this mapping may only be approximate.
     */
    private Map getPrefixMapper() {
        // PENDING - must also take xmlPrefixMapper into account
        JspParserAPI.ParseResult result = getParseResult();
        Map prefixMapper = null;
        if (result != null && result.getPageInfo() != null) {
            //if (result.isParsingSuccess()) {
                // PENDING - can we somehow get incomplete parsed information ?
            if (result.getPageInfo().getXMLPrefixMapper().size() > 0) {
                prefixMapper = result.getPageInfo().getApproxXmlPrefixMapper();
                if (prefixMapper.size() == 0){
                    prefixMapper = result.getPageInfo().getXMLPrefixMapper();
                }
                prefixMapper.putAll(result.getPageInfo().getJspPrefixMapper());

            }
            else {
                prefixMapper = result.getPageInfo().getJspPrefixMapper();
            }
            //}
        }
        return prefixMapper;
    }
    
    private Map getTagLibraries() {
        JspParserAPI.ParseResult result = getParseResult();
        if (result != null) {
            return result.getPageInfo().getTagLibraries();
        }
        return null;
    }
    
    private TagLibraryInfo getTagLibrary(String prefix) {
        Map mapper = getPrefixMapper();
        if (mapper != null) {
            Object uri = mapper.get(prefix);
            if (uri != null) {
                Map taglibs = getTagLibraries();
                if (taglibs != null) {
                    return (TagLibraryInfo)taglibs.get(uri);
                }
            }
        }
        return null;
    }
    
    protected SyntaxSupport createSyntaxSupport(Class syntaxSupportClass) {
        if (syntaxSupportClass.isAssignableFrom (JspJavaSyntaxSupport.class)) {
            return new JspJavaSyntaxSupport(getDocument(), this);
        }
        SyntaxSupport support = super.createSyntaxSupport(syntaxSupportClass);
        if (support != null)
            return support;
        //System.out.println("JspSyntaxSupport- createSyntaxSupport - " + NbEditorUtilities.getMimeType(getDocument()) );
        
        EditorKit kit;
        // try the content language support
        kit = JEditorPane.createEditorKitForContentType(JspUtils.getContentLanguage());
        if (kit instanceof BaseKit) {
            support = ((BaseKit)kit).createSyntaxSupport(getDocument());
            if (support != null)
                return support;
        }
        // try the scripting language support
        kit = JEditorPane.createEditorKitForContentType(JspUtils.getScriptingLanguage());
        if (kit instanceof BaseKit) {
            support = ((BaseKit)kit).createSyntaxSupport(getDocument());
            if (support != null)
                return support;
        }
        return null;
    }

    /** Returns SyntaxSupport corresponding to content type of JSP data object. 
     *  HTMLSyntaxSupport is used when we can't find it. */
    protected ExtSyntaxSupport getContentLanguageSyntaxSupport() {
        if (contentLanguageSyntaxSupport != null) {
            return contentLanguageSyntaxSupport;
        }
        
        EditorKit kit =
            JEditorPane.createEditorKitForContentType(JspUtils.getContentLanguage());
        if (kit instanceof BaseKit) {
            SyntaxSupport support = ((BaseKit)kit).createSyntaxSupport(getDocument());
            if (support != null && support instanceof ExtSyntaxSupport) {
                contentLanguageSyntaxSupport = (ExtSyntaxSupport) support;
                return contentLanguageSyntaxSupport;
            }
        }
        return (ExtSyntaxSupport)get( org.netbeans.editor.ext.html.HTMLSyntaxSupport.class );
    }

    /** This method decides what kind of completion (html, java, jsp-tag, ...) should be opened 
     * or whether the completion window should be closed if it is opened.
     */
    public int checkCompletion(JTextComponent target, String typedText, boolean visible ) {
        
        char first = typedText.charAt(0); //get typed char
        
        TokenItem item = null; //get token on the cursor
        try{
            item = getItemAtOrBefore(target.getCaret().getDot());
        }catch(BadLocationException e) {
            return COMPLETION_HIDE;
        }
        if (item == null) return COMPLETION_HIDE;
        
        TokenContextPath tcp = item.getTokenContextPath();

        //System.out.println("typed '" + first + "' ;token = " + item);
        
        if(tcp.contains(HTMLTokenContext.contextPath)) {
            //we are in content language
            if (err.isLoggable(ErrorManager.INFORMATIONAL)) err.log("CONTENTL_COMPLETION_CONTEXT");   // NOI18N
            ExtSyntaxSupport support = getContentLanguageSyntaxSupport();
            if (support != null) {
                return support.checkCompletion( target, typedText, visible );
            }
        }
        
        if(tcp.contains(JavaTokenContext.contextPath)) {
            //we are in scripting language
            if (err.isLoggable(ErrorManager.INFORMATIONAL)) err.log("SCRIPTINGL_COMPLETION_CONTEXT" );   // NOI18N
            if (JspUtils.getScriptingLanguage().equals("text/x-java")) { // NOI18N
                return ((ExtSyntaxSupport)get( org.netbeans.editor.ext.java.JavaSyntaxSupport.class )).checkCompletion( target, typedText, visible );
            }
        }
        
        //JSP tag or directive
        if(tcp.contains(JspTagTokenContext.contextPath)) {
            //need to distinguish tag/end_tag/directive - search back throught the token chain for <%@ ,  which is recognized as a JspTagToken, but before that there are java tokens
                if(image.equals("%>")) return COMPLETION_HIDE;
                
                if(tracking.getImage().startsWith("<%")) {
                    //we are in a directive
                    if (err.isLoggable(ErrorManager.INFORMATIONAL)) err.log("DIRECTIVE_COMPLETION_CONTEXT");   // NOI18N

                    //open completion also in such a case: <%=|
                    if( !visible && first == '=' && tracking.getImage().equals("<%")) return COMPLETION_POPUP;
                    
                    if( !visible && first == '%' || first == '@' || first == ' ' ) return COMPLETION_POPUP;
                    if( visible && first == '=' || first == '>' ) return COMPLETION_HIDE;
                    return visible ? COMPLETION_POST_REFRESH : COMPLETION_CANCEL;
                }
                if(tracking.getImage().equals("<")) {
                    //we are in a tag
                    if (err.isLoggable(ErrorManager.INFORMATIONAL)) err.log("TAG_COMPLETION_CONTEXT");   // NOI18N
                    if( !visible && first == ' ' || first == ':' ) return COMPLETION_POPUP;
                    if( visible && first == '>' ) return COMPLETION_HIDE;
                    return visible ? COMPLETION_POST_REFRESH : COMPLETION_CANCEL;
                }
                if(tracking.getImage().equals("' ) return COMPLETION_HIDE;
                    return visible ? COMPLETION_POST_REFRESH : COMPLETION_CANCEL;
                }
                //test whether we are still in the tag context
                if(!tracking.getTokenContextPath().contains(JspTagTokenContext.contextPath)) {
                    ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, new IllegalStateException("we are out of jsp tag without finding any tag start token!"));
                    break;
                }
                
                //search previous token
                tracking = tracking.getPrevious();
                
            } while(maxBacktrace-- > 0); 
            
        }//eof JSP tag
        
        if(tcp.contains(ELTokenContext.contextPath)) {
            //we are in expression language - we do not provide any code completion so far
            if (visible) return COMPLETION_HIDE;
        }
        
        return COMPLETION_HIDE;
    }
    
    /** Returns offset where the next offset after this offset starts. */
    private final int getTokenEnd( TokenItem item ) {
        if (item == null)
            return 0; //getDocument().getLength();
        return item.getOffset() + item.getImage().length();
    }
    
    /** Filters list of strings so only strings starting 
    * with a given prefix are returned in the new List. */
    public final List filterList(List toFilter, String prefix) {
        List newList = new ArrayList();
        Object item;
        for (int i = 0; i < toFilter.size(); i++) {
            item = toFilter.get(i);
            String txt;
            if (item instanceof TagInfo) 
                txt = ((TagInfo)item).getTagName ();
            else if (item instanceof TagAttributeInfo) 
                txt = ((TagAttributeInfo)item).getName ();
            else
                txt = (String)item;
            
            if (txt != null && txt.startsWith(prefix)) {
                newList.add(item);
            }
        }
        return newList;
    }
    
    /** Gets all 'jsp prefixes' whose 'string prefix' matches complPrefix as a list of Strings. */
    protected final List getTagPrefixes(String complPrefix) {
        return filterList(getAllTagPrefixes(), complPrefix);
    }
    
    /** Gets all tags whose 'string prefix' matches complPrefix as a list of Strings. 
    * Assumes that complPrefix also includes the 'jsp prefix'.
    */
    protected final List getTags(String complPrefix) {
        int colonIndex = complPrefix.indexOf(":");  // NOI18N
        if (colonIndex == -1)
            throw new IllegalArgumentException();
        return getTags(complPrefix.substring(0, colonIndex), 
                       complPrefix.substring(colonIndex + 1));
    }
    
    /** Gets all tags whose 'string prefix' matches complPrefix and whose 'jsp prefix'
    * is tagPrefix as a list of Strings. 
    * Assumes that complPrefix does not include the 'jsp prefix'.
    */
    protected final List getTags(String tagPrefix, String complPrefix) {
        return filterList(getAllTags(tagPrefix), complPrefix);
    }
    
    /** Gets attributes for tag whose prefix + name
    * is tagPrefixName as a list of Strings. 
    * The attribute's 'string prefix' must match complPrefix.
    */
    protected final List getTagAttributes(String tagPrefixName, String complPrefix) {
        int colonIndex = tagPrefixName.indexOf(":");    // NOI18N
        if (colonIndex == -1)
            throw new IllegalArgumentException();
        return getTagAttributes(tagPrefixName.substring(0, colonIndex), 
                       tagPrefixName.substring(colonIndex + 1), complPrefix);
    }
    
    /** Gets attributes for tag whose 'jsp prefix'
    * is tagPrefix and whose tag name is tagName as a list of Strings. 
    * The attribute's 'string prefix' must match complPrefix.
    */
    protected final List getTagAttributes(String tagPrefix, String tagName, String complPrefix) {
        return filterList(getAllTagAttributes(tagPrefix, tagName), complPrefix);
    }
    
    /** Gets all directives whose 'string prefix' matches complPrefix as a list of Strings. */
    protected final List getDirectives(String complPrefix) {
        return filterList(getAllDirectives(), complPrefix);
    }
    
    /** Gets attributes for directive directive as a list of Strings.
    * The attribute's 'string prefix' must match complPrefix.  */
    protected final List getDirectiveAttributes(String directive, String complPrefix) {
        return filterList(getAllDirectiveAttributes(directive), complPrefix);
    }
    
    /** 
    *  Returns a list of strings - prefixes available in this support context (JSP file).
    */
    protected List getAllTagPrefixes() {
        List items = new ArrayList();
        
        // jsp: prefix
        items.add(STANDARD_JSP_PREFIX);
        
        Map mapper = getPrefixMapper();
        if (mapper != null) {
            // sort it
            TreeSet ts = new TreeSet();
            ts.addAll(mapper.keySet());
            ts.remove("jsp"); // remove jsp prefix when is declared in xml syntax
            items.addAll(ts);
        }
        // prefixes for tag libraries
/*        TagLibParseSupport support = (dobj == null) ? 
            null : (TagLibParseSupport)dobj.getCookie(TagLibParseSupport.class);
        if (support != null) {
            // add all prefixes from the support
            TagLibParseSupport.TagLibData[] tagLibData = support.getTagLibEditorData().getTagLibData();
            for (int i = 0; i < tagLibData.length; i++) 
                items.add(tagLibData[i].getPrefix());
        }
*/        
        return items;
    }
    
    /**  Returns a list of strings - tag names available for a particular prefix.
     */
    protected List getAllTags(String prefix) {
        List items = new ArrayList();
        
        
        
        // standard JSP tags (jsp:)
        initCompletionData();
        if (STANDARD_JSP_PREFIX.equals(prefix)) {
            TagInfo[] stanTagDatas = getTagInfos();
            for (int i=0; i return nothing.
        if (isXmlSyntax) return items;
        
        TagInfo[] directiveData;
        if(NbEditorUtilities.getMimeType(getDocument()).equals(JspUtils.TAG_MIME_TYPE))
            directiveData = directiveTagFileData;
        else {
            directiveData = directiveJspData;
            
        }
        for (int i = 0; i < directiveData.length; i++){
                items.add(directiveData[i]);
            }
        return items;
    }
    
    /** Should be overriden ny subclasses to support JSP 1.1. */
    protected List getAllDirectiveAttributes(String directive) {
        initCompletionData();
        List items = new ArrayList();
        //Is xml syntax? => return nothing.
        if (isXmlSyntax) return items;
        
        TagInfo[] directiveData;
        if(NbEditorUtilities.getMimeType(getDocument()).equals(JspUtils.TAG_MIME_TYPE))
            directiveData = directiveTagFileData;
        else
            directiveData = directiveJspData;
        for (int i=0; ioffset
     * This method is largely a workaround for a bug in getTokenChain().
     * If offset falls right between two items, returns one which is just before
     * offset. If offset == 0, retruns null. */
    public TokenItem getItemAtOrBefore(int offset) throws BadLocationException {
        TokenItem backItem = null;
        int chainLength = 100;
        while (backItem == null) {
            if (offset < getDocument().getLength()) {
                backItem = getTokenChain( offset, 
                    Math.min(offset + chainLength, getDocument().getLength())/*, false*/ );
            }
            else {
                // @ end of document
                backItem = getTokenChain (Math.max (offset-50, 0), offset);
            }
                
            if (chainLength++ > 1000)
                break;
        }
        if (backItem == null)
            return null;
        
        // forward to the offset where our token definitely is
//System.out.println("looking for item at offset " + offset);        
//System.out.println("backitem " + backItem);
        TokenItem item;
        while (true) {
            item = backItem.getNext();
//System.out.println("looking at item " + item);
            if (item == null) {
                item = backItem;
//System.out.println("break1");
                break;
            }
            if (item.getOffset() > offset) {
                item = backItem;
//System.out.println("break2");
                break;
            }
            backItem = item;
//System.out.println("backitem2 " + backItem);
        }
        
//System.out.println("REAL Token at offset " + offset + " is " + item );
        TokenItem adjustedItem = (item.getOffset() == offset) ? 
            item.getPrevious() : item;
//System.out.println("ADJUSTED Token at offset " + offset + " is " + adjustedItem );

        TokenID id = (adjustedItem == null) ?
            item.getTokenID() : adjustedItem.getTokenID();
//System.out.println("TokenID (adjusted) at offset " + offset + " is " + id );
        return adjustedItem;
    }
    
    /** 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 before the offset
     */
    public SyntaxElement getElementChain( int offset ) throws BadLocationException {
        TokenItem item = getItemAtOrBefore(offset);
        if (item == null)
            return null;
        TokenID id = item.getTokenID();
        
        if (id == JspTagTokenContext.COMMENT || 
            id == JspTagTokenContext.ERROR || 
            id == JspTagTokenContext.TEXT ||
            id == JspMultiTokenContext.ERROR) {
//System.out.println("uninteresting JspTag token");
            return null;
        }
        
        if (id == JspTagTokenContext.SYMBOL2) {
//System.out.println("just at symbol");
            if ((getTokenEnd(item) == offset) && isScriptStartToken(item)) {
                return getScriptingChain(item.getNext(), offset);
            }
            if ((getTokenEnd(item) == offset) && isScriptEndToken(item)) {
                TokenItem nextItem = item.getNext();
                if (!isTagDirToken(item))
                    return getContentChain(nextItem, offset);
            }
            return null;
        }
        
        if (id == JspTagTokenContext.TAG ||
            id == JspTagTokenContext.SYMBOL ||
            id == JspTagTokenContext.ATTRIBUTE ||
            id == JspTagTokenContext.ATTR_VALUE ||
            id == JspTagTokenContext.EOL) {
            // may be intetesting: tag, directive, 
            // but may also be a comment. Look back for SYMBOL: <, ")) // NOI18N
                return true;
        }
        return false;
    }
    
    /** Returns true if item is an item which can be INSIDE 
     * a JSP tag or directive (i.e. excuding delimeters). */
    private boolean isInnerTagDirToken(TokenItem item) {
        if (!isTagDirToken(item))
            return false;
        TokenID id = item.getTokenID();
        if (id == JspTagTokenContext.SYMBOL) {
            String image = item.getImage();
            if (image.equals("<") ||    // NOI18N
                image.equals("") ||   // NOI18N
                image.equals(">") ||    // NOI18N
                image.equals("/>"))     // NOI18N
                return false;
        }
        return true;
    }
        
    /** Returns true if item is an item which can be INSIDE 
     * a JSP tag or directive (i.e. excuding delimeters). */
    private boolean isTagDirToken(TokenItem item) {
        if (item == null)
            return false;
        TokenID id = item.getTokenID();
        if (id == null)
            return false;
        if ((id != JspTagTokenContext.TEXT) &&
            (id != JspTagTokenContext.ERROR) &&
            (id != JspTagTokenContext.TAG) &&
            (id != JspTagTokenContext.SYMBOL) &&
            (id != JspTagTokenContext.ATTRIBUTE) &&
            (id != JspTagTokenContext.ATTR_VALUE) &&
            (id != JspTagTokenContext.EOL)) {
            return false;
        }
        // PENDING - EOL can still be a comment
        return true;
    }

    /** Return true if this item does not belong to JSP syntax
     *  and belongs to one of the syntaxes we delegate to. */
    private boolean isScriptingOrContentToken(TokenItem item) {
        if (item == null)
            return true;
        TokenID id = item.getTokenID();
        if (id == null)
            return true;
        if ((id == JspTagTokenContext.TEXT) ||
            (id == JspTagTokenContext.ERROR) ||
            (id == JspTagTokenContext.TAG) ||
            (id == JspTagTokenContext.SYMBOL) ||
            (id == JspTagTokenContext.COMMENT) ||
            (id == JspTagTokenContext.ATTRIBUTE) ||
            (id == JspTagTokenContext.ATTR_VALUE) ||
            (id == JspTagTokenContext.SYMBOL2) ||
            (id == JspTagTokenContext.EOL) ||
            (id == JspMultiTokenContext.ERROR))
            return false;
        return true;
    }
    
    public boolean isValueBeginning(String text) {
        if (text.trim().endsWith("\"\""))   // NOI18N
            return false;
        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            if ((c != ' ') &&
                (c != '=') &&
                (c != '"'))
                return false;
        }
        return true;
    }
        
    // ------- METHODS FOR CONSTRUCTING SEMANTICALLY LIKNKED CHAINS OF TOKENS ------
    
    /** Gets an element representing a tag or directive starting with token item firstToken. */
    private SyntaxElement getTagOrDirectiveChain(boolean tag, TokenItem firstToken, int offset) {
        TokenItem item = firstToken.getNext();
        String name = getWholeWord(item, JspTagTokenContext.TAG);
        while ((item != null) && (item.getTokenID() == JspTagTokenContext.TAG))
            item = item.getNext();
        TreeMap attributes = new TreeMap();
        while (isInnerTagDirToken(item)) {
            // collect the attributes
            if (item.getTokenID() == JspTagTokenContext.ATTRIBUTE) {
                String attributeName = getWholeWord(item, JspTagTokenContext.ATTRIBUTE);
                // forward to the next non-ATTRIBUTE token
                while ((item != null) && (item.getTokenID() == JspTagTokenContext.ATTRIBUTE))
                    item = item.getNext();
                // find the value
                while ((item != null) && 
                       (item.getTokenID() == JspTagTokenContext.SYMBOL) &&
                       (isValueBeginning(item.getImage())))
                    item = item.getNext();
                StringBuffer value = new StringBuffer();
                while ((item != null) && (item.getTokenID() == JspTagTokenContext.ATTR_VALUE)) {
                    value.append(item.getImage());
                    item = item.getNext();
                    // request time values
                    if ((item != null) && (item.getTokenID() == JspTagTokenContext.SYMBOL2)) {
                        // scripting language - something like request time value of a JSP tag
                        while (!isScriptEndToken(item)) {
                            if (item == null)
                                break;
                            else {
                                value.append(item.getImage());
                                item = item.getNext();
                            }
                        }
                        // now it's a script end token
                        if (item != null) {
                            value.append(item.getImage());
                            item = item.getNext();
                        }
                    }
                }
                String vString = value.toString();
                // cut off the beginning and ending quotes
                if (vString.startsWith("\""))   // NOI18N
                    vString = vString.substring(1);
                if (vString.endsWith("\""))     // NOI18N
                    vString = vString.substring(0, vString.length() - 1);
                attributes.put(attributeName, vString);
                continue;
            }
            if (item.getTokenID() == JspTagTokenContext.SYMBOL2) {
                // scripting language - something like request time value of a JSP tag
                while (!isScriptEndToken(item)) {
                    if (item == null)
                        break;
                    else
                        item = item.getNext();
                }
                // now it's a script end token
                if (item != null)
                    item = item.getNext();
                continue;
            }
            // a token I am not interested in
            item = item.getNext();
        }
        if (tag) {
            boolean endslash= false;
            if (item != null)
                endslash = (item.getImage ().equals ("/>"))? true: false;   // NOI18N
                
            return new SyntaxElement.Tag(this, firstToken.getOffset(), 
                (item != null)? getTokenEnd(item): getDocument ().getLength (), 
                name, attributes, endslash);
        }    
        else {
            return new SyntaxElement.Directive(this, firstToken.getOffset(), 
                (item != null)? getTokenEnd(item): getDocument ().getLength (), 
                name, attributes);
        }    
    }
    
    private SyntaxElement getEndTagChain(TokenItem firstToken, int offset) {
        TokenItem item = firstToken.getNext();
        String name = getWholeWord(item, JspTagTokenContext.TAG);
        while ((item != null) && (item.getTokenID() == JspTagTokenContext.TAG))
            item = item.getNext();
        while (isInnerTagDirToken(item)) {
            item = item.getNext();
        }
        return new SyntaxElement.EndTag(this, firstToken.getOffset(), 
            getTokenEnd(item), name);
    }
    
    private String getWholeWord(TokenItem firstToken, TokenID requestedTokenID) {
        StringBuffer sb = new StringBuffer();
        while ((firstToken != null) && (firstToken.getTokenID() == requestedTokenID)) {
            sb.append(firstToken.getImage());
            firstToken = firstToken.getNext();
        }
        return sb.toString().trim();
    }

    /** Returns an element of scripting language starting with firstToken. 
     * If forstToken is null, returns element representing end of the document. 
     */
    private SyntaxElement getScriptingChain(TokenItem firstToken, int offset) {
        if (firstToken == null) {
            return new SyntaxElement.ScriptingL(this, 
                getDocument().getLength(), getDocument().getLength());
        }
        TokenItem item = firstToken;
        do {
            TokenItem nextItem = item.getNext();
            if (nextItem == null) {
                return new SyntaxElement.ScriptingL(this, 
                    firstToken.getOffset(), getDocument().getLength());
            }
            if (!isScriptingOrContentToken(nextItem))
                return new SyntaxElement.ScriptingL(this, 
                    firstToken.getOffset(), getTokenEnd(item));
            item = nextItem;
        }
        while (true);
    }
    
    /** Returns an element of content language starting with firstToken. 
     * If forstToken is null, returns element representing end of the document. 
     */
    private SyntaxElement getContentChain(TokenItem firstToken, int offset) {
        if (firstToken == null) {
            return new SyntaxElement.ContentL(this, 
                getDocument().getLength(), getDocument().getLength());
        }
        TokenItem item = firstToken;
        do {
            TokenItem nextItem = item.getNext();
            if (nextItem == null) {
                return new SyntaxElement.ContentL(this, 
                    firstToken.getOffset(), getDocument().getLength());
            }
            if (!isScriptingOrContentToken(nextItem))
                return new SyntaxElement.ContentL(this, 
                    firstToken.getOffset(), getTokenEnd(item));
            item = nextItem;
        }
        while (true);
    }

    /** The way how to get previous SyntaxElement in document. It is not intended
     * for direct usage, and thus is not public. Usually, it is called from
     * SyntaxElement's method getPrevious()
     */
    SyntaxElement getPreviousElement( int offset ) throws BadLocationException {
        if (offset == 0)
            return null;
        SyntaxElement elem = null;
        offset--;
        do {
            elem = getElementChain( offset);
            if (elem == null){
                TokenItem ti = getItemAtOrBefore(offset);
                if (ti == null)
                    return null;
                offset = ti.getOffset() -1 ;
            }
        } while (elem == null && offset >= 0);
        return elem;
    }

    public List getPossibleEndTags (int offset, String pattern) throws BadLocationException {
        SyntaxElement elem = getElementChain( offset );
        Stack stack = new Stack();
        List result = new ArrayList();
        Set found = new HashSet();
        
        if( elem != null ) {
            elem = elem.getPrevious();  // we need smtg. before our  0 ) {
                elem = getElementChain( offset-1 );
            } else { // beginning of document too, not much we can do on empty doc
                return result;
            }
        }
        

        for( ; elem != null; elem = elem.getPrevious() ) {
            
            if( elem instanceof SyntaxElement.EndTag ) {
                stack.push( ((SyntaxElement.EndTag)elem).getName() );
            } else if( elem instanceof SyntaxElement.Tag ) {
                SyntaxElement.Tag tag = (SyntaxElement.Tag)elem;
                
                if (tag.isClosed ())
                    continue;
                
                String image = tag.getName ();
                String prefix = image.substring (0, image.indexOf (':'));
                String name = image.substring (image.indexOf (':')+1);
                TagInfo ti = null;

                TagLibraryInfo tli = getTagLibrary(prefix);
                if (tli != null) {
                    ti = tli.getTag(name);
                }
                
                if (STANDARD_JSP_PREFIX.equals (prefix)) { 
                    initCompletionData ();
                    TagInfo[] stanTagDatas = getTagInfos();
                    for (int i=0; i' as bracket chars. It is set to be used 
     * in findMatchingBlock.
     */
    protected ExtSyntaxSupport.BracketFinder getMatchingBracketFinder (char bracketChar) {
        if (useCustomBracketFinder) {
            JspSyntaxSupport.BracketFinder bf = new JspSyntaxSupport.BracketFinder (bracketChar);
            return bf.isValid ()? bf: null;
        }
	else{
            return super.getMatchingBracketFinder (bracketChar); 
	}
    }
    
    /** Find matching bracket or more generally block
     * that matches with the current position.
     * @param offset position of the starting bracket
     * @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 - jsp tag matching
	     */
	    int [] r_value = null;
	    
	    TokenItem token = getItemAtOrBefore ((offset' || token.getImage().charAt(0)== '<'))){
					    token = token.getNext();
					}
					if (token != null)
					    end = token.getOffset()+1;
					return new int[] {start, end};
				    }
				    else {
					poss++;
				    }
				}
				if (token.getImage().length() == 2){
				    poss--;
				}
			    }

			}
		    }				    
		    token = token.getPrevious();
		}

	    }
	    else{
		if ((token.getImage().length() == 1) && token.getImage().charAt(0) == '<'){
		    poss = 1;
		    TokenItem hToken;
		    while ( token != null){
			if (token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID) {
			    if (token.getImage().trim().equals(tag)){
				hToken = token;
				while (token != null && token.getTokenID().getNumericID() != JspTagTokenContext.SYMBOL_ID) {				
				    token = token.getPrevious();				
				}
				if (token != null) {
				    if (token.getImage().length() == 2){
					if (poss == 0){
					    start = token.getOffset();
					    end = hToken.getOffset()+hToken.getImage().length()+1;
					    token = token.getNext();

					    while (token != null && (token.getTokenID().getNumericID() != JspTagTokenContext.SYMBOL_ID
						|| token.getImage().charAt(0)!='>')){
						token = token.getNext();
					    }
					    if (token != null)
						end = token.getOffset()+1;
					    return new int[] {start, end};
					}
					else {
					    poss++;
					}
				    }
				    if (token.getImage().length() == 1){
					poss--;
				    }
				}
				token = hToken;
			    }
			}				    
			token = token.getNext();
		    }
		}
	    }
	}
	return null;
    }
    
    /** Get the array of token IDs that should be skipped when
     * searching for matching bracket. It usually includes comments
     * and character and string constants. Returns empty array by default.
     */
    protected TokenID[] getBracketSkipTokens () {
        return JSP_BRACKET_SKIP_TOKENS;
    }
    
    /** Finder for the matching bracket. It gets the original bracket char
    * and searches for the appropriate matching bracket character.
    */
    public class BracketFinder extends ExtSyntaxSupport.BracketFinder {
        
        BracketFinder (char c) { 
            super (c);
        }
        
        /** Check whether the bracketChar really contains
         * the bracket character. If so assign the matchChar
         * and moveCount variables.
         */
        protected boolean updateStatus() {
            if (super.updateStatus ())
                return true;
            boolean valid = true;
            switch (bracketChar) {
                case '<':
                    matchChar = '>';
                    moveCount = +1;
                    break;
                case '>':
                    matchChar = '<';
                    moveCount = -1;
                    break;
                default:
                    valid = false;
            }
            return valid;
        }
        
        boolean isValid () {
            return (moveCount != 0);
        }

    }    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.