blob: b965fc3b8082a448bb8baf727f3d8f5cc817daa2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.web.core.syntax;
import java.io.File;
import java.util.*;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.JTextComponent;
import javax.servlet.jsp.tagext.*;
import org.netbeans.api.lexer.Language;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.modules.web.core.syntax.completion.api.JspCompletionItem;
import org.netbeans.modules.web.core.syntax.deprecated.ELTokenContext;
import org.netbeans.modules.web.core.syntax.deprecated.JspDirectiveTokenContext;
import org.netbeans.modules.web.core.syntax.deprecated.JspMultiTokenContext;
import org.netbeans.modules.web.core.syntax.deprecated.JspTagTokenContext;
import org.netbeans.modules.web.core.api.JspContextInfo;
import org.netbeans.modules.web.jsps.parserapi.JspParserAPI.JspOpenInfo;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.openide.filesystems.FileAttributeEvent;
import org.openide.filesystems.FileChangeListener;
import org.openide.filesystems.FileEvent;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileRenameEvent;
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.java.JavaTokenContext;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.modules.html.editor.api.completion.HtmlCompletionItem;
import org.openide.filesystems.FileUtil;
import org.openide.modules.InstalledFileLocator;
import org.openide.text.CloneableEditorSupport;
import org.openide.util.WeakListeners;
/**
*
* @author Petr Jiricka, Petr Nejedly
* @author Marek.Fukala@Sun.COM
*/
public class JspSyntaxSupport extends ExtSyntaxSupport implements FileChangeListener {
/** ErrorManager shared by whole module (package) for logging */
static final Logger err =
Logger.getLogger("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;
/** Completion context for expression language */
public static final int EL_COMPLETION_CONTEXT = 9;
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;
/** Special bracket finder is used when caret is in JSP context */
private boolean useCustomBracketFinder = true;
/** Creates new JspSyntaxSupport */
public static synchronized JspSyntaxSupport get(Document doc) {
JspSyntaxSupport sup = (JspSyntaxSupport)doc.getProperty(JspSyntaxSupport.class);
if(sup == null) {
sup = new JspSyntaxSupport((BaseDocument)doc);
doc.putProperty(JspSyntaxSupport.class, sup);
}
return sup;
}
public JspSyntaxSupport(BaseDocument doc) {
super(doc);
fobj = null;
if (doc != null){
initFileObject();
}
}
private void initFileObject() {
DataObject dobj = NbEditorUtilities.getDataObject(getDocument());
if(dobj != null) {
fobj = NbEditorUtilities.getDataObject(getDocument()).getPrimaryFile();
fobj.addFileChangeListener(WeakListeners.create(FileChangeListener.class, this, fobj));
}
}
public String[] getImports(){
JspParserAPI.ParseResult pre = getParseResult();
if (pre != null){
PageInfo pi = pre.getPageInfo();
if(pi == null) {
//report error but do not break the entire CC
err.log(Level.WARNING, null, new NullPointerException("PageInfo obtained from JspParserAPI.ParseResult is null"));
return null;
}
List<String> imports = pi.getImports();
return imports.toArray(new String[imports.size()]);
}
return null;
}
public boolean isXmlSyntax(){
JspContextInfo jspCO = JspContextInfo.getContextInfo(fobj);
if(jspCO == null) {
return false;
}
JspOpenInfo jspOpenInfo = jspCO.getCachedOpenInfo(fobj, false);
if(jspOpenInfo == null) {
return false;
}
return jspOpenInfo.isXmlSyntax();
}
protected JspParserAPI.ParseResult getParseResult() {
JspParserAPI.ParseResult result = JspUtils.getCachedParseResult(fobj, true, false);
if (result == null) {
result = JspUtils.getCachedParseResult(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.
*/
protected 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(boolean requiresFresh) {
//refresh tag libraries mappings - this call causes the WebAppParseSupport to refresh taglibs mapping
getTagLibraryMappings();
//if requiresFresh force the parser to update the parse information for the file
JspParserAPI.ParseResult result = JspUtils.getCachedParseResult(fobj, false, requiresFresh, requiresFresh);
if (result != null) {
PageInfo pi = result.getPageInfo();
if(pi == null) {
//report error but do not break the entire CC
err.log(Level.INFO, null, new NullPointerException("PageInfo obtained from JspParserAPI.ParseResult is null"));
return null;
} else {
return pi.getTagLibraries();
}
}
return null; //an error
}
private TagInfo[] getSortedTagInfos(TagInfo[] tinfos) {
Arrays.sort(tinfos, new Comparator() {
public int compare(Object o1, Object o2) {
TagInfo ti1 = (TagInfo)o1;
TagInfo ti2 = (TagInfo)o2;
String tname1 = (ti1.getDisplayName() == null ? ti1.getTagName() : ti1.getDisplayName());
String tname2 = (ti2.getDisplayName() == null ? ti2.getTagName() : ti2.getDisplayName());
if(tname1 == null || tname2 == null) return 0;
return tname1.compareTo(tname2);
}
@Override
public boolean equals(Object o) {
return o.equals(this);
}
});
return tinfos;
}
private TagLibraryInfo getTagLibrary(String prefix, boolean requiresFresh) {
Map mapper = getPrefixMapper();
if (mapper != null) {
Object uri = mapper.get(prefix);
if (uri != null) {
Map taglibs = getTagLibraries(requiresFresh);
if (taglibs != null) {
return (TagLibraryInfo)taglibs.get(uri);
}
}
}
return null;
}
@Override
protected SyntaxSupport createSyntaxSupport(Class syntaxSupportClass) {
SyntaxSupport support = super.createSyntaxSupport(syntaxSupportClass);
if (support != null)
return support;
EditorKit kit;
// try the content language support
kit = CloneableEditorSupport.getEditorKit("text/html"); //NOI18N
if (kit instanceof BaseKit) {
support = ((BaseKit)kit).createSyntaxSupport(getDocument());
if (support != null)
return support;
}
// try the scripting language support
kit = CloneableEditorSupport.getEditorKit("text/x-java"); //NOI18N
if (kit instanceof BaseKit) {
support = ((BaseKit)kit).createSyntaxSupport(getDocument());
if (support != null)
return support;
}
return null;
}
/** 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.
*/
@Override
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();
if(tcp.contains(JavaTokenContext.contextPath)) {
return COMPLETION_CANCEL; //the JavaCompletionProvider handles this
}
//JSP tag or directive
if(tcp.contains(JspTagTokenContext.contextPath)) {
//need to distinguish tag/end_tag/directive - search back throught the token chain for <%@ , </ or < tokens
TokenItem tracking = item;
//the maxBacktrace number says how far back we are willing to look for
//the start tag tokens <%, </ and < (simply the JSP tag or directive beginning).
//There may happen a situation when user starts to write a tag that the a large
//part of the document behind the cursor is recognized as a jsp tag.
//In such a case typing somewhere in the end of this 'badly' recognized block
//would slow down the typing rapidy due to the backtracking oven a long token chain.
//
//So we suppose that tag has less than 20 attributes (and one attribute-value pair consumes 4 tokens)
int maxBacktrace = 20 * 4;
do {
//test whether the token is not an error token
if(tracking.getTokenID() == JspTagTokenContext.ERROR) return COMPLETION_HIDE;
String image = tracking.getImage();
//System.out.println("tracking: " + tracking);
//this is a case when use types %> 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(Level.FINE)) err.log(Level.FINE, "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(Level.FINE)) err.log(Level.FINE, "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("</")) {
//we are in an end tag
if (err.isLoggable(Level.FINE)) err.log(Level.FINE, "ENDTAG_COMPLETION_CONTEXT"); // NOI18N
if( visible && first == '>' ) 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)) {
if (err.isLoggable(Level.FINE)) err.log(Level.FINE, "We are out of jsp tag without finding any tag start token!");
break;
}
if(tracking.getImage().equals(">")) {
try {
//check if the cursor is behind an open tag
SyntaxElement se = getElementChain(tracking.getOffset());
if(se != null && (se instanceof SyntaxElement.Tag)) {
return COMPLETION_POPUP;
}
}catch(BadLocationException e) {
//do nothing
}
return COMPLETION_HIDE;
}
//search previous token
tracking = tracking.getPrevious();
} while((maxBacktrace-- > 0) && (tracking != null));
}//eof JSP tag
if(tcp.contains(ELTokenContext.contextPath)) {
if(first == '.') {
return COMPLETION_POPUP;
}
}
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 static final List<Object> filterList(List<? extends Object> toFilter, String prefix) {
List<Object> newList = new ArrayList<Object>();
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;
}
/** Filters list of strings so only strings starting
* with a given prefix are returned in the new List. */
public static final List<String> filterStrings(List<String> toFilter, String prefix) {
List<String> newList = new ArrayList<String>();
for(String val : toFilter) {
if(val.startsWith(prefix)) {
newList.add(val);
}
}
return newList;
}
/** Gets all 'jsp prefixes' whose 'string prefix' matches complPrefix as a list of Strings. */
public final List<Object> 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'.
*/
public 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'.
*/
public final List getTags(String tagPrefix, String complPrefix) {
return filterList(getAllTags(tagPrefix, true), complPrefix);
}
/** Gets attributes for tag whose prefix + name
* is tagPrefixName as a list of Strings.
* The attribute's 'string prefix' must match complPrefix.
*/
public 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. */
public final List getDirectives(String complPrefix) {
return filterList(getAllDirectives(), complPrefix);
}
/** Gets attributes for directive <code>directive</code> as a list of Strings.
* The attribute's 'string prefix' must match complPrefix. */
public 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;
}
/** This method merges tagInfos of tags and file tags from a tagLibrary.
* @param tagLibrary
* @return array of TagInfo of all tags and tag files from the library
* or an empty array of if there is not any tag or tagfile defined in the library.
*/
private TagInfo[] getAllTagInfos(TagLibraryInfo tagLibrary) {
// if there is not any tag or file tag, then the empty array is returned
TagInfo[] allTags = new TagInfo[0];
if (tagLibrary != null) {
int tagInfosLength = 0;
int tagAllInfosLength = 0;
TagInfo[] tagInfos = tagLibrary.getTags();
TagFileInfo[] tagFileInfos = tagLibrary.getTagFiles();
if (tagInfos != null) { // it can be null, when the jsp parser finished unexpectedly
tagInfosLength = tagInfos.length;
tagAllInfosLength = tagInfosLength;
}
if (tagFileInfos != null) { // it can be null, when the jsp parser finished unexpectedly
tagAllInfosLength = tagAllInfosLength + tagFileInfos.length;
}
allTags = new TagInfo[tagAllInfosLength];
if (tagInfos != null) {
for (int i = 0; i < tagInfosLength; i++) {
allTags[i] = tagInfos[i];
}
}
if (tagFileInfos != null) {
for (int i = 0; i < tagFileInfos.length; i++) {
allTags[tagInfosLength + i] = tagFileInfos[i].getTagInfo();
}
}
}
return allTags;
}
/** Returns a list of strings - tag names available for a particular prefix.
*/
protected List getAllTags(String prefix, boolean requiresFresh) {
List items = new ArrayList();
// standard JSP tags (jsp:)
initCompletionData();
if (STANDARD_JSP_PREFIX.equals(prefix)) {
TagInfo[] stanTagDatas = getTagInfos();
for (int i=0; i<stanTagDatas.length; i++) {
items.add(stanTagDatas[i]);
}
}
TagLibraryInfo info = getTagLibrary(prefix, requiresFresh);
TagInfo[] tags = null;
if (info != null) {
tags = getAllTagInfos(info);
}
if (tags != null) {
tags = getSortedTagInfos(tags);
String url = (String)helpMap.get(info.getURI());
if (url != null && !url.equals("")){
for (int i = 0; i < tags.length; i++) {
items.add(new TagInfo(tags[i].getTagName(),
tags[i].getTagClassName(), tags[i].getBodyContent(),
url + tags[i].getTagName() + ".html#tag-start-" + tags[i].getTagName()
+ "#tag-end-" + tags[i].getTagName(), info,
tags[i].getTagExtraInfo(), tags[i].getAttributes(),
tags[i].getDisplayName(), tags[i].getSmallIcon(), tags[i].getLargeIcon(),
tags[i].getTagVariableInfos(), tags[i].hasDynamicAttributes()));
}
} else {
for (int i = 0; i < tags.length; i++) {
items.add(tags[i]);
}
}
}
return items;
}
/** Should be overriden ny subclasses to support JSP 1.1.
* Returns a list of strings - attribute names available for a particular prefix and tag name.
*/
protected List getAllTagAttributes(String prefix, String tag) {
List items = new ArrayList();
// attributes for standard JSP tags (jsp:)
initCompletionData();
if (STANDARD_JSP_PREFIX.equals(prefix)) {
TagInfo[] stanTagDatas = getTagInfos();
for (int i=0; i<stanTagDatas.length; i++) {
if (stanTagDatas[i].getTagName().equals(tag)) {
TagAttributeInfo[] attrs = stanTagDatas[i].getAttributes();
for (int j=0; j<attrs.length; j++)
items.add(attrs[j]);
break;
}
}
}
TagLibraryInfo info = getTagLibrary(prefix, true);
if (info != null) {
TagInfo tagInfo = info.getTag(tag);
if (tagInfo == null) {
TagFileInfo tagFile = info.getTagFile(tag);
if (tagFile != null) {
tagInfo = tagFile.getTagInfo();
}
}
if (tagInfo != null) {
TagAttributeInfo[] attributes = tagInfo.getAttributes();
String url = (String)helpMap.get(tagInfo.getTagLibrary().getURI());
if (url != null && !url.equals("")){
for (int i = 0; i < attributes.length; i++) {
items.add(new TagAttributeInfo(attributes[i].getName(),
attributes[i].isRequired(),
url + tagInfo.getTagName() + ".html#attribute-start-" + attributes[i].getName()
+ "#attribute-end-" + attributes[i].getName(),
attributes[i].canBeRequestTime(),
attributes[i].isFragment()));
}
} else {
for (int i = 0; i < attributes.length; i++)
items.add(attributes[i]);
}
}
}
return items;
}
/** Should be overriden ny subclasses to support JSP 1.1. */
protected List<TagInfo> getAllDirectives() {
initCompletionData();
List<TagInfo> items = new ArrayList<TagInfo>();
//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; 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; i<directiveData.length; i++) {
if (directiveData[i].getTagName().equals(directive)) {
TagAttributeInfo[] attrs = directiveData[i].getAttributes();
for (int j=0; j<attrs.length; j++)
items.add(attrs[j]);
break;
}
}
return items;
}
public PageInfo.BeanData[] getBeanData() {
JspParserAPI.ParseResult result = getParseResult();
if (result != null) {
PageInfo pageInfo = result.getPageInfo();
//pageInfo can be null in some cases when the parser cannot parse
//the webmodule or the page itself
if(pageInfo != null) {
return pageInfo.getBeans();
}
}
/*TagLibParseSupport support = (dobj == null) ?
null : (TagLibParseSupport)dobj.getCookie(TagLibParseSupport.class);
return support.getTagLibEditorData().getBeanData();*/
return null;
}
public boolean isErrorPage() {
JspParserAPI.ParseResult result = getParseResult();
if (result != null) {
if (result.getPageInfo() != null)
return result.getPageInfo().isErrorPage();
}
/*TagLibParseSupport support = (dobj == null) ?
null : (TagLibParseSupport)dobj.getCookie(TagLibParseSupport.class);
return support.getTagLibEditorData().isErrorPage ();*/
return false;
}
/**
* The mapping of the 'global' tag library URI to the location
* (resource path) of the TLD associated with that tag library.
* The location is returned as a String array:
* [0] The location
* [1] If the location is a jar file, this is the location
* of the tld.
*/
public Map getTagLibraryMappings() {
if (fobj == null) {
return null;
}
return JspUtils.getTaglibMap(fobj);
}
private static void initHelp(){
if (helpMap == null){
String url="";
File f = InstalledFileLocator.getDefault().locate("docs/jstl11-doc.zip", "org.netbeans.modules.web.core.syntax", false); //NoI18N
if (f != null){
try {
URL urll = f.toURL();
urll = FileUtil.getArchiveRoot(urll);
url = urll.toString();
} catch (java.net.MalformedURLException e){
err.log(Level.WARNING, null, e);
// nothing to do
}
}
helpMap = new HashMap();
// The URI from the tld file URL, where are the files for the library
helpMap.put("http://java.sun.com/jsp/jstl/core", url + "c/");
helpMap.put("http://java.sun.com/jstl/core", url + "c/");
helpMap.put("http://java.sun.com/jstl/core_rt", url + "c_rt/");
helpMap.put("http://java.sun.com/jsp/jstl/fmt", url + "fmt/");
helpMap.put("http://java.sun.com/jstl/fmt", url + "fmt/");
helpMap.put("http://java.sun.com/jstl/fmt_rt", url + "fmt_rt/");
helpMap.put("http://java.sun.com/jsp/jstl/functions", url + "fn/");
helpMap.put("http://java.sun.com/jstl/functions", url + "fn/");
helpMap.put("http://jakarta.apache.org/taglibs/standard/permittedTaglibs", url+"permittedTaglibs/");
helpMap.put("http://jakarta.apache.org/taglibs/standard/scriptfree", url+ "scriptfree/");
helpMap.put("http://java.sun.com/jsp/jstl/sql", url + "sql/");
helpMap.put("http://java.sun.com/jstl/sql", url + "sql/");
helpMap.put("http://java.sun.com/jstl/sql_rt", url + "sql_rt/");
helpMap.put("http://java.sun.com/jsp/jstl/xml", url + "x/");
helpMap.put("http://java.sun.com/jstl/xml", url + "x/");
helpMap.put("http://java.sun.com/jstl/xml_rt", url + "x_rt/");
f = InstalledFileLocator.getDefault().locate("docs/jsf12-tlddoc.zip", "org.netbeans.modules.web.core.syntax", false); //NoI18N
if (f != null){
try {
URL urll = f.toURL();
urll = FileUtil.getArchiveRoot(urll);
url = urll.toString();
helpMap.put("http://java.sun.com/jsf/html", url + "h/");
helpMap.put("http://java.sun.com/jsf/core", url + "f/");
} catch (java.net.MalformedURLException e){
err.log(Level.WARNING, null, e);
// nothing to do
}
}
f = InstalledFileLocator.getDefault().locate("docs/struts-tags.zip", "org.netbeans.modules.web.core.syntax", false);
if (f != null){
try {
URL urll = f.toURL();
urll = FileUtil.getArchiveRoot(urll);
url = urll.toString();
helpMap.put("http://jakarta.apache.org/struts/tags-bean", url + "bean/");
helpMap.put("http://struts.apache.org/tags-bean", url + "bean/");
helpMap.put("/WEB-INF/struts-bean.tld", url + "bean/");
helpMap.put("http://jakarta.apache.org/struts/tags-html", url + "html/");
helpMap.put("http://struts.apache.org/tags-html", url + "html/");
helpMap.put("/WEB-INF/struts-html.tld", url + "html/");
helpMap.put("http://jakarta.apache.org/struts/tags-logic", url + "logic/");
helpMap.put("http://struts.apache.org/tags-logic", url + "logic/");
helpMap.put("/WEB-INF/struts-logic.tld", url + "logic/");
helpMap.put("http://jakarta.apache.org/struts/tags-nested", url + "nested/");
helpMap.put("http://struts.apache.org/tags-nested", url + "nested/");
helpMap.put("/WEB-INF/struts-nested.tld", url + "nested/");
helpMap.put("http://jakarta.apache.org/struts/tags-tiles", url + "tiles/");
helpMap.put("http://struts.apache.org/tags-tiles", url + "tiles/");
helpMap.put("/WEB-INF/struts-tiles.tld", url + "tiles/");
} catch (java.net.MalformedURLException e){
err.log(Level.WARNING, null, e);
// nothing to do
}
}
}
}
private static void initCompletionData() {
if (helpMap == null)
initHelp();
String url = ""; // NOI18N
if (directiveJspData == null){
directiveJspData = new TagInfo[] {
new TagInfo("include", null, TagInfo.BODY_CONTENT_EMPTY, url+"syntaxref209.html#1003408#8975", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("file", true, url + "syntaxref209.html#16836#10636", false)}), // NOI18N
new TagInfo("page", null, TagInfo.BODY_CONTENT_EMPTY, url+"syntaxref2010.html", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("autoFlush", false, url+"syntaxref2010.html#15673#15675", false), // NOI18N
new TagAttributeInfo("buffer", false, url+"syntaxref2010.html#15671#15673", false), // NOI18N
new TagAttributeInfo("contentType", false, url+"syntaxref2010.html#15683#1001361", false), // NOI18N
new TagAttributeInfo("errorPage", false, url+"syntaxref2010.html#15679#15681", false), // NOI18N
new TagAttributeInfo("extends", false, url+"syntaxref2010.html#15665#16862", false), // NOI18N
new TagAttributeInfo("import", false, url+"syntaxref2010.html#16862#15669", false), // NOI18N
new TagAttributeInfo("info", false, url+"syntaxref2010.html#15677#15679", false), // NOI18N
new TagAttributeInfo("isELIgnored", false, url+"syntaxref2010.html#1011216#18865", false), // NOI18N
new TagAttributeInfo("isErrorPage", false, url+"syntaxref2010.html#15681#15683", false), // NOI18N
new TagAttributeInfo("isThreadSafe", false, url+"syntaxref2010.html#15675#15677", false), // NOI18N
new TagAttributeInfo("language", false, url+"syntaxref2010.html#15663#15665", false), // NOI18N
new TagAttributeInfo("pageEncoding", false, url+"syntaxref2010.html#1001361#1011216", false), // NOI18N
new TagAttributeInfo("session", false, url+"syntaxref2010.html#15669#15671", false)}), // NOI18N
new TagInfo("taglib", null, TagInfo.BODY_CONTENT_EMPTY, url+"syntaxref2012.html#1003416#1002041", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("prefix", true, url+"syntaxref2012.html#1011290#1002041", false), // NOI18N
new TagAttributeInfo("uri", false, url+"syntaxref2012.html#10721#1011294", false), // NOI18N
new TagAttributeInfo("tagdir", false, url + "syntaxref2012.html#1011294#1011290", false)}) // NOI18N
};
}
if (directiveTagFileData == null){
directiveTagFileData = new TagInfo[]{
new TagInfo("attribute", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref208.html", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("description", false, url + "syntaxref208.html#1004672", false), // NOI18N
new TagAttributeInfo("fragment", false, url + "syntaxref208.html#1004657#1004666", false), // NOI18N
new TagAttributeInfo("name", true, url + "syntaxref208.html#1004648#1004655", false), // NOI18N
new TagAttributeInfo("required", false, url + "syntaxref208.html#1004655#1004657", false), // NOI18N
new TagAttributeInfo("rtexprvalue", false, url + "syntaxref208.html#1004666#1004669", false), // NOI18N
new TagAttributeInfo("type", false, url + "syntaxref208.html#1004669#1004672", false)}), // NOI18N
directiveJspData[0],
new TagInfo("tag", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref2011.html", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("body-content", false, url + "syntaxref2011.html#1005164#005172", false), // NOI18N
new TagAttributeInfo("description", false, url + "syntaxref2011.html#1005196#1005198", false), // NOI18N
new TagAttributeInfo("display-name", false, url + "syntaxref2011.html#1005161#1005164", false), // NOI18N
new TagAttributeInfo("dynamic-attributes", false, url + "syntaxref2011.html#005172#1005190", false), // NOI18N
new TagAttributeInfo("example", false, url + "syntaxref2011.html#1005198#1005201", false), // NOI18N
new TagAttributeInfo("import", false, url + "syntaxref2011.html#1005203#1005209", false), // NOI18N
new TagAttributeInfo("isELIgnored", false, url + "syntaxref2011.html#1005214#1005291#1005291", false), // NOI18N
//new TagAttributeInfo("isScriptingEnabled", false, url + "syntaxref2011.html#", false), // NOI18N
new TagAttributeInfo("large-icon", false, url + "syntaxref2011.html#1005193#1005196", false), // NOI18N
new TagAttributeInfo("language", false, url + "syntaxref2011.html#1005201#1005203", false), // NOI18N
new TagAttributeInfo("pageEncoding", false, url + "syntaxref2011.html#1005209#1005214", false), // NOI18N
new TagAttributeInfo("small-icon", false, url + "syntaxref2011.html#1005190#1005193", false)}), // NOI18N
directiveJspData[2],
new TagInfo("variable", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref2013.html#15694#1003563", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("alias", false, url + "syntaxref2013.html#1005914#1005956", false), // NOI18N
new TagAttributeInfo("declare", false, url + "syntaxref2013.html#1006001#1006019", false), // NOI18N
new TagAttributeInfo("description", false, url + "syntaxref2013.html#1005991#1003563", false), // NOI18N
new TagAttributeInfo("name-given", false, url + "syntaxref2013.html#1003561#1005914", false), // NOI18N
new TagAttributeInfo("scope", false, url + "syntaxref2013.html#1006019#1005991", false), // NOI18N
new TagAttributeInfo("variable-class", false, url + "syntaxref2013.html#1005956#1006001", false)}) // NOI18N
};
}
if (standardJspTagDatas == null) {
final String helpFiles = "docs/syntaxref20.zip"; //NoI18N
File f = InstalledFileLocator.getDefault().locate(helpFiles, "org.netbeans.modules.web.core.syntax", true); //NoI18N
if (f != null){
try {
URL urll = f.toURL();
urll = FileUtil.getArchiveRoot(urll);
url = urll.toString();
} catch (java.net.MalformedURLException e){
err.log(Level.WARNING, null, e);
// nothing to do
}
}
standardJspTagDatas = new TagInfo[] {
new TagInfo("attribute", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2014.html", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("name", true, url + "syntaxref2014.html#1003581#1006483", false), // NOI18N
new TagAttributeInfo("trim", false, url + "syntaxref2014.html#1006483#1003583", false)}), // NOI18N
new TagInfo("body", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2015.html#1006731#1003768", // NOI18N
null, null, new TagAttributeInfo[]{}),
new TagInfo("element", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2016.html#1003696#1003708", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("name", true, url + "syntaxref2016.html#1003706#1003708", false)}), // NOI18N
new TagInfo("expression", null, TagInfo.BODY_CONTENT_JSP, url+"syntaxref205.html#1004353#11268", // NOI18N
null, null, new TagAttributeInfo[] {}),
new TagInfo("fallback", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2023.html#11583#19029", // NOI18N
null, null, new TagAttributeInfo[] {}),
new TagInfo("forward", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2018.html#1003349#15708", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("page", true, url + "syntaxref2018.html#15704#15708", true)}), // NOI18N
new TagInfo("getProperty", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref2019.html#8820#9201", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("name", true, url + "syntaxref2019.html#15748#10919", false), // NOI18N
new TagAttributeInfo("property", true, url + "syntaxref2019.html#10919#19482", false)}), // NOI18N
new TagInfo("include", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2020.html#8828#9228", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("flush", true, url + "syntaxref2020.html#17145#18376", false), // NOI18N
new TagAttributeInfo("page", true, url + "syntaxref2020.html#10930#17145", true)}), // NOI18N
new TagInfo("param", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref2023.html#11538#11583", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("name", true, url + "syntaxref2023.html#11538#11583", false), // NOI18N
new TagAttributeInfo("value", true, url + "syntaxref2023.html#11538#11583", true)}), // NOI18N
new TagInfo("params", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2023.html#11538#11583", // NOI18N
null, null, new TagAttributeInfo[] {}),
new TagInfo("plugin", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2023.html#1004158#19029", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("align", false, url + "syntaxref2023.html#11516#11518", false), // NOI18N
new TagAttributeInfo("archive", false, url + "syntaxref2023.html#11553#11516", false), // NOI18N
new TagAttributeInfo("code", true, url + "syntaxref2023.html#11514#11515", false), // NOI18N
new TagAttributeInfo("codebase", true, url + "syntaxref2023.html#11515#11547", false), // NOI18N
new TagAttributeInfo("height", false, url + "syntaxref2023.html#11518#11568", false), // NOI18N
new TagAttributeInfo("hspace", false, url + "syntaxref2023.html#11568#11520", false), // NOI18N
new TagAttributeInfo("iepluginurl", false, url + "syntaxref2023.html#11526#11538", false),// NOI18N
new TagAttributeInfo("jreversion", false, url + "syntaxref2023.html#11520#11525", false), // NOI18N
new TagAttributeInfo("name", false, url + "syntaxref2023.html#11547#11553", false), // NOI18N
new TagAttributeInfo("nspluginurl", false,url + "syntaxref2023.html#11525#11526", false),// NOI18N
new TagAttributeInfo("type", true, url + "syntaxref2023.html#10935#11514", false), // NOI18N
new TagAttributeInfo("vspace", false, url + "syntaxref2023.html#11568#11520", false), // NOI18N
new TagAttributeInfo("width", false, url + "syntaxref2023.html#11518#11568", false)}), // NOI18N
new TagInfo("setProperty", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref2025.html#8856#9329", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("name", true, url + "syntaxref2025.html#17612#1001786", true), // NOI18N
new TagAttributeInfo("param", false, url + "syntaxref2025.html#9919#20483", false), // NOI18N
new TagAttributeInfo("property", false, url + "syntaxref2025.html#1001786#9329", false), // NOI18N
new TagAttributeInfo("value", false, url + "syntaxref2025.html#20483#9329", true)}), // NOI18N
new TagInfo("text", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2026.html",
null, null, new TagAttributeInfo[]{}),
new TagInfo("useBean", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2027.html#8865#9359", // NOI18N
null, null, new TagAttributeInfo[] { new TagAttributeInfo("beanName", false, url + "syntaxref2027.html#15804#9359", false), // NOI18N
new TagAttributeInfo("class", false, url + "syntaxref2027.html#10968#19433", false), // NOI18N
new TagAttributeInfo("id", true, url + "syntaxref2027.html#10964#10966", false), // NOI18N
new TagAttributeInfo("scope", true, url + "syntaxref2027.html#10966#10968", false), // NOI18N
new TagAttributeInfo("type", false, url + "syntaxref2027.html#19433#18019", false)}), // NOI18N
new TagInfo("declaration", null, TagInfo.BODY_CONTENT_JSP, url+"syntaxref204.html#10983#10991", // NOI18N
null, null, new TagAttributeInfo[] {}),
new TagInfo("scriptlet", null, TagInfo.BODY_CONTENT_JSP, url+"syntaxref206.html#10996#11007", // NOI18N
null, null, new TagAttributeInfo[] {}),
new TagInfo("directive.page", null, TagInfo.BODY_CONTENT_EMPTY, directiveJspData[1].getInfoString(), // NOI18N
null, null, directiveJspData[1].getAttributes()),
new TagInfo("directive.include", null, TagInfo.BODY_CONTENT_EMPTY, directiveJspData[1].getInfoString(), // NOI18N
null, null, directiveJspData[0].getAttributes())
};
List<TagInfo> standardTagTagDatasList = new ArrayList<TagInfo>();
standardTagTagDatasList.addAll(Arrays.asList(standardJspTagDatas));
standardTagTagDatasList.add(new TagInfo("doBody", null, TagInfo.BODY_CONTENT_EMPTY, url + "syntaxref2017.html", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("scope", false, url + "syntaxref2017.html#1006246#syntaxref20.html", false), // NOI18N
new TagAttributeInfo("var", false, url + "syntaxref2017.html#1006234#1006240", false), // NOI18N
new TagAttributeInfo("varReader", false, url + "syntaxref2017.html#1006240#1006246", false)})); // NOI18N
standardTagTagDatasList.add(new TagInfo("invoke", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2021.html#8837#1003634", null, null, // NOI18N
new TagAttributeInfo[] { new TagAttributeInfo("fragment", true, url + "syntaxref2021.html#1007359#1007361", false), // NOI18N
new TagAttributeInfo("scope", false, url + "syntaxref2021.html#1007373#1003634", false), // NOI18N
new TagAttributeInfo("var", false, url + "syntaxref2021.html#1007361#1007367", false), // NOI18N
new TagAttributeInfo("varReader", false, url + "syntaxref2021.html#1007367#1007373", false)})); // NOI18N
standardTagTagDatasList.add(new TagInfo("directive.tag", null, TagInfo.BODY_CONTENT_EMPTY, directiveTagFileData[2].getInfoString(), // NOI18N
null, null, directiveTagFileData[2].getAttributes()));
standardTagTagDatasList.add(new TagInfo("directive.attribute", null, TagInfo.BODY_CONTENT_EMPTY, directiveTagFileData[0].getInfoString(), // NOI18N
null, null, directiveTagFileData[0].getAttributes()));
standardTagTagDatasList.add(new TagInfo("directive.variable", null, TagInfo.BODY_CONTENT_EMPTY, directiveTagFileData[4].getInfoString(), // NOI18N
null, null, directiveTagFileData[4].getAttributes()));
standardTagTagDatas = standardTagTagDatasList.toArray(new TagInfo[]{});
}
if (xmlJspTagDatas == null) {
TagInfo[] commonXMLTagDatas;
commonXMLTagDatas = new TagInfo[]{
new TagInfo("output", null, TagInfo.BODY_CONTENT_JSP, url + "syntaxref2022.html#1004130#1007521", // NOI18N
null, null, new TagAttributeInfo[] {new TagAttributeInfo("doctype-public", false, "url + syntaxref2022.html#1007534#1007521", false), // NOI18N
new TagAttributeInfo("doctype-root-element", false, "url + syntaxref2022.html#1007528#1007532", false), // NOI18N
new TagAttributeInfo("doctype-system", false, url + "syntaxref2022.html#1007532#1007534", false), // NOI18N
new TagAttributeInfo("omit-xml-declaration", false, url + "syntaxref2022.html#1007525#1007528" , false)}), // NOI18N
new TagInfo("root", null, TagInfo.BODY_CONTENT_JSP, url+"syntaxref2024.html#1003283#1003311", // NOI18N
null, null, new TagAttributeInfo[] {new TagAttributeInfo("version", false, url+"syntaxref2024.html#1003299#1003301", false),
new TagAttributeInfo("xmlns:jsp", false, url+"syntaxref2024.html#1003297#1003299", false),
new TagAttributeInfo("xmlns:x", false, url+"syntaxref2024.html#1003301#1003311", false)})
};
List<TagInfo> xmlJspTagDatasList = new ArrayList<TagInfo>();
xmlJspTagDatasList.addAll(Arrays.asList(standardJspTagDatas));
xmlJspTagDatasList.addAll(Arrays.asList(commonXMLTagDatas));
xmlJspTagDatas = xmlJspTagDatasList.toArray(new TagInfo[]{});
List<TagInfo> xmlTagFileTagDatasList = new ArrayList<TagInfo>();
xmlTagFileTagDatasList.addAll(Arrays.asList(standardTagTagDatas));
xmlTagFileTagDatasList.addAll(Arrays.asList(commonXMLTagDatas));
xmlTagFileTagDatas = xmlTagFileTagDatasList.toArray(new TagInfo[]{});
}
}
private TagInfo[] getTagInfos(){
TagInfo[] rValue;
if (fobj != null &&
("text/x-jsp".equals(fobj.getMIMEType()) ||
"text/x-tag".equals(fobj.getMIMEType()))){
if ( isXmlSyntax()){
if (NbEditorUtilities.getMimeType(getDocument()).equals(JspUtils.TAG_MIME_TYPE))
rValue = xmlTagFileTagDatas;
else
rValue = xmlJspTagDatas;
} else
if (NbEditorUtilities.getMimeType(getDocument()).equals(JspUtils.TAG_MIME_TYPE))
rValue = standardTagTagDatas;
else
rValue = standardJspTagDatas;
} else
rValue = new TagInfo[0];
return rValue;
}
@Override
public String toString() {
return printJspCompletionInfo();
}
/** Debug output of all tags and directives. */
private String printJspCompletionInfo() {
StringBuffer output = new StringBuffer();
output.append("TAGS\n"); // NOI18N
List tagPrefixes = getTagPrefixes(""); // NOI18N
for (int i = 0; i < tagPrefixes.size(); i++) {
String prefix = (String)tagPrefixes.get(i);
output.append(" " + prefix + "\n"); // NOI18N
List tags = getTags(prefix, ""); // NOI18N
for (int j = 0; j < tags.size(); j++) {
if (tags.get(j) instanceof TagInfo){
TagInfo ti = (TagInfo) tags.get(j);
output.append(" " + ti.getTagName() + "\n");
TagAttributeInfo[] attributes = ti.getAttributes();
for (int k = 0; k < attributes.length; k++) {
output.append(" " + attributes[k].getName() + "\n");// NOI18N
}
} else {
String tagName = (String)tags.get(j);
output.append(" " + tagName + "\n"); // NOI18N
List attributes = getTagAttributes(prefix, tagName, "");// NOI18N
for (int k = 0; k < attributes.size(); k++) {
String attribute = (String)attributes.get(k);
output.append(" " + attribute + "\n");// NOI18N
}
}
}
}
output.append("DIRECTIVES\n");// NOI18N
List directives = getDirectives("");// NOI18N
for (int i = 0; i < directives.size(); i++) {
if (directives.get(i) instanceof TagInfo){
TagInfo ti = (TagInfo) directives.get(i);
output.append(" " + ti.getTagName() + "\n");
TagAttributeInfo[] attributes = ti.getAttributes();
for (int k = 0; k < attributes.length; k++) {
output.append(" " + attributes[k].getName() + "\n");// NOI18N
}
} else {
String directive = (String)directives.get(i);
output.append(" " + directive + "\n");// NOI18N
List attributes = getDirectiveAttributes(directive, "");// NOI18N
for (int k = 0; k < attributes.size(); k++) {
String attribute = (String)attributes.get(k);
output.append(" " + attribute + "\n");// NOI18N
}
}
}
return output.toString();
}
/** Returns an item on offset <code>offset</code>
* 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 <code>offset == 0</code>, retruns null. */
@Deprecated()
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
TokenItem item;
while (true) {
item = backItem.getNext();
//System.out.println("looking at item " + item);
if (item == null) {
item = backItem;
break;
}
if (item.getOffset() > offset) {
item = backItem;
break;
}
backItem = item;
}
TokenItem adjustedItem = (item.getOffset() == offset) ?
item.getPrevious() : item;
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 ||
/*id == JspDirectiveTokenContext.COMMENT || */
id == JspDirectiveTokenContext.ERROR ||
id == JspDirectiveTokenContext.TEXT
) {
return null;
}
//JSP comment
if(id == JspTagTokenContext.COMMENT || id == JspDirectiveTokenContext.COMMENT) {
return getCommentChain(item, offset);
}
//Expression language handling
if(item.getTokenContextPath().contains(ELTokenContext.contextPath)) {
return getELChain(item, offset);
}
if (id == JspTagTokenContext.SYMBOL2 || id == JspDirectiveTokenContext.SYMBOL2) {
if (isScriptStartToken(item)) {
return getScriptingChain(item, offset);
}
if ((getTokenEnd(item) == offset) && isScriptEndToken(item)) {
item.getNext();
if (!isTagDirToken(item))
return getContentChain(item, offset);
}
return null;
}
if (id == JspTagTokenContext.TAG ||
id == JspTagTokenContext.SYMBOL ||
id == JspTagTokenContext.ATTRIBUTE ||
id == JspTagTokenContext.ATTR_VALUE ||
id == JspTagTokenContext.WHITESPACE ||
id == JspTagTokenContext.EOL ||
id == JspDirectiveTokenContext.TAG ||
id == JspDirectiveTokenContext.SYMBOL ||
id == JspDirectiveTokenContext.ATTRIBUTE ||
id == JspDirectiveTokenContext.ATTR_VALUE ||
id == JspDirectiveTokenContext.WHITESPACE ||
id == JspDirectiveTokenContext.EOL) {
// may be intetesting: tag, directive,
// but may also be a comment. Look back for SYMBOL: <, </, <%@
// or COMMENT
TokenItem elementStart = item;
do {
//System.out.println("backtracking, elementStart = " + elementStart);
if (elementStart == null)
return null;
if (elementStart.getTokenID() == JspTagTokenContext.SYMBOL
|| elementStart.getTokenID() == JspDirectiveTokenContext.SYMBOL) {
if (elementStart.getImage().equals("<")) { // NOI18N
return getTagOrDirectiveChain(true, elementStart, offset);
}
if (elementStart.getImage().equals("</")) { // NOI18N
return getEndTagChain(elementStart, offset);
}
if (elementStart.getImage().equals("<%@")) { // NOI18N
return getTagOrDirectiveChain(false, elementStart, offset);
}
}
if(elementStart.getTokenID() == JspTagTokenContext.ERROR
|| elementStart.getTokenID() == JspDirectiveTokenContext.ERROR) {
//an error in JSP code
return null;
}
if (elementStart.getTokenID() == JspTagTokenContext.COMMENT
|| elementStart.getTokenID() == JspDirectiveTokenContext.COMMENT) {
return null;
}
elementStart = elementStart.getPrevious();
}
while (true);
}
// now we are either in the scripting language or in the content language.
// to determine which one it is, look back for SYMBOL2: <%, <%=, <%!
// (scripting language) or for any other JspTag token (content language).
// if nothing found, we are in the content language
if (isScriptingOrContentToken(item)) {
TokenItem elementStart = item;
do {
if (elementStart.getPrevious() == null) {
// we backtracked to the beginning without finding
// a distinguishing symbol - we are in the content language
return getContentChain(elementStart, offset);
}
elementStart = elementStart.getPrevious(); // now non-null
if (!isScriptingOrContentToken(elementStart)
|| elementStart.getTokenID() == JspTagTokenContext.COMMENT
|| elementStart.getTokenID() == JspDirectiveTokenContext.COMMENT
|| elementStart.getTokenContextPath().contains(ELTokenContext.contextPath)) {
// something from JSP
if (isScriptStartToken(elementStart)) {
return getScriptingChain(elementStart.getNext(), offset);
} else {
return getContentChain(elementStart.getNext(), offset);
}
}
}
while (true);
}
return null;
}
/** Returns true if item is a starting symbol for a block in
* the scripting language. */
private boolean isScriptStartToken(TokenItem item) {
if (item == null)
return false;
TokenID id = item.getTokenID();
if (id == JspTagTokenContext.SYMBOL2
|| id == JspDirectiveTokenContext.SYMBOL2) {
String image = item.getImage();
if (image.equals("<%") || // NOI18N
image.equals("<%=") || // NOI18N
image.equals("<%!")) // NOI18N
return true;
}
return false;
}
/** Returns true if item is an ending symbol for a block in
* the scripting language. */
private boolean isScriptEndToken(TokenItem item) {
if (item == null)
return false;
TokenID id = item.getTokenID();
if (id == JspTagTokenContext.SYMBOL2
|| id == JspDirectiveTokenContext.SYMBOL2) {
String image = item.getImage();
if (image.equals("%>")) // 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
|| id == JspDirectiveTokenContext.SYMBOL) {
String image = item.getImage();
if (image.equals("<") || // NOI18N
image.equals("</") || // NOI18N
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.WHITESPACE) &&
(id != JspTagTokenContext.EOL) &&
(id != JspDirectiveTokenContext.TEXT) &&
(id != JspDirectiveTokenContext.ERROR) &&
(id != JspDirectiveTokenContext.TAG) &&
(id != JspDirectiveTokenContext.SYMBOL) &&
(id != JspDirectiveTokenContext.ATTRIBUTE) &&
(id != JspDirectiveTokenContext.ATTR_VALUE) &&
(id != JspDirectiveTokenContext.WHITESPACE) &&
(id != JspDirectiveTokenContext.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.ATTRIBUTE) ||
(id == JspTagTokenContext.ATTR_VALUE) ||
(id == JspTagTokenContext.SYMBOL2) ||
(id == JspTagTokenContext.EOL) ||
(id == JspDirectiveTokenContext.TEXT) ||
(id == JspDirectiveTokenContext.ERROR) ||
(id == JspDirectiveTokenContext.TAG) ||
(id == JspDirectiveTokenContext.SYMBOL) ||
(id == JspDirectiveTokenContext.ATTRIBUTE) ||
(id == JspDirectiveTokenContext.ATTR_VALUE) ||
(id == JspDirectiveTokenContext.SYMBOL2) ||
(id == JspDirectiveTokenContext.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 ------
private SyntaxElement getCommentChain(TokenItem token, int offset) {
//we are somewhere in a JSP comment - need to find its start and end
//backtrace for start
TokenItem start = null;
TokenItem search = token;
do {
if(search != null && search.getImage().startsWith("<%--")) { //NOI18N
start = search;
break;
}
search = search.getPrevious();
} while(search != null);
if(start == null) {
//didn't find a comment start - strange???
return null;
}
//find comment end
TokenItem end = null;
TokenItem prevNonNullToken = token;
search = token;
do {
if(search != null && search.getImage().endsWith("--%>")) { //NOI18N
end = search;
break;
}
prevNonNullToken = search;
search = search.getNext();
} while(search != null);
if(end == null) {
//comment to the end of the file - may happed
end = prevNonNullToken; //last non-null token
}
return new SyntaxElement.Comment(this, start.getOffset(), getTokenEnd(end));
}
private SyntaxElement getELChain(TokenItem token, int offset) {
//we are somewhere in an expression language - need to find its start and end
//backtrace for start
TokenItem scan = token;
TokenItem start = null;
do {
start = scan;
scan = scan.getPrevious();
} while(scan != null && scan.getTokenContextPath().contains(ELTokenContext.contextPath));
//find comment end
TokenItem end = null;
scan = token;
do {
end = scan;
scan = scan.getNext();
} while(scan != null && scan.getTokenContextPath().contains(ELTokenContext.contextPath));
return new SyntaxElement.ExpressionLanguage(this, start.getOffset(), getTokenEnd(end));
}
/** Gets an element representing a tag or directive starting with token item firstToken. */
private SyntaxElement getTagOrDirectiveChain(boolean tag, TokenItem firstToken, int offset) {
//suppose we are in a tag or directive => we do not have to distinguish tokenIDs -
//it is sufficient to work with numeric ids (solves directive/tag tokenID problem)
TokenItem item = firstToken.getNext();
String name = getWholeWord(item, JspTagTokenContext.TAG);
while ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID ||
item.getTokenID().getNumericID() == JspTagTokenContext.WHITESPACE_ID ))
item = item.getNext();
TreeMap attributes = new TreeMap();
while (isInnerTagDirToken(item)) {
// collect the attributes
if (item.getTokenID().getNumericID() == JspTagTokenContext.ATTRIBUTE_ID) {
String attributeName = getWholeWord(item, JspTagTokenContext.ATTRIBUTE);
// forward to the next non-ATTRIBUTE token
while ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.ATTRIBUTE_ID))
item = item.getNext();
// find the value
while ((item != null) &&
(item.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID) &&
(isValueBeginning(item.getImage())))
item = item.getNext();
StringBuffer value = new StringBuffer();
while ((item != null)
&& ((item.getTokenID().getNumericID() == JspTagTokenContext.ATTR_VALUE_ID)
|| (item.getTokenID().getNumericID() == JspTagTokenContext.EOL_ID))) {
value.append(item.getImage());
item = item.getNext();
// request time values
if ((item != null) && (item.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL2_ID)) {
// 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();
}
}
//expression language - just put its content into attribute value
if((item != null) && (item.getTokenContextPath().contains(ELTokenContext.contextPath))) {
//go over all EL tokens and add them to the attribute value
while(item.getTokenContextPath().contains(ELTokenContext.contextPath)) {
item = item.getNext();
if(item == null) break;
value.append(item.getImage());
}
}
}
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().getNumericID() == JspTagTokenContext.SYMBOL2_ID) {
// 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("/>")); // 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().getNumericID() == requestedTokenID.getNumericID())) {
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(nextItem));
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)
|| nextItem.getTokenID() == JspTagTokenContext.COMMENT
|| nextItem.getTokenID() == JspDirectiveTokenContext.COMMENT
|| nextItem.getTokenContextPath().contains(ELTokenContext.contextPath))
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;
}
SyntaxElement getNextElement(int offset) throws BadLocationException {
int doclen = getDocument().getLength();
if(offset >= doclen)
return null;
SyntaxElement elem = null;
offset++;
do {
elem = getElementChain(offset);
if(elem == null) {
TokenItem ti = getItemAtOrBefore(offset);
if(ti == null)
return null;
offset = getTokenEnd(ti) + 1;
}
} while(elem == null && offset < doclen);
return elem;
}
public List<JspCompletionItem> getPossibleEndTags(int offset, int anchor, String pattern) throws BadLocationException {
return getPossibleEndTags(offset, anchor, pattern, false); //return all end tags
}
public List<JspCompletionItem> getPossibleEndTags(int offset, int anchor, String pattern, boolean firstOnly) throws BadLocationException {
SyntaxElement elem = getElementChain( offset );
Stack<String> stack = new Stack<String>();
List<JspCompletionItem> result = new ArrayList<JspCompletionItem>();
Set<String> found = new HashSet<String>();
if( elem != null ) {
elem = elem.getPrevious(); // we need smtg. before our </
} else { // End of Document
if( offset > 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, true);
if (tli != null) {
ti = tli.getTag(name);
if (ti == null) {
// try to find the info among tag files
TagFileInfo tagFileInfo = tli.getTagFile(name);
if (tagFileInfo != null) {
ti = tagFileInfo.getTagInfo();
}
}
}
if (STANDARD_JSP_PREFIX.equals(prefix)) {
initCompletionData();
TagInfo[] stanTagDatas = getTagInfos();
for (int i=0; i<stanTagDatas.length; i++) {
if (stanTagDatas[i].getTagName().equals(name)) {
ti = stanTagDatas[i];
break;
}
}
}
if (ti == null) continue; // Unknown tag - ignore
if( stack.empty() ) { // empty stack - we are on the same tree deepnes - can close this tag
if( image.startsWith( pattern ) && !found.contains( image ) ) { // add only new items
found.add( image );
if (ti.getBodyContent().equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY))
continue;
result.add(JspCompletionItem.createTag("/"+image, anchor, ti ) ); // NOI18N
if(firstOnly) break; //return only the first found not-finished start token
}
// if( ! tag.hasOptionalEnd() ) break; // If this tag have required EndTag, we can't go higher until completing this tag
} else { // not empty - we match content of stack
if( stack.peek().equals( image ) ) { // match - close this branch of document tree
stack.pop();
} // else if( ! tag.hasOptionalEnd() ) break; // we reached error in document structure, give up
}
// this is error - end of empty tag
if (ti.getBodyContent().equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY))
continue;
// if( tag.isEmpty() ) continue; // ignore empty Tags - they are like start and imediate end
}
}
return result;
}
public CompletionItem getAutocompletedEndTag(int offset) {
try {
SyntaxElement elem = getElementChain( offset - 1);
if(elem != null && elem instanceof SyntaxElement.Tag) {
String tagName = ((SyntaxElement.Tag)elem).getName();
return HtmlCompletionItem.createAutocompleteEndTag(tagName, offset);
}
}catch(BadLocationException e) {
Logger.getLogger("global").log(Level.INFO, null, e);
}
return null;
}
public FileObject getFileObject() {
return fobj;
}
/** Get the bracket finder that will search for the matching bracket
* or null if the bracket character doesn't belong to bracket
* characters.
*
* Customized finder recognizes also '<' and '>' as bracket chars. It is set to be used
* in findMatchingBlock.
*/
@Override
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.
*/
@Override
public int[] findMatchingBlock(int offset, boolean simpleSearch)
throws BadLocationException {
int [] r_value = null;
TokenItem token = getItemAtOrBefore((offset<getDocument().getLength())?offset+1:offset);
if (token != null){
//do we need to match jsp comment?
if (token.getTokenID() == JspTagTokenContext.COMMENT) {
return findMatchingJspComment(token, offset);
}
// Is it matching of scriptlet delimiters?
if (token.getTokenContextPath().contains(JspTagTokenContext.contextPath)
&& token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL2_ID){
return findMatchingScripletDelimiter(token);
}
// Try to match the tags.
if (token.getTokenContextPath().contains(JspTagTokenContext.contextPath)){
return findMatchingTag(token);
} else {
if (isScriptingOrContentToken(token)) {
useCustomBracketFinder = false;
} else {
useCustomBracketFinder = true;
}
r_value = super.findMatchingBlock(offset, simpleSearch);
}
}
return r_value;
}
private int[] findMatchingJspComment(TokenItem token, int offset) {
String tokenImage = token.getImage();
if(tokenImage.startsWith("<%--") && (offset < (token.getOffset()) + "<%--".length())) { //NOI18N
//start html token - we need to find the end token of the html comment
while(token != null) {
if((token.getTokenID() == JspTagTokenContext.COMMENT)
|| (token.getTokenID() == JspTagTokenContext.EOL)) {
if(token.getImage().endsWith("--%>")) { //NOI18N
//found end token
int start = token.getOffset() + token.getImage().length() - "--%>".length(); //NOI18N
int end = token.getOffset() + token.getImage().length();
return new int[] {start, end};
}
} else break;
token = token.getNext();
}
} else if(tokenImage.endsWith("--%>") && (offset >= (token.getOffset()) + token.getImage().length() - "--%>".length())) { //NOI18N
//end html token - we need to find the start token of the html comment
while(token != null) {
if((token.getTokenID() == JspTagTokenContext.COMMENT)
|| (token.getTokenID() == JspTagTokenContext.EOL)) {
if(token.getImage().startsWith("<%--")) { //NOI18N
//found end token
int start = token.getOffset();
int end = token.getOffset() + "<%--".length(); //NOI18N
return new int[] {start, end};
}
} else break;
token = token.getPrevious();
}
}
return null;
}
private int [] findMatchingScripletDelimiter(TokenItem token){
if (token.getImage().charAt(0) == '<'){
do{
token = token.getNext();
} while (token != null
&& !(token.getTokenContextPath().contains(JspTagTokenContext.contextPath)
&&token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL2_ID));
} else {
do{
token = token.getPrevious();
} while (token != null
&& !(token.getTokenContextPath().contains(JspTagTokenContext.contextPath)
&&token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL2_ID));
}
if (token != null){
return new int [] {token.getOffset(), token.getOffset() + token.getImage().length()};
}
return null;
}
private int[] findMatchingTag(TokenItem token){
// TODO - replanning to the other thread. Now it's in awt thread
// if the curret is after jsp tag ( after the char '>' ), ship inside the tag
if (token != null && token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID
&& token.getImage().charAt(token.getImage().length()-1) == '>')
token = token.getPrevious();
boolean isInside = false; // flag, whether the curret is somewhere in a jsp tag
if (token != null
&& ((token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID && token.getImage().trim().length() > 0)
|| (token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID && token.getImage().charAt(0)=='<'))){
if (token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID) // the starting of the jsp tag
// we are somewhere at beginning of the jsp tag. Find out the token with the jsp tag.
while (token !=null
&& !(token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID
&& token.getImage().trim().length() > 0))
token = token.getNext(); // move at the jsp tag
isInside = true; // the curret is somewhere in '<jsptag' or '</jsptag'
} else {
// find out whether the curret is inside a jsp tag
if (token != null
&& !(token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID && token.getImage().trim().length() > 0)){
token = token.getPrevious();
//try to find the beginning of the tag.
while (token!=null
&& !(token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID
&& token.getImage().trim().length() > 0) // this is hack, because a whitspaces are returned are with TAG_ID
&& !(token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID
&& token.getImage().charAt(token.getImage().length()-1) == '>'))
token = token.getPrevious();
if (token!=null && token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID)
isInside = true;
}
}
// Now we have the begining of the tag and we can start with the finding opposit tag.
if (token != null && token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID && isInside){
int start; // possition where the matched tag starts
int end; // possition where the matched tag ends
int poss = 0; // how many the same tags is inside the mathed tag
boolean singleTag = false;
String tag = token.getImage().trim();
while (token != null && token.getTokenID().getNumericID() != JspTagTokenContext.SYMBOL_ID) {
token = token.getPrevious();
}
if (token == null)
return null;
if ((token.getImage().length() == 2) && token.getImage().charAt(1) == '/'){
while ( token != null){
// see issue #229149
if (token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID) {
if ("/>".equals(token.getImage())) { //NOI18N
singleTag = true;
} else if ("<".equals(token.getImage())) { //NOI18N
singleTag = false;
}
}
if (token.getTokenID().getNumericID() == JspTagTokenContext.TAG_ID) {
if (token.getImage().trim().equals(tag) && !singleTag){
while (token != null && token.getTokenID().getNumericID() != JspTagTokenContext.SYMBOL_ID) {
token = token.getPrevious();
}
if (token != null) {
if (token.getImage().length() == 1){
if (poss == 0){
start = token.getOffset();
token = token.getNext();
end = token.getOffset()+token.getImage().length();
// add everything up to ending > or /> into the selection
TokenItem next = token.getNext();
while (next != null &&
!((next.getTokenID() == JspTagTokenContext.SYMBOL && next.getImage().endsWith(">"))
|| next.getTokenID() == JspTagTokenContext.EOL)) {
end += next.getImage().length();
next = next.getNext();
}
if (next != null && next.getTokenID() != JspTagTokenContext.EOL) {
end += next.getImage().length();
}
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){
// see issue #229149
while (token != null) {
if (token.getTokenID().getNumericID() == JspTagTokenContext.SYMBOL_ID) {
if ("/>".equals(token.getImage())) { //NOI18N
break;
} else if (">".equals(token.getImage())) { //NOI18N
poss--;
break;
}
}
token = token.getNext();
}
}
}
token = hToken;
}
}
token = token.getNext();
}
}
}
}
return null;
}
/** Finds out whether the given tagTokenItem is a part of a singleton tag (e.g. <div style=""/>).
* @tagTokenItem a token item whithin a tag
* @return true is the token is a part of singleton tag
*/
public boolean isSingletonTag(TokenItem tagTokenItem) {
TokenItem ti = tagTokenItem;
while(ti != null) {
if(ti.getTokenID() == JspTagTokenContext.SYMBOL){
if("/>".equals(ti.getImage())) { // NOI18N
//it is a singleton tag => do not match
return true;
}
if(">".equals(ti.getImage())) return false; // NOI18N
}
//break the loop on TEXT or on another open tag symbol
//(just to prevent long loop in case the tag is not closed)
if(ti.getTokenID() == JspTagTokenContext.TEXT) break;
ti = ti.getNext();
}
return false;
}
/** 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.
*/
@Override
protected TokenID[] getBracketSkipTokens() {
return JSP_BRACKET_SKIP_TOKENS;
}
public static TokenSequence tokenSequence(TokenHierarchy hi, Language language, int offset) {
TokenSequence ts = hi.tokenSequence(language);
if(ts == null) {
//Html language is not top level one
ts = hi.tokenSequence();
if(ts == null) {
return null; //seems to might happen during j2ee ergonomics lazy load
}
ts.move(offset);
if(!ts.moveNext() && !ts.movePrevious()) {
return null; //no token found
} else {
ts = ts.embedded(language);
}
}
return ts;
}
/** 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.
*/
@Override
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);
}
}
public void fileFolderCreated(FileEvent fe) {
}
public void fileDataCreated(FileEvent fe) {
}
public void fileChanged(FileEvent fe) {
}
public void fileDeleted(FileEvent fe) {
//refresh fileobject
initFileObject();
}
public void fileRenamed(FileRenameEvent fe) {
}
public void fileAttributeChanged(FileAttributeEvent fe) {
}
}