blob: 58aa16a6b1cffd5b7c5a8c576c80526499906a8c [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.completion;
import org.netbeans.modules.web.core.syntax.completion.api.JspCompletionItem;
import java.util.*;
import java.beans.*;
import java.io.Closeable;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.text.JTextComponent;
import javax.swing.ImageIcon;
import org.netbeans.modules.editor.NbEditorUtilities;
import org.netbeans.modules.web.jsps.parserapi.PageInfo;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.filesystems.FileSystem;
import org.netbeans.modules.web.core.syntax.*;
import org.netbeans.modules.web.core.syntax.completion.JavaJspCompletionProvider.CompletionQueryDelegatedToJava;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.netbeans.spi.editor.completion.CompletionProvider;
import org.openide.util.ImageUtilities;
/** Support for code completion of default JSP tags.
*
* @author pjiricka
* @author Marek Fukala
*/
public class AttrSupports {
private static final Logger logger = Logger.getLogger(AttrSupports.class.getName());
public static class ScopeSupport extends AttributeValueSupport.Default {
public ScopeSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("application"); // NOI18N
list.add("page"); // NOI18N
list.add("request"); // NOI18N
list.add("session"); // NOI18N
return list;
}
}
public static class BodyContentSupport extends AttributeValueSupport.Default {
public BodyContentSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("scriptless"); // NOI18N
list.add("tagdependent"); // NOI18N
list.add("empty"); // NOI18N
return list;
}
}
public static class RootVersionSupport extends AttributeValueSupport.Default {
public RootVersionSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("1.2"); // NOI18N
list.add("2.0"); // NOI18N
return list;
}
}
public static class PluginTypeSupport extends AttributeValueSupport.Default {
public PluginTypeSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("bean"); // NOI18N
list.add("applet"); // NOI18N
return list;
}
}
public static class VariableScopeSupport extends AttributeValueSupport.Default {
public VariableScopeSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("AT_BEGIN"); // NOI18N
list.add("AT_END"); // NOI18N
list.add("NESTED"); // NOI18N
return list;
}
}
public static class YesNoTrueFalseSupport extends AttributeValueSupport.Default {
public YesNoTrueFalseSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("false"); // NOI18N
list.add("no"); // NOI18N
list.add("true"); // NOI18N
list.add("yes"); // NOI18N
return list;
}
}
/**
* Provides code completion for a class name context
*/
@Deprecated
public static class ClassNameSupport extends AttributeValueSupport.Default {
public ClassNameSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
return new ArrayList();
}
protected String getFakedClassBody(String prefix){
return "class Foo extends " + prefix; //NOI18N
}
/** Returns the complete result that contains elements from getCompletionItems. */
@Override
public void result(JspCompletionQuery.CompletionResultSet result, JTextComponent component, int offset,
JspSyntaxSupport sup, SyntaxElement.TagDirective item, String valuePart) {
String fakedClassBody = getFakedClassBody(valuePart);
int shiftedOffset = fakedClassBody.length();
logger.fine("JSP CC: delegating CC query to java file:\n" //NOI18N
+ fakedClassBody.substring(0, shiftedOffset)
+ "|" + fakedClassBody.substring(shiftedOffset) + "\n"); //NOI18N
CompletionQueryDelegatedToJava delegate = new CompletionQueryDelegatedToJava(
offset, shiftedOffset, CompletionProvider.COMPLETION_QUERY_TYPE);
delegate.create(component.getDocument(), fakedClassBody);
List<? extends CompletionItem> items = delegate.getCompletionItems();
result.addAllItems(items);
result.setAnchorOffset(offset - (valuePart.length() - valuePart.lastIndexOf('.')) + 1);
}
}
/**
* Provides code completion for a comma-separated list of imports context
*/
@Deprecated
public static class PackageListSupport extends ClassNameSupport {
public PackageListSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override protected String getFakedClassBody(String prefix){
int commaPos = prefix.lastIndexOf(",");
if (commaPos > -1){
prefix = prefix.substring(commaPos + 1);
}
return "import " + prefix; //NOI18N
}
}
public static class GetSetPropertyName extends AttributeValueSupport.Default {
public GetSetPropertyName(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
PageInfo.BeanData[] beanData = sup.getBeanData();
if(beanData != null) {
for (int i = 0; i < beanData.length; i++) {
list.add(beanData[i].getId());
}
}
return list;
}
}
public abstract static class GetSetPropertyProperty extends AttributeValueSupport.Default {
public GetSetPropertyProperty(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item, boolean setter) {
ArrayList<String> list = new ArrayList<String>();
String namePropertyValue = (String)item.getAttributes().get("name"); // NOI18N
if (namePropertyValue != null) {
String className = null;
PageInfo.BeanData[] beanData = sup.getBeanData();
for (int i = 0; i < beanData.length; i++) {
if (beanData[i] == null || beanData[i].getId() == null)
continue;
if (beanData[i].getId().equals(namePropertyValue)) {
className = beanData[i].getClassName();
break;
}
}
if (className != null) {
ClassLoader cld = null;
try {
FileObject fileObject = NbEditorUtilities.getDataObject( sup.getDocument()).getPrimaryFile();
cld = JspUtils.getModuleClassLoader(fileObject);
Class beanClass = Class.forName(className, false, cld);
Introspector.flushFromCaches(beanClass);
BeanInfo benInfo = Introspector.getBeanInfo(beanClass);
PropertyDescriptor[] properties = benInfo.getPropertyDescriptors();
for (int j = 0; j < properties.length; j++) {
if (setter && (properties[j].getWriteMethod() != null))
list.add(properties[j].getName());
if (!setter && (properties[j].getReadMethod() != null) && !properties[j].getName().equals("class")) //NOI18N
list.add(properties[j].getName());
}
} catch (ClassNotFoundException e) {
//do nothing
} catch (IntrospectionException e) {
//do nothing
} finally {
// avoids JAR locking
if (cld instanceof Closeable) {
try {
((Closeable) cld).close();
} catch (IOException ex) {
Logger.getLogger(AttrSupports.class.getName()).log(Level.INFO, null, ex);
}
}
}
}
}
return list;
}
}
public static class GetPropertyProperty extends GetSetPropertyProperty {
public GetPropertyProperty() {
super(true, "jsp:getProperty", "property"); // NOI18N
}
@Override
public List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
return possibleValues(sup, item, false);
}
}
public static class SetPropertyProperty extends GetSetPropertyProperty {
public SetPropertyProperty() {
super(true, "jsp:setProperty", "property"); // NOI18N
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
List<String> list = possibleValues(sup, item, true);
list.add(0, "*"); // NOI18N
return list;
}
}
public static class TaglibURI extends AttributeValueSupport.Default {
public TaglibURI() {
super(false, "taglib", "uri"); // NOI18N
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
List<String> list = new ArrayList<String>();
Map map = sup.getTagLibraryMappings();
if (map != null) {
Iterator iterator = map.keySet().iterator();
while(iterator.hasNext()) {
String s = (String)iterator.next();
list.add(s);
}
}
// sort alphabetically
Collections.sort(list);
return list;
}
}
public static class TaglibTagdir extends AttributeValueSupport.Default {
public TaglibTagdir() {
super(false, "taglib", "tagdir"); // NOI18N
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
List<String> l = new ArrayList<String>();
FileObject orig = sup.getFileObject();
FileObject documentBase = JspUtils.guessWebModuleRoot(orig);
if (documentBase != null) {
FileObject webInfTags = JspUtils.findRelativeFileObject(documentBase, "WEB-INF/tags");
if (webInfTags != null) {
// WEB-INF/tags itself
if (isValidTagDir(webInfTags)) {
l.add(JspUtils.findRelativeContextPath(documentBase, webInfTags));
}
// subfolders of WEB-INF/tags
Enumeration en = webInfTags.getFolders(true);
while (en.hasMoreElements()) {
FileObject subF = (FileObject)en.nextElement();
if (isValidTagDir(subF)) {
l.add(JspUtils.findRelativeContextPath(documentBase, subF));
}
}
}
}
// sort alphabetically
Collections.sort(l);
return l;
}
private boolean isValidTagDir(FileObject subF) {
// must contain at least one file
return subF.getChildren(false).hasMoreElements();
}
}
/** Support for code completing of package and class. */
public static class FilenameSupport extends AttributeValueSupport.Default {
static final ImageIcon PACKAGE_ICON =
ImageUtilities.loadImageIcon("org/openide/loaders/defaultFolder.gif", false); // NOI18N
public FilenameSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
/** Returns the complete result that contains elements from getCompletionItems. */
@Override
public void result(JspCompletionQuery.CompletionResultSet result, JTextComponent component, int offset,
JspSyntaxSupport sup, SyntaxElement.TagDirective item, String valuePart) {
String path = ""; // NOI18N
String fileNamePart = valuePart;
int lastSlash = valuePart.lastIndexOf('/');
if (lastSlash == 0) {
path = "/"; // NOI18N
fileNamePart = valuePart.substring(1);
} else if (lastSlash > 0) { // not a leading slash?
path = valuePart.substring(0, lastSlash);
fileNamePart = (lastSlash == valuePart.length())? "": valuePart.substring(lastSlash+1); // NOI18N
}
int anchor = offset - valuePart.length() + lastSlash + 1; // works even with -1
try {
FileObject orig = sup.getFileObject();
FileObject documentBase = JspUtils.guessWebModuleRoot(orig);
// need to normalize fileNamePart with respect to orig
String ctxPath = JspUtils.resolveRelativeURL("/"+orig.getPath(), path); // NOI18N
//is this absolute path?
if (path.startsWith("/") && documentBase != null) {
ctxPath = documentBase.getPath() + path;
} else {
ctxPath = ctxPath.substring(1);
}
FileSystem fs = orig.getFileSystem();
FileObject folder = fs.findResource(ctxPath);
if (folder != null) {
//add all accessible files from current context
result.addAllItems(files(anchor, folder, fileNamePart, sup));
//add go up in the directories structure item
if (!folder.equals(documentBase) && !path.startsWith("/") // NOI18N
&& (path.length() == 0 || (path.lastIndexOf("../")+3 == path.length()))){ // NOI18N
result.addItem(JspCompletionItem.createGoUpFileCompletionItem(anchor, java.awt.Color.BLUE, PACKAGE_ICON)); // NOI18N
}
}
} catch (FileStateInvalidException ex) {
// unreachable FS - disable completion
} catch (IllegalArgumentException ex) {
// resolving failed
}
result.setAnchorOffset(anchor);
}
private List<JspCompletionItem> files(int offset, FileObject folder, String prefix, JspSyntaxSupport sup) {
ArrayList<JspCompletionItem> res = new ArrayList<JspCompletionItem>();
TreeMap<String, JspCompletionItem> resFolders = new TreeMap<String, JspCompletionItem>();
TreeMap<String, JspCompletionItem> resFiles = new TreeMap<String, JspCompletionItem>();
Enumeration<? extends FileObject> files = folder.getChildren(false);
while (files.hasMoreElements()) {
FileObject file = files.nextElement();
String fname = file.getNameExt();
if (fname.startsWith(prefix) && !"cvs".equalsIgnoreCase(fname)) {
if (file.isFolder())
resFolders.put(file.getNameExt(), JspCompletionItem.createFileCompletionItem(file.getNameExt() + "/", offset, java.awt.Color.BLUE, PACKAGE_ICON));
else{
java.awt.Image icon = JspUtils.getIcon(file);
if (icon != null)
resFiles.put(file.getNameExt(), JspCompletionItem.createFileCompletionItem(file.getNameExt(), offset, java.awt.Color.BLACK, new javax.swing.ImageIcon(icon)));
else
resFiles.put(file.getNameExt(), JspCompletionItem.createFileCompletionItem(file.getNameExt(), offset, java.awt.Color.BLACK, null));
}
}
}
res.addAll(resFolders.values());
res.addAll(resFiles.values());
return res;
}
}
public static class TrueFalseSupport extends AttributeValueSupport.Default {
public TrueFalseSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("false"); // NOI18N
list.add("true"); // NOI18N
return list;
}
}
public static class PageLanguage extends AttributeValueSupport.Default {
public PageLanguage() {
super(false, "page", "language"); // NOI18N
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
list.add("java"); // NOI18N
return list;
}
}
public static class EncodingSupport extends AttributeValueSupport.Default {
public EncodingSupport(boolean tag, String longName, String attrName) {
super(tag, longName, attrName);
}
@Override
protected List<String> possibleValues(JspSyntaxSupport sup, SyntaxElement.TagDirective item) {
ArrayList<String> list = new ArrayList<String>();
Iterator<String> iter = java.nio.charset.Charset.availableCharsets().keySet().iterator();
while (iter.hasNext())
list.add(iter.next());
return list;
}
}
}