28 Commits
Author SHA1 Message Date
SuspiciousActivity cfa278cd3b Annotations fix 2021-05-02 23:22:43 +02:00
SuspiciousActivity 46c0596768 Fix global search 2021-04-20 20:59:16 +02:00
SuspiciousActivity 74e9491fcf Easier Types 2021-04-20 20:56:00 +02:00
SuspiciousActivity 198c76823b Fixed delete 2021-04-19 11:04:16 +02:00
SuspiciousActivity 1c6a72d951 Bug fix 2021-03-28 22:55:09 +02:00
SuspiciousActivity ed9b6a177b Removed Fernflower 2021-03-12 23:57:42 +01:00
SuspiciousActivity a9612c058c Removed ".class" from internal classNodes map 2021-03-12 23:29:47 +01:00
SuspiciousActivity b86e8658e2 Fixed subclasses in decompilers 2021-03-12 23:04:10 +01:00
SuspiciousActivity a0b955a6ae Fix for decompilers being editable 2021-03-12 12:42:44 +01:00
SuspiciousActivity 97422300c8 Executor improvement 2021-03-12 12:35:52 +01:00
SuspiciousActivity 8832d67d8b Cleanup 2021-03-12 12:28:00 +01:00
SuspiciousActivity 71c224f83f Indentation in decompilers to 4 spaces, fixed some threading issues 2021-03-12 00:29:09 +01:00
SuspiciousActivity 71437eac81 Cleanup 2021-03-11 19:05:26 +01:00
SuspiciousActivity 5a5cb6425f Improvements 2021-03-10 00:20:09 +01:00
SuspiciousActivity a85171624b Deleted some unused files 2021-03-09 23:58:38 +01:00
SuspiciousActivity c2e22e55ad Cleanup 2021-03-09 23:51:41 +01:00
SuspiciousActivity ea282d240e Fixed replacing in decompilers 2021-03-09 18:35:44 +01:00
SuspiciousActivity 5bec62789e Fixed replacing in decompilers 2021-03-09 18:33:44 +01:00
SuspiciousActivity be2930f720 Wrong file 2021-03-09 18:32:13 +01:00
SuspiciousActivity e665527182 Fixed replacing in decompilers 2021-03-09 18:31:02 +01:00
SuspiciousActivity 3a30fbdd78 Update README.md 2021-03-09 18:26:47 +01:00
SuspiciousActivity bc0ec73071 Cleanup 2021-03-09 18:24:07 +01:00
SuspiciousActivity b2267947c8 Merge pull request #1 from Outfluencer/master
Added some decompilers and decompile and disassemble async
2021-03-09 16:33:57 +01:00
Outfluencer 15c581ceb6 Added some decompilers and decompile and disassemble async 2021-03-09 00:55:38 +01:00
SuspiciousActivity c0005493fd Fix & Styling 2021-03-08 23:51:28 +01:00
Nico fb4da77359 Fixes and updates 2021-03-07 15:22:55 +01:00
SuspiciousActivity d1710ea6a5 Better frames 2020-11-29 00:38:20 +01:00
SuspiciousActivity ad55d6793c Fixed number obfuscator 2020-11-27 11:06:26 +01:00
274 changed files with 11917 additions and 109268 deletions

No files matched your search

+1 -5
View File
@@ -2,8 +2,6 @@
A simple, text based Java Bytecode Editor.
It uses ObjectWeb ASM (http://asm.ow2.org/) and the RSyntaxTextArea from https://github.com/bobbylight/RSyntaxTextArea
Get the precompiled version from here: https://github.com/SuspiciousActivity/Random-Java-Stuff/blob/master/ByteEdit.jar
To open up a file, drag it onto the left side. To save a class, press CTRL + S on the right side, to save the jar file, press on the left side. With F5 you can reload either the file or only the class.
To search/replace in a file, press CTRL + F.
@@ -12,12 +10,10 @@ To open a class, press CTRL + T.
To change settings like auto-computation of stack frames and max locals/stack press CTRL + O.
To go to the field/method definition in the selected line press CTRL + G.
To (un-)escape unicode characters, press CTRL + U.
To rename a field/method with attention to references in the selected line, press CTRL + R.
To quickly compile some java code, press CTRL + E.
To switch between light and dark theme, press CTRL + B.
To switch between themes, press CTRL + B.
+53 -31
View File
@@ -15,6 +15,8 @@ import java.io.StringWriter;
import java.net.URI;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JFrame;
@@ -32,7 +34,8 @@ import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import me.ByteEdit.edit.Disassembler;
import me.ByteEdit.decompiler.EnumDecompiler;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
import me.ByteEdit.main.ThemeManager;
@@ -105,44 +108,63 @@ public class CompilationBox extends JFrame {
null, Arrays.asList(new JavaSourceFromString("Compiled", source))).call();
String res = out.toString();
if (res.isEmpty()) {
File clazz = new File(tmpFolder, "Compiled.class");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = new FileInputStream(clazz);
byte[] tmp = new byte[1024];
int r;
while ((r = is.read(tmp)) > 0) {
baos.write(tmp, 0, r);
}
is.close();
ClassReader read = new ClassReader(baos.toByteArray());
ClassNode node = new ClassNode();
read.accept(node, 0);
String dis = Disassembler.disassemble(node);
File[] files = tmpFolder.listFiles();
if (files != null)
for (File f : files) {
if (f.getName().equals("Compiled.class"))
continue;
baos = new ByteArrayOutputStream();
is = new FileInputStream(f);
SingleThreadedExecutor.submit(() -> {
try {
File clazz = new File(tmpFolder, "Compiled.class");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = new FileInputStream(clazz);
byte[] tmp = new byte[1024];
int r;
while ((r = is.read(tmp)) > 0) {
baos.write(tmp, 0, r);
}
is.close();
read = new ClassReader(baos.toByteArray());
node = new ClassNode();
ClassReader read = new ClassReader(baos.toByteArray());
ClassNode node = new ClassNode();
read.accept(node, 0);
dis += "\n" + Disassembler.disassemble(node);
EnumDecompiler decompiler = EnumDecompiler.BYTEEDIT;
ClassNode mainNode = node;
Map<String, ClassNode> classNodes;
synchronized (Main.classNodes) {
classNodes = new HashMap<>(Main.classNodes);
}
classNodes.put("Compiled", node);
File[] files = tmpFolder.listFiles();
if (files != null)
for (File f : files) {
if (f.getName().equals("Compiled.class"))
continue;
baos = new ByteArrayOutputStream();
is = new FileInputStream(f);
while ((r = is.read(tmp)) > 0) {
baos.write(tmp, 0, r);
}
is.close();
read = new ClassReader(baos.toByteArray());
node = new ClassNode();
read.accept(node, 0);
classNodes.put(node.name, node);
}
compSuccess.textArea
.setText(decompiler.getDecompiler().decompile(mainNode, classNodes));
compSuccess.setVisible(true);
} catch (IOException e2) {
e2.printStackTrace();
}
compSuccess.textArea.setText(dis);
compSuccess.setVisible(true);
try {
for (File f : tmpFolder.listFiles()) {
Files.delete(f.toPath());
}
Files.delete(tmpFolder.toPath());
} catch (IOException e2) {
e2.printStackTrace();
}
});
} else {
JOptionPane.showMessageDialog(CompilationBox.this, res, "Error!", JOptionPane.ERROR_MESSAGE);
}
for (File f : tmpFolder.listFiles()) {
Files.delete(f.toPath());
}
Files.delete(tmpFolder.toPath());
} catch (IOException e1) {
e1.printStackTrace();
}
@@ -150,7 +172,7 @@ public class CompilationBox extends JFrame {
}, ctrlS, JComponent.WHEN_FOCUSED);
}
class JavaSourceFromString extends SimpleJavaFileObject {
private static class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
public JavaSourceFromString(String name, String code) {
+17 -36
View File
@@ -1,8 +1,6 @@
package me.ByteEdit.boxes;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
@@ -18,6 +16,7 @@ import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
@@ -59,13 +58,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblFind);
txtString = new JTextField();
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
txtString.registerKeyboardAction(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
find();
}
}, enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtString.registerKeyboardAction(e -> find(), enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtString.setBounds(91, 22, 149, 20);
contentPane.add(txtString);
txtString.setColumns(10);
@@ -73,12 +66,7 @@ public class GlobalSearchBox extends JFrame {
chckbxCaseSensitive.setBounds(10, 199, 114, 23);
contentPane.add(chckbxCaseSensitive);
JButton btnFind = new JButton("Find");
btnFind.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
btnFind.addActionListener(e -> find());
btnFind.setBounds(130, 199, 110, 23);
contentPane.add(btnFind);
@@ -91,12 +79,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblOwner);
txtOwner = new JTextField();
txtOwner.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
txtOwner.addActionListener(e -> find());
txtOwner.setColumns(10);
txtOwner.setBounds(91, 78, 149, 20);
contentPane.add(txtOwner);
@@ -106,12 +89,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblName);
txtName = new JTextField();
txtName.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
txtName.addActionListener(e -> find());
txtName.setColumns(10);
txtName.setBounds(91, 106, 149, 20);
contentPane.add(txtName);
@@ -121,12 +99,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblDesc);
txtDesc = new JTextField();
txtDesc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
txtDesc.addActionListener(e -> find());
txtDesc.setColumns(10);
txtDesc.setBounds(91, 134, 149, 20);
contentPane.add(txtDesc);
@@ -141,6 +114,7 @@ public class GlobalSearchBox extends JFrame {
if (ignoreCase) {
str = str.toLowerCase();
}
String fstr = str;
for (ClassNode cn : Main.classNodes.values()) {
for (MethodNode mn : cn.methods) {
for (AbstractInsnNode insn : mn.instructions.toArray()) {
@@ -154,6 +128,15 @@ public class GlobalSearchBox extends JFrame {
}
}
}
if (mn.visibleAnnotations != null) {
for (AnnotationNode anno : mn.visibleAnnotations) {
if (anno.values == null)
continue;
anno.values.stream().filter(o -> o instanceof String).map(o -> (String) o)
.map(s -> ignoreCase ? s.toLowerCase() : s).filter(s -> s.contains(fstr))
.forEach(s -> set.add(new Info(cn.name, mn.name, mn.desc, mn)));
}
}
}
}
} else {
@@ -183,9 +166,7 @@ public class GlobalSearchBox extends JFrame {
}
}
}
for (Info s : set) {
openBox.model.addElement(s);
}
set.forEach(openBox.model::addElement);
openBox.setVisible(true);
}
}
+3 -13
View File
@@ -11,10 +11,10 @@ import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
public class GlobalSearchOpenBox extends JFrame {
@@ -36,18 +36,10 @@ public class GlobalSearchOpenBox extends JFrame {
model = new DefaultListModel<>();
JList<Info> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
Info val = list.getSelectedValue();
if (val != null && !val.owner.equals(Main.currentNodeName)) {
int lineFound = Main.selectFileWithSearch(val.nodePath, val.mn);
if (lineFound != -1) {
try {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
if (!e.getValueIsAdjusting() && val != null && !val.owner.equals(Main.currentNodeName)) {
SingleThreadedExecutor.submit(() -> Main.selectFile(val.owner + ".class"));
}
}
});
@@ -65,14 +57,12 @@ public class GlobalSearchOpenBox extends JFrame {
String name;
String desc;
MethodNode mn;
String nodePath;
public Info(String owner, String name, String desc, MethodNode mn) {
this.owner = owner;
this.name = name;
this.desc = desc;
this.mn = mn;
nodePath = Main.getFullName(owner);
}
@Override
+12 -25
View File
@@ -20,7 +20,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.edit.Disassembler;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.UnicodeUtils;
@@ -79,7 +79,7 @@ public class RenameBox extends JFrame {
JOptionPane.showMessageDialog(null, "Desc not set!", "Error!", JOptionPane.ERROR_MESSAGE);
return;
}
ClassNode clazz = Main.classNodes.get(Main.getFullName(className));
ClassNode clazz = Main.classNodes.get(className);
if (clazz == null) {
JOptionPane.showMessageDialog(null, "ClassNode not found!", "Error!", JOptionPane.ERROR_MESSAGE);
return;
@@ -94,7 +94,7 @@ public class RenameBox extends JFrame {
if (newDesc.startsWith("(")) { // method
ClassNode lastHit = clazz;
while (clazz != null && clazz.superName != null) {
ClassNode tmp = Main.classNodes.get(Main.getFullName(clazz.superName));
ClassNode tmp = Main.classNodes.get(clazz.superName);
if (tmp != null) {
boolean found = false;
for (MethodNode mn : clazz.methods) {
@@ -127,7 +127,7 @@ public class RenameBox extends JFrame {
extendsMyClass = true;
break;
}
ClassNode tmp = Main.classNodes.get(Main.getFullName(checkCN.superName));
ClassNode tmp = Main.classNodes.get(checkCN.superName);
if (tmp != null) {
checkCN = tmp;
} else {
@@ -210,27 +210,14 @@ public class RenameBox extends JFrame {
return;
}
}
try { // refresh
ClassNode classNode = Main.classNodes.get(Main.currentNodeName);
int prev = Main.txtByteEditView.getCaretPosition();
String dis = Disassembler.disassemble(classNode);
String substr = Main.currentNodeName.substring(0, Main.currentNodeName.length() - 6);
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
if (split[0].equals(substr)) {
dis += "\n" + Disassembler.disassemble(Main.classNodes.get(key));
}
}
}
Main.txtByteEditView.setText(dis);
Main.txtByteEditView.setCaretPosition(prev);
} catch (Exception e2) {
e2.printStackTrace();
}
name = newName;
desc = newDesc;
RenameBox.this.setVisible(false);
SingleThreadedExecutor.submit(() -> {
// refresh
Main.decompileCurrentNode();
name = newName;
desc = newDesc;
RenameBox.this.setVisible(false);
});
}
});
btnRename.setBounds(345, 237, 89, 23);
+4 -1
View File
@@ -123,13 +123,16 @@ public class SearchBox extends JFrame {
}
public void replace() {
String txt = Main.txtByteEditView.getText();
if (!Main.decompiler.isEditable())
return;
int startPos = Main.txtByteEditView.getSelectionStart();
Main.txtByteEditView.replaceSelection(txtReplace.getText());
Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length());
}
public void replaceAll() {
if (!Main.decompiler.isEditable())
return;
String toFind = txtFind.getText();
String toReplace = txtReplace.getText();
String txt = Main.txtByteEditView.getText();
+10 -11
View File
@@ -17,6 +17,7 @@ import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
import me.ByteEdit.utils.UnicodeUtils;
@@ -39,11 +40,11 @@ public class TypeOpenBox extends JFrame {
DefaultListModel<String> model = new DefaultListModel<>();
JList<String> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
String val = list.getSelectedValue();
if (val != null && !val.equals(Main.currentNodeName)) {
Main.selectFile(val);
if (!e.getValueIsAdjusting() && val != null && !val.equals(Main.currentNodeName)) {
SingleThreadedExecutor.submit(() -> Main.selectFile(val + ".class"));
}
}
});
@@ -78,19 +79,17 @@ public class TypeOpenBox extends JFrame {
}
String search = UnicodeUtils.unescape(null, txtSearch.getText(), true).toLowerCase();
if (search.contains("/")) {
for (String s : Main.classNodes.keySet()) {
String className = s.substring(0, s.length() - 6);
if (!className.contains("$") && className.toLowerCase().startsWith(search)) {
model.addElement(s);
for (String fullName : Main.classNodes.keySet()) {
if (!fullName.contains("$") && fullName.toLowerCase().startsWith(search)) {
model.addElement(fullName);
}
}
} else {
for (String s : Main.classNodes.keySet()) {
String[] split = s.split("/");
for (String fullName : Main.classNodes.keySet()) {
String[] split = fullName.split("/");
String className = split[split.length - 1];
className = className.substring(0, className.length() - 6);
if (!className.contains("$") && className.toLowerCase().startsWith(search)) {
model.addElement(s);
model.addElement(fullName);
}
}
}
@@ -0,0 +1,136 @@
package me.ByteEdit.decompiler;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.benf.cfr.reader.PluginRunner;
import org.benf.cfr.reader.api.ClassFileSource;
import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
import org.benf.cfr.reader.state.ClassFileSourceImpl;
import org.benf.cfr.reader.util.MiscConstants.Version;
import org.benf.cfr.reader.util.getopt.OptionsImpl;
import org.objectweb.asm.tree.ClassNode;
public class CFRDecompiler implements IDecompiler {
public static final HashMap<String, String> options = new HashMap<>();
static {
options.put("aexagg", "false");
options.put("allowcorrecting", "true");
options.put("arrayiter", "true");
options.put("caseinsensitivefs", "false");
options.put("clobber", "false");
options.put("collectioniter", "true");
options.put("commentmonitors", "false");
options.put("decodeenumswitch", "true");
options.put("decodefinally", "true");
options.put("decodelambdas", "true");
options.put("decodestringswitch", "true");
options.put("dumpclasspath", "false");
options.put("eclipse", "true");
options.put("elidescala", "false");
options.put("forcecondpropagate", "false");
options.put("forceexceptionprune", "false");
options.put("forcereturningifs", "false");
options.put("forcetopsort", "false");
options.put("forcetopsortaggress", "false");
options.put("forloopaggcapture", "false");
options.put("hidebridgemethods", "false");
options.put("hidelangimports", "true");
options.put("hidelongstrings", "false");
options.put("hideutf", "true");
options.put("innerclasses", "true");
options.put("j14classobj", "false");
options.put("labelledblocks", "true");
options.put("lenient", "false");
options.put("liftconstructorinit", "true");
options.put("override", "true");
options.put("pullcodecase", "false");
options.put("recover", "true");
options.put("recovertypeclash", "false");
options.put("recovertypehints", "false");
options.put("relinkconststring", "true");
options.put("removebadgenerics", "true");
options.put("removeboilerplate", "true");
options.put("removedeadmethods", "true");
options.put("removeinnerclasssynthetics", "true");
options.put("rename", "false");
options.put("renamedupmembers", "false");
options.put("renameenumidents", "false");
options.put("renameillegalidents", "false");
options.put("showinferrable", "false");
options.put("silent", "false");
options.put("stringbuffer", "false");
options.put("stringbuilder", "true");
options.put("sugarasserts", "true");
options.put("sugarboxing", "true");
options.put("sugarenums", "true");
options.put("tidymonitors", "true");
options.put("usenametable", "true");
try {
Field f = Version.class.getDeclaredField("version");
f.setAccessible(true);
f.set(null, "ByteEdit");
} catch (Exception ex) {
}
}
@Override
public String decompile(ClassNode cn, Map<String, ClassNode> classNodes) {
return doDecompilation(cn, IDecompiler.getBytes(cn), classNodes);
}
private static String doDecompilation(ClassNode cn, byte[] b, Map<String, ClassNode> classNodes) {
try {
HashMap<String, String> ops = options;
ClassFileSource cfs = new ClassFileSourceImpl(new OptionsImpl(Collections.EMPTY_MAP)) {
@Override
public void informAnalysisRelativePathDetail(String a, String b) {
}
@Override
public String getPossiblyRenamedPath(String path) {
return path;
}
@Override
public Pair<byte[], String> getClassFileContent(String path) throws IOException {
String name = path.substring(0, path.length() - 6);
if (name.equals(cn.name))
return Pair.make(b, name);
// try loading other classes from jar
ClassNode node;
synchronized (classNodes) {
node = classNodes.get(name);
}
if (node != null)
return Pair.make(IDecompiler.getBytes(node), name);
return super.getClassFileContent(path);
}
@Override
public Collection<String> addJar(String arg0) {
throw new RuntimeException();
}
};
String decompilation = new PluginRunner(ops, cfs).getDecompilationFor(cn.name);
System.gc(); // cfr has a performance bug
return decompilation;
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}
}
}
@@ -0,0 +1,44 @@
package me.ByteEdit.decompiler;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import me.ByteEdit.edit.Disassembler;
public enum EnumDecompiler {
BYTEEDIT("ByteEdit", new Disassembler(), false), PROCYON("Procyon", new ProcyonDecompiler()),
JD_GUI("JD-GUI", new JDDecompiler()), CFR("CFR", new CFRDecompiler());
private final String name;
private final IDecompiler decompiler;
private final String syntaxStyle;
private final boolean isEditable;
private EnumDecompiler(String name, IDecompiler decompiler) {
this(name, decompiler, true);
}
private EnumDecompiler(String name, IDecompiler decompiler, boolean isDecompiler) {
this.name = name;
this.decompiler = decompiler;
this.syntaxStyle = isDecompiler ? SyntaxConstants.SYNTAX_STYLE_JAVA
: SyntaxConstants.SYNTAX_STYLE_JAVA_DISASSEMBLE;
this.isEditable = !isDecompiler;
}
public IDecompiler getDecompiler() {
return decompiler;
}
public String getSyntaxStyle() {
return syntaxStyle;
}
public boolean isEditable() {
return isEditable;
}
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,18 @@
package me.ByteEdit.decompiler;
import java.util.Map;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
public interface IDecompiler {
String decompile(ClassNode cn, Map<String, ClassNode> classNodes);
static byte[] getBytes(ClassNode cn) {
ClassWriter classWriter = new ClassWriter(0);
cn.accept(classWriter);
return classWriter.toByteArray();
}
}
@@ -0,0 +1,137 @@
package me.ByteEdit.decompiler;
import java.util.Map;
import org.jd.core.v1.ClassFileToJavaSourceDecompiler;
import org.jd.core.v1.api.loader.Loader;
import org.jd.core.v1.api.loader.LoaderException;
import org.jd.core.v1.api.printer.Printer;
import org.objectweb.asm.tree.ClassNode;
public class JDDecompiler implements Loader, Printer, IDecompiler {
protected static final String TAB = " ";
protected static final String NEWLINE = "\n";
protected int indentationCount = 0;
protected StringBuilder sb = new StringBuilder();
private Map<String, ClassNode> classNodes;
@Override
public String decompile(ClassNode cn, Map<String, ClassNode> classNodes) {
this.classNodes = classNodes;
ClassFileToJavaSourceDecompiler decompiler = new ClassFileToJavaSourceDecompiler();
try {
decompiler.decompile(this, this, cn.name);
} catch (Throwable e) {
e.printStackTrace();
String str = e.getClass().getName() + " - " + e.getMessage() + System.lineSeparator();
for (int i = 0; i < e.getStackTrace().length; i++) {
str += e.getStackTrace()[i].toString() + System.lineSeparator();
}
return str;
}
String decomp = toString();
indentationCount = 0;
sb = new StringBuilder();
return decomp;
}
@Override
public boolean canLoad(String internalName) {
synchronized (classNodes) {
return classNodes.containsKey(internalName);
}
}
@Override
public byte[] load(String internalName) throws LoaderException {
ClassNode node;
synchronized (classNodes) {
node = classNodes.get(internalName);
}
if (node != null)
return IDecompiler.getBytes(node);
throw new LoaderException("Failed to load '" + internalName + "'");
}
@Override
public String toString() {
return sb.toString();
}
@Override
public void start(int maxLineNumber, int majorVersion, int minorVersion) {
}
@Override
public void end() {
}
@Override
public void printText(String text) {
sb.append(text);
}
@Override
public void printNumericConstant(String constant) {
sb.append(constant);
}
@Override
public void printStringConstant(String constant, String ownerInternalName) {
sb.append(constant);
}
@Override
public void printKeyword(String keyword) {
sb.append(keyword);
}
@Override
public void printDeclaration(int type, String internalTypeName, String name, String descriptor) {
sb.append(name);
}
@Override
public void printReference(int type, String internalTypeName, String name, String descriptor,
String ownerInternalName) {
sb.append(name);
}
@Override
public void indent() {
this.indentationCount++;
}
@Override
public void unindent() {
this.indentationCount--;
}
@Override
public void startLine(int lineNumber) {
for (int i = 0; i < indentationCount; i++)
sb.append(TAB);
}
@Override
public void endLine() {
sb.append(NEWLINE);
}
@Override
public void extraLine(int count) {
while (count-- > 0)
sb.append(NEWLINE);
}
@Override
public void startMarker(int type) {
}
@Override
public void endMarker(int type) {
}
}
@@ -0,0 +1,85 @@
package me.ByteEdit.decompiler;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Map;
import org.objectweb.asm.tree.ClassNode;
import com.strobel.assembler.InputTypeLoader;
import com.strobel.assembler.metadata.Buffer;
import com.strobel.assembler.metadata.ITypeLoader;
import com.strobel.assembler.metadata.MetadataSystem;
import com.strobel.assembler.metadata.TypeDefinition;
import com.strobel.assembler.metadata.TypeReference;
import com.strobel.decompiler.DecompilationOptions;
import com.strobel.decompiler.DecompilerSettings;
import com.strobel.decompiler.PlainTextOutput;
public class ProcyonDecompiler implements IDecompiler {
@Override
public String decompile(ClassNode cn, Map<String, ClassNode> classNodes) {
return doDecompilation(cn, IDecompiler.getBytes(cn), classNodes);
}
private static String doDecompilation(ClassNode cn, byte[] b, Map<String, ClassNode> classNodes) {
try {
// TODO decompile method only
DecompilerSettings settings = new DecompilerSettings();
try {
for (Field f : settings.getClass().getDeclaredFields()) {
if (f.getType() == boolean.class) {
f.setAccessible(true);
f.setBoolean(settings, true);
}
}
} catch (Throwable t) {
t.printStackTrace();
}
settings.setShowSyntheticMembers(true);
MetadataSystem metadataSystem = new MetadataSystem(new ITypeLoader() {
private InputTypeLoader backLoader = new InputTypeLoader();
@Override
public boolean tryLoadType(String s, Buffer buffer) {
if (s.equals(cn.name)) {
buffer.putByteArray(b, 0, b.length);
buffer.position(0);
return true;
}
ClassNode node;
synchronized (classNodes) {
node = classNodes.get(s);
}
if (node != null) {
byte[] data = IDecompiler.getBytes(node);
buffer.putByteArray(data, 0, data.length);
buffer.position(0);
return true;
}
return backLoader.tryLoadType(s, buffer);
}
});
TypeReference type = metadataSystem.lookupType(cn.name);
DecompilationOptions decompilationOptions = new DecompilationOptions();
decompilationOptions.setSettings(DecompilerSettings.javaDefaults());
decompilationOptions.setFullDecompilation(true);
TypeDefinition resolvedType = null;
if (type == null || ((resolvedType = type.resolve()) == null)) {
return "error";
}
StringWriter stringwriter = new StringWriter();
settings.getLanguage().decompileType(resolvedType, new PlainTextOutput(stringwriter), decompilationOptions);
String decompiledSource = stringwriter.toString();
return decompiledSource;
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}
}
}
@@ -0,0 +1,44 @@
package me.ByteEdit.decompiler;
import java.util.concurrent.LinkedBlockingQueue;
public class SingleThreadedExecutor {
// We don't need to run every Runnable,
// we only care about the last submitted Runnable.
private static final Object lock = new Object();
private static Runnable runnable;
private static final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
private static final Thread thread = new Thread(() -> {
try {
while (true) {
Runnable r = null;
synchronized (lock) {
while (runnable == null)
lock.wait();
r = runnable;
runnable = null;
}
try {
r.run();
} catch (Throwable e) {
e.printStackTrace();
}
}
} catch (InterruptedException e) {
}
}, "SingleThreadedExecutor");
static {
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
public static void submit(Runnable r) {
synchronized (lock) {
runnable = r;
lock.notifyAll();
}
}
}
+104 -150
View File
@@ -4,6 +4,7 @@ import java.io.BufferedReader;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -54,6 +55,7 @@ import me.ByteEdit.utils.UnicodeUtils;
public class Assembler {
public static final Pattern SPACE = Pattern.compile(" ");
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
public static ClassNode assemble(String input) {
Main.txtByteEditView.removeAllLineHighlights();
@@ -338,6 +340,8 @@ public class Assembler {
tryCatchBlocksToParse.clear();
stage = 0;
} else if (s.startsWith("// #Signature: ")) {
if (node == null)
throw new IllegalStateException("node is null");
node.signature = UnicodeUtils.unescape(hsr, s.substring(15));
} else if (s.equals("// #TryCatch:")) {
stage = 1;
@@ -351,6 +355,8 @@ public class Assembler {
localVarsToParse.add(s);
}
} else if (s.equals("}")) {
if (node == null)
throw new IllegalStateException("node is null");
if (!annotationsForNext.isEmpty()) {
if (node.visibleAnnotations == null) {
node.visibleAnnotations = new ArrayList<>();
@@ -403,6 +409,8 @@ public class Assembler {
}
clazz.methods.add(node);
} else if (!s.startsWith("\t")) {
if (node == null)
throw new IllegalStateException("node is null");
s = s.substring(0, s.length() - 2);
if (s.contains(" throws ")) {
String exceptions = s.substring(s.lastIndexOf(" throws ") + 8);
@@ -476,6 +484,8 @@ public class Assembler {
s = s.substring(1);
if (!temp.isEmpty()) {
if (s.equals("]")) {
if (node == null)
throw new IllegalStateException("node is null");
temp += s;
node.instructions.add(getNode(hsr, temp, methodLabelMap));
temp = "";
@@ -486,6 +496,8 @@ public class Assembler {
if (s.endsWith("[")) {
temp += s + "\n";
} else {
if (node == null)
throw new IllegalStateException("node is null");
node.instructions.add(getNode(hsr, s, methodLabelMap));
}
}
@@ -494,18 +506,21 @@ public class Assembler {
}
return clazz;
} catch (Throwable e) {
System.err.println("Error at line: " + read.currentLine);
try {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine));
} catch (BadLocationException e1) {
System.err.println("Can't show line!");
if (read != null) {
System.err.println("Error at line: " + read.currentLine);
try {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine));
} catch (BadLocationException e1) {
System.err.println("Can't show line!");
}
}
Main.showError(e.getMessage());
e.printStackTrace();
return null;
} finally {
try {
read.close();
if (read != null)
read.close();
} catch (Exception e) {
}
}
@@ -559,7 +574,9 @@ public class Assembler {
String value = split2[1];
if (value.endsWith("]"))
value = value.substring(0, value.length() - 1);
if (value.startsWith("{ ")) {
if (value.equals("{ }")) {
node.values.add(Collections.EMPTY_LIST);
} else if (value.startsWith("{ ")) {
String split3[] = splitWithStrings(value.substring(2, value.length() - 2));
List<Object> list = new ArrayList<>();
for (String rofl : split3) {
@@ -599,26 +616,28 @@ public class Assembler {
if (args.length == 0) {
return null;
}
String ret = args[start];
StringBuilder sb = new StringBuilder(args[start]);
if (args.length > start + 1) {
for (int i = start + 1; i < args.length; i++) {
ret = ret + " " + args[i];
sb.append(' ');
sb.append(args[i]);
}
}
return ret;
return sb.toString();
}
private static String consolidateStrings(String[] args, int start, int end) {
if (end == 0) {
return null;
}
String ret = args[start];
StringBuilder sb = new StringBuilder(args[start]);
if (end > start + 1) {
for (int i = start + 1; i < end; i++) {
ret = ret + " " + args[i];
sb.append(' ');
sb.append(args[i]);
}
}
return ret;
return sb.toString();
}
private static Object getValue(HugeStringsRev hsr, String s, String to) {
@@ -674,118 +693,41 @@ public class Assembler {
labels.put(ln, labelNr);
return new LineNumberNode(Integer.parseInt(s.split(" ")[0]), ln);
} else if (s.startsWith("// frame ")) {
String str1 = s.substring(7);
String str = str1.substring(str1.indexOf(' ', 3) + 1);
Object[] local;
Object[] stack;
ArrayList<Object> arr = new ArrayList<>();
if (str.startsWith("l:null")) {
local = new Object[0];
} else {
String l = str.substring(3, str.indexOf("s:") - 2);
if (l.isEmpty()) {
local = new Object[0];
} else {
for (String asd : l.split(", ")) {
if (!asd.startsWith("(")) {
int frameType = ClassUtil.getFrameTypeByName(asd);
if (frameType != -1)
arr.add(new Integer(frameType));
else
arr.add(UnicodeUtils.unescape(hsr, asd.substring(1, asd.length() - 1)));
} else {
if (asd.startsWith("(label) ")) {
int labelNr = Integer.parseInt(asd.split(" ")[1]);
boolean found = false;
for (Map.Entry<LabelNode, Integer> entry : labels.entrySet()) {
if (entry.getValue() == labelNr) {
arr.add(entry.getKey());
found = true;
}
}
if (!found) {
LabelNode ln = new LabelNode(new Label());
labels.put(ln, labelNr);
arr.add(ln);
}
} else {
arr.add(ClassUtil.getCastedValue(asd.split(" ")[1], asd.split("\\) ")[0].substring(1)));
}
}
}
local = new Object[arr.size()];
int c = 0;
for (Object o : arr) {
local[c] = o;
c++;
}
}
}
arr.clear();
if (str.endsWith("s:null")) {
stack = new Object[0];
} else {
String st = str.substring(str.indexOf("s:") + 3, str.length() - 1);
if (st.isEmpty()) {
stack = new Object[0];
} else {
for (String asd : st.split(", ")) {
if (!asd.startsWith("(")) {
int frameType = ClassUtil.getFrameTypeByName(asd);
if (frameType != -1)
arr.add(new Integer(frameType));
else
arr.add(UnicodeUtils.unescape(hsr, asd.substring(1, asd.length() - 1)));
} else {
if (asd.startsWith("(label) ")) {
int labelNr = Integer.parseInt(asd.split(" ")[1]);
boolean found = false;
for (Map.Entry<LabelNode, Integer> entry : labels.entrySet()) {
if (entry.getValue() == labelNr) {
arr.add(entry.getKey());
found = true;
}
}
if (!found) {
LabelNode ln = new LabelNode(new Label());
labels.put(ln, labelNr);
arr.add(ln);
}
} else {
arr.add(ClassUtil.getCastedValue(asd.split(" ")[1], asd.split("\\) ")[0].substring(1)));
}
}
}
stack = new Object[arr.size()];
int c = 0;
for (Object o : arr) {
stack[c] = o;
c++;
}
}
}
String str1 = s.substring(9);
String[] split = str1.split(" ", 2);
int type = 0;
switch (str1.substring(str1.indexOf(' ', 1) + 1, str1.indexOf(' ', 3))) {
case "NEW":
type = Opcodes.F_NEW;
break;
case "FULL":
type = Opcodes.F_FULL;
break;
case "APPEND":
type = Opcodes.F_APPEND;
break;
case "CHOP":
type = Opcodes.F_CHOP;
break;
Object[] local = null;
Object[] stack = null;
int localLen = 0;
int stackLen = 0;
switch (split[0].toUpperCase()) {
case "SAME":
type = Opcodes.F_SAME;
break;
case "SAME1":
type = Opcodes.F_SAME1;
stack = parseFrameList(hsr, split[1], labels);
stackLen = stack.length;
break;
case "CHOP":
type = Opcodes.F_CHOP;
localLen = Integer.parseInt(split[1]);
break;
case "APPEND":
type = Opcodes.F_APPEND;
local = parseFrameList(hsr, split[1], labels);
localLen = local.length;
break;
case "FULL":
type = Opcodes.F_FULL;
split = split[1].split("\\] \\[", 2);
local = parseFrameList(hsr, split[0] + "]", labels);
localLen = local.length;
stack = parseFrameList(hsr, "[" + split[1], labels);
stackLen = stack.length;
break;
}
return new FrameNode(type, local.length, local, stack.length, stack);
return new FrameNode(type, localLen, local, stackLen, stack);
} else {
String[] split = s.split(" ");
String command = split[0];
@@ -823,23 +765,17 @@ public class Assembler {
case "dconst_1":
return new InsnNode(15);
case "bipush": {
return new IntInsnNode(16, Integer.parseInt(split[1]));
return new IntInsnNode(16, (byte) Integer.parseInt(split[1]));
}
case "sipush": {
return new IntInsnNode(17, Integer.parseInt(split[1]));
return new IntInsnNode(17, (short) Integer.parseInt(split[1]));
}
case "ldc": {
String str = consolidateStrings(split, 1);
Object val;
try {
if (str.startsWith("Type: ")) {
str = str.substring(8, str.length() - 2);
String[] split2 = str.split("\n\t");
String type = split2[0].substring(7);
// Reflection
val = new Type(ClassUtil.getIDFromClassNameForType(type),
UnicodeUtils.unescape(hsr, split2[3].substring(6, split2[3].length() - 1)),
Integer.parseInt(split2[1].substring(7)), Integer.parseInt(split2[2].substring(5)));
if (str.startsWith("Type ")) {
val = Type.getType(UnicodeUtils.unescape(hsr, str.substring(6, str.length() - 1), true));
} else if (str.startsWith("\"") && str.endsWith("\"")) {
val = UnicodeUtils.unescape(hsr, str.substring(1, str.length() - 1), true);
} else if (str.startsWith("#")) {
@@ -1517,17 +1453,7 @@ public class Assembler {
for (int i = 10; i < sp.length - 1; i++) {
String st = sp[i].substring(1);
if (st.equals("]")) {
if (stage == 1) {
try {
// Reflection
args.add(new Type(ClassUtil.getIDFromClassNameForType(vals.get(0).substring(6)),
UnicodeUtils.unescape(hsr, vals.get(3).substring(6, vals.get(3).length() - 1)),
Integer.parseInt(vals.get(1).substring(7)),
Integer.parseInt(vals.get(2).substring(5))));
} catch (Exception e) {
throw e;
}
} else if (stage == 2) {
if (stage == 2) {
args.add(new Handle(OpcodesReverse.getHandleOpcode(vals.get(4).substring(5)),
UnicodeUtils.unescape(hsr, vals.get(1).substring(7)),
UnicodeUtils.unescape(hsr, vals.get(0).substring(6)),
@@ -1536,11 +1462,8 @@ public class Assembler {
vals.clear();
}
stage = 0;
} else if (st.equals("Type: [")) {
stage = 1;
} else if (stage == 1) {
st = st.substring(1);
vals.add(st);
} else if (st.startsWith("Type ")) {
args.add(Type.getType(UnicodeUtils.unescape(hsr, st.substring(6, st.length() - 1), true)));
} else if (st.equals("Handle: [")) {
vals.clear();
stage = 2;
@@ -1561,12 +1484,6 @@ public class Assembler {
}
}
}
Object[] _args = new Object[args.size()];
stage = 0;
for (Object o : args) {
_args[stage] = o;
stage++;
}
return new InvokeDynamicInsnNode(UnicodeUtils.unescape(hsr, sp[0].substring(7)),
UnicodeUtils.unescape(hsr, sp[1].substring(6)),
new Handle(OpcodesReverse.getHandleOpcode(sp[7].substring(6)),
@@ -1574,7 +1491,7 @@ public class Assembler {
UnicodeUtils.unescape(hsr, sp[3].substring(7)),
UnicodeUtils.unescape(hsr, sp[5].substring(7)),
Boolean.parseBoolean(sp[6].substring(14))),
_args);
args.toArray(new Object[args.size()]));
}
case "new": {
return new TypeInsnNode(187, UnicodeUtils.unescape(hsr, split[1]));
@@ -1633,4 +1550,41 @@ public class Assembler {
}
}
}
private static Object[] parseFrameList(HugeStringsRev hsr, String str, HashMap<LabelNode, Integer> labels) {
ArrayList<Object> list = new ArrayList<>();
String l = str.substring(1, str.length() - 1);
if (l.isEmpty())
return EMPTY_OBJECT_ARRAY;
for (String asd : l.split(", ")) {
if (!asd.startsWith("(")) {
int frameType = ClassUtil.getFrameTypeByName(asd);
if (frameType != -1)
list.add(Integer.valueOf(frameType));
else
list.add(UnicodeUtils.unescape(hsr, asd.substring(1, asd.length() - 1)));
} else {
if (asd.startsWith("(label) ")) {
int labelNr = Integer.parseInt(asd.split(" ")[1]);
boolean found = false;
for (Map.Entry<LabelNode, Integer> entry : labels.entrySet()) {
if (entry.getValue() == labelNr) {
list.add(entry.getKey());
found = true;
}
}
if (!found) {
LabelNode ln = new LabelNode(new Label());
labels.put(ln, labelNr);
list.add(ln);
}
} else {
list.add(ClassUtil.getCastedValue(asd.split(" ")[1], asd.split("\\) ")[0].substring(1)));
}
}
}
return list.toArray(new Object[list.size()]);
}
}
+221 -391
View File
@@ -1,16 +1,34 @@
package me.ByteEdit.edit;
import static org.objectweb.asm.tree.AbstractInsnNode.FIELD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.IINC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INT_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INVOKE_DYNAMIC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LDC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LINE;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.MULTIANEWARRAY_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.VAR_INSN;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
@@ -25,7 +43,6 @@ import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
@@ -42,55 +59,45 @@ import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import me.ByteEdit.decompiler.IDecompiler;
import me.ByteEdit.main.Main;
import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.OpcodesReverse;
import me.ByteEdit.utils.UnicodeUtils;
public class Disassembler {
public class Disassembler implements IDecompiler {
private static final ExecutorService exec = Executors
.newFixedThreadPool((Runtime.getRuntime().availableProcessors() + 1) / 2);
public static String disassemble(ClassNode classNode) {
return disassemble(classNode, null).getDisassembly();
@Override
public String decompile(ClassNode cn, Map<String, ClassNode> classNodes) {
String name = cn.name;
StringBuilder dis = new StringBuilder(disassemble(cn));
synchronized (classNodes) {
for (Entry<String, ClassNode> entry : classNodes.entrySet()) {
if (entry.getKey().contains("$")) {
String[] split = entry.getKey().split("\\$");
if (split[0].equals(name)) {
dis.append('\n');
dis.append(disassemble(entry.getValue()));
}
}
}
}
return dis.toString();
}
public static class DisassembleTuple {
private final String dis;
private final int line;
public DisassembleTuple(String dis) {
this.dis = dis;
this.line = -1;
}
public DisassembleTuple(String dis, int line) {
this.dis = dis;
this.line = line;
}
public String getDisassembly() {
return dis;
}
public int getLine() {
return line;
}
}
public static DisassembleTuple disassemble(ClassNode classNode, Object nodeToFind) {
private static String disassemble(ClassNode classNode) {
if (classNode == null) {
return new DisassembleTuple("ClassNode is null! This is not a valid java class file!");
return "ClassNode is null! This is not a valid java class file!";
}
try {
HugeStrings hs = new HugeStrings();
AtomicInteger lineFound = new AtomicInteger(-1);
StringContext ctx = new StringContext(18);
ctx.next("// #Annotations:\n");
ctx.next(doAnnotations(classNode.visibleAnnotations, hs));
ctx.next(doAnnotations(classNode.visibleAnnotations, hs, ""));
ctx.next("// #Class v:" + classNode.version + "\n");
if (classNode.signature != null)
ctx.next("// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, classNode.signature) + "\n");
@@ -133,28 +140,27 @@ public class Disassembler {
+ (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(hs, classNode.sourceFile)) + "\n");
int hugeIdx = ctx.next("");
ctx.next("\n// #Fields\n");
ctx.next(doFields(classNode.fields, nodeToFind, lineFound, hs));
ctx.next(doFields(classNode.fields, hs));
ctx.next("\n// #Methods\n");
ctx.next(doMethods(classNode.methods, classNode.name, nodeToFind, lineFound, hs));
ctx.next(doMethods(classNode.methods, hs));
ctx.next("}\n");
if (!hs.isEmpty())
ctx.set(hugeIdx, hs.makeStringsList());
return new DisassembleTuple(ctx.finish(), lineFound.get());
return ctx.finish();
} catch (Throwable e) {
return new DisassembleTuple("Class couldn't be decompiled:\n" + e.getClass().getName() + ": "
+ e.getMessage() + "\n"
return "Class couldn't be decompiled:\n" + e.getClass().getName() + ": " + e.getMessage() + "\n"
+ Arrays.toString(e.getStackTrace()).replace(", ", "\n\tat ").replace("[", "\tat ").replace("]", "")
+ "\n");
+ "\n";
}
}
private static String doAnnotations(List<AnnotationNode> annotations, HugeStrings hs) {
private static String doAnnotations(List<AnnotationNode> annotations, HugeStrings hs, String linePrefix) {
if (annotations == null)
return "";
StringContext ctx = new StringContext(annotations.size());
for (AnnotationNode annotationNode : annotations) {
String s = "@" + UnicodeUtils.escapeWithSpaces(hs, annotationNode.desc);
String s = linePrefix + "@" + UnicodeUtils.escapeWithSpaces(hs, annotationNode.desc);
if (annotationNode.values != null && !annotationNode.values.isEmpty()) {
s += " (";
boolean valBefore = true;
@@ -163,48 +169,51 @@ public class Disassembler {
s += o + " = [";
valBefore = false;
continue;
} else {
if (o instanceof String[]) {
String[] arr = (String[]) o;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "]";
}
if (o instanceof String[]) {
String[] arr = (String[]) o;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "]";
}
}
s += ", ";
} else if (o instanceof List) {
List list = (List) o;
s += "{ ";
for (Object obj : list) {
if (obj instanceof String[]) {
String[] arr = (String[]) obj;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(hs, rofl);
}
}
} else {
s += "\"" + UnicodeUtils.escape(hs, String.valueOf(obj)) + "\"";
}
s += ", ";
} else if (o instanceof List) {
List list = (List) o;
s += "{ ";
for (Object obj : list) {
if (obj instanceof String[]) {
String[] arr = (String[]) obj;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(hs, rofl);
}
}
} else {
s += "\"" + UnicodeUtils.escape(hs, String.valueOf(obj)) + "\"";
}
s += ", ";
}
s = s.substring(0, s.length() - 2);
s += " }]";
} else if (o instanceof String) {
s += "\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"], ";
} else {
s += "(" + o.getClass().getName().replace(".", "/") + ") " + o + "], ";
}
valBefore = true;
if (s.endsWith(", ")) {
s = s.substring(0, s.length() - 2);
s += " }], ";
} else {
s += "}], ";
}
} else if (o instanceof String) {
s += "\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"], ";
} else {
s += "(" + o.getClass().getName().replace(".", "/") + ") " + o + "], ";
}
valBefore = true;
}
if (s.endsWith(", "))
s = s.substring(0, s.length() - 2);
@@ -216,8 +225,8 @@ public class Disassembler {
return ctx.finish();
}
private static String doMethods(List<MethodNode> methods, String className, Object nodeToFind,
AtomicInteger lineFound, HugeStrings hs) throws InterruptedException, ExecutionException {
private static String doMethods(List<MethodNode> methods, HugeStrings hs)
throws InterruptedException, ExecutionException {
boolean multithreaded = Main.INSTANCE.mntmMultithreaded.isSelected();
Future<String>[] futures = new Future[methods.size()];
for (int i = 0; i < futures.length; i++) {
@@ -230,72 +239,11 @@ public class Disassembler {
ms += "\t// #Max: l:" + mn.maxLocals + " s:" + mn.maxStack + "\n";
if (mn.signature != null)
ms += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, mn.signature) + "\n";
String[] dis = disassembleMethod(className, mn, hs);
String[] dis = disassembleMethod(mn, hs);
ms += dis[2];
ms += dis[1];
if (mn.visibleAnnotations != null && !mn.visibleAnnotations.isEmpty()) {
for (AnnotationNode annotationNode : mn.visibleAnnotations) {
ms += "\t@" + annotationNode.desc;
if (annotationNode.values != null && !annotationNode.values.isEmpty()) {
ms += " (";
boolean valBefore = true;
for (Object o : annotationNode.values) {
if (valBefore) {
ms += o + " = [";
valBefore = false;
continue;
} else {
if (o instanceof String[]) {
String[] arr = (String[]) o;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
ms += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
ms += UnicodeUtils.escapeWithSpaces(hs, rofl) + "]";
}
}
ms += ", ";
} else if (o instanceof List) {
List list = (List) o;
ms += "{ ";
for (Object obj : list) {
if (obj instanceof String[]) {
String[] arr = (String[]) obj;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
ms += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
ms += UnicodeUtils.escapeWithSpaces(hs, rofl);
}
}
} else {
ms += "\"" + UnicodeUtils.escape(hs, String.valueOf(obj))
+ "\"";
}
ms += ", ";
}
ms = ms.substring(0, ms.length() - 2);
ms += " }]";
} else {
ms += "(" + o.getClass().getName().replace(".", "/") + ") " + o + "], ";
}
valBefore = true;
}
}
if (ms.endsWith(", "))
ms = ms.substring(0, ms.length() - 2);
ms += ")";
}
ms += "\n";
}
}
if (mn.equals(nodeToFind)) {
lineFound.set(ms.split("\\n").length);
}
if (mn.visibleAnnotations != null && !mn.visibleAnnotations.isEmpty())
ms += doAnnotations(mn.visibleAnnotations, hs, "\t");
ms += "\t" + ClassUtil.getAccessFlagsFull(mn.access)
+ UnicodeUtils.escapeWithSpaces(hs, mn.name) + " "
+ UnicodeUtils.escapeWithSpaces(hs, mn.desc) + " ";
@@ -337,74 +285,14 @@ public class Disassembler {
return s;
}
private static String doFields(List<FieldNode> fields, Object nodeToFind, AtomicInteger lineFound, HugeStrings hs) {
private static String doFields(List<FieldNode> fields, HugeStrings hs) {
StringContext ctx = new StringContext(fields.size());
for (FieldNode fn : fields) {
String s = "";
if (fn.signature != null)
s += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, fn.signature) + "\n";
if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty()) {
for (AnnotationNode annotationNode : fn.visibleAnnotations) {
s += "\t@" + UnicodeUtils.escapeWithSpaces(hs, annotationNode.desc);
if (annotationNode.values != null && !annotationNode.values.isEmpty()) {
s += " (";
boolean valBefore = true;
for (Object o : annotationNode.values) {
if (valBefore) {
s += o + " = [";
valBefore = false;
continue;
} else {
if (o instanceof String[]) {
String[] arr = (String[]) o;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "]";
}
}
s += ", ";
} else if (o instanceof List) {
List list = (List) o;
s += "{ ";
for (Object obj : list) {
if (obj instanceof String[]) {
String[] arr = (String[]) obj;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(hs, rofl);
}
}
} else {
s += "\"" + UnicodeUtils.escape(hs, String.valueOf(obj)) + "\"";
}
s += ", ";
}
s = s.substring(0, s.length() - 2);
s += " }]";
} else {
s += "(" + o.getClass().getName().replace(".", "/") + ") " + o + "], ";
}
valBefore = true;
}
}
if (s.endsWith(", "))
s = s.substring(0, s.length() - 2);
s += ")";
}
s += "\n";
}
}
if (fn.equals(nodeToFind)) {
lineFound.set(s.split("\\n").length);
}
if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty())
s += doAnnotations(fn.visibleAnnotations, hs, "\t");
s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient")
+ UnicodeUtils.escapeWithSpaces(hs, fn.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, fn.name);
if (fn.value != null) {
@@ -416,7 +304,7 @@ public class Disassembler {
return ctx.finish();
}
private static String[] disassembleMethod(String className, MethodNode mn, HugeStrings hs) {
private static String[] disassembleMethod(MethodNode mn, HugeStrings hs) {
HashMap<Label, Integer> labels = new HashMap<Label, Integer>();
String localVarTable;
String tryCatchTable;
@@ -429,7 +317,7 @@ public class Disassembler {
for (AbstractInsnNode n = insnList.getFirst(); n != null; n = n.getNext()) {
if (n instanceof LabelNode) {
int id = labels.size() + 1;
if (deobfNumbers)
if (deobfNumbers && labelsReversed != null)
labelsReversed.add(((LabelNode) n).getLabel());
labels.put(((LabelNode) n).getLabel(), id);
}
@@ -466,7 +354,7 @@ public class Disassembler {
insnList = calc.get();
int i = 0;
for (AbstractInsnNode n = insnList.getFirst(); n != null; n = n.getNext()) {
if (n instanceof LabelNode) {
if (n instanceof LabelNode && labelsReversed != null) {
((LabelNode) n).setLabel(labelsReversed.get(i++));
}
}
@@ -479,102 +367,41 @@ public class Disassembler {
return new String[] { ctx.finish(), localVarTable, tryCatchTable };
}
public static final HashMap<Class, InstructionInterface> iiMap = new HashMap<>();
static {
iiMap.put(FieldInsnNode.class, (n, labels, hs) -> {
public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels, HugeStrings hs) {
switch (n.getType()) {
case INSN: {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + "\n";
}
case INT_INSN: {
IntInsnNode node = (IntInsnNode) n;
if (n.getOpcode() == Opcodes.NEWARRAY) {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " "
+ ClassUtil.getArrayTypeByID(node.operand) + "\n";
}
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.operand + "\n";
}
case VAR_INSN: {
VarInsnNode node = (VarInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + "\n";
}
case TYPE_INSN: {
TypeInsnNode node = (TypeInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + "\n";
}
case FIELD_INSN: {
FieldInsnNode node = (FieldInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
+ "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
});
iiMap.put(LabelNode.class, (n, labels, hs) -> {
LabelNode node = (LabelNode) n;
return "\t\t// label " + labels.get(node.getLabel()) + "\n";
});
iiMap.put(FrameNode.class, (n, labels, hs) -> {
FrameNode node = (FrameNode) n;
String type = "";
switch (node.type) {
case Opcodes.F_NEW:
type = "NEW";
break;
case Opcodes.F_FULL:
type = "FULL";
break;
case Opcodes.F_APPEND:
type = "APPEND";
break;
case Opcodes.F_CHOP:
type = "CHOP";
break;
case Opcodes.F_SAME:
type = "SAME";
break;
case Opcodes.F_SAME1:
type = "SAME1";
break;
}
String s = "\t\t// frame " + type + " l:";
ArrayList<String> arr = new ArrayList<>();
if (node.local != null) {
for (Object o : node.local) {
if (o == null) {
arr.add(null);
} else {
if (o instanceof String) {
arr.add("\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"");
} else if (o instanceof LabelNode) {
arr.add("(label) " + labels.get(((LabelNode) o).getLabel()));
} else if (o instanceof Integer) {
String frameType = ClassUtil.getFrameTypeByID(((Integer) o).intValue());
if (frameType != null)
arr.add(frameType);
else
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
} else {
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
}
}
}
s += arr.toString();
} else {
s += "null";
}
arr.clear();
s += " s:";
if (node.stack != null) {
for (Object o : node.stack) {
if (o == null) {
arr.add(null);
} else {
if (o instanceof String) {
arr.add("\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"");
} else if (o instanceof LabelNode) {
arr.add("(label) " + labels.get(((LabelNode) o).getLabel()));
} else if (o instanceof Integer) {
String frameType = ClassUtil.getFrameTypeByID(((Integer) o).intValue());
if (frameType != null)
arr.add(frameType);
else
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
} else {
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
}
}
}
s += arr.toString();
} else {
s += "null";
}
s += "\n";
return s;
});
iiMap.put(LineNumberNode.class, (n, labels, hs) -> {
LineNumberNode node = (LineNumberNode) n;
return "\t\t// line " + node.line + " " + labels.get(node.start.getLabel()) + "\n";
});
iiMap.put(InvokeDynamicInsnNode.class, (n, labels, hs) -> {
}
case METHOD_INSN: {
MethodInsnNode node = (MethodInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
+ "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
}
case INVOKE_DYNAMIC_INSN: {
InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n\t\t\tdesc: "
@@ -586,15 +413,7 @@ public class Disassembler {
+ "\n\t\t\t]\n\t\t\targs: [\n";
for (Object l : node.bsmArgs) {
if (l instanceof Type) {
Type type = (Type) l;
int valueBegin = type.valueBegin;
int valueEnd = type.valueEnd;
String buf = type.valueBuffer;
// Reflection
s += "\t\t\t\tType: [\n\t\t\t\t\ttype: " + ClassUtil.getClassNameFromType(type)
+ "\n\t\t\t\t\tstart: " + valueBegin + "\n\t\t\t\t\tend: " + valueEnd + "\n\t\t\t\t\tbuf: "
+ ClassUtil.getDecompiledValue(buf, "", hs);
s += "\n\t\t\t\t]\n";
s += "\t\t\t\tType " + ClassUtil.getDecompiledValue(((Type) l).getDescriptor(), "", hs) + "\n";
} else if (l instanceof Handle) {
Handle h = (Handle) l;
s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: "
@@ -610,70 +429,36 @@ public class Disassembler {
}
s += "\t\t\t]\n\t\t]\n";
return s;
});
iiMap.put(MethodInsnNode.class, (n, labels, hs) -> {
MethodInsnNode node = (MethodInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
+ "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
});
iiMap.put(TypeInsnNode.class, (n, labels, hs) -> {
TypeInsnNode node = (TypeInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + "\n";
});
iiMap.put(MultiANewArrayInsnNode.class, (n, labels, hs) -> {
MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + node.dims + "\n";
});
iiMap.put(LdcInsnNode.class, (n, labels, hs) -> {
LdcInsnNode node = (LdcInsnNode) n;
if (node.cst instanceof Type) {
Type type = (Type) node.cst;
int valueBegin = type.valueBegin;
int valueEnd = type.valueEnd;
String buf = type.valueBuffer;
// Reflection
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " Type: [\n\t\t\ttype: "
+ ClassUtil.getClassNameFromType(type) + "\n\t\t\tstart: " + valueBegin + "\n\t\t\tend: "
+ valueEnd + "\n\t\t\tbuf: " + ClassUtil.getDecompiledValue(buf, "", hs);
s += "\n\t\t]\n";
return s;
} else {
String decomp = ClassUtil.getDecompiledValue(node.cst, "", hs);
if (node.cst instanceof String) {
if (decomp.startsWith("\"#") && decomp.endsWith("\""))
decomp = decomp.substring(1, decomp.length() - 1);
else if (decomp.startsWith("\"\\u0023"))
decomp = "\"#" + decomp.substring(7);
}
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + decomp + "\n";
}
});
iiMap.put(VarInsnNode.class, (n, labels, hs) -> {
VarInsnNode node = (VarInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + "\n";
});
iiMap.put(InsnNode.class, (n, labels, hs) -> {
InsnNode node = (InsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + "\n";
});
iiMap.put(IntInsnNode.class, (n, labels, hs) -> {
IntInsnNode node = (IntInsnNode) n;
if (n.getOpcode() == Opcodes.NEWARRAY) {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " "
+ ClassUtil.getArrayTypeByID(node.operand) + "\n";
} else {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.operand + "\n";
}
});
iiMap.put(JumpInsnNode.class, (n, labels, hs) -> {
}
case JUMP_INSN: {
JumpInsnNode node = (JumpInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + labels.get(node.label.getLabel())
+ "\n";
});
iiMap.put(TableSwitchInsnNode.class, (n, labels, hs) -> {
}
case LABEL: {
LabelNode node = (LabelNode) n;
return "\t\t// label " + labels.get(node.getLabel()) + "\n";
}
case LDC_INSN: {
LdcInsnNode node = (LdcInsnNode) n;
if (node.cst instanceof Type) {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " Type "
+ ClassUtil.getDecompiledValue(((Type) node.cst).getDescriptor(), "", hs) + "\n";
}
String decomp = ClassUtil.getDecompiledValue(node.cst, "", hs);
if (node.cst instanceof String) {
if (decomp.startsWith("\"#") && decomp.endsWith("\""))
decomp = decomp.substring(1, decomp.length() - 1);
else if (decomp.startsWith("\"\\u0023"))
decomp = "\"#" + decomp.substring(7);
}
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + decomp + "\n";
}
case IINC_INSN: {
IincInsnNode node = (IincInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + " " + node.incr + "\n";
}
case TABLESWITCH_INSN: {
TableSwitchInsnNode node = (TableSwitchInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tmin: " + node.min
+ "\n\t\t\tmax: " + node.max + "\n\t\t\tdefault: " + labels.get(node.dflt.getLabel())
@@ -683,8 +468,8 @@ public class Disassembler {
}
s += "\t\t\t]\n\t\t]\n";
return s;
});
iiMap.put(LookupSwitchInsnNode.class, (n, labels, hs) -> {
}
case LOOKUPSWITCH_INSN: {
LookupSwitchInsnNode node = (LookupSwitchInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tdefault: "
+ labels.get(node.dflt.getLabel()) + "\n\t\t\tkeys: [\n";
@@ -697,24 +482,69 @@ public class Disassembler {
}
s += "\t\t\t]\n\t\t]\n";
return s;
});
iiMap.put(IincInsnNode.class, (n, labels, hs) -> {
IincInsnNode node = (IincInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + " " + node.incr + "\n";
});
}
static interface InstructionInterface {
String apply(AbstractInsnNode n, HashMap<Label, Integer> labels, HugeStrings hs);
}
public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels, HugeStrings hs) {
InstructionInterface ii = iiMap.get(n.getClass());
if (ii != null)
return ii.apply(n, labels, hs);
else
}
case MULTIANEWARRAY_INSN: {
MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + node.dims + "\n";
}
case FRAME: {
FrameNode node = (FrameNode) n;
String out = "";
switch (node.type) {
case Opcodes.F_SAME:
out = "SAME";
break;
case Opcodes.F_SAME1:
out = "SAME1 " + getFrameArray(hs, node.stack, labels);
break;
case Opcodes.F_CHOP:
out = "CHOP " + Integer.toString(node.local.size());
break;
case Opcodes.F_APPEND:
out = "APPEND " + getFrameArray(hs, node.local, labels);
break;
case Opcodes.F_FULL:
out = "FULL " + getFrameArray(hs, node.local, labels) + " " + getFrameArray(hs, node.stack, labels);
break;
}
return "\t\t// frame " + out + "\n";
}
case LINE: {
LineNumberNode node = (LineNumberNode) n;
return "\t\t// line " + node.line + " " + labels.get(node.start.getLabel()) + "\n";
}
default: {
return "\t\tNOT HANDLED! " + OpcodesReverse.reverseOpcode(n.getOpcode()) + " "
+ n.getClass().getSimpleName() + "\n";
}
}
}
private static String getFrameArray(HugeStrings hs, List<Object> list, HashMap<Label, Integer> labels) {
ArrayList<String> arr = new ArrayList<>();
if (list != null) {
for (Object o : list) {
if (o == null) {
arr.add(null);
} else {
if (o instanceof String) {
arr.add("\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"");
} else if (o instanceof LabelNode) {
arr.add("(label) " + labels.get(((LabelNode) o).getLabel()));
} else if (o instanceof Integer) {
String frameType = ClassUtil.getFrameTypeByID(((Integer) o).intValue());
if (frameType != null)
arr.add(frameType);
else
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
} else {
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
}
}
}
return arr.toString();
}
return "null";
}
}
-60
View File
@@ -1,60 +0,0 @@
package me.ByteEdit.edit;
import java.awt.EventQueue;
import java.awt.Toolkit;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
public class OptionBox extends JFrame {
private JPanel contentPane;
public JCheckBox chckbxComputeFrames;
public JCheckBox chckbxComputeMax;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
OptionBox frame = new OptionBox();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the frame.
*/
public OptionBox() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
setResizable(false);
setTitle("Options");
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 150),
(int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 100), 300, 200);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
chckbxComputeFrames = new JCheckBox("Compute Frames");
chckbxComputeFrames.setBounds(6, 7, 138, 23);
contentPane.add(chckbxComputeFrames);
chckbxComputeMax = new JCheckBox("Compute Max");
chckbxComputeMax.setBounds(6, 33, 138, 23);
contentPane.add(chckbxComputeMax);
}
}
-151
View File
@@ -1,151 +0,0 @@
package me.ByteEdit.edit;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.regex.Pattern;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import me.ByteEdit.main.Main;
public class SearchBox extends JFrame {
public JPanel contentPane;
public JTextField txtFind;
public JTextField txtReplace;
public JCheckBox chckbxCaseSensitive;
/**
* Create the frame.
*/
public SearchBox() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
setResizable(false);
setTitle("Find/Replace");
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 128),
(int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 181), 256, 262);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblFind = new JLabel("Find:");
lblFind.setBounds(10, 25, 71, 14);
contentPane.add(lblFind);
txtFind = new JTextField();
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
txtFind.registerKeyboardAction(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
find();
}
}, enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtFind.setBounds(91, 22, 149, 20);
contentPane.add(txtFind);
txtFind.setColumns(10);
JLabel lblReplaceWith = new JLabel("Replace with:");
lblReplaceWith.setBounds(10, 53, 71, 14);
contentPane.add(lblReplaceWith);
txtReplace = new JTextField();
txtReplace.setColumns(10);
txtReplace.setBounds(91, 50, 149, 20);
contentPane.add(txtReplace);
chckbxCaseSensitive = new JCheckBox("Case sensitive");
chckbxCaseSensitive.setBounds(10, 103, 230, 23);
contentPane.add(chckbxCaseSensitive);
JButton btnFind = new JButton("Find");
btnFind.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
btnFind.setBounds(10, 150, 110, 23);
contentPane.add(btnFind);
JButton btnReplacefind = new JButton("Replace/Find");
btnReplacefind.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
replaceFind();
}
});
btnReplacefind.setBounds(130, 150, 110, 23);
contentPane.add(btnReplacefind);
JButton btnReplace = new JButton("Replace");
btnReplace.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
replace();
}
});
btnReplace.setBounds(10, 184, 110, 23);
contentPane.add(btnReplace);
JButton btnReplaceAll = new JButton("Replace All");
btnReplaceAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
replaceAll();
}
});
btnReplaceAll.setBounds(130, 184, 110, 23);
contentPane.add(btnReplaceAll);
}
public void find() {
int startPos = Main.txtByteEditView.getCaretPosition();
String toFind = txtFind.getText();
String txt = chckbxCaseSensitive.isSelected() ? Main.txtByteEditView.getText().substring(startPos)
: Main.txtByteEditView.getText().substring(startPos).toLowerCase();
int index = txt.indexOf(chckbxCaseSensitive.isSelected() ? toFind : toFind.toLowerCase());
if (index == -1) {
txt = chckbxCaseSensitive.isSelected() ? Main.txtByteEditView.getText() : Main.txtByteEditView.getText().toLowerCase();
index = txt.indexOf(chckbxCaseSensitive.isSelected() ? toFind : toFind.toLowerCase());
startPos = 0;
if (index == -1) {
return;
}
}
Main.txtByteEditView.select(startPos + index, startPos + index + toFind.length());
}
public void replaceFind() {
replace();
find();
}
public void replace() {
String txt = Main.txtByteEditView.getText();
int startPos = Main.txtByteEditView.getSelectionStart();
Main.txtByteEditView.replaceSelection(txtReplace.getText());
Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length());
}
public void replaceAll() {
String toFind = txtFind.getText();
String toReplace = txtReplace.getText();
String txt = Main.txtByteEditView.getText();
int prev = Main.txtByteEditView.getCaretPosition();
if (chckbxCaseSensitive.isSelected()) {
Main.txtByteEditView.replaceRange(txt.replace(toFind, toReplace), 0, txt.length());
} else {
Main.txtByteEditView.replaceRange(txt.replaceAll("(?i)" + Pattern.quote(toFind), toReplace), 0, txt.length());
}
Main.txtByteEditView.setCaretPosition(prev);
}
}
@@ -25,7 +25,7 @@ public class StackBasedCalculator implements Opcodes, Runnable {
HashMap<LabelNode, LabelNode> labelMap = new HashMap<>();
for (AbstractInsnNode node = insns.getFirst(); node != null; node = node.getNext()) {
if (node.getType() == 8) {
if (node.getType() == AbstractInsnNode.LABEL) {
labelMap.put((LabelNode) node, new LabelNode());
}
}
@@ -675,14 +675,10 @@ public class StackBasedCalculator implements Opcodes, Runnable {
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_5));
else if (val == -1)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_M1));
else
insns.insertBefore(insns.get(idx), new LdcInsnNode(val));
} else if (n instanceof Long) {
long val = n.longValue();
if (val == 0l)
insns.insertBefore(insns.get(idx), new InsnNode(LCONST_0));
else if (val == 1l)
insns.insertBefore(insns.get(idx), new InsnNode(LCONST_1));
else if (val == (byte) val)
insns.insertBefore(insns.get(idx), new IntInsnNode(BIPUSH, (byte) val));
else if (val == (short) val)
insns.insertBefore(insns.get(idx), new IntInsnNode(SIPUSH, (short) val));
else
insns.insertBefore(insns.get(idx), new LdcInsnNode(val));
} else {
-119
View File
@@ -1,119 +0,0 @@
package me.ByteEdit.edit;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import me.ByteEdit.main.Main;
public class TypeOpenBox extends JFrame {
public JPanel contentPane;
public JTextField txtSearch;
/**
* Create the frame.
*/
public TypeOpenBox() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
setResizable(false);
setTitle("Open Type");
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setBounds((int) (Toolkit.getDefaultToolkit().getScreenSize().getWidth() / 2 - 200),
(int) (Toolkit.getDefaultToolkit().getScreenSize().getHeight() / 2 - 140), 400, 280);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
DefaultListModel<String> model = new DefaultListModel<>();
JList<String> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
String val = list.getSelectedValue();
if (val != null && !val.equals(Main.currentNodeName)) {
Main.selectFile(val);
}
}
});
txtSearch = new JTextField();
txtSearch.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (model.size() == 1) {
list.setSelectedIndex(0);
}
}
});
txtSearch.getDocument().addDocumentListener(new DocumentListener() {
public void changedUpdate(DocumentEvent e) {
updateList();
}
public void removeUpdate(DocumentEvent e) {
updateList();
}
public void insertUpdate(DocumentEvent e) {
updateList();
}
public void updateList() {
model.clear();
if (txtSearch.getText().isEmpty()) {
return;
}
String search = txtSearch.getText().toLowerCase();
if (search.contains("/")) {
for (String s : Main.classNodes.keySet()) {
String className = s.substring(0, s.length() - 6);
if (!className.contains("$") && className.toLowerCase().startsWith(search)) {
model.addElement(s);
}
}
} else {
for (String s : Main.classNodes.keySet()) {
String[] split = s.split("/");
String className = split[split.length - 1];
className = className.substring(0, className.length() - 6);
if (!className.contains("$") && className.toLowerCase().startsWith(search)) {
model.addElement(s);
}
}
}
}
});
txtSearch.setBounds(10, 11, 374, 20);
contentPane.add(txtSearch);
txtSearch.setColumns(10);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 46, 374, 194);
contentPane.add(scrollPane);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
scrollPane.setViewportView(list);
}
}
+198
View File
@@ -0,0 +1,198 @@
package me.ByteEdit.main;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javax.swing.tree.DefaultTreeModel;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
public class ArchiveTreeModel extends DefaultTreeModel {
public final ArrayList<String> paths = new ArrayList<>();
private final HashMap<String, ClassNode> classNodes;
private final HashMap<String, byte[]> otherFiles;
private final ZipFile jar;
public boolean newCreated = false;
public ArchiveTreeModel(HashMap<String, ClassNode> classNodes, HashMap<String, byte[]> otherFiles) {
super(new ByteEditTreeNode("New"));
this.newCreated = true;
this.jar = null;
this.classNodes = classNodes;
this.otherFiles = otherFiles;
}
public ArchiveTreeModel(ZipFile jar, HashMap<String, ClassNode> classNodes, HashMap<String, byte[]> otherFiles) {
super(new ByteEditTreeNode(jar.getName().split(File.separator.equals("\\") ? "\\\\"
: File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length
- 1]));
this.newCreated = false;
this.jar = jar;
this.classNodes = classNodes;
this.otherFiles = otherFiles;
initialize();
}
private void initialize() {
try {
classNodes.clear();
otherFiles.clear();
Enumeration<? extends ZipEntry> enumeration = jar.entries();
while (enumeration.hasMoreElements()) {
ZipEntry next = enumeration.nextElement();
byte[] data = toByteArray(jar.getInputStream(next));
if (next.getSize() != 0 && !next.getName().startsWith("META-INF")
&& (next.getName().endsWith(".class") || next.getName().endsWith(".class/"))) {
try {
ClassReader reader = new ClassReader(data);
ClassNode node = new ClassNode();
reader.accept(node, 0);
classNodes.put(node.name, node);
if ((next.getName().contains("/")
? (!Main.patternSlash
.split(next.getName())[Main.patternSlash.split(next.getName()).length - 1]
.contains("$"))
: (!next.getName().contains("$")))
|| (next.getName().startsWith("$") || next.getName().contains("$$")
|| next.getName().endsWith("$"))) {
paths.add(node.name + ".class");
}
} catch (Exception e) {
otherFiles.put(next.getName(), data);
}
} else {
otherFiles.put(next.getName(), data);
}
}
jar.close();
refresh();
} catch (Throwable e) {
classNodes.clear();
otherFiles.clear();
e.printStackTrace();
Main.showError(e);
}
}
private static int getSlashCount(String s) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '/')
count++;
}
return count;
}
public void refresh() {
setRoot(new ByteEditTreeNode(((ByteEditTreeNode) getRoot()).toString()));
Collections.sort(paths, String.CASE_INSENSITIVE_ORDER);
Collections.sort(paths, new Comparator<String>() {
public int compare(String s1, String s2) {
return getSlashCount(s2) - getSlashCount(s1);
}
});
// Windows-like sorting (like file explorer)
List<String> tmp = new ArrayList<>(paths);
List<String> folders = tmp.stream().filter(s -> {
int c = countSlashes(s, 50);
return c > 0 && c < 50;
}).map(s -> {
int idx = s.lastIndexOf('/');
if (idx == s.length() - 1)
idx = s.lastIndexOf('/', idx - 1);
if (idx == -1)
return s;
return s.substring(0, idx);
}).distinct().sorted(new Comparator<String>() {
@Override
public int compare(String s, String s2) {
// From String#compareToIgnoreCase(String)
int n = s.length();
int n2 = s2.length();
int n3 = Math.min(n, n2);
for (int i = 0; i < n3; ++i) {
char c;
char c2 = s.charAt(i);
if (c2 == (c = s2.charAt(i)) || (c2 = Character.toUpperCase(c2)) == (c = Character.toUpperCase(c))
|| (c2 = Character.toLowerCase(c2)) == (c = Character.toLowerCase(c)))
continue;
return c2 - c;
}
// Other way around
return n2 - n;
}
}).collect(Collectors.toList());
tmp.removeAll(folders);
tmp.addAll(0, folders);
for (String s : tmp) {
String[] elements = Main.patternSlash.split(s, 50);
if (elements.length >= 50) {
((ByteEditTreeNode) getRoot()).add(new ByteEditTreeNode(s, s));
} else {
ByteEditTreeNode currentNode = (ByteEditTreeNode) getRoot();
for (int i = 0; i < elements.length; i++) {
String token = elements[i];
ByteEditTreeNode nextNode = findNode(currentNode, token);
if (nextNode == null) {
nextNode = new ByteEditTreeNode(token, s);
currentNode.add(nextNode);
}
currentNode = nextNode;
}
}
}
}
private static ByteEditTreeNode findNode(ByteEditTreeNode parent, String name) {
Enumeration<?> e = parent.children();
while (e.hasMoreElements()) {
ByteEditTreeNode element = (ByteEditTreeNode) e.nextElement();
if (element.getUserObject().equals(name)) {
return element;
}
}
return null;
}
private static int countSlashes(String s, int max) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '/') {
count++;
if (count == max)
break;
}
}
return count;
}
private static byte[] toByteArray(final InputStream input) throws IOException {
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
return output.toByteArray();
}
}
}
@@ -0,0 +1,34 @@
package me.ByteEdit.main;
import javax.swing.JTree;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
public class FurtherExpandingTreeExpansionListener implements TreeExpansionListener {
private final JTree tree;
public FurtherExpandingTreeExpansionListener(JTree tree) {
this.tree = tree;
}
public void treeExpanded(TreeExpansionEvent event) {
TreePath treePath = event.getPath();
Object expandedTreePathObject = treePath.getLastPathComponent();
if (!(expandedTreePathObject instanceof TreeNode))
return;
TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject;
if (expandedTreeNode.getChildCount() == 1) {
TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0);
if (descendantTreeNode.isLeaf())
return;
TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode);
tree.expandPath(nextTreePath);
}
}
public void treeCollapsed(TreeExpansionEvent event) {
}
}
@@ -0,0 +1,96 @@
package me.ByteEdit.main;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.ToolTipSupplier;
import me.ByteEdit.edit.Assembler;
public class HugeStringsToolTips implements ToolTipSupplier {
private final RSyntaxTextArea txtByteEditView;
public HugeStringsToolTips(RSyntaxTextArea txtByteEditView) {
this.txtByteEditView = txtByteEditView;
}
@Override
public String getToolTipText(RTextArea textArea, MouseEvent e) {
int idx = textArea.viewToModel(e.getPoint());
if (idx < 0)
return null;
int subIdx = Math.max(idx - 15, 0);
int subLen = Math.min(30, textArea.getDocument().getLength() - subIdx);
if (subLen <= 0)
return null;
try {
String cut = textArea.getText(subIdx, subLen);
int hashIdx = -1;
for (int off = idx - subIdx; off >= 0; off--) {
if (cut.charAt(off) == '#') {
hashIdx = off;
break;
}
}
if (hashIdx < 0) {
for (int off = idx - subIdx; off < cut.length(); off++) {
if (cut.charAt(off) == '#') {
hashIdx = off;
break;
}
}
}
if (hashIdx < 0)
return null;
if (hashIdx >= 5 && cut.substring(hashIdx - 5, hashIdx).equals("ldc \"")) // ldc "#..."
return null;
int numLen = 1;
for (int i = hashIdx + 1; i < cut.length(); i++) {
if (!(cut.charAt(i) - '0' >= 0 && cut.charAt(i) - '0' <= 9)) {
break;
}
numLen++;
}
if (numLen < 2 || hashIdx > idx - subIdx || idx - subIdx - numLen >= hashIdx)
return null;
String hashNum = cut.substring(hashIdx, hashIdx + numLen);
String txt = txtByteEditView.getText();
int len = 0;
for (String asm : txt.split("\\/\\/ #Annotations:\n")) {
len += 19; // "\\/\\/ #Annotations:\n".length()
if (asm.isEmpty())
continue;
if (idx >= len && idx < len + asm.length()) {
try (BufferedReader reader = new BufferedReader(new StringReader(asm))) {
String s;
while (!(s = reader.readLine()).startsWith("// #SourceFile: ")) {
}
while (!(s = reader.readLine()).equals("// #Fields")) {
if (s.startsWith("#")) {
String[] split = Assembler.SPACE.split(s, 2);
if (hashNum.equals(split[0].substring(0, split[0].length() - 1))) {
String toolTip = split[1];
if (toolTip.length() > 500)
toolTip = toolTip.substring(0, 500) + "...";
return toolTip;
}
}
}
} catch (IOException e1) {
}
break;
}
len += asm.length();
}
} catch (Exception e1) {
}
return null;
}
}
+161 -504
View File
@@ -1,5 +1,6 @@
package me.ByteEdit.main;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
@@ -15,33 +16,29 @@ import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
@@ -57,16 +54,10 @@ import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.fife.ui.autocomplete.AutoCompletion;
@@ -78,13 +69,9 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.folding.CurlyFoldParser;
import org.fife.ui.rsyntaxtextarea.folding.FoldParserManager;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.fife.ui.rtextarea.ToolTipSupplier;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.boxes.CompilationBox;
@@ -94,9 +81,9 @@ import me.ByteEdit.boxes.SearchBox;
import me.ByteEdit.boxes.ThemeBox;
import me.ByteEdit.boxes.TypeOpenBox;
import me.ByteEdit.boxes.UnicodeBox;
import me.ByteEdit.decompiler.EnumDecompiler;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.edit.Assembler;
import me.ByteEdit.edit.Disassembler;
import me.ByteEdit.edit.Disassembler.DisassembleTuple;
import me.ByteEdit.edit.HugeStrings;
import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.OpcodesReverse;
@@ -105,11 +92,18 @@ import me.ByteEdit.utils.UnicodeUtils;
public class Main extends JFrame {
public static Main INSTANCE;
public static final Pattern patternSlash = Pattern.compile("/");
private static final Pattern patternRenameableField = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*");
private static final Pattern patternRenameableMethod = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{");
private JPanel contentPane;
private volatile boolean isChangingFile;
public static File jarFile;
public static HashMap<String, byte[]> OTHER_FILES = new HashMap<>();
public static HashMap<String, ClassNode> classNodes = new HashMap<>();
public static final HashMap<String, ClassNode> classNodes = new HashMap<>();
public static final HashMap<String, byte[]> otherFiles = new HashMap<>();
public static String currentNodeName;
public static RSyntaxTextArea txtByteEditView;
public static GlobalSearchBox globalSearchBox;
@@ -123,7 +117,6 @@ public class Main extends JFrame {
public static RTextScrollPane scrollPane_ByteEdit;
public static File saveFolder;
private static final Pattern SLASH = Pattern.compile("/");
private JMenuBar menuBar;
private JMenu mnFile;
private JMenuItem mntmOpenJar;
@@ -153,29 +146,27 @@ public class Main extends JFrame {
public static final Object treeLock = new Object();
public static EnumDecompiler decompiler = EnumDecompiler.BYTEEDIT;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Main frame = new Main();
frame.setVisible(true);
ThemeManager.applyRSTATheme();
} catch (Exception e) {
e.printStackTrace();
}
EventQueue.invokeLater(() -> {
try {
Main frame = new Main();
frame.setVisible(true);
ThemeManager.applyRSTATheme();
} catch (Exception e) {
e.printStackTrace();
}
});
}
/**
* Create a simple provider that adds some Java-related completions.
*/
private CompletionProvider createCompletionProvider() {
private static CompletionProvider createCompletionProvider() {
DefaultCompletionProvider provider = new DefaultCompletionProvider();
for (int i = 0; i < 200; i++) {
String s = OpcodesReverse.reverseOpcode(i);
@@ -231,7 +222,17 @@ public class Main extends JFrame {
"// #Max: l:0 s:0\n\t// #TryCatch:\n\t// #LocalVars:\n\tpublic static method ()V {\n\t\treturn\n\t}",
null, "<html><b><u>method</u></b><br>Creates an example method</html>"));
provider.addCompletion(new ShorthandCompletion(provider, "frame", "frame FULL l:[] s:[]", null,
"<html><b><u>frame</u></b><br>Creates a stack map frame</html>"));
"<html><b><u>frame</u></b><br>Creates a stack map frame<br><br><b>Possible frames:</b><br><ul><li><code>SAME: same locals, empty stack</code></li><li><code>SAME1: same locals, stack has 1 entry</code></li><li><code>CHOP: same locals - k, empty stack</code></li><li><code>APPEND: same locals + k, empty stack</code></li><li><code>FULL: define locals, define stack</code></li></ul></html>"));
provider.addCompletion(new BasicCompletion(provider, "SAME", null,
"<html><b><u>Frames:</u></b><br><code>SAME</code><br><br><u>Locals:</u> same as previous<br><u>Stack:</u> empty</html>"));
provider.addCompletion(new BasicCompletion(provider, "SAME1", null,
"<html><b><u>Frames:</u></b><br><code>SAME1 [(type)]</code><br><br><u>Locals:</u> same as previous<br><u>Stack:</u> 1 entry</html>"));
provider.addCompletion(new BasicCompletion(provider, "CHOP", null,
"<html><b><u>Frames:</u></b><br><code>CHOP (int:k)</code><br><br><u>Locals:</u> removes the top <i>k</i> elements<br><u>Stack:</u> empty</html>"));
provider.addCompletion(new BasicCompletion(provider, "APPEND", null,
"<html><b><u>Frames:</u></b><br><code>APPEND [(type), (type), ...]</code><br><br><u>Locals:</u> adds the elements to the top<br><u>Stack:</u> empty</html>"));
provider.addCompletion(new BasicCompletion(provider, "FULL", null,
"<html><b><u>Frames:</u></b><br><code>FULL [(type), (type), ...] [(type), (type), ...]</code><br><i>first locals, then stack</i><br><br><u>Locals:</u> sets the elements<br><u>Stack:</u> sets the elements</html>"));
return provider;
}
@@ -287,7 +288,6 @@ public class Main extends JFrame {
if (action == 0) {
final File file = fileChooser.getSelectedFile();
saveFolder = file.getParentFile();
boolean shouldSave = false;
if (file.exists()) {
synchronized (treeLock) {
jarFile = file;
@@ -301,8 +301,9 @@ public class Main extends JFrame {
new Thread(new Runnable() {
public void run() {
try {
ArchiveTreeModel model = new ArchiveTreeModel(new JarFile(jarFile));
EventQueue.invokeLater(new Runnable() {
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile), classNodes,
otherFiles);
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
tree.setModel(model);
@@ -319,7 +320,7 @@ public class Main extends JFrame {
}
}
});
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -417,10 +418,10 @@ public class Main extends JFrame {
group.add(rdbtnmntm1000);
group.add(rdbtnmntm5000);
group.add(rdbtnmntm10000);
mnDeobfuscation = new JMenu("Deobfuscation");
menuBar.add(mnDeobfuscation);
mntmNumbers = new JCheckBoxMenuItem("Numbers");
mnDeobfuscation.add(mntmNumbers);
@@ -440,9 +441,8 @@ public class Main extends JFrame {
new CurlyFoldParser(false, true));
tree = new JTree(new DefaultTreeModel(null));
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener());
tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener(tree));
tree.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 116 && jarFile != null) { // F5
@@ -456,8 +456,9 @@ public class Main extends JFrame {
new Thread(new Runnable() {
public void run() {
try {
ArchiveTreeModel model = new ArchiveTreeModel(new JarFile(jarFile));
EventQueue.invokeLater(new Runnable() {
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile), classNodes,
otherFiles);
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
tree.setModel(model);
@@ -466,7 +467,7 @@ public class Main extends JFrame {
}
}
});
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -482,19 +483,19 @@ public class Main extends JFrame {
String s = ((ByteEditTreeNode) tree.getSelectionPath().getLastPathComponent()).path;
if (JOptionPane.showConfirmDialog(INSTANCE, "Do you want to delete\n\"" + s + "\"?", "Delete",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
String substr = s.substring(0, s.length() - 6);
s = s.substring(0, s.length() - 6);
ArrayList<String> toRemove = new ArrayList<>();
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
if (split[0].equals(substr)) {
if (split[0].equals(s)) {
toRemove.add(key);
}
}
}
ArchiveTreeModel model = (ArchiveTreeModel) tree.getModel();
classNodes.remove(s);
model.paths.remove(s);
model.paths.remove(s + ".class");
for (String rem : toRemove) {
classNodes.remove(rem);
}
@@ -511,16 +512,8 @@ public class Main extends JFrame {
}
});
scrollPane.setViewportView(tree);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
synchronized (treeLock) {
if (!isChangingFile) {
selectFile(((ByteEditTreeNode) e.getPath().getLastPathComponent()).path);
}
}
}
});
tree.addTreeSelectionListener(e -> SingleThreadedExecutor
.submit(() -> selectFile(((ByteEditTreeNode) e.getPath().getLastPathComponent()).path)));
new DropTarget(tree, new DropTargetListener() {
@Override
@@ -536,7 +529,7 @@ public class Main extends JFrame {
for (int i = 0; i < flavors.length; i++) {
if (flavors[i].isFlavorJavaFileListType()) {
dtde.acceptDrop(dtde.getDropAction());
java.util.List<File> files = (java.util.List<File>) tr.getTransferData(flavors[i]);
List<File> files = (List<File>) tr.getTransferData(flavors[i]);
final File file = files.get(0);
if (file.getName().endsWith(".jar")) {
jarFile = file;
@@ -550,8 +543,9 @@ public class Main extends JFrame {
new Thread(new Runnable() {
public void run() {
try {
ArchiveTreeModel model = new ArchiveTreeModel(new JarFile(jarFile));
EventQueue.invokeLater(new Runnable() {
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile),
classNodes, otherFiles);
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
tree.setModel(model);
@@ -568,7 +562,7 @@ public class Main extends JFrame {
}
}
});
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -607,108 +601,12 @@ public class Main extends JFrame {
txtByteEditView = new RSyntaxTextArea();
ac.install(txtByteEditView);
txtByteEditView.setCodeFoldingEnabled(true);
// ToolTips for HugeStrings
txtByteEditView.setToolTipSupplier(new ToolTipSupplier() {
@Override
public String getToolTipText(RTextArea textArea, MouseEvent e) {
int idx = textArea.viewToModel(e.getPoint());
if (idx < 0)
return null;
int subIdx = Math.max(idx - 15, 0);
int subLen = Math.min(30, textArea.getDocument().getLength() - subIdx);
if (subLen <= 0)
return null;
try {
String cut = textArea.getText(subIdx, subLen);
int hashIdx = -1;
for (int off = idx - subIdx; off >= 0; off--) {
if (cut.charAt(off) == '#') {
hashIdx = off;
break;
}
}
if (hashIdx < 0) {
for (int off = idx - subIdx; off < cut.length(); off++) {
if (cut.charAt(off) == '#') {
hashIdx = off;
break;
}
}
}
if (hashIdx < 0)
return null;
if (hashIdx >= 5 && cut.substring(hashIdx - 5, hashIdx).equals("ldc \"")) // ldc "#..."
return null;
int numLen = 1;
for (int i = hashIdx + 1; i < cut.length(); i++) {
if (!(cut.charAt(i) - '0' >= 0 && cut.charAt(i) - '0' <= 9)) {
break;
}
numLen++;
}
if (numLen < 2 || hashIdx > idx - subIdx || idx - subIdx - numLen >= hashIdx)
return null;
String hashNum = cut.substring(hashIdx, hashIdx + numLen);
String txt = txtByteEditView.getText();
int len = 0;
for (String asm : txt.split("\\/\\/ #Annotations:\n")) {
len += 19; // "\\/\\/ #Annotations:\n".length()
if (asm.isEmpty())
continue;
if (idx >= len && idx < len + asm.length()) {
try (BufferedReader reader = new BufferedReader(new StringReader(asm))) {
String s;
while (!(s = reader.readLine()).startsWith("// #SourceFile: ")) {
}
while (!(s = reader.readLine()).equals("// #Fields")) {
if (s.startsWith("#")) {
String[] split = Assembler.SPACE.split(s, 2);
if (hashNum.equals(split[0].substring(0, split[0].length() - 1))) {
String toolTip = split[1];
if (toolTip.length() > 500)
toolTip = toolTip.substring(0, 500) + "...";
return toolTip;
}
}
}
} catch (IOException e1) {
}
break;
} else {
len += asm.length();
}
}
} catch (BadLocationException e1) {
}
return null;
}
});
txtByteEditView.setToolTipSupplier(new HugeStringsToolTips(txtByteEditView));
txtByteEditView.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 116 && currentNodeName != null) { // F5
try {
ClassNode classNode = classNodes.get(currentNodeName);
int prev = txtByteEditView.getCaretPosition();
String dis = Disassembler.disassemble(classNode);
String substr = currentNodeName.substring(0, currentNodeName.length() - 6);
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
if (split[0].equals(substr)) {
dis += "\n" + Disassembler.disassemble(classNodes.get(key));
}
}
}
txtByteEditView.setText(dis);
if (dis.length() > prev)
txtByteEditView.setCaretPosition(prev);
} catch (Exception e2) {
e2.printStackTrace();
}
SingleThreadedExecutor.submit(Main::decompileCurrentNode);
}
}
});
@@ -745,122 +643,74 @@ public class Main extends JFrame {
// specific
tree.registerKeyboardAction(e -> save(), ctrlS, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> saveCurrentClassNode(), ctrlS, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> {
try {
goToSelected();
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}, ctrlG, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> {
try {
renameSelected();
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}, ctrlR, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> renameSelected(), ctrlR, JComponent.WHEN_FOCUSED);
txtByteEditView.setEditable(true);
txtByteEditView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA_DISASSEMBLE);
txtByteEditView.setCodeFoldingEnabled(true);
scrollPane_ByteEdit = new RTextScrollPane();
ThemeManager.registerTextArea(txtByteEditView);
splitPane.setRightComponent(scrollPane_ByteEdit);
JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPane_ByteEdit, BorderLayout.CENTER);
final JComboBox<String> decompilerMode = new JComboBox<>();
DefaultComboBoxModel model = new DefaultComboBoxModel();
Stream.of(EnumDecompiler.values()).forEach(model::addElement);
decompilerMode.setModel(model);
decompilerMode.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
EnumDecompiler selected = (EnumDecompiler) e.getItem();
SingleThreadedExecutor.submit(() -> {
decompiler = selected;
decompileCurrentNode();
try {
EventQueue.invokeAndWait(() -> {
synchronized (treeLock) {
txtByteEditView.setCaretPosition(0);
try {
txtByteEditView.setSyntaxEditingStyle(decompiler.getSyntaxStyle());
} catch (Throwable t) {
}
txtByteEditView.setEditable(decompiler.isEditable());
}
});
} catch (Exception e1) {
e1.printStackTrace();
}
});
}
}
});
panel.add(decompilerMode, BorderLayout.NORTH);
splitPane.setRightComponent(panel);
scrollPane_ByteEdit.setViewportView(txtByteEditView);
scrollPane_ByteEdit.setLineNumbersEnabled(true);
scrollPane_ByteEdit.setFoldIndicatorEnabled(true);
}
private final Pattern jumpableInstructionPattern = Pattern.compile("^\t\t(?!//|\t+).+ .+");
public void goToSelected() throws BadLocationException {
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
if (!jumpableInstructionPattern.matcher(line).matches()) {
return;
}
line = line.substring(2);
if (line.startsWith("invoke")) {
String[] split = line.split(" ");
String desc = UnicodeUtils.unescape(null, split[1], true);
String target = split[2];
int index = target.lastIndexOf("/");
String className = UnicodeUtils.unescape(null, target.substring(0, index), true);
String methodName = UnicodeUtils.unescape(null, target.substring(index + 1), true);
ClassNode classNode = classNodes.get(getFullName(className));
if (classNode == null) {
return;
}
for (MethodNode mn : classNode.methods) {
if (mn.name.equals(methodName) && mn.desc.equals(desc)) {
while (classNode.outerClass != null) {
classNode = classNodes.get(getFullName(classNode.outerClass));
}
int lineFound = selectFileWithSearch(getFullName(classNode.name), mn);
if (lineFound != -1) {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
}
break;
}
}
} else if (line.startsWith("new ")) {
String[] split = line.split(" ");
String className = UnicodeUtils.unescape(null, split[1], true);
ClassNode classNode = classNodes.get(getFullName(className));
if (classNode == null) {
return;
}
selectFile(getFullName(className));
} else if (line.startsWith("getstatic ") || line.startsWith("putstatic ") || line.startsWith("getfield ")
|| line.startsWith("putfield ")) {
String[] split = line.split(" ");
String desc = UnicodeUtils.unescape(null, split[1], true);
String target = split[2];
int index = target.lastIndexOf("/");
String className = UnicodeUtils.unescape(null, target.substring(0, index), true);
String fieldName = UnicodeUtils.unescape(null, target.substring(index + 1), true);
ClassNode classNode = classNodes.get(getFullName(className));
if (classNode == null) {
return;
}
for (FieldNode fn : classNode.fields) {
if (fn.name.equals(fieldName) && fn.desc.equals(desc)) {
while (classNode.outerClass != null) {
classNode = classNodes.get(getFullName(classNode.outerClass));
}
int lineFound = selectFileWithSearch(getFullName(classNode.name), fn);
if (lineFound != -1) {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
}
break;
}
}
}
}
private final Pattern renameableFieldPattern = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*");
private final Pattern renameableMethodPattern = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{");
public void renameSelected() throws BadLocationException {
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
Matcher m = renameableFieldPattern.matcher(line);
if (m.find()) {
setRenameInfo(m, lineEnd);
} else {
m = renameableMethodPattern.matcher(line);
public void renameSelected() {
try {
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
Matcher m = patternRenameableField.matcher(line);
if (m.find()) {
if (ClassUtil.isObjectClassMethod(m.group(1), m.group(2), false)) {
JOptionPane.showMessageDialog(null, "This method can not be renamed!", "Error!",
JOptionPane.ERROR_MESSAGE);
return;
}
setRenameInfo(m, lineEnd);
} else {
m = patternRenameableMethod.matcher(line);
if (m.find()) {
if (ClassUtil.isObjectClassMethod(m.group(1), m.group(2), false)) {
JOptionPane.showMessageDialog(null, "This method can not be renamed!", "Error!",
JOptionPane.ERROR_MESSAGE);
return;
}
setRenameInfo(m, lineEnd);
}
}
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
@@ -885,9 +735,9 @@ public class Main extends JFrame {
String[] split = s.split(" ");
String className = UnicodeUtils.unescape(null, split[split.length - 1], true);
renameBox.className = className;
String name = UnicodeUtils.unescape(null, m.pattern() == renameableFieldPattern ? m.group(2) : m.group(1),
String name = UnicodeUtils.unescape(null, m.pattern() == patternRenameableField ? m.group(2) : m.group(1),
true);
String desc = UnicodeUtils.unescape(null, m.pattern() == renameableFieldPattern ? m.group(1) : m.group(2),
String desc = UnicodeUtils.unescape(null, m.pattern() == patternRenameableField ? m.group(1) : m.group(2),
true);
renameBox.name = name;
renameBox.desc = desc;
@@ -898,7 +748,7 @@ public class Main extends JFrame {
}
}
private void saveCurrentClassNode() {
private static void saveCurrentClassNode() {
String txt = txtByteEditView.getText();
for (String s : txt.split("\\/\\/ #Annotations:\n")) {
if (s.isEmpty())
@@ -907,15 +757,15 @@ public class Main extends JFrame {
if (node == null) {
continue;
}
if (classNodes.replace(getFullName(node.name), node) == null) {
classNodes.put(getFullName(node.name), node);
if (classNodes.replace(node.name, node) == null) {
classNodes.put(node.name, node);
ArchiveTreeModel model;
if (tree.getModel().getClass().equals(DefaultTreeModel.class)) {
tree.setModel(model = new ArchiveTreeModel());
tree.setModel(model = new ArchiveTreeModel(classNodes, otherFiles));
} else {
model = (ArchiveTreeModel) tree.getModel();
}
String name = getFullName(node.name);
String name = node.name + ".class";
if (name.contains("/") ? (!name.split("/")[name.split("/").length - 1].contains("$"))
: (!name.contains("$"))) {
model.paths.add(name);
@@ -928,41 +778,34 @@ public class Main extends JFrame {
}
}
public static int selectFileWithSearch(String s, Object nodeToFind) {
if (s != null && s.endsWith(".class")) {
currentNodeName = s;
int lineFound = -1;
ClassNode classNode = classNodes.get(s);
if (classNode == null)
return -1;
String dis = "";
DisassembleTuple tuple = Disassembler.disassemble(classNode, nodeToFind);
if (tuple.getLine() != -1) {
lineFound = dis.split("\\n").length + tuple.getLine() - 1;
}
dis += tuple.getDisassembly();
String substr = s.substring(0, s.length() - 6);
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
if (split[0].equals(substr)) {
tuple = Disassembler.disassemble(classNodes.get(key), nodeToFind);
if (tuple.getLine() != -1) {
lineFound = dis.split("\\n").length + tuple.getLine() + 1;
}
dis += "\n" + tuple.getDisassembly();
}
public static void decompileCurrentNode() {
if (currentNodeName == null)
return;
try {
EnumDecompiler decompiler = Main.decompiler;
String current = currentNodeName;
ClassNode classNode = classNodes.get(current);
int prev = txtByteEditView.getCaretPosition();
String dis = decompiler.getDecompiler().decompile(classNode, Main.classNodes);
EventQueue.invokeAndWait(() -> {
synchronized (treeLock) {
txtByteEditView.setText(dis);
if (dis.length() > prev)
txtByteEditView.setCaretPosition(prev);
}
}
txtByteEditView.setText(dis);
txtByteEditView.setCaretPosition(0);
return lineFound;
});
} catch (Exception e2) {
e2.printStackTrace();
}
return -1;
}
public static int selectFile(String s) {
return selectFileWithSearch(s, null);
public static void selectFile(String s) {
if (s != null && s.endsWith(".class")) {
s = s.substring(0, s.length() - 6);
currentNodeName = s;
decompileCurrentNode();
txtByteEditView.setCaretPosition(0);
}
}
public void save() {
@@ -988,7 +831,6 @@ public class Main extends JFrame {
int dialogResult = JOptionPane.showConfirmDialog(null, "This file already exists! Overwrite it?",
"Warning", JOptionPane.YES_NO_OPTION);
if (dialogResult == JOptionPane.YES_OPTION) {
save(file, classNodes.values());
shouldSave = true;
}
} else {
@@ -1011,14 +853,16 @@ public class Main extends JFrame {
public void run() {
synchronized (treeLock) {
save(file, classNodes.values());
EventQueue.invokeLater(new Runnable() {
public void run() {
synchronized (treeLock) {
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
isChangingFile = false;
Main.this.setTitle("ByteEdit");
}
}
});
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
@@ -1032,7 +876,7 @@ public class Main extends JFrame {
public void save(File jar, Collection<ClassNode> classes) {
try {
final JarOutputStream output = new JarOutputStream(new FileOutputStream(jar));
final ZipOutputStream output = new ZipOutputStream(new FileOutputStream(jar));
if (tree.getModel().getClass().equals(DefaultTreeModel.class)
|| ((ArchiveTreeModel) tree.getModel()).newCreated) {
int acc = ClassUtil.ACC_PUBLIC | ClassUtil.ACC_STATIC;
@@ -1051,13 +895,13 @@ public class Main extends JFrame {
if (name != null && !name.isEmpty()) {
String val = "Manifest-Version: 1.0\n" + "Class-Path: .\n" + "Main-Class: " + name.replace('/', '.')
+ "\n\n";
OTHER_FILES.put("META-INF/MANIFEST.MF", val.getBytes());
otherFiles.put("META-INF/MANIFEST.MF", val.getBytes());
}
if (tree.getModel() instanceof ArchiveTreeModel)
((ArchiveTreeModel) tree.getModel()).newCreated = false;
}
for (Entry<String, byte[]> entry : OTHER_FILES.entrySet()) {
JarEntry ent = new JarEntry(entry.getKey());
for (Entry<String, byte[]> entry : otherFiles.entrySet()) {
ZipEntry ent = new ZipEntry(entry.getKey());
output.putNextEntry(ent);
output.write(entry.getValue());
output.closeEntry();
@@ -1072,7 +916,7 @@ public class Main extends JFrame {
for (ClassNode node : classes) {
ClassWriter writer = new ClassWriter(computeFlags);
node.accept(writer);
output.putNextEntry(new JarEntry(getFullName(node.name)));
output.putNextEntry(new ZipEntry(node.name + ".class"));
output.write(writer.toByteArray());
output.closeEntry();
}
@@ -1098,191 +942,4 @@ public class Main extends JFrame {
public static void showError(String s) {
JOptionPane.showMessageDialog(INSTANCE, s, "Error!", JOptionPane.ERROR_MESSAGE);
}
public static String getFullName(String s) {
return s + ".class";
}
class ArchiveTreeModel extends DefaultTreeModel {
public ArrayList<String> paths = new ArrayList<>();
public boolean newCreated = false;
public ArchiveTreeModel() {
super(new ByteEditTreeNode("New"));
newCreated = true;
}
public ArchiveTreeModel(JarFile jar) {
super(new ByteEditTreeNode(jar.getName().split(File.separator.equals("\\") ? "\\\\"
: File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length
- 1]));
try {
classNodes.clear();
OTHER_FILES.clear();
Enumeration<JarEntry> enumeration = jar.entries();
while (enumeration.hasMoreElements()) {
JarEntry next = enumeration.nextElement();
byte[] data = toByteArray(jar.getInputStream(next));
if (next.getSize() != 0 && !next.getName().startsWith("META-INF")
&& (next.getName().endsWith(".class") || next.getName().endsWith(".class/"))) {
try {
ClassReader reader = new ClassReader(data);
ClassNode node = new ClassNode();
reader.accept(node, 0);
classNodes.put(getFullName(node.name), node);
if ((next.getName().contains("/")
? (!SLASH.split(next.getName())[SLASH.split(next.getName()).length - 1]
.contains("$"))
: (!next.getName().contains("$")))
|| (next.getName().startsWith("$") || next.getName().contains("$$")
|| next.getName().endsWith("$"))) {
paths.add(getFullName(node.name));
}
} catch (Exception e) {
OTHER_FILES.put(next.getName(), data);
}
} else {
OTHER_FILES.put(next.getName(), data);
}
}
jar.close();
refresh();
} catch (Throwable e) {
classNodes.clear();
OTHER_FILES.clear();
e.printStackTrace();
showError(e);
}
}
private int getSlashCount(String s) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '/')
count++;
}
return count;
}
public void refresh() {
setRoot(new ByteEditTreeNode(((ByteEditTreeNode) getRoot()).toString()));
Collections.sort(paths, String.CASE_INSENSITIVE_ORDER);
Collections.sort(paths, new Comparator<String>() {
public int compare(String s1, String s2) {
return getSlashCount(s2) - getSlashCount(s1);
}
});
// Windows-like sorting (like file explorer)
List<String> tmp = new ArrayList<>(paths);
List<String> folders = tmp.stream().filter(s -> {
int c = countSlashes(s, 50);
return c > 0 && c < 50;
}).map(s -> {
int idx = s.lastIndexOf('/');
if (idx == s.length() - 1)
idx = s.lastIndexOf('/', idx - 1);
if (idx == -1)
return s;
return s.substring(0, idx);
}).distinct().sorted(new Comparator<String>() {
@Override
public int compare(String s, String s2) {
// From String#compareToIgnoreCase(String)
int n = s.length();
int n2 = s2.length();
int n3 = Math.min(n, n2);
for (int i = 0; i < n3; ++i) {
char c;
char c2 = s.charAt(i);
if (c2 == (c = s2.charAt(i))
|| (c2 = Character.toUpperCase(c2)) == (c = Character.toUpperCase(c))
|| (c2 = Character.toLowerCase(c2)) == (c = Character.toLowerCase(c)))
continue;
return c2 - c;
}
// Other way around
return n2 - n;
}
}).collect(Collectors.toList());
tmp.removeAll(folders);
tmp.addAll(0, folders);
for (String s : tmp) {
String[] elements = SLASH.split(s, 50);
if (elements.length >= 50) {
((ByteEditTreeNode) getRoot()).add(new ByteEditTreeNode(s, s));
} else {
ByteEditTreeNode currentNode = (ByteEditTreeNode) getRoot();
for (int i = 0; i < elements.length; i++) {
String token = elements[i];
ByteEditTreeNode nextNode = findNode(currentNode, token);
if (nextNode == null) {
nextNode = new ByteEditTreeNode(token, s);
currentNode.add(nextNode);
}
currentNode = nextNode;
}
}
}
}
private ByteEditTreeNode findNode(ByteEditTreeNode parent, String name) {
Enumeration<?> e = parent.children();
while (e.hasMoreElements()) {
ByteEditTreeNode element = (ByteEditTreeNode) e.nextElement();
if (element.getUserObject().equals(name)) {
return element;
}
}
return null;
}
private int countSlashes(String s, int max) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '/') {
count++;
if (count == max)
break;
}
}
return count;
}
}
private static class FurtherExpandingTreeExpansionListener implements TreeExpansionListener {
public void treeExpanded(TreeExpansionEvent event) {
TreePath treePath = event.getPath();
Object expandedTreePathObject = treePath.getLastPathComponent();
if (!(expandedTreePathObject instanceof TreeNode))
return;
TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject;
if (expandedTreeNode.getChildCount() == 1) {
TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0);
if (descendantTreeNode.isLeaf())
return;
TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode);
tree.expandPath(nextTreePath);
}
}
public void treeCollapsed(TreeExpansionEvent event) {
}
}
public static byte[] toByteArray(final InputStream input) throws IOException {
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
return output.toByteArray();
}
}
}
+3 -6
View File
@@ -320,11 +320,9 @@ public class ClassUtil {
s += "enum ";
acc ^= ACC_ENUM;
}
if (acc == access) {
if (acc == access)
return s;
} else {
return "0x" + Integer.toHexString(access) + " ";
}
return "0x" + Integer.toHexString(access) + " ";
}
/**
@@ -544,8 +542,7 @@ public class ClassUtil {
DecompilationInterface di = diMap.get(o.getClass());
if (di != null)
return di.apply(o, desc, escapeSpaces, hs);
else
return o.toString();
return o.toString();
}
public static String getDecompiledValue(Object o, String desc, HugeStrings hs) {
+2 -4
View File
@@ -84,11 +84,9 @@ public class UnicodeUtils {
sb.append(c);
}
if (stage != 0) {
if (unicodeBuffer != 0) {
if (unicodeBuffer != 0)
throw new IllegalArgumentException("The unicode at the end is incomplete.");
} else {
throw new IllegalArgumentException("'\\' at the end is invalid.");
}
throw new IllegalArgumentException("'\\' at the end is invalid.");
}
return sb.toString();
}
+1 -1
View File
@@ -12,7 +12,7 @@
<baseFont size="12"/>
<!-- General editor colors. -->
<background color="ffffff"/>
<background color="c0c0c0"/>
<caret color="000000"/>
<selection bg="dddddd" fg="000000"/>
<currentLineHighlight color="dddddd" fade="false"/>
+13 -13
View File
@@ -12,10 +12,10 @@
<baseFont size="12"/>
<!-- General editor colors. -->
<background color="000000"/>
<background color="2f2f2f"/>
<caret color="eeeeee"/>
<selection bg="222222" fg="eeeeee"/>
<currentLineHighlight color="222222" fade="false"/>
<selection bg="555555" fg="eeeeee"/>
<currentLineHighlight color="111111" fade="false"/>
<marginLine fg="4f4b46"/>
<markAllHighlight color="947e76"/> <!-- TODO: Fix me -->
<markOccurrencesHighlight color="2b2b2b" border="false"/>
@@ -36,23 +36,23 @@
<!-- Syntax tokens. -->
<tokenStyles>
<style token="IDENTIFIER" fg="eeeeee"/>
<style token="RESERVED_WORD" fg="dd02d6" bold="true"/>
<style token="RESERVED_WORD_2" fg="dd02d6" bold="true"/>
<style token="RESERVED_WORD" fg="cc6c1d" bold="true"/>
<style token="RESERVED_WORD_2" fg="cc6c1d" bold="true"/>
<style token="ANNOTATION" fg="808080"/>
<style token="COMMENT_DOCUMENTATION" fg="279627"/>
<style token="COMMENT_EOL" fg="279627"/>
<style token="COMMENT_MULTILINE" fg="279627"/>
<style token="COMMENT_KEYWORD" fg="806040" bold="true"/>
<style token="COMMENT_MARKUP" fg="808060"/>
<style token="DATA_TYPE" fg="dd02d6" bold="true"/>
<style token="DATA_TYPE" fg="cc6c1d"/>
<style token="FUNCTION" fg="eeeeee"/>
<style token="LITERAL_BOOLEAN" fg="dd02d6" bold="true"/>
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="eeeeee"/>
<style token="LITERAL_NUMBER_FLOAT" fg="eeeeee"/>
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="eeeeee"/>
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="00aeff"/>
<style token="LITERAL_CHAR" fg="00aeff"/>
<style token="LITERAL_BACKQUOTE" fg="00aeff"/>
<style token="LITERAL_BOOLEAN" fg="cc6c1d"/>
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="6897bb"/>
<style token="LITERAL_NUMBER_FLOAT" fg="6897bb"/>
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="6897bb"/>
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="17c6a3"/>
<style token="LITERAL_CHAR" fg="17c6a3"/>
<style token="LITERAL_BACKQUOTE" fg="17c6a3"/>
<style token="MARKUP_TAG_DELIMITER" fg="008080"/>
<style token="MARKUP_TAG_NAME" fg="3f7f7f"/>
<style token="MARKUP_TAG_ATTRIBUTE" fg="7f007f"/>
+38 -46
View File
@@ -20,7 +20,7 @@ import javax.swing.text.Segment;
* @version 1.0
*/
public class DocumentReader extends Reader {
/**
* The stream's position in the document.
*/
@@ -37,12 +37,11 @@ public class DocumentReader extends Reader {
* Used for fast character access.
*/
private Segment segment;
/**
* Constructor.
*
* @param document
* The document we're 'reading'.
* @param document The document we're 'reading'.
*/
public DocumentReader(Document document) {
position = 0;
@@ -50,34 +49,34 @@ public class DocumentReader extends Reader {
this.document = document;
this.segment = new Segment();
}
/**
* This currently does nothing...
*/
@Override
public void close() {}
public void close() {
}
/**
* Marks the present position in the stream. Subsequent calls to
* <code>reset()</code> will reposition the stream to this point.
*
* @param readAheadLimit
* Ignored.
* @param readAheadLimit Ignored.
*/
@Override
public void mark(int readAheadLimit) {
mark = position;
}
/**
* Tells whether this reader supports the <code>mark</code> operation.
* This always returns <code>true</code> for <code>DocumentReader</code>.
* Tells whether this reader supports the <code>mark</code> operation. This
* always returns <code>true</code> for <code>DocumentReader</code>.
*/
@Override
public boolean markSupported() {
return true;
}
/**
* Reads the single character at the current position in the document.
*/
@@ -96,31 +95,27 @@ public class DocumentReader extends Reader {
return -1;
}
}
/**
* Read <code>array.length</code> characters from the beginning
* of the document into <code>array</code>.
* Read <code>array.length</code> characters from the beginning of the document
* into <code>array</code>.
*
* @param array
* The array to read characters into.
* @param array The array to read characters into.
* @return The number of characters read.
*/
@Override
public int read(char[] array) {
return read(array, 0, array.length);
}
/**
* Reads characters into a portion of an array.
*
* @param cbuf
* The destination buffer.
* @param off
* Offset at which to start storing characters.
* @param len
* Maximum number of characters to read.
* @return The number of characters read, or <code>-1</code> if the
* end of the stream (document) has been reached.
* @param cbuf The destination buffer.
* @param off Offset at which to start storing characters.
* @param len Maximum number of characters to read.
* @return The number of characters read, or <code>-1</code> if the end of the
* stream (document) has been reached.
*/
@Override
public int read(char[] cbuf, int off, int len) {
@@ -145,24 +140,23 @@ public class DocumentReader extends Reader {
return -1;
}
}
/**
* Tells whether this reader is ready to be read without
* blocking for input. <code>DocumentReader</code> will
* always return true.
* Tells whether this reader is ready to be read without blocking for input.
* <code>DocumentReader</code> will always return true.
*
* @return <code>true</code> if the next read operation will
* return without blocking.
* @return <code>true</code> if the next read operation will return without
* blocking.
*/
@Override
public boolean ready() {
return true;
}
/**
* Resets the stream. If the stream has been marked, then attempt to
* reposition it at the mark. If the stream has not been marked, then
* move it to the beginning of the document.
* Resets the stream. If the stream has been marked, then attempt to reposition
* it at the mark. If the stream has not been marked, then move it to the
* beginning of the document.
*/
@Override
public void reset() {
@@ -173,12 +167,11 @@ public class DocumentReader extends Reader {
mark = -1;
}
}
/**
* Skips characters. This will not 'skip' past the end of the document.
*
* @param n
* The number of characters to skip.
* @param n The number of characters to skip.
* @return The number of characters actually skipped.
*/
@Override
@@ -191,14 +184,13 @@ public class DocumentReader extends Reader {
position = document.getLength();
return document.getLength() - temp;
}
/**
* Move to the specified position in the document. If <code>pos</code>
* is greater than the document's length, the stream's position is moved
* to the end of the document.
* Move to the specified position in the document. If <code>pos</code> is
* greater than the document's length, the stream's position is moved to the end
* of the document.
*
* @param pos
* The position in the document to move to.
* @param pos The position in the document to move to.
*/
public void seek(long pos) {
position = Math.min(pos, document.getLength());
+47 -73
View File
@@ -28,12 +28,11 @@ import java.io.Writer;
* @version 0.7
*/
public class UnicodeWriter extends Writer {
/**
* If this system property evaluates to "<code>false</code>", ignoring
* case, files written out as UTF-8 will not have a BOM written for them.
* Otherwise (even if the property is not set), UTF-8 files will have a
* BOM written.
* If this system property evaluates to "<code>false</code>", ignoring case,
* files written out as UTF-8 will not have a BOM written for them. Otherwise
* (even if the property is not set), UTF-8 files will have a BOM written.
*/
public static final String PROPERTY_WRITE_UTF8_BOM = "UnicodeWriter.writeUtf8BOM";
/**
@@ -45,73 +44,62 @@ public class UnicodeWriter extends Writer {
private static final byte[] UTF16BE_BOM = new byte[] { (byte) 0xFE, (byte) 0xFF };
private static final byte[] UTF32LE_BOM = new byte[] { (byte) 0xFF, (byte) 0xFE, (byte) 0x00, (byte) 0x00 };
private static final byte[] UTF32BE_BOM = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0xFE, (byte) 0xFF };
/**
* This is a utility constructor since the vast majority of the time, this
* class will be used to write Unicode files.
* This is a utility constructor since the vast majority of the time, this class
* will be used to write Unicode files.
*
* @param fileName
* The file to which to write the Unicode output.
* @param encoding
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
* @param fileName The file to which to write the Unicode output.
* @param encoding The encoding to use.
* @throws IOException If an IO exception occurs.
*/
public UnicodeWriter(String fileName, String encoding) throws IOException {
this(new FileOutputStream(fileName), encoding);
}
/**
* This is a utility constructor since the vast majority of the time, this
* class will be used to write Unicode files.
* This is a utility constructor since the vast majority of the time, this class
* will be used to write Unicode files.
*
* @param file
* The file to which to write the Unicode output.
* @param encoding
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
* @param file The file to which to write the Unicode output.
* @param encoding The encoding to use.
* @throws IOException If an IO exception occurs.
*/
public UnicodeWriter(File file, String encoding) throws IOException {
this(new FileOutputStream(file), encoding);
}
/**
* Creates a new writer.
*
* @param out
* The output stream to write.
* @param encoding
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
* @param out The output stream to write.
* @param encoding The encoding to use.
* @throws IOException If an IO exception occurs.
*/
public UnicodeWriter(OutputStream out, String encoding) throws IOException {
init(out, encoding);
}
/**
* Closes this writer.
*
* @throws IOException
* If an IO exception occurs.
* @throws IOException If an IO exception occurs.
*/
@Override
public void close() throws IOException {
internalOut.close();
}
/**
* Flushes the stream.
*
* @throws IOException
* If an IO exception occurs.
* @throws IOException If an IO exception occurs.
*/
@Override
public void flush() throws IOException {
internalOut.flush();
}
/**
* Returns the encoding being used to write this output stream (i.e., the
* encoding of the file).
@@ -121,7 +109,7 @@ public class UnicodeWriter extends Writer {
public String getEncoding() {
return internalOut.getEncoding();
}
/**
* Returns whether UTF-8 files should have a BOM in them when written.
*
@@ -137,17 +125,14 @@ public class UnicodeWriter extends Writer {
}
return true;
}
/**
* Initializes the internal output stream and writes the BOM if the
* specified encoding is a Unicode encoding.
* Initializes the internal output stream and writes the BOM if the specified
* encoding is a Unicode encoding.
*
* @param out
* The output stream we are writing.
* @param encoding
* The encoding in which to write.
* @throws IOException
* If an I/O error occurs while writing a BOM.
* @param out The output stream we are writing.
* @param encoding The encoding in which to write.
* @throws IOException If an I/O error occurs while writing a BOM.
*/
private void init(OutputStream out, String encoding) throws IOException {
internalOut = new OutputStreamWriter(out, encoding);
@@ -169,60 +154,49 @@ public class UnicodeWriter extends Writer {
out.write(UTF32BE_BOM, 0, UTF32BE_BOM.length);
}
}
/**
* Sets whether UTF-8 files should have a BOM written in them.
*
* @param write
* Whether to write a BOM.
* @param write Whether to write a BOM.
* @see #getWriteUtf8BOM()
* @see UnicodeWriter
*/
public static void setWriteUtf8BOM(boolean write) {
System.setProperty(UnicodeWriter.PROPERTY_WRITE_UTF8_BOM, Boolean.toString(write));
}
/**
* Writes a portion of an array of characters.
*
* @param cbuf
* The buffer of characters.
* @param off
* The offset from which to start writing characters.
* @param len
* The number of characters to write.
* @throws IOException
* If an I/O error occurs.
* @param cbuf The buffer of characters.
* @param off The offset from which to start writing characters.
* @param len The number of characters to write.
* @throws IOException If an I/O error occurs.
*/
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
internalOut.write(cbuf, off, len);
}
/**
* Writes a single character.
*
* @param c
* An integer specifying the character to write.
* @throws IOException
* If an IO error occurs.
* @param c An integer specifying the character to write.
* @throws IOException If an IO error occurs.
*/
@Override
public void write(int c) throws IOException {
internalOut.write(c);
}
/**
* Writes a portion of a string.
*
* @param str
* The string from which to write.
* @param off
* The offset from which to start writing characters.
* @param len
* The number of characters to write.
* @throws IOException
* If an IO error occurs.
* @param str The string from which to write.
* @param off The offset from which to start writing characters.
* @param len The number of characters to write.
* @throws IOException If an IO error occurs.
*/
@Override
public void write(String str, int off, int len) throws IOException {
+65 -88
View File
@@ -30,7 +30,7 @@ import javax.swing.text.Utilities;
* @version 1.0
*/
public abstract class RPrintUtilities {
private static int currentDocLineNumber; // The line number in the document
// we are currently on.
private static int numDocLines; // The number of lines in the current
@@ -52,27 +52,21 @@ public abstract class RPrintUtilities {
* The metrics of the font currently being used to print.
*/
private static FontMetrics fm;
/**
* Returns the position closest to, but before, position
* <code>maxCharsPerLine</code> in
* <code>line</code> of one of the chars in <code>breakChars</code>, or
* simply returns
* <code>maxCharsPerLine-1</code> if none of the <code>breakChars</code>
* comes before
* that position. This position represents the logical line break for this
* <code>java.lang.String</code>
* if it is being printed in a monospaced font when lines can only be
* <code>maxCharsPerLine</code>
* characters long.
* <code>maxCharsPerLine</code> in <code>line</code> of one of the chars in
* <code>breakChars</code>, or simply returns <code>maxCharsPerLine-1</code> if
* none of the <code>breakChars</code> comes before that position. This position
* represents the logical line break for this <code>java.lang.String</code> if
* it is being printed in a monospaced font when lines can only be
* <code>maxCharsPerLine</code> characters long.
*
* @param line
* The text being printed.
* @param maxCharsPerLine
* Only up-to this many characters from
* <code>line</code> can be printed on one line.
* @return The logical position at which to stop printing <code>line</code>
* to simulate word wrap.
* @param line The text being printed.
* @param maxCharsPerLine Only up-to this many characters from <code>line</code>
* can be printed on one line.
* @return The logical position at which to stop printing <code>line</code> to
* simulate word wrap.
*/
private static int getLineBreakPoint(String line, final int maxCharsPerLine) {
int breakPoint = -1;
@@ -84,30 +78,23 @@ public abstract class RPrintUtilities {
}
return (breakPoint == -1 ? maxCharsPerLine - 1 : breakPoint);
}
/**
* Prints a <code>Document</code> using a monospaced font, and does no word
* wrapping (ie,
* words will wrap mid-word to the next line). This method is expected to be
* called from
* Printable 'print(Graphics g)' functions.
* wrapping (ie, words will wrap mid-word to the next line). This method is
* expected to be called from Printable 'print(Graphics g)' functions.
*
* @param g
* The graphics context to write to.
* @param doc
* The <code>javax.swing.text.Document</code> to print.
* @param fontSize
* the point size to use for the monospaced font.
* @param pageIndex
* The page number to print.
* @param pageFormat
* The format to print the page with.
* @param tabSize
* The number of spaces to expand tabs to.
* @param g The graphics context to write to.
* @param doc The <code>javax.swing.text.Document</code> to print.
* @param fontSize the point size to use for the monospaced font.
* @param pageIndex The page number to print.
* @param pageFormat The format to print the page with.
* @param tabSize The number of spaces to expand tabs to.
*
* @see #printDocumentMonospacedWordWrap
*/
public static int printDocumentMonospaced(Graphics g, Document doc, int fontSize, int pageIndex, PageFormat pageFormat, int tabSize) {
public static int printDocumentMonospaced(Graphics g, Document doc, int fontSize, int pageIndex,
PageFormat pageFormat, int tabSize) {
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
// Initialize our static variables (these are used by our tab expander
@@ -202,29 +189,23 @@ public abstract class RPrintUtilities {
}
return Printable.NO_SUCH_PAGE;
}
/**
* Prints a <code>Document</code> using a monospaced font, word wrapping on
* the characters ' ', '\t', '\n', ',', '.', and ';'. This method is
* expected to be called from Printable 'print(Graphics g)' functions.
* Prints a <code>Document</code> using a monospaced font, word wrapping on the
* characters ' ', '\t', '\n', ',', '.', and ';'. This method is expected to be
* called from Printable 'print(Graphics g)' functions.
*
* @param g
* The graphics context to write to.
* @param doc
* The <code>javax.swing.text.Document</code> to print.
* @param fontSize
* the point size to use for the monospaced font.
* @param pageIndex
* The page number to print.
* @param pageFormat
* The format to print the page with.
* @param tabSize
* The number of spaces to expand tabs to.
* @param g The graphics context to write to.
* @param doc The <code>javax.swing.text.Document</code> to print.
* @param fontSize the point size to use for the monospaced font.
* @param pageIndex The page number to print.
* @param pageFormat The format to print the page with.
* @param tabSize The number of spaces to expand tabs to.
*
* @see #printDocumentMonospaced
*/
public static int printDocumentMonospacedWordWrap(Graphics g, Document doc, int fontSize, int pageIndex, PageFormat pageFormat,
int tabSize) {
public static int printDocumentMonospacedWordWrap(Graphics g, Document doc, int fontSize, int pageIndex,
PageFormat pageFormat, int tabSize) {
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
// Initialize our static variables (these are used by our tab expander
@@ -320,30 +301,24 @@ public abstract class RPrintUtilities {
}
return Printable.NO_SUCH_PAGE;
}
/**
* Prints a <code>Document</code> using the specified font, word wrapping
* on the characters ' ', '\t', '\n', ',', '.', and ';'. This method is
* expected to be called from Printable 'print(Graphics g)' functions.
* Prints a <code>Document</code> using the specified font, word wrapping on the
* characters ' ', '\t', '\n', ',', '.', and ';'. This method is expected to be
* called from Printable 'print(Graphics g)' functions.
*
* @param g
* The graphics context to write to.
* @param textComponent
* The <code>javax.swing.text.JTextComponent</code>
* whose text you're printing.
* @param font
* The font to use for printing. If <code>null</code>, then
* <code>textComponent</code>'s font is used.
* @param pageIndex
* The page number to print.
* @param pageFormat
* The format to print the page with.
* @param tabSize
* The number of spaces to convert tabs to.
* @param g The graphics context to write to.
* @param textComponent The <code>javax.swing.text.JTextComponent</code> whose
* text you're printing.
* @param font The font to use for printing. If <code>null</code>, then
* <code>textComponent</code>'s font is used.
* @param pageIndex The page number to print.
* @param pageFormat The format to print the page with.
* @param tabSize The number of spaces to convert tabs to.
*
*/
public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent, Font font, int pageIndex, PageFormat pageFormat,
int tabSize) {
public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent, Font font, int pageIndex,
PageFormat pageFormat, int tabSize) {
// Initialize our graphics object.
g.setColor(Color.BLACK);
g.setFont(font != null ? font : textComponent.getFont());
@@ -389,7 +364,8 @@ public abstract class RPrintUtilities {
// we'd start after the part
// that has already been printed).
try {
doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset), currentLineSeg);
doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset),
currentLineSeg);
} catch (BadLocationException ble) {
System.err.println("BadLocationException in print (where there shouldn't be one!): " + ble);
return Printable.NO_SUCH_PAGE;
@@ -452,7 +428,8 @@ public abstract class RPrintUtilities {
System.err.println(ble);
return Printable.NO_SUCH_PAGE;
}
currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander, 0);
currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander,
0);
} while (currentLineLengthInPixels <= lineLengthInPixels);
currentPos--;
}
@@ -460,8 +437,8 @@ public abstract class RPrintUtilities {
doc.getText((currentLineStart + startingOffset), currentPos, currentLineSeg);
} catch (BadLocationException ble) {
System.err.println("BadLocationException in print (a):");
System.err.println("==> currentLineStart: " + currentLineStart + "; startingOffset: " + startingOffset
+ "; currentPos: " + currentPos);
System.err.println("==> currentLineStart: " + currentLineStart + "; startingOffset: "
+ startingOffset + "; currentPos: " + currentPos);
System.err.println("==> Range: " + (currentLineStart + startingOffset) + " - "
+ (currentLineStart + startingOffset + currentPos));
ble.printStackTrace();
@@ -491,12 +468,11 @@ public abstract class RPrintUtilities {
}
return Printable.NO_SUCH_PAGE;
}
/**
* Removes any spaces or tabs from the end of the segment.
*
* @param segment
* The segment from which to remove tailing whitespace.
* @param segment The segment from which to remove tailing whitespace.
* @return <code>segment</code> with trailing whitespace removed.
*/
private static Segment removeEndingWhitespace(Segment segment) {
@@ -510,15 +486,16 @@ public abstract class RPrintUtilities {
String newStringVal = stringVal.substring(0, stringVal.length() - toTrim);
return new Segment(newStringVal.toCharArray(), 0, newStringVal.length());
}
/**
* A tab expander for the document currently being printed with the
* font being used for the printing.
* A tab expander for the document currently being printed with the font being
* used for the printing.
*/
private static class RPrintTabExpander implements TabExpander {
RPrintTabExpander() {}
RPrintTabExpander() {
}
@Override
public float nextTabStop(float x, int tabOffset) {
if (tabSizeInSpaces == 0) {
@@ -17,17 +17,17 @@ import javax.swing.text.JTextComponent;
* lexicographically (ignoring case).
* <p>
*
* This implementation assumes the input text and replacement text are the
* same value. It also returns the input text from its {@link #toString()}
* method (which is what <code>DefaultListCellRenderer</code> uses to render
* objects). Subclasses that wish to override any of this behavior can simply
* override the corresponding method(s) needed to do so.
* This implementation assumes the input text and replacement text are the same
* value. It also returns the input text from its {@link #toString()} method
* (which is what <code>DefaultListCellRenderer</code> uses to render objects).
* Subclasses that wish to override any of this behavior can simply override the
* corresponding method(s) needed to do so.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class AbstractCompletion implements Completion {
/**
* The provider that created this completion;
*/
@@ -38,35 +38,32 @@ public abstract class AbstractCompletion implements Completion {
private Icon icon;
/**
* The relevance of this completion. Completion instances with higher
* "relevance" values are inserted higher into the list of possible
* completions than those with lower values. Completion instances with
* equal relevance values are sorted alphabetically.
* "relevance" values are inserted higher into the list of possible completions
* than those with lower values. Completion instances with equal relevance
* values are sorted alphabetically.
*/
private int relevance;
/**
* Constructor.
*
* @param provider
* The provider that created this completion.
* @param provider The provider that created this completion.
*/
protected AbstractCompletion(CompletionProvider provider) {
this.provider = provider;
}
/**
* Constructor.
*
* @param provider
* The provider that created this completion.
* @param icon
* The icon for this completion.
* @param provider The provider that created this completion.
* @param icon The icon for this completion.
*/
protected AbstractCompletion(CompletionProvider provider, Icon icon) {
this(provider);
setIcon(icon);
}
/**
* {@inheritDoc}
*/
@@ -79,7 +76,7 @@ public abstract class AbstractCompletion implements Completion {
}
return -1;
}
/**
* {@inheritDoc}
*/
@@ -87,7 +84,7 @@ public abstract class AbstractCompletion implements Completion {
public String getAlreadyEntered(JTextComponent comp) {
return provider.getAlreadyEnteredText(comp);
}
/**
* {@inheritDoc}
*/
@@ -95,10 +92,10 @@ public abstract class AbstractCompletion implements Completion {
public Icon getIcon() {
return icon;
}
/**
* Returns the text the user has to (start) typing for this completion
* to be offered. The default implementation simply returns
* Returns the text the user has to (start) typing for this completion to be
* offered. The default implementation simply returns
* {@link #getReplacementText()}.
*
* @return The text the user has to (start) typing for this completion.
@@ -108,7 +105,7 @@ public abstract class AbstractCompletion implements Completion {
public String getInputText() {
return getReplacementText();
}
/**
* {@inheritDoc}
*/
@@ -116,7 +113,7 @@ public abstract class AbstractCompletion implements Completion {
public CompletionProvider getProvider() {
return provider;
}
/**
* {@inheritDoc}
*/
@@ -124,10 +121,10 @@ public abstract class AbstractCompletion implements Completion {
public int getRelevance() {
return relevance;
}
/**
* The default implementation returns <code>null</code>. Subclasses
* can override this method.
* The default implementation returns <code>null</code>. Subclasses can override
* this method.
*
* @return The tool tip text.
*/
@@ -135,29 +132,27 @@ public abstract class AbstractCompletion implements Completion {
public String getToolTipText() {
return null;
}
/**
* Sets the icon to use for this completion.
*
* @param icon
* The icon to use.
* @param icon The icon to use.
* @see #getIcon()
*/
public void setIcon(Icon icon) {
this.icon = icon;
}
/**
* Sets the relevance of this completion.
*
* @param relevance
* The new relevance of this completion.
* @param relevance The new relevance of this completion.
* @see #getRelevance()
*/
public void setRelevance(int relevance) {
this.relevance = relevance;
}
/**
* Returns a string representation of this completion. The default
* implementation returns {@link #getInputText()}.
@@ -43,14 +43,14 @@ import javax.swing.event.HyperlinkListener;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* The optional "description" window that describes the currently selected
* item in the auto-completion window.
* The optional "description" window that describes the currently selected item
* in the auto-completion window.
*
* @author Robert Futrell
* @version 1.0
*/
class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescWindowCallback {
/**
* The parent AutoCompletion instance.
*/
@@ -89,9 +89,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
private int historyPos;
/**
* Provides a slight delay between asking to set a description and actually
* displaying it, so that if the user is scrolling quickly through
* completions, those with slow-to-calculate summaries won't bog down the
* scrolling.
* displaying it, so that if the user is scrolling quickly through completions,
* those with slow-to-calculate summaries won't bog down the scrolling.
*/
private Timer timer;
/**
@@ -103,24 +102,21 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
*/
private ResourceBundle bundle;
/**
* The amount of time to wait after the user changes the selected
* completion to refresh the description. This delay is in place to help
* performance for {@link Completion}s that may be slow to compute their
* summary text.
* The amount of time to wait after the user changes the selected completion to
* refresh the description. This delay is in place to help performance for
* {@link Completion}s that may be slow to compute their summary text.
*/
private static final int INITIAL_TIMER_DELAY = 120;
/**
* The resource bundle name.
*/
private static final String MSG = "org.fife.ui.autocomplete.AutoCompleteDescWindow";
/**
* Constructor.
*
* @param owner
* The parent window.
* @param ac
* The parent auto-completion.
* @param owner The parent window.
* @param ac The parent auto-completion.
*/
public AutoCompleteDescWindow(Window owner, AutoCompletion ac) {
super(owner);
@@ -146,12 +142,12 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
descWindowNavBar.add(new JButton(forwardAction));
bottomPanel = new JPanel(new BorderLayout());
b = new AbstractBorder() {
@Override
public Insets getBorderInsets(Component c) {
return new Insets(1, 0, 0, 0);
}
@Override
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
g.setColor(UIManager.getColor("controlDkShadow"));
@@ -179,19 +175,18 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
timer = new Timer(INITIAL_TIMER_DELAY, timerAction);
timer.setRepeats(false);
}
/**
* Sets the currently displayed description and updates the history.
*
* @param historyItem
* The item to add to the history.
* @param historyItem The item to add to the history.
*/
private void addToHistory(HistoryEntry historyItem) {
history.add(++historyPos, historyItem);
clearHistoryAfterCurrentPos();
setActionStates();
}
/**
* Clears the history of viewed descriptions.
*/
@@ -202,7 +197,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
setActionStates();
}
}
/**
* Makes the current history page the last one in the history.
*/
@@ -212,10 +207,10 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
setActionStates();
}
/**
* Copies from the description text area, if it is visible and there is
* a selection.
* Copies from the description text area, if it is visible and there is a
* selection.
*
* @return Whether a copy occurred.
*/
@@ -226,12 +221,11 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
return false;
}
/**
* Returns the localized message for the specified key.
*
* @param key
* The key.
* @param key The key.
* @return The localized message.
*/
private String getString(String key) {
@@ -240,12 +234,11 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
return bundle.getString(key);
}
/**
* Called when a hyperlink is clicked.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
@@ -261,7 +254,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
URL newUrl = redirector.possiblyRedirect(url);
if (newUrl != null && newUrl != url) {
url = newUrl;
e = new HyperlinkEvent(e.getSource(), e.getEventType(), newUrl, e.getDescription(), e.getSourceElement());
e = new HyperlinkEvent(e.getSource(), e.getEventType(), newUrl, e.getDescription(),
e.getSourceElement());
}
}
}
@@ -300,7 +294,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
}
}
/**
* Enables or disables the back and forward actions as appropriate.
*/
@@ -323,47 +317,41 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
forwardAction.putValue(Action.SHORT_DESCRIPTION, desc);
}
/**
* Sets the description displayed in this window. This clears the
* history.
* Sets the description displayed in this window. This clears the history.
*
* @param item
* The item whose description you want to display.
* @param item The item whose description you want to display.
*/
public void setDescriptionFor(Completion item) {
setDescriptionFor(item, false);
}
/**
* Sets the description displayed in this window.
*
* @param item
* The item whose description you want to display.
* @param addToHistory
* Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
* @param item The item whose description you want to display.
* @param addToHistory Whether to add this page to the page history (as opposed
* to clearing it and starting anew).
*/
protected void setDescriptionFor(Completion item, boolean addToHistory) {
setDescriptionFor(item, null, addToHistory);
}
/**
* Sets the description displayed in this window.
*
* @param item
* The item whose description you want to display.
* @param item The item whose description you want to display.
* @parma anchor The anchor to jump to, or <code>null</code> if none.
* @param addToHistory
* Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
* @param addToHistory Whether to add this page to the page history (as opposed
* to clearing it and starting anew).
*/
protected void setDescriptionFor(Completion item, String anchor, boolean addToHistory) {
timer.stop();
timerAction.setCompletion(item, anchor, addToHistory);
timer.start();
}
private void setDisplayedDesc(Completion completion, final String anchor, boolean addToHistory) {
String desc = completion == null ? null : completion.getSummary();
if (desc == null) {
@@ -372,7 +360,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
descArea.setText(desc);
if (anchor != null) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
descArea.scrollToReference(anchor);
@@ -388,7 +376,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
addToHistory(new HistoryEntry(completion, desc, null));
}
/**
* {@inheritDoc}
*/
@@ -399,21 +387,19 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
super.setVisible(visible);
}
/**
* Callback for custom <code>ExternalURLHandler</code>s.
*
* @param completion
* The completion to display.
* @param anchor
* The anchor in the HTML to jump to, or <code>null</code>
* if none.
* @param completion The completion to display.
* @param anchor The anchor in the HTML to jump to, or <code>null</code> if
* none.
*/
@Override
public void showSummaryFor(Completion completion, String anchor) {
setDescriptionFor(completion, anchor, true);
}
/**
* Called by the parent completion popup window the LookAndFeel is updated.
*/
@@ -425,25 +411,25 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
scrollPane.getViewport().setBackground(descArea.getBackground());
((JPanel) getContentPane()).setBorder(TipUtil.getToolTipBorder());
}
/**
* A completion and its cached summary text.
*/
private static class HistoryEntry {
public Completion completion;
public String summary;
public String anchor;
public HistoryEntry(Completion completion, String summary, String anchor) {
this.completion = completion;
this.summary = summary;
this.anchor = anchor;
}
/**
* Overridden to display a short name for the completion, since it's
* used in the tool tips for the "back" and "forward" buttons.
* Overridden to display a short name for the completion, since it's used in the
* tool tips for the "back" and "forward" buttons.
*
* @return A string representation of this history entry.
*/
@@ -452,16 +438,16 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
return completion.getInputText();
}
}
/**
* Action that actually updates the summary text displayed.
*/
private class TimerAction extends AbstractAction {
private Completion completion;
private String anchor;
private boolean addToHistory;
/**
* Called when the timer is fired.
*/
@@ -469,26 +455,26 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
public void actionPerformed(ActionEvent e) {
setDisplayedDesc(completion, anchor, addToHistory);
}
public void setCompletion(Completion c, String anchor, boolean addToHistory) {
this.completion = c;
this.anchor = anchor;
this.addToHistory = addToHistory;
}
}
/**
* Action that moves to the previous description displayed.
*/
class ToolBarBackAction extends AbstractAction {
public ToolBarBackAction(boolean ltr) {
String img = "org/fife/ui/autocomplete/arrow_" + (ltr ? "left.png" : "right.png");
ClassLoader cl = getClass().getClassLoader();
Icon icon = new ImageIcon(cl.getResource(img));
putValue(Action.SMALL_ICON, icon);
}
@Override
public void actionPerformed(ActionEvent e) {
if (historyPos > 0) {
@@ -504,19 +490,19 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
}
}
/**
* Action that moves to the previous description displayed.
*/
class ToolBarForwardAction extends AbstractAction {
public ToolBarForwardAction(boolean ltr) {
String img = "org/fife/ui/autocomplete/arrow_" + (ltr ? "right.png" : "left.png");
ClassLoader cl = getClass().getClassLoader();
Icon icon = new ImageIcon(cl.getResource(img));
putValue(Action.SMALL_ICON, icon);
}
@Override
public void actionPerformed(ActionEvent e) {
if (history != null && historyPos < history.size() - 1) {
@@ -43,16 +43,15 @@ import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* The actual popup window of choices. When visible, this window intercepts
* certain keystrokes in the parent text component and uses them to navigate
* the completion choices instead. If Enter or Escape is pressed, the window
* hides itself and notifies the {@link AutoCompletion} to insert the selected
* text.
* certain keystrokes in the parent text component and uses them to navigate the
* completion choices instead. If Enter or Escape is pressed, the window hides
* itself and notifies the {@link AutoCompletion} to insert the selected text.
*
* @author Robert Futrell
* @version 1.0
*/
class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSelectionListener, MouseListener {
/**
* The parent AutoCompletion instance.
*/
@@ -66,27 +65,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
*/
private CompletionListModel model;
/**
* A hack to work around the fact that we clear our completion model (and
* our selection) when hiding the completion window. This allows us to
* still know what the user selected after the popup is hidden.
* A hack to work around the fact that we clear our completion model (and our
* selection) when hiding the completion window. This allows us to still know
* what the user selected after the popup is hidden.
*/
private Completion lastSelection;
/**
* Optional popup window containing a description of the currently
* selected completion.
* Optional popup window containing a description of the currently selected
* completion.
*/
private AutoCompleteDescWindow descWindow;
/**
* The preferred size of the optional description window. This field
* only exists because the user may (and usually will) set the size of
* the description window before it exists (it must be parented to a
* Window).
* The preferred size of the optional description window. This field only exists
* because the user may (and usually will) set the size of the description
* window before it exists (it must be parented to a Window).
*/
private Dimension preferredDescWindowSize;
/**
* Whether the completion window and the optional description window
* should be displayed above the current caret position (as opposed to
* underneath it, which is preferred unless there is not enough space).
* Whether the completion window and the optional description window should be
* displayed above the current caret position (as opposed to underneath it,
* which is preferred unless there is not enough space).
*/
private boolean aboveCaret;
private int lastLine;
@@ -103,7 +101,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
private KeyActionPair pageUpKap;
private KeyActionPair pageDownKap;
private KeyActionPair ctrlCKap;
private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight, oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown, oldCtrlC;
private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight, oldEnter, oldTab, oldHome, oldEnd, oldPageUp,
oldPageDown, oldCtrlC;
/**
* The space between the caret and the completion popup.
*/
@@ -112,14 +111,12 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
* The class name of the Substance List UI.
*/
private static final String SUBSTANCE_LIST_UI = "org.pushingpixels.substance.internal.ui.SubstanceListUI";
/**
* Constructor.
*
* @param parent
* The parent window (hosting the text component).
* @param ac
* The auto-completion instance.
* @param parent The parent window (hosting the text component).
* @param ac The auto-completion instance.
*/
public AutoCompletePopupWindow(Window parent, final AutoCompletion ac) {
super(parent);
@@ -131,7 +128,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.addListSelectionListener(this);
list.addMouseListener(this);
JPanel contentPane = new JPanel(new BorderLayout());
JScrollPane sp = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
JScrollPane sp = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// In 1.4, JScrollPane.setCorner() has a bug where it won't accept
// JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
// defined. So we have to put the logic added in 1.5 to handle it
@@ -155,7 +153,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
setFocusableWindowState(false);
lastLine = -1;
}
@Override
public void caretUpdate(CaretEvent e) {
if (isVisible()) { // Should always be true
@@ -170,7 +168,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
Thread.dumpStack();
}
}
/**
* Creates the description window.
*
@@ -186,10 +184,10 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
dw.setSize(size);
return dw;
}
/**
* Creates the mappings from keys to Actions we'll be putting into the
* text component's ActionMap and InputMap.
* Creates the mappings from keys to Actions we'll be putting into the text
* component's ActionMap and InputMap.
*/
private void createKeyActionPairs() {
// Actions we'll install.
@@ -220,11 +218,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
oldPageDown = new KeyActionPair();
oldCtrlC = new KeyActionPair();
}
protected void doAutocomplete() {
lastLine = ac.refreshPopupWindow();
}
/**
* Returns the copy keystroke to use for this platform.
*
@@ -235,10 +233,10 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
return KeyStroke.getKeyStroke(key, mask);
}
/**
* Returns the default list cell renderer used when a completion provider
* does not supply its own.
* Returns the default list cell renderer used when a completion provider does
* not supply its own.
*
* @return The default list cell renderer.
* @see #setListCellRenderer(ListCellRenderer)
@@ -247,7 +245,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.getCellRenderer();
return dcr.getFallbackCellRenderer();
}
/**
* Returns the selected value, or <code>null</code> if nothing is selected.
*
@@ -256,7 +254,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
public Completion getSelection() {
return isShowing() ? (Completion) list.getSelectedValue() : lastSelection;
}
/**
* Inserts the currently selected completion.
*
@@ -266,10 +264,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
Completion comp = getSelection();
ac.insertCompletion(comp);
}
/**
* Registers keyboard actions to listen for in the text component and
* intercept.
* Registers keyboard actions to listen for in the text component and intercept.
*
* @see #uninstallKeyBindings()
*/
@@ -313,30 +310,34 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
comp.addCaretListener(this);
keyBindingsInstalled = true;
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
insertSelectedCompletion();
}
}
@Override
public void mouseEntered(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {}
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {
}
/**
* Positions the description window relative to the completion choices
* window. We assume there is room on one side of the other for this
* entire window to fit.
* Positions the description window relative to the completion choices window.
* We assume there is room on one side of the other for this entire window to
* fit.
*/
private void positionDescWindow() {
boolean showDescWindow = descWindow != null && ac.getShowDescWindow();
@@ -372,42 +373,31 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
descWindow.setLocation(x, y);
}
}
/**
* "Puts back" the original key/Action pair for a keystroke. This is used
* when this popup is hidden.
* "Puts back" the original key/Action pair for a keystroke. This is used when
* this popup is hidden.
*
* @param im
* The input map.
* @param am
* The action map.
* @param key
* The keystroke whose key/Action pair to change.
* @param kap
* The (original) key/Action pair.
* @see #replaceAction(InputMap, ActionMap, int, KeyActionPair,
* KeyActionPair)
* @param im The input map.
* @param am The action map.
* @param key The keystroke whose key/Action pair to change.
* @param kap The (original) key/Action pair.
* @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
*/
private void putBackAction(InputMap im, ActionMap am, int key, KeyActionPair kap) {
KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
am.put(im.get(ks), kap.action); // Original action for the "new" key
im.put(ks, kap.key); // Original key for the keystroke.
}
/**
* Replaces a key/Action pair in an InputMap and ActionMap with a new
* pair.
* Replaces a key/Action pair in an InputMap and ActionMap with a new pair.
*
* @param im
* The input map.
* @param am
* The action map.
* @param key
* The keystroke whose information to replace.
* @param kap
* The new key/Action pair for <code>key</code>.
* @param old
* A buffer in which to place the old key/Action pair.
* @param im The input map.
* @param am The action map.
* @param key The keystroke whose information to replace.
* @param kap The new key/Action pair for <code>key</code>.
* @param old A buffer in which to place the old key/Action pair.
* @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
*/
private void replaceAction(InputMap im, ActionMap am, int key, KeyActionPair kap, KeyActionPair old) {
@@ -417,7 +407,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
old.action = am.get(kap.key);
am.put(kap.key, kap.action);
}
/**
* Selects the first item in the completion list.
*
@@ -429,7 +419,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.ensureIndexIsVisible(0);
}
}
/**
* Selects the last item in the completion list.
*
@@ -442,7 +432,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.ensureIndexIsVisible(index);
}
}
/**
* Selects the next item in the completion list.
*
@@ -456,10 +446,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.ensureIndexIsVisible(index);
}
}
/**
* Selects the completion item one "page down" from the currently selected
* one.
* Selects the completion item one "page down" from the currently selected one.
*
* @see #selectPageUpItem()
*/
@@ -469,10 +458,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.setSelectedIndex(i);
list.ensureIndexIsVisible(i);
}
/**
* Selects the completion item one "page up" from the currently selected
* one.
* Selects the completion item one "page up" from the currently selected one.
*
* @see #selectPageDownItem()
*/
@@ -482,7 +470,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.setSelectedIndex(i);
list.ensureIndexIsVisible(i);
}
/**
* Selects the previous item in the completion list.
*
@@ -491,40 +479,38 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
private void selectPreviousItem() {
int index = list.getSelectedIndex();
switch (index) {
case 0:
index = list.getModel().getSize() - 1;
break;
case -1: // Check for an empty list (would be an error)
index = list.getModel().getSize() - 1;
if (index == -1) {
return;
}
break;
default:
index = index - 1;
break;
case 0:
index = list.getModel().getSize() - 1;
break;
case -1: // Check for an empty list (would be an error)
index = list.getModel().getSize() - 1;
if (index == -1) {
return;
}
break;
default:
index = index - 1;
break;
}
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
/**
* Sets the completions to display in the choices list. The first
* completion is selected.
* Sets the completions to display in the choices list. The first completion is
* selected.
*
* @param completions
* The completions to display.
* @param completions The completions to display.
*/
public void setCompletions(List<Completion> completions) {
model.setContents(completions);
selectFirstItem();
}
/**
* Sets the size of the description window.
*
* @param size
* The new size. This cannot be <code>null</code>.
* @param size The new size. This cannot be <code>null</code>.
*/
public void setDescriptionWindowSize(Dimension size) {
if (descWindow != null) {
@@ -533,28 +519,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
preferredDescWindowSize = size;
}
}
/**
* Sets the default list cell renderer to use when a completion provider
* does not supply its own.
* Sets the default list cell renderer to use when a completion provider does
* not supply its own.
*
* @param renderer
* The renderer to use. If this is <code>null</code>,
* a default renderer is used.
* @param renderer The renderer to use. If this is <code>null</code>, a default
* renderer is used.
* @see #getListCellRenderer()
*/
public void setListCellRenderer(ListCellRenderer renderer) {
DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.getCellRenderer();
dcr.setFallbackCellRenderer(renderer);
}
/**
* Sets the location of this window to be "good" relative to the specified
* rectangle. That rectangle should be the location of the text
* component's caret, in screen coordinates.
* rectangle. That rectangle should be the location of the text component's
* caret, in screen coordinates.
*
* @param r
* The text component's caret position, in screen coordinates.
* @param r The text component's caret position, in screen coordinates.
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
@@ -596,12 +580,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
positionDescWindow();
}
}
/**
* Toggles the visibility of this popup window.
*
* @param visible
* Whether this window should be visible.
* @param visible Whether this window should be visible.
*/
@Override
public void setVisible(boolean visible) {
@@ -649,7 +632,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
/**
* Stops intercepting certain keystrokes from the text component.
*
@@ -683,10 +666,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
comp.removeCaretListener(this);
keyBindingsInstalled = false;
}
/**
* Updates the <tt>LookAndFeel</tt> of this window and the description
* window.
* Updates the <tt>LookAndFeel</tt> of this window and the description window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(this);
@@ -694,12 +676,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
descWindow.updateUI();
}
}
/**
* Called when a new item is selected in the popup list.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void valueChanged(ListSelectionEvent e) {
@@ -711,9 +692,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class CopyAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
boolean doNormalCopy = false;
@@ -725,9 +706,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class DownAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -735,9 +716,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class EndAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -745,9 +726,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class EnterAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -755,9 +736,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class EscapeAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -765,9 +746,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class HomeAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -775,25 +756,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
/**
* A mapping from a key (an Object) to an Action.
*/
private static class KeyActionPair {
public Object key;
public Action action;
public KeyActionPair() {}
public KeyActionPair() {
}
public KeyActionPair(Object key, Action a) {
this.key = key;
this.action = a;
}
}
class LeftAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -813,9 +795,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class PageDownAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -823,9 +805,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class PageUpAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -833,16 +815,16 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
/**
* The actual list of completion choices in this popup window.
*/
private class PopupList extends JList {
public PopupList(CompletionListModel model) {
super(model);
}
@Override
public void setUI(ListUI ui) {
if (Util.getUseSubstanceRenderers() && SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
@@ -863,9 +845,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
super.setUI(ui);
}
}
class RightAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
@@ -885,9 +867,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
}
}
class UpAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isVisible()) {
File diff suppressed because it is too large. Load diff
@@ -8,34 +8,31 @@ package org.fife.ui.autocomplete;
import java.util.EventObject;
/**
* An event fired by an instance of {@link AutoCompletion}. This can be
* used by applications that wish to be notified of the auto-complete popup
* window showing and hiding.
* An event fired by an instance of {@link AutoCompletion}. This can be used by
* applications that wish to be notified of the auto-complete popup window
* showing and hiding.
*
* @author Robert Futrell
* @version 1.0
*/
public class AutoCompletionEvent extends EventObject {
/**
* The type of this event.
*/
private Type type;
/**
* Constructor.
*
* @param source
* The <code>AutoCompletion</code> instance that fired
* this event.
* @param type
* The event type.
* @param source The <code>AutoCompletion</code> instance that fired this event.
* @param type The event type.
*/
public AutoCompletionEvent(AutoCompletion source, Type type) {
super(source);
this.type = type;
}
/**
* Returns the source <code>AutoCompletion</code> instance. This is just
* shorthand for <code>return (AutoCompletion)getSource();</code>.
@@ -45,7 +42,7 @@ public class AutoCompletionEvent extends EventObject {
public AutoCompletion getAutoCompletion() {
return (AutoCompletion) getSource();
}
/**
* Returns the type of this event.
*
@@ -54,7 +51,7 @@ public class AutoCompletionEvent extends EventObject {
public Type getEventType() {
return type;
}
/**
* Enumeration of the various types of this event.
*/
@@ -15,13 +15,12 @@ import java.util.EventListener;
* @version 1.0
*/
public interface AutoCompletionListener extends EventListener {
/**
* Callback notified when a change to the <code>AutoCompletion</code>'s
* status occurs.
* Callback notified when a change to the <code>AutoCompletion</code>'s status
* occurs.
*
* @param e
* The event.
* @param e The event.
*/
void autoCompleteUpdate(AutoCompletionEvent e);
}
@@ -15,32 +15,32 @@ import java.awt.Color;
* @version 1.0
*/
public class AutoCompletionStyleContext {
/**
* The color used to denote the ending caret position for parameterized
* completions.
*/
private Color parameterizedCompletionCursorPositionColor;
/**
* The color used to highlight copies of editable parameters in
* parameterized completions.
* The color used to highlight copies of editable parameters in parameterized
* completions.
*/
private Color parameterCopyColor;
/**
* The color of the outline highlight used to denote editable parameters
* in parameterized completions.
* The color of the outline highlight used to denote editable parameters in
* parameterized completions.
*/
private Color parameterOutlineColor;
public AutoCompletionStyleContext() {
setParameterOutlineColor(Color.gray);
setParameterCopyColor(new Color(0xb4d7ff));
setParameterizedCompletionCursorPositionColor(new Color(0x00b400));
}
/**
* Returns the color of the highlight painted on copies of editable
* parameters in parameterized completions.
* Returns the color of the highlight painted on copies of editable parameters
* in parameterized completions.
*
* @return The color used.
* @see #setParameterCopyColor(Color)
@@ -48,10 +48,10 @@ public class AutoCompletionStyleContext {
public Color getParameterCopyColor() {
return parameterCopyColor;
}
/**
* Returns the color used to denote the ending caret position for
* parameterized completions.
* Returns the color used to denote the ending caret position for parameterized
* completions.
*
* @return The color used.
* @see #setParameterizedCompletionCursorPositionColor(Color)
@@ -59,10 +59,10 @@ public class AutoCompletionStyleContext {
public Color getParameterizedCompletionCursorPositionColor() {
return parameterizedCompletionCursorPositionColor;
}
/**
* Returns the color of the outline highlight used to denote editable
* parameters in parameterized completions.
* Returns the color of the outline highlight used to denote editable parameters
* in parameterized completions.
*
* @return The color used.
* @see #setParameterOutlineColor(Color)
@@ -70,37 +70,34 @@ public class AutoCompletionStyleContext {
public Color getParameterOutlineColor() {
return parameterOutlineColor;
}
/**
* Sets the color of the highlight painted on copies of editable
* parameters in parameterized completions.
* Sets the color of the highlight painted on copies of editable parameters in
* parameterized completions.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #setParameterCopyColor(Color)
*/
public void setParameterCopyColor(Color color) {
this.parameterCopyColor = color;
}
/**
* Sets the color used to denote the ending caret position for
* parameterized completions.
* Sets the color used to denote the ending caret position for parameterized
* completions.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #getParameterizedCompletionCursorPositionColor()
*/
public void setParameterizedCompletionCursorPositionColor(Color color) {
this.parameterizedCompletionCursorPositionColor = color;
}
/**
* Sets the color of the outline highlight used to denote editable
* parameters in parameterized completions.
* Sets the color of the outline highlight used to denote editable parameters in
* parameterized completions.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #getParameterOutlineColor()
*/
public void setParameterOutlineColor(Color color) {
@@ -7,9 +7,9 @@
package org.fife.ui.autocomplete;
/**
* A straightforward {@link Completion} implementation. This implementation
* can be used if you have a relatively short number of static completions
* with no (or short) summaries.
* A straightforward {@link Completion} implementation. This implementation can
* be used if you have a relatively short number of static completions with no
* (or short) summaries.
* <p>
*
* This implementation uses the replacement text as the input text. It also
@@ -20,53 +20,44 @@ package org.fife.ui.autocomplete;
* @version 1.0
*/
public class BasicCompletion extends AbstractCompletion {
private String replacementText;
private String shortDesc;
private String summary;
/**
* Constructor.
*
* @param provider
* The parent completion provider.
* @param replacementText
* The text to replace.
* @param provider The parent completion provider.
* @param replacementText The text to replace.
*/
public BasicCompletion(CompletionProvider provider, String replacementText) {
this(provider, replacementText, null);
}
/**
* Constructor.
*
* @param provider
* The parent completion provider.
* @param replacementText
* The text to replace.
* @param shortDesc
* A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param provider The parent completion provider.
* @param replacementText The text to replace.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
*/
public BasicCompletion(CompletionProvider provider, String replacementText, String shortDesc) {
this(provider, replacementText, shortDesc, null);
}
/**
* Constructor.
*
* @param provider
* The parent completion provider.
* @param replacementText
* The text to replace.
* @param shortDesc
* A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param summary
* The summary of this completion. This should be HTML.
* This may be <code>null</code>.
* @param provider The parent completion provider.
* @param replacementText The text to replace.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param summary The summary of this completion. This should be HTML.
* This may be <code>null</code>.
*/
public BasicCompletion(CompletionProvider provider, String replacementText, String shortDesc, String summary) {
super(provider);
@@ -74,7 +65,7 @@ public class BasicCompletion extends AbstractCompletion {
this.shortDesc = shortDesc;
this.summary = summary;
}
/**
* {@inheritDoc}
*/
@@ -82,10 +73,10 @@ public class BasicCompletion extends AbstractCompletion {
public String getReplacementText() {
return replacementText;
}
/**
* Returns the short description of this completion, usually used in
* the completion choices list.
* Returns the short description of this completion, usually used in the
* completion choices list.
*
* @return The short description, or <code>null</code> if there is none.
* @see #setShortDescription(String)
@@ -93,7 +84,7 @@ public class BasicCompletion extends AbstractCompletion {
public String getShortDescription() {
return shortDesc;
}
/**
* {@inheritDoc}
*/
@@ -101,32 +92,30 @@ public class BasicCompletion extends AbstractCompletion {
public String getSummary() {
return summary;
}
/**
* Sets the short description of this completion.
*
* @param shortDesc
* The short description of this completion.
* @param shortDesc The short description of this completion.
* @see #getShortDescription()
*/
public void setShortDescription(String shortDesc) {
this.shortDesc = shortDesc;
}
/**
* Sets the summary for this completion.
*
* @param summary
* The summary for this completion.
* @param summary The summary for this completion.
* @see #getSummary()
*/
public void setSummary(String summary) {
this.summary = summary;
}
/**
* Returns a string representation of this completion. If the short
* description is not <code>null</code>, this method will return:
* Returns a string representation of this completion. If the short description
* is not <code>null</code>, this method will return:
*
* <code>getInputText() + " - " + shortDesc</code>
*
+51 -59
View File
@@ -10,20 +10,20 @@ import javax.swing.Icon;
import javax.swing.text.JTextComponent;
/**
* Represents a completion choice. A {@link CompletionProvider} returns lists
* of objects implementing this interface. A <tt>Completion</tt> contains the
* Represents a completion choice. A {@link CompletionProvider} returns lists of
* objects implementing this interface. A <tt>Completion</tt> contains the
* following information:
*
* <ul>
* <li>The text the user must (begin to) input for this to be a completion
* choice.
* <li>The text that will be filled in if the user chooses this completion.
* Note that often, this is the same as the text the user must (begin to)
* enter, but this doesn't have to be the case.
* <li>Summary HTML that describes this completion. This is information that
* can be displayed in a helper "tooltip"-style window beside the
* completion list. This may be <code>null</code>. It may also be
* lazily generated to cut down on memory usage.
* <li>The text that will be filled in if the user chooses this completion. Note
* that often, this is the same as the text the user must (begin to) enter, but
* this doesn't have to be the case.
* <li>Summary HTML that describes this completion. This is information that can
* be displayed in a helper "tooltip"-style window beside the completion list.
* This may be <code>null</code>. It may also be lazily generated to cut down on
* memory usage.
* <li>The <tt>CompletionProvider</tt> that returned this completion.
* <li>Tool tip text that can be displayed when a mouse hovers over this
* completion in a text component.
@@ -34,112 +34,104 @@ import javax.swing.text.JTextComponent;
* @see AbstractCompletion
*/
public interface Completion extends Comparable<Completion> {
/**
* Compares this completion to another one lexicographically, ignoring
* case.
* Compares this completion to another one lexicographically, ignoring case.
*
* @param other
* Another completion instance.
* @param other Another completion instance.
* @return How this completion compares to the other one.
*/
@Override
public int compareTo(Completion other);
/**
* Returns the portion of this completion that has already been entered
* into the text component. The match is case-insensitive.
* Returns the portion of this completion that has already been entered into the
* text component. The match is case-insensitive.
* <p>
*
* This is a convenience method for:
* <code>getProvider().getAlreadyEnteredText(comp)</code>.
*
* @param comp
* The text component.
* @param comp The text component.
* @return The already-entered portion of this completion.
*/
public String getAlreadyEntered(JTextComponent comp);
/**
* Returns the icon to use for this completion.
*
* @return The icon, or <code>null</code> for none.
*/
public Icon getIcon();
/**
* Returns the text that the user has to (start) typing for this completion
* to be offered. Note that this will usually be the same value as
* {@link #getReplacementText()}, but not always (a completion could be
* a way to implement shorthand, for example, "<code>sysout</code>" mapping
* to "<code>System.out.println(</code>").
* Returns the text that the user has to (start) typing for this completion to
* be offered. Note that this will usually be the same value as
* {@link #getReplacementText()}, but not always (a completion could be a way to
* implement shorthand, for example, "<code>sysout</code>" mapping to
* "<code>System.out.println(</code>").
*
* @return The text the user has to (start) typing for this completion to
* be offered.
* @return The text the user has to (start) typing for this completion to be
* offered.
* @see #getReplacementText()
*/
public String getInputText();
/**
* Returns the provider that returned this completion.
*
* @return The provider.
*/
public CompletionProvider getProvider();
/**
* Returns the "relevance" of this completion. This is used when sorting
* completions by their relevance. It is an abstract concept that may
* mean different things to different languages, and may depend on the
* context of the completion.
* completions by their relevance. It is an abstract concept that may mean
* different things to different languages, and may depend on the context of the
* completion.
* <p>
*
* By default, all completions have a relevance of <code>0</code>. The
* higher the value returned by this method, the higher up in the list
* this completion will be; the lower the value returned, the lower it will
* be. <code>Completion</code>s with equal relevance values will be
* sorted alphabetically.
* By default, all completions have a relevance of <code>0</code>. The higher
* the value returned by this method, the higher up in the list this completion
* will be; the lower the value returned, the lower it will be.
* <code>Completion</code>s with equal relevance values will be sorted
* alphabetically.
*
* @return The relevance of this completion.
*/
public int getRelevance();
/**
* Returns the text to insert as the result of this auto-completion. This
* is the "complete" text, including any text that replaces what the user
* has already typed.
* Returns the text to insert as the result of this auto-completion. This is the
* "complete" text, including any text that replaces what the user has already
* typed.
*
* @return The replacement text.
* @see #getInputText()
*/
public String getReplacementText();
/**
* Returns the description of this auto-complete choice. This can be
* used in a popup "description window."
* Returns the description of this auto-complete choice. This can be used in a
* popup "description window."
*
* @return This item's description. This should be HTML. It may be
* <code>null</code> if there is no description for this
* completion.
* <code>null</code> if there is no description for this completion.
*/
public String getSummary();
/**
* Returns the tool tip text to display for mouse hovers over this
* completion.
* Returns the tool tip text to display for mouse hovers over this completion.
* <p>
*
* Note that for this functionality to be enabled, a
* <tt>JTextComponent</tt> must be registered with the
* <tt>ToolTipManager</tt>, and the text component must know to search
* for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* Note that for this functionality to be enabled, a <tt>JTextComponent</tt>
* must be registered with the <tt>ToolTipManager</tt>, and the text component
* must know to search for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this can
* be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
*
* @return The tool tip text for this completion, or <code>null</code> if
* none.
* @return The tool tip text for this completion, or <code>null</code> if none.
*/
public String getToolTipText();
}
@@ -26,8 +26,8 @@ import javax.swing.text.View;
/**
* A cell renderer that adds some pizazz when rendering the standard
* {@link Completion} types, like Eclipse and NetBeans do. Specifically,
* this renderer handles:
* {@link Completion} types, like Eclipse and NetBeans do. Specifically, this
* renderer handles:
*
* <ul>
* <li>{@link FunctionCompletion}s</li>
@@ -41,20 +41,20 @@ import javax.swing.text.View;
* @version 1.0
*/
public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* The alternating background color, or <code>null</code> if alternating
* row colors should not be used.
* The alternating background color, or <code>null</code> if alternating row
* colors should not be used.
*/
private static Color altBG;
/**
* The font to use when rendering items, or <code>null</code> if the
* list's default font should be used.
* The font to use when rendering items, or <code>null</code> if the list's
* default font should be used.
*/
private Font font;
/**
* Whether to display the types of fields and return types of functions
* in the completion text.
* Whether to display the types of fields and return types of functions in the
* completion text.
*/
private boolean showTypes;
/**
@@ -92,33 +92,32 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
* words.
*/
private static final String PREFIX = "<html><nobr>";
/**
* Constructor.
*/
public CompletionCellRenderer() {
init();
}
/**
* Constructor. This is primarily a hook for Substance, or any other
* Look and Feel whose renderers look drastically different than standard
* Constructor. This is primarily a hook for Substance, or any other Look and
* Feel whose renderers look drastically different than standard
* <code>DefaultListCellRenderer</code>s. Everything except for the text
* rendering will be done by the delegate. In almost all scenarios, you
* will want to use the no-argument constructor instead of this one.
* rendering will be done by the delegate. In almost all scenarios, you will
* want to use the no-argument constructor instead of this one.
*
* @param delegate
* The delegate renderer.
* @param delegate The delegate renderer.
* @see #delegateToSubstanceRenderer()
*/
public CompletionCellRenderer(DefaultListCellRenderer delegate) {
setDelegateRenderer(delegate);
init();
}
/**
* Creates the icon to use if no icon is found for a specific completion.
* The default implementation returns a 16x16 empty icon.
* Creates the icon to use if no icon is found for a specific completion. The
* default implementation returns a 16x16 empty icon.
*
* @return The icon.
* @see #getEmptyIcon()
@@ -126,34 +125,32 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
protected Icon createEmptyIcon() {
return new EmptyIcon(16);
}
/**
* Returns a decent "parameter" color based on the current default
* foreground color.
* Returns a decent "parameter" color based on the current default foreground
* color.
*
* @return The parameter color to use.
*/
private String createParamColor() {
return Util.isLightForeground(getForeground()) ? Util.getHexString(Util.getHyperlinkForeground()) : "#aa0077";
}
/**
* Returns a decent "type" color based on the current default foreground
* color.
* Returns a decent "type" color based on the current default foreground color.
*
* @return The type color to use.
*/
private String createTypeColor() {
return "#808080";
}
/**
* Attempts to delegate rendering to a Substance cell renderer. This
* should only be called if Substance is known to be on the classpath.
* Attempts to delegate rendering to a Substance cell renderer. This should only
* be called if Substance is known to be on the classpath.
*
* @throws Exception
* If Substance is not on the classpath, or some other
* error occurs creating the Substance cell renderer.
* @throws Exception If Substance is not on the classpath, or some other error
* occurs creating the Substance cell renderer.
* @see Util#getUseSubstanceRenderers()
* @see #setDelegateRenderer(DefaultListCellRenderer)
*/
@@ -162,7 +159,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
DefaultListCellRenderer delegate = (DefaultListCellRenderer) clazz.newInstance();
setDelegateRenderer(delegate);
}
/**
* Returns the background color to use on alternating lines.
*
@@ -173,7 +170,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
public static Color getAlternateBackground() {
return altBG;
}
/**
* Returns the delegate renderer, or <code>null</code> if there is none.
*
@@ -183,21 +180,21 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
public DefaultListCellRenderer getDelegateRenderer() {
return delegate;
}
/**
* Returns the font used when rendering completions.
*
* @return The font. If this is <code>null</code>, then the default list
* font is used.
* @return The font. If this is <code>null</code>, then the default list font is
* used.
* @see #setDisplayFont(Font)
*/
public Font getDisplayFont() {
return font;
}
/**
* Returns the icon to use if no icon is found for a specific completion.
* This icon is lazily created if necessary.
* Returns the icon to use if no icon is found for a specific completion. This
* icon is lazily created if necessary.
*
* @return The icon.
* @see #createEmptyIcon()
@@ -208,13 +205,12 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
return emptyIcon;
}
/**
* Returns an icon.
*
* @param resource
* The icon to retrieve. This should either be a file,
* or a resource loadable by the current ClassLoader.
* @param resource The icon to retrieve. This should either be a file, or a
* resource loadable by the current ClassLoader.
* @return The icon.
*/
protected Icon getIcon(String resource) {
@@ -229,23 +225,19 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
return url != null ? new ImageIcon(url) : null;
}
/**
* Returns the renderer.
*
* @param list
* The list of choices being rendered.
* @param value
* The {@link Completion} being rendered.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param value The {@link Completion} being rendered.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected,
boolean hasFocus) {
super.getListCellRendererComponent(list, value, index, selected, hasFocus);
if (font != null) {
setFont(font); // Overrides super's setFont(list.getFont()).
@@ -282,10 +274,10 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
return this;
}
/**
* Returns whether the types of fields and return types of methods are
* shown in the completion text.
* Returns whether the types of fields and return types of methods are shown in
* the completion text.
*
* @return Whether to show the types.
* @see #setShowTypes(boolean)
@@ -293,7 +285,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
public boolean getShowTypes() {
return showTypes;
}
private void init() {
// setDisplayFont(new Font("Monospaced", Font.PLAIN, 12));
setShowTypes(true);
@@ -301,7 +293,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
paramColor = createParamColor();
paintTextR = new Rectangle();
}
@Override
protected void paintComponent(Graphics g) {
// super.paintComponent(g);
@@ -342,22 +334,18 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
}
}
/**
* Prepares this renderer to display a function completion.
*
* @param list
* The list of choices being rendered.
* @param fc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param fc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForFunctionCompletion(JList list, FunctionCompletion fc, int index, boolean selected, boolean hasFocus) {
protected void prepareForFunctionCompletion(JList list, FunctionCompletion fc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(fc.getName());
char paramListStart = fc.getProvider().getParameterListStart();
@@ -404,41 +392,32 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
setText(sb.toString());
}
/**
* Prepares this renderer to display a markup tag completion.
*
* @param list
* The list of choices being rendered.
* @param mc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param mc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForMarkupTagCompletion(JList list, MarkupTagCompletion mc, int index, boolean selected, boolean hasFocus) {
protected void prepareForMarkupTagCompletion(JList list, MarkupTagCompletion mc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(mc.getName());
setText(sb.toString());
}
/**
* Prepares this renderer to display a completion not specifically handled
* elsewhere.
*
* @param list
* The list of choices being rendered.
* @param c
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param c The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForOtherCompletion(JList list, Completion c, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
@@ -458,22 +437,18 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
setText(sb.toString());
}
/**
* Prepares this renderer to display a template completion.
*
* @param list
* The list of choices being rendered.
* @param tc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param tc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForTemplateCompletion(JList list, TemplateCompletion tc, int index, boolean selected, boolean hasFocus) {
protected void prepareForTemplateCompletion(JList list, TemplateCompletion tc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(tc.getInputText());
String definition = tc.getShortDescription();
@@ -489,22 +464,18 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
setText(sb.toString());
}
/**
* Prepares this renderer to display a variable completion.
*
* @param list
* The list of choices being rendered.
* @param vc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param vc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForVariableCompletion(JList list, VariableCompletion vc, int index, boolean selected, boolean hasFocus) {
protected void prepareForVariableCompletion(JList list, VariableCompletion vc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(vc.getName());
if (getShowTypes() && vc.getType() != null) {
@@ -519,81 +490,73 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
setText(sb.toString());
}
/**
* Sets the background color to use on alternating lines.
*
* @param altBG
* The new alternate background color. If this is
* <code>null</code>, alternating lines will not use different
* background colors.
* @param altBG The new alternate background color. If this is
* <code>null</code>, alternating lines will not use different
* background colors.
* @see #getAlternateBackground()
*/
public static void setAlternateBackground(Color altBG) {
CompletionCellRenderer.altBG = altBG;
}
/**
* Sets the delegate renderer. Most users will never use this method; it
* is primarily a hook for Substance and other Look and Feels whose
* renderers look drastically different from the standard
* <code>DefaultListCellRenderer</code>.
* Sets the delegate renderer. Most users will never use this method; it is
* primarily a hook for Substance and other Look and Feels whose renderers look
* drastically different from the standard <code>DefaultListCellRenderer</code>.
*
* @param delegate
* The new delegate renderer. If this is <code>null</code>,
* the default rendering of this component is used.
* @param delegate The new delegate renderer. If this is <code>null</code>, the
* default rendering of this component is used.
* @see #getDelegateRenderer()
* @see #delegateToSubstanceRenderer()
*/
public void setDelegateRenderer(DefaultListCellRenderer delegate) {
this.delegate = delegate;
}
/**
* Sets the font to use when rendering completion items.
*
* @param font
* The font to use. If this is <code>null</code>, then
* the default list font is used.
* @param font The font to use. If this is <code>null</code>, then the default
* list font is used.
* @see #getDisplayFont()
*/
public void setDisplayFont(Font font) {
this.font = font;
}
/**
* Sets the icon to display based off of a completion, falling back to the
* empty icon if the completion has no icon.
* Sets the icon to display based off of a completion, falling back to the empty
* icon if the completion has no icon.
*
* @param completion
* The completion to check.
* @param completion The completion to check.
* @see #setIconWithDefault(Completion, Icon)
*/
protected void setIconWithDefault(Completion completion) {
setIconWithDefault(completion, getEmptyIcon());
}
/**
* Sets the icon to display based off of a completion, falling back to a
* default icon if the completion has no icon.
* Sets the icon to display based off of a completion, falling back to a default
* icon if the completion has no icon.
*
* @param completion
* The completion to check.
* @param defaultIcon
* The icon to use if <code>completion</code> does not
* specify an icon.
* @param completion The completion to check.
* @param defaultIcon The icon to use if <code>completion</code> does not
* specify an icon.
* @see #setIconWithDefault(Completion)
*/
protected void setIconWithDefault(Completion completion, Icon defaultIcon) {
Icon icon = completion.getIcon();
setIcon(icon != null ? icon : (defaultIcon != null ? defaultIcon : emptyIcon));
}
/**
* Sets the color to use for function arguments.
*
* @param color
* The color to use. This is ignored if <code>null</code>.
* @param color The color to use. This is ignored if <code>null</code>.
* @see #setTypeColor(Color)
*/
public void setParamColor(Color color) {
@@ -601,27 +564,25 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
paramColor = Util.getHexString(color);
}
}
/**
* Sets whether the types of fields and return types of methods are
* shown in the completion text.
* Sets whether the types of fields and return types of methods are shown in the
* completion text.
*
* @param show
* Whether to show the types.
* @param show Whether to show the types.
* @see #getShowTypes()
*/
public void setShowTypes(boolean show) {
this.showTypes = show;
}
/**
* Sets the color to use for function/field types. Note that if
* {@link #getShowTypes()} returns <code>false</code>, this property
* effectively does nothing.
* {@link #getShowTypes()} returns <code>false</code>, this property effectively
* does nothing.
*
* @param color
* The color to use for types. This is ignored if
* <code>null</code>.
* @param color The color to use for types. This is ignored if
* <code>null</code>.
* @see #setShowTypes(boolean)
* @see #setParamColor(Color)
*/
@@ -630,7 +591,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
typeColor = Util.getHexString(color);
}
}
/**
* Overridden to update our delegate, if necessary.
*/
@@ -13,30 +13,29 @@ import java.util.List;
import javax.swing.AbstractListModel;
/**
* A list model implementation that allows the bulk addition of elements.
* This is the only feature missing from <code>DefaultListModel</code> that
* we need.
* A list model implementation that allows the bulk addition of elements. This
* is the only feature missing from <code>DefaultListModel</code> that we need.
*
* @author Robert Futrell
* @version 1.0
*/
class CompletionListModel extends AbstractListModel {
/**
* Container for items in this model.
*/
private List<Completion> delegate;
/**
* Constructor.
*/
public CompletionListModel() {
delegate = new ArrayList<Completion>();
}
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns (unless it throws an exception).
* Removes all of the elements from this list. The list will be empty after this
* call returns (unless it throws an exception).
*
* @see #setContents(Collection)
*/
@@ -47,7 +46,7 @@ class CompletionListModel extends AbstractListModel {
fireIntervalRemoved(this, 0, end);
}
}
/**
* {@inheritDoc}
*/
@@ -55,7 +54,7 @@ class CompletionListModel extends AbstractListModel {
public Object getElementAt(int index) {
return delegate.get(index);
}
/**
* {@inheritDoc}
*/
@@ -63,12 +62,11 @@ class CompletionListModel extends AbstractListModel {
public int getSize() {
return delegate.size();
}
/**
* Sets the contents of this model. All previous contents are removed.
*
* @param contents
* The new contents of this model.
* @param contents The new contents of this model.
*/
public void setContents(Collection<Completion> contents) {
clear();
@@ -25,7 +25,7 @@ import javax.swing.text.JTextComponent;
* @version 1.0
*/
public interface CompletionProvider {
/**
* Clears the values used to identify and insert "parameterized completions"
* (e.g. functions or methods). After this method is called, functions and
@@ -34,111 +34,103 @@ public interface CompletionProvider {
* @see #setParameterizedCompletionParams(char, String, char)
*/
public void clearParameterizedCompletionParams();
/**
* Returns the text just before the current caret position that could be
* the start of something auto-completable.
* Returns the text just before the current caret position that could be the
* start of something auto-completable.
*
* @param comp
* The text component.
* @return The text. A return value of <code>null</code> means nothing
* should be auto-completed; a value of an empty string
* (<code>""</code>) means auto-completion should still be
* considered (i.e., all possible choices are valid).
* @param comp The text component.
* @return The text. A return value of <code>null</code> means nothing should be
* auto-completed; a value of an empty string (<code>""</code>) means
* auto-completion should still be considered (i.e., all possible
* choices are valid).
*/
public String getAlreadyEnteredText(JTextComponent comp);
/**
* Gets the possible completions for the text component at the current
* caret position.
* Gets the possible completions for the text component at the current caret
* position.
*
* @param comp
* The text component.
* @return The list of {@link Completion}s. If no completions are
* available, this method should return an empty list.
* @param comp The text component.
* @return The list of {@link Completion}s. If no completions are available,
* this method should return an empty list.
*/
public List<Completion> getCompletions(JTextComponent comp);
/**
* Returns the completions that have been entered at the specified visual
* location. This can be used for tool tips when the user hovers the
* mouse over completed text.
* location. This can be used for tool tips when the user hovers the mouse over
* completed text.
*
* @param comp
* The text component.
* @param p
* The position, usually from a <tt>MouseEvent</tt>.
* @param comp The text component.
* @param p The position, usually from a <tt>MouseEvent</tt>.
* @return The completions, or an empty list if there are none.
*/
public List<Completion> getCompletionsAt(JTextComponent comp, Point p);
/**
* Returns the cell renderer for completions returned from this provider.
*
* @return The cell renderer, or <code>null</code> if the default should
* be used.
* @return The cell renderer, or <code>null</code> if the default should be
* used.
* @see #setListCellRenderer(ListCellRenderer)
*/
public ListCellRenderer getListCellRenderer();
/**
* Returns an object that can return a list of completion choices for
* parameters. This is used when a user code-completes a parameterized
* completion, such as a function or method. For any parameter to the
* function/method, this object can return possible completions.
*
* @return The parameter choices provider, or <code>null</code> if
* none is installed.
* @return The parameter choices provider, or <code>null</code> if none is
* installed.
*/
public ParameterChoicesProvider getParameterChoicesProvider();
/**
* Returns a list of parameterized completions that have been entered
* at the current caret position of a text component (and thus can have
* their completion choices displayed).
* Returns a list of parameterized completions that have been entered at the
* current caret position of a text component (and thus can have their
* completion choices displayed).
*
* @param tc
* The text component.
* @return The list of {@link ParameterizedCompletion}s. If no completions
* are available, this may be <code>null</code>.
* @param tc The text component.
* @return The list of {@link ParameterizedCompletion}s. If no completions are
* available, this may be <code>null</code>.
*/
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc);
/**
* Returns the text that marks the end of a list of parameters to a
* function or method.
* Returns the text that marks the end of a list of parameters to a function or
* method.
*
* @return The text for a parameter list end, for example,
* '<code>)</code>'.
* @return The text for a parameter list end, for example, '<code>)</code>'.
* @see #getParameterListStart()
* @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char)
*/
public char getParameterListEnd();
/**
* Returns the text that separates parameters to a function or method.
*
* @return The text that separates parameters, for example,
* "<code>, </code>".
* @return The text that separates parameters, for example, "<code>, </code>".
* @see #getParameterListStart()
* @see #getParameterListEnd()
* @see #setParameterizedCompletionParams(char, String, char)
*/
public String getParameterListSeparator();
/**
* Returns the text that marks the start of a list of parameters to a
* function or method.
* Returns the text that marks the start of a list of parameters to a function
* or method.
*
* @return The text for a parameter list start, for example,
* "<code>(</code>".
* @return The text for a parameter list start, for example, "<code>(</code>".
* @see #getParameterListEnd()
* @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char)
*/
public char getParameterListStart();
/**
* Returns the parent completion provider.
*
@@ -146,59 +138,53 @@ public interface CompletionProvider {
* @see #setParent(CompletionProvider)
*/
public CompletionProvider getParent();
/**
* This method is called if auto-activation is enabled in the parent
* {@link AutoCompletion} after the user types a single character. This
* provider should check the text at the current caret position of the
* text component, and decide whether auto-activation would be appropriate
* here. For example, a <code>CompletionProvider</code> for Java might
* want to return <code>true</code> for this method only if the last
* character typed was a '<code>.</code>'.
* {@link AutoCompletion} after the user types a single character. This provider
* should check the text at the current caret position of the text component,
* and decide whether auto-activation would be appropriate here. For example, a
* <code>CompletionProvider</code> for Java might want to return
* <code>true</code> for this method only if the last character typed was a
* '<code>.</code>'.
*
* @param tc
* The text component.
* @param tc The text component.
* @return Whether auto-activation would be appropriate.
*/
public boolean isAutoActivateOkay(JTextComponent tc);
/**
* Sets the renderer to use when displaying completion choices.
*
* @param r
* The renderer to use.
* @param r The renderer to use.
* @see #getListCellRenderer()
*/
public void setListCellRenderer(ListCellRenderer r);
/**
* Sets the values used to identify and insert "parameterized completions"
* (e.g. functions or methods). If this method isn't called, functions
* and methods will not have their parameters auto-completed.
* Sets the values used to identify and insert "parameterized completions" (e.g.
* functions or methods). If this method isn't called, functions and methods
* will not have their parameters auto-completed.
*
* @param listStart
* The character that marks the beginning of a list of
* parameters, such as '<tt>(</tt>' in C or Java.
* @param separator
* Text that should separate parameters in a parameter
* list when one is inserted. For example, "<tt>, </tt>".
* @param listEnd
* The character that marks the end of a list of parameters,
* such as '<tt>)</tt>' in C or Java.
* @throws IllegalArgumentException
* If either <tt>listStart</tt> or
* <tt>listEnd</tt> is not printable ASCII, or if
* <tt>separator</tt> is <code>null</code> or an empty string.
* @param listStart The character that marks the beginning of a list of
* parameters, such as '<tt>(</tt>' in C or Java.
* @param separator Text that should separate parameters in a parameter list
* when one is inserted. For example, "<tt>, </tt>".
* @param listEnd The character that marks the end of a list of parameters,
* such as '<tt>)</tt>' in C or Java.
* @throws IllegalArgumentException If either <tt>listStart</tt> or
* <tt>listEnd</tt> is not printable ASCII, or
* if <tt>separator</tt> is <code>null</code>
* or an empty string.
* @see #clearParameterizedCompletionParams()
*/
public void setParameterizedCompletionParams(char listStart, String separator, char listEnd);
/**
* Sets the parent completion provider.
*
* @param parent
* The parent provider. <code>null</code> means there will
* be no parent provider.
* @param parent The parent provider. <code>null</code> means there will be no
* parent provider.
* @see #getParent()
*/
public void setParent(CompletionProvider parent);
@@ -182,7 +182,8 @@ public abstract class CompletionProviderBase implements CompletionProvider {
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
return (autoActivateAfterLetters && Character.isLetter(ch)) || (autoActivateChars != null && autoActivateChars.indexOf(ch) > -1);
return (autoActivateAfterLetters && Character.isLetter(ch))
|| (autoActivateChars != null && autoActivateChars.indexOf(ch) > -1);
}
/**
@@ -18,15 +18,15 @@ import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Parser for an XML file describing a procedural language such as C. XML
* files will be validated against the <code>CompletionXml.dtd</code> DTD
* found in this package.
* Parser for an XML file describing a procedural language such as C. XML files
* will be validated against the <code>CompletionXml.dtd</code> DTD found in
* this package.
*
* @author Robert Futrell
* @version 1.0
*/
public class CompletionXMLParser extends DefaultHandler {
/**
* The completions found after parsing the XML.
*/
@@ -68,36 +68,31 @@ public class CompletionXMLParser extends DefaultHandler {
*/
private String funcCompletionType;
/**
* The class loader to use to load custom completion classes, such as
* the one defined by {@link #funcCompletionType}. If this is
* <code>null</code>, then a default class loader is used. This field
* will usually be <code>null</code>.
* The class loader to use to load custom completion classes, such as the one
* defined by {@link #funcCompletionType}. If this is <code>null</code>, then a
* default class loader is used. This field will usually be <code>null</code>.
*/
private static ClassLoader DEFAULT_COMPLETION_CLASS_LOADER;
/**
* Constructor.
*
* @param provider
* The provider to get completions for.
* @param provider The provider to get completions for.
* @see #reset(CompletionProvider)
*/
public CompletionXMLParser(CompletionProvider provider) {
this(provider, null);
}
/**
* Constructor.
*
* @param provider
* The provider to get completions for.
* @param cl
* The class loader to use, if necessary, when loading classes
* from the XML (custom {@link FunctionCompletion}s, for
* example).
* This may be <code>null</code> if the default is to be used, or
* if the XML does not define specific classes for completion
* types.
* @param provider The provider to get completions for.
* @param cl The class loader to use, if necessary, when loading classes
* from the XML (custom {@link FunctionCompletion}s, for
* example). This may be <code>null</code> if the default is to
* be used, or if the XML does not define specific classes for
* completion types.
* @see #reset(CompletionProvider)
*/
public CompletionXMLParser(CompletionProvider provider, ClassLoader cl) {
@@ -115,7 +110,7 @@ public class CompletionXMLParser extends DefaultHandler {
paramStartChar = paramEndChar = 0;
paramSeparator = null;
}
/**
* Called when character data inside an element is found.
*/
@@ -129,7 +124,7 @@ public class CompletionXMLParser extends DefaultHandler {
returnValDesc.append(ch, start, length);
}
}
private FunctionCompletion createFunctionCompletion() {
FunctionCompletion fc = null;
if (funcCompletionType != null) {
@@ -163,7 +158,7 @@ public class CompletionXMLParser extends DefaultHandler {
}
return fc;
}
private BasicCompletion createOtherCompletion() {
BasicCompletion bc = new BasicCompletion(provider, name);
if (desc.length() > 0) {
@@ -172,7 +167,7 @@ public class CompletionXMLParser extends DefaultHandler {
}
return bc;
}
private MarkupTagCompletion createMarkupTagCompletion() {
MarkupTagCompletion mc = new MarkupTagCompletion(provider, name);
if (desc.length() > 0) {
@@ -183,7 +178,7 @@ public class CompletionXMLParser extends DefaultHandler {
mc.setDefinedIn(definedIn);
return mc;
}
private VariableCompletion createVariableCompletion() {
VariableCompletion vc = new VariableCompletion(provider, name, returnType);
if (desc.length() > 0) {
@@ -193,7 +188,7 @@ public class CompletionXMLParser extends DefaultHandler {
vc.setDefinedIn(definedIn);
return vc;
}
/**
* Called when an element is closed.
*/
@@ -246,12 +241,12 @@ public class CompletionXMLParser extends DefaultHandler {
}
}
}
@Override
public void error(SAXParseException e) throws SAXException {
throw e;
}
/**
* Returns the completions found after parsing the XML.
*
@@ -260,7 +255,7 @@ public class CompletionXMLParser extends DefaultHandler {
public List<Completion> getCompletions() {
return completions;
}
/**
* Returns the parameter end character specified.
*
@@ -269,7 +264,7 @@ public class CompletionXMLParser extends DefaultHandler {
public char getParamEndChar() {
return paramEndChar;
}
/**
* Returns the parameter end string specified.
*
@@ -278,7 +273,7 @@ public class CompletionXMLParser extends DefaultHandler {
public String getParamSeparator() {
return paramSeparator;
}
/**
* Returns the parameter start character specified.
*
@@ -287,16 +282,15 @@ public class CompletionXMLParser extends DefaultHandler {
public char getParamStartChar() {
return paramStartChar;
}
private static final char getSingleChar(String str) {
return str.length() == 1 ? str.charAt(0) : 0;
}
/**
* Resets this parser to grab more completions.
*
* @param provider
* The new provider to get completions for.
* @param provider The new provider to get completions for.
*/
public void reset(CompletionProvider provider) {
this.provider = provider;
@@ -305,28 +299,26 @@ public class CompletionXMLParser extends DefaultHandler {
paramStartChar = paramEndChar = 0;
paramSeparator = null;
}
@Override
public InputSource resolveEntity(String publicID, String systemID) throws SAXException {
return new InputSource(getClass().getResourceAsStream("CompletionXml.dtd"));
}
/**
* Sets the class loader to use when loading custom classes to use for
* various {@link Completion} types, such as {@link FunctionCompletion}s,
* from XML.
* Sets the class loader to use when loading custom classes to use for various
* {@link Completion} types, such as {@link FunctionCompletion}s, from XML.
* <p>
*
* Users should very rarely have a need to use this method.
*
* @param cl
* The class loader to use. If this is <code>null</code>, then
* a default is used.
* @param cl The class loader to use. If this is <code>null</code>, then a
* default is used.
*/
public static void setDefaultCompletionClassLoader(ClassLoader cl) {
DEFAULT_COMPLETION_CLASS_LOADER = cl;
}
/**
* Called when an element starts.
*/
@@ -375,7 +367,7 @@ public class CompletionXMLParser extends DefaultHandler {
}
}
}
@Override
public void warning(SAXParseException e) throws SAXException {
throw e;
@@ -28,29 +28,29 @@ import org.xml.sax.SAXException;
/**
* A basic completion provider implementation. This provider has no
* understanding of language semantics. It simply checks the text entered up
* to the caret position for a match against known completions. This is all
* that is needed in the majority of cases.
* understanding of language semantics. It simply checks the text entered up to
* the caret position for a match against known completions. This is all that is
* needed in the majority of cases.
*
* @author Robert Futrell
* @version 1.0
*/
public class DefaultCompletionProvider extends AbstractCompletionProvider {
protected Segment seg;
/**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}.
*/
private String lastCompletionsAtText;
/**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)},
* since this may be called multiple times in succession (this is usually
* called by <tt>JTextComponent.getToolTipText()</tt>, and if the user
* wiggles the mouse while a tool tip is displayed, this method gets
* repeatedly called. It can be costly so we try to speed it up a tad).
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}, since this
* may be called multiple times in succession (this is usually called by
* <tt>JTextComponent.getToolTipText()</tt>, and if the user wiggles the mouse
* while a tool tip is displayed, this method gets repeatedly called. It can be
* costly so we try to speed it up a tad).
*/
private List<Completion> lastParameterizedCompletionsAt;
/**
* Constructor. The returned provider will not be aware of any completions.
*
@@ -59,28 +59,27 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
public DefaultCompletionProvider() {
init();
}
/**
* Creates a completion provider that provides completion for a simple
* list of words.
* Creates a completion provider that provides completion for a simple list of
* words.
*
* @param words
* The words to offer as completion suggestions. If this is
* <code>null</code>, no completions will be known.
* @param words The words to offer as completion suggestions. If this is
* <code>null</code>, no completions will be known.
* @see BasicCompletion
*/
public DefaultCompletionProvider(String[] words) {
init();
addWordCompletions(words);
}
/**
* Returns the text just before the current caret position that could be
* the start of something auto-completable.
* Returns the text just before the current caret position that could be the
* start of something auto-completable.
* <p>
*
* This method returns all characters before the caret that are matched
* by {@link #isValidChar(char)}.
* This method returns all characters before the caret that are matched by
* {@link #isValidChar(char)}.
*
* {@inheritDoc}
*/
@@ -108,7 +107,7 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
len = segEnd - start;
return len == 0 ? EMPTY_STRING : new String(seg.array, start, len);
}
/**
* {@inheritDoc}
*/
@@ -156,7 +155,7 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
lastCompletionsAtText = null;
return lastParameterizedCompletionsAt = null;
}
/**
* {@inheritDoc}
*/
@@ -212,36 +211,33 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
}
return list;
}
/**
* Initializes this completion provider.
*/
protected void init() {
seg = new Segment();
}
/**
* Returns whether the specified character is valid in an auto-completion.
* The default implementation is equivalent to
* "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses
* can override this method to change what characters are matched.
* Returns whether the specified character is valid in an auto-completion. The
* default implementation is equivalent to
* "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses can
* override this method to change what characters are matched.
*
* @param ch
* The character.
* @param ch The character.
* @return Whether the character is valid.
*/
protected boolean isValidChar(char ch) {
return Character.isLetterOrDigit(ch) || ch == '_';
}
/**
* Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param file
* An XML file to load from.
* @throws IOException
* If an IO error occurs.
* @param file An XML file to load from.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(File file) throws IOException {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file));
@@ -251,34 +247,28 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
bin.close();
}
}
/**
* Loads completions from an XML input stream. The XML should validate
* against <code>CompletionXml.dtd</code>.
* Loads completions from an XML input stream. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param in
* The input stream to read from.
* @throws IOException
* If an IO error occurs.
* @param in The input stream to read from.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(InputStream in) throws IOException {
loadFromXML(in, null);
}
/**
* Loads completions from an XML input stream. The XML should validate
* against <code>CompletionXml.dtd</code>.
* Loads completions from an XML input stream. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param in
* The input stream to read from.
* @param cl
* The class loader to use when loading any extra classes defined
* in the XML, such as custom {@link FunctionCompletion}s. This
* may be <code>null</code> if the default is to be used, or if
* no
* custom completions are defined in the XML.
* @throws IOException
* If an IO error occurs.
* @param in The input stream to read from.
* @param cl The class loader to use when loading any extra classes defined in
* the XML, such as custom {@link FunctionCompletion}s. This may be
* <code>null</code> if the default is to be used, or if no custom
* completions are defined in the XML.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(InputStream in, ClassLoader cl) throws IOException {
// long start = System.currentTimeMillis();
@@ -309,15 +299,13 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
bin.close();
}
}
/**
* Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param resource
* A resource the current ClassLoader can get to.
* @throws IOException
* If an IO error occurs.
* @param resource A resource the current ClassLoader can get to.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(String resource) throws IOException {
ClassLoader cl = getClass().getClassLoader();
@@ -15,22 +15,22 @@ import javax.swing.JList;
import javax.swing.ListCellRenderer;
/**
* List cell renderer that delegates to a {@link CompletionProvider}'s
* renderer, if it has one. If it doesn't, it calls into a fallback renderer.
* If a fallback renderer isn't specified, it simply renders
* List cell renderer that delegates to a {@link CompletionProvider}'s renderer,
* if it has one. If it doesn't, it calls into a fallback renderer. If a
* fallback renderer isn't specified, it simply renders
* <code>(({@link Completion})value).toString()</code>.
*
* @author Robert Futrell
* @version 1.0
*/
class DelegatingCellRenderer extends DefaultListCellRenderer {
/**
* The renderer to fall back on if one isn't specified by a provider.
* This is usually <tt>this</tt>.
* The renderer to fall back on if one isn't specified by a provider. This is
* usually <tt>this</tt>.
*/
private ListCellRenderer fallback;
/**
* Returns the fallback cell renderer.
*
@@ -40,12 +40,13 @@ class DelegatingCellRenderer extends DefaultListCellRenderer {
public ListCellRenderer getFallbackCellRenderer() {
return fallback;
}
/**
* {@inheritDoc}
*/
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected,
boolean hasFocus) {
Completion c = (Completion) value;
CompletionProvider p = c.getProvider();
ListCellRenderer r = p.getListCellRenderer();
@@ -57,19 +58,18 @@ class DelegatingCellRenderer extends DefaultListCellRenderer {
}
return fallback.getListCellRendererComponent(list, value, index, selected, hasFocus);
}
/**
* Sets the fallback cell renderer.
*
* @param fallback
* The fallback cell renderer. If this is
* <code>null</code>, <tt>this</tt> will be used.
* @param fallback The fallback cell renderer. If this is <code>null</code>,
* <tt>this</tt> will be used.
* @see #getFallbackCellRenderer()
*/
public void setFallbackCellRenderer(ListCellRenderer fallback) {
this.fallback = fallback;
}
/**
* {@inheritDoc}
*/
@@ -14,15 +14,13 @@ package org.fife.ui.autocomplete;
* @see ExternalURLHandler
*/
public interface DescWindowCallback {
/**
* Callback allowing a new code completion's description to be displayed
* in the description window.
* Callback allowing a new code completion's description to be displayed in the
* description window.
*
* @param completion
* The new completion.
* @param anchor
* The anchor to scroll to, or <code>null</code> if none.
* @param completion The new completion.
* @param anchor The anchor to scroll to, or <code>null</code> if none.
*/
public void showSummaryFor(Completion completion, String anchor);
}
+7 -6
View File
@@ -21,23 +21,24 @@ import javax.swing.Icon;
* @version 1.0
*/
public class EmptyIcon implements Icon, Serializable {
private int size;
public EmptyIcon(int size) {
this.size = size;
}
@Override
public int getIconHeight() {
return size;
}
@Override
public int getIconWidth() {
return size;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {}
public void paintIcon(Component c, Graphics g, int x, int y) {
}
}
@@ -10,12 +10,12 @@ package org.fife.ui.autocomplete;
import javax.swing.event.HyperlinkEvent;
/**
* A callback for when an external URL is clicked in the description window.
* If no handler is installed, and if running in Java 6, the system default
* web browser is used to open the URL. If not running Java 6, nothing will
* happen. If you want browser support for pre-Java 6 JRE's, you will need
* to register one of these callbacks on your {@link AutoCompletion}, and
* open the URL in a web browser yourself.
* A callback for when an external URL is clicked in the description window. If
* no handler is installed, and if running in Java 6, the system default web
* browser is used to open the URL. If not running Java 6, nothing will happen.
* If you want browser support for pre-Java 6 JRE's, you will need to register
* one of these callbacks on your {@link AutoCompletion}, and open the URL in a
* web browser yourself.
* <p>
*
* Alternatively, folks implementing robust code completion support for a
@@ -27,17 +27,13 @@ import javax.swing.event.HyperlinkEvent;
* @see AutoCompletion#setExternalURLHandler(ExternalURLHandler)
*/
public interface ExternalURLHandler {
/**
* Called when an external URL is clicked in the description window.
*
* @param e
* The event containing the hyperlink clicked.
* @param c
* The completion currently being displayed.
* @param callback
* Allows you to display new content in the description
* window.
* @param e The event containing the hyperlink clicked.
* @param c The completion currently being displayed.
* @param callback Allows you to display new content in the description window.
*/
public void urlClicked(HyperlinkEvent e, Completion c, DescWindowCallback callback);
}
+27 -29
View File
@@ -20,37 +20,37 @@ import javax.swing.plaf.basic.BasicListUI;
/**
* A custom list UI, used by the completion choices list. If the number of
* completion choices is "large," it does a fast estimate of the preferred
* width and height of list items. This allows HTML renderers to be used (such
* as {@link CompletionCellRenderer}), with thousands of completion choices,
* with no performance penalty. With standard BasicListUI subclasses, this can
* cause very poor performance <b>each time</b> the list is displayed, which
* is bad for lists that are repeatedly hidden and re-displayed, such as
* completion choices. This is all because the calculation to get the
* preferred size of each list item, when it is displayed with HTML, is slow.
* completion choices is "large," it does a fast estimate of the preferred width
* and height of list items. This allows HTML renderers to be used (such as
* {@link CompletionCellRenderer}), with thousands of completion choices, with
* no performance penalty. With standard BasicListUI subclasses, this can cause
* very poor performance <b>each time</b> the list is displayed, which is bad
* for lists that are repeatedly hidden and re-displayed, such as completion
* choices. This is all because the calculation to get the preferred size of
* each list item, when it is displayed with HTML, is slow.
*
* @author Robert Futrell
* @version 1.0
*/
class FastListUI extends BasicListUI {
/**
* Whether the selection background was overridden (usually because of
* Nimbus) so we know to manually uninstall the color we installed.
* Whether the selection background was overridden (usually because of Nimbus)
* so we know to manually uninstall the color we installed.
*/
private boolean overriddenBackground;
/**
* Whether the selection foreground was overridden (usually because of
* Nimbus) so we know to manually uninstall the color we installed.
* Whether the selection foreground was overridden (usually because of Nimbus)
* so we know to manually uninstall the color we installed.
*/
private boolean overriddenForeground;
/**
* If there are more than this many completions in a single list, this
* UI will estimate the cell width and height needed for each item instead
* of computing it, for performance reasons.
* If there are more than this many completions in a single list, this UI will
* estimate the cell width and height needed for each item instead of computing
* it, for performance reasons.
*/
private static final int ESTIMATION_THRESHOLD = 200;
private Color determineSelectionBackground() {
Color c = UIManager.getColor("List.selectionBackground");
if (c == null) {
@@ -68,7 +68,7 @@ class FastListUI extends BasicListUI {
// actually be a ColorUIResource
return new Color(c.getRGB());// new ColorUIResource(c);
}
private Color determineSelectionForeground() {
Color c = UIManager.getColor("List.selectionForeground");
if (c == null) {
@@ -84,12 +84,11 @@ class FastListUI extends BasicListUI {
// may actually be a ColorUIResource
return new Color(c.getRGB());
}
/**
* Overridden to ensure we have selection background/foreground colors
* defined, even if we're in some weirdo LAF such as Nimbus which doesn't
* define them. Since FastListUI extends BasicListUI, we need these values
* to be defined.
* Overridden to ensure we have selection background/foreground colors defined,
* even if we're in some weirdo LAF such as Nimbus which doesn't define them.
* Since FastListUI extends BasicListUI, we need these values to be defined.
*/
@Override
protected void installDefaults() {
@@ -103,7 +102,7 @@ class FastListUI extends BasicListUI {
overriddenForeground = true;
}
}
/**
* Overridden to work around a Nimbus issue.
*/
@@ -117,12 +116,11 @@ class FastListUI extends BasicListUI {
list.setSelectionForeground(null);
}
}
/**
* Recalculates the cell width and height of each cell in the list. This
* method is overridden to do a fast estimation if the completion list is
* too long, to improve performance for lists with huge amounts of
* completions.
* Recalculates the cell width and height of each cell in the list. This method
* is overridden to do a fast estimation if the completion list is too long, to
* improve performance for lists with huge amounts of completions.
*/
@Override
protected void updateLayoutState() {
@@ -20,7 +20,7 @@ import javax.swing.text.Position;
* @version 1.0
*/
public class FunctionCompletion extends VariableCompletion implements ParameterizedCompletion {
/**
* Parameters to the function.
*/
@@ -33,33 +33,29 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
* Used to improve performance of sorting FunctionCompletions.
*/
private String compareString;
/**
* Constructor.
*
* @param provider
* The parent provider.
* @param name
* The name of this function.
* @param returnType
* The return type of this function.
* @param provider The parent provider.
* @param name The name of this function.
* @param returnType The return type of this function.
*/
public FunctionCompletion(CompletionProvider provider, String name, String returnType) {
super(provider, name, returnType);
}
@Override
protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>");
sb.append(getDefinitionString());
sb.append("</b>");
}
/**
* Adds HTML describing the parameters to this function to a buffer.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void addParameters(StringBuilder sb) {
// TODO: Localize me
@@ -86,12 +82,11 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
sb.append("</td></tr></table></center><br><br>");
}
}
/**
* Overridden to compare methods by their comparison strings.
*
* @param c2
* A <code>Completion</code> to compare to.
* @param c2 A <code>Completion</code> to compare to.
* @return The sort order.
*/
@Override
@@ -106,7 +101,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
}
return rc;
}
/**
* Returns a string used to compare this method completion to another.
*
@@ -114,11 +109,9 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
*/
private String getCompareString() {
/*
* This string compares the following parts of methods in this order,
* to optimize sort order in completion lists.
* 1. First, by name
* 2. Next, by number of parameters.
* 3. Finally, by parameter type.
* This string compares the following parts of methods in this order, to
* optimize sort order in completion lists. 1. First, by name 2. Next, by number
* of parameters. 3. Finally, by parameter type.
*/
if (compareString == null) {
StringBuilder sb = new StringBuilder(getName());
@@ -139,10 +132,10 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
}
return compareString;
}
/**
* Returns the "definition string" for this function completion. For
* example, for the C "<code>printf</code>" function, this would return
* Returns the "definition string" for this function completion. For example,
* for the C "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>".
*
* @return The definition string.
@@ -186,7 +179,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
}
return sb.toString();
}
@Override
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces) {
ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo();
@@ -238,7 +231,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
info.setTextToInsert(sb.toString());
return info;
}
/**
* {@inheritDoc}
*/
@@ -246,7 +239,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public Parameter getParam(int index) {
return params.get(index);
}
/**
* Returns the number of parameters to this function.
*
@@ -257,7 +250,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public int getParamCount() {
return params == null ? 0 : params.size();
}
/**
* {@inheritDoc}
*/
@@ -265,12 +258,11 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public boolean getShowParameterToolTip() {
return true;
}
/**
* Returns the text to insert for a parameter.
*
* @param param
* The parameter.
* @param param The parameter.
* @return The text.
*/
private String getParamText(ParameterizedCompletion.Parameter param) {
@@ -283,7 +275,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
}
return text;
}
/**
* Returns the description of the return value of this function.
*
@@ -293,7 +285,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public String getReturnValueDescription() {
return returnValDesc;
}
/**
* {@inheritDoc}
*/
@@ -308,7 +300,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
possiblyAddDefinedIn(sb);
return sb.toString();
}
/**
* {@inheritDoc}
*/
@@ -320,13 +312,12 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
}
return text;
}
/**
* Sets the parameters to this function.
*
* @param params
* The parameters. This should be a list of
* {@link ParameterizedCompletion.Parameter}s.
* @param params The parameters. This should be a list of
* {@link ParameterizedCompletion.Parameter}s.
* @see #getParam(int)
* @see #getParamCount()
*/
@@ -336,12 +327,11 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
this.params = new ArrayList<Parameter>(params);
}
}
/**
* Sets the description of the return value of this function.
*
* @param desc
* The description.
* @param desc The description.
* @see #getReturnValueDescription()
*/
public void setReturnValueDescription(String desc) {
@@ -33,13 +33,13 @@ import org.fife.ui.rtextarea.ToolTipSupplier;
* <li>A documentation comment</li>
* </ul>
*
* This allows for different completion choices in comments than in code,
* for example.
* This allows for different completion choices in comments than in code, for
* example.
* <p>
*
* This provider also implements the
* <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it
* to display tooltips for completion choices. Thus the standard
* <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it to
* display tooltips for completion choices. Thus the standard
* {@link VariableCompletion} and {@link FunctionCompletion} completions should
* be able to display tooltips with the variable declaration or function
* definition (provided the <tt>RSyntaxTextArea</tt> was registered with the
@@ -49,10 +49,9 @@ import org.fife.ui.rtextarea.ToolTipSupplier;
* @version 1.0
*/
public class LanguageAwareCompletionProvider extends CompletionProviderBase implements ToolTipSupplier {
/**
* The provider to use when no provider is assigned to a particular token
* type.
* The provider to use when no provider is assigned to a particular token type.
*/
private CompletionProvider defaultProvider;
/**
@@ -67,41 +66,40 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
* The provider to use while in documentation comments.
*/
private CompletionProvider docCommentCompletionProvider;
/**
* Constructor subclasses can use when they don't have their default
* provider created at construction time. They should call
* Constructor subclasses can use when they don't have their default provider
* created at construction time. They should call
* {@link #setDefaultCompletionProvider(CompletionProvider)} in this
* constructor.
*/
protected LanguageAwareCompletionProvider() {}
protected LanguageAwareCompletionProvider() {
}
/**
* Constructor.
*
* @param defaultProvider
* The provider to use when no provider is assigned
* to a particular token type. This cannot be <code>null</code>.
* @param defaultProvider The provider to use when no provider is assigned to a
* particular token type. This cannot be
* <code>null</code>.
*/
public LanguageAwareCompletionProvider(CompletionProvider defaultProvider) {
setDefaultCompletionProvider(defaultProvider);
}
/**
* Calling this method will result in an
* {@link UnsupportedOperationException} being thrown. To set the
* parameter completion parameters, do so on the provider returned by
* {@link #getDefaultCompletionProvider()}.
* Calling this method will result in an {@link UnsupportedOperationException}
* being thrown. To set the parameter completion parameters, do so on the
* provider returned by {@link #getDefaultCompletionProvider()}.
*
* @throws UnsupportedOperationException
* Always.
* @throws UnsupportedOperationException Always.
* @see #setParameterizedCompletionParams(char, String, char)
*/
@Override
public void clearParameterizedCompletionParams() {
throw new UnsupportedOperationException();
}
/**
* {@inheritDoc}
*/
@@ -113,7 +111,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
CompletionProvider provider = getProviderFor(comp);
return provider != null ? provider.getAlreadyEnteredText(comp) : null;
}
/**
* Returns the completion provider to use for comments.
*
@@ -123,7 +121,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getCommentCompletionProvider() {
return commentCompletionProvider;
}
/**
* {@inheritDoc}
*/
@@ -131,14 +129,12 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public List<Completion> getCompletionsAt(JTextComponent tc, Point p) {
return defaultProvider == null ? null : defaultProvider.getCompletionsAt(tc, p);
}
/**
* Does the dirty work of creating a list of completions.
*
* @param comp
* The text component to look in.
* @return The list of possible completions, or an empty list if there
* are none.
* @param comp The text component to look in.
* @return The list of possible completions, or an empty list if there are none.
*/
@Override
protected List<Completion> getCompletionsImpl(JTextComponent comp) {
@@ -150,10 +146,10 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
}
return Collections.emptyList();
}
/**
* Returns the completion provider used when one isn't defined for a
* particular token type.
* Returns the completion provider used when one isn't defined for a particular
* token type.
*
* @return The completion provider to use.
* @see #setDefaultCompletionProvider(CompletionProvider)
@@ -161,7 +157,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getDefaultCompletionProvider() {
return defaultProvider;
}
/**
* Returns the completion provider to use for documentation comments.
*
@@ -171,7 +167,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getDocCommentCompletionProvider() {
return docCommentCompletionProvider;
}
/**
* {@inheritDoc}
*/
@@ -183,7 +179,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
CompletionProvider provider = getProviderFor(tc);
return provider == defaultProvider ? provider.getParameterizedCompletions(tc) : null;
}
/**
* {@inheritDoc}
*/
@@ -191,7 +187,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public char getParameterListEnd() {
return defaultProvider.getParameterListEnd();
}
/**
* {@inheritDoc}
*/
@@ -199,7 +195,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public String getParameterListSeparator() {
return defaultProvider.getParameterListSeparator();
}
/**
* {@inheritDoc}
*/
@@ -207,13 +203,12 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public char getParameterListStart() {
return defaultProvider.getParameterListStart();
}
/**
* Returns the completion provider to use at the current caret position in
* a text component.
* Returns the completion provider to use at the current caret position in a
* text component.
*
* @param comp
* The text component to check.
* @param comp The text component to check.
* @return The completion provider to use.
*/
private CompletionProvider getProviderFor(JTextComponent comp) {
@@ -242,15 +237,15 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
type = doc.getClosestStandardTokenTypeForInternalType(type);
}
switch (type) {
case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider();
case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider();
default:
return getDefaultCompletionProvider();
case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider();
case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider();
default:
return getDefaultCompletionProvider();
}
}
// FIXME: This isn't always a safe assumption.
@@ -261,27 +256,27 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
return getDefaultCompletionProvider();
}
switch (curToken.getType()) {
case Token.LITERAL_STRING_DOUBLE_QUOTE:
case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider();
case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider();
case Token.NULL:
case Token.WHITESPACE:
case Token.IDENTIFIER:
case Token.VARIABLE:
case Token.PREPROCESSOR:
case Token.DATA_TYPE:
case Token.FUNCTION:
case Token.OPERATOR:
return getDefaultCompletionProvider();
case Token.LITERAL_STRING_DOUBLE_QUOTE:
case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider();
case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider();
case Token.NULL:
case Token.WHITESPACE:
case Token.IDENTIFIER:
case Token.VARIABLE:
case Token.PREPROCESSOR:
case Token.DATA_TYPE:
case Token.FUNCTION:
case Token.OPERATOR:
return getDefaultCompletionProvider();
}
return null; // In a token type we can't auto-complete from.
}
/**
* Returns the completion provider to use for strings.
*
@@ -291,7 +286,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getStringCompletionProvider() {
return stringCompletionProvider;
}
/**
* {@inheritDoc}
*/
@@ -300,24 +295,22 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
CompletionProvider provider = getProviderFor(tc);
return provider != null ? provider.isAutoActivateOkay(tc) : false;
}
/**
* Sets the comment completion provider.
*
* @param provider
* The provider to use in comments.
* @param provider The provider to use in comments.
* @see #getCommentCompletionProvider()
*/
public void setCommentCompletionProvider(CompletionProvider provider) {
this.commentCompletionProvider = provider;
}
/**
* Sets the default completion provider.
*
* @param provider
* The provider to use when no provider is assigned to a
* particular token type. This cannot be <code>null</code>.
* @param provider The provider to use when no provider is assigned to a
* particular token type. This cannot be <code>null</code>.
* @see #getDefaultCompletionProvider()
*/
public void setDefaultCompletionProvider(CompletionProvider provider) {
@@ -326,59 +319,53 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
}
this.defaultProvider = provider;
}
/**
* Sets the documentation comment completion provider.
*
* @param provider
* The provider to use in comments.
* @param provider The provider to use in comments.
* @see #getDocCommentCompletionProvider()
*/
public void setDocCommentCompletionProvider(CompletionProvider provider) {
this.docCommentCompletionProvider = provider;
}
/**
* Calling this method will result in an
* {@link UnsupportedOperationException} being thrown. To set the
* parameter completion parameters, do so on the provider returned by
* {@link #getDefaultCompletionProvider()}.
* Calling this method will result in an {@link UnsupportedOperationException}
* being thrown. To set the parameter completion parameters, do so on the
* provider returned by {@link #getDefaultCompletionProvider()}.
*
* @throws UnsupportedOperationException
* Always.
* @throws UnsupportedOperationException Always.
* @see #clearParameterizedCompletionParams()
*/
@Override
public void setParameterizedCompletionParams(char listStart, String separator, char listEnd) {
throw new UnsupportedOperationException();
}
/**
* Sets the completion provider to use while in a string.
*
* @param provider
* The provider to use.
* @param provider The provider to use.
* @see #getStringCompletionProvider()
*/
public void setStringCompletionProvider(CompletionProvider provider) {
stringCompletionProvider = provider;
}
/**
* Returns the tool tip to display for a mouse event.
* <p>
*
* For this method to be called, the <tt>RSyntaxTextArea</tt> must be
* registered with the <tt>javax.swing.ToolTipManager</tt> like so:
* For this method to be called, the <tt>RSyntaxTextArea</tt> must be registered
* with the <tt>javax.swing.ToolTipManager</tt> like so:
*
* <pre>
* ToolTipManager.sharedInstance().registerComponent(textArea);
* </pre>
*
* @param textArea
* The text area.
* @param e
* The mouse event.
* @param textArea The text area.
* @param e The mouse event.
* @return The tool tip text, or <code>null</code> if none.
*/
@Override
@@ -8,14 +8,13 @@ package org.fife.ui.autocomplete;
import java.net.URL;
/**
* Possibly redirects one URL to another. Useful if you want "external" URL's
* in code completion documentation to point to a local copy instead, for
* example.
* Possibly redirects one URL to another. Useful if you want "external" URL's in
* code completion documentation to point to a local copy instead, for example.
*
* @author Robert Futrell
* @version 1.0
*/
public interface LinkRedirector {
public URL possiblyRedirect(URL original);
}
@@ -19,7 +19,7 @@ import org.fife.ui.autocomplete.ParameterizedCompletion.Parameter;
* @version 1.0
*/
public class MarkupTagCompletion extends AbstractCompletion {
private String name;
private String desc;
private String definedIn;
@@ -27,25 +27,22 @@ public class MarkupTagCompletion extends AbstractCompletion {
* Attributes of the tag.
*/
private List<Parameter> attrs;
/**
* Constructor.
*
* @param provider
* The parent provider instance.
* @param name
* The name of the tag.
* @param provider The parent provider instance.
* @param name The name of the tag.
*/
public MarkupTagCompletion(CompletionProvider provider, String name) {
super(provider);
this.name = name;
}
/**
* Adds HTML describing the attributes of this tag to a buffer.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void addAttributes(StringBuilder sb) {
// TODO: Localize me.
@@ -67,11 +64,11 @@ public class MarkupTagCompletion extends AbstractCompletion {
sb.append("</td></tr></table></center><br><br>");
}
}
protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>").append(name).append("</b>");
}
/**
* Returns all attributes of this tag.
*
@@ -82,19 +79,18 @@ public class MarkupTagCompletion extends AbstractCompletion {
public List<Parameter> getAttributes() {
return attrs;
}
/**
* Returns the specified {@link ParameterizedCompletion.Parameter}.
*
* @param index
* The index of the attribute to retrieve.
* @param index The index of the attribute to retrieve.
* @return The attribute.
* @see #getAttributeCount()
*/
public Parameter getAttribute(int index) {
return attrs.get(index);
}
/**
* Returns the number of attributes of this tag.
*
@@ -104,7 +100,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
public int getAttributeCount() {
return attrs == null ? 0 : attrs.size();
}
/**
* Returns where this variable is defined.
*
@@ -114,19 +110,17 @@ public class MarkupTagCompletion extends AbstractCompletion {
public String getDefinedIn() {
return definedIn;
}
/**
* Returns a short description of this variable. This should be an
* HTML snippet.
* Returns a short description of this variable. This should be an HTML snippet.
*
* @return A short description of this variable. This may be
* <code>null</code>.
* @return A short description of this variable. This may be <code>null</code>.
* @see #setDescription(String)
*/
public String getDescription() {
return desc;
}
/**
* Returns the name of this tag.
*
@@ -135,7 +129,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
public String getName() {
return name;
}
/**
* {@inheritDoc}
*/
@@ -143,7 +137,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
public String getReplacementText() {
return getName();
}
/**
* {@inheritDoc}
*/
@@ -156,13 +150,12 @@ public class MarkupTagCompletion extends AbstractCompletion {
possiblyAddDefinedIn(sb);
return sb.toString();
}
/**
* Adds some HTML describing where this variable is defined, if this
* information is known.
* Adds some HTML describing where this variable is defined, if this information
* is known.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn != null) {
@@ -170,13 +163,11 @@ public class MarkupTagCompletion extends AbstractCompletion {
sb.append(" <em>").append(definedIn).append("</em>");
}
}
/**
* Adds the description text as HTML to a buffer, if a description is
* defined.
* Adds the description text as HTML to a buffer, if a description is defined.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void possiblyAddDescription(StringBuilder sb) {
if (desc != null) {
@@ -185,36 +176,31 @@ public class MarkupTagCompletion extends AbstractCompletion {
sb.append("<br><br><br>");
}
}
/**
* Sets where this variable is defined.
*
* @param definedIn
* Where this variable is defined.
* @param definedIn Where this variable is defined.
* @see #getDefinedIn()
*/
public void setDefinedIn(String definedIn) {
this.definedIn = definedIn;
}
/**
* Sets the short description of this tag. This should be an
* HTML snippet.
* Sets the short description of this tag. This should be an HTML snippet.
*
* @param desc
* A short description of this tag. This may be
* <code>null</code>.
* @param desc A short description of this tag. This may be <code>null</code>.
* @see #getDescription()
*/
public void setDescription(String desc) {
this.desc = desc;
}
/**
* Sets the attributes of this tag.
*
* @param attrs
* The attributes.
* @param attrs The attributes.
* @see #getAttribute(int)
* @see #getAttributeCount()
*/
@@ -19,40 +19,39 @@ import javax.swing.text.Position;
import javax.swing.text.View;
/**
* Highlight painter that draws an outline around the text. This is used to
* draw bounds around function/method parameters.
* Highlight painter that draws an outline around the text. This is used to draw
* bounds around function/method parameters.
*
* @author Robert Futrell
* @version 1.0
*/
/*
* NOTE: Whenever you see text like "Workaround for Java Highlight issues",
* this is because highlighted text in a JTextComponent gets "pushed" forward
* when the caret is at the Highlight's start, when we need it to instead get
* NOTE: Whenever you see text like "Workaround for Java Highlight issues", this
* is because highlighted text in a JTextComponent gets "pushed" forward when
* the caret is at the Highlight's start, when we need it to instead get
* prepended to. For this reason, the auto-complete package adds its Highlights
* 1 char too long (1 char earlier than where it should really start), but only
* paint the Highlight from the 2nd char on.
*/
class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter {
/**
* DefaultHighlightPainter doesn't allow changing color, so we must cache
* ours here.
* DefaultHighlightPainter doesn't allow changing color, so we must cache ours
* here.
*/
private Color color;
/**
* Constructor.
*
* @param color
* The color to draw the bounding boxes with. This cannot
* be <code>null</code>.
* @param color The color to draw the bounding boxes with. This cannot be
* <code>null</code>.
*/
public OutlineHighlightPainter(Color color) {
super(color);
setColor(color);
}
/**
* Returns the color to paint bounding boxes with.
*
@@ -63,7 +62,7 @@ class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter
public Color getColor() {
return color;
}
/**
* {@inheritDoc}
*/
@@ -110,12 +109,11 @@ class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter
return null;
}
}
/**
* Sets the color to paint the bounding boxes with.
*
* @param color
* The new color. This cannot be <code>null</code>.
* @param color The new color. This cannot be <code>null</code>.
* @see #getColor()
*/
public void setColor(Color color) {
@@ -12,25 +12,23 @@ import java.util.List;
import javax.swing.text.JTextComponent;
/**
* Provides completions for a {@link ParameterizedCompletion}'s parameters.
* So, for example, if the user code-completes a function or method, if
* a <code>ParameterChoicesProvider</code> is installed, it can return possible
* Provides completions for a {@link ParameterizedCompletion}'s parameters. So,
* for example, if the user code-completes a function or method, if a
* <code>ParameterChoicesProvider</code> is installed, it can return possible
* completions for the parameters to that function or method.
*
* @author Robert Futrell
* @version 1.0
*/
public interface ParameterChoicesProvider {
/**
* Returns a list of choices for a specific parameter.
*
* @param tc
* The text component.
* @param param
* The currently focused parameter.
* @return The list of parameters. This may be <code>null</code> for
* "no parameters," but might also be an empty list.
* @param tc The text component.
* @param param The currently focused parameter.
* @return The list of parameters. This may be <code>null</code> for "no
* parameters," but might also be an empty list.
*/
public List<Completion> getParameterChoices(JTextComponent tc, ParameterizedCompletion.Parameter param);
}
@@ -15,26 +15,25 @@ import javax.swing.text.JTextComponent;
* @version 1.0
*/
public interface ParameterizedCompletion extends Completion {
/**
* Returns the "definition string" for this completion. For example,
* for the C "<code>printf</code>" function, this would return
* Returns the "definition string" for this completion. For example, for the C
* "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>".
*
* @return The definition string.
*/
public String getDefinitionString();
/**
* Returns the specified {@link Parameter}.
*
* @param index
* The index of the parameter to retrieve.
* @param index The index of the parameter to retrieve.
* @return The parameter.
* @see #getParamCount()
*/
public Parameter getParam(int index);
/**
* Returns the number of parameters this completion takes.
*
@@ -42,82 +41,73 @@ public interface ParameterizedCompletion extends Completion {
* @see #getParam(int)
*/
public int getParamCount();
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces);
/**
* Returns whether a tool tip displaying assistance for each parameter
* while it is being edited is appropriate for this completion.
* Returns whether a tool tip displaying assistance for each parameter while it
* is being edited is appropriate for this completion.
*
* @return Whether the tool tip is appropriate to display.
*/
public boolean getShowParameterToolTip();
/**
* A parameter passed to a parameterized {@link Completion}.
*/
public static class Parameter {
private String name;
private Object type;
private String desc;
private boolean isEndParam;
/**
* Constructor.
*
* @param type
* The type of this parameter. This may be
* <code>null</code> for languages without specific types,
* dynamic typing, etc. Usually you'll pass a String for this
* value, but you may pass any object representing a type in
* your language, as long as its <code>toString()</code>
* method
* returns a string representation of the type.
* @param name
* The name of the parameter.
* @param type The type of this parameter. This may be <code>null</code> for
* languages without specific types, dynamic typing, etc. Usually
* you'll pass a String for this value, but you may pass any object
* representing a type in your language, as long as its
* <code>toString()</code> method returns a string representation of
* the type.
* @param name The name of the parameter.
*/
public Parameter(Object type, String name) {
this(type, name, false);
}
/**
* Constructor.
*
* @param type
* The type of this parameter. This may be
* <code>null</code> for languages without specific types,
* dynamic typing, etc. Usually you'll pass a String for this
* value, but you may pass any object representing a type in
* your language, as long as its <code>toString()</code>
* method
* returns a string representation of the type.
* @param name
* The name of the parameter.
* @param endParam
* Whether this parameter is an "ending parameter;"
* that is, whether this parameter is at a logical "ending
* point" in the completion text. If the user types in a
* parameter that is an ending point, parameter completion
* mode
* terminates. Set this to <code>true</code> for a trailing
* parameter after a function call's closing ')', for
* example.
* @param type The type of this parameter. This may be <code>null</code> for
* languages without specific types, dynamic typing, etc.
* Usually you'll pass a String for this value, but you may pass
* any object representing a type in your language, as long as
* its <code>toString()</code> method returns a string
* representation of the type.
* @param name The name of the parameter.
* @param endParam Whether this parameter is an "ending parameter;" that is,
* whether this parameter is at a logical "ending point" in the
* completion text. If the user types in a parameter that is an
* ending point, parameter completion mode terminates. Set this
* to <code>true</code> for a trailing parameter after a
* function call's closing ')', for example.
*/
public Parameter(Object type, String name, boolean endParam) {
this.name = name;
this.type = type;
this.isEndParam = endParam;
}
public String getDescription() {
return desc;
}
public String getName() {
return name;
}
/**
* Returns the type of this parameter, as a string.
*
@@ -126,7 +116,7 @@ public interface ParameterizedCompletion extends Completion {
public String getType() {
return type == null ? null : type.toString();
}
/**
* Returns the object used to describe the type of this parameter.
*
@@ -135,22 +125,21 @@ public interface ParameterizedCompletion extends Completion {
public Object getTypeObject() {
return type;
}
/**
* @return Whether this parameter is an "ending parameter;"
* that is, whether this parameter is at a logical "ending
* point" in the completion text. If the user types in a
* parameter that is an ending point, parameter completion mode
* terminates.
* @return Whether this parameter is an "ending parameter;" that is, whether
* this parameter is at a logical "ending point" in the completion text.
* If the user types in a parameter that is an ending point, parameter
* completion mode terminates.
*/
public boolean isEndParam() {
return isEndParam;
}
public void setDescription(String desc) {
this.desc = desc;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -28,17 +28,17 @@ import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* A small popup window offering a list of likely choices for a parameter
* when the user has code-completed a parameterized completion. For example,
* if they have just code-completed the C function "<code>fprintf</code>",
* when entering the file name, this popup might display all local variables
* of type "<code>char *</code>".
* A small popup window offering a list of likely choices for a parameter when
* the user has code-completed a parameterized completion. For example, if they
* have just code-completed the C function "<code>fprintf</code>", when entering
* the file name, this popup might display all local variables of type
* "<code>char *</code>".
*
* @author Robert Futrell
* @version 1.0
*/
public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* The parent AutoCompletion instance.
*/
@@ -60,22 +60,20 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
*/
private JScrollPane sp;
/**
* Comparator used to sort completions by their relevance before sorting
* them lexicographically.
* Comparator used to sort completions by their relevance before sorting them
* lexicographically.
*/
private static final Comparator<Completion> sortByRelevanceComparator = new SortByRelevanceComparator();
/**
* Constructor.
*
* @param parent
* The parent window (hosting the text component).
* @param ac
* The auto-completion instance.
* @param context
* The completion context.
* @param parent The parent window (hosting the text component).
* @param ac The auto-completion instance.
* @param context The completion context.
*/
public ParameterizedCompletionChoicesWindow(Window parent, AutoCompletion ac, final ParameterizedCompletionContext context) {
public ParameterizedCompletionChoicesWindow(Window parent, AutoCompletion ac,
final ParameterizedCompletionContext context) {
super(parent);
this.ac = ac;
ComponentOrientation o = ac.getTextComponentOrientation();
@@ -85,7 +83,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
list.setCellRenderer(ac.getParamChoicesRenderer());
}
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
@@ -103,23 +101,21 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
decorator.decorate(this);
}
}
/**
* Returns the selected value.
*
* @return The selected value, or <code>null</code> if nothing is
* selected.
* @return The selected value, or <code>null</code> if nothing is selected.
*/
public String getSelectedChoice() {
Completion c = (Completion) list.getSelectedValue();
return c == null ? null : c.toString();
}
/**
* Changes the selected index.
*
* @param amount
* The amount by which to change the selected index.
* @param amount The amount by which to change the selected index.
*/
public void incSelection(int amount) {
int selection = list.getSelectedIndex();
@@ -133,14 +129,12 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
list.setSelectedIndex(selection);
list.ensureIndexIsVisible(selection);
}
/**
* Initializes this window to offer suggestions for the parameters of
* a specific completion.
* Initializes this window to offer suggestions for the parameters of a specific
* completion.
*
* @param pc
* The completion whose parameters we should offer suggestions
* for.
* @param pc The completion whose parameters we should offer suggestions for.
*/
public void initialize(ParameterizedCompletion pc) {
CompletionProvider provider = pc.getProvider();
@@ -158,12 +152,11 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
choicesListList.add(choices);
}
}
/**
* Sets the location of this window relative to the given rectangle.
*
* @param r
* The visual position of the caret (in screen coordinates).
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
@@ -187,18 +180,16 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
}
setLocation(x, y);
}
/**
* Displays the choices for the specified parameter matching the given
* text. This will display or hide this popup window as necessary.
* Displays the choices for the specified parameter matching the given text.
* This will display or hide this popup window as necessary.
*
* @param param
* The index of the parameter the caret is currently in.
* This may be <code>-1</code> if not in a parameter (i.e., on
* the comma between parameters).
* @param prefix
* Text in the parameter before the dot. This may
* be <code>null</code> to represent the empty string.
* @param param The index of the parameter the caret is currently in. This may
* be <code>-1</code> if not in a parameter (i.e., on the comma
* between parameters).
* @param prefix Text in the parameter before the dot. This may be
* <code>null</code> to represent the empty string.
*/
public void setParameter(int param, String prefix) {
model.clear();
@@ -250,12 +241,11 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
setVisible(false);
}
}
/**
* Toggles the visibility of this popup window.
*
* @param visible
* Whether this window should be visible.
* @param visible Whether this window should be visible.
*/
@Override
public void setVisible(boolean visible) {
@@ -268,7 +258,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
super.setVisible(visible);
}
}
/**
* Updates the <tt>LookAndFeel</tt> of this window.
*/
@@ -48,8 +48,8 @@ import org.fife.ui.rtextarea.ChangeableHighlightPainter;
/**
* Manages UI and state specific to parameterized completions - the parameter
* description tool tip, the parameter completion choices list, the actual
* highlights in the editor, etc. This component installs new key bindings
* when appropriate to allow the user to cycle through the parameters of the
* highlights in the editor, etc. This component installs new key bindings when
* appropriate to allow the user to cycle through the parameters of the
* completion, and optionally cycle through completion choices for those
* parameters.
*
@@ -57,7 +57,7 @@ import org.fife.ui.rtextarea.ChangeableHighlightPainter;
* @version 1.0
*/
class ParameterizedCompletionContext {
/**
* The parent window.
*/
@@ -95,13 +95,13 @@ class ParameterizedCompletionContext {
*/
private Listener listener;
/**
* The minimum offset into the document that the caret can move to
* before this tool tip disappears.
* The minimum offset into the document that the caret can move to before this
* tool tip disappears.
*/
private int minPos;
/**
* The maximum offset into the document that the caret can move to
* before this tool tip disappears.
* The maximum offset into the document that the caret can move to before this
* tool tip disappears.
*/
private Position maxPos; // Moves with text inserted.
private Position defaultEndOffs;
@@ -140,7 +140,7 @@ class ParameterizedCompletionContext {
private static final String IM_KEY_ESCAPE = "ParamCompKey.Escape";
private static final String IM_KEY_ENTER = "ParamCompKey.Enter";
private static final String IM_KEY_CLOSING = "ParamCompKey.Closing";
/**
* Constructor.
*/
@@ -156,7 +156,7 @@ class ParameterizedCompletionContext {
tags = new ArrayList<Object>(1); // Usually small
paramCopyInfos = new ArrayList<ParamCopyInfo>(1);
}
/**
* Activates parameter completion support.
*
@@ -195,7 +195,7 @@ class ParameterizedCompletionContext {
prepareParamChoicesWindow();
paramChoicesWindow.setVisible(true);
}
/**
* Creates the completion window offering suggestions for parameters.
*
@@ -206,10 +206,9 @@ class ParameterizedCompletionContext {
pcw.initialize(pc);
return pcw;
}
/**
* Hides any popup windows and terminates parameterized completion
* assistance.
* Hides any popup windows and terminates parameterized completion assistance.
*
* @see #activate()
*/
@@ -226,15 +225,13 @@ class ParameterizedCompletionContext {
paramChoicesWindow.setVisible(false);
}
}
/**
* Returns the text inserted for the parameter containing the specified
* offset.
* Returns the text inserted for the parameter containing the specified offset.
*
* @param offs
* The offset into the document.
* @return The text of the parameter containing the offset, or
* <code>null</code> if the offset is not in a parameter.
* @param offs The offset into the document.
* @return The text of the parameter containing the offset, or <code>null</code>
* if the offset is not in a parameter.
*/
public String getArgumentText(int offs) {
List<Highlight> paramHighlights = getParameterHighlights();
@@ -258,12 +255,12 @@ class ParameterizedCompletionContext {
}
return null;
}
/**
* Returns the highlight of the current parameter.
*
* @return The current parameter's highlight, or <code>null</code> if
* the caret is not in a parameter's bounds.
* @return The current parameter's highlight, or <code>null</code> if the caret
* is not in a parameter's bounds.
* @see #getCurrentParameterStartOffset()
*/
private Highlight getCurrentParameterHighlight() {
@@ -280,7 +277,7 @@ class ParameterizedCompletionContext {
}
return null;
}
private int getCurrentParameterIndex() {
JTextComponent tc = ac.getTextComponent();
int dot = tc.getCaretPosition();
@@ -296,27 +293,26 @@ class ParameterizedCompletionContext {
}
return -1;
}
/**
* Returns the starting offset of the current parameter.
*
* @return The current parameter's starting offset, or <code>-1</code> if
* the caret is not in a parameter's bounds.
* @return The current parameter's starting offset, or <code>-1</code> if the
* caret is not in a parameter's bounds.
* @see #getCurrentParameterHighlight()
*/
private int getCurrentParameterStartOffset() {
Highlight h = getCurrentParameterHighlight();
return h != null ? h.getStartOffset() + 1 : -1;
}
/**
* Returns the highlight from a list that comes "first" in a list. Even
* though most parameter highlights are ordered, sometimes they aren't
* (e.g. the "cursor" parameter in a template completion is always last,
* even though it can be anywhere in the template).
* Returns the highlight from a list that comes "first" in a list. Even though
* most parameter highlights are ordered, sometimes they aren't (e.g. the
* "cursor" parameter in a template completion is always last, even though it
* can be anywhere in the template).
*
* @param highlights
* The list of highlights. Assumed to be non-empty.
* @param highlights The list of highlights. Assumed to be non-empty.
* @return The highlight that comes first in the document.
* @see #getLastHighlight(List)
*/
@@ -332,15 +328,14 @@ class ParameterizedCompletionContext {
}
return first;
}
/**
* Returns the highlight from a list that comes "last" in that list. Even
* though most parameter highlights are ordered, sometimes they aren't
* (e.g. the "cursor" parameter in a template completion is always last,
* even though it can be anywhere in the template.
* Returns the highlight from a list that comes "last" in that list. Even though
* most parameter highlights are ordered, sometimes they aren't (e.g. the
* "cursor" parameter in a template completion is always last, even though it
* can be anywhere in the template.
*
* @param highlights
* The list of highlights. Assumed to be non-empty.
* @param highlights The list of highlights. Assumed to be non-empty.
* @return The highlight that comes last in the document.
* @see #getFirstHighlight(List)
*/
@@ -356,7 +351,7 @@ class ParameterizedCompletionContext {
}
return last;
}
public List<Highlight> getParameterHighlights() {
List<Highlight> paramHighlights = new ArrayList<Highlight>(2);
JTextComponent tc = ac.getTextComponent();
@@ -369,12 +364,12 @@ class ParameterizedCompletionContext {
}
return paramHighlights;
}
/**
* Inserts the choice selected in the parameter choices window.
*
* @return Whether the choice was inserted. This will be <code>false</code>
* if the window is not visible, or no choice is selected.
* @return Whether the choice was inserted. This will be <code>false</code> if
* the window is not visible, or no choice is selected.
*/
boolean insertSelectedChoice() {
if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) {
@@ -396,10 +391,10 @@ class ParameterizedCompletionContext {
}
return false;
}
/**
* Installs key bindings on the text component that facilitate the user
* editing this completion's parameters.
* Installs key bindings on the text component that facilitate the user editing
* this completion's parameters.
*
* @see #uninstallKeyBindings()
*/
@@ -447,7 +442,7 @@ class ParameterizedCompletionContext {
oldClosingAction = am.get(IM_KEY_CLOSING);
am.put(IM_KEY_CLOSING, new ClosingAction());
}
/**
* Moves to and selects the next parameter.
*
@@ -486,7 +481,7 @@ class ParameterizedCompletionContext {
tc.setSelectionEnd(currentNext.getEndOffset());
updateToolTipText(pos);
}
/**
* Moves to and selects the previous parameter.
*
@@ -507,8 +502,8 @@ class ParameterizedCompletionContext {
List<Highlight> highlights = getParameterHighlights();
for (int i = 0; i < highlights.size(); i++) {
Highlight h = highlights.get(i);
if (currentPrev == null || currentPrev.getStartOffset() >= dot
|| (h.getStartOffset() < selStart && (h.getStartOffset() > currentPrev.getStartOffset() || pos == lastSelectedParam))) {
if (currentPrev == null || currentPrev.getStartOffset() >= dot || (h.getStartOffset() < selStart
&& (h.getStartOffset() > currentPrev.getStartOffset() || pos == lastSelectedParam))) {
currentPrev = h;
pos = i;
}
@@ -533,7 +528,7 @@ class ParameterizedCompletionContext {
deactivate();
}
}
private void possiblyUpdateParamCopies(Document doc) {
int index = getCurrentParameterIndex();
// FunctionCompletions add an extra param at end of inserted text
@@ -567,7 +562,7 @@ class ParameterizedCompletionContext {
deactivate();
}
}
/**
* Updates the optional window listing likely completion choices,
*/
@@ -595,7 +590,7 @@ class ParameterizedCompletionContext {
paramChoicesWindow.setParameter(lastSelectedParam, paramPrefix);
}
}
/**
* Removes the bounding boxes around parameters.
*/
@@ -611,19 +606,16 @@ class ParameterizedCompletionContext {
}
paramCopyInfos.clear();
}
/**
* Replaces highlighted text with new text. Takes special care so that
* the highlight stays just around the newly-highlighted text, since
* Swing's <code>Highlight</code> classes are funny about insertions at
* their start offsets.
* Replaces highlighted text with new text. Takes special care so that the
* highlight stays just around the newly-highlighted text, since Swing's
* <code>Highlight</code> classes are funny about insertions at their start
* offsets.
*
* @param doc
* The document.
* @param h
* The highlight whose text to change.
* @param replacement
* The new text to be in the highlight.
* @param doc The document.
* @param h The highlight whose text to change.
* @param replacement The new text to be in the highlight.
* @return The replacement highlight for <code>h</code>.
*/
private Highlight replaceHighlightedText(Document doc, Highlight h, String replacement) {
@@ -646,7 +638,7 @@ class ParameterizedCompletionContext {
}
return null;
}
/**
* Removes the key bindings we installed.
*
@@ -682,11 +674,10 @@ class ParameterizedCompletionContext {
im.put(ks, oldClosingKey);
am.put(IM_KEY_CLOSING, oldClosingAction);
}
/**
* Updates the text in the tool tip to have the current parameter
* displayed in bold. The "current parameter" is determined from the
* current caret position.
* Updates the text in the tool tip to have the current parameter displayed in
* bold. The "current parameter" is determined from the current caret position.
*
* @return The "prefix" of text in the caret's parameter before the caret.
*/
@@ -718,7 +709,7 @@ class ParameterizedCompletionContext {
updateToolTipText(index);
return paramPrefix;
}
private void updateToolTipText(int selectedParam) {
if (selectedParam != lastSelectedParam) {
if (tip != null) {
@@ -727,7 +718,7 @@ class ParameterizedCompletionContext {
this.lastSelectedParam = selectedParam;
}
}
/**
* Updates the <code>LookAndFeel</code> of all popup windows this context
* manages.
@@ -740,12 +731,12 @@ class ParameterizedCompletionContext {
paramChoicesWindow.updateUI();
}
}
/**
* Called when the user presses Enter while entering parameters.
*/
private class GotoEndAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
// If the param choices window is visible and something is chosen,
@@ -771,19 +762,19 @@ class ParameterizedCompletionContext {
}
}
}
private Action getDefaultEnterAction(JTextComponent tc) {
ActionMap am = tc.getActionMap();
return am.get(DefaultEditorKit.insertBreakAction);
}
}
/**
* Called when the user types the character marking the closing of the
* parameter list, such as '<code>)</code>'.
* Called when the user types the character marking the closing of the parameter
* list, such as '<code>)</code>'.
*/
private class ClosingAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
JTextComponent tc = ac.getTextComponent();
@@ -812,7 +803,7 @@ class ParameterizedCompletionContext {
tc.replaceSelection(Character.toString(end));
}
}
public int getCount(String text, char ch) {
int count = 0;
int old = 0;
@@ -824,12 +815,12 @@ class ParameterizedCompletionContext {
return count;
}
}
/**
* Action performed when the user hits the escape key.
*/
private class HideAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
// On first escape press, if the param choices window is visible,
@@ -844,20 +835,19 @@ class ParameterizedCompletionContext {
}
}
}
/**
* Listens for various events in the text component while this tool tip
* is visible.
* Listens for various events in the text component while this tool tip is
* visible.
*/
private class Listener implements FocusListener, CaretListener, DocumentListener {
private boolean markOccurrencesEnabled;
/**
* Called when the text component's caret moves.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void caretUpdate(CaretEvent e) {
@@ -875,37 +865,36 @@ class ParameterizedCompletionContext {
prepareParamChoicesWindow();
}
}
@Override
public void changedUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {
}
/**
* Called when the text component gains focus.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void focusGained(FocusEvent e) {
// Do nothing
}
/**
* Called when the text component loses focus.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void focusLost(FocusEvent e) {
deactivate();
}
private void handleDocumentEvent(final DocumentEvent e) {
if (!ignoringDocumentEvents) {
ignoringDocumentEvents = true;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
possiblyUpdateParamCopies(e.getDocument());
@@ -914,17 +903,16 @@ class ParameterizedCompletionContext {
});
}
}
@Override
public void insertUpdate(DocumentEvent e) {
handleDocumentEvent(e);
}
/**
* Installs this listener onto a text component.
*
* @param tc
* The text component to install onto.
* @param tc The text component to install onto.
* @see #uninstall()
*/
public void install(JTextComponent tc) {
@@ -950,7 +938,8 @@ class ParameterizedCompletionContext {
}
for (int i = 0; i < info.getReplacementCopyCount(); i++) {
ReplacementCopy rc = info.getReplacementCopy(i);
paramCopyInfos.add(new ParamCopyInfo(rc.getId(), (Highlight) h.addHighlight(rc.getStart(), rc.getEnd(), paramCopyP)));
paramCopyInfos.add(new ParamCopyInfo(rc.getId(),
(Highlight) h.addHighlight(rc.getStart(), rc.getEnd(), paramCopyP)));
}
// Go back and start at the first parameter.
tc.setCaretPosition(info.getSelectionStart());
@@ -981,12 +970,12 @@ class ParameterizedCompletionContext {
tc.addFocusListener(this);
installKeyBindings();
}
@Override
public void removeUpdate(DocumentEvent e) {
handleDocumentEvent(e);
}
/**
* Uninstalls this listener from the current text component.
*/
@@ -1005,21 +994,21 @@ class ParameterizedCompletionContext {
removeParameterHighlights();
}
}
/**
* Action performed when the user presses the up or down arrow keys and
* the parameter completion choices popup is visible.
* Action performed when the user presses the up or down arrow keys and the
* parameter completion choices popup is visible.
*/
private class NextChoiceAction extends AbstractAction {
private Action oldAction;
private int amount;
public NextChoiceAction(int amount, Action oldAction) {
this.amount = amount;
this.oldAction = oldAction;
}
@Override
public void actionPerformed(ActionEvent e) {
if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) {
@@ -1031,34 +1020,34 @@ class ParameterizedCompletionContext {
}
}
}
/**
* Action performed when the user hits the tab key.
*/
private class NextParamAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
moveToNextParam();
}
}
private static class ParamCopyInfo {
private String paramName;
private Highlight h;
public ParamCopyInfo(String paramName, Highlight h) {
this.paramName = paramName;
this.h = h;
}
}
/**
* Action performed when the user hits shift+tab.
*/
private class PrevParamAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
moveToPreviousParam();
@@ -28,7 +28,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* @version 1.0
*/
class ParameterizedCompletionDescriptionToolTip {
/**
* The actual tool tip.
*/
@@ -41,23 +41,21 @@ class ParameterizedCompletionDescriptionToolTip {
* The completion being described.
*/
private ParameterizedCompletion pc;
/**
* Constructor.
*
* @param owner
* The parent window.
* @param ac
* The parent auto-completion.
* @param pc
* The completion being described.
* @param owner The parent window.
* @param ac The parent auto-completion.
* @param pc The completion being described.
*/
public ParameterizedCompletionDescriptionToolTip(Window owner, ParameterizedCompletionContext context, AutoCompletion ac,
ParameterizedCompletion pc) {
public ParameterizedCompletionDescriptionToolTip(Window owner, ParameterizedCompletionContext context,
AutoCompletion ac, ParameterizedCompletion pc) {
tooltip = new JWindow(owner);
this.pc = pc;
descLabel = new JLabel();
descLabel.setBorder(BorderFactory.createCompoundBorder(TipUtil.getToolTipBorder(), BorderFactory.createEmptyBorder(2, 5, 2, 5)));
descLabel.setBorder(BorderFactory.createCompoundBorder(TipUtil.getToolTipBorder(),
BorderFactory.createEmptyBorder(2, 5, 2, 5)));
descLabel.setOpaque(true);
descLabel.setBackground(TipUtil.getToolTipBackground());
// It appears that if a JLabel is set as a content pane directly, when
@@ -77,7 +75,7 @@ class ParameterizedCompletionDescriptionToolTip {
updateText(0);
tooltip.setFocusableWindowState(false);
}
/**
* Returns whether this tool tip is visible.
*
@@ -87,12 +85,11 @@ class ParameterizedCompletionDescriptionToolTip {
public boolean isVisible() {
return tooltip.isVisible();
}
/**
* Sets the location of this tool tip relative to the given rectangle.
*
* @param r
* The visual position of the caret (in screen coordinates).
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
@@ -119,24 +116,22 @@ class ParameterizedCompletionDescriptionToolTip {
}
tooltip.setLocation(x, y);
}
/**
* Toggles the visibility of this tool tip.
*
* @param visible
* Whether this tool tip should be visible.
* @param visible Whether this tool tip should be visible.
* @see #isVisible()
*/
public void setVisible(boolean visible) {
tooltip.setVisible(visible);
}
/**
* Updates the text in the tool tip to have the current parameter
* displayed in bold.
* Updates the text in the tool tip to have the current parameter displayed in
* bold.
*
* @param selectedParam
* The index of the selected parameter.
* @param selectedParam The index of the selected parameter.
* @return Whether the text needed to be updated.
*/
public boolean updateText(int selectedParam) {
@@ -170,10 +165,9 @@ class ParameterizedCompletionDescriptionToolTip {
tooltip.pack();
return true;
}
/**
* Updates the <tt>LookAndFeel</tt> of this window and the description
* window.
* Updates the <tt>LookAndFeel</tt> of this window and the description window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(tooltip);
@@ -22,7 +22,7 @@ import org.fife.ui.rsyntaxtextarea.DocumentRange;
* @version 1.0
*/
class ParameterizedCompletionInsertionInfo {
private int minOffs;
private Position maxOffs;
private int defaultEnd;
@@ -31,26 +31,24 @@ class ParameterizedCompletionInsertionInfo {
private String textToInsert;
private List<DocumentRange> replacementLocations;
private List<ReplacementCopy> replacementCopies;
public ParameterizedCompletionInsertionInfo() {
defaultEnd = -1;
}
public void addReplacementCopy(String id, int start, int end) {
if (replacementCopies == null) {
replacementCopies = new ArrayList<ReplacementCopy>(1);
}
replacementCopies.add(new ReplacementCopy(id, start, end));
}
/**
* Marks a region of the replacement text as representing a variable name
* or some other construct that the user should replace.
* Marks a region of the replacement text as representing a variable name or
* some other construct that the user should replace.
*
* @param start
* The start offset.
* @param end
* The end offset.
* @param start The start offset.
* @param end The end offset.
* @see #getReplacementCount()
* @see #getReplacementLocation(int)
*/
@@ -60,14 +58,14 @@ class ParameterizedCompletionInsertionInfo {
}
replacementLocations.add(new DocumentRange(start, end));
}
public int getDefaultEndOffs() {
return defaultEnd > -1 ? defaultEnd : getMaxOffset().getOffset();
}
/**
* Returns the maximum offset the caret can move to before being outside
* of the text inserted for this completion.
* Returns the maximum offset the caret can move to before being outside of the
* text inserted for this completion.
*
* @return The maximum offset.
* @see #getMinOffset()
@@ -75,10 +73,10 @@ class ParameterizedCompletionInsertionInfo {
public Position getMaxOffset() {
return maxOffs;
}
/**
* Returns the minimum offset the caret can move to before being outside
* of the text inserted for this completion.
* Returns the minimum offset the caret can move to before being outside of the
* text inserted for this completion.
*
* @return The minimum offset.
* @see #getMaxOffset()
@@ -86,11 +84,11 @@ class ParameterizedCompletionInsertionInfo {
public int getMinOffset() {
return minOffs;
}
public int getReplacementCopyCount() {
return replacementCopies == null ? 0 : replacementCopies.size();
}
/**
* Returns the number of replacements in the completion.
*
@@ -99,28 +97,27 @@ class ParameterizedCompletionInsertionInfo {
public int getReplacementCount() {
return replacementLocations == null ? 0 : replacementLocations.size();
}
public ReplacementCopy getReplacementCopy(int index) {
return replacementCopies.get(index);
}
/**
* Returns the starting- and ending-offsets of the replacement regions
* in the completion.
* Returns the starting- and ending-offsets of the replacement regions in the
* completion.
*
* @param index
* The replacement region.
* @param index The replacement region.
* @return The range in the document of that replacement region.
* @see #getReplacementCount()
*/
public DocumentRange getReplacementLocation(int index) {
return replacementLocations.get(index);
}
/**
* Returns the offset that should be the end of the initially selected
* text when the completion is inserted (i.e., the end offset of the first
* replacement region).
* Returns the offset that should be the end of the initially selected text when
* the completion is inserted (i.e., the end offset of the first replacement
* region).
*
* @return The end offset for the initial selection.
* @see #getSelectionStart()
@@ -128,11 +125,11 @@ class ParameterizedCompletionInsertionInfo {
public int getSelectionEnd() {
return selEnd;
}
/**
* Returns the offset that should be the start of the initially selected
* text when the completion is inserted (i.e., the start offset of the
* first replacement region).
* Returns the offset that should be the start of the initially selected text
* when the completion is inserted (i.e., the start offset of the first
* replacement region).
*
* @return The start offset for the initial selection.
* @see #getSelectionEnd()
@@ -140,7 +137,7 @@ class ParameterizedCompletionInsertionInfo {
public int getSelectionStart() {
return selStart;
}
/**
* Returns the actual text to insert when the completion is selected.
*
@@ -150,24 +147,22 @@ class ParameterizedCompletionInsertionInfo {
public String getTextToInsert() {
return textToInsert;
}
/**
* Returns whether or not there is an initial selected region for the
* completion (i.e., whether the completion actually has any parameters).
* Returns whether or not there is an initial selected region for the completion
* (i.e., whether the completion actually has any parameters).
*
* @return Whether there is a region to initially select for the completion.
*/
public boolean hasSelection() {
return selEnd != selStart;
}
/**
* Sets the initially selected region for the completion.
*
* @param selStart
* The selection start.
* @param selEnd
* The selection end.
* @param selStart The selection start.
* @param selEnd The selection end.
* @see #getSelectionEnd()
* @see #getSelectionStart()
*/
@@ -175,16 +170,14 @@ class ParameterizedCompletionInsertionInfo {
this.selStart = selStart;
this.selEnd = selEnd;
}
/**
* Sets the document range the caret can move around in before being
* outside of the text inserted for the completion.
* Sets the document range the caret can move around in before being outside of
* the text inserted for the completion.
*
* @param minOffs
* The minimum offset.
* @param maxOffs
* The maximum offset, that will track its location as the
* document is modified.
* @param minOffs The minimum offset.
* @param maxOffs The maximum offset, that will track its location as the
* document is modified.
* @see #getMinOffset()
* @see #getMaxOffset()
*/
@@ -192,42 +185,41 @@ class ParameterizedCompletionInsertionInfo {
this.minOffs = minOffs;
this.maxOffs = maxOffs;
}
public void setDefaultEndOffs(int end) {
defaultEnd = end;
}
/**
* Sets the text to insert for the completion.
*
* @param text
* The text to insert.
* @param text The text to insert.
* @see #getTextToInsert()
*/
public void setTextToInsert(String text) {
this.textToInsert = text;
}
public static class ReplacementCopy {
private String id;
private int start;
private int end;
public ReplacementCopy(String id, int start, int end) {
this.id = id;
this.start = start;
this.end = end;
}
public int getEnd() {
return end;
}
public String getId() {
return id;
}
public int getStart() {
return start;
}
@@ -14,8 +14,8 @@ import javax.swing.Action;
/**
* An <code>AutoCompletion</code> that adds the ability to cycle through a set
* of <code>CompletionProvider</code>s via the trigger key. This allows the
* application to logically "group together" completions of similar kinds;
* for example, Java code completions vs. template completions.
* application to logically "group together" completions of similar kinds; for
* example, Java code completions vs. template completions.
* <p>
*
* Usage:
@@ -33,15 +33,14 @@ import javax.swing.Action;
* @author mschlegel
*/
public class RoundRobinAutoCompletion extends AutoCompletion {
/** The List of CompletionProviders to use */
private List<CompletionProvider> cycle = new ArrayList<CompletionProvider>();
/**
* Constructor.
*
* @param provider
* A single completion provider.
* @param provider A single completion provider.
* @see #addCompletionProvider(CompletionProvider)
*/
public RoundRobinAutoCompletion(CompletionProvider provider) {
@@ -58,25 +57,24 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
// chosen.
setAutoCompleteSingleChoices(false);
}
/**
* Adds an additional <code>CompletionProvider</code> to the list to
* cycle through.
* Adds an additional <code>CompletionProvider</code> to the list to cycle
* through.
*
* @param provider
* The new completion provider.
* @param provider The new completion provider.
*/
public void addCompletionProvider(CompletionProvider provider) {
cycle.add(provider);
}
/**
* Moves to the next Provider internally. Needs refresh of the popup window
* to display the changes.
* Moves to the next Provider internally. Needs refresh of the popup window to
* display the changes.
*
* @return true if the next provider was the default one (thus returned to
* the default view). May be used in case you like to hide the
* popup in this case.
* @return true if the next provider was the default one (thus returned to the
* default view). May be used in case you like to hide the popup in this
* case.
*/
public boolean advanceProvider() {
CompletionProvider currentProvider = getCompletionProvider();
@@ -84,7 +82,7 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
setCompletionProvider(cycle.get(i));
return i == 0;
}
/**
* Overridden to provide our own implementation of the action.
*/
@@ -92,7 +90,7 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
protected Action createAutoCompleteAction() {
return new CycleAutoCompleteAction();
}
/**
* Resets the cycle to use the default provider on next refresh.
*/
@@ -103,14 +101,14 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
setCompletionProvider(defaultProvider);
}
}
/**
* An implementation of the auto-complete action that ensures the proper
* <code>CompletionProvider</code> is displayed based on the context in
* which the user presses the trigger key.
* <code>CompletionProvider</code> is displayed based on the context in which
* the user presses the trigger key.
*/
private class CycleAutoCompleteAction extends AutoCompleteAction {
@Override
public void actionPerformed(ActionEvent e) {
if (isAutoCompleteEnabled()) {
@@ -17,68 +17,58 @@ package org.fife.ui.autocomplete;
* @version 1.0
*/
public class ShorthandCompletion extends BasicCompletion {
/**
* The text the user can start typing that will match this completion.
*/
private String inputText;
/**
* Constructor.
*
* @param provider
* The provider that returns this completion.
* @param inputText
* The text the user inputs to get this completion.
* @param replacementText
* The replacement text of the completion.
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText) {
super(provider, replacementText);
this.inputText = inputText;
}
/**
* Constructor.
*
* @param provider
* The provider that returns this completion.
* @param inputText
* The text the user inputs to get this completion.
* @param replacementText
* The replacement text of the completion.
* @param shortDesc
* A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText, String shortDesc) {
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText,
String shortDesc) {
super(provider, replacementText, shortDesc);
this.inputText = inputText;
}
/**
* Constructor.
*
* @param provider
* The provider that returns this completion.
* @param inputText
* The text the user inputs to get this completion.
* @param replacementText
* The replacement text of the completion.
* @param shortDesc
* A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param summary
* The summary of this completion. This should be HTML.
* This may be <code>null</code>.
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param summary The summary of this completion. This should be HTML.
* This may be <code>null</code>.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText, String shortDesc, String summary) {
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText, String shortDesc,
String summary) {
super(provider, replacementText, shortDesc, summary);
this.inputText = inputText;
}
/**
* Returns the text the user must start typing to get this completion.
*
@@ -88,13 +78,12 @@ public class ShorthandCompletion extends BasicCompletion {
public String getInputText() {
return inputText;
}
/**
* If a summary has been set, that summary is returned. Otherwise, the
* replacement text is returned.
*
* @return A description of this completion (the text that will be
* inserted).
* @return A description of this completion (the text that will be inserted).
* @see #getReplacementText()
*/
@Override
@@ -102,11 +91,11 @@ public class ShorthandCompletion extends BasicCompletion {
String summary = super.getSummary();
return summary != null ? summary : ("<html><body>" + getSummaryBody());
}
/**
* Returns the "body" of the HTML returned by {@link #getSummary()} when
* no summary text has been set. This is defined to return the replacement
* text in a monospaced font.
* Returns the "body" of the HTML returned by {@link #getSummary()} when no
* summary text has been set. This is defined to return the replacement text in
* a monospaced font.
*
* @return The summary text's body, if no other summary has been defined.
* @see #getReplacementText()
+21 -24
View File
@@ -37,32 +37,31 @@ import javax.swing.event.MouseInputAdapter;
* @version 1.0
*/
class SizeGrip extends JPanel {
/**
* The size grip to use if we're on OS X.
*/
private Image osxSizeGrip;
public SizeGrip() {
MouseHandler adapter = new MouseHandler();
addMouseListener(adapter);
addMouseMotionListener(adapter);
setPreferredSize(new Dimension(16, 16));
}
/**
* Overridden to ensure that the cursor for this component is appropriate
* for the orientation.
* Overridden to ensure that the cursor for this component is appropriate for
* the orientation.
*
* @param o
* The new orientation.
* @param o The new orientation.
*/
@Override
public void applyComponentOrientation(ComponentOrientation o) {
possiblyFixCursor(o.isLeftToRight());
super.applyComponentOrientation(o);
}
/**
* Creates and returns the OS X size grip image.
*
@@ -94,12 +93,11 @@ class SizeGrip extends JPanel {
}
return image;
}
/**
* Paints this panel.
*
* @param g
* The graphics context.
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
@@ -147,13 +145,12 @@ class SizeGrip extends JPanel {
g.fillRect(2, height - 9, 2, 2);
}
}
/**
* Ensures that the cursor for this component is appropriate for the
* orientation.
*
* @param ltr
* Whether the current component orientation is LTR.
* @param ltr Whether the current component orientation is LTR.
*/
protected void possiblyFixCursor(boolean ltr) {
int cursor = Cursor.NE_RESIZE_CURSOR;
@@ -164,7 +161,7 @@ class SizeGrip extends JPanel {
setCursor(Cursor.getPredefinedCursor(cursor));
}
}
@Override
public void updateUI() {
super.updateUI();
@@ -178,21 +175,21 @@ class SizeGrip extends JPanel {
osxSizeGrip = null;
}
}
/**
* Listens for mouse events on this panel and resizes the parent window
* appropriately.
*/
/*
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using
* the locations relative to the corner component because the latter proved
* buggy - stretch the window too wide and some kind of arithmetic error
* started happening somewhere - our window would grow way too large.
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using the
* locations relative to the corner component because the latter proved buggy -
* stretch the window too wide and some kind of arithmetic error started
* happening somewhere - our window would grow way too large.
*/
private class MouseHandler extends MouseInputAdapter {
private Point origPos;
@Override
public void mouseDragged(MouseEvent e) {
Point newPos = e.getPoint();
@@ -222,13 +219,13 @@ class SizeGrip extends JPanel {
}
origPos.setLocation(newPos);
}
@Override
public void mousePressed(MouseEvent e) {
origPos = e.getPoint();
SwingUtilities.convertPointToScreen(origPos, SizeGrip.this);
}
@Override
public void mouseReleased(MouseEvent e) {
origPos = null;
@@ -10,14 +10,14 @@ package org.fife.ui.autocomplete;
import java.util.Comparator;
/**
* Compares two <code>Completion</code>s by their relevance before
* sorting them lexicographically.
* Compares two <code>Completion</code>s by their relevance before sorting them
* lexicographically.
*
* @author Robert Futrell
* @version 1.0
*/
public class SortByRelevanceComparator implements Comparator<Completion> {
@Override
public int compare(Completion c1, Completion c2) {
int rel1 = c1.getRelevance();
@@ -37,12 +37,12 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* }
* </pre>
*
* In the above example, the first <code>${i}</code> is a parameter for the
* user to type into; all the other <code>${i}</code> instances are
* automatically changed to what the user types in the first one. The parameter
* named <code>${cursor}</code> is the "ending position" of the template. It's
* where the caret moves after it cycles through all other parameters. If the
* user types into it, template mode terminates. If more than one
* In the above example, the first <code>${i}</code> is a parameter for the user
* to type into; all the other <code>${i}</code> instances are automatically
* changed to what the user types in the first one. The parameter named
* <code>${cursor}</code> is the "ending position" of the template. It's where
* the caret moves after it cycles through all other parameters. If the user
* types into it, template mode terminates. If more than one
* <code>${cursor}</code> parameter is specified, behavior is undefined.
* <p>
*
@@ -51,15 +51,15 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* fault-tolerant.
* <p>
*
* Leading whitespace is automatically added to lines if the template spans
* more than one line, and if used with a text component using a
* Leading whitespace is automatically added to lines if the template spans more
* than one line, and if used with a text component using a
* <code>PlainDocument</code>, tabs will be converted to spaces if requested.
*
* @author Robert Futrell
* @version 1.0
*/
public class TemplateCompletion extends AbstractCompletion implements ParameterizedCompletion {
private List<TemplatePiece> pieces;
private String inputText;
private String definitionString;
@@ -69,11 +69,11 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
* The template's parameters.
*/
private List<Parameter> params;
public TemplateCompletion(CompletionProvider provider, String inputText, String definitionString, String template) {
this(provider, inputText, definitionString, template, null, null);
}
public TemplateCompletion(CompletionProvider provider, String inputText, String definitionString, String template,
String shortDescription, String summary) {
super(provider);
@@ -85,7 +85,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
params = new ArrayList<Parameter>(3);
parse(template);
}
private void addTemplatePiece(TemplatePiece piece) {
pieces.add(piece);
if (piece instanceof Param && !"cursor".equals(piece.getText())) {
@@ -94,12 +94,12 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
params.add(param);
}
}
@Override
public String getInputText() {
return inputText;
}
private String getPieceText(int index, String leadingWS) {
TemplatePiece piece = pieces.get(index);
String text = piece.getText();
@@ -108,10 +108,10 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
}
return text;
}
/**
* Returns <code>null</code>; template completions insert all of their
* text via <code>getInsertionInfo()</code>.
* Returns <code>null</code>; template completions insert all of their text via
* <code>getInsertionInfo()</code>.
*
* @return <code>null</code> always.
*/
@@ -119,21 +119,21 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public String getReplacementText() {
return null;
}
@Override
public String getSummary() {
return summary;
}
@Override
public String getDefinitionString() {
return definitionString;
}
public String getShortDescription() {
return shortDescription;
}
/**
* {@inheritDoc}
*/
@@ -141,7 +141,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public boolean getShowParameterToolTip() {
return false;
}
@Override
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces) {
ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo();
@@ -214,7 +214,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
info.setTextToInsert(sb.toString());
return info;
}
/**
* {@inheritDoc}
*/
@@ -222,7 +222,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public Parameter getParam(int index) {
return params.get(index);
}
/**
* {@inheritDoc}
*/
@@ -230,12 +230,11 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public int getParamCount() {
return params == null ? 0 : params.size();
}
/**
* Returns whether a parameter is already defined with a specific name.
*
* @param name
* The name.
* @param name The name.
* @return Whether a parameter is defined with that name.
*/
private boolean isParamDefined(String name) {
@@ -247,12 +246,11 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
}
return false;
}
/**
* Parses a template string into logical pieces used by this class.
*
* @param template
* The template to parse.
* @param template The template to parse.
*/
private void parse(String template) {
int offs = 0;
@@ -260,23 +258,23 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
while ((offs = template.indexOf('$', lastOffs)) > -1 && offs < template.length() - 1) {
char next = template.charAt(offs + 1);
switch (next) {
case '$': // "$$" => escaped single dollar sign
addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs + 1)));
lastOffs = offs + 2;
break;
case '{': // "${...}" => variable
int closingCurly = template.indexOf('}', offs + 2);
if (closingCurly > -1) {
addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs)));
String varName = template.substring(offs + 2, closingCurly);
if (!"cursor".equals(varName) && isParamDefined(varName)) {
addTemplatePiece(new TemplatePiece.ParamCopy(varName));
} else {
addTemplatePiece(new TemplatePiece.Param(varName));
}
lastOffs = closingCurly + 1;
case '$': // "$$" => escaped single dollar sign
addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs + 1)));
lastOffs = offs + 2;
break;
case '{': // "${...}" => variable
int closingCurly = template.indexOf('}', offs + 2);
if (closingCurly > -1) {
addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs)));
String varName = template.substring(offs + 2, closingCurly);
if (!"cursor".equals(varName) && isParamDefined(varName)) {
addTemplatePiece(new TemplatePiece.ParamCopy(varName));
} else {
addTemplatePiece(new TemplatePiece.Param(varName));
}
break;
lastOffs = closingCurly + 1;
}
break;
}
}
if (lastOffs < template.length()) {
@@ -284,7 +282,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
addTemplatePiece(new TemplatePiece.Text(text));
}
}
private int possiblyReplaceTabsWithSpaces(StringBuilder sb, String text, JTextComponent tc, int start) {
int tab = text.indexOf('\t');
if (tab > -1) {
@@ -315,18 +313,17 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
}
return start;
}
/**
* Sets the short description of this template completion.
*
* @param shortDesc
* The new short description.
* @param shortDesc The new short description.
* @see #getShortDescription()
*/
public void setShortDescription(String shortDesc) {
this.shortDescription = shortDesc;
}
@Override
public String toString() {
return getDefinitionString();
+18 -18
View File
@@ -7,68 +7,68 @@
package org.fife.ui.autocomplete;
/**
* A piece of a <code>TemplateCompletion</code>. You add instances of this
* class to template completions to define them.
* A piece of a <code>TemplateCompletion</code>. You add instances of this class
* to template completions to define them.
*
* @author Robert Futrell
* @version 1.0
* @see TemplateCompletion
*/
interface TemplatePiece {
String getText();
public class Text implements TemplatePiece {
private String text;
public Text(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public String toString() {
return "[TemplatePiece.Text: text=" + text + "]";
}
}
public class Param implements TemplatePiece {
String text;
public Param(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public String toString() {
return "[TemplatePiece.Param: param=" + text + "]";
}
}
public class ParamCopy implements TemplatePiece {
private String text;
public ParamCopy(String text) {
this.text = text;
}
@Override
public String getText() {
return text;
}
@Override
public String toString() {
return "[TemplatePiece.ParamCopy: param=" + text + "]";
+23 -24
View File
@@ -22,17 +22,18 @@ import javax.swing.text.html.HTMLDocument;
* Static utility methods for homemade tool tips.
* <p>
*
* This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class
* of the same name, but isn't re-used to prevent a hard dependency on the
* RSTA library.
* This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class of
* the same name, but isn't re-used to prevent a hard dependency on the RSTA
* library.
*
* @author Robert Futrell
* @version 1.0
*/
class TipUtil {
private TipUtil() {}
private TipUtil() {
}
/**
* Returns the default background color to use for tool tip windows.
*
@@ -55,7 +56,7 @@ class TipUtil {
}
return c;
}
/**
* Returns the border used by tool tips in this look and feel.
*
@@ -71,21 +72,20 @@ class TipUtil {
}
return border;
}
/**
* Returns whether a color is a Nimbus DerivedColor, which is troublesome
* in that it doesn't use its RGB values (uses HSB instead?) and so
* querying them is useless.
* Returns whether a color is a Nimbus DerivedColor, which is troublesome in
* that it doesn't use its RGB values (uses HSB instead?) and so querying them
* is useless.
*
* @param c
* The color to check.
* @param c The color to check.
* @return Whether it is a DerivedColor
*/
private static final boolean isDerivedColor(Color c) {
return c != null
&& (c.getClass().getName().endsWith(".DerivedColor") || c.getClass().getName().endsWith(".DerivedColor$UIResource"));
return c != null && (c.getClass().getName().endsWith(".DerivedColor")
|| c.getClass().getName().endsWith(".DerivedColor$UIResource"));
}
/**
* Returns whether the Nimbus Look and Feel is installed.
*
@@ -94,14 +94,13 @@ class TipUtil {
private static final boolean isNimbusLookAndFeel() {
return UIManager.getLookAndFeel().getName().equals("Nimbus");
}
/**
* Tweaks a <code>JEditorPane</code> so it can be used to render the
* content in a focusable pseudo-tool tip. It is assumed that the editor
* pane is using an <code>HTMLDocument</code>.
* Tweaks a <code>JEditorPane</code> so it can be used to render the content in
* a focusable pseudo-tool tip. It is assumed that the editor pane is using an
* <code>HTMLDocument</code>.
*
* @param textArea
* The editor pane to tweak.
* @param textArea The editor pane to tweak.
*/
public static void tweakTipEditorPane(JEditorPane textArea) {
// Jump through a few hoops to get things looking nice in Nimbus
@@ -134,8 +133,8 @@ class TipUtil {
font = new Font("SansSerif", Font.PLAIN, 12);
}
HTMLDocument doc = (HTMLDocument) textArea.getDocument();
doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt" + "; color: "
+ Util.getHexString(fg) + "; }");
doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt"
+ "; color: " + Util.getHexString(fg) + "; }");
// Always add link foreground rule. Unfortunately these CSS rules
// stack each time the LaF is changed (how can we overwrite them
// without clearing out the important "standard" ones?).
+59 -67
View File
@@ -28,31 +28,31 @@ import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
* @version 1.0
*/
public class Util {
/**
* If a system property is defined with this name and set, ignoring case,
* to <code>true</code>, this library will not attempt to use Substance
* renderers. Otherwise, if a Substance Look and Feel is installed, we
* will attempt to use Substance cell renderers in all of our dropdowns.
* If a system property is defined with this name and set, ignoring case, to
* <code>true</code>, this library will not attempt to use Substance renderers.
* Otherwise, if a Substance Look and Feel is installed, we will attempt to use
* Substance cell renderers in all of our dropdowns.
* <p>
*
* Note that we do not have a build dependency on Substance, so all access
* to Substance stuff is done via reflection. We will fall back onto
* default renderers if something goes horribly wrong.
* Note that we do not have a build dependency on Substance, so all access to
* Substance stuff is done via reflection. We will fall back onto default
* renderers if something goes horribly wrong.
*/
public static final String PROPERTY_DONT_USE_SUBSTANCE_RENDERERS = "org.fife.ui.autocomplete.DontUseSubstanceRenderers";
/**
* If this system property is <code>true</code>, then even the "main" two
* auto-complete windows will allow window decorations via
* {@link PopupWindowDecorator}. If this property is undefined or
* <code>false</code>, they won't honor such decorations. This is due to
* certain performance issues with translucent windows (used for drop
* shadows), even as of Java 7u2.
* <code>false</code>, they won't honor such decorations. This is due to certain
* performance issues with translucent windows (used for drop shadows), even as
* of Java 7u2.
*/
public static final String PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS = "org.fife.ui.autocomplete.allowDecoratedAutoCompleteWindows";
/**
* Used for the color of hyperlinks when a LookAndFeel uses light text
* against a dark background.
* Used for the color of hyperlinks when a LookAndFeel uses light text against a
* dark background.
*/
private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);
private static final Pattern TAG_PATTERN = Pattern.compile("<[^>]*>");
@@ -60,15 +60,14 @@ public class Util {
private static boolean desktopCreationAttempted;
private static Object desktop;
private static final Object LOCK_DESKTOP_CREATION = new Object();
/**
* Attempts to open a web browser to the specified URI.
*
* @param uri
* The URI to open. If this is <code>null</code>, nothing
* happens and this method returns <code>false</code>.
* @return Whether the operation was successful. This will be
* <code>false</code> on JRE's older than 1.6.
* @param uri The URI to open. If this is <code>null</code>, nothing happens and
* this method returns <code>false</code>.
* @return Whether the operation was successful. This will be <code>false</code>
* on JRE's older than 1.6.
*/
public static boolean browse(URI uri) {
boolean success = false;
@@ -88,11 +87,11 @@ public class Util {
}
return success;
}
/**
* Returns the singleton <code>java.awt.Desktop</code> instance, or
* <code>null</code> if it is unsupported on this platform (or the JRE
* is older than 1.6).
* <code>null</code> if it is unsupported on this platform (or the JRE is older
* than 1.6).
*
* @return The desktop, as an {@link Object}.
*/
@@ -117,14 +116,13 @@ public class Util {
}
return desktop;
}
/**
* Returns a hex string for the specified color, suitable for HTML.
*
* @param c
* The color.
* @return The string representation, in the form "<code>#rrggbb</code>",
* or <code>null</code> if <code>c</code> is <code>null</code>.
* @param c The color.
* @return The string representation, in the form "<code>#rrggbb</code>", or
* <code>null</code> if <code>c</code> is <code>null</code>.
*/
public static String getHexString(Color c) {
if (c == null) {
@@ -150,12 +148,12 @@ public class Util {
sb.append(Integer.toHexString(b));
return sb.toString();
}
/**
* Returns the color to use for hyperlink-style components. This method
* will return <code>Color.blue</code> unless it appears that the current
* LookAndFeel uses light text on a dark background, in which case a
* brighter alternative is returned.
* Returns the color to use for hyperlink-style components. This method will
* return <code>Color.blue</code> unless it appears that the current LookAndFeel
* uses light text on a dark background, in which case a brighter alternative is
* returned.
*
* @return The color to use for hyperlinks.
*/
@@ -168,16 +166,14 @@ public class Util {
}
return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue;
}
/**
* Returns the screen coordinates for the monitor that contains the
* specified point. This is useful for setups with multiple monitors,
* to ensure that popup windows are positioned properly.
* Returns the screen coordinates for the monitor that contains the specified
* point. This is useful for setups with multiple monitors, to ensure that popup
* windows are positioned properly.
*
* @param x
* The x-coordinate, in screen coordinates.
* @param y
* The y-coordinate, in screen coordinates.
* @param x The x-coordinate, in screen coordinates.
* @param y The y-coordinate, in screen coordinates.
* @return The bounds of the monitor that contains the specified point.
*/
public static Rectangle getScreenBoundsForPoint(int x, int y) {
@@ -193,12 +189,12 @@ public class Util {
// If point is outside all monitors, default to default monitor (?)
return env.getMaximumWindowBounds();
}
/**
* Give apps a chance to decorate us with drop shadows, etc. Since very
* scrolly things such as lists (of e.g. completions) are *very* slow when
* in per-pixel translucent windows, even as of Java 7u2, we force the user
* to specify an extra option for the two "main" auto-complete windows.
* Give apps a chance to decorate us with drop shadows, etc. Since very scrolly
* things such as lists (of e.g. completions) are *very* slow when in per-pixel
* translucent windows, even as of Java 7u2, we force the user to specify an
* extra option for the two "main" auto-complete windows.
*
* @return Whether to allow decorating the main auto-complete windows.
* @see #PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS
@@ -210,40 +206,37 @@ public class Util {
return false;
}
}
/**
* Returns whether we should attempt to use Substance cell renderers and
* styles for things such as completion choices, if a Substance Look and
* Feel is installed. If this is <code>false</code>, we'll use our
* standard rendering for completions, even when Substance is being used.
* Returns whether we should attempt to use Substance cell renderers and styles
* for things such as completion choices, if a Substance Look and Feel is
* installed. If this is <code>false</code>, we'll use our standard rendering
* for completions, even when Substance is being used.
*
* @return Whether to use Substance renderers if Substance is installed.
*/
public static boolean getUseSubstanceRenderers() {
return useSubstanceRenderers;
}
/**
* Returns whether the specified color is "light" to use as a foreground.
* Colors that return <code>true</code> indicate that the current Look and
* Feel probably uses light text colors on a dark background.
* Returns whether the specified color is "light" to use as a foreground. Colors
* that return <code>true</code> indicate that the current Look and Feel
* probably uses light text colors on a dark background.
*
* @param fg
* The foreground color.
* @param fg The foreground color.
* @return Whether it is a "light" foreground color.
*/
public static final boolean isLightForeground(Color fg) {
return fg.getRed() > 0xa0 && fg.getGreen() > 0xa0 && fg.getBlue() > 0xa0;
}
/**
* Returns whether a string starts with a specified prefix, ignoring case.
* This method does not support characters outside of the BMP.
* Returns whether a string starts with a specified prefix, ignoring case. This
* method does not support characters outside of the BMP.
*
* @param str
* The string to check. This cannot be {@code null}.
* @param prefix
* The prefix to check for. This cannot be {@code null}.
* @param str The string to check. This cannot be {@code null}.
* @param prefix The prefix to check for. This cannot be {@code null}.
* @return Whether {@code str} starts with {@code prefix}, ignoring case.
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
@@ -253,13 +246,12 @@ public class Util {
}
return false;
}
/**
* Strips any HTML from a string. The string must start with
* "<code>&lt;html&gt;</code>" for markup tags to be stripped.
*
* @param text
* The string.
* @param text The string.
* @return The string, with any HTML stripped.
*/
public static String stripHtml(String text) {
@@ -269,7 +261,7 @@ public class Util {
// TODO: Micro-optimize me, might be called in renderers and loops
return TAG_PATTERN.matcher(text).replaceAll("");
}
static {
boolean use = true;
try {
@@ -13,16 +13,16 @@ import javax.swing.text.JTextComponent;
* <p>
*
* This completion type uses its <tt>shortDescription</tt> property as part of
* its summary returned by {@link #getSummary()}; for this reason, it may be
* a little longer (even much longer), if desired, than what is recommended
* for <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used
* in {@link #toString()} for <tt>ListCellRenderers</tt>).
* its summary returned by {@link #getSummary()}; for this reason, it may be a
* little longer (even much longer), if desired, than what is recommended for
* <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used in
* {@link #toString()} for <tt>ListCellRenderers</tt>).
*
* @author Robert Futrell
* @version 1.0
*/
public class VariableCompletion extends BasicCompletion {
/**
* The variable's type.
*/
@@ -31,27 +31,24 @@ public class VariableCompletion extends BasicCompletion {
* What library (for example) this variable is defined in.
*/
private String definedIn;
/**
* Constructor.
*
* @param provider
* The parent provider.
* @param name
* The name of this variable.
* @param type
* The type of this variable (e.g. "<code>int</code>",
* "<code>String</code>", etc.).
* @param provider The parent provider.
* @param name The name of this variable.
* @param type The type of this variable (e.g. "<code>int</code>",
* "<code>String</code>", etc.).
*/
public VariableCompletion(CompletionProvider provider, String name, String type) {
super(provider, name);
this.type = type;
}
protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>").append(getDefinitionString()).append("</b>");
}
public String getDefinitionString() {
StringBuilder sb = new StringBuilder();
// Add the return type if applicable (C macros like NULL have no type).
@@ -62,7 +59,7 @@ public class VariableCompletion extends BasicCompletion {
sb.append(getName());
return sb.toString();
}
/**
* Returns where this variable is defined.
*
@@ -72,7 +69,7 @@ public class VariableCompletion extends BasicCompletion {
public String getDefinedIn() {
return definedIn;
}
/**
* Returns the name of this variable.
*
@@ -81,7 +78,7 @@ public class VariableCompletion extends BasicCompletion {
public String getName() {
return getReplacementText();
}
/**
* {@inheritDoc}
*/
@@ -93,29 +90,25 @@ public class VariableCompletion extends BasicCompletion {
possiblyAddDefinedIn(sb);
return sb.toString();
}
/**
* Returns the tool tip text to display for mouse hovers over this
* completion.
* Returns the tool tip text to display for mouse hovers over this completion.
* <p>
*
* Note that for this functionality to be enabled, a
* <tt>JTextComponent</tt> must be registered with the
* <tt>ToolTipManager</tt>, and the text component must know to search
* for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* Note that for this functionality to be enabled, a <tt>JTextComponent</tt>
* must be registered with the <tt>ToolTipManager</tt>, and the text component
* must know to search for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this can
* be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
*
* @return The tool tip text for this completion, or <code>null</code> if
* none.
* @return The tool tip text for this completion, or <code>null</code> if none.
*/
@Override
public String getToolTipText() {
return getDefinitionString();
}
/**
* Returns the type of this variable.
*
@@ -124,13 +117,12 @@ public class VariableCompletion extends BasicCompletion {
public String getType() {
return type;
}
/**
* Adds some HTML describing where this variable is defined, if this
* information is known.
* Adds some HTML describing where this variable is defined, if this information
* is known.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn != null) {
@@ -138,13 +130,11 @@ public class VariableCompletion extends BasicCompletion {
sb.append(" <em>").append(definedIn).append("</em>");
}
}
/**
* Adds the description text as HTML to a buffer, if a description is
* defined.
* Adds the description text as HTML to a buffer, if a description is defined.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
* @return Whether there was a description to add.
*/
protected boolean possiblyAddDescription(StringBuilder sb) {
@@ -156,18 +146,17 @@ public class VariableCompletion extends BasicCompletion {
}
return false;
}
/**
* Sets where this variable is defined.
*
* @param definedIn
* Where this variable is defined.
* @param definedIn Where this variable is defined.
* @see #getDefinedIn()
*/
public void setDefinedIn(String definedIn) {
this.definedIn = definedIn;
}
/**
* Overridden to return the name of the variable being completed.
*
@@ -30,18 +30,18 @@ import org.fife.ui.rtextarea.RTextArea;
* @version 1.0
*/
public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
private final Action INSERT_BREAK_ACTION;
private static final Pattern MLC_PATTERN = Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
protected AbstractJFlexCTokenMaker() {
INSERT_BREAK_ACTION = createInsertBreakAction();
}
/**
* Creates and returns the action to use when the user inserts a newline.
* The default implementation intelligently closes multi-line comments.
* Subclasses can override.
* Creates and returns the action to use when the user inserts a newline. The
* default implementation intelligently closes multi-line comments. Subclasses
* can override.
*
* @return The action.
* @see #getInsertBreakAction()
@@ -49,10 +49,10 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
protected Action createInsertBreakAction() {
return new CStyleInsertBreakAction();
}
/**
* Returns <code>true</code> always as C-style languages use curly braces
* to denote code blocks.
* Returns <code>true</code> always as C-style languages use curly braces to
* denote code blocks.
*
* @return <code>true</code> always.
*/
@@ -60,11 +60,10 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return true;
}
/**
* Returns an action to handle "insert break" key presses (i.e. Enter).
* An action is returned that handles newlines differently in multi-line
* comments.
* Returns an action to handle "insert break" key presses (i.e. Enter). An
* action is returned that handles newlines differently in multi-line comments.
*
* @return The action.
*/
@@ -72,7 +71,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public Action getInsertBreakAction() {
return INSERT_BREAK_ACTION;
}
/**
* {@inheritDoc}
*/
@@ -80,7 +79,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public boolean getMarkOccurrencesOfTokenType(int type) {
return type == Token.IDENTIFIER || type == Token.FUNCTION;
}
/**
* {@inheritDoc}
*/
@@ -92,19 +91,17 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
return false;
}
/**
* Returns whether a given token is an internal token type that represents
* an MLC or documentation comment continuing on to the next line. This is
* done by languages such as JavaScript that are a little more verbose
* than necessary so that their code can be copy-and-pasted into other
* <code>TokenMaker</code>s that use them as nested languages (such as
* HTML, JSP, etc.).
* Returns whether a given token is an internal token type that represents an
* MLC or documentation comment continuing on to the next line. This is done by
* languages such as JavaScript that are a little more verbose than necessary so
* that their code can be copy-and-pasted into other <code>TokenMaker</code>s
* that use them as nested languages (such as HTML, JSP, etc.).
*
* @param t
* The token to check. This cannot be <code>null</code>.
* @return Whether the token is an internal token representing the end of
* a line for an MLC/doc comment continuing on to the next line.
* @param t The token to check. This cannot be <code>null</code>.
* @return Whether the token is an internal token representing the end of a line
* for an MLC/doc comment continuing on to the next line.
*/
private boolean isInternalEolTokenForMLCs(Token t) {
int type = t.getType();
@@ -114,13 +111,13 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
return false;
}
/**
* Action that knows how to special-case inserting a newline in a
* multi-line comment for languages like C and Java.
* Action that knows how to special-case inserting a newline in a multi-line
* comment for languages like C and Java.
*/
protected class CStyleInsertBreakAction extends RSyntaxTextAreaEditorKit.InsertBreakAction {
@Override
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
if (!textArea.isEditable() || !textArea.isEnabled()) {
@@ -141,13 +138,12 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
handleInsertBreak(rsta, true);
}
}
/**
* Returns whether the MLC token containing <code>offs</code> appears
* to have a "nested" comment (i.e., contains "<code>/*</code>"
* somewhere inside of it). This implies that it is likely a "new" MLC
* and needs to be closed. While not foolproof, this is usually good
* enough of a sign.
* Returns whether the MLC token containing <code>offs</code> appears to have a
* "nested" comment (i.e., contains "<code>/*</code>" somewhere inside of it).
* This implies that it is likely a "new" MLC and needs to be closed. While not
* foolproof, this is usually good enough of a sign.
*
* @param textArea
* @param line
@@ -183,7 +179,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
return true; // No match - MLC goes to the end of the file
}
private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea, int line) {
Matcher m = null;
int start = -1;
@@ -11,35 +11,32 @@ import javax.swing.text.Segment;
/**
* Base class for JFlex-generated token makers. This class attempts to factor
* out all common code from these classes. Many methods <em>almost</em> could
* be factored out into this class, but cannot because they reference JFlex
* out all common code from these classes. Many methods <em>almost</em> could be
* factored out into this class, but cannot because they reference JFlex
* variables that we cannot access from this class.
*
* @author Robert Futrell
* @version 0.1
*/
public abstract class AbstractJFlexTokenMaker extends TokenMakerBase {
protected Segment s;
protected int start; // Just for states.
protected int offsetShift; // As parser always starts at 0, but our line
// doesn't.
/**
* Declared here so we can define overloads that refer to this method.
*
* @param newState
* The new JFlex state to enter.
* @param newState The new JFlex state to enter.
*/
public abstract void yybegin(int newState);
/**
* Starts a new JFlex state and changes the current language index.
*
* @param state
* The new JFlex state to enter.
* @param languageIndex
* The new language index.
* @param state The new JFlex state to enter.
* @param languageIndex The new language index.
*/
protected void yybegin(int state, int languageIndex) {
yybegin(state);
@@ -8,9 +8,9 @@ package org.fife.ui.rsyntaxtextarea;
/**
* An abstract implementation of the
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should
* be overridden for every language for which you want to provide
* syntax highlighting.
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should be
* overridden for every language for which you want to provide syntax
* highlighting.
* <p>
*
* @see Token
@@ -19,34 +19,33 @@ package org.fife.ui.rsyntaxtextarea;
* @version 0.2
*/
public abstract class AbstractTokenMaker extends TokenMakerBase {
/**
* Hash table of words to highlight and what token type they are.
* The keys are the words to highlight, and their values are the
* token types, for example, <code>Token.RESERVED_WORD</code> or
* <code>Token.FUNCTION</code>.
* Hash table of words to highlight and what token type they are. The keys are
* the words to highlight, and their values are the token types, for example,
* <code>Token.RESERVED_WORD</code> or <code>Token.FUNCTION</code>.
*/
protected TokenMap wordsToHighlight;
/**
* Constructor.
*/
public AbstractTokenMaker() {
wordsToHighlight = getWordsToHighlight();
}
/**
* Returns the words to highlight for this programming language.
*
* @return A <code>TokenMap</code> containing the words to highlight for
* this programming language.
* @return A <code>TokenMap</code> containing the words to highlight for this
* programming language.
*/
public abstract TokenMap getWordsToHighlight();
/**
* Removes the token last added from the linked list of tokens. The
* programmer should never have to call this directly; it can be called
* by subclasses of <code>TokenMaker</code> if necessary.
* Removes the token last added from the linked list of tokens. The programmer
* should never have to call this directly; it can be called by subclasses of
* <code>TokenMaker</code> if necessary.
*/
public void removeLastToken() {
if (previousToken == null) {
@@ -18,15 +18,14 @@ import java.util.Set;
* @version 1.0
*/
public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/**
* A mapping from keys to the names of {@link TokenMaker} implementation
* class names. When {@link #getTokenMaker(String)} is called with a key
* defined in this map, a <code>TokenMaker</code> of the corresponding type
* is returned.
* A mapping from keys to the names of {@link TokenMaker} implementation class
* names. When {@link #getTokenMaker(String)} is called with a key defined in
* this map, a <code>TokenMaker</code> of the corresponding type is returned.
*/
private Map<String, Object> tokenMakerMap;
/**
* Constructor.
*/
@@ -34,14 +33,13 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
tokenMakerMap = new HashMap<String, Object>();
initTokenMakerMap();
}
/**
* Returns a {@link TokenMaker} for the specified key.
*
* @param key
* The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
* if none matches the specified key.
* @param key The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code> if
* none matches the specified key.
*/
@Override
protected TokenMaker getTokenMakerImpl(String key) {
@@ -57,18 +55,18 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
}
return null;
}
/**
* Populates the mapping from keys to instances of
* <code>TokenMakerCreator</code>s. Subclasses should override this method
* and call one of the <code>putMapping</code> overloads to register
* <code>TokenMakerCreator</code>s. Subclasses should override this method and
* call one of the <code>putMapping</code> overloads to register
* {@link TokenMaker}s for syntax constants.
*
* @see #putMapping(String, String)
* @see #putMapping(String, String, ClassLoader)
*/
protected abstract void initTokenMakerMap();
/**
* {@inheritDoc}
*/
@@ -76,50 +74,45 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
public Set<String> keySet() {
return tokenMakerMap.keySet();
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
* Adds a mapping from a key to a <code>TokenMaker</code> implementation class
* name.
*
* @param key
* The key.
* @param className
* The <code>TokenMaker</code> class name.
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @see #putMapping(String, String, ClassLoader)
*/
public void putMapping(String key, String className) {
putMapping(key, className, null);
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
* Adds a mapping from a key to a <code>TokenMaker</code> implementation class
* name.
*
* @param key
* The key.
* @param className
* The <code>TokenMaker</code> class name.
* @param cl
* The class loader to use when loading the class.
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @param cl The class loader to use when loading the class.
* @see #putMapping(String, String)
*/
public void putMapping(String key, String className, ClassLoader cl) {
tokenMakerMap.put(key, new TokenMakerCreator(className, cl));
}
/**
* Wrapper that handles the creation of TokenMaker instances.
*/
private static class TokenMakerCreator {
private String className;
private ClassLoader cl;
public TokenMakerCreator(String className, ClassLoader cl) {
this.className = className;
this.cl = cl != null ? cl : getClass().getClassLoader();
}
public TokenMaker create() throws Exception {
return (TokenMaker) Class.forName(className, true, cl).newInstance();
}
@@ -17,44 +17,39 @@ import java.util.EventObject;
* @version 1.0
*/
public class ActiveLineRangeEvent extends EventObject {
private int min;
private int max;
/**
* Constructor.
*
* @param source
* The text area.
* @param min
* The first line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param max
* The last line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param source The text area.
* @param min The first line in the active line range, or <code>-1</code> if
* the line range is being cleared.
* @param max The last line in the active line range, or <code>-1</code> if
* the line range is being cleared.
*/
public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
super(source);
this.min = min;
this.max = max;
}
/**
* Returns the last line in the active line range.
*
* @return The last line, or <code>-1</code> if the range is being
* cleared.
* @return The last line, or <code>-1</code> if the range is being cleared.
* @see #getMin()
*/
public int getMax() {
return max;
}
/**
* Returns the first line in the active line range.
*
* @return The first line, or <code>-1</code> if the range is being
* cleared.
* @return The first line, or <code>-1</code> if the range is being cleared.
* @see #getMax()
*/
public int getMin() {
@@ -13,29 +13,27 @@ import java.util.EventListener;
* Listens for "active line range" events from an <code>RSyntaxTextArea</code>.
* If a text area contains some semantic knowledge of the programming language
* being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the
* caret moves into a new "block" of code. Listeners can listen for these
* events and respond accordingly.
* caret moves into a new "block" of code. Listeners can listen for these events
* and respond accordingly.
* <p>
*
* See the <code>RSTALanguageSupport</code> project at
* <a href="http://fifesoft.com">http://fifesoft.com</a> for some
* <code>LanguageSupport</code> implementations that may broadcast these
* events. Note that if an RSTA/LanguageSupport does not support broadcasting
* these events, the listener will simply never receive any notifications.
* <code>LanguageSupport</code> implementations that may broadcast these events.
* Note that if an RSTA/LanguageSupport does not support broadcasting these
* events, the listener will simply never receive any notifications.
*
* @author Robert Futrell
* @version 1.0
*/
public interface ActiveLineRangeListener extends EventListener {
/**
* Called whenever the "active line range" changes.
*
* @param e
* Information about the line range change. If there is no longer
* an "active line range," the "minimum" and "maximum" line
* values
* should both be <code>-1</code>.
* @param e Information about the line range change. If there is no longer an
* "active line range," the "minimum" and "maximum" line values should
* both be <code>-1</code>.
*/
void activeLineRangeChanged(ActiveLineRangeEvent e);
}
@@ -13,14 +13,14 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The default implementation of {@link OccurrenceMarker}. It goes through
* the document and marks all instances of the specified token.
* The default implementation of {@link OccurrenceMarker}. It goes through the
* document and marks all instances of the specified token.
*
* @author Robert Futrell
* @version 1.0
*/
class DefaultOccurrenceMarker implements OccurrenceMarker {
/**
* {@inheritDoc}
*/
@@ -45,7 +45,7 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
}
return t;
}
/**
* {@inheritDoc}
*/
@@ -53,7 +53,7 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@@ -61,21 +61,18 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
public void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
markOccurrencesOfToken(doc, t, h, p);
}
/**
* Highlights all instances of tokens identical to <code>t</code> in the
* specified document.
*
* @param doc
* The document.
* @param t
* The document whose relevant occurrences should be marked.
* @param h
* The highlighter to add the highlights to.
* @param p
* The painter for the highlights.
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
*/
public static final void markOccurrencesOfToken(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
public static final void markOccurrencesOfToken(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h,
SmartHighlightPainter p) {
char[] lexeme = t.getLexeme().toCharArray();
int type = t.getType();
int lineCount = doc.getDefaultRootElement().getElementCount();
@@ -10,46 +10,43 @@ import javax.swing.text.Segment;
/**
* This class generates tokens for a {@link TokenMaker}. This class is here
* because it reuses tokens when they aren't needed anymore to prevent
* This class doesn't actually create new tokens every time
* <code>createToken</code> is called. Instead, it internally keeps a stack of
* available already-created tokens. When more tokens are needed to properly
* display a line, more tokens are added to the available stack. This saves
* from needless repetitive memory allocation. However, it makes it IMPERATIVE
* that users call <code>resetTokenList</code> when creating a new token list so
* that the token maker can keep an accurate list of available tokens.
* because it reuses tokens when they aren't needed anymore to prevent This
* class doesn't actually create new tokens every time <code>createToken</code>
* is called. Instead, it internally keeps a stack of available already-created
* tokens. When more tokens are needed to properly display a line, more tokens
* are added to the available stack. This saves from needless repetitive memory
* allocation. However, it makes it IMPERATIVE that users call
* <code>resetTokenList</code> when creating a new token list so that the token
* maker can keep an accurate list of available tokens.
* <p>
*
* NOTE: This class should only be used by {@link TokenMaker}; nobody else
* needs it!
* NOTE: This class should only be used by {@link TokenMaker}; nobody else needs
* it!
*
* @author Robert Futrell
* @version 0.1
*/
class DefaultTokenFactory implements TokenFactory {
private int size;
private int increment;
private TokenImpl[] tokenList;
private int currentFreeToken;
protected static final int DEFAULT_START_SIZE = 30;
protected static final int DEFAULT_INCREMENT = 10;
/**
* Constructor.
*/
DefaultTokenFactory() {
this(DEFAULT_START_SIZE, DEFAULT_INCREMENT);
}
/**
* Constructor.
*
* @param size
* The initial number of tokens in this factory.
* @param increment
* How many tokens to increment by when the stack gets
* empty.
* @param size The initial number of tokens in this factory.
* @param increment How many tokens to increment by when the stack gets empty.
*/
DefaultTokenFactory(int size, int increment) {
this.size = size;
@@ -61,10 +58,10 @@ class DefaultTokenFactory implements TokenFactory {
tokenList[i] = new TokenImpl();
}
}
/**
* Adds tokens to the internal token list. This is called whenever a
* request is made and no more tokens are available.
* Adds tokens to the internal token list. This is called whenever a request is
* made and no more tokens are available.
*/
private void augmentTokenList() {
TokenImpl[] temp = new TokenImpl[size + increment];
@@ -76,37 +73,37 @@ class DefaultTokenFactory implements TokenFactory {
}
// System.err.println("... size up to: " + size);
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken() {
TokenImpl token = tokenList[currentFreeToken];
token.text = null;
token.setType(Token.NULL);
token.setOffset(-1);
token.setNextToken(null);
currentFreeToken++;
if (currentFreeToken == size) {
token.reset();
if (++currentFreeToken == size) {
augmentTokenList();
}
return token;
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final Segment line, final int beg, final int end, final int startOffset, final int type) {
public TokenImpl createToken(final Segment line, final int beg, final int end, final int startOffset,
final int type) {
return createToken(line.array, beg, end, startOffset, type);
}
/**
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final char[] line, final int beg, final int end, final int startOffset, final int type) {
public TokenImpl createToken(final char[] line, final int beg, final int end, final int startOffset,
final int type) {
// if (tokenList.length - 1 > currentFreeToken)
// return null;
TokenImpl token = tokenList[currentFreeToken];
token.set(line, beg, end, startOffset, type);
currentFreeToken++;
@@ -115,11 +112,11 @@ class DefaultTokenFactory implements TokenFactory {
}
return token;
}
/**
* Resets the state of this token maker. This method should be called
* by the <code>TokenMaker</code> every time a token list is generated for
* a new line so the tokens can be reused.
* Resets the state of this token maker. This method should be called by the
* <code>TokenMaker</code> every time a token list is generated for a new line
* so the tokens can be reused.
*/
@Override
public void resetAllTokens() {
@@ -15,7 +15,7 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
class DefaultTokenMakerFactory extends AbstractTokenMakerFactory implements SyntaxConstants {
/**
* {@inheritDoc}
*/
@@ -20,22 +20,22 @@ import javax.swing.text.TabExpander;
* @version 1.0
*/
class DefaultTokenPainter implements TokenPainter {
/**
* Rectangle used for filling token backgrounds.
*/
private Rectangle2D.Float bgRect;
/**
* Micro-optimization; buffer used to compute tab width. If the width is
* correct it's not re-allocated, to prevent lots of very small garbage.
* Only used when painting tab lines.
* Micro-optimization; buffer used to compute tab width. If the width is correct
* it's not re-allocated, to prevent lots of very small garbage. Only used when
* painting tab lines.
*/
private static char[] tabBuf;
DefaultTokenPainter() {
bgRect = new Rectangle2D.Float();
}
/**
* {@inheritDoc}
*/
@@ -43,57 +43,51 @@ class DefaultTokenPainter implements TokenPainter {
public final float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e) {
return paint(token, g, x, y, host, e, 0);
}
/**
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart) {
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart) {
return paintImpl(token, g, x, y, host, e, clipStart, false, false);
}
/**
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart, boolean paintBG) {
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart, boolean paintBG) {
return paintImpl(token, g, x, y, host, e, clipStart, !paintBG, false);
}
/**
* Paints the background of a token.
*
* @param x
* The x-coordinate of the token.
* @param y
* The y-coordinate of the token.
* @param width
* The width of the token (actually, the width of the part of
* the token to paint).
* @param height
* The height of the token.
* @param g
* The graphics context with which to paint.
* @param fontAscent
* The ascent of the token's font.
* @param host
* The text area.
* @param color
* The color with which to paint.
* @param x The x-coordinate of the token.
* @param y The y-coordinate of the token.
* @param width The width of the token (actually, the width of the part of
* the token to paint).
* @param height The height of the token.
* @param g The graphics context with which to paint.
* @param fontAscent The ascent of the token's font.
* @param host The text area.
* @param color The color with which to paint.
*/
protected void paintBackground(float x, float y, float width, float height, Graphics2D g, int fontAscent, RSyntaxTextArea host,
Color color) {
protected void paintBackground(float x, float y, float width, float height, Graphics2D g, int fontAscent,
RSyntaxTextArea host, Color color) {
g.setColor(color);
bgRect.setRect(x, y - fontAscent, width, height);
// g.fill(bgRect);
g.fillRect((int) x, (int) (y - fontAscent), (int) width, (int) height);
}
/**
* Does the dirty-work of actually painting the token.
*/
protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean selected, boolean useSTC) {
protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart, boolean selected, boolean useSTC) {
int origX = (int) x;
int textOffs = token.getTextOffset();
char[] text = token.getTextArray();
@@ -107,22 +101,22 @@ class DefaultTokenPainter implements TokenPainter {
FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
for (int i = textOffs; i < end; i++) {
switch (text[i]) {
case '\t':
nextX = e.nextTabStop(x + fm.charsWidth(text, flushIndex, flushLen), 0);
if (bg != null) {
paintBackground(x, y, nextX - x, fm.getHeight(), g, fm.getAscent(), host, bg);
}
if (flushLen > 0) {
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
flushLen = 0;
}
flushIndex = i + 1;
x = nextX;
break;
default:
flushLen += 1;
break;
case '\t':
nextX = e.nextTabStop(x + fm.charsWidth(text, flushIndex, flushLen), 0);
if (bg != null) {
paintBackground(x, y, nextX - x, fm.getHeight(), g, fm.getAscent(), host, bg);
}
if (flushLen > 0) {
g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
flushLen = 0;
}
flushIndex = i + 1;
x = nextX;
break;
default:
flushLen += 1;
break;
}
}
nextX = x + fm.charsWidth(text, flushIndex, flushLen);
@@ -152,48 +146,43 @@ class DefaultTokenPainter implements TokenPainter {
}
return nextX;
}
/**
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, boolean useSTC) {
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
boolean useSTC) {
return paintSelected(token, g, x, y, host, e, 0, useSTC);
}
/**
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean useSTC) {
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart, boolean useSTC) {
return paintImpl(token, g, x, y, host, e, clipStart, true, useSTC);
}
/**
* Paints dotted "tab" lines; that is, lines that show where your caret
* would go to on the line if you hit "tab". This visual effect is usually
* done in the leading whitespace token(s) of lines.
* Paints dotted "tab" lines; that is, lines that show where your caret would go
* to on the line if you hit "tab". This visual effect is usually done in the
* leading whitespace token(s) of lines.
*
* @param token
* The token to render.
* @param x
* The starting x-offset of this token. It is assumed that this
* is the left margin of the text area (may be non-zero due to
* insets), since tab lines are only painted for leading
* whitespace.
* @param y
* The baseline where this token was painted.
* @param endX
* The ending x-offset of this token.
* @param g
* The graphics context.
* @param e
* Used to expand tabs.
* @param host
* The text area.
* @param token The token to render.
* @param x The starting x-offset of this token. It is assumed that this is
* the left margin of the text area (may be non-zero due to
* insets), since tab lines are only painted for leading
* whitespace.
* @param y The baseline where this token was painted.
* @param endX The ending x-offset of this token.
* @param g The graphics context.
* @param e Used to expand tabs.
* @param host The text area.
*/
protected void paintTabLines(Token token, int x, int y, int endX, Graphics2D g, TabExpander e, RSyntaxTextArea host) {
protected void paintTabLines(Token token, int x, int y, int endX, Graphics2D g, TabExpander e,
RSyntaxTextArea host) {
// We allow tab lines to be painted in more than just Token.WHITESPACE,
// i.e. for MLC's and Token.IDENTIFIERS (for TokenMakers that return
// whitespace as identifiers for performance). But we only paint tab
@@ -13,30 +13,27 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
public class DocumentRange implements Comparable<DocumentRange> {
private int startOffs;
private int endOffs;
/**
* Constructor.
*
* @param startOffs
* The starting offset in the document, inclusive.
* @param endOffs
* The ending offset in the document, exclusive.
* @throws IllegalArgumentException
* If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument is less than zero.
* @param startOffs The starting offset in the document, inclusive.
* @param endOffs The ending offset in the document, exclusive.
* @throws IllegalArgumentException If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument
* is less than zero.
*/
public DocumentRange(int startOffs, int endOffs) {
set(startOffs, endOffs);
}
/**
* Compares this document range to another.
*
* @param other
* Another document range.
* @param other Another document range.
* @return How the two should be sorted relative to each other.
*/
@Override
@@ -50,14 +47,13 @@ public class DocumentRange implements Comparable<DocumentRange> {
}
return endOffs - other.endOffs;
}
/**
* Returns whether this document range is equal to another one.
*
* @param other
* Another object, presumably a document range.
* @return Whether <code>other</code> is also a document range, and equal
* to this one.
* @param other Another object, presumably a document range.
* @return Whether <code>other</code> is also a document range, and equal to
* this one.
*/
@Override
public boolean equals(Object other) {
@@ -69,7 +65,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
}
return false;
}
/**
* Gets the end offset of the range.
*
@@ -79,7 +75,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int getEndOffset() {
return endOffs;
}
/**
* Gets the starting offset of the range.
*
@@ -89,7 +85,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int getStartOffset() {
return startOffs;
}
/**
* Overridden simply as a best practice, since {@link #equals(Object)} is
* overridden.
@@ -100,29 +96,26 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int hashCode() {
return startOffs + endOffs;
}
/**
* Returns whether this document range has zero length. This can happen,
* for example, with regex searches of forms like
* <code>"foo|"</code>, where the right-hand sub-expression matches empty
* strings.
* Returns whether this document range has zero length. This can happen, for
* example, with regex searches of forms like <code>"foo|"</code>, where the
* right-hand sub-expression matches empty strings.
*
* @return Whether this document range has zero length.
*/
public boolean isZeroLength() {
return startOffs == endOffs;
}
/**
* Sets the document range.
*
* @param start
* The new start value, inclusive.
* @param end
* The new end value, exclusive.
* @throws IllegalArgumentException
* If <code>end</code> is less than
* <code>start</code>, or either argument is less than zero.
* @param start The new start value, inclusive.
* @param end The new end value, exclusive.
* @throws IllegalArgumentException If <code>end</code> is less than
* <code>start</code>, or either argument is
* less than zero.
*/
public void set(int start, int end) {
if (start < 0 || end < 0) {
@@ -134,7 +127,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
this.startOffs = start;
this.endOffs = end;
}
/**
* Returns a string representation of this object.
*
@@ -144,12 +137,11 @@ public class DocumentRange implements Comparable<DocumentRange> {
public String toString() {
return "[DocumentRange: " + startOffs + "-" + endOffs + "]";
}
/**
* Translates this document range by a given amount.
*
* @param amount
* The amount to translate this range by.
* @param amount The amount to translate this range by.
* @return This (modified) range.
*/
public DocumentRange translate(int amount) {
+113 -130
View File
@@ -39,16 +39,15 @@ import org.fife.ui.rsyntaxtextarea.parser.TaskTagParser.TaskNotice;
import org.fife.ui.rtextarea.RTextArea;
/**
* A component to sit alongside an {@link RSyntaxTextArea} that displays
* colored markers for locations of interest (parser errors, marked
* occurrences, etc.).
* A component to sit alongside an {@link RSyntaxTextArea} that displays colored
* markers for locations of interest (parser errors, marked occurrences, etc.).
* <p>
*
* <code>ErrorStrip</code>s display <code>ParserNotice</code>s from
* {@link Parser}s. Currently, the only way to get lines flagged in this
* component is to register a <code>Parser</code> on an RSyntaxTextArea and
* return <code>ParserNotice</code>s for each line to display an icon for.
* The severity of each notice must be at least the threshold set by
* return <code>ParserNotice</code>s for each line to display an icon for. The
* severity of each notice must be at least the threshold set by
* {@link #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)}
* to be displayed in this error strip. The default threshold is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
@@ -76,7 +75,7 @@ import org.fife.ui.rtextarea.RTextArea;
// are now "empty").
//
public class ErrorStrip extends JPanel {
/**
* The text area.
*/
@@ -86,23 +85,23 @@ public class ErrorStrip extends JPanel {
*/
private transient Listener listener;
/**
* Whether "marked occurrences" in the text area should be shown in this
* error strip.
* Whether "marked occurrences" in the text area should be shown in this error
* strip.
*/
private boolean showMarkedOccurrences;
/**
* Whether markers for "mark all" highlights should be shown in this
* error strip.
* Whether markers for "mark all" highlights should be shown in this error
* strip.
*/
private boolean showMarkAll;
/**
* Mapping of colors to brighter colors. This is kept to prevent
* unnecessary creation of the same Colors over and over.
* Mapping of colors to brighter colors. This is kept to prevent unnecessary
* creation of the same Colors over and over.
*/
private Map<Color, Color> brighterColors;
/**
* Only notices of this severity (or worse) will be displayed in this
* error strip.
* Only notices of this severity (or worse) will be displayed in this error
* strip.
*/
private ParserNotice.Level levelThreshold;
/**
@@ -130,12 +129,11 @@ public class ErrorStrip extends JPanel {
*/
private static final int PREFERRED_WIDTH = 14;
private static final ResourceBundle MSG = ResourceBundle.getBundle("org.fife.ui.rsyntaxtextarea.ErrorStrip");
/**
* Constructor.
*
* @param textArea
* The text area we are examining.
* @param textArea The text area we are examining.
*/
public ErrorStrip(RSyntaxTextArea textArea) {
this.textArea = textArea;
@@ -150,10 +148,10 @@ public class ErrorStrip extends JPanel {
setCaretMarkerColor(Color.BLACK);
setMarkerToolTipProvider(null); // Install default
}
/**
* Overridden so we only start listening for parser notices when this
* component (and presumably the text area) are visible.
* Overridden so we only start listening for parser notices when this component
* (and presumably the text area) are visible.
*/
@Override
public void addNotify() {
@@ -165,7 +163,7 @@ public class ErrorStrip extends JPanel {
textArea.addPropertyChangeListener(RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);
refreshMarkers();
}
/**
* Manually manages layout since this component uses no layout manager.
*/
@@ -177,12 +175,11 @@ public class ErrorStrip extends JPanel {
}
listener.caretUpdate(null); // Force recalculation of caret line pos
}
/**
* Returns a "brighter" color.
*
* @param c
* The color.
* @param c The color.
* @return A brighter color.
*/
private Color getBrighterColor(Color c) {
@@ -201,7 +198,7 @@ public class ErrorStrip extends JPanel {
}
return brighter;
}
/**
* returns the color to use when painting the caret marker.
*
@@ -211,7 +208,7 @@ public class ErrorStrip extends JPanel {
public Color getCaretMarkerColor() {
return caretMarkerColor;
}
/**
* Returns whether the caret's position should be drawn.
*
@@ -221,7 +218,7 @@ public class ErrorStrip extends JPanel {
public boolean getFollowCaret() {
return followCaret;
}
/**
* {@inheritDoc}
*/
@@ -230,11 +227,11 @@ public class ErrorStrip extends JPanel {
int height = textArea.getPreferredScrollableViewportSize().height;
return new Dimension(PREFERRED_WIDTH, height);
}
/**
* Returns the minimum severity a parser notice must be for it to be
* displayed in this error strip. This will be one of the constants
* defined in the <code>ParserNotice</code> class.
* Returns the minimum severity a parser notice must be for it to be displayed
* in this error strip. This will be one of the constants defined in the
* <code>ParserNotice</code> class.
*
* @return The minimum severity.
* @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)
@@ -242,7 +239,7 @@ public class ErrorStrip extends JPanel {
public ParserNotice.Level getLevelThreshold() {
return levelThreshold;
}
/**
* Returns whether "mark all" highlights are shown in this error strip.
*
@@ -252,7 +249,7 @@ public class ErrorStrip extends JPanel {
public boolean getShowMarkAll() {
return showMarkAll;
}
/**
* Returns whether marked occurrences are shown in this error strip.
*
@@ -262,7 +259,7 @@ public class ErrorStrip extends JPanel {
public boolean getShowMarkedOccurrences() {
return showMarkedOccurrences;
}
/**
* {@inheritDoc}
*/
@@ -276,13 +273,12 @@ public class ErrorStrip extends JPanel {
}
return text;
}
/**
* Returns the y-offset in this component corresponding to a line in the
* text component.
* Returns the y-offset in this component corresponding to a line in the text
* component.
*
* @param line
* The line.
* @param line The line.
* @return The y-offset.
* @see #yToLine(int)
*/
@@ -291,12 +287,11 @@ public class ErrorStrip extends JPanel {
float lineCount = textArea.getLineCount();
return (int) (((line - 1) / (lineCount - 1)) * (h - 2));
}
/**
* Overridden to (possibly) draw the caret's position.
*
* @param g
* The graphics context.
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
@@ -306,12 +301,11 @@ public class ErrorStrip extends JPanel {
g.fillRect(0, caretLineY, getWidth(), 2);
}
}
/**
* Returns a possibly brighter component for a color.
*
* @param i
* An RGB component for a color (0-255).
* @param i An RGB component for a color (0-255).
* @return A possibly brighter value for the component.
*/
private static int possiblyBrighter(int i) {
@@ -320,7 +314,7 @@ public class ErrorStrip extends JPanel {
}
return i;
}
/**
* Refreshes the markers displayed in this error strip.
*/
@@ -354,16 +348,13 @@ public class ErrorStrip extends JPanel {
revalidate();
repaint();
}
/**
* Adds markers for a list of ranges in the document.
*
* @param ranges
* The list of ranges in the document.
* @param markerMap
* A mapping from line number to <code>Marker</code>.
* @param color
* The color to use for the markers.
* @param ranges The list of ranges in the document.
* @param markerMap A mapping from line number to <code>Marker</code>.
* @param color The color to use for the markers.
*/
private void addMarkersForRanges(List<DocumentRange> ranges, Map<Integer, Marker> markerMap, Color color) {
for (DocumentRange range : ranges) {
@@ -388,7 +379,7 @@ public class ErrorStrip extends JPanel {
}
}
}
/**
* {@inheritDoc}
*/
@@ -401,12 +392,11 @@ public class ErrorStrip extends JPanel {
textArea.removePropertyChangeListener(RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);
textArea.removePropertyChangeListener(RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);
}
/**
* Sets the color to use when painting the caret marker.
*
* @param color
* The new caret marker color.
* @param color The new caret marker color.
* @see #getCaretMarkerColor()
*/
public void setCaretMarkerColor(Color color) {
@@ -415,12 +405,11 @@ public class ErrorStrip extends JPanel {
listener.caretUpdate(null); // Force repaint
}
}
/**
* Toggles whether the caret's current location should be drawn.
*
* @param follow
* Whether the caret's current location should be followed.
* @param follow Whether the caret's current location should be followed.
* @see #getFollowCaret()
*/
public void setFollowCaret(boolean follow) {
@@ -434,15 +423,14 @@ public class ErrorStrip extends JPanel {
listener.caretUpdate(null); // Possibly repaint
}
}
/**
* Sets the minimum severity a parser notice must be for it to be displayed
* in this error strip. This should be one of the constants defined in
* the <code>ParserNotice</code> class. The default value is
* Sets the minimum severity a parser notice must be for it to be displayed in
* this error strip. This should be one of the constants defined in the
* <code>ParserNotice</code> class. The default value is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
*
* @param level
* The new severity threshold.
* @param level The new severity threshold.
* @see #getLevelThreshold()
* @see ParserNotice
*/
@@ -452,25 +440,23 @@ public class ErrorStrip extends JPanel {
refreshMarkers();
}
}
/**
* Sets the provider of tool tips for markers in this error strip.
* Applications can use this method to control the content and format of
* the tool tip descriptions of line markers.
* Sets the provider of tool tips for markers in this error strip. Applications
* can use this method to control the content and format of the tool tip
* descriptions of line markers.
*
* @param provider
* The provider. If this is <code>null</code>, a default
* implementation will be used.
* @param provider The provider. If this is <code>null</code>, a default
* implementation will be used.
*/
public void setMarkerToolTipProvider(ErrorStripMarkerToolTipProvider provider) {
markerToolTipProvider = provider != null ? provider : new DefaultErrorStripMarkerToolTipProvider();
}
/**
* Sets whether "mark all" highlights are shown in this error strip.
*
* @param show
* Whether to show markers for "mark all" highlights.
* @param show Whether to show markers for "mark all" highlights.
* @see #getShowMarkAll()
*/
public void setShowMarkAll(boolean show) {
@@ -481,12 +467,11 @@ public class ErrorStrip extends JPanel {
}
}
}
/**
* Sets whether marked occurrences are shown in this error strip.
*
* @param show
* Whether to show marked occurrences.
* @param show Whether to show marked occurrences.
* @see #getShowMarkedOccurrences()
*/
public void setShowMarkedOccurrences(boolean show) {
@@ -497,13 +482,12 @@ public class ErrorStrip extends JPanel {
}
}
}
/**
* Returns the line in the text area corresponding to a y-offset in this
* component.
*
* @param y
* The y-offset.
* @param y The y-offset.
* @return The line.
* @see #lineToY(int)
*/
@@ -516,15 +500,15 @@ public class ErrorStrip extends JPanel {
}
return line;
}
/**
* The default implementation of the provider of tool tips for markers in
* an error strip.
* The default implementation of the provider of tool tips for markers in an
* error strip.
*
* @author predi
*/
private static class DefaultErrorStripMarkerToolTipProvider implements ErrorStripMarkerToolTipProvider {
@Override
public String getToolTipText(List<ParserNotice> notices) {
String text = null;
@@ -545,34 +529,33 @@ public class ErrorStrip extends JPanel {
return text;
}
}
/**
* Returns tool tip text for the markers in an {@link ErrorStrip} that
* denote one or more parser notices.
* Returns tool tip text for the markers in an {@link ErrorStrip} that denote
* one or more parser notices.
*
* @author predi
*/
public interface ErrorStripMarkerToolTipProvider {
/**
* Returns the tool tip text for a marker in an <code>ErrorStrip</code>
* that denotes a given list of parser notices.
* Returns the tool tip text for a marker in an <code>ErrorStrip</code> that
* denotes a given list of parser notices.
*
* @param notices
* The list of parser notices.
* @return The tool tip text. This may be HTML. Returning
* <code>null</code> will result in no tool tip being displayed.
* @param notices The list of parser notices.
* @return The tool tip text. This may be HTML. Returning <code>null</code> will
* result in no tool tip being displayed.
*/
String getToolTipText(List<ParserNotice> notices);
}
/**
* Listens for events in the error strip and its markers.
*/
private class Listener extends MouseAdapter implements PropertyChangeListener, CaretListener {
private Rectangle visibleRect = new Rectangle();
@Override
public void caretUpdate(CaretEvent e) {
if (getFollowCaret()) {
@@ -587,7 +570,7 @@ public class ErrorStrip extends JPanel {
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
Component source = (Component) e.getSource();
@@ -605,7 +588,7 @@ public class ErrorStrip extends JPanel {
}
}
}
@Override
public void propertyChange(PropertyChangeEvent e) {
String propName = e.getPropertyName();
@@ -636,30 +619,30 @@ public class ErrorStrip extends JPanel {
}
}
}
/**
* A notice that wraps a "marked occurrence" instance.
*/
private class MarkedOccurrenceNotice implements ParserNotice {
private DocumentRange range;
private Color color;
MarkedOccurrenceNotice(DocumentRange range, Color color) {
this.range = range;
this.color = color;
}
@Override
public int compareTo(ParserNotice other) {
return 0; // Value doesn't matter
}
@Override
public boolean containsPosition(int pos) {
return pos >= range.getStartOffset() && pos < range.getEndOffset();
}
@Override
public boolean equals(Object o) {
// FindBugs - Define equals() when defining compareTo()
@@ -668,12 +651,12 @@ public class ErrorStrip extends JPanel {
}
return compareTo((ParserNotice) o) == 0;
}
@Override
public Color getColor() {
return color;
}
/**
* {@inheritDoc}
*/
@@ -681,17 +664,17 @@ public class ErrorStrip extends JPanel {
public boolean getKnowsOffsetAndLength() {
return true;
}
@Override
public int getLength() {
return range.getEndOffset() - range.getStartOffset();
}
@Override
public Level getLevel() {
return Level.INFO; // Won't matter
}
@Override
public int getLine() {
try {
@@ -700,7 +683,7 @@ public class ErrorStrip extends JPanel {
return 0;
}
}
@Override
public String getMessage() {
String text = null;
@@ -713,40 +696,40 @@ public class ErrorStrip extends JPanel {
}
return text;
}
@Override
public int getOffset() {
return range.getStartOffset();
}
@Override
public Parser getParser() {
return null;
}
@Override
public boolean getShowInEditor() {
return false; // Value doesn't matter
}
@Override
public String getToolTipText() {
return null;
}
@Override
public int hashCode() { // FindBugs, since we override equals()
return 0; // Value doesn't matter for us.
}
}
/**
* A "marker" in this error strip, representing one or more notices.
*/
private class Marker extends JComponent {
private List<ParserNotice> notices;
Marker(ParserNotice notice) {
notices = new ArrayList<ParserNotice>(1); // Usually just 1
addNotice(notice);
@@ -754,11 +737,11 @@ public class ErrorStrip extends JPanel {
setSize(getPreferredSize());
ToolTipManager.sharedInstance().registerComponent(this);
}
public void addNotice(ParserNotice notice) {
notices.add(notice);
}
public boolean containsMarkedOccurence() {
boolean result = false;
for (int i = 0; i < notices.size(); i++) {
@@ -769,7 +752,7 @@ public class ErrorStrip extends JPanel {
}
return result;
}
public Color getColor() {
// Return the color for the highest-level parser.
Color c = null;
@@ -782,18 +765,18 @@ public class ErrorStrip extends JPanel {
}
return c;
}
@Override
public Dimension getPreferredSize() {
int w = PREFERRED_WIDTH - 4; // 2-pixel empty border
return new Dimension(w, 5);
}
@Override
public String getToolTipText() {
return markerToolTipProvider.getToolTipText(Collections.unmodifiableList(notices));
}
protected void mouseClicked(MouseEvent e) {
ParserNotice pn = notices.get(0);
int offs = pn.getOffset();
@@ -812,7 +795,7 @@ public class ErrorStrip extends JPanel {
}
}
}
@Override
protected void paintComponent(Graphics g) {
// TODO: Give "priorities" and always pick color of a notice with
@@ -829,14 +812,14 @@ public class ErrorStrip extends JPanel {
g.setColor(borderColor);
g.drawRect(0, 0, w - 1, h - 1);
}
@Override
public void removeNotify() {
super.removeNotify();
ToolTipManager.sharedInstance().unregisterComponent(this);
removeMouseListener(listener);
}
public void updateLocation() {
int line = notices.get(0).getLine();
int y = lineToY(line);
@@ -20,17 +20,16 @@ import java.io.OutputStream;
* @version 1.0
*/
class FileFileLocation extends FileLocation {
/**
* The file. This may or may not actually exist.
*/
private File file;
/**
* Constructor.
*
* @param file
* The local file.
* @param file The local file.
*/
FileFileLocation(File file) {
try {
@@ -40,7 +39,7 @@ class FileFileLocation extends FileLocation {
this.file = file;
}
}
/**
* {@inheritDoc}
*/
@@ -48,7 +47,7 @@ class FileFileLocation extends FileLocation {
protected long getActualLastModified() {
return file.lastModified();
}
/**
* Returns the full path to the file.
*
@@ -59,7 +58,7 @@ class FileFileLocation extends FileLocation {
public String getFileFullPath() {
return file.getAbsolutePath();
}
/**
* {@inheritDoc}
*/
@@ -67,7 +66,7 @@ class FileFileLocation extends FileLocation {
public String getFileName() {
return file.getName();
}
/**
* {@inheritDoc}
*/
@@ -75,7 +74,7 @@ class FileFileLocation extends FileLocation {
protected InputStream getInputStream() throws IOException {
return new FileInputStream(file);
}
/**
* {@inheritDoc}
*/
@@ -83,7 +82,7 @@ class FileFileLocation extends FileLocation {
protected OutputStream getOutputStream() throws IOException {
return new FileOutputStream(file);
}
/**
* Always returns <code>true</code>.
*
@@ -94,10 +93,10 @@ class FileFileLocation extends FileLocation {
public boolean isLocal() {
return true;
}
/**
* Since file locations of this type are guaranteed to be local, this
* method returns whether the file exists.
* Since file locations of this type are guaranteed to be local, this method
* returns whether the file exists.
*
* @return Whether this local file actually exists.
* @see #isLocal()
@@ -14,23 +14,23 @@ import java.net.MalformedURLException;
import java.net.URL;
/**
* Holds the location of a local or remote file. This provides a common way
* to read, write, and check properties of both local and remote files.
* Holds the location of a local or remote file. This provides a common way to
* read, write, and check properties of both local and remote files.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class FileLocation {
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param fileFullPath
* The full path to a local file.
* @param fileFullPath The full path to a local file.
* @return The file's location.
*/
public static FileLocation create(String fileFullPath) {
if (fileFullPath.startsWith("http://") || fileFullPath.startsWith("https://") || fileFullPath.startsWith("ftp://")) {
if (fileFullPath.startsWith("http://") || fileFullPath.startsWith("https://")
|| fileFullPath.startsWith("ftp://")) {
try {
return new URLFileLocation(new URL(fileFullPath));
} catch (MalformedURLException mue) {
@@ -39,23 +39,21 @@ public abstract class FileLocation {
}
return new FileFileLocation(new File(fileFullPath));
}
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param file
* A local file.
* @param file A local file.
* @return The file's location.
*/
public static FileLocation create(File file) {
return new FileFileLocation(file);
}
/**
* Creates a {@link FileLocation} instance for the specified file.
*
* @param url
* The URL of a file.
* @param url The URL of a file.
* @return The file's location.
*/
public static FileLocation create(URL url) {
@@ -64,25 +62,25 @@ public abstract class FileLocation {
}
return new URLFileLocation(url);
}
/**
* Returns the last time this file was modified, or
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be
* computed (such as for a remote file).
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be computed
* (such as for a remote file).
*
* @return The last time this file was modified.
*/
protected abstract long getActualLastModified();
/**
* Returns the full path to the file. This will be stripped of
* sensitive information such as passwords for remote files.
* Returns the full path to the file. This will be stripped of sensitive
* information such as passwords for remote files.
*
* @return The full path to the file.
* @see #getFileName()
*/
public abstract String getFileFullPath();
/**
* Returns the name of the file.
*
@@ -90,26 +88,24 @@ public abstract class FileLocation {
* @see #getFileFullPath()
*/
public abstract String getFileName();
/**
* Opens an input stream for reading from this file.
*
* @return The input stream.
* @throws IOException
* If the file does not exist, or some other IO error
* occurs.
* @throws IOException If the file does not exist, or some other IO error
* occurs.
*/
protected abstract InputStream getInputStream() throws IOException;
/**
* Opens an output stream for writing this file.
*
* @return An output stream.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
protected abstract OutputStream getOutputStream() throws IOException;
/**
* Returns whether this file location is a local file.
*
@@ -117,16 +113,15 @@ public abstract class FileLocation {
* @see #isLocalAndExists()
*/
public abstract boolean isLocal();
/**
* Returns whether this file location is a local file that already
* exists.
* Returns whether this file location is a local file that already exists.
*
* @return Whether this file is local and actually exists.
* @see #isLocal()
*/
public abstract boolean isLocalAndExists();
/**
* Returns whether this file location is a remote location.
*
@@ -28,17 +28,16 @@ import org.fife.ui.rtextarea.IconRowHeader;
* @version 1.0
*/
public class FoldingAwareIconRowHeader extends IconRowHeader {
/**
* Constructor.
*
* @param textArea
* The parent text area.
* @param textArea The parent text area.
*/
public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
super(textArea);
}
/**
* {@inheritDoc}
*/
@@ -160,11 +159,11 @@ public class FoldingAwareIconRowHeader extends IconRowHeader {
}
}
}
/**
* Paints icons when line wrapping is enabled. Note that this does not
* override the parent class's implementation to avoid this version being
* called when line wrapping is disabled.
* Paints icons when line wrapping is enabled. Note that this does not override
* the parent class's implementation to avoid this version being called when
* line wrapping is disabled.
*/
private void paintComponentWrapped(Graphics g) {
// The variables we use are as follows:
@@ -20,49 +20,49 @@ import javax.swing.text.Element;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks occurrences of the current token for HTML. Tags that require a
* closing tag have their "opposite" tag closed.
* Marks occurrences of the current token for HTML. Tags that require a closing
* tag have their "opposite" tag closed.
*
* @author Robert Futrell
* @version 1.0
*/
public class HtmlOccurrenceMarker implements OccurrenceMarker {
private static final char[] CLOSE_TAG_START = { '<', '/' };
private static final char[] TAG_SELF_CLOSE = { '/', '>' };
private static final Set<String> TAGS_REQUIRING_CLOSING = getRequiredClosingTags();
public static final Set<String> getRequiredClosingTags() {
final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav", "article", "aside", "h1",
"h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog", "blockquote", "ol", "ul", "dl", "a", "q",
"cite", "em", "strong", "small", "mark", "dfn", "abbr", "time", "progress", "meter", "code", "var", "samp", "kbd", "sub",
"sup", "span", "i", "b", "bdo", "ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas",
"map", "table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output", "details",
"bb", "menu", "legend", "div",
final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav",
"article", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog",
"blockquote", "ol", "ul", "dl", "a", "q", "cite", "em", "strong", "small", "mark", "dfn", "abbr",
"time", "progress", "meter", "code", "var", "samp", "kbd", "sub", "sup", "span", "i", "b", "bdo",
"ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas", "map",
"table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output",
"details", "bb", "menu", "legend", "div",
// Obsolete elements
"acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing", "marquee", "nobr",
"noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", };
"acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing",
"marquee", "nobr", "noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", };
return new HashSet<String>(Arrays.asList(tags));
}
/**
* If the caret is inside of a tag, this method returns the token
* representing the tag name; otherwise, <code>null</code> is returned.
* If the caret is inside of a tag, this method returns the token representing
* the tag name; otherwise, <code>null</code> is returned.
* <p>
*
* Currently, this method only checks for tag names on the same line as
* the caret, for simplicity. In the future it could check prior lines
* until the tag name is found.
* Currently, this method only checks for tag names on the same line as the
* caret, for simplicity. In the future it could check prior lines until the tag
* name is found.
*
* @param textArea
* The text area.
* @param occurrenceMarker
* The occurrence marker.
* @return The token to mark occurrences of. Note that, if the
* specified occurrence marker identifies tokens other than
* tag names, these other element types may be returned.
* @param textArea The text area.
* @param occurrenceMarker The occurrence marker.
* @return The token to mark occurrences of. Note that, if the specified
* occurrence marker identifies tokens other than tag names, these other
* element types may be returned.
*/
public static final Token getTagNameTokenForCaretOffset(RSyntaxTextArea textArea, OccurrenceMarker occurrenceMarker) {
public static final Token getTagNameTokenForCaretOffset(RSyntaxTextArea textArea,
OccurrenceMarker occurrenceMarker) {
// Get the tag name token.
// For now, we only check for tags on the current line, for simplicity.
int dot = textArea.getCaretPosition();
@@ -93,7 +93,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
}
return toMark;
}
/**
* {@inheritDoc}
*/
@@ -101,7 +101,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
public Token getTokenToMark(RSyntaxTextArea textArea) {
return getTagNameTokenForCaretOffset(textArea, this);
}
/**
* {@inheritDoc}
*/
@@ -109,7 +109,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType());
}
/**
* {@inheritDoc}
*/
@@ -254,15 +254,15 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
} while (curLine >= 0);
}
}
/**
* Used internally when searching backward for a matching "open" tag.
*/
private static class Entry {
private boolean open;
private Token t;
Entry(boolean open, Token t) {
this.open = open;
this.t = t;
@@ -28,17 +28,14 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0
*/
public interface LinkGenerator {
/**
* If a region of text under the mouse position should be considered a
* hyperlink, a result object is returned. This object describes what
* region of text is the link, and what action to perform if the link is
* clicked.
* hyperlink, a result object is returned. This object describes what region of
* text is the link, and what action to perform if the link is clicked.
*
* @param textArea
* The text component.
* @param offs
* The offset in the document under the mouse position.
* @param textArea The text component.
* @param offs The offset in the document under the mouse position.
* @return The link information, or <code>null</code> if no link is at the
* specified offset.
*/
@@ -23,19 +23,19 @@ import javax.swing.event.HyperlinkEvent;
* @see SelectRegionLinkGeneratorResult
*/
public interface LinkGeneratorResult {
/**
* Executes the action associated with this object. If the result is a
* URL to open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this
* method itself.
* Executes the action associated with this object. If the result is a URL to
* open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this method
* itself.
*
* @return The hyperlink event to broadcast from the text area, or
* <code>null</code> if the action's behavior occurs in this method
* directly.
*/
HyperlinkEvent execute();
/**
* Returns the starting offset of the link specified by the parent
* <code>LinkGenerator</code>.
@@ -19,15 +19,15 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks all occurrences of the token at the current caret position, if it is
* an identifier.
* Marks all occurrences of the token at the current caret position, if it is an
* identifier.
*
* @author Robert Futrell
* @version 1.0
* @see OccurrenceMarker
*/
class MarkOccurrencesSupport implements CaretListener, ActionListener {
private RSyntaxTextArea textArea;
private Timer timer;
private SmartHighlightPainter p;
@@ -39,36 +39,33 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
* The default delay.
*/
static final int DEFAULT_DELAY_MS = 1000;
/**
* Constructor. Creates a listener with a 1 second delay.
*/
MarkOccurrencesSupport() {
this(DEFAULT_DELAY_MS);
}
/**
* Constructor.
*
* @param delay
* The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param delay The delay between when the caret last moves and when the text
* should be scanned for matching occurrences. This should be in
* milliseconds.
*/
MarkOccurrencesSupport(int delay) {
this(delay, DEFAULT_COLOR);
}
/**
* Constructor.
*
* @param delay
* The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param color
* The color to use to mark the occurrences. This cannot be
* <code>null</code>.
* @param delay The delay between when the caret last moves and when the text
* should be scanned for matching occurrences. This should be in
* milliseconds.
* @param color The color to use to mark the occurrences. This cannot be
* <code>null</code>.
*/
MarkOccurrencesSupport(int delay, Color color) {
timer = new Timer(delay, this);
@@ -76,18 +73,17 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
p = new SmartHighlightPainter();
setColor(color);
}
/**
* Called after the caret has been moved and a fixed time delay has
* elapsed. This locates and highlights all occurrences of the identifier
* at the caret position, if any.
* Called after the caret has been moved and a fixed time delay has elapsed.
* This locates and highlights all occurrences of the identifier at the caret
* position, if any.
* <p>
*
* Callers should not call this method directly, but should rather prefer
* {@link #doMarkOccurrences()} to mark occurrences.
*
* @param e
* The event.
* @param e The event.
* @see #doMarkOccurrences()
*/
@Override
@@ -124,18 +120,17 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
textArea.fireMarkedOccurrencesChanged();
}
}
/**
* Called when the caret moves in the text area.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void caretUpdate(CaretEvent e) {
timer.restart();
}
/**
* Removes all highlights added to the text area by this listener.
*/
@@ -145,16 +140,15 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
h.clearMarkOccurrencesHighlights();
}
}
/**
* Immediately marks all occurrences of the token at the current caret
* position.
* Immediately marks all occurrences of the token at the current caret position.
*/
public void doMarkOccurrences() {
timer.stop();
actionPerformed(null);
}
/**
* Returns the color being used to mark occurrences.
*
@@ -164,7 +158,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public Color getColor() {
return (Color) p.getPaint();
}
/**
* Returns the delay, in milliseconds.
*
@@ -174,7 +168,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public int getDelay() {
return timer.getDelay();
}
/**
* Returns whether a border is painted around marked occurrences.
*
@@ -185,13 +179,12 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public boolean getPaintBorder() {
return p.getPaintBorder();
}
/**
* Installs this listener on a text area. If it is already installed on
* another text area, it is uninstalled first.
* Installs this listener on a text area. If it is already installed on another
* text area, it is uninstalled first.
*
* @param textArea
* The text area to install on.
* @param textArea The text area to install on.
*/
public void install(RSyntaxTextArea textArea) {
if (this.textArea != null) {
@@ -203,12 +196,11 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
setColor(textArea.getMarkOccurrencesColor());
}
}
/**
* Sets the color to use when marking occurrences.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #getColor()
* @see #setPaintBorder(boolean)
*/
@@ -219,25 +211,23 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
caretUpdate(null); // Force a highlight repaint.
}
}
/**
* Sets the delay between the last caret position change and when the
* text is scanned for matching identifiers. A delay is needed to prevent
* repeated scanning while the user is typing.
* Sets the delay between the last caret position change and when the text is
* scanned for matching identifiers. A delay is needed to prevent repeated
* scanning while the user is typing.
*
* @param delay
* The new delay.
* @param delay The new delay.
* @see #getDelay()
*/
public void setDelay(int delay) {
timer.setInitialDelay(delay);
}
/**
* Toggles whether a border is painted around marked highlights.
*
* @param paint
* Whether to paint a border.
* @param paint Whether to paint a border.
* @see #getPaintBorder()
* @see #setColor(Color)
*/
@@ -249,10 +239,10 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
}
}
}
/**
* Uninstalls this listener from the current text area. Does nothing if
* it not currently installed on any text area.
* Uninstalls this listener from the current text area. Does nothing if it not
* currently installed on any text area.
*
* @see #install(RSyntaxTextArea)
*/
@@ -41,11 +41,11 @@ import org.fife.ui.rsyntaxtextarea.focusabletip.TipUtil;
* @version 1.0
*/
class MatchedBracketPopup extends JWindow {
private RSyntaxTextArea textArea;
private transient Listener listener;
private static final int LEFT_EMPTY_BORDER = 5;
MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int offsToRender) {
super(parent);
this.textArea = textArea;
@@ -59,7 +59,7 @@ class MatchedBracketPopup extends JWindow {
listener = new Listener();
setLocation();
}
/**
* Overridden to ensure this popup stays in a specific size range.
*/
@@ -71,7 +71,7 @@ class MatchedBracketPopup extends JWindow {
}
return size;
}
private String getText(int offsToRender) {
int line = 0;
try {
@@ -110,7 +110,7 @@ class MatchedBracketPopup extends JWindow {
}
return sb.toString();
}
/**
* Adds key bindings to this popup.
*/
@@ -121,10 +121,9 @@ class MatchedBracketPopup extends JWindow {
im.put(escapeKS, "onEscape");
am.put("onEscape", new EscapeAction());
}
/**
* Positions this popup to be in the top right-hand corner of the parent
* editor.
* Positions this popup to be in the top right-hand corner of the parent editor.
*/
private void setLocation() {
Point topLeft = textArea.getVisibleRect().getLocation();
@@ -132,23 +131,23 @@ class MatchedBracketPopup extends JWindow {
topLeft.y = Math.max(topLeft.y - 24, 0);
setLocation(topLeft.x - LEFT_EMPTY_BORDER, topLeft.y);
}
/**
* Action performed when Escape is pressed in this popup.
*/
private class EscapeAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
listener.uninstallAndHide();
}
}
/**
* Listens for events in this popup.
*/
private class Listener extends WindowAdapter implements ComponentListener {
Listener() {
addWindowFocusListener(this);
// If anything happens to the "parent" window, hide this popup
@@ -157,42 +156,42 @@ class MatchedBracketPopup extends JWindow {
parent.addWindowListener(this);
parent.addComponentListener(this);
}
@Override
public void componentResized(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentMoved(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentShown(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void componentHidden(ComponentEvent e) {
uninstallAndHide();
}
@Override
public void windowActivated(WindowEvent e) {
checkForParentWindowEvent(e);
}
@Override
public void windowLostFocus(WindowEvent e) {
uninstallAndHide();
}
@Override
public void windowIconified(WindowEvent e) {
checkForParentWindowEvent(e);
}
private boolean checkForParentWindowEvent(WindowEvent e) {
if (e.getSource() == getParent()) {
uninstallAndHide();
@@ -200,7 +199,7 @@ class MatchedBracketPopup extends JWindow {
}
return false;
}
private void uninstallAndHide() {
Window parent = (Window) getParent();
parent.removeWindowFocusListener(this);
@@ -10,52 +10,44 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* An <code>OccurrenceMarker</code> is called when the caret stops moving after
* a short period. If the current {@link TokenMaker} returns an instance of
* this class, it is told to mark all occurrences of the identifier at the
* caret position.
* a short period. If the current {@link TokenMaker} returns an instance of this
* class, it is told to mark all occurrences of the identifier at the caret
* position.
*
* @author Robert Futrell
* @version 1.0
*/
public interface OccurrenceMarker {
/**
* Returns the token to mark occurrences, of, provided it matches the
* criteria put forth by {@link #isValidType(RSyntaxTextArea, Token)}.
* For most languages, this method should return the token at the caret
* position.
* Returns the token to mark occurrences, of, provided it matches the criteria
* put forth by {@link #isValidType(RSyntaxTextArea, Token)}. For most
* languages, this method should return the token at the caret position.
*
* @param textArea
* The text area.
* @return The token to (possibly) mark occurrences of, or
* <code>null</code> if none.
* @param textArea The text area.
* @return The token to (possibly) mark occurrences of, or <code>null</code> if
* none.
*/
Token getTokenToMark(RSyntaxTextArea textArea);
/**
* Returns whether the specified token is a type that we can do a
* "mark occurrences" of. Typically, this will delegate to
* Returns whether the specified token is a type that we can do a "mark
* occurrences" of. Typically, this will delegate to
* {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}.
*
* @param textArea
* The text area.
* @param t
* The token.
* @param textArea The text area.
* @param t The token.
* @return Whether we should mark all occurrences of this token.
*/
boolean isValidType(RSyntaxTextArea textArea, Token t);
/**
* Called when occurrences of a token should be marked.
*
* @param doc
* The document.
* @param t
* The document whose relevant occurrences should be marked.
* @param h
* The highlighter to add the highlights to.
* @param p
* The painter for the highlights.
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
*/
void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p);
}
+116 -153
View File
@@ -46,7 +46,7 @@ import org.fife.ui.rtextarea.RTextAreaHighlighter.HighlightInfo;
* @version 0.9
*/
class ParserManager implements DocumentListener, ActionListener, HyperlinkListener, PropertyChangeListener {
private RSyntaxTextArea textArea;
private List<Parser> parsers;
private Timer timer;
@@ -55,20 +55,21 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
private Position firstOffsetModded;
private Position lastOffsetModded;
/**
* Mapping of notices to their highlights in the editor. Can't use a Map
* since parsers could return two <code>ParserNotice</code>s that compare
* equally via <code>equals()</code>. Real-world example: The Perl
* compiler will return 2+ identical error messages if the same error is
* committed in a single line more than once.
* Mapping of notices to their highlights in the editor. Can't use a Map since
* parsers could return two <code>ParserNotice</code>s that compare equally via
* <code>equals()</code>. Real-world example: The Perl compiler will return 2+
* identical error messages if the same error is committed in a single line more
* than once.
*/
private List<NoticeHighlightPair> noticeHighlightPairs;
/**
* Painter used to underline errors.
*/
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter = new SquiggleUnderlineHighlightPainter(Color.RED);
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter = new SquiggleUnderlineHighlightPainter(
Color.RED);
/**
* If this system property is set to <code>true</code>, debug messages
* will be printed to stdout to help diagnose parsing issues.
* If this system property is set to <code>true</code>, debug messages will be
* printed to stdout to help diagnose parsing issues.
*/
private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing";
/**
@@ -76,31 +77,26 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
*/
private static final boolean DEBUG_PARSING;
/**
* The default delay between the last key press and when the document
* is parsed, in milliseconds.
* The default delay between the last key press and when the document is parsed,
* in milliseconds.
*/
private static final int DEFAULT_DELAY_MS = 1250;
/**
* Constructor.
*
* @param textArea
* The text area whose document the parser will be
* parsing.
* @param textArea The text area whose document the parser will be parsing.
*/
ParserManager(RSyntaxTextArea textArea) {
this(DEFAULT_DELAY_MS, textArea);
}
/**
* Constructor.
*
* @param delay
* The delay between the last key press and when the document
* is parsed.
* @param textArea
* The text area whose document the parser will be
* parsing.
* @param delay The delay between the last key press and when the document is
* parsed.
* @param textArea The text area whose document the parser will be parsing.
*/
ParserManager(int delay, RSyntaxTextArea textArea) {
this.textArea = textArea;
@@ -111,12 +107,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.setRepeats(false);
running = true;
}
/**
* Called when the timer fires (e.g. it's time to parse the document).
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void actionPerformed(ActionEvent e) {
@@ -132,7 +127,8 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
Element root = doc.getDefaultRootElement();
int firstLine = firstOffsetModded == null ? 0 : root.getElementIndex(firstOffsetModded.getOffset());
int lastLine = lastOffsetModded == null ? root.getElementCount() - 1 : root.getElementIndex(lastOffsetModded.getOffset());
int lastLine = lastOffsetModded == null ? root.getElementCount() - 1
: root.getElementIndex(lastOffsetModded.getOffset());
firstOffsetModded = lastOffsetModded = null;
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine);
@@ -158,13 +154,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
System.out.println("Total parsing time: " + time + " seconds");
}
}
/**
* Adds a parser for the text area.
*
* @param parser
* The new parser. If this is <code>null</code>, nothing
* happens.
* @param parser The new parser. If this is <code>null</code>, nothing happens.
* @see #getParser(int)
* @see #removeParser(Parser)
*/
@@ -183,13 +177,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
}
}
/**
* Adds highlights for a list of parser notices. Any current notices
* from the same Parser, in the same parsed range, are removed.
* Adds highlights for a list of parser notices. Any current notices from the
* same Parser, in the same parsed range, are removed.
*
* @param res
* The result of a parsing.
* @param res The result of a parsing.
* @see #clearParserNoticeHighlights()
*/
private void addParserNoticeHighlights(ParseResult res) {
@@ -227,16 +220,16 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
System.out.println("[DEBUG]: Done adding parser notices from " + res.getParser());
}
}
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
@Override
public void changedUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {
}
private void clearParserNoticeHighlights() {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
if (h != null) {
@@ -246,12 +239,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
noticeHighlightPairs.clear();
}
}
/**
* Removes all parser notice highlights for a specific parser.
*
* @param parser
* The parser whose highlights to remove.
* @param parser The parser whose highlights to remove.
*/
private void clearParserNoticeHighlights(Parser parser) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
@@ -268,7 +260,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
}
}
/**
* Removes all parsers and any highlights they have created.
*
@@ -280,20 +272,18 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
parsers.clear();
textArea.fireParserNoticesChange();
}
/**
* Forces the given {@link Parser} to re-parse the content of this text
* area.
* Forces the given {@link Parser} to re-parse the content of this text area.
* <p>
*
* This method can be useful when a <code>Parser</code> can be configured
* as to what notices it returns. For example, if a Java language parser
* can be configured to set whether no serialVersionUID is a warning,
* error, or ignored, this method can be called after changing the expected
* notice type to have the document re-parsed.
* This method can be useful when a <code>Parser</code> can be configured as to
* what notices it returns. For example, if a Java language parser can be
* configured to set whether no serialVersionUID is a warning, error, or
* ignored, this method can be called after changing the expected notice type to
* have the document re-parsed.
*
* @param parser
* The index of the <code>Parser</code> to re-run.
* @param parser The index of the <code>Parser</code> to re-run.
* @see #getParser(int)
*/
public void forceReparsing(int parser) {
@@ -313,10 +303,10 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
doc.readUnlock();
}
}
/**
* Returns the delay between the last "concurrent" edit and when the
* document is re-parsed.
* Returns the delay between the last "concurrent" edit and when the document is
* re-parsed.
*
* @return The delay, in milliseconds.
* @see #setDelay(int)
@@ -324,12 +314,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
public int getDelay() {
return timer.getDelay();
}
/**
* Returns the specified parser.
*
* @param index
* The index of the parser.
* @param index The index of the parser.
* @return The parser.
* @see #getParserCount()
* @see #addParser(Parser)
@@ -338,7 +327,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
public Parser getParser(int index) {
return parsers.get(index);
}
/**
* Returns the number of registered parsers.
*
@@ -347,14 +336,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
public int getParserCount() {
return parsers.size();
}
/**
* Returns a list of the current parser notices for this text area.
* This method (like most Swing methods) should only be called on the
* EDT.
* Returns a list of the current parser notices for this text area. This method
* (like most Swing methods) should only be called on the EDT.
*
* @return The list of notices. This will be an empty list if there are
* none.
* @return The list of notices. This will be an empty list if there are none.
*/
public List<ParserNotice> getParserNotices() {
List<ParserNotice> notices = new ArrayList<ParserNotice>();
@@ -365,15 +352,13 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
return notices;
}
/**
* Returns the tool tip to display for a mouse event at the given
* location. This method is overridden to give a registered parser a
* chance to display a tool tip (such as an error description when the
* mouse is over an error highlight).
* Returns the tool tip to display for a mouse event at the given location. This
* method is overridden to give a registered parser a chance to display a tool
* tip (such as an error description when the mouse is over an error highlight).
*
* @param e
* The mouse event.
* @param e The mouse event.
* @return The tool tip to display, and possibly a hyperlink event handler.
*/
public ToolTipInfo getToolTipText(MouseEvent e) {
@@ -385,19 +370,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
int pos = textArea.viewToModel(p);
/*
* Highlighter.Highlight[] highlights = textArea.getHighlighter().
* getHighlights();
* for (int i=0; i<highlights.length; i++) {
* Highlighter.Highlight h = highlights[i];
* //if (h instanceof ParserNoticeHighlight) {
* // ParserNoticeHighlight pnh = (ParserNoticeHighlight)h;
* int start = h.getStartOffset();
* int end = h.getEndOffset();
* if (start<=pos && end>=pos) {
* //return pnh.getMessage();
* return textArea.getText(start, end-start);
* }
* //}
* }
* getHighlights(); for (int i=0; i<highlights.length; i++) {
* Highlighter.Highlight h = highlights[i]; //if (h instanceof
* ParserNoticeHighlight) { // ParserNoticeHighlight pnh =
* (ParserNoticeHighlight)h; int start = h.getStartOffset(); int end =
* h.getEndOffset(); if (start<=pos && end>=pos) { //return pnh.getMessage();
* return textArea.getText(start, end-start); } //} }
*/
if (noticeHighlightPairs != null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
@@ -418,25 +396,23 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
URL imageBase = parserForTip == null ? null : parserForTip.getImageBase();
return new ToolTipInfo(tip, listener, imageBase);
}
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
public void handleDocumentEvent(DocumentEvent e) {
if (running && parsers.size() > 0) {
timer.restart();
}
}
/**
* Called when the user clicks a hyperlink in a
* {@link org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip}.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
@@ -444,12 +420,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
parserForTip.getHyperlinkListener().linkClicked(textArea, e);
}
}
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
@Override
public void insertUpdate(DocumentEvent e) {
@@ -469,14 +444,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
handleDocumentEvent(e);
}
/**
* Returns whether a parser notice contains the specified offset.
*
* @param notice
* The notice.
* @param offs
* The offset.
* @param notice The notice.
* @param offs The offset.
* @return Whether the notice contains the offset.
*/
private boolean noticeContainsPosition(ParserNotice notice, int offs) {
@@ -492,20 +465,18 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
Element elem = root.getElement(line);
return elem != null && offs >= elem.getStartOffset() && offs < elem.getEndOffset();
}
/**
* Since <code>viewToModel()</code> returns the <em>closest</em> model
* position, and the position doesn't <em>necessarily</em> contain the
* point passed in as an argument, this method checks whether the point is
* indeed contained in the view rectangle for the specified offset.
* Since <code>viewToModel()</code> returns the <em>closest</em> model position,
* and the position doesn't <em>necessarily</em> contain the point passed in as
* an argument, this method checks whether the point is indeed contained in the
* view rectangle for the specified offset.
*
* @param notice
* The parser notice.
* @param p
* The point possibly contained in the view range of the
* parser notice.
* @return Whether the parser notice actually contains the specified point
* in the view.
* @param notice The parser notice.
* @param p The point possibly contained in the view range of the parser
* notice.
* @return Whether the parser notice actually contains the specified point in
* the view.
*/
private boolean noticeContainsPointInView(ParserNotice notice, Point p) {
try {
@@ -543,12 +514,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
return true;
}
}
/**
* Called when a property we're interested in changes.
*
* @param e
* The property change event.
* @param e The property change event.
*/
@Override
public void propertyChange(PropertyChangeEvent e) {
@@ -565,12 +535,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
}
}
/**
* Removes a parser.
*
* @param parser
* The parser to remove.
* @param parser The parser to remove.
* @return Whether the parser was found.
* @see #addParser(Parser)
* @see #getParser(int)
@@ -583,13 +552,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
return removed;
}
/**
* Removes all parser notices (and clears highlights in the editor) from
* a particular parser.
* Removes all parser notices (and clears highlights in the editor) from a
* particular parser.
*
* @param parser
* The parser.
* @param parser The parser.
*/
private void removeParserNotices(Parser parser) {
if (noticeHighlightPairs != null) {
@@ -603,14 +571,13 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
}
}
/**
* Removes any currently stored notices (and the corresponding highlights
* from the editor) from the same Parser, and in the given line range,
* as in the results.
* Removes any currently stored notices (and the corresponding highlights from
* the editor) from the same Parser, and in the given line range, as in the
* results.
*
* @param res
* The results.
* @param res The results.
*/
private void removeParserNotices(ParseResult res) {
if (noticeHighlightPairs != null) {
@@ -632,12 +599,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
}
}
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
@Override
public void removeUpdate(DocumentEvent e) {
@@ -658,7 +624,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
handleDocumentEvent(e);
}
/**
* Restarts parsing the document.
*
@@ -668,14 +634,13 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.restart();
running = true;
}
/**
* Sets the delay between the last "concurrent" edit and when the document
* is re-parsed.
* Sets the delay between the last "concurrent" edit and when the document is
* re-parsed.
*
* @param millis
* The new delay, in milliseconds. This must be greater
* than <code>0</code>.
* @param millis The new delay, in milliseconds. This must be greater than
* <code>0</code>.
* @see #getDelay()
*/
public void setDelay(int millis) {
@@ -688,20 +653,18 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.start();
}
}
/**
* Returns whether a parser notice should be removed, based on a parse
* result.
* Returns whether a parser notice should be removed, based on a parse result.
*
* @param notice
* The notice in question.
* @param res
* The result.
* @param notice The notice in question.
* @param res The result.
* @return Whether the notice should be removed.
*/
private boolean shouldRemoveNotice(ParserNotice notice, ParseResult res) {
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: ... ... shouldRemoveNotice " + notice + ": " + (notice.getParser() == res.getParser()));
System.out.println(
"[DEBUG]: ... ... shouldRemoveNotice " + notice + ": " + (notice.getParser() == res.getParser()));
}
// NOTE: We must currently remove all notices for the parser. Parser
// implementors are required to parse the entire document each parsing
@@ -712,7 +675,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
// return same_parser && (in_reparsed_range || in_deleted_end_of_doc)
return notice.getParser() == res.getParser();
}
/**
* Stops parsing the document.
*
@@ -722,21 +685,21 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.stop();
running = false;
}
/**
* Mapping of a parser notice to its highlight in the editor.
*/
private static class NoticeHighlightPair {
private ParserNotice notice;
private HighlightInfo highlight;
NoticeHighlightPair(ParserNotice notice, HighlightInfo highlight) {
this.notice = notice;
this.highlight = highlight;
}
}
static {
boolean debugParsing = false;
try {
@@ -12,48 +12,45 @@ import javax.swing.JWindow;
/**
* A hook allowing hosting applications to decorate JWindows created by the
* AutoComplete library. For example, you could use the
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows
* to the windows.
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows to
* the windows.
*
* @author Robert Futrell
* @version 1.0
*/
public abstract class PopupWindowDecorator {
/**
* The singleton instance of this class.
*/
private static PopupWindowDecorator decorator;
/**
* Callback called whenever an appropriate JWindow is created by the
* AutoComplete library. Implementations can decorate the window however
* they see fit.
* AutoComplete library. Implementations can decorate the window however they
* see fit.
*
* @param window
* The newly-created window.
* @param window The newly-created window.
*/
public abstract void decorate(JWindow window);
/**
* Returns the singleton instance of this class. This should only be
* called on the EDT.
* Returns the singleton instance of this class. This should only be called on
* the EDT.
*
* @return The singleton instance of this class, or <code>null</code>
* for none.
* @return The singleton instance of this class, or <code>null</code> for none.
* @see #set(PopupWindowDecorator)
*/
public static PopupWindowDecorator get() {
return decorator;
}
/**
* Sets the singleton instance of this class. This should only be called
* on the EDT.
* Sets the singleton instance of this class. This should only be called on the
* EDT.
*
* @param decorator
* The new instance of this class. This may be
* <code>null</code>.
* @param decorator The new instance of this class. This may be
* <code>null</code>.
* @see #get()
*/
public static void set(PopupWindowDecorator decorator) {
+20 -28
View File
@@ -17,46 +17,38 @@ import javax.swing.text.BadLocationException;
* @version 1.0
*/
interface RSTAView {
/**
* Returns the y-coordinate of the specified line.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
* This method is quicker than using traditional <code>modelToView(int)</code>
* calls, as the entire bounding box isn't computed.
*
* @param alloc
* The area the text area can render into.
* @param line
* The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException
* If <code>line</code> isn't a valid line
* number for this document.
* @param alloc The area the text area can render into.
* @param line The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if this
* text area doesn't yet have a positive size or the line is hidden
* (i.e. from folding).
* @throws BadLocationException If <code>line</code> isn't a valid line number
* for this document.
*/
int yForLine(Rectangle alloc, int line) throws BadLocationException;
/**
* Returns the y-coordinate of the line containing a specified offset.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
* This method is quicker than using traditional <code>modelToView(int)</code>
* calls, as the entire bounding box isn't computed.
*
* @param alloc
* The area the text area can render into.
* @param offs
* The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException
* If <code>offs</code> isn't a valid offset
* into the document.
* @param alloc The area the text area can render into.
* @param offs The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if this
* text area doesn't yet have a positive size or the line is hidden
* (i.e. from folding).
* @throws BadLocationException If <code>offs</code> isn't a valid offset into
* the document.
*/
int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException;
}
@@ -49,10 +49,9 @@ import me.ByteEdit.edit.JavaAssemblyTokenMaker;
* @version 1.0
*/
public class RSyntaxDocument extends RDocument implements Iterable<Token>, SyntaxConstants {
/**
* Creates a {@link TokenMaker} appropriate for a given programming
* language.
* Creates a {@link TokenMaker} appropriate for a given programming language.
*/
private transient TokenMakerFactory tokenMakerFactory;
/**
@@ -64,8 +63,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
*/
private String syntaxStyle;
/**
* Array of values representing the "last token type" on each line. This is
* used
* Array of values representing the "last token type" on each line. This is used
* in cases such as multi-line comments: if the previous line ended with an
* (unclosed) multi-line comment, we can use this knowledge and start the
* current line's syntax highlighting in multi-line comment state.
@@ -77,35 +75,28 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
private transient int tokenRetrievalCount = 0;
private transient Segment s;
/**
* If this is set to <code>true</code>, debug information about how much
* token
* If this is set to <code>true</code>, debug information about how much token
* caching is helping is printed to stdout.
*/
private static final boolean DEBUG_TOKEN_CACHING = false;
/**
* Constructs a plain text document. A default root element is created, and
* the
* Constructs a plain text document. A default root element is created, and the
* tab size set to 5.
*
* @param syntaxStyle
* The syntax highlighting scheme to use.
* @param syntaxStyle The syntax highlighting scheme to use.
*/
public RSyntaxDocument(String syntaxStyle) {
this(null, syntaxStyle);
}
/**
* Constructs a plain text document. A default root element is created, and
* the
* Constructs a plain text document. A default root element is created, and the
* tab size set to 5.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this
* is
* <code>null</code>, a default factory is used.
* @param syntaxStyle
* The syntax highlighting scheme to use.
* @param tmf The <code>TokenMakerFactory</code> for this document. If
* this is <code>null</code>, a default factory is used.
* @param syntaxStyle The syntax highlighting scheme to use.
*/
public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
putProperty(tabSizeAttribute, Integer.valueOf(5));
@@ -115,27 +106,23 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
setTokenMakerFactory(tmf);
setSyntaxStyle(syntaxStyle);
}
/**
* Alerts all listeners to this document of an insertion. This is overridden
* so
* Alerts all listeners to this document of an insertion. This is overridden so
* we can update our syntax highlighting stuff.
* <p>
* The syntax highlighting stuff has to be here instead of in
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called
* by
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called by
* the undo/redo actions, but this method is.
*
* @param e
* The change.
* @param e The change.
*/
@Override
protected void fireInsertUpdate(DocumentEvent e) {
cachedTokenList = null;
/*
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and
* "last tokens on lines"
* structure, we can update our token elements and "last tokens on lines"
* structure.
*/
Element lineMap = getDefaultRootElement();
@@ -174,23 +161,20 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
// Let all listeners know about the insertion.
super.fireInsertUpdate(e);
}
/**
* This method is called AFTER the content has been inserted into the
* document
* This method is called AFTER the content has been inserted into the document
* and the element structure has been updated.
* <p>
* The syntax-highlighting updates need to be done here (as opposed to an
* override of <code>postRemoveUpdate</code>) as this method is called in
* response to undo/redo events, whereas <code>postRemoveUpdate</code> is
* not.
* response to undo/redo events, whereas <code>postRemoveUpdate</code> is not.
* <p>
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and "last tokens on lines"
* structure.
*
* @param chng
* The change that occurred.
* @param chng The change that occurred.
* @see #removeUpdate
*/
@Override
@@ -240,26 +224,22 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
// Let all of our listeners know about the removal.
super.fireRemoveUpdate(chng);
}
/**
* Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
*
* @param type
* The token type.
* @return The closest "standard" token type. If a mapping is not defined
* for
* @param type The token type.
* @return The closest "standard" token type. If a mapping is not defined for
* this language, then <code>type</code> is returned.
*/
public int getClosestStandardTokenTypeForInternalType(int type) {
return tokenMaker.getClosestStandardTokenTypeForInternalType(type);
}
/**
* Returns whether closing markup tags should be automatically completed.
* This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()}
* also
* Returns whether closing markup tags should be automatically completed. This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()} also
* returns <code>true</code>.
*
* @return Whether markup closing tags should be automatically completed.
@@ -269,26 +249,24 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
// TODO: Remove terrible dependency on AbstractMarkupTokenMaker
return getLanguageIsMarkup() && ((AbstractMarkupTokenMaker) tokenMaker).getCompleteCloseTags();
}
/**
* Returns whether the current programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks.
*
* @param languageIndex
* The language index at the offset in question. Since some
* <code>TokenMaker</code>s effectively have nested languages
* (such
* as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @param languageIndex The language index at the offset in question. Since some
* <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter
* tells the <code>TokenMaker</code> what sub-language to
* look at.
* @return Whether curly braces denote code blocks.
*/
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return tokenMaker.getCurlyBracesDenoteCodeBlocks(languageIndex);
}
/**
* Returns whether the current language is a markup language, such as HTML,
* XML
* Returns whether the current language is a markup language, such as HTML, XML
* or PHP.
*
* @return Whether the current language is a markup language.
@@ -296,50 +274,42 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public boolean getLanguageIsMarkup() {
return tokenMaker.isMarkupLanguage();
}
/**
* Returns the token type of the last token on the given line.
*
* @param line
* The line to inspect.
* @return The token type of the last token on the specified line. If the
* line
* @param line The line to inspect.
* @return The token type of the last token on the specified line. If the line
* is invalid, an exception is thrown.
*/
public int getLastTokenTypeOnLine(int line) {
return lastTokensOnLines.get(line);
}
/**
* Returns the text to place at the beginning and end of a line to "comment"
* it
* Returns the text to place at the beginning and end of a line to "comment" it
* in this programming language.
*
* @return The start and end strings to add to a line to "comment" it out. A
* <code>null</code> value for either means there is no string to
* add
* for that part. A value of <code>null</code> for the array means
* this
* <code>null</code> value for either means there is no string to add
* for that part. A value of <code>null</code> for the array means this
* language does not support commenting/uncommenting lines.
*/
public String[] getLineCommentStartAndEnd(int languageIndex) {
return tokenMaker.getLineCommentStartAndEnd(languageIndex);
}
/**
* Returns whether tokens of the specified type should have "mark
* occurrences"
* Returns whether tokens of the specified type should have "mark occurrences"
* enabled for the current programming language.
*
* @param type
* The token type.
* @return Whether tokens of this type should have "mark occurrences"
* enabled.
* @param type The token type.
* @return Whether tokens of this type should have "mark occurrences" enabled.
*/
boolean getMarkOccurrencesOfTokenType(int type) {
return tokenMaker.getMarkOccurrencesOfTokenType(type);
}
/**
* Returns the occurrence marker for the current language.
*
@@ -348,13 +318,12 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
OccurrenceMarker getOccurrenceMarker() {
return tokenMaker.getOccurrenceMarker();
}
/**
* This method returns whether auto indentation should be done if Enter is
* pressed at the end of the specified line.
*
* @param line
* The line to check.
* @param line The line to check.
* @return Whether an extra indentation should be done.
*/
public boolean getShouldIndentNextLine(int line) {
@@ -362,7 +331,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
t = t.getLastNonCommentNonWhitespaceToken();
return tokenMaker.getShouldIndentNextLineAfter(t);
}
/**
* Returns the syntax style being used.
*
@@ -372,16 +341,14 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public String getSyntaxStyle() {
return syntaxStyle;
}
/**
* Returns a token list for the specified segment of text representing the
* specified line number. This method is basically a wrapper for
* <code>tokenMaker.getTokenList</code> that takes into account the last
* token
* <code>tokenMaker.getTokenList</code> that takes into account the last token
* on the previous line to assure token accuracy.
*
* @param line
* The line number of <code>text</code> in the document, &gt;= 0.
* @param line The line number of <code>text</code> in the document, &gt;= 0.
* @return A token list representing the specified line.
*/
public final Token getTokenListForLine(int line) {
@@ -412,7 +379,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
cachedTokenList = tokenMaker.getTokenList(s, initialTokenType, startOffset);
return cachedTokenList;
}
boolean insertBreakSpecialHandling(ActionEvent e) {
Action a = tokenMaker.getInsertBreakAction();
if (a != null) {
@@ -421,28 +388,23 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
return false;
}
/**
* Returns whether a character could be part of an "identifier" token in a
* specific language. This is used to identify such things as the bounds of
* the
* specific language. This is used to identify such things as the bounds of the
* "word" to select on double-clicking.
*
* @param languageIndex
* The language index the character was found in.
* @param ch
* The character.
* @param languageIndex The language index the character was found in.
* @param ch The character.
* @return Whether the character could be part of an "identifier" token.
*/
public boolean isIdentifierChar(int languageIndex, char ch) {
return tokenMaker.isIdentifierChar(languageIndex, ch);
}
/**
* Returns an iterator over the paintable tokens in this document. Results
* are
* undefined if this document is modified while the iterator is being
* iterated
* Returns an iterator over the paintable tokens in this document. Results are
* undefined if this document is modified while the iterator is being iterated
* through, so this should only be used on the EDT.
* <p>
*
@@ -455,12 +417,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public Iterator<Token> iterator() {
return new TokenIterator(this);
}
/**
* Deserializes a document.
*
* @param in
* The stream to read from.
* @param in The stream to read from.
* @throws ClassNotFoundException
* @throws IOException
*/
@@ -476,16 +437,13 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
lastTokensOnLines = new DynamicIntArray(lineCount);
setSyntaxStyle(syntaxStyle); // Actually install (transient) TokenMaker
}
/**
* Makes our private <code>Segment s</code> point to the text in our
* document
* referenced by the specified element. Note that <code>line</code> MUST be
* a
* Makes our private <code>Segment s</code> point to the text in our document
* referenced by the specified element. Note that <code>line</code> MUST be a
* valid line number in the document.
*
* @param line
* The line number you want to get.
* @param line The line number you want to get.
*/
private void setSharedSegment(int line) {
Element map = getDefaultRootElement();
@@ -504,20 +462,17 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
throw new InternalError("Text range not in document: " + startOffset + "-" + endOffset);
}
}
/**
* Sets the syntax style being used for syntax highlighting in this
* document.
* Sets the syntax style being used for syntax highlighting in this document.
* What styles are supported by a document is determined by its
* {@link TokenMakerFactory}. By default, all <code>RSyntaxDocument</code>s
* support all languages built into <code>RSyntaxTextArea</code>.
*
* @param styleKey
* The new style to use, such as
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is
* not
* known or supported by this document, then
* {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* @param styleKey The new style to use, such as
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is
* not known or supported by this document, then
* {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* @see #setSyntaxStyle(TokenMaker)
* @see #getSyntaxStyle()
*/
@@ -530,15 +485,13 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
updateSyntaxHighlightingInformation();
this.syntaxStyle = styleKey;
}
/**
* Sets the syntax style being used for syntax highlighting in this
* document.
* Sets the syntax style being used for syntax highlighting in this document.
* You should call this method if you've created a custom token maker for a
* language not normally supported by <code>RSyntaxTextArea</code>.
*
* @param tokenMaker
* The new token maker to use.
* @param tokenMaker The new token maker to use.
* @see #setSyntaxStyle(String)
*/
public void setSyntaxStyle(TokenMaker tokenMaker) {
@@ -546,35 +499,28 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
updateSyntaxHighlightingInformation();
this.syntaxStyle = "text/unknown"; // TODO: Make me public?
}
/**
* Sets the token maker factory used by this document.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this
* is
* @param tmf The <code>TokenMakerFactory</code> for this document. If this is
* <code>null</code>, a default factory is used.
*/
public void setTokenMakerFactory(TokenMakerFactory tmf) {
tokenMakerFactory = tmf != null ? tmf : TokenMakerFactory.getDefaultInstance();
}
/**
* Loops through the last-tokens-on-lines array from a specified point
* onward,
* updating last-token values until they stop changing. This should be
* called
* when lines are updated/inserted/removed, as doing so may cause lines
* below to
* Loops through the last-tokens-on-lines array from a specified point onward,
* updating last-token values until they stop changing. This should be called
* when lines are updated/inserted/removed, as doing so may cause lines below to
* change color.
*
* @param line
* The first line to check for a change in last-token value.
* @param numLines
* The number of lines in the document.
* @param previousTokenType
* The last-token value of the line just before
* <code>line</code>.
* @param line The first line to check for a change in last-token
* value.
* @param numLines The number of lines in the document.
* @param previousTokenType The last-token value of the line just before
* <code>line</code>.
* @return The last line that needs repainting.
*/
private int updateLastTokensBelow(int line, int numLines, int previousTokenType) {
@@ -627,7 +573,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
return line;
}
/**
* Updates internal state information; e.g. the "last tokens on lines" data.
* After this, a changed update is fired to let listeners know that the
File diff suppressed because it is too large. Load diff
@@ -27,8 +27,8 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The highlighter implementation used by {@link RSyntaxTextArea}s. It knows to
* always paint "marked occurrences" highlights below selection highlights,
* and squiggle underline highlights above all other highlights.
* always paint "marked occurrences" highlights below selection highlights, and
* squiggle underline highlights above all other highlights.
* <p>
*
* Most of this code is copied from javax.swing.text.DefaultHighlighter;
@@ -38,22 +38,22 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
* @version 1.0
*/
public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
/**
* Marked occurrences in the document (to be painted separately from
* other highlights).
* Marked occurrences in the document (to be painted separately from other
* highlights).
*/
private List<SyntaxLayeredHighlightInfoImpl> markedOccurrences;
/**
* Highlights from document parsers. These should be painted "on top of"
* all other highlights to ensure they are always above the selection.
* Highlights from document parsers. These should be painted "on top of" all
* other highlights to ensure they are always above the selection.
*/
private List<SyntaxLayeredHighlightInfoImpl> parserHighlights;
/**
* The default color used for parser notices when none is specified.
*/
private static final Color DEFAULT_PARSER_NOTICE_COLOR = Color.RED;
/**
* Constructor.
*/
@@ -62,7 +62,7 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
parserHighlights = new ArrayList<SyntaxLayeredHighlightInfoImpl>(0); // Often
// unused
}
/**
* Adds a special "marked occurrence" highlight.
*
@@ -88,12 +88,11 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
mapper.damageRange(textArea, start, end);
return i;
}
/**
* Adds a highlight from a parser.
*
* @param notice
* The notice from a {@link Parser}.
* @param notice The notice from a {@link Parser}.
* @return A tag with which to reference the highlight.
* @throws BadLocationException
* @see #clearParserHighlights()
@@ -128,7 +127,7 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
mapper.damageRange(textArea, start, end);
return i;
}
/**
* Removes all "marked occurrences" highlights from the view.
*
@@ -142,7 +141,7 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
markedOccurrences.clear();
}
/**
* Removes all parser highlights.
*
@@ -157,12 +156,11 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
parserHighlights.clear();
}
/**
* Removes all of the highlights for a specific parser.
*
* @param parser
* The parser.
* @param parser The parser.
*/
public void clearParserHighlights(Parser parser) {
Iterator<SyntaxLayeredHighlightInfoImpl> i = parserHighlights.iterator();
@@ -176,7 +174,7 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
}
}
/**
* {@inheritDoc}
*/
@@ -186,10 +184,10 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
markedOccurrences.clear();
parserHighlights.clear();
}
/**
* Returns a list of "marked occurrences" in the text area. If there are
* no marked occurrences, this will be an empty list.
* Returns a list of "marked occurrences" in the text area. If there are no
* marked occurrences, this will be an empty list.
*
* @return The list of marked occurrences, or an empty list if none. The
* contents of this list will be of type {@link DocumentRange}.
@@ -210,19 +208,19 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
return list;
}
@Override
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor, View view) {
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor,
View view) {
paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, markedOccurrences);
super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view);
paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, parserHighlights);
}
/**
* Removes a parser highlight from this view.
*
* @param tag
* The reference to the highlight.
* @param tag The reference to the highlight.
* @see #addParserHighlight(ParserNotice,
* javax.swing.text.Highlighter.HighlightPainter)
*/
@@ -230,16 +228,15 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
repaintListHighlight(tag);
parserHighlights.remove(tag);
}
/**
* Highlight info implementation used for parser notices and marked
* occurrences.
* Highlight info implementation used for parser notices and marked occurrences.
*/
private static class SyntaxLayeredHighlightInfoImpl extends LayeredHighlightInfoImpl {
private ParserNotice notice;// Color color; // Used only by Parser
// highlights.
@Override
public Color getColor() {
// return color;
@@ -252,11 +249,11 @@ public class RSyntaxTextAreaHighlighter extends RTextAreaHighlighter {
}
return color;
}
@Override
public String toString() {
return "[SyntaxLayeredHighlightInfoImpl: " + "startOffs=" + getStartOffset() + ", endOffs=" + getEndOffset() + ", color="
+ getColor() + "]";
return "[SyntaxLayeredHighlightInfoImpl: " + "startOffs=" + getStartOffset() + ", endOffs=" + getEndOffset()
+ ", color=" + getColor() + "]";
}
}
}
@@ -27,34 +27,33 @@ import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextAreaUI;
/**
* UI used by <code>RSyntaxTextArea</code>. This allows us to implement
* syntax highlighting.
* UI used by <code>RSyntaxTextArea</code>. This allows us to implement syntax
* highlighting.
*
* @author Robert Futrell
* @version 0.1
*/
public class RSyntaxTextAreaUI extends RTextAreaUI {
private static final String SHARED_ACTION_MAP_NAME = "RSyntaxTextAreaUI.actionMap";
private static final String SHARED_INPUT_MAP_NAME = "RSyntaxTextAreaUI.inputMap";
private static final EditorKit DEFAULT_KIT = new RSyntaxTextAreaEditorKit();
public static ComponentUI createUI(JComponent ta) {
return new RSyntaxTextAreaUI(ta);
}
/**
* Constructor.
*/
public RSyntaxTextAreaUI(JComponent rSyntaxTextArea) {
super(rSyntaxTextArea);
}
/**
* Creates the view for an element.
*
* @param elem
* The element.
* @param elem The element.
* @return The view.
*/
@Override
@@ -72,7 +71,7 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
}
return null;
}
/**
* Creates the highlighter to use for syntax text areas.
*
@@ -82,11 +81,11 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
protected Highlighter createHighlighter() {
return new RSyntaxTextAreaHighlighter();
}
/**
* Returns the name to use to cache/fetch the shared action map. This
* should be overridden by subclasses if the subclass has its own custom
* editor kit to install, so its actions get picked up.
* Returns the name to use to cache/fetch the shared action map. This should be
* overridden by subclasses if the subclass has its own custom editor kit to
* install, so its actions get picked up.
*
* @return The name of the cached action map.
*/
@@ -94,12 +93,11 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
protected String getActionMapName() {
return SHARED_ACTION_MAP_NAME;
}
/**
* Fetches the EditorKit for the UI.
*
* @param tc
* The text component for which this UI is installed.
* @param tc The text component for which this UI is installed.
* @return The editor capabilities.
* @see javax.swing.plaf.TextUI#getEditorKit
*/
@@ -107,16 +105,16 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
public EditorKit getEditorKit(JTextComponent tc) {
return DEFAULT_KIT;
}
/**
* Get the InputMap to use for the UI.
* <p>
*
* This method is not named <code>getInputMap()</code> because there is
* a package-private method in <code>BasicTextAreaUI</code> with that name.
* Thus, creating a new method with that name causes certain compilers to
* issue warnings that you are not actually overriding the original method
* (since it is package-private).
* This method is not named <code>getInputMap()</code> because there is a
* package-private method in <code>BasicTextAreaUI</code> with that name. Thus,
* creating a new method with that name causes certain compilers to issue
* warnings that you are not actually overriding the original method (since it
* is package-private).
*/
@Override
protected InputMap getRTextAreaInputMap() {
@@ -132,18 +130,17 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
map.setParent(shared);
return map;
}
@Override
protected void paintEditorAugmentations(Graphics g) {
super.paintEditorAugmentations(g);
paintMatchedBracket(g);
}
/**
* Paints the "matched bracket", if any.
*
* @param g
* The graphics context.
* @param g The graphics context.
*/
protected void paintMatchedBracket(Graphics g) {
RSyntaxTextArea rsta = (RSyntaxTextArea) textArea;
@@ -160,7 +157,7 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
}
}
}
protected void paintMatchedBracketImpl(Graphics g, RSyntaxTextArea rsta, Rectangle r) {
// We must add "-1" to the height because otherwise we'll paint below
// the region that gets invalidated.
@@ -183,13 +180,12 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
g.drawRect(r.x, r.y, r.width, r.height - 1);
}
}
/**
* Gets called whenever a bound property is changed on this UI's
* <code>RSyntaxTextArea</code>.
*
* @param e
* The property change event.
* @param e The property change event.
*/
@Override
protected void propertyChange(PropertyChangeEvent e) {
@@ -204,7 +200,7 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
super.propertyChange(e);
}
}
/**
* Updates the view. This should be called when the underlying
* <code>RSyntaxTextArea</code> changes its syntax editing style.
@@ -212,14 +208,13 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
public void refreshSyntaxHighlighting() {
modelChanged();
}
/**
* Returns the y-coordinate of the specified line.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
* This method is quicker than using traditional <code>modelToView(int)</code>
* calls, as the entire bounding box isn't computed.
*/
@Override
public int yForLine(int line) throws BadLocationException {
@@ -230,7 +225,7 @@ public class RSyntaxTextAreaUI extends RTextAreaUI {
}
return -1;
}
/**
* Returns the y-coordinate of the line containing a specified offset.
* <p>
File diff suppressed because it is too large. Load diff
+89 -101
View File
@@ -20,25 +20,24 @@ import java.io.StringReader;
* Gets the plain text version of RTF documents.
* <p>
*
* This is used by <code>RtfTransferable</code> to return the plain text
* version of the transferable when the receiver does not support RTF.
* This is used by <code>RtfTransferable</code> to return the plain text version
* of the transferable when the receiver does not support RTF.
*
* @author Robert Futrell
* @version 1.0
*/
final class RtfToText {
private Reader r;
private StringBuilder sb;
private StringBuilder controlWord;
private int blockCount;
private boolean inControlWord;
/**
* Private constructor.
*
* @param r
* The reader to read RTF text from.
* @param r The reader to read RTF text from.
*/
private RtfToText(Reader r) {
this.r = r;
@@ -47,15 +46,14 @@ final class RtfToText {
blockCount = 0;
inControlWord = false;
}
/**
* Converts the RTF text read from this converter's <code>Reader</code>
* into plain text. It is the caller's responsibility to close the
* reader after this method is called.
* Converts the RTF text read from this converter's <code>Reader</code> into
* plain text. It is the caller's responsibility to close the reader after this
* method is called.
*
* @return The plain text.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
private String convert() throws IOException {
// Skip over first curly brace as the whole file is in '{' and '}'
@@ -66,76 +64,76 @@ final class RtfToText {
while ((i = r.read()) != -1) {
char ch = (char) i;
switch (ch) {
case '{':
if (inControlWord && controlWord.length() == 0) { // "\{"
sb.append('{');
controlWord.setLength(0);
inControlWord = false;
} else {
blockCount++;
}
break;
case '}':
if (inControlWord && controlWord.length() == 0) { // "\}"
sb.append('}');
controlWord.setLength(0);
inControlWord = false;
} else {
blockCount--;
}
break;
case '\\':
if (blockCount == 0) {
if (inControlWord) {
if (controlWord.length() == 0) { // "\\"
sb.append('\\');
controlWord.setLength(0);
inControlWord = false;
} else {
endControlWord();
inControlWord = true;
}
case '{':
if (inControlWord && controlWord.length() == 0) { // "\{"
sb.append('{');
controlWord.setLength(0);
inControlWord = false;
} else {
blockCount++;
}
break;
case '}':
if (inControlWord && controlWord.length() == 0) { // "\}"
sb.append('}');
controlWord.setLength(0);
inControlWord = false;
} else {
blockCount--;
}
break;
case '\\':
if (blockCount == 0) {
if (inControlWord) {
if (controlWord.length() == 0) { // "\\"
sb.append('\\');
controlWord.setLength(0);
inControlWord = false;
} else {
endControlWord();
inControlWord = true;
}
} else {
inControlWord = true;
}
break;
case ' ':
if (blockCount == 0) {
if (inControlWord) {
endControlWord();
} else {
sb.append(' ');
}
}
break;
case ' ':
if (blockCount == 0) {
if (inControlWord) {
endControlWord();
} else {
sb.append(' ');
}
break;
case '\r':
case '\n':
if (blockCount == 0) {
if (inControlWord) {
endControlWord();
}
// Otherwise, ignore
}
break;
case '\r':
case '\n':
if (blockCount == 0) {
if (inControlWord) {
endControlWord();
}
break;
default:
if (blockCount == 0) {
if (inControlWord) {
controlWord.append(ch);
} else {
sb.append(ch);
}
// Otherwise, ignore
}
break;
default:
if (blockCount == 0) {
if (inControlWord) {
controlWord.append(ch);
} else {
sb.append(ch);
}
break;
}
break;
}
}
return sb.toString();
}
/**
* Ends a control word. Checks whether it is a common one that affects
* the plain text output (such as "<code>par</code>" or "<code>tab</code>")
* and updates the text buffer accordingly.
* Ends a control word. Checks whether it is a common one that affects the plain
* text output (such as "<code>par</code>" or "<code>tab</code>") and updates
* the text buffer accordingly.
*/
private void endControlWord() {
String word = controlWord.toString();
@@ -147,58 +145,50 @@ final class RtfToText {
controlWord.setLength(0);
inControlWord = false;
}
/**
* Converts the contents of the specified byte array representing
* an RTF document into plain text.
* Converts the contents of the specified byte array representing an RTF
* document into plain text.
*
* @param rtf
* The byte array representing an RTF document.
* @param rtf The byte array representing an RTF document.
* @return The contents of the RTF document, in plain text.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
public static String getPlainText(byte[] rtf) throws IOException {
return getPlainText(new ByteArrayInputStream(rtf));
}
/**
* Converts the contents of the specified RTF file to plain text.
*
* @param file
* The RTF file to convert.
* @param file The RTF file to convert.
* @return The contents of the file, in plain text.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
public static String getPlainText(File file) throws IOException {
return getPlainText(new BufferedReader(new FileReader(file)));
}
/**
* Converts the contents of the specified input stream to plain text.
* The input stream will be closed when this method returns.
* Converts the contents of the specified input stream to plain text. The input
* stream will be closed when this method returns.
*
* @param in
* The input stream to convert. This will be closed when this
* method returns.
* @param in The input stream to convert. This will be closed when this method
* returns.
* @return The contents of the stream, in plain text.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
public static String getPlainText(InputStream in) throws IOException {
return getPlainText(new InputStreamReader(in, "US-ASCII"));
}
/**
* Converts the contents of the specified <code>Reader</code> to plain text.
*
* @param r
* The <code>Reader</code>. This will be closed when this method
* returns.
* @param r The <code>Reader</code>. This will be closed when this method
* returns.
* @return The contents of the <code>Reader</code>, in plain text.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
private static String getPlainText(Reader r) throws IOException {
try {
@@ -208,15 +198,13 @@ final class RtfToText {
r.close();
}
}
/**
* Converts the contents of the specified String to plain text.
*
* @param rtf
* A string whose contents represent an RTF document.
* @param rtf A string whose contents represent an RTF document.
* @return The contents of the String, in plain text.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
public static String getPlainText(String rtf) throws IOException {
return getPlainText(new StringReader(rtf));
Loaded 100 of 274 files, more files were not shown because too many files have changed in this diff. Show more