package org.netbeans.freemarker;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.StyledDocument;
import org.netbeans.api.editor.completion.Completion;
import org.netbeans.spi.editor.completion.CompletionDocumentation;
import org.netbeans.spi.editor.completion.CompletionItem;
import org.netbeans.spi.editor.completion.CompletionResultSet;
import org.netbeans.spi.editor.completion.CompletionTask;
import org.netbeans.spi.editor.completion.support.AsyncCompletionQuery;
import org.netbeans.spi.editor.completion.support.AsyncCompletionTask;
import org.netbeans.spi.editor.completion.support.CompletionUtilities;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
/**
*
* @author rostanek
*/
public class FTLCompletionItem implements CompletionItem {
static enum Type {
DIRECTIVE, BUILTIN
}
private static Map<String, FTLCompletionDocumentation> docCache = new HashMap<String, FTLCompletionDocumentation>();
private static Map<String, CompletionDocumentation> docCache2 = new HashMap<String, CompletionDocumentation>();
private static Map<String, String> dirMap = new HashMap<String, String>() {
{
put("recover", "attempt");
put("noescape", "escape");
put("noEscape", "escape");
put("noParse", "noparse");
put("return", "function");
put("else", "if");
put("elseif", "if");
put("elseIf", "if");
put("items", "list");
put("sep", "list");
put("nested", "macro");
put("case", "switch");
put("default", "switch");
put("break", "switch");
put("lt", "t");
put("rt", "t");
}
};
private Type type;
private String text;
private String suffix = "";
private int caretOffset;
private int dotOffset;
private static ImageIcon fieldIcon = new ImageIcon(ImageUtilities.loadImage("org/netbeans/freemarker/icon.png"));
public FTLCompletionItem(String text, int dotOffset, int caretOffset) {
this.text = text;
this.caretOffset = caretOffset;
this.dotOffset = dotOffset;
}
public FTLCompletionItem(String text, String suffix, int dotOffset, int caretOffset) {
this.text = text;
this.suffix = suffix;
this.caretOffset = caretOffset;
this.dotOffset = dotOffset;
}
static FTLCompletionItem directive(String text, int dotOffset, int caretOffset) {
FTLCompletionItem item = new FTLCompletionItem(text, dotOffset, caretOffset);
item.type = Type.DIRECTIVE;
return item;
}
static FTLCompletionItem builtin(String text, int dotOffset, int caretOffset) {
FTLCompletionItem item = new FTLCompletionItem(text, dotOffset, caretOffset);
item.type = Type.BUILTIN;
return item;
}
@Override
public void defaultAction(JTextComponent jtc) {
try {
StyledDocument doc = (StyledDocument) jtc.getDocument();
doc.remove(dotOffset, caretOffset - dotOffset);
doc.insertString(dotOffset, text + suffix, null);
if (text.equals("if")) {
doc.insertString(dotOffset + 2, " >\n\n</#if>\n", null);
}
Completion.get().hideAll();
} catch (BadLocationException ex) {
Exceptions.printStackTrace(ex);
}
}
@Override
public void processKeyEvent(KeyEvent ke) {
}
@Override
public int getPreferredWidth(Graphics grphcs, Font font) {
return CompletionUtilities.getPreferredWidth(text, null, grphcs, font);
}
@Override
public void render(Graphics grphcs, Font font, Color defaultColor, Color backgroundColor, int width, int height, boolean selected) {
CompletionUtilities.renderHtml(fieldIcon, text, null, grphcs, font, defaultColor, width, height, selected);
}
@Override
public CompletionTask createDocumentationTask() {
if (type != null) {
return new AsyncCompletionTask(new AsyncCompletionQuery() {
@Override
protected void query(CompletionResultSet result, Document doc, int i) {
if (type == Type.DIRECTIVE) {
result.setDocumentation(FTLCompletionItem.getDocForDirective(text));
} else if (type == Type.BUILTIN) {
result.setDocumentation(FTLCompletionItem.getDocForBuiltin(text));
}
result.finish();
}
});
}
return null;
}
@Override
public CompletionTask createToolTipTask() {
return null;
}
@Override
public boolean instantSubstitution(JTextComponent jtc) {
return false;
}
@Override
public int getSortPriority() {
return 0;
}
@Override
public CharSequence getSortText() {
return text;
}
@Override
public CharSequence getInsertPrefix() {
return text;
}
private static FTLCompletionDocumentation getDocForDirective(String directive) {
String page = directive;
if (dirMap.containsKey(directive)) {
page = dirMap.get(directive);
}
if (!docCache.containsKey(page)) {
docCache.put(page, new FTLCompletionDocumentation(page));
}
return docCache.get(page);
}
private static Map<String, String> builtinGroups;
private static CompletionDocumentation getDocForBuiltin(String builtin) {
if (builtinGroups == null) {
initBuiltins();
}
builtin = builtin.replaceAll("[A-Z]", "_$0").toLowerCase(); // camelCase to underscore_case
String page = builtinGroups.get(builtin);
if (!docCache2.containsKey(builtin)) {
docCache2.put(builtin, new BuiltinDocumentation(page));
}
return docCache2.get(builtin);
}
private static void initBuiltins() {
builtinGroups = new HashMap<String, String>();
try {
String content = download(new URL("http://freemarker.org/docs/ref_builtins_alphaidx.html"));
int start = content.indexOf("<ul>");
int end = content.indexOf("</ul>", start);
String index = content.substring(start, end);
Pattern pat = Pattern.compile(".*<a href=\"([A-Za-z_#\\.]+)\">([a-z_]+)</a>.*");
Matcher mat = pat.matcher(index);
while (mat.find()) {
String href = mat.group(1);
String name = mat.group(2);
builtinGroups.put(name, href);
}
} catch (IOException ex) {
}
}
private static String download(URL url) {
try {
InputStream in = url.openStream();
byte[] buffer = new byte[1024];
StringBuilder sb = new StringBuilder(16000);
while (in.read(buffer) > 0) {
sb.append(new String(buffer));
}
return sb.toString();
} catch (IOException ex) {
//Exceptions.printStackTrace(ex);
}
return "";
}
public static class FTLCompletionDocumentation implements CompletionDocumentation {
private URL url;
private String text;
public FTLCompletionDocumentation(String text) {
try {
url = new URL("http://freemarker.org/docs/ref_directive_" + text + ".html");
} catch (MalformedURLException ex) {
url = null;
}
}
public FTLCompletionDocumentation(URL url) {
this.url = url;
}
@Override
public String getText() {
if (text == null) {
// download documentation
String content = download(url);
int start = content.indexOf("<div class=\"page-content\">");
int end = content.indexOf("<div class=\"bottom-pagers-wrapper\">", start);
if (start > 0 && end > 0) {
text = content.substring(start, end);
}
}
return text;
}
@Override
public URL getURL() {
return url;
}
@Override
public CompletionDocumentation resolveLink(String string) {
if (!string.startsWith("#")) {
if (string.startsWith("ref_directive_")) {
return FTLCompletionItem.getDocForDirective(string.substring(14, string.lastIndexOf(".")));
}
try {
return new FTLCompletionDocumentation(new URL("http://freemarker.org/docs/" + string));
} catch (MalformedURLException ex) {
//Exceptions.printStackTrace(ex);
}
}
return null;
}
@Override
public Action getGotoSourceAction() {
return null;
}
}
public static class BuiltinDocumentation implements CompletionDocumentation {
private URL url;
private String text;
public BuiltinDocumentation(String page) {
try {
url = new URL("http://freemarker.org/docs/" + page);
} catch (MalformedURLException ex) {
url = null;
}
}
@Override
public String getText() {
if (text == null) {
// download documentation
String content = download(url);
int start = content.indexOf("<h2 class=\"content-header header-section2\" id=\"" + url.getRef());
int end = content.indexOf("<h2", start + 60);
if (start > 0 && end > 0) {
text = content.substring(start, end);
}
}
return text;
}
@Override
public URL getURL() {
return url;
}
@Override
public CompletionDocumentation resolveLink(String string) {
return null;
}
@Override
public Action getGotoSourceAction() {
return null;
}
}
}