31 Commits
Author SHA1 Message Date
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
SuspiciousActivity ac247a89ca Fixed number deobfuscator 2020-11-27 01:01:30 +01:00
SuspiciousActivity b5e4847be5 Added number deobfuscator 2020-11-27 00:07:03 +01:00
SuspiciousActivity 30c9c81d38 Fixed crash 2020-11-26 17:07:36 +01:00
SuspiciousActivity 65235c6b5b ToolTips for #... 2020-11-26 15:52:35 +01:00
SuspiciousActivity d8096a4ec5 Fixes and improvements 2020-11-25 23:53:58 +01:00
SuspiciousActivity add82951e6 Huge String improvements 2020-11-25 23:14:14 +01:00
SuspiciousActivity afc59d63d3 Update 2020-11-25 20:32:57 +01:00
279 changed files with 13978 additions and 110165 deletions

No files matched your search

+1 -5
View File
@@ -2,8 +2,6 @@
A simple, text based Java Bytecode Editor. A simple, text based Java Bytecode Editor.
It uses ObjectWeb ASM (http://asm.ow2.org/) and the RSyntaxTextArea from https://github.com/bobbylight/RSyntaxTextArea 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 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. 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 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 (un-)escape unicode characters, press CTRL + U.
To rename a field/method with attention to references in the selected line, press CTRL + R. 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 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.net.URI;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.JFrame; import javax.swing.JFrame;
@@ -32,7 +34,8 @@ import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode; 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.Main;
import me.ByteEdit.main.ThemeManager; import me.ByteEdit.main.ThemeManager;
@@ -105,44 +108,63 @@ public class CompilationBox extends JFrame {
null, Arrays.asList(new JavaSourceFromString("Compiled", source))).call(); null, Arrays.asList(new JavaSourceFromString("Compiled", source))).call();
String res = out.toString(); String res = out.toString();
if (res.isEmpty()) { if (res.isEmpty()) {
File clazz = new File(tmpFolder, "Compiled.class"); SingleThreadedExecutor.submit(() -> {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); try {
InputStream is = new FileInputStream(clazz); File clazz = new File(tmpFolder, "Compiled.class");
byte[] tmp = new byte[1024]; ByteArrayOutputStream baos = new ByteArrayOutputStream();
int r; InputStream is = new FileInputStream(clazz);
while ((r = is.read(tmp)) > 0) { byte[] tmp = new byte[1024];
baos.write(tmp, 0, r); int 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);
while ((r = is.read(tmp)) > 0) { while ((r = is.read(tmp)) > 0) {
baos.write(tmp, 0, r); baos.write(tmp, 0, r);
} }
is.close(); is.close();
read = new ClassReader(baos.toByteArray());
node = new ClassNode(); ClassReader read = new ClassReader(baos.toByteArray());
ClassNode node = new ClassNode();
read.accept(node, 0); 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); try {
compSuccess.setVisible(true); for (File f : tmpFolder.listFiles()) {
Files.delete(f.toPath());
}
Files.delete(tmpFolder.toPath());
} catch (IOException e2) {
e2.printStackTrace();
}
});
} else { } else {
JOptionPane.showMessageDialog(CompilationBox.this, res, "Error!", JOptionPane.ERROR_MESSAGE); 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) { } catch (IOException e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
@@ -150,7 +172,7 @@ public class CompilationBox extends JFrame {
}, ctrlS, JComponent.WHEN_FOCUSED); }, ctrlS, JComponent.WHEN_FOCUSED);
} }
class JavaSourceFromString extends SimpleJavaFileObject { private static class JavaSourceFromString extends SimpleJavaFileObject {
final String code; final String code;
public JavaSourceFromString(String name, String code) { public JavaSourceFromString(String name, String code) {
+21 -40
View File
@@ -1,8 +1,6 @@
package me.ByteEdit.boxes; package me.ByteEdit.boxes;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -18,6 +16,7 @@ import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode; import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LdcInsnNode; import org.objectweb.asm.tree.LdcInsnNode;
@@ -59,13 +58,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblFind); contentPane.add(lblFind);
txtString = new JTextField(); txtString = new JTextField();
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0); KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
txtString.registerKeyboardAction(new ActionListener() { txtString.registerKeyboardAction(e -> find(), enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
@Override
public void actionPerformed(ActionEvent e) {
find();
}
}, enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtString.setBounds(91, 22, 149, 20); txtString.setBounds(91, 22, 149, 20);
contentPane.add(txtString); contentPane.add(txtString);
txtString.setColumns(10); txtString.setColumns(10);
@@ -73,12 +66,7 @@ public class GlobalSearchBox extends JFrame {
chckbxCaseSensitive.setBounds(10, 199, 114, 23); chckbxCaseSensitive.setBounds(10, 199, 114, 23);
contentPane.add(chckbxCaseSensitive); contentPane.add(chckbxCaseSensitive);
JButton btnFind = new JButton("Find"); JButton btnFind = new JButton("Find");
btnFind.addActionListener(new ActionListener() { btnFind.addActionListener(e -> find());
public void actionPerformed(ActionEvent e) {
find();
}
});
btnFind.setBounds(130, 199, 110, 23); btnFind.setBounds(130, 199, 110, 23);
contentPane.add(btnFind); contentPane.add(btnFind);
@@ -91,12 +79,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblOwner); contentPane.add(lblOwner);
txtOwner = new JTextField(); txtOwner = new JTextField();
txtOwner.addActionListener(new ActionListener() { txtOwner.addActionListener(e -> find());
public void actionPerformed(ActionEvent e) {
find();
}
});
txtOwner.setColumns(10); txtOwner.setColumns(10);
txtOwner.setBounds(91, 78, 149, 20); txtOwner.setBounds(91, 78, 149, 20);
contentPane.add(txtOwner); contentPane.add(txtOwner);
@@ -106,12 +89,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblName); contentPane.add(lblName);
txtName = new JTextField(); txtName = new JTextField();
txtName.addActionListener(new ActionListener() { txtName.addActionListener(e -> find());
public void actionPerformed(ActionEvent e) {
find();
}
});
txtName.setColumns(10); txtName.setColumns(10);
txtName.setBounds(91, 106, 149, 20); txtName.setBounds(91, 106, 149, 20);
contentPane.add(txtName); contentPane.add(txtName);
@@ -121,12 +99,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblDesc); contentPane.add(lblDesc);
txtDesc = new JTextField(); txtDesc = new JTextField();
txtDesc.addActionListener(new ActionListener() { txtDesc.addActionListener(e -> find());
public void actionPerformed(ActionEvent e) {
find();
}
});
txtDesc.setColumns(10); txtDesc.setColumns(10);
txtDesc.setBounds(91, 134, 149, 20); txtDesc.setBounds(91, 134, 149, 20);
contentPane.add(txtDesc); contentPane.add(txtDesc);
@@ -137,10 +110,11 @@ public class GlobalSearchBox extends JFrame {
openBox.model.clear(); openBox.model.clear();
Set<Info> set = new HashSet<>(); Set<Info> set = new HashSet<>();
if (!txtString.getText().isEmpty()) { if (!txtString.getText().isEmpty()) {
String str = UnicodeUtils.unescape(txtString.getText()); String str = UnicodeUtils.unescape(null, txtString.getText(), true);
if (ignoreCase) { if (ignoreCase) {
str = str.toLowerCase(); str = str.toLowerCase();
} }
String fstr = str;
for (ClassNode cn : Main.classNodes.values()) { for (ClassNode cn : Main.classNodes.values()) {
for (MethodNode mn : cn.methods) { for (MethodNode mn : cn.methods) {
for (AbstractInsnNode insn : mn.instructions.toArray()) { for (AbstractInsnNode insn : mn.instructions.toArray()) {
@@ -154,14 +128,23 @@ 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 { } else {
String owner = UnicodeUtils.unescape(txtOwner.getText()); String owner = UnicodeUtils.unescape(null, txtOwner.getText(), true);
boolean wildCardOwner = owner.isEmpty(); boolean wildCardOwner = owner.isEmpty();
String name = UnicodeUtils.unescape(txtName.getText()); String name = UnicodeUtils.unescape(null, txtName.getText(), true);
boolean wildCardName = name.isEmpty(); boolean wildCardName = name.isEmpty();
String desc = UnicodeUtils.unescape(txtDesc.getText()); String desc = UnicodeUtils.unescape(null, txtDesc.getText(), true);
boolean wildCardDesc = desc.isEmpty(); boolean wildCardDesc = desc.isEmpty();
for (ClassNode cn : Main.classNodes.values()) { for (ClassNode cn : Main.classNodes.values()) {
for (MethodNode mn : cn.methods) { for (MethodNode mn : cn.methods) {
@@ -183,9 +166,7 @@ public class GlobalSearchBox extends JFrame {
} }
} }
} }
for (Info s : set) { set.forEach(openBox.model::addElement);
openBox.model.addElement(s);
}
openBox.setVisible(true); openBox.setVisible(true);
} }
} }
+6 -14
View File
@@ -11,10 +11,10 @@ import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main; import me.ByteEdit.main.Main;
public class GlobalSearchOpenBox extends JFrame { public class GlobalSearchOpenBox extends JFrame {
@@ -36,19 +36,13 @@ public class GlobalSearchOpenBox extends JFrame {
model = new DefaultListModel<>(); model = new DefaultListModel<>();
JList<Info> list = new JList(model); JList<Info> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() { list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) { public void valueChanged(ListSelectionEvent e) {
Info val = list.getSelectedValue(); SingleThreadedExecutor.submit(() -> {
if (val != null && !val.owner.equals(Main.currentNodeName)) { Info val = list.getSelectedValue();
int lineFound = Main.selectFileWithSearch(val.nodePath, val.mn); if (val != null && !val.owner.equals(Main.currentNodeName)) {
if (lineFound != -1) { Main.selectFile(val.owner);
try {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
} catch (BadLocationException e1) {
e1.printStackTrace();
}
} }
} });
} }
}); });
@@ -65,14 +59,12 @@ public class GlobalSearchOpenBox extends JFrame {
String name; String name;
String desc; String desc;
MethodNode mn; MethodNode mn;
String nodePath;
public Info(String owner, String name, String desc, MethodNode mn) { public Info(String owner, String name, String desc, MethodNode mn) {
this.owner = owner; this.owner = owner;
this.name = name; this.name = name;
this.desc = desc; this.desc = desc;
this.mn = mn; this.mn = mn;
nodePath = Main.getFullName(owner);
} }
@Override @Override
+14 -27
View File
@@ -20,7 +20,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.edit.Disassembler; import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main; import me.ByteEdit.main.Main;
import me.ByteEdit.utils.ClassUtil; import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.UnicodeUtils; import me.ByteEdit.utils.UnicodeUtils;
@@ -79,13 +79,13 @@ public class RenameBox extends JFrame {
JOptionPane.showMessageDialog(null, "Desc not set!", "Error!", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(null, "Desc not set!", "Error!", JOptionPane.ERROR_MESSAGE);
return; return;
} }
ClassNode clazz = Main.classNodes.get(Main.getFullName(className)); ClassNode clazz = Main.classNodes.get(className);
if (clazz == null) { if (clazz == null) {
JOptionPane.showMessageDialog(null, "ClassNode not found!", "Error!", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(null, "ClassNode not found!", "Error!", JOptionPane.ERROR_MESSAGE);
return; return;
} }
String newName = UnicodeUtils.unescape(txtName.getText()); String newName = UnicodeUtils.unescape(null, txtName.getText(), true);
String newDesc = UnicodeUtils.unescape(txtDesc.getText()); String newDesc = UnicodeUtils.unescape(null, txtDesc.getText(), true);
if (ClassUtil.isObjectClassMethod(newName, newDesc, true)) { if (ClassUtil.isObjectClassMethod(newName, newDesc, true)) {
JOptionPane.showMessageDialog(null, "Invalid name!", "Error!", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(null, "Invalid name!", "Error!", JOptionPane.ERROR_MESSAGE);
return; return;
@@ -94,7 +94,7 @@ public class RenameBox extends JFrame {
if (newDesc.startsWith("(")) { // method if (newDesc.startsWith("(")) { // method
ClassNode lastHit = clazz; ClassNode lastHit = clazz;
while (clazz != null && clazz.superName != null) { 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) { if (tmp != null) {
boolean found = false; boolean found = false;
for (MethodNode mn : clazz.methods) { for (MethodNode mn : clazz.methods) {
@@ -127,7 +127,7 @@ public class RenameBox extends JFrame {
extendsMyClass = true; extendsMyClass = true;
break; break;
} }
ClassNode tmp = Main.classNodes.get(Main.getFullName(checkCN.superName)); ClassNode tmp = Main.classNodes.get(checkCN.superName);
if (tmp != null) { if (tmp != null) {
checkCN = tmp; checkCN = tmp;
} else { } else {
@@ -210,27 +210,14 @@ public class RenameBox extends JFrame {
return; return;
} }
} }
try { // refresh
ClassNode classNode = Main.classNodes.get(Main.currentNodeName); SingleThreadedExecutor.submit(() -> {
int prev = Main.txtByteEditView.getCaretPosition(); // refresh
String dis = Disassembler.disassemble(classNode); Main.decompileCurrentNode();
String substr = Main.currentNodeName.substring(0, Main.currentNodeName.length() - 6); name = newName;
for (String key : Main.classNodes.keySet()) { desc = newDesc;
if (key.contains("$")) { RenameBox.this.setVisible(false);
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);
} }
}); });
btnRename.setBounds(345, 237, 89, 23); btnRename.setBounds(345, 237, 89, 23);
+4 -1
View File
@@ -123,13 +123,16 @@ public class SearchBox extends JFrame {
} }
public void replace() { public void replace() {
String txt = Main.txtByteEditView.getText(); if (!Main.decompiler.isEditable())
return;
int startPos = Main.txtByteEditView.getSelectionStart(); int startPos = Main.txtByteEditView.getSelectionStart();
Main.txtByteEditView.replaceSelection(txtReplace.getText()); Main.txtByteEditView.replaceSelection(txtReplace.getText());
Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length()); Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length());
} }
public void replaceAll() { public void replaceAll() {
if (!Main.decompiler.isEditable())
return;
String toFind = txtFind.getText(); String toFind = txtFind.getText();
String toReplace = txtReplace.getText(); String toReplace = txtReplace.getText();
String txt = Main.txtByteEditView.getText(); String txt = Main.txtByteEditView.getText();
+11 -12
View File
@@ -17,6 +17,7 @@ import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main; import me.ByteEdit.main.Main;
import me.ByteEdit.utils.UnicodeUtils; import me.ByteEdit.utils.UnicodeUtils;
@@ -39,11 +40,11 @@ public class TypeOpenBox extends JFrame {
DefaultListModel<String> model = new DefaultListModel<>(); DefaultListModel<String> model = new DefaultListModel<>();
JList<String> list = new JList(model); JList<String> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() { list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) { public void valueChanged(ListSelectionEvent e) {
String val = list.getSelectedValue(); String val = list.getSelectedValue();
if (val != null && !val.equals(Main.currentNodeName)) { if (!e.getValueIsAdjusting() && val != null && !val.equals(Main.currentNodeName)) {
Main.selectFile(val); SingleThreadedExecutor.submit(() -> Main.selectFile(val + ".class"));
} }
} }
}); });
@@ -76,21 +77,19 @@ public class TypeOpenBox extends JFrame {
if (txtSearch.getText().isEmpty()) { if (txtSearch.getText().isEmpty()) {
return; return;
} }
String search = UnicodeUtils.unescape(txtSearch.getText()).toLowerCase(); String search = UnicodeUtils.unescape(null, txtSearch.getText(), true).toLowerCase();
if (search.contains("/")) { if (search.contains("/")) {
for (String s : Main.classNodes.keySet()) { for (String fullName : Main.classNodes.keySet()) {
String className = s.substring(0, s.length() - 6); if (!fullName.contains("$") && fullName.toLowerCase().startsWith(search)) {
if (!className.contains("$") && className.toLowerCase().startsWith(search)) { model.addElement(fullName);
model.addElement(s);
} }
} }
} else { } else {
for (String s : Main.classNodes.keySet()) { for (String fullName : Main.classNodes.keySet()) {
String[] split = s.split("/"); String[] split = fullName.split("/");
String className = split[split.length - 1]; String className = split[split.length - 1];
className = className.substring(0, className.length() - 6);
if (!className.contains("$") && className.toLowerCase().startsWith(search)) { if (!className.contains("$") && className.toLowerCase().startsWith(search)) {
model.addElement(s); model.addElement(fullName);
} }
} }
} }
+2 -2
View File
@@ -36,7 +36,7 @@ public class UnicodeBox extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { try {
textArea.setText(UnicodeUtils.escape(textArea.getText())); textArea.setText(UnicodeUtils.escape(null, textArea.getText(), true));
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Main.showError(ex.getMessage()); Main.showError(ex.getMessage());
} }
@@ -50,7 +50,7 @@ public class UnicodeBox extends JFrame {
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { try {
textArea.setText(UnicodeUtils.unescape(textArea.getText())); textArea.setText(UnicodeUtils.unescape(null, textArea.getText(), true));
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
Main.showError(ex.getMessage()); Main.showError(ex.getMessage());
} }
@@ -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();
}
}
}
+199 -201
View File
@@ -1,5 +1,6 @@
package me.ByteEdit.edit; package me.ByteEdit.edit;
import java.io.BufferedReader;
import java.io.StringReader; import java.io.StringReader;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
@@ -9,6 +10,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.regex.Pattern;
import javax.swing.text.BadLocationException; import javax.swing.text.BadLocationException;
@@ -51,30 +53,48 @@ import me.ByteEdit.utils.UnicodeUtils;
public class Assembler { 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) { public static ClassNode assemble(String input) {
Main.txtByteEditView.removeAllLineHighlights(); Main.txtByteEditView.removeAllLineHighlights();
CustomBufferedReader read = null; CustomBufferedReader read = null;
try { try {
ClassNode clazz = new ClassNode(); ClassNode clazz = new ClassNode();
HugeStringsRev hsr = new HugeStringsRev();
try (BufferedReader reader = new BufferedReader(new StringReader(input))) {
String s;
while (!(s = reader.readLine()).startsWith("// #SourceFile: ")) {
}
while (!(s = reader.readLine()).equals("// #Fields")) {
if (s.startsWith("#")) {
String[] split = SPACE.split(s, 2);
hsr.add(Integer.parseInt(split[0].substring(1, split[0].length() - 1)),
UnicodeUtils.unescape(null, split[1], true));
}
}
}
read = new CustomBufferedReader(new StringReader(input)); read = new CustomBufferedReader(new StringReader(input));
String s; String s;
while (!(s = read.readLine()).startsWith("// #Class v:")) { while (!(s = read.readLine()).startsWith("// #Class v:")) {
if (clazz.visibleAnnotations == null) { if (clazz.visibleAnnotations == null) {
clazz.visibleAnnotations = new ArrayList<>(); clazz.visibleAnnotations = new ArrayList<>();
} }
clazz.visibleAnnotations.add(parseAnnotation(s)); clazz.visibleAnnotations.add(parseAnnotation(hsr, s));
} }
clazz.version = Integer.parseInt(s.substring(12)); clazz.version = Integer.parseInt(s.substring(12));
while ((s = read.readLine()).startsWith("// ")) { while ((s = read.readLine()).startsWith("// ")) {
if (s.startsWith("// #Signature: ")) { if (s.startsWith("// #Signature: ")) {
clazz.signature = s.substring(15).equals("null") ? null : UnicodeUtils.unescape(s.substring(15)); clazz.signature = s.substring(15).equals("null") ? null
: UnicodeUtils.unescape(hsr, s.substring(15));
} else if (s.startsWith("// #OuterClass: ")) { } else if (s.startsWith("// #OuterClass: ")) {
clazz.outerClass = s.substring(16).equals("null") ? null : UnicodeUtils.unescape(s.substring(16)); clazz.outerClass = s.substring(16).equals("null") ? null
: UnicodeUtils.unescape(hsr, s.substring(16));
} else if (s.startsWith("// #OuterMethod: ")) { } else if (s.startsWith("// #OuterMethod: ")) {
String[] split = s.split(" "); String[] split = s.split(" ");
if (split.length > 3) { if (split.length > 3) {
clazz.outerMethod = UnicodeUtils.unescape(split[2]); clazz.outerMethod = UnicodeUtils.unescape(hsr, split[2]);
clazz.outerMethodDesc = UnicodeUtils.unescape(split[3]); clazz.outerMethodDesc = UnicodeUtils.unescape(hsr, split[3]);
} }
} else if (s.equals("// #InnerClasses:")) { } else if (s.equals("// #InnerClasses:")) {
clazz.innerClasses = new ArrayList<>(); clazz.innerClasses = new ArrayList<>();
@@ -137,9 +157,9 @@ public class Assembler {
access ^= ClassUtil.ACC_MANDATED; access ^= ClassUtil.ACC_MANDATED;
} }
} }
clazz.innerClasses.add(new InnerClassNode(UnicodeUtils.unescape(split[1]), clazz.innerClasses.add(new InnerClassNode(UnicodeUtils.unescape(hsr, split[1]),
split[2].equals("null") ? null : UnicodeUtils.unescape(split[2]), split[2].equals("null") ? null : UnicodeUtils.unescape(hsr, split[2]),
split[3].equals("null") ? null : UnicodeUtils.unescape(split[3]), access)); split[3].equals("null") ? null : UnicodeUtils.unescape(hsr, split[3]), access));
} }
} }
{ {
@@ -148,14 +168,14 @@ public class Assembler {
String interfaces = s.substring(s.lastIndexOf(" implements ") + 12); String interfaces = s.substring(s.lastIndexOf(" implements ") + 12);
clazz.interfaces = new ArrayList<>(); clazz.interfaces = new ArrayList<>();
for (String interf : interfaces.split(", ")) { for (String interf : interfaces.split(", ")) {
clazz.interfaces.add(UnicodeUtils.unescape(interf)); clazz.interfaces.add(UnicodeUtils.unescape(hsr, interf));
} }
s = s.substring(0, s.lastIndexOf(" implements ")); s = s.substring(0, s.lastIndexOf(" implements "));
} }
clazz.superName = UnicodeUtils.unescape(s.substring(s.lastIndexOf(" extends ") + 9)); clazz.superName = UnicodeUtils.unescape(hsr, s.substring(s.lastIndexOf(" extends ") + 9));
s = s.substring(0, s.lastIndexOf(" extends ")); s = s.substring(0, s.lastIndexOf(" extends "));
String[] split = s.split(" "); String[] split = s.split(" ");
clazz.name = UnicodeUtils.unescape(split[split.length - 1]); clazz.name = UnicodeUtils.unescape(hsr, split[split.length - 1]);
clazz.access = ClassUtil.ACC_SUPER; clazz.access = ClassUtil.ACC_SUPER;
switch (split[split.length - 2]) { switch (split[split.length - 2]) {
case "class": case "class":
@@ -192,7 +212,7 @@ public class Assembler {
} }
} }
s = read.readLine(); s = read.readLine();
clazz.sourceFile = s.substring(16).equals("null") ? null : UnicodeUtils.unescape(s.substring(16)); clazz.sourceFile = s.substring(16).equals("null") ? null : UnicodeUtils.unescape(hsr, s.substring(16));
while (!(s = read.readLine()).equals("// #Fields")) { while (!(s = read.readLine()).equals("// #Fields")) {
} }
clazz.fields = new ArrayList<>(); clazz.fields = new ArrayList<>();
@@ -207,7 +227,7 @@ public class Assembler {
annotationsForNext.add(s); annotationsForNext.add(s);
} else { } else {
if (s.startsWith("// #Signature: ")) { if (s.startsWith("// #Signature: ")) {
signature = UnicodeUtils.unescape(s.substring(15)); signature = UnicodeUtils.unescape(hsr, s.substring(15));
} else { } else {
String[] split = s.split(" "); String[] split = s.split(" ");
boolean hasValue = false; boolean hasValue = false;
@@ -218,9 +238,9 @@ public class Assembler {
if (hasValue) { if (hasValue) {
split = s.substring(0, s.indexOf(" = ")).split(" "); split = s.substring(0, s.indexOf(" = ")).split(" ");
} }
String name = UnicodeUtils.unescape(split[split.length - 1]); String name = UnicodeUtils.unescape(hsr, split[split.length - 1]);
String desc = UnicodeUtils.unescape(split[split.length - 2]); String desc = UnicodeUtils.unescape(hsr, split[split.length - 2]);
value = getValue((String) value, desc); value = getValue(hsr, (String) value, desc);
int access = 0; int access = 0;
if (split.length > 2) { if (split.length > 2) {
String cons = consolidateStrings(split, 0, split.length - 2); String cons = consolidateStrings(split, 0, split.length - 2);
@@ -283,7 +303,7 @@ public class Assembler {
node.visibleAnnotations = new ArrayList<>(); node.visibleAnnotations = new ArrayList<>();
} }
for (String anno : annotationsForNext) { for (String anno : annotationsForNext) {
node.visibleAnnotations.add(parseAnnotation(anno)); node.visibleAnnotations.add(parseAnnotation(hsr, anno));
} }
annotationsForNext.clear(); annotationsForNext.clear();
} }
@@ -319,7 +339,9 @@ public class Assembler {
tryCatchBlocksToParse.clear(); tryCatchBlocksToParse.clear();
stage = 0; stage = 0;
} else if (s.startsWith("// #Signature: ")) { } else if (s.startsWith("// #Signature: ")) {
node.signature = UnicodeUtils.unescape(s.substring(15)); if (node == null)
throw new IllegalStateException("node is null");
node.signature = UnicodeUtils.unescape(hsr, s.substring(15));
} else if (s.equals("// #TryCatch:")) { } else if (s.equals("// #TryCatch:")) {
stage = 1; stage = 1;
} else if (s.equals("// #LocalVars:")) { } else if (s.equals("// #LocalVars:")) {
@@ -332,12 +354,14 @@ public class Assembler {
localVarsToParse.add(s); localVarsToParse.add(s);
} }
} else if (s.equals("}")) { } else if (s.equals("}")) {
if (node == null)
throw new IllegalStateException("node is null");
if (!annotationsForNext.isEmpty()) { if (!annotationsForNext.isEmpty()) {
if (node.visibleAnnotations == null) { if (node.visibleAnnotations == null) {
node.visibleAnnotations = new ArrayList<>(); node.visibleAnnotations = new ArrayList<>();
} }
for (String anno : annotationsForNext) { for (String anno : annotationsForNext) {
node.visibleAnnotations.add(parseAnnotation(anno)); node.visibleAnnotations.add(parseAnnotation(hsr, anno));
} }
annotationsForNext.clear(); annotationsForNext.clear();
} }
@@ -345,7 +369,7 @@ public class Assembler {
String[] sp = st.split(":"); String[] sp = st.split(":");
int start = Integer.parseInt(sp[3].substring(0, sp[3].length() - 2)); int start = Integer.parseInt(sp[3].substring(0, sp[3].length() - 2));
int end = Integer.parseInt(sp[4].substring(0, sp[4].length() - 4)); int end = Integer.parseInt(sp[4].substring(0, sp[4].length() - 4));
String signat = sp[5].equals("null") ? null : UnicodeUtils.unescape(sp[5]); String signat = sp[5].equals("null") ? null : UnicodeUtils.unescape(hsr, sp[5]);
LabelNode _start = null; LabelNode _start = null;
LabelNode _end = null; LabelNode _end = null;
for (Entry<LabelNode, Integer> entry : methodLabelMap.entrySet()) { for (Entry<LabelNode, Integer> entry : methodLabelMap.entrySet()) {
@@ -356,9 +380,9 @@ public class Assembler {
_end = entry.getKey(); _end = entry.getKey();
} }
} }
node.localVariables.add(new LocalVariableNode(UnicodeUtils.unescape(sp[0]), node.localVariables.add(new LocalVariableNode(UnicodeUtils.unescape(hsr, sp[0]),
UnicodeUtils.unescape(sp[1].substring(1, sp[1].length() - 2)), signat, _start, _end, UnicodeUtils.unescape(hsr, sp[1].substring(1, sp[1].length() - 2)), signat, _start,
Integer.parseInt(sp[2].substring(0, sp[2].length() - 2)))); _end, Integer.parseInt(sp[2].substring(0, sp[2].length() - 2))));
} }
for (String st : tryCatchBlocksToParse) { for (String st : tryCatchBlocksToParse) {
String[] sp = st.split(" "); String[] sp = st.split(" ");
@@ -380,21 +404,23 @@ public class Assembler {
} }
} }
node.tryCatchBlocks.add(new TryCatchBlockNode(_start, _end, _handler, node.tryCatchBlocks.add(new TryCatchBlockNode(_start, _end, _handler,
sp[0].equals("null") ? null : UnicodeUtils.unescape(sp[0]))); sp[0].equals("null") ? null : UnicodeUtils.unescape(hsr, sp[0])));
} }
clazz.methods.add(node); clazz.methods.add(node);
} else if (!s.startsWith("\t")) { } else if (!s.startsWith("\t")) {
if (node == null)
throw new IllegalStateException("node is null");
s = s.substring(0, s.length() - 2); s = s.substring(0, s.length() - 2);
if (s.contains(" throws ")) { if (s.contains(" throws ")) {
String exceptions = s.substring(s.lastIndexOf(" throws ") + 8); String exceptions = s.substring(s.lastIndexOf(" throws ") + 8);
for (String excp : exceptions.split(", ")) { for (String excp : exceptions.split(", ")) {
node.exceptions.add(UnicodeUtils.unescape(excp)); node.exceptions.add(UnicodeUtils.unescape(hsr, excp));
} }
s = s.substring(0, s.lastIndexOf(" throws ")); s = s.substring(0, s.lastIndexOf(" throws "));
} }
String[] split = s.split(" "); String[] split = s.split(" ");
node.desc = UnicodeUtils.unescape(split[split.length - 1]); node.desc = UnicodeUtils.unescape(hsr, split[split.length - 1]);
node.name = UnicodeUtils.unescape(split[split.length - 2]); node.name = UnicodeUtils.unescape(hsr, split[split.length - 2]);
int access = 0; int access = 0;
if (split.length > 2) { if (split.length > 2) {
String cons = consolidateStrings(split, 0, split.length - 2); String cons = consolidateStrings(split, 0, split.length - 2);
@@ -457,8 +483,10 @@ public class Assembler {
s = s.substring(1); s = s.substring(1);
if (!temp.isEmpty()) { if (!temp.isEmpty()) {
if (s.equals("]")) { if (s.equals("]")) {
if (node == null)
throw new IllegalStateException("node is null");
temp += s; temp += s;
node.instructions.add(getNode(temp, methodLabelMap)); node.instructions.add(getNode(hsr, temp, methodLabelMap));
temp = ""; temp = "";
} else { } else {
temp += s + "\n"; temp += s + "\n";
@@ -467,7 +495,9 @@ public class Assembler {
if (s.endsWith("[")) { if (s.endsWith("[")) {
temp += s + "\n"; temp += s + "\n";
} else { } else {
node.instructions.add(getNode(s, methodLabelMap)); if (node == null)
throw new IllegalStateException("node is null");
node.instructions.add(getNode(hsr, s, methodLabelMap));
} }
} }
} }
@@ -475,18 +505,21 @@ public class Assembler {
} }
return clazz; return clazz;
} catch (Throwable e) { } catch (Throwable e) {
System.err.println("Error at line: " + read.currentLine); if (read != null) {
try { System.err.println("Error at line: " + read.currentLine);
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine)); try {
} catch (BadLocationException e1) { Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine));
System.err.println("Can't show line!"); } catch (BadLocationException e1) {
System.err.println("Can't show line!");
}
} }
Main.showError(e.getMessage()); Main.showError(e.getMessage());
e.printStackTrace(); e.printStackTrace();
return null; return null;
} finally { } finally {
try { try {
read.close(); if (read != null)
read.close();
} catch (Exception e) { } catch (Exception e) {
} }
} }
@@ -496,31 +529,37 @@ public class Assembler {
ArrayList<String> ret = new ArrayList<>(); ArrayList<String> ret = new ArrayList<>();
String[] split = s.split(", "); String[] split = s.split(", ");
String building = null; String building = null;
boolean inString = false;
for (int i = 0; i < split.length; i++) { for (int i = 0; i < split.length; i++) {
if (split[i].startsWith("(")) { if (split[i].startsWith("(")) {
ret.add(split[i]); ret.add(split[i]);
} else if (split[i].endsWith("\"")) { } else if (split[i].startsWith("\"") && split[i].endsWith("\"")) {
ret.add(split[i]);
} else if (split[i].endsWith("\"") && inString) {
if (building == null) if (building == null)
ret.add(split[i]); ret.add(split[i]);
else else
ret.add(building + ", " + split[i]); ret.add(building + ", " + split[i]);
building = null; building = null;
} else { } else if (inString || split[i].startsWith("\"")) {
inString = true;
if (building == null) if (building == null)
building = split[i]; building = split[i];
else else
building += ", " + split[i]; building += ", " + split[i];
} else {
ret.add(split[i]);
} }
} }
return ret.toArray(new String[0]); return ret.toArray(new String[0]);
} }
private static AnnotationNode parseAnnotation(String s) { private static AnnotationNode parseAnnotation(HugeStringsRev hsr, String s) {
s = s.substring(1); s = s.substring(1);
String[] split = s.split(" "); String[] split = s.split(" ");
AnnotationNode node = new AnnotationNode(UnicodeUtils.unescape(split[0])); AnnotationNode node = new AnnotationNode(UnicodeUtils.unescape(hsr, split[0]));
if (split.length > 1) { if (split.length > 1) {
if (node.values == null) { if (node.values == null) {
node.values = new ArrayList<>(); node.values = new ArrayList<>();
@@ -538,7 +577,7 @@ public class Assembler {
String split3[] = splitWithStrings(value.substring(2, value.length() - 2)); String split3[] = splitWithStrings(value.substring(2, value.length() - 2));
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
for (String rofl : split3) { for (String rofl : split3) {
rofl = UnicodeUtils.unescape(rofl); rofl = UnicodeUtils.unescape(hsr, rofl);
if (rofl.startsWith("\"")) { if (rofl.startsWith("\"")) {
int index = rofl.lastIndexOf("\""); int index = rofl.lastIndexOf("\"");
list.add(rofl.substring(1, index)); list.add(rofl.substring(1, index));
@@ -552,7 +591,7 @@ public class Assembler {
} }
node.values.add(list); node.values.add(list);
} else { } else {
value = UnicodeUtils.unescape(value); value = UnicodeUtils.unescape(hsr, value);
if (value.startsWith("\"")) { if (value.startsWith("\"")) {
int index = value.lastIndexOf("\""); int index = value.lastIndexOf("\"");
node.values.add(value.substring(1, index)); node.values.add(value.substring(1, index));
@@ -574,29 +613,31 @@ public class Assembler {
if (args.length == 0) { if (args.length == 0) {
return null; return null;
} }
String ret = args[start]; StringBuilder sb = new StringBuilder(args[start]);
if (args.length > start + 1) { if (args.length > start + 1) {
for (int i = start + 1; i < args.length; i++) { 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) { private static String consolidateStrings(String[] args, int start, int end) {
if (end == 0) { if (end == 0) {
return null; return null;
} }
String ret = args[start]; StringBuilder sb = new StringBuilder(args[start]);
if (end > start + 1) { if (end > start + 1) {
for (int i = start + 1; i < end; i++) { 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(String s, String to) { private static Object getValue(HugeStringsRev hsr, String s, String to) {
if (s == null) { if (s == null) {
return null; return null;
} }
@@ -618,14 +659,15 @@ public class Assembler {
case "S": case "S":
return Short.parseShort(s); return Short.parseShort(s);
case "Ljava/lang/String;": case "Ljava/lang/String;":
return UnicodeUtils.unescape(s.substring(1, s.length() - 1)); return UnicodeUtils.unescape(hsr, s.substring(1, s.length() - 1));
default: { default: {
return null; return null;
} }
} }
} }
private static AbstractInsnNode getNode(String s, HashMap<LabelNode, Integer> labels) throws Exception { private static AbstractInsnNode getNode(HugeStringsRev hsr, String s, HashMap<LabelNode, Integer> labels)
throws Exception {
if (s.startsWith("// label ")) { if (s.startsWith("// label ")) {
int labelNr = Integer.parseInt(s.substring(9)); int labelNr = Integer.parseInt(s.substring(9));
for (Map.Entry<LabelNode, Integer> entry : labels.entrySet()) { for (Map.Entry<LabelNode, Integer> entry : labels.entrySet()) {
@@ -648,124 +690,41 @@ public class Assembler {
labels.put(ln, labelNr); labels.put(ln, labelNr);
return new LineNumberNode(Integer.parseInt(s.split(" ")[0]), ln); return new LineNumberNode(Integer.parseInt(s.split(" ")[0]), ln);
} else if (s.startsWith("// frame ")) { } else if (s.startsWith("// frame ")) {
String str1 = s.substring(7); String str1 = s.substring(9);
String str = str1.substring(str1.indexOf(' ', 3) + 1); String[] split = str1.split(" ", 2);
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 {
String frame = UnicodeUtils.unescape(asd);
frame = frame.substring(1, frame.length() - 1);
arr.add(frame);
}
} 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 {
String frame = UnicodeUtils.unescape(asd);
frame = frame.substring(1, frame.length() - 1);
arr.add(frame);
}
} 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++;
}
}
}
int type = 0; int type = 0;
switch (str1.substring(str1.indexOf(' ', 1) + 1, str1.indexOf(' ', 3))) { Object[] local = null;
case "NEW": Object[] stack = null;
type = Opcodes.F_NEW; int localLen = 0;
break; int stackLen = 0;
case "FULL": switch (split[0].toUpperCase()) {
type = Opcodes.F_FULL;
break;
case "APPEND":
type = Opcodes.F_APPEND;
break;
case "CHOP":
type = Opcodes.F_CHOP;
break;
case "SAME": case "SAME":
type = Opcodes.F_SAME; type = Opcodes.F_SAME;
break; break;
case "SAME1": case "SAME1":
type = Opcodes.F_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; break;
} }
return new FrameNode(type, local.length, local, stack.length, stack); return new FrameNode(type, localLen, local, stackLen, stack);
} else { } else {
String[] split = s.split(" "); String[] split = s.split(" ");
String command = split[0]; String command = split[0];
@@ -803,10 +762,10 @@ public class Assembler {
case "dconst_1": case "dconst_1":
return new InsnNode(15); return new InsnNode(15);
case "bipush": { case "bipush": {
return new IntInsnNode(16, Integer.parseInt(split[1])); return new IntInsnNode(16, (byte) Integer.parseInt(split[1]));
} }
case "sipush": { case "sipush": {
return new IntInsnNode(17, Integer.parseInt(split[1])); return new IntInsnNode(17, (short) Integer.parseInt(split[1]));
} }
case "ldc": { case "ldc": {
String str = consolidateStrings(split, 1); String str = consolidateStrings(split, 1);
@@ -818,10 +777,12 @@ public class Assembler {
String type = split2[0].substring(7); String type = split2[0].substring(7);
// Reflection // Reflection
val = new Type(ClassUtil.getIDFromClassNameForType(type), val = new Type(ClassUtil.getIDFromClassNameForType(type),
UnicodeUtils.unescape(split2[3].substring(6, split2[3].length() - 1)), UnicodeUtils.unescape(hsr, split2[3].substring(6, split2[3].length() - 1)),
Integer.parseInt(split2[1].substring(7)), Integer.parseInt(split2[2].substring(5))); Integer.parseInt(split2[1].substring(7)), Integer.parseInt(split2[2].substring(5)));
} else if (str.startsWith("\"") && str.endsWith("\"")) { } else if (str.startsWith("\"") && str.endsWith("\"")) {
val = UnicodeUtils.unescape(str.substring(1, str.length() - 1)); val = UnicodeUtils.unescape(hsr, str.substring(1, str.length() - 1), true);
} else if (str.startsWith("#")) {
val = UnicodeUtils.unescape(hsr, str);
} else if (str.endsWith("l")) { } else if (str.endsWith("l")) {
val = Long.parseLong(str.substring(0, str.length() - 1)); val = Long.parseLong(str.substring(0, str.length() - 1));
} else if (str.endsWith("f")) { } else if (str.endsWith("f")) {
@@ -1437,50 +1398,54 @@ public class Assembler {
case "getstatic": { case "getstatic": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new FieldInsnNode(178, UnicodeUtils.unescape(target.substring(0, index)), return new FieldInsnNode(178, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
} }
case "putstatic": { case "putstatic": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new FieldInsnNode(179, UnicodeUtils.unescape(target.substring(0, index)), return new FieldInsnNode(179, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
} }
case "getfield": { case "getfield": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new FieldInsnNode(180, UnicodeUtils.unescape(target.substring(0, index)), return new FieldInsnNode(180, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
} }
case "putfield": { case "putfield": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new FieldInsnNode(181, UnicodeUtils.unescape(target.substring(0, index)), return new FieldInsnNode(181, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1])); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
} }
case "invokevirtual": { case "invokevirtual": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new MethodInsnNode(182, UnicodeUtils.unescape(target.substring(0, index)), return new MethodInsnNode(182, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
false);
} }
case "invokespecial": { case "invokespecial": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new MethodInsnNode(183, UnicodeUtils.unescape(target.substring(0, index)), return new MethodInsnNode(183, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
false);
} }
case "invokestatic": { case "invokestatic": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new MethodInsnNode(184, UnicodeUtils.unescape(target.substring(0, index)), return new MethodInsnNode(184, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
false);
} }
case "invokeinterface": { case "invokeinterface": {
String target = split[2]; String target = split[2];
int index = target.lastIndexOf("/"); int index = target.lastIndexOf("/");
return new MethodInsnNode(185, UnicodeUtils.unescape(target.substring(0, index)), return new MethodInsnNode(185, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), true); UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
true);
} }
case "invokedynamic": { case "invokedynamic": {
String str = s.substring(16, s.length() - 2).replace("", ""); String str = s.substring(16, s.length() - 2).replace("", "");
@@ -1495,7 +1460,7 @@ public class Assembler {
try { try {
// Reflection // Reflection
args.add(new Type(ClassUtil.getIDFromClassNameForType(vals.get(0).substring(6)), args.add(new Type(ClassUtil.getIDFromClassNameForType(vals.get(0).substring(6)),
UnicodeUtils.unescape(vals.get(3).substring(6, vals.get(3).length() - 1)), UnicodeUtils.unescape(hsr, vals.get(3).substring(6, vals.get(3).length() - 1)),
Integer.parseInt(vals.get(1).substring(7)), Integer.parseInt(vals.get(1).substring(7)),
Integer.parseInt(vals.get(2).substring(5)))); Integer.parseInt(vals.get(2).substring(5))));
} catch (Exception e) { } catch (Exception e) {
@@ -1503,9 +1468,9 @@ public class Assembler {
} }
} else if (stage == 2) { } else if (stage == 2) {
args.add(new Handle(OpcodesReverse.getHandleOpcode(vals.get(4).substring(5)), args.add(new Handle(OpcodesReverse.getHandleOpcode(vals.get(4).substring(5)),
UnicodeUtils.unescape(vals.get(1).substring(7)), UnicodeUtils.unescape(hsr, vals.get(1).substring(7)),
UnicodeUtils.unescape(vals.get(0).substring(6)), UnicodeUtils.unescape(hsr, vals.get(0).substring(6)),
UnicodeUtils.unescape(vals.get(2).substring(6)), UnicodeUtils.unescape(hsr, vals.get(2).substring(6)),
Boolean.parseBoolean(vals.get(3).substring(13)))); Boolean.parseBoolean(vals.get(3).substring(13))));
vals.clear(); vals.clear();
} }
@@ -1523,7 +1488,7 @@ public class Assembler {
vals.add(st); vals.add(st);
} else if (stage == 0) { } else if (stage == 0) {
if (st.startsWith("\"") && st.endsWith("\"")) { if (st.startsWith("\"") && st.endsWith("\"")) {
args.add(UnicodeUtils.unescape(st.substring(1, st.length() - 1))); args.add(UnicodeUtils.unescape(hsr, st.substring(1, st.length() - 1)));
} else if (st.endsWith("l")) { } else if (st.endsWith("l")) {
args.add(Long.parseLong(st.substring(0, st.length() - 1))); args.add(Long.parseLong(st.substring(0, st.length() - 1)));
} else if (st.endsWith("f")) { } else if (st.endsWith("f")) {
@@ -1535,27 +1500,23 @@ public class Assembler {
} }
} }
} }
Object[] _args = new Object[args.size()]; return new InvokeDynamicInsnNode(UnicodeUtils.unescape(hsr, sp[0].substring(7)),
stage = 0; UnicodeUtils.unescape(hsr, sp[1].substring(6)),
for (Object o : args) {
_args[stage] = o;
stage++;
}
return new InvokeDynamicInsnNode(UnicodeUtils.unescape(sp[0].substring(7)),
UnicodeUtils.unescape(sp[1].substring(6)),
new Handle(OpcodesReverse.getHandleOpcode(sp[7].substring(6)), new Handle(OpcodesReverse.getHandleOpcode(sp[7].substring(6)),
UnicodeUtils.unescape(sp[4].substring(8)), UnicodeUtils.unescape(sp[3].substring(7)), UnicodeUtils.unescape(hsr, sp[4].substring(8)),
UnicodeUtils.unescape(sp[5].substring(7)), Boolean.parseBoolean(sp[6].substring(14))), UnicodeUtils.unescape(hsr, sp[3].substring(7)),
_args); UnicodeUtils.unescape(hsr, sp[5].substring(7)),
Boolean.parseBoolean(sp[6].substring(14))),
args.toArray(new Object[args.size()]));
} }
case "new": { case "new": {
return new TypeInsnNode(187, UnicodeUtils.unescape(split[1])); return new TypeInsnNode(187, UnicodeUtils.unescape(hsr, split[1]));
} }
case "newarray": { case "newarray": {
return new IntInsnNode(188, ClassUtil.getArrayIDByType(split[1])); return new IntInsnNode(188, ClassUtil.getArrayIDByType(split[1]));
} }
case "anewarray": { case "anewarray": {
return new TypeInsnNode(189, UnicodeUtils.unescape(split[1])); return new TypeInsnNode(189, UnicodeUtils.unescape(hsr, split[1]));
} }
case "arraylength": { case "arraylength": {
return new InsnNode(190); return new InsnNode(190);
@@ -1564,10 +1525,10 @@ public class Assembler {
return new InsnNode(191); return new InsnNode(191);
} }
case "checkcast": { case "checkcast": {
return new TypeInsnNode(192, UnicodeUtils.unescape(split[1])); return new TypeInsnNode(192, UnicodeUtils.unescape(hsr, split[1]));
} }
case "instanceof": { case "instanceof": {
return new TypeInsnNode(193, UnicodeUtils.unescape(split[1])); return new TypeInsnNode(193, UnicodeUtils.unescape(hsr, split[1]));
} }
case "monitorenter": { case "monitorenter": {
return new InsnNode(194); return new InsnNode(194);
@@ -1576,7 +1537,7 @@ public class Assembler {
return new InsnNode(195); return new InsnNode(195);
} }
case "multianewarray": { case "multianewarray": {
return new MultiANewArrayInsnNode(UnicodeUtils.unescape(split[1]), Integer.parseInt(split[2])); return new MultiANewArrayInsnNode(UnicodeUtils.unescape(hsr, split[1]), Integer.parseInt(split[2]));
} }
case "ifnull": { case "ifnull": {
int labelNr = Integer.parseInt(s.split(" ")[1]); int labelNr = Integer.parseInt(s.split(" ")[1]);
@@ -1605,4 +1566,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()]);
}
} }
+304 -414
View File
@@ -1,15 +1,34 @@
package me.ByteEdit.edit; 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.FutureTask;
import org.objectweb.asm.Handle; import org.objectweb.asm.Handle;
import org.objectweb.asm.Label; import org.objectweb.asm.Label;
@@ -23,7 +42,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.FrameNode; import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode; import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.InnerClassNode;
import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.InvokeDynamicInsnNode; import org.objectweb.asm.tree.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.JumpInsnNode;
@@ -40,111 +59,108 @@ import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode; import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode; 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.ClassUtil;
import me.ByteEdit.utils.OpcodesReverse; import me.ByteEdit.utils.OpcodesReverse;
import me.ByteEdit.utils.UnicodeUtils; import me.ByteEdit.utils.UnicodeUtils;
public class Disassembler { public class Disassembler implements IDecompiler {
private static final ExecutorService exec = Executors private static final ExecutorService exec = Executors
.newFixedThreadPool((Runtime.getRuntime().availableProcessors() + 1) / 2); .newFixedThreadPool((Runtime.getRuntime().availableProcessors() + 1) / 2);
public static String disassemble(ClassNode classNode) { @Override
return disassemble(classNode, null).getDisassembly(); 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 static String disassemble(ClassNode classNode) {
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) {
if (classNode == null) { 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 { try {
AtomicInteger lineFound = new AtomicInteger(-1); HugeStrings hs = new HugeStrings();
StringContext ctx = new StringContext(16); StringContext ctx = new StringContext(18);
ctx.next("// #Annotations:\n"); ctx.next("// #Annotations:\n");
ctx.next(doAnnotations(classNode.visibleAnnotations)); ctx.next(doAnnotations(classNode.visibleAnnotations, hs, ""));
ctx.next("// #Class v:" + classNode.version + "\n"); ctx.next("// #Class v:" + classNode.version + "\n");
if (classNode.signature != null) if (classNode.signature != null)
ctx.next("// #Signature: " + UnicodeUtils.escapeWithSpaces(classNode.signature) + "\n"); ctx.next("// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, classNode.signature) + "\n");
if (classNode.outerMethod != null) if (classNode.outerMethod != null)
ctx.next("// #OuterMethod: " + (classNode.outerMethod == null ? "null" ctx.next(
: (UnicodeUtils.escapeWithSpaces(classNode.outerMethod) + " " "// #OuterMethod: "
+ UnicodeUtils.escapeWithSpaces(classNode.outerMethodDesc))) + (classNode.outerMethod == null ? "null"
+ "\n"); : (UnicodeUtils.escapeWithSpaces(hs, classNode.outerMethod) + " "
+ UnicodeUtils.escapeWithSpaces(hs, classNode.outerMethodDesc)))
+ "\n");
if (classNode.outerClass != null) if (classNode.outerClass != null)
ctx.next("// #OuterClass: " + UnicodeUtils.escapeWithSpaces(classNode.outerClass) + "\n"); ctx.next("// #OuterClass: " + UnicodeUtils.escapeWithSpaces(hs, classNode.outerClass) + "\n");
ctx.next("// #InnerClasses:\n"); ctx.next("// #InnerClasses:\n");
if (classNode.innerClasses != null) { if (classNode.innerClasses != null) {
String ics = ""; String ics = "";
for (InnerClassNode icn : classNode.innerClasses) { for (InnerClassNode icn : classNode.innerClasses) {
ics += "// " + UnicodeUtils.escapeWithSpaces(icn.name) + " " ics += "// " + UnicodeUtils.escapeWithSpaces(hs, icn.name) + " "
+ UnicodeUtils.escapeWithSpaces(icn.outerName) + " " + UnicodeUtils.escapeWithSpaces(hs, icn.outerName) + " "
+ UnicodeUtils.escapeWithSpaces(icn.innerName) + UnicodeUtils.escapeWithSpaces(hs, icn.innerName)
+ (icn.access == 0 ? "" : " " + ClassUtil.getAccessFlagsFull(icn.access).trim()) + "\n"; + (icn.access == 0 ? "" : " " + ClassUtil.getAccessFlagsFull(icn.access).trim()) + "\n";
} }
ctx.next(ics); ctx.next(ics);
} }
ctx.next(ClassUtil.getAccessFlagsClass(classNode.access) + UnicodeUtils.escapeWithSpaces(classNode.name) ctx.next(ClassUtil.getAccessFlagsClass(classNode.access) + UnicodeUtils.escapeWithSpaces(hs, classNode.name)
+ " "); + " ");
ctx.next("extends " + UnicodeUtils.escapeWithSpaces(classNode.superName) + " "); ctx.next("extends " + UnicodeUtils.escapeWithSpaces(hs, classNode.superName) + " ");
if (classNode.interfaces != null && !classNode.interfaces.isEmpty()) { if (classNode.interfaces != null && !classNode.interfaces.isEmpty()) {
String interfaceStr = ""; String interfaceStr = "";
for (String interfc : classNode.interfaces) { for (String interfc : classNode.interfaces) {
if (interfaceStr.isEmpty()) { if (interfaceStr.isEmpty()) {
interfaceStr += UnicodeUtils.escapeWithSpaces(interfc); interfaceStr += UnicodeUtils.escapeWithSpaces(hs, interfc);
} else { } else {
interfaceStr += ", " + UnicodeUtils.escapeWithSpaces(interfc); interfaceStr += ", " + UnicodeUtils.escapeWithSpaces(hs, interfc);
} }
} }
if (!interfaceStr.isEmpty()) if (!interfaceStr.isEmpty())
ctx.next("implements " + interfaceStr + " "); ctx.next("implements " + interfaceStr + " ");
} }
ctx.next("{\n// #SourceFile: " ctx.next("{\n// #SourceFile: "
+ (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(classNode.sourceFile)) + (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(hs, classNode.sourceFile)) + "\n");
+ "\n\n// #Fields\n"); int hugeIdx = ctx.next("");
ctx.next(doFields(classNode.fields, nodeToFind, lineFound)); ctx.next("\n// #Fields\n");
ctx.next(doFields(classNode.fields, hs));
ctx.next("\n// #Methods\n"); ctx.next("\n// #Methods\n");
ctx.next(doMethods(classNode.methods, classNode.name, nodeToFind, lineFound)); ctx.next(doMethods(classNode.methods, hs));
ctx.next("}\n"); ctx.next("}\n");
return new DisassembleTuple(ctx.finish(), lineFound.get());
if (!hs.isEmpty())
ctx.set(hugeIdx, hs.makeStringsList());
return ctx.finish();
} catch (Throwable e) { } catch (Throwable e) {
return new DisassembleTuple("Class couldn't be decompiled:\n" + e.getClass().getName() + ": " return "Class couldn't be decompiled:\n" + e.getClass().getName() + ": " + e.getMessage() + "\n"
+ e.getMessage() + "\n"
+ Arrays.toString(e.getStackTrace()).replace(", ", "\n\tat ").replace("[", "\tat ").replace("]", "") + Arrays.toString(e.getStackTrace()).replace(", ", "\n\tat ").replace("[", "\tat ").replace("]", "")
+ "\n"); + "\n";
} }
} }
private static String doAnnotations(List<AnnotationNode> annotations) { private static String doAnnotations(List<AnnotationNode> annotations, HugeStrings hs, String linePrefix) {
if (annotations == null) if (annotations == null)
return ""; return "";
StringContext ctx = new StringContext(annotations.size()); StringContext ctx = new StringContext(annotations.size());
for (AnnotationNode annotationNode : annotations) { for (AnnotationNode annotationNode : annotations) {
String s = "@" + UnicodeUtils.escapeWithSpaces(annotationNode.desc); String s = linePrefix + "@" + UnicodeUtils.escapeWithSpaces(hs, annotationNode.desc);
if (annotationNode.values != null && !annotationNode.values.isEmpty()) { if (annotationNode.values != null && !annotationNode.values.isEmpty()) {
s += " ("; s += " (";
boolean valBefore = true; boolean valBefore = true;
@@ -153,48 +169,47 @@ public class Disassembler {
s += o + " = ["; s += o + " = [";
valBefore = false; valBefore = false;
continue; continue;
} else { }
if (o instanceof String[]) { if (o instanceof String[]) {
String[] arr = (String[]) o; String[] arr = (String[]) o;
boolean w8ing = false; boolean w8ing = false;
for (String rofl : arr) { for (String rofl : arr) {
if (!w8ing) { if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(rofl) + "/"; s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true; w8ing = true;
} else { } else {
s += UnicodeUtils.escapeWithSpaces(rofl) + "]"; 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 += ", ";
} 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(rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(rofl);
}
}
} else {
s += "\"" + UnicodeUtils.escape(String.valueOf(obj)) + "\"";
}
s += ", ";
}
s = s.substring(0, s.length() - 2);
s += " }]";
} else if (o instanceof String) {
s += "\"" + UnicodeUtils.escapeWithSpaces((String) o) + "\"], ";
} else {
s += "(" + o.getClass().getName().replace(".", "/") + ") " + o + "], ";
} }
valBefore = true; 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(", ")) if (s.endsWith(", "))
s = s.substring(0, s.length() - 2); s = s.substring(0, s.length() - 2);
@@ -206,93 +221,35 @@ public class Disassembler {
return ctx.finish(); return ctx.finish();
} }
private static String doMethods(List<MethodNode> methods, String className, Object nodeToFind, private static String doMethods(List<MethodNode> methods, HugeStrings hs)
AtomicInteger lineFound) throws InterruptedException, ExecutionException { throws InterruptedException, ExecutionException {
boolean multithreaded = Main.INSTANCE.mntmMultithreaded.isSelected();
Future<String>[] futures = new Future[methods.size()]; Future<String>[] futures = new Future[methods.size()];
for (int i = 0; i < futures.length; i++) { for (int i = 0; i < futures.length; i++) {
MethodNode mn = methods.get(i); MethodNode mn = methods.get(i);
futures[i] = exec.submit(new Callable<String>() { Callable<String> callable = new Callable<String>() {
@Override @Override
public String call() throws Exception { public String call() throws Exception {
String ms = ""; String ms = "";
try { try {
ms += "\t// #Max: l:" + mn.maxLocals + " s:" + mn.maxStack + "\n"; ms += "\t// #Max: l:" + mn.maxLocals + " s:" + mn.maxStack + "\n";
if (mn.signature != null) if (mn.signature != null)
ms += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(mn.signature) + "\n"; ms += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, mn.signature) + "\n";
String[] dis = disassembleMethod(className, mn); String[] dis = disassembleMethod(mn, hs);
ms += dis[2]; ms += dis[2];
ms += dis[1]; ms += dis[1];
if (mn.visibleAnnotations != null && !mn.visibleAnnotations.isEmpty()) { if (mn.visibleAnnotations != null && !mn.visibleAnnotations.isEmpty())
for (AnnotationNode annotationNode : mn.visibleAnnotations) { ms += doAnnotations(mn.visibleAnnotations, hs, "\t");
ms += "\t@" + annotationNode.desc; ms += "\t" + ClassUtil.getAccessFlagsFull(mn.access)
if (annotationNode.values != null && !annotationNode.values.isEmpty()) { + UnicodeUtils.escapeWithSpaces(hs, mn.name) + " "
ms += " ("; + UnicodeUtils.escapeWithSpaces(hs, mn.desc) + " ";
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(rofl) + "/";
w8ing = true;
} else {
ms += UnicodeUtils.escapeWithSpaces(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(rofl) + "/";
w8ing = true;
} else {
ms += UnicodeUtils.escapeWithSpaces(rofl);
}
}
} else {
ms += "\"" + UnicodeUtils.escape(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);
}
ms += "\t" + ClassUtil.getAccessFlagsFull(mn.access) + UnicodeUtils.escapeWithSpaces(mn.name)
+ " " + UnicodeUtils.escapeWithSpaces(mn.desc) + " ";
if (mn.exceptions != null && !mn.exceptions.isEmpty()) { if (mn.exceptions != null && !mn.exceptions.isEmpty()) {
String exceptionStr = ""; String exceptionStr = "";
for (String exc : mn.exceptions) { for (String exc : mn.exceptions) {
if (exceptionStr.isEmpty()) { if (exceptionStr.isEmpty()) {
exceptionStr += UnicodeUtils.escapeWithSpaces(exc); exceptionStr += UnicodeUtils.escapeWithSpaces(hs, exc);
} else { } else {
exceptionStr += ", " + UnicodeUtils.escapeWithSpaces(exc); exceptionStr += ", " + UnicodeUtils.escapeWithSpaces(hs, exc);
} }
} }
if (!exceptionStr.isEmpty()) if (!exceptionStr.isEmpty())
@@ -309,87 +266,33 @@ public class Disassembler {
} }
return ms; return ms;
} }
}); };
futures[i] = multithreaded ? exec.submit(callable) : new FutureTask<>(callable);
} }
StringContext ctx = new StringContext(futures.length); StringContext ctx = new StringContext(futures.length);
for (Future<String> f : futures) { for (Future<String> f : futures) {
if (f instanceof FutureTask)
((FutureTask) f).run();
ctx.next(f.get()); ctx.next(f.get());
} }
return ctx.finish(); String s = ctx.finish();
if (s.isEmpty())
return "\n";
return s;
} }
private static String doFields(List<FieldNode> fields, Object nodeToFind, AtomicInteger lineFound) { private static String doFields(List<FieldNode> fields, HugeStrings hs) {
StringContext ctx = new StringContext(fields.size()); StringContext ctx = new StringContext(fields.size());
for (FieldNode fn : fields) { for (FieldNode fn : fields) {
String s = ""; String s = "";
if (fn.signature != null) if (fn.signature != null)
s += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(fn.signature) + "\n"; s += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, fn.signature) + "\n";
if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty()) { if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty())
for (AnnotationNode annotationNode : fn.visibleAnnotations) { s += doAnnotations(fn.visibleAnnotations, hs, "\t");
s += "\t@" + UnicodeUtils.escapeWithSpaces(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(rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(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(rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(rofl);
}
}
} else {
s += "\"" + UnicodeUtils.escape(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);
}
s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient") s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient")
+ UnicodeUtils.escapeWithSpaces(fn.desc) + " " + UnicodeUtils.escapeWithSpaces(fn.name); + UnicodeUtils.escapeWithSpaces(hs, fn.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, fn.name);
if (fn.value != null) { if (fn.value != null) {
s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc, true); s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc, true, hs);
} }
s += "\n"; s += "\n";
ctx.next(s); ctx.next(s);
@@ -397,13 +300,22 @@ public class Disassembler {
return ctx.finish(); return ctx.finish();
} }
private static String[] disassembleMethod(String className, MethodNode mn) { private static String[] disassembleMethod(MethodNode mn, HugeStrings hs) {
HashMap<Label, Integer> labels = new HashMap<Label, Integer>(); HashMap<Label, Integer> labels = new HashMap<Label, Integer>();
String localVarTable; String localVarTable;
String tryCatchTable; String tryCatchTable;
for (AbstractInsnNode n : mn.instructions.toArray()) {
InsnList insnList = mn.instructions;
boolean deobfNumbers = Main.INSTANCE.mntmNumbers.isSelected();
ArrayList<Label> labelsReversed = deobfNumbers ? new ArrayList<>() : null;
for (AbstractInsnNode n = insnList.getFirst(); n != null; n = n.getNext()) {
if (n instanceof LabelNode) { if (n instanceof LabelNode) {
labels.put(((LabelNode) n).getLabel(), labels.size() + 1); int id = labels.size() + 1;
if (deobfNumbers && labelsReversed != null)
labelsReversed.add(((LabelNode) n).getLabel());
labels.put(((LabelNode) n).getLabel(), id);
} }
} }
if (mn.localVariables != null && !mn.localVariables.isEmpty()) { if (mn.localVariables != null && !mn.localVariables.isEmpty()) {
@@ -411,10 +323,10 @@ public class Disassembler {
StringContext ctx = new StringContext(list.size() + 1); StringContext ctx = new StringContext(list.size() + 1);
ctx.next("\t// #LocalVars:\n"); ctx.next("\t// #LocalVars:\n");
for (LocalVariableNode lvn : list) { for (LocalVariableNode lvn : list) {
ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(lvn.name) + ": " ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(hs, lvn.name) + ": "
+ UnicodeUtils.escapeWithSpaces(lvn.desc) + " i:" + lvn.index + " s:" + UnicodeUtils.escapeWithSpaces(hs, lvn.desc) + " i:" + lvn.index + " s:"
+ labels.get(lvn.start.getLabel()) + " e:" + labels.get(lvn.end.getLabel()) + " sig:" + labels.get(lvn.start.getLabel()) + " e:" + labels.get(lvn.end.getLabel()) + " sig:"
+ UnicodeUtils.escapeWithSpaces(lvn.signature) + "\n"); + UnicodeUtils.escapeWithSpaces(hs, lvn.signature) + "\n");
} }
localVarTable = ctx.finish(); localVarTable = ctx.finish();
} else } else
@@ -424,123 +336,75 @@ public class Disassembler {
StringContext ctx = new StringContext(list.size() + 1); StringContext ctx = new StringContext(list.size() + 1);
ctx.next("\t// #TryCatch:\n"); ctx.next("\t// #TryCatch:\n");
for (TryCatchBlockNode tcbn : mn.tryCatchBlocks) { for (TryCatchBlockNode tcbn : mn.tryCatchBlocks) {
ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(tcbn.type) + " s:" + labels.get(tcbn.start.getLabel()) ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(hs, tcbn.type) + " s:"
+ " e:" + labels.get(tcbn.end.getLabel()) + " h:" + labels.get(tcbn.handler.getLabel()) + "\n"); + labels.get(tcbn.start.getLabel()) + " e:" + labels.get(tcbn.end.getLabel()) + " h:"
+ labels.get(tcbn.handler.getLabel()) + "\n");
} }
tryCatchTable = ctx.finish(); tryCatchTable = ctx.finish();
} else } else
tryCatchTable = ""; tryCatchTable = "";
AbstractInsnNode[] insns = mn.instructions.toArray();
StringContext ctx = new StringContext(insns.length); if (deobfNumbers) {
for (AbstractInsnNode n : insns) { StackBasedCalculator calc = new StackBasedCalculator(insnList);
ctx.next(disassembleInstruction(n, labels)); calc.run();
insnList = calc.get();
int i = 0;
for (AbstractInsnNode n = insnList.getFirst(); n != null; n = n.getNext()) {
if (n instanceof LabelNode && labelsReversed != null) {
((LabelNode) n).setLabel(labelsReversed.get(i++));
}
}
}
StringContext ctx = new StringContext(insnList.size());
for (AbstractInsnNode n = insnList.getFirst(); n != null; n = n.getNext()) {
ctx.next(disassembleInstruction(n, labels, hs));
} }
return new String[] { ctx.finish(), localVarTable, tryCatchTable }; return new String[] { ctx.finish(), localVarTable, tryCatchTable };
} }
public static final HashMap<Class, InstructionInterface> iiMap = new HashMap<>(); public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels, HugeStrings hs) {
switch (n.getType()) {
static { case INSN: {
iiMap.put(FieldInsnNode.class, (n, labels) -> { 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; FieldInsnNode node = (FieldInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(node.desc) + " " + UnicodeUtils.escapeWithSpaces(node.owner) + "/" + UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
+ UnicodeUtils.escapeWithSpaces(node.name).replace("/", "\\u002F") + "\n"; + "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
}); }
iiMap.put(LabelNode.class, (n, labels) -> { case METHOD_INSN: {
LabelNode node = (LabelNode) n; MethodInsnNode node = (MethodInsnNode) n;
return "\t\t// label " + labels.get(node.getLabel()) + "\n"; return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
}); + UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
iiMap.put(FrameNode.class, (n, labels) -> { + "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
FrameNode node = (FrameNode) n; }
String type = ""; case INVOKE_DYNAMIC_INSN: {
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((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((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) -> {
LineNumberNode node = (LineNumberNode) n;
return "\t\t// line " + node.line + " " + labels.get(node.start.getLabel()) + "\n";
});
iiMap.put(InvokeDynamicInsnNode.class, (n, labels) -> {
InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n; InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: " String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(node.name).replace("/", "\\u002F") + "\n\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(node.desc) + "\n\t\t\tHandle: [\n\t\t\t\tname: " + UnicodeUtils.escapeWithSpaces(hs, node.desc) + "\n\t\t\tHandle: [\n\t\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(node.bsm.getName()) + "\n\t\t\t\towner: " + UnicodeUtils.escapeWithSpaces(hs, node.bsm.getName()) + "\n\t\t\t\towner: "
+ UnicodeUtils.escapeWithSpaces(node.bsm.getOwner()) + "\n\t\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(hs, node.bsm.getOwner()) + "\n\t\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(node.bsm.getDesc()) + "\n\t\t\t\tisInterface: " + UnicodeUtils.escapeWithSpaces(hs, node.bsm.getDesc()) + "\n\t\t\t\tisInterface: "
+ node.bsm.isInterface() + "\n\t\t\t\ttag: " + OpcodesReverse.reverseHandleOpcode(node.bsm.getTag()) + node.bsm.isInterface() + "\n\t\t\t\ttag: " + OpcodesReverse.reverseHandleOpcode(node.bsm.getTag())
+ "\n\t\t\t]\n\t\t\targs: [\n"; + "\n\t\t\t]\n\t\t\targs: [\n";
for (Object l : node.bsmArgs) { for (Object l : node.bsmArgs) {
@@ -552,40 +416,34 @@ public class Disassembler {
// Reflection // Reflection
s += "\t\t\t\tType: [\n\t\t\t\t\ttype: " + ClassUtil.getClassNameFromType(type) 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: " + "\n\t\t\t\t\tstart: " + valueBegin + "\n\t\t\t\t\tend: " + valueEnd + "\n\t\t\t\t\tbuf: "
+ ClassUtil.getDecompiledValue(buf, ""); + ClassUtil.getDecompiledValue(buf, "", hs);
s += "\n\t\t\t\t]\n"; s += "\n\t\t\t\t]\n";
} else if (l instanceof Handle) { } else if (l instanceof Handle) {
Handle h = (Handle) l; Handle h = (Handle) l;
s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: " s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(h.getName()).replace("/", "\\u002F") + "\n\t\t\t\t\towner: " + UnicodeUtils.escapeWithSpaces(hs, h.getName()).replace("/", "\\u002F")
+ UnicodeUtils.escapeWithSpaces(h.getOwner()) + "\n\t\t\t\t\tdesc: " + "\n\t\t\t\t\towner: " + UnicodeUtils.escapeWithSpaces(hs, h.getOwner())
+ UnicodeUtils.escapeWithSpaces(h.getDesc()) + "\n\t\t\t\t\tisInterface: " + h.isInterface() + "\n\t\t\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(hs, h.getDesc())
+ "\n\t\t\t\t\ttag: " + OpcodesReverse.reverseHandleOpcode(h.getTag()); + "\n\t\t\t\t\tisInterface: " + h.isInterface() + "\n\t\t\t\t\ttag: "
+ OpcodesReverse.reverseHandleOpcode(h.getTag());
s += "\n\t\t\t\t]\n"; s += "\n\t\t\t\t]\n";
} else { } else {
s += "\t\t\t\t" + ClassUtil.getDecompiledValue(l, "") + "\n"; s += "\t\t\t\t" + ClassUtil.getDecompiledValue(l, "", hs) + "\n";
} }
} }
s += "\t\t\t]\n\t\t]\n"; s += "\t\t\t]\n\t\t]\n";
return s; return s;
}); }
iiMap.put(MethodInsnNode.class, (n, labels) -> { case JUMP_INSN: {
MethodInsnNode node = (MethodInsnNode) n; JumpInsnNode node = (JumpInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + labels.get(node.label.getLabel())
+ UnicodeUtils.escapeWithSpaces(node.desc) + " " + UnicodeUtils.escapeWithSpaces(node.owner) + "/" + "\n";
+ UnicodeUtils.escapeWithSpaces(node.name).replace("/", "\\u002F") + "\n"; }
}); case LABEL: {
iiMap.put(TypeInsnNode.class, (n, labels) -> { LabelNode node = (LabelNode) n;
TypeInsnNode node = (TypeInsnNode) n; return "\t\t// label " + labels.get(node.getLabel()) + "\n";
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " }
+ UnicodeUtils.escapeWithSpaces(node.desc) + "\n"; case LDC_INSN: {
});
iiMap.put(MultiANewArrayInsnNode.class, (n, labels) -> {
MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(node.desc) + " " + node.dims + "\n";
});
iiMap.put(LdcInsnNode.class, (n, labels) -> {
LdcInsnNode node = (LdcInsnNode) n; LdcInsnNode node = (LdcInsnNode) n;
if (node.cst instanceof Type) { if (node.cst instanceof Type) {
Type type = (Type) node.cst; Type type = (Type) node.cst;
@@ -595,37 +453,24 @@ public class Disassembler {
// Reflection // Reflection
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " Type: [\n\t\t\ttype: " 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: " + ClassUtil.getClassNameFromType(type) + "\n\t\t\tstart: " + valueBegin + "\n\t\t\tend: "
+ valueEnd + "\n\t\t\tbuf: " + ClassUtil.getDecompiledValue(buf, ""); + valueEnd + "\n\t\t\tbuf: " + ClassUtil.getDecompiledValue(buf, "", hs);
s += "\n\t\t]\n"; s += "\n\t\t]\n";
return s; return s;
} else {
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ ClassUtil.getDecompiledValue(node.cst, "") + "\n";
} }
}); String decomp = ClassUtil.getDecompiledValue(node.cst, "", hs);
iiMap.put(VarInsnNode.class, (n, labels) -> { if (node.cst instanceof String) {
VarInsnNode node = (VarInsnNode) n; if (decomp.startsWith("\"#") && decomp.endsWith("\""))
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + "\n"; decomp = decomp.substring(1, decomp.length() - 1);
}); else if (decomp.startsWith("\"\\u0023"))
iiMap.put(InsnNode.class, (n, labels) -> { decomp = "\"#" + decomp.substring(7);
InsnNode node = (InsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + "\n";
});
iiMap.put(IntInsnNode.class, (n, labels) -> {
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";
} }
}); return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + decomp + "\n";
iiMap.put(JumpInsnNode.class, (n, labels) -> { }
JumpInsnNode node = (JumpInsnNode) n; case IINC_INSN: {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + labels.get(node.label.getLabel()) IincInsnNode node = (IincInsnNode) n;
+ "\n"; return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + " " + node.incr + "\n";
}); }
iiMap.put(TableSwitchInsnNode.class, (n, labels) -> { case TABLESWITCH_INSN: {
TableSwitchInsnNode node = (TableSwitchInsnNode) n; TableSwitchInsnNode node = (TableSwitchInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tmin: " + node.min 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()) + "\n\t\t\tmax: " + node.max + "\n\t\t\tdefault: " + labels.get(node.dflt.getLabel())
@@ -635,8 +480,8 @@ public class Disassembler {
} }
s += "\t\t\t]\n\t\t]\n"; s += "\t\t\t]\n\t\t]\n";
return s; return s;
}); }
iiMap.put(LookupSwitchInsnNode.class, (n, labels) -> { case LOOKUPSWITCH_INSN: {
LookupSwitchInsnNode node = (LookupSwitchInsnNode) n; LookupSwitchInsnNode node = (LookupSwitchInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tdefault: " String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tdefault: "
+ labels.get(node.dflt.getLabel()) + "\n\t\t\tkeys: [\n"; + labels.get(node.dflt.getLabel()) + "\n\t\t\tkeys: [\n";
@@ -649,24 +494,69 @@ public class Disassembler {
} }
s += "\t\t\t]\n\t\t]\n"; s += "\t\t\t]\n\t\t]\n";
return s; return s;
}); }
iiMap.put(IincInsnNode.class, (n, labels) -> { case MULTIANEWARRAY_INSN: {
IincInsnNode node = (IincInsnNode) n; MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + " " + node.incr + "\n"; return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
}); + UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + node.dims + "\n";
} }
case FRAME: {
static interface InstructionInterface { FrameNode node = (FrameNode) n;
String apply(AbstractInsnNode n, HashMap<Label, Integer> labels); String out = "";
} switch (node.type) {
case Opcodes.F_SAME:
public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels) { out = "SAME";
InstructionInterface ii = iiMap.get(n.getClass()); break;
if (ii != null) case Opcodes.F_SAME1:
return ii.apply(n, labels); out = "SAME1 " + getFrameArray(hs, node.stack, labels);
else 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()) + " " return "\t\tNOT HANDLED! " + OpcodesReverse.reverseOpcode(n.getOpcode()) + " "
+ n.getClass().getSimpleName() + "\n"; + 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";
}
} }
+53
View File
@@ -0,0 +1,53 @@
package me.ByteEdit.edit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import me.ByteEdit.utils.UnicodeUtils;
public class HugeStrings {
public static int THRESHOLD = 1000;
private final HashMap<String, HugeData> map = new HashMap<>();
public String onString(String s) {
synchronized (map) {
HugeData i = map.get(s);
if (i == null)
map.put(s, i = new HugeData(map.size(), UnicodeUtils.escape(this, s, true)));
return "#" + Integer.toString(i.id);
}
}
public boolean isEmpty() {
return map.isEmpty();
}
public String makeStringsList() {
StringContext ctx = new StringContext(map.size());
List<HugeData> list = new ArrayList<>(map.values());
list.sort((o1, o2) -> Integer.compare(o1.id, o2.id));
for (HugeData hd : list) {
StringBuilder sb = new StringBuilder(20 + hd.escaped.length());
sb.append('#');
sb.append(Integer.toString(hd.id));
sb.append(": ");
sb.append(hd.escaped);
sb.append('\n');
ctx.next(sb.toString());
}
return ctx.finish();
}
private static class HugeData {
int id;
String escaped;
public HugeData(int id, String escaped) {
this.id = id;
this.escaped = escaped;
}
}
}
+20
View File
@@ -0,0 +1,20 @@
package me.ByteEdit.edit;
import java.util.HashMap;
public class HugeStringsRev {
private final HashMap<Integer, String> map = new HashMap<>();
public void add(int i, String s) {
map.put(Integer.valueOf(i), s);
}
public String get(int i) {
String s = map.get(Integer.valueOf(i));
if (s == null)
s = "#" + Integer.toString(i);
return s;
}
}
-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);
}
}
@@ -0,0 +1,689 @@
package me.ByteEdit.edit;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Stack;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
public class StackBasedCalculator implements Opcodes, Runnable {
private final InsnList insns;
private final Stack<Number> stack = new Stack<>();
private AbstractInsnNode start;
private int startIdx = -1;
private boolean isAtEnd = false;
public StackBasedCalculator(InsnList insns) {
this.insns = new InsnList();
HashMap<LabelNode, LabelNode> labelMap = new HashMap<>();
for (AbstractInsnNode node = insns.getFirst(); node != null; node = node.getNext()) {
if (node.getType() == AbstractInsnNode.LABEL) {
labelMap.put((LabelNode) node, new LabelNode());
}
}
for (AbstractInsnNode node = insns.getFirst(); node != null; node = node.getNext()) {
this.insns.add(node.clone(labelMap));
}
}
public InsnList get() {
return insns;
}
@Override
public void run() {
while (!isAtEnd) {
out: {
AbstractInsnNode[] arr = insns.toArray();
for (int i = startIdx == -1 ? 0 : startIdx; i < arr.length; i++) {
if (!handle(arr[i]))
break out;
}
isAtEnd = true;
}
}
}
private boolean handle(AbstractInsnNode node) {
try {
switch (node.getOpcode()) {
case ICONST_0:
stack.push(Integer.valueOf(0));
if (start == null)
start = node;
break;
case ICONST_1:
stack.push(Integer.valueOf(1));
if (start == null)
start = node;
break;
case ICONST_2:
stack.push(Integer.valueOf(2));
if (start == null)
start = node;
break;
case ICONST_3:
stack.push(Integer.valueOf(3));
if (start == null)
start = node;
break;
case ICONST_4:
stack.push(Integer.valueOf(4));
if (start == null)
start = node;
break;
case ICONST_5:
stack.push(Integer.valueOf(5));
if (start == null)
start = node;
break;
case ICONST_M1:
stack.push(Integer.valueOf(-1));
if (start == null)
start = node;
break;
case DCONST_0:
stack.push(Double.valueOf(0));
if (start == null)
start = node;
break;
case DCONST_1:
stack.push(Double.valueOf(1));
if (start == null)
start = node;
break;
case FCONST_0:
stack.push(Float.valueOf(0));
if (start == null)
start = node;
break;
case FCONST_1:
stack.push(Float.valueOf(1));
if (start == null)
start = node;
break;
case FCONST_2:
stack.push(Float.valueOf(2));
if (start == null)
start = node;
break;
case LCONST_0:
stack.push(Long.valueOf(0));
if (start == null)
start = node;
break;
case LCONST_1:
stack.push(Long.valueOf(1));
if (start == null)
start = node;
break;
case BIPUSH:
case SIPUSH:
stack.push(((IntInsnNode) node).operand);
if (start == null)
start = node;
break;
case D2F:
stack.push(((Double) stack.pop()).floatValue());
change(node);
return false;
case D2I:
stack.push(((Double) stack.pop()).intValue());
change(node);
return false;
case D2L:
stack.push(((Double) stack.pop()).longValue());
change(node);
return false;
case F2D:
stack.push(((Float) stack.pop()).doubleValue());
change(node);
return false;
case F2I:
stack.push(((Float) stack.pop()).intValue());
change(node);
return false;
case F2L:
stack.push(((Float) stack.pop()).longValue());
change(node);
return false;
case L2D:
stack.push(((Long) stack.pop()).doubleValue());
change(node);
return false;
case L2F:
stack.push(((Long) stack.pop()).floatValue());
change(node);
return false;
case L2I:
stack.push(((Long) stack.pop()).intValue());
change(node);
return false;
case I2B:
error(node);
return true;
case I2C:
error(node);
return true;
case I2D:
stack.push(((Integer) stack.pop()).doubleValue());
change(node);
return false;
case I2F:
stack.push(((Integer) stack.pop()).floatValue());
change(node);
return false;
case I2L:
stack.push(((Integer) stack.pop()).longValue());
change(node);
return false;
case I2S:
error(node);
return true;
case NOP:
break;
case LDC:
LdcInsnNode ldc = (LdcInsnNode) node;
if (ldc.cst instanceof Number) {
stack.push((Number) ldc.cst);
if (start == null)
start = node;
} else {
error(node);
return true;
}
break;
// long
case LADD: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom + top);
change(node);
return false;
}
case LSUB: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom - top);
change(node);
return false;
}
case LMUL: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom * top);
change(node);
return false;
}
case LDIV: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom / top);
change(node);
return false;
}
case LREM: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom % top);
change(node);
return false;
}
case LNEG: {
long top = ((Long) stack.pop()).longValue();
stack.push(-top);
change(node);
return false;
}
case LAND: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom & top);
change(node);
return false;
}
case LOR: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom | top);
change(node);
return false;
}
case LXOR: {
long top = ((Long) stack.pop()).longValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom ^ top);
change(node);
return false;
}
case LSHR: {
int top = ((Integer) stack.pop()).intValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom >> top);
change(node);
return false;
}
case LSHL: {
int top = ((Integer) stack.pop()).intValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom << top);
change(node);
return false;
}
case LUSHR: {
int top = ((Integer) stack.pop()).intValue();
long bottom = ((Long) stack.pop()).longValue();
stack.push(bottom >>> top);
change(node);
return false;
}
// int
case IADD: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom + top);
change(node);
return false;
}
case ISUB: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom - top);
change(node);
return false;
}
case IMUL: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom * top);
change(node);
return false;
}
case IDIV: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom / top);
change(node);
return false;
}
case IREM: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom % top);
change(node);
return false;
}
case INEG: {
int top = ((Integer) stack.pop()).intValue();
stack.push(-top);
change(node);
return false;
}
case IAND: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom & top);
change(node);
return false;
}
case IOR: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom | top);
change(node);
return false;
}
case IXOR: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom ^ top);
change(node);
return false;
}
case ISHR: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom >> top);
change(node);
return false;
}
case ISHL: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom << top);
change(node);
return false;
}
case IUSHR: {
int top = ((Integer) stack.pop()).intValue();
int bottom = ((Integer) stack.pop()).intValue();
stack.push(bottom >>> top);
change(node);
return false;
}
// double
case DADD: {
double top = ((Double) stack.pop()).doubleValue();
double bottom = ((Double) stack.pop()).doubleValue();
stack.push(bottom + top);
change(node);
return false;
}
case DSUB: {
double top = ((Double) stack.pop()).doubleValue();
double bottom = ((Double) stack.pop()).doubleValue();
stack.push(bottom - top);
change(node);
return false;
}
case DMUL: {
double top = ((Double) stack.pop()).doubleValue();
double bottom = ((Double) stack.pop()).doubleValue();
stack.push(bottom * top);
change(node);
return false;
}
case DDIV: {
double top = ((Double) stack.pop()).doubleValue();
double bottom = ((Double) stack.pop()).doubleValue();
stack.push(bottom / top);
change(node);
return false;
}
case DREM: {
double top = ((Double) stack.pop()).doubleValue();
double bottom = ((Double) stack.pop()).doubleValue();
stack.push(bottom % top);
change(node);
return false;
}
case DNEG: {
double top = ((Double) stack.pop()).doubleValue();
stack.push(-top);
change(node);
return false;
}
// float
case FADD: {
float top = ((Float) stack.pop()).floatValue();
float bottom = ((Float) stack.pop()).floatValue();
stack.push(bottom + top);
change(node);
return false;
}
case FSUB: {
float top = ((Float) stack.pop()).floatValue();
float bottom = ((Float) stack.pop()).floatValue();
stack.push(bottom - top);
change(node);
return false;
}
case FMUL: {
float top = ((Float) stack.pop()).floatValue();
float bottom = ((Float) stack.pop()).floatValue();
stack.push(bottom * top);
change(node);
return false;
}
case FDIV: {
float top = ((Float) stack.pop()).floatValue();
float bottom = ((Float) stack.pop()).floatValue();
stack.push(bottom / top);
change(node);
return false;
}
case FREM: {
float top = ((Float) stack.pop()).floatValue();
float bottom = ((Float) stack.pop()).floatValue();
stack.push(bottom % top);
change(node);
return false;
}
case FNEG: {
float top = ((Float) stack.pop()).floatValue();
stack.push(-top);
change(node);
return false;
}
// dup
case DUP: {
Number num = stack.pop();
if (num instanceof Double || num instanceof Long) {
error(node);
return true;
}
stack.push(num);
stack.push(num);
change(node);
return false;
}
case DUP_X1: {
Number top = stack.pop();
Number bottom = stack.pop();
if (bottom instanceof Double || bottom instanceof Long || top instanceof Double
|| top instanceof Long) {
error(node);
return true;
}
stack.push(top);
stack.push(bottom);
stack.push(top);
change(node);
return false;
}
case DUP_X2: {
Number top = stack.pop();
Number bottom = stack.pop();
if (top instanceof Double || top instanceof Long) {
error(node);
return true;
}
Number bottom2 = null;
if (!(bottom instanceof Double || bottom instanceof Long)) {
bottom2 = stack.pop();
if (bottom instanceof Double || bottom instanceof Long) {
error(node);
return true;
}
}
stack.push(top);
if (bottom2 != null)
stack.push(bottom2);
stack.push(bottom);
stack.push(top);
change(node);
return false;
}
case DUP2: {
Number top = stack.pop();
Number bottom = null;
if (!(top instanceof Double || top instanceof Long)) {
bottom = stack.pop();
if (bottom instanceof Double || bottom instanceof Long) {
error(node);
return true;
}
}
if (bottom != null)
stack.push(bottom);
stack.push(top);
if (bottom != null)
stack.push(bottom);
stack.push(top);
change(node);
return false;
}
case DUP2_X1: {
Number top = stack.pop();
Number bottom = null;
if (!(top instanceof Double || top instanceof Long)) {
bottom = stack.pop();
if (bottom instanceof Double || bottom instanceof Long) {
error(node);
return true;
}
}
Number bottom2 = stack.pop();
if (bottom != null)
stack.push(bottom);
stack.push(top);
stack.push(bottom2);
if (bottom != null)
stack.push(bottom);
stack.push(top);
change(node);
return false;
}
case DUP2_X2: {
Number top = stack.pop();
Number bottom = null;
if (!(top instanceof Double || top instanceof Long)) {
bottom = stack.pop();
if (bottom instanceof Double || bottom instanceof Long) {
error(node);
return true;
}
}
Number top2 = stack.pop();
Number bottom2 = null;
if (!(top2 instanceof Double || top2 instanceof Long)) {
bottom2 = stack.pop();
if (bottom2 instanceof Double || bottom2 instanceof Long) {
error(node);
return true;
}
}
if (bottom != null)
stack.push(bottom);
stack.push(top);
if (bottom2 != null)
stack.push(bottom2);
stack.push(top2);
if (bottom != null)
stack.push(bottom);
stack.push(top);
change(node);
return false;
}
// stack
case POP:
stack.pop();
change(node);
return false;
case POP2:
stack.pop();
stack.pop();
change(node);
return false;
case SWAP: {
Number top = stack.pop();
Number bottom = stack.pop();
if (bottom instanceof Double || bottom instanceof Long || top instanceof Double
|| top instanceof Long) {
error(node);
return true;
}
stack.push(top);
stack.push(bottom);
change(node);
return false;
}
default:
error(node);
return true;
}
} catch (ClassCastException | EmptyStackException ex) {
error(node);
return true;
}
return true;
}
private void error(AbstractInsnNode node) {
change(node.getPrevious());
stack.clear();
start = null;
}
private void change(AbstractInsnNode node) {
if (start == null)
return;
int idx = insns.indexOf(start);
startIdx = idx;
int idx2 = insns.indexOf(node);
if (idx != idx2) {
for (int i = 0; i <= idx2 - idx; i++) {
insns.remove(insns.get(idx));
}
while (!stack.isEmpty()) {
addNode(idx, stack.pop());
}
}
stack.clear();
start = null;
}
private void addNode(int idx, Number n) {
if (n instanceof Double) {
double val = n.doubleValue();
if (val == 0.0)
insns.insertBefore(insns.get(idx), new InsnNode(DCONST_0));
else if (val == 1.0)
insns.insertBefore(insns.get(idx), new InsnNode(DCONST_1));
else
insns.insertBefore(insns.get(idx), new LdcInsnNode(val));
} else if (n instanceof Float) {
float val = n.floatValue();
if (val == 0.0f)
insns.insertBefore(insns.get(idx), new InsnNode(FCONST_0));
else if (val == 1.0f)
insns.insertBefore(insns.get(idx), new InsnNode(FCONST_1));
else if (val == 2.0f)
insns.insertBefore(insns.get(idx), new InsnNode(FCONST_2));
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
insns.insertBefore(insns.get(idx), new LdcInsnNode(val));
} else if (n instanceof Integer) {
int val = n.intValue();
if (val == 0)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_0));
else if (val == 1)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_1));
else if (val == 2)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_2));
else if (val == 3)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_3));
else if (val == 4)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_4));
else if (val == 5)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_5));
else if (val == -1)
insns.insertBefore(insns.get(idx), new InsnNode(ICONST_M1));
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 {
throw new RuntimeException("Unexpected number: " + n);
}
}
}
+9 -1
View File
@@ -16,8 +16,16 @@ public class StringContext {
arrs = new String[amt]; arrs = new String[amt];
} }
public void next(String s) { public int next(String s) {
int i = idx;
len += (arrs[idx++] = s).length(); len += (arrs[idx++] = s).length();
return i;
}
public void set(int i, String s) {
if (arrs[i] != null)
len -= arrs[i].length();
len += (arrs[i] = s).length();
} }
public String finish() { public String finish() {
-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;
}
}
File diff suppressed because it is too large. Load diff
+16 -16
View File
@@ -6,6 +6,8 @@ import java.util.HashSet;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import me.ByteEdit.edit.HugeStrings;
public class ClassUtil { public class ClassUtil {
public static final int ACC_PUBLIC = 0x0001; public static final int ACC_PUBLIC = 0x0001;
@@ -318,11 +320,9 @@ public class ClassUtil {
s += "enum "; s += "enum ";
acc ^= ACC_ENUM; acc ^= ACC_ENUM;
} }
if (acc == access) { if (acc == access)
return s; return s;
} else { return "0x" + Integer.toHexString(access) + " ";
return "0x" + Integer.toHexString(access) + " ";
}
} }
/** /**
@@ -525,28 +525,28 @@ public class ClassUtil {
public static final HashMap<Class, DecompilationInterface> diMap = new HashMap<>(); public static final HashMap<Class, DecompilationInterface> diMap = new HashMap<>();
static { static {
diMap.put(String.class, (o, desc, escapeSpaces) -> "\"" diMap.put(String.class, (o, desc, escapeSpaces, hs) -> "\""
+ (escapeSpaces ? UnicodeUtils.escapeWithSpaces((String) o) : UnicodeUtils.escape((String) o)) + "\""); + (escapeSpaces ? UnicodeUtils.escapeWithSpaces(hs, (String) o) : UnicodeUtils.escape(hs, (String) o))
+ "\"");
diMap.put(Integer.class, diMap.put(Integer.class,
(o, desc, escape) -> desc.equals("Z") ? (((int) o) == 0 ? "false" : "true") : o.toString()); (o, desc, escape, hs) -> desc.equals("Z") ? (((int) o) == 0 ? "false" : "true") : o.toString());
diMap.put(Long.class, (o, desc, escape) -> o.toString() + "l"); diMap.put(Long.class, (o, desc, escape, hs) -> o.toString() + "l");
diMap.put(Float.class, (o, desc, escape) -> o.toString() + "f"); diMap.put(Float.class, (o, desc, escape, hs) -> o.toString() + "f");
} }
static interface DecompilationInterface { static interface DecompilationInterface {
String apply(Object o, String desc, boolean escape); String apply(Object o, String desc, boolean escape, HugeStrings hs);
} }
public static String getDecompiledValue(Object o, String desc, boolean escapeSpaces) { public static String getDecompiledValue(Object o, String desc, boolean escapeSpaces, HugeStrings hs) {
DecompilationInterface di = diMap.get(o.getClass()); DecompilationInterface di = diMap.get(o.getClass());
if (di != null) if (di != null)
return di.apply(o, desc, escapeSpaces); return di.apply(o, desc, escapeSpaces, hs);
else return o.toString();
return o.toString();
} }
public static String getDecompiledValue(Object o, String desc) { public static String getDecompiledValue(Object o, String desc, HugeStrings hs) {
return getDecompiledValue(o, desc, false); return getDecompiledValue(o, desc, false, hs);
} }
public static Object getCastedValue(String o, String className) { public static Object getCastedValue(String o, String className) {
+42 -9
View File
@@ -1,11 +1,26 @@
package me.ByteEdit.utils; package me.ByteEdit.utils;
import me.ByteEdit.edit.HugeStrings;
import me.ByteEdit.edit.HugeStringsRev;
public class UnicodeUtils { public class UnicodeUtils {
public static String unescape(String s) { public static String unescape(HugeStringsRev hsr, String s) {
return unescape(hsr, s, false);
}
public static String unescape(HugeStringsRev hsr, String s, boolean force) {
if (s == null) { if (s == null) {
return null; return null;
} }
if (!force && !s.isEmpty() && s.charAt(0) == '#' && s.length() < 12) {
try {
int id = Integer.parseInt(s.substring(1));
return hsr.get(id);
} catch (NumberFormatException ex) {
// Doesn't seem like a HugeString
}
}
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
char unicodeBuffer = 0; char unicodeBuffer = 0;
int stage = 0; int stage = 0;
@@ -69,19 +84,20 @@ public class UnicodeUtils {
sb.append(c); sb.append(c);
} }
if (stage != 0) { if (stage != 0) {
if (unicodeBuffer != 0) { if (unicodeBuffer != 0)
throw new IllegalArgumentException("The unicode at the end is incomplete."); 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(); return sb.toString();
} }
public static String escapeWithSpaces(String s) { public static String escapeWithSpaces(HugeStrings hs, String s) {
if (s == null) { if (s == null) {
return null; return null;
} }
if (s.length() > HugeStrings.THRESHOLD) {
return hs.onString(s);
}
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
char[] arr = s.toCharArray(); char[] arr = s.toCharArray();
int i = 0; int i = 0;
@@ -92,6 +108,9 @@ public class UnicodeUtils {
if (arr[0] == '/') { if (arr[0] == '/') {
sb.append("\\u002F"); sb.append("\\u002F");
i++; i++;
} else if (arr[0] == '#') {
sb.append("\\u0023");
i++;
} }
if (arr[len - 1] == '/') { if (arr[len - 1] == '/') {
addSlash = true; addSlash = true;
@@ -158,12 +177,26 @@ public class UnicodeUtils {
return sb.toString(); return sb.toString();
} }
public static String escape(String s) { public static String escape(HugeStrings hs, String s) {
return escape(hs, s, false);
}
public static String escape(HugeStrings hs, String s, boolean force) {
if (s == null) { if (s == null) {
return null; return null;
} }
StringBuilder sb = new StringBuilder(); if (!force && s.length() > HugeStrings.THRESHOLD) {
for (char c : s.toCharArray()) { return hs.onString(s);
}
char[] arr = s.toCharArray();
StringBuilder sb = new StringBuilder(arr.length);
int i = 0;
if (arr.length > 0 && arr[0] == '#') {
sb.append("\\u0023");
i++;
}
for (; i < arr.length; i++) {
char c = arr[i];
switch (c) { switch (c) {
case 'Ä': case 'Ä':
case 'ä': case 'ä':
+1 -1
View File
@@ -12,7 +12,7 @@
<baseFont size="12"/> <baseFont size="12"/>
<!-- General editor colors. --> <!-- General editor colors. -->
<background color="ffffff"/> <background color="c0c0c0"/>
<caret color="000000"/> <caret color="000000"/>
<selection bg="dddddd" fg="000000"/> <selection bg="dddddd" fg="000000"/>
<currentLineHighlight color="dddddd" fade="false"/> <currentLineHighlight color="dddddd" fade="false"/>
+13 -13
View File
@@ -12,10 +12,10 @@
<baseFont size="12"/> <baseFont size="12"/>
<!-- General editor colors. --> <!-- General editor colors. -->
<background color="000000"/> <background color="2f2f2f"/>
<caret color="eeeeee"/> <caret color="eeeeee"/>
<selection bg="222222" fg="eeeeee"/> <selection bg="555555" fg="eeeeee"/>
<currentLineHighlight color="222222" fade="false"/> <currentLineHighlight color="111111" fade="false"/>
<marginLine fg="4f4b46"/> <marginLine fg="4f4b46"/>
<markAllHighlight color="947e76"/> <!-- TODO: Fix me --> <markAllHighlight color="947e76"/> <!-- TODO: Fix me -->
<markOccurrencesHighlight color="2b2b2b" border="false"/> <markOccurrencesHighlight color="2b2b2b" border="false"/>
@@ -36,23 +36,23 @@
<!-- Syntax tokens. --> <!-- Syntax tokens. -->
<tokenStyles> <tokenStyles>
<style token="IDENTIFIER" fg="eeeeee"/> <style token="IDENTIFIER" fg="eeeeee"/>
<style token="RESERVED_WORD" fg="dd02d6" bold="true"/> <style token="RESERVED_WORD" fg="cc6c1d" bold="true"/>
<style token="RESERVED_WORD_2" fg="dd02d6" bold="true"/> <style token="RESERVED_WORD_2" fg="cc6c1d" bold="true"/>
<style token="ANNOTATION" fg="808080"/> <style token="ANNOTATION" fg="808080"/>
<style token="COMMENT_DOCUMENTATION" fg="279627"/> <style token="COMMENT_DOCUMENTATION" fg="279627"/>
<style token="COMMENT_EOL" fg="279627"/> <style token="COMMENT_EOL" fg="279627"/>
<style token="COMMENT_MULTILINE" fg="279627"/> <style token="COMMENT_MULTILINE" fg="279627"/>
<style token="COMMENT_KEYWORD" fg="806040" bold="true"/> <style token="COMMENT_KEYWORD" fg="806040" bold="true"/>
<style token="COMMENT_MARKUP" fg="808060"/> <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="FUNCTION" fg="eeeeee"/>
<style token="LITERAL_BOOLEAN" fg="dd02d6" bold="true"/> <style token="LITERAL_BOOLEAN" fg="cc6c1d"/>
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="eeeeee"/> <style token="LITERAL_NUMBER_DECIMAL_INT" fg="6897bb"/>
<style token="LITERAL_NUMBER_FLOAT" fg="eeeeee"/> <style token="LITERAL_NUMBER_FLOAT" fg="6897bb"/>
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="eeeeee"/> <style token="LITERAL_NUMBER_HEXADECIMAL" fg="6897bb"/>
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="00aeff"/> <style token="LITERAL_STRING_DOUBLE_QUOTE" fg="17c6a3"/>
<style token="LITERAL_CHAR" fg="00aeff"/> <style token="LITERAL_CHAR" fg="17c6a3"/>
<style token="LITERAL_BACKQUOTE" fg="00aeff"/> <style token="LITERAL_BACKQUOTE" fg="17c6a3"/>
<style token="MARKUP_TAG_DELIMITER" fg="008080"/> <style token="MARKUP_TAG_DELIMITER" fg="008080"/>
<style token="MARKUP_TAG_NAME" fg="3f7f7f"/> <style token="MARKUP_TAG_NAME" fg="3f7f7f"/>
<style token="MARKUP_TAG_ATTRIBUTE" fg="7f007f"/> <style token="MARKUP_TAG_ATTRIBUTE" fg="7f007f"/>
+38 -46
View File
@@ -20,7 +20,7 @@ import javax.swing.text.Segment;
* @version 1.0 * @version 1.0
*/ */
public class DocumentReader extends Reader { public class DocumentReader extends Reader {
/** /**
* The stream's position in the document. * The stream's position in the document.
*/ */
@@ -37,12 +37,11 @@ public class DocumentReader extends Reader {
* Used for fast character access. * Used for fast character access.
*/ */
private Segment segment; private Segment segment;
/** /**
* Constructor. * Constructor.
* *
* @param document * @param document The document we're 'reading'.
* The document we're 'reading'.
*/ */
public DocumentReader(Document document) { public DocumentReader(Document document) {
position = 0; position = 0;
@@ -50,34 +49,34 @@ public class DocumentReader extends Reader {
this.document = document; this.document = document;
this.segment = new Segment(); this.segment = new Segment();
} }
/** /**
* This currently does nothing... * This currently does nothing...
*/ */
@Override @Override
public void close() {} public void close() {
}
/** /**
* Marks the present position in the stream. Subsequent calls to * Marks the present position in the stream. Subsequent calls to
* <code>reset()</code> will reposition the stream to this point. * <code>reset()</code> will reposition the stream to this point.
* *
* @param readAheadLimit * @param readAheadLimit Ignored.
* Ignored.
*/ */
@Override @Override
public void mark(int readAheadLimit) { public void mark(int readAheadLimit) {
mark = position; mark = position;
} }
/** /**
* Tells whether this reader supports the <code>mark</code> operation. * Tells whether this reader supports the <code>mark</code> operation. This
* This always returns <code>true</code> for <code>DocumentReader</code>. * always returns <code>true</code> for <code>DocumentReader</code>.
*/ */
@Override @Override
public boolean markSupported() { public boolean markSupported() {
return true; return true;
} }
/** /**
* Reads the single character at the current position in the document. * Reads the single character at the current position in the document.
*/ */
@@ -96,31 +95,27 @@ public class DocumentReader extends Reader {
return -1; return -1;
} }
} }
/** /**
* Read <code>array.length</code> characters from the beginning * Read <code>array.length</code> characters from the beginning of the document
* of the document into <code>array</code>. * into <code>array</code>.
* *
* @param array * @param array The array to read characters into.
* The array to read characters into.
* @return The number of characters read. * @return The number of characters read.
*/ */
@Override @Override
public int read(char[] array) { public int read(char[] array) {
return read(array, 0, array.length); return read(array, 0, array.length);
} }
/** /**
* Reads characters into a portion of an array. * Reads characters into a portion of an array.
* *
* @param cbuf * @param cbuf The destination buffer.
* The destination buffer. * @param off Offset at which to start storing characters.
* @param off * @param len Maximum number of characters to read.
* Offset at which to start storing characters. * @return The number of characters read, or <code>-1</code> if the end of the
* @param len * stream (document) has been reached.
* 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 @Override
public int read(char[] cbuf, int off, int len) { public int read(char[] cbuf, int off, int len) {
@@ -145,24 +140,23 @@ public class DocumentReader extends Reader {
return -1; return -1;
} }
} }
/** /**
* Tells whether this reader is ready to be read without * Tells whether this reader is ready to be read without blocking for input.
* blocking for input. <code>DocumentReader</code> will * <code>DocumentReader</code> will always return true.
* always return true.
* *
* @return <code>true</code> if the next read operation will * @return <code>true</code> if the next read operation will return without
* return without blocking. * blocking.
*/ */
@Override @Override
public boolean ready() { public boolean ready() {
return true; return true;
} }
/** /**
* Resets the stream. If the stream has been marked, then attempt to * Resets the stream. If the stream has been marked, then attempt to reposition
* reposition it at the mark. If the stream has not been marked, then * it at the mark. If the stream has not been marked, then move it to the
* move it to the beginning of the document. * beginning of the document.
*/ */
@Override @Override
public void reset() { public void reset() {
@@ -173,12 +167,11 @@ public class DocumentReader extends Reader {
mark = -1; mark = -1;
} }
} }
/** /**
* Skips characters. This will not 'skip' past the end of the document. * Skips characters. This will not 'skip' past the end of the document.
* *
* @param n * @param n The number of characters to skip.
* The number of characters to skip.
* @return The number of characters actually skipped. * @return The number of characters actually skipped.
*/ */
@Override @Override
@@ -191,14 +184,13 @@ public class DocumentReader extends Reader {
position = document.getLength(); position = document.getLength();
return document.getLength() - temp; return document.getLength() - temp;
} }
/** /**
* Move to the specified position in the document. If <code>pos</code> * Move to the specified position in the document. If <code>pos</code> is
* is greater than the document's length, the stream's position is moved * greater than the document's length, the stream's position is moved to the end
* to the end of the document. * of the document.
* *
* @param pos * @param pos The position in the document to move to.
* The position in the document to move to.
*/ */
public void seek(long pos) { public void seek(long pos) {
position = Math.min(pos, document.getLength()); position = Math.min(pos, document.getLength());
+47 -73
View File
@@ -28,12 +28,11 @@ import java.io.Writer;
* @version 0.7 * @version 0.7
*/ */
public class UnicodeWriter extends Writer { public class UnicodeWriter extends Writer {
/** /**
* If this system property evaluates to "<code>false</code>", ignoring * If this system property evaluates to "<code>false</code>", ignoring case,
* case, files written out as UTF-8 will not have a BOM written for them. * files written out as UTF-8 will not have a BOM written for them. Otherwise
* Otherwise (even if the property is not set), UTF-8 files will have a * (even if the property is not set), UTF-8 files will have a BOM written.
* BOM written.
*/ */
public static final String PROPERTY_WRITE_UTF8_BOM = "UnicodeWriter.writeUtf8BOM"; 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[] 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[] 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 }; 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 * This is a utility constructor since the vast majority of the time, this class
* class will be used to write Unicode files. * will be used to write Unicode files.
* *
* @param fileName * @param fileName The file to which to write the Unicode output.
* The file to which to write the Unicode output. * @param encoding The encoding to use.
* @param encoding * @throws IOException If an IO exception occurs.
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
*/ */
public UnicodeWriter(String fileName, String encoding) throws IOException { public UnicodeWriter(String fileName, String encoding) throws IOException {
this(new FileOutputStream(fileName), encoding); this(new FileOutputStream(fileName), encoding);
} }
/** /**
* This is a utility constructor since the vast majority of the time, this * This is a utility constructor since the vast majority of the time, this class
* class will be used to write Unicode files. * will be used to write Unicode files.
* *
* @param file * @param file The file to which to write the Unicode output.
* The file to which to write the Unicode output. * @param encoding The encoding to use.
* @param encoding * @throws IOException If an IO exception occurs.
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
*/ */
public UnicodeWriter(File file, String encoding) throws IOException { public UnicodeWriter(File file, String encoding) throws IOException {
this(new FileOutputStream(file), encoding); this(new FileOutputStream(file), encoding);
} }
/** /**
* Creates a new writer. * Creates a new writer.
* *
* @param out * @param out The output stream to write.
* The output stream to write. * @param encoding The encoding to use.
* @param encoding * @throws IOException If an IO exception occurs.
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
*/ */
public UnicodeWriter(OutputStream out, String encoding) throws IOException { public UnicodeWriter(OutputStream out, String encoding) throws IOException {
init(out, encoding); init(out, encoding);
} }
/** /**
* Closes this writer. * Closes this writer.
* *
* @throws IOException * @throws IOException If an IO exception occurs.
* If an IO exception occurs.
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
internalOut.close(); internalOut.close();
} }
/** /**
* Flushes the stream. * Flushes the stream.
* *
* @throws IOException * @throws IOException If an IO exception occurs.
* If an IO exception occurs.
*/ */
@Override @Override
public void flush() throws IOException { public void flush() throws IOException {
internalOut.flush(); internalOut.flush();
} }
/** /**
* Returns the encoding being used to write this output stream (i.e., the * Returns the encoding being used to write this output stream (i.e., the
* encoding of the file). * encoding of the file).
@@ -121,7 +109,7 @@ public class UnicodeWriter extends Writer {
public String getEncoding() { public String getEncoding() {
return internalOut.getEncoding(); return internalOut.getEncoding();
} }
/** /**
* Returns whether UTF-8 files should have a BOM in them when written. * Returns whether UTF-8 files should have a BOM in them when written.
* *
@@ -137,17 +125,14 @@ public class UnicodeWriter extends Writer {
} }
return true; return true;
} }
/** /**
* Initializes the internal output stream and writes the BOM if the * Initializes the internal output stream and writes the BOM if the specified
* specified encoding is a Unicode encoding. * encoding is a Unicode encoding.
* *
* @param out * @param out The output stream we are writing.
* The output stream we are writing. * @param encoding The encoding in which to write.
* @param encoding * @throws IOException If an I/O error occurs while writing a BOM.
* 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 { private void init(OutputStream out, String encoding) throws IOException {
internalOut = new OutputStreamWriter(out, encoding); internalOut = new OutputStreamWriter(out, encoding);
@@ -169,60 +154,49 @@ public class UnicodeWriter extends Writer {
out.write(UTF32BE_BOM, 0, UTF32BE_BOM.length); out.write(UTF32BE_BOM, 0, UTF32BE_BOM.length);
} }
} }
/** /**
* Sets whether UTF-8 files should have a BOM written in them. * Sets whether UTF-8 files should have a BOM written in them.
* *
* @param write * @param write Whether to write a BOM.
* Whether to write a BOM.
* @see #getWriteUtf8BOM() * @see #getWriteUtf8BOM()
* @see UnicodeWriter * @see UnicodeWriter
*/ */
public static void setWriteUtf8BOM(boolean write) { public static void setWriteUtf8BOM(boolean write) {
System.setProperty(UnicodeWriter.PROPERTY_WRITE_UTF8_BOM, Boolean.toString(write)); System.setProperty(UnicodeWriter.PROPERTY_WRITE_UTF8_BOM, Boolean.toString(write));
} }
/** /**
* Writes a portion of an array of characters. * Writes a portion of an array of characters.
* *
* @param cbuf * @param cbuf The buffer of characters.
* The buffer of characters. * @param off The offset from which to start writing characters.
* @param off * @param len The number of characters to write.
* The offset from which to start writing characters. * @throws IOException If an I/O error occurs.
* @param len
* The number of characters to write.
* @throws IOException
* If an I/O error occurs.
*/ */
@Override @Override
public void write(char[] cbuf, int off, int len) throws IOException { public void write(char[] cbuf, int off, int len) throws IOException {
internalOut.write(cbuf, off, len); internalOut.write(cbuf, off, len);
} }
/** /**
* Writes a single character. * Writes a single character.
* *
* @param c * @param c An integer specifying the character to write.
* An integer specifying the character to write. * @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/ */
@Override @Override
public void write(int c) throws IOException { public void write(int c) throws IOException {
internalOut.write(c); internalOut.write(c);
} }
/** /**
* Writes a portion of a string. * Writes a portion of a string.
* *
* @param str * @param str The string from which to write.
* The string from which to write. * @param off The offset from which to start writing characters.
* @param off * @param len The number of characters to write.
* The offset from which to start writing characters. * @throws IOException If an IO error occurs.
* @param len
* The number of characters to write.
* @throws IOException
* If an IO error occurs.
*/ */
@Override @Override
public void write(String str, int off, int len) throws IOException { 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 * @version 1.0
*/ */
public abstract class RPrintUtilities { public abstract class RPrintUtilities {
private static int currentDocLineNumber; // The line number in the document private static int currentDocLineNumber; // The line number in the document
// we are currently on. // we are currently on.
private static int numDocLines; // The number of lines in the current 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. * The metrics of the font currently being used to print.
*/ */
private static FontMetrics fm; private static FontMetrics fm;
/** /**
* Returns the position closest to, but before, position * Returns the position closest to, but before, position
* <code>maxCharsPerLine</code> in * <code>maxCharsPerLine</code> in <code>line</code> of one of the chars in
* <code>line</code> of one of the chars in <code>breakChars</code>, or * <code>breakChars</code>, or simply returns <code>maxCharsPerLine-1</code> if
* simply returns * none of the <code>breakChars</code> comes before that position. This position
* <code>maxCharsPerLine-1</code> if none of the <code>breakChars</code> * represents the logical line break for this <code>java.lang.String</code> if
* comes before * it is being printed in a monospaced font when lines can only be
* that position. This position represents the logical line break for this * <code>maxCharsPerLine</code> characters long.
* <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 * @param line The text being printed.
* The text being printed. * @param maxCharsPerLine Only up-to this many characters from <code>line</code>
* @param maxCharsPerLine * can be printed on one line.
* Only up-to this many characters from * @return The logical position at which to stop printing <code>line</code> to
* <code>line</code> can be printed on one line. * simulate word wrap.
* @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) { private static int getLineBreakPoint(String line, final int maxCharsPerLine) {
int breakPoint = -1; int breakPoint = -1;
@@ -84,30 +78,23 @@ public abstract class RPrintUtilities {
} }
return (breakPoint == -1 ? maxCharsPerLine - 1 : breakPoint); return (breakPoint == -1 ? maxCharsPerLine - 1 : breakPoint);
} }
/** /**
* Prints a <code>Document</code> using a monospaced font, and does no word * Prints a <code>Document</code> using a monospaced font, and does no word
* wrapping (ie, * wrapping (ie, words will wrap mid-word to the next line). This method is
* words will wrap mid-word to the next line). This method is expected to be * expected to be called from Printable 'print(Graphics g)' functions.
* called from
* Printable 'print(Graphics g)' functions.
* *
* @param g * @param g The graphics context to write to.
* The graphics context to write to. * @param doc The <code>javax.swing.text.Document</code> to print.
* @param doc * @param fontSize the point size to use for the monospaced font.
* The <code>javax.swing.text.Document</code> to print. * @param pageIndex The page number to print.
* @param fontSize * @param pageFormat The format to print the page with.
* the point size to use for the monospaced font. * @param tabSize The number of spaces to expand tabs to.
* @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 * @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.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, fontSize)); g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
// Initialize our static variables (these are used by our tab expander // Initialize our static variables (these are used by our tab expander
@@ -202,29 +189,23 @@ public abstract class RPrintUtilities {
} }
return Printable.NO_SUCH_PAGE; return Printable.NO_SUCH_PAGE;
} }
/** /**
* Prints a <code>Document</code> using a monospaced font, word wrapping on * Prints a <code>Document</code> using a monospaced font, word wrapping on the
* the characters ' ', '\t', '\n', ',', '.', and ';'. This method is * characters ' ', '\t', '\n', ',', '.', and ';'. This method is expected to be
* expected to be called from Printable 'print(Graphics g)' functions. * called from Printable 'print(Graphics g)' functions.
* *
* @param g * @param g The graphics context to write to.
* The graphics context to write to. * @param doc The <code>javax.swing.text.Document</code> to print.
* @param doc * @param fontSize the point size to use for the monospaced font.
* The <code>javax.swing.text.Document</code> to print. * @param pageIndex The page number to print.
* @param fontSize * @param pageFormat The format to print the page with.
* the point size to use for the monospaced font. * @param tabSize The number of spaces to expand tabs to.
* @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 * @see #printDocumentMonospaced
*/ */
public static int printDocumentMonospacedWordWrap(Graphics g, Document doc, int fontSize, int pageIndex, PageFormat pageFormat, public static int printDocumentMonospacedWordWrap(Graphics g, Document doc, int fontSize, int pageIndex,
int tabSize) { PageFormat pageFormat, int tabSize) {
g.setColor(Color.BLACK); g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, fontSize)); g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
// Initialize our static variables (these are used by our tab expander // Initialize our static variables (these are used by our tab expander
@@ -320,30 +301,24 @@ public abstract class RPrintUtilities {
} }
return Printable.NO_SUCH_PAGE; return Printable.NO_SUCH_PAGE;
} }
/** /**
* Prints a <code>Document</code> using the specified font, word wrapping * Prints a <code>Document</code> using the specified font, word wrapping on the
* on the characters ' ', '\t', '\n', ',', '.', and ';'. This method is * characters ' ', '\t', '\n', ',', '.', and ';'. This method is expected to be
* expected to be called from Printable 'print(Graphics g)' functions. * called from Printable 'print(Graphics g)' functions.
* *
* @param g * @param g The graphics context to write to.
* The graphics context to write to. * @param textComponent The <code>javax.swing.text.JTextComponent</code> whose
* @param textComponent * text you're printing.
* The <code>javax.swing.text.JTextComponent</code> * @param font The font to use for printing. If <code>null</code>, then
* whose text you're printing. * <code>textComponent</code>'s font is used.
* @param font * @param pageIndex The page number to print.
* The font to use for printing. If <code>null</code>, then * @param pageFormat The format to print the page with.
* <code>textComponent</code>'s font is used. * @param tabSize The number of spaces to convert tabs to.
* @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, public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent, Font font, int pageIndex,
int tabSize) { PageFormat pageFormat, int tabSize) {
// Initialize our graphics object. // Initialize our graphics object.
g.setColor(Color.BLACK); g.setColor(Color.BLACK);
g.setFont(font != null ? font : textComponent.getFont()); g.setFont(font != null ? font : textComponent.getFont());
@@ -389,7 +364,8 @@ public abstract class RPrintUtilities {
// we'd start after the part // we'd start after the part
// that has already been printed). // that has already been printed).
try { try {
doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset), currentLineSeg); doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset),
currentLineSeg);
} catch (BadLocationException ble) { } catch (BadLocationException ble) {
System.err.println("BadLocationException in print (where there shouldn't be one!): " + ble); System.err.println("BadLocationException in print (where there shouldn't be one!): " + ble);
return Printable.NO_SUCH_PAGE; return Printable.NO_SUCH_PAGE;
@@ -452,7 +428,8 @@ public abstract class RPrintUtilities {
System.err.println(ble); System.err.println(ble);
return Printable.NO_SUCH_PAGE; return Printable.NO_SUCH_PAGE;
} }
currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander, 0); currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander,
0);
} while (currentLineLengthInPixels <= lineLengthInPixels); } while (currentLineLengthInPixels <= lineLengthInPixels);
currentPos--; currentPos--;
} }
@@ -460,8 +437,8 @@ public abstract class RPrintUtilities {
doc.getText((currentLineStart + startingOffset), currentPos, currentLineSeg); doc.getText((currentLineStart + startingOffset), currentPos, currentLineSeg);
} catch (BadLocationException ble) { } catch (BadLocationException ble) {
System.err.println("BadLocationException in print (a):"); System.err.println("BadLocationException in print (a):");
System.err.println("==> currentLineStart: " + currentLineStart + "; startingOffset: " + startingOffset System.err.println("==> currentLineStart: " + currentLineStart + "; startingOffset: "
+ "; currentPos: " + currentPos); + startingOffset + "; currentPos: " + currentPos);
System.err.println("==> Range: " + (currentLineStart + startingOffset) + " - " System.err.println("==> Range: " + (currentLineStart + startingOffset) + " - "
+ (currentLineStart + startingOffset + currentPos)); + (currentLineStart + startingOffset + currentPos));
ble.printStackTrace(); ble.printStackTrace();
@@ -491,12 +468,11 @@ public abstract class RPrintUtilities {
} }
return Printable.NO_SUCH_PAGE; return Printable.NO_SUCH_PAGE;
} }
/** /**
* Removes any spaces or tabs from the end of the segment. * Removes any spaces or tabs from the end of the segment.
* *
* @param segment * @param segment The segment from which to remove tailing whitespace.
* The segment from which to remove tailing whitespace.
* @return <code>segment</code> with trailing whitespace removed. * @return <code>segment</code> with trailing whitespace removed.
*/ */
private static Segment removeEndingWhitespace(Segment segment) { private static Segment removeEndingWhitespace(Segment segment) {
@@ -510,15 +486,16 @@ public abstract class RPrintUtilities {
String newStringVal = stringVal.substring(0, stringVal.length() - toTrim); String newStringVal = stringVal.substring(0, stringVal.length() - toTrim);
return new Segment(newStringVal.toCharArray(), 0, newStringVal.length()); return new Segment(newStringVal.toCharArray(), 0, newStringVal.length());
} }
/** /**
* A tab expander for the document currently being printed with the * A tab expander for the document currently being printed with the font being
* font being used for the printing. * used for the printing.
*/ */
private static class RPrintTabExpander implements TabExpander { private static class RPrintTabExpander implements TabExpander {
RPrintTabExpander() {} RPrintTabExpander() {
}
@Override @Override
public float nextTabStop(float x, int tabOffset) { public float nextTabStop(float x, int tabOffset) {
if (tabSizeInSpaces == 0) { if (tabSizeInSpaces == 0) {
@@ -17,17 +17,17 @@ import javax.swing.text.JTextComponent;
* lexicographically (ignoring case). * lexicographically (ignoring case).
* <p> * <p>
* *
* This implementation assumes the input text and replacement text are the * This implementation assumes the input text and replacement text are the same
* same value. It also returns the input text from its {@link #toString()} * value. It also returns the input text from its {@link #toString()} method
* method (which is what <code>DefaultListCellRenderer</code> uses to render * (which is what <code>DefaultListCellRenderer</code> uses to render objects).
* objects). Subclasses that wish to override any of this behavior can simply * Subclasses that wish to override any of this behavior can simply override the
* override the corresponding method(s) needed to do so. * corresponding method(s) needed to do so.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public abstract class AbstractCompletion implements Completion { public abstract class AbstractCompletion implements Completion {
/** /**
* The provider that created this completion; * The provider that created this completion;
*/ */
@@ -38,35 +38,32 @@ public abstract class AbstractCompletion implements Completion {
private Icon icon; private Icon icon;
/** /**
* The relevance of this completion. Completion instances with higher * The relevance of this completion. Completion instances with higher
* "relevance" values are inserted higher into the list of possible * "relevance" values are inserted higher into the list of possible completions
* completions than those with lower values. Completion instances with * than those with lower values. Completion instances with equal relevance
* equal relevance values are sorted alphabetically. * values are sorted alphabetically.
*/ */
private int relevance; private int relevance;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider that created this completion.
* The provider that created this completion.
*/ */
protected AbstractCompletion(CompletionProvider provider) { protected AbstractCompletion(CompletionProvider provider) {
this.provider = provider; this.provider = provider;
} }
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider that created this completion.
* The provider that created this completion. * @param icon The icon for this completion.
* @param icon
* The icon for this completion.
*/ */
protected AbstractCompletion(CompletionProvider provider, Icon icon) { protected AbstractCompletion(CompletionProvider provider, Icon icon) {
this(provider); this(provider);
setIcon(icon); setIcon(icon);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -79,7 +76,7 @@ public abstract class AbstractCompletion implements Completion {
} }
return -1; return -1;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -87,7 +84,7 @@ public abstract class AbstractCompletion implements Completion {
public String getAlreadyEntered(JTextComponent comp) { public String getAlreadyEntered(JTextComponent comp) {
return provider.getAlreadyEnteredText(comp); return provider.getAlreadyEnteredText(comp);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -95,10 +92,10 @@ public abstract class AbstractCompletion implements Completion {
public Icon getIcon() { public Icon getIcon() {
return icon; return icon;
} }
/** /**
* Returns the text the user has to (start) typing for this completion * Returns the text the user has to (start) typing for this completion to be
* to be offered. The default implementation simply returns * offered. The default implementation simply returns
* {@link #getReplacementText()}. * {@link #getReplacementText()}.
* *
* @return The text the user has to (start) typing for this completion. * @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() { public String getInputText() {
return getReplacementText(); return getReplacementText();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -116,7 +113,7 @@ public abstract class AbstractCompletion implements Completion {
public CompletionProvider getProvider() { public CompletionProvider getProvider() {
return provider; return provider;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -124,10 +121,10 @@ public abstract class AbstractCompletion implements Completion {
public int getRelevance() { public int getRelevance() {
return relevance; return relevance;
} }
/** /**
* The default implementation returns <code>null</code>. Subclasses * The default implementation returns <code>null</code>. Subclasses can override
* can override this method. * this method.
* *
* @return The tool tip text. * @return The tool tip text.
*/ */
@@ -135,29 +132,27 @@ public abstract class AbstractCompletion implements Completion {
public String getToolTipText() { public String getToolTipText() {
return null; return null;
} }
/** /**
* Sets the icon to use for this completion. * Sets the icon to use for this completion.
* *
* @param icon * @param icon The icon to use.
* The icon to use.
* @see #getIcon() * @see #getIcon()
*/ */
public void setIcon(Icon icon) { public void setIcon(Icon icon) {
this.icon = icon; this.icon = icon;
} }
/** /**
* Sets the relevance of this completion. * Sets the relevance of this completion.
* *
* @param relevance * @param relevance The new relevance of this completion.
* The new relevance of this completion.
* @see #getRelevance() * @see #getRelevance()
*/ */
public void setRelevance(int relevance) { public void setRelevance(int relevance) {
this.relevance = relevance; this.relevance = relevance;
} }
/** /**
* Returns a string representation of this completion. The default * Returns a string representation of this completion. The default
* implementation returns {@link #getInputText()}. * implementation returns {@link #getInputText()}.
@@ -43,14 +43,14 @@ import javax.swing.event.HyperlinkListener;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator; import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/** /**
* The optional "description" window that describes the currently selected * The optional "description" window that describes the currently selected item
* item in the auto-completion window. * in the auto-completion window.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescWindowCallback { class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescWindowCallback {
/** /**
* The parent AutoCompletion instance. * The parent AutoCompletion instance.
*/ */
@@ -89,9 +89,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
private int historyPos; private int historyPos;
/** /**
* Provides a slight delay between asking to set a description and actually * Provides a slight delay between asking to set a description and actually
* displaying it, so that if the user is scrolling quickly through * displaying it, so that if the user is scrolling quickly through completions,
* completions, those with slow-to-calculate summaries won't bog down the * those with slow-to-calculate summaries won't bog down the scrolling.
* scrolling.
*/ */
private Timer timer; private Timer timer;
/** /**
@@ -103,24 +102,21 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
*/ */
private ResourceBundle bundle; private ResourceBundle bundle;
/** /**
* The amount of time to wait after the user changes the selected * The amount of time to wait after the user changes the selected completion to
* completion to refresh the description. This delay is in place to help * refresh the description. This delay is in place to help performance for
* performance for {@link Completion}s that may be slow to compute their * {@link Completion}s that may be slow to compute their summary text.
* summary text.
*/ */
private static final int INITIAL_TIMER_DELAY = 120; private static final int INITIAL_TIMER_DELAY = 120;
/** /**
* The resource bundle name. * The resource bundle name.
*/ */
private static final String MSG = "org.fife.ui.autocomplete.AutoCompleteDescWindow"; private static final String MSG = "org.fife.ui.autocomplete.AutoCompleteDescWindow";
/** /**
* Constructor. * Constructor.
* *
* @param owner * @param owner The parent window.
* The parent window. * @param ac The parent auto-completion.
* @param ac
* The parent auto-completion.
*/ */
public AutoCompleteDescWindow(Window owner, AutoCompletion ac) { public AutoCompleteDescWindow(Window owner, AutoCompletion ac) {
super(owner); super(owner);
@@ -146,12 +142,12 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
descWindowNavBar.add(new JButton(forwardAction)); descWindowNavBar.add(new JButton(forwardAction));
bottomPanel = new JPanel(new BorderLayout()); bottomPanel = new JPanel(new BorderLayout());
b = new AbstractBorder() { b = new AbstractBorder() {
@Override @Override
public Insets getBorderInsets(Component c) { public Insets getBorderInsets(Component c) {
return new Insets(1, 0, 0, 0); return new Insets(1, 0, 0, 0);
} }
@Override @Override
public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
g.setColor(UIManager.getColor("controlDkShadow")); g.setColor(UIManager.getColor("controlDkShadow"));
@@ -179,19 +175,18 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
timer = new Timer(INITIAL_TIMER_DELAY, timerAction); timer = new Timer(INITIAL_TIMER_DELAY, timerAction);
timer.setRepeats(false); timer.setRepeats(false);
} }
/** /**
* Sets the currently displayed description and updates the history. * Sets the currently displayed description and updates the history.
* *
* @param historyItem * @param historyItem The item to add to the history.
* The item to add to the history.
*/ */
private void addToHistory(HistoryEntry historyItem) { private void addToHistory(HistoryEntry historyItem) {
history.add(++historyPos, historyItem); history.add(++historyPos, historyItem);
clearHistoryAfterCurrentPos(); clearHistoryAfterCurrentPos();
setActionStates(); setActionStates();
} }
/** /**
* Clears the history of viewed descriptions. * Clears the history of viewed descriptions.
*/ */
@@ -202,7 +197,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
setActionStates(); setActionStates();
} }
} }
/** /**
* Makes the current history page the last one in the history. * Makes the current history page the last one in the history.
*/ */
@@ -212,10 +207,10 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
} }
setActionStates(); setActionStates();
} }
/** /**
* Copies from the description text area, if it is visible and there is * Copies from the description text area, if it is visible and there is a
* a selection. * selection.
* *
* @return Whether a copy occurred. * @return Whether a copy occurred.
*/ */
@@ -226,12 +221,11 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
} }
return false; return false;
} }
/** /**
* Returns the localized message for the specified key. * Returns the localized message for the specified key.
* *
* @param key * @param key The key.
* The key.
* @return The localized message. * @return The localized message.
*/ */
private String getString(String key) { private String getString(String key) {
@@ -240,12 +234,11 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
} }
return bundle.getString(key); return bundle.getString(key);
} }
/** /**
* Called when a hyperlink is clicked. * Called when a hyperlink is clicked.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void hyperlinkUpdate(HyperlinkEvent e) { public void hyperlinkUpdate(HyperlinkEvent e) {
@@ -261,7 +254,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
URL newUrl = redirector.possiblyRedirect(url); URL newUrl = redirector.possiblyRedirect(url);
if (newUrl != null && newUrl != url) { if (newUrl != null && newUrl != url) {
url = newUrl; 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. * 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); forwardAction.putValue(Action.SHORT_DESCRIPTION, desc);
} }
/** /**
* Sets the description displayed in this window. This clears the * Sets the description displayed in this window. This clears the history.
* history.
* *
* @param item * @param item The item whose description you want to display.
* The item whose description you want to display.
*/ */
public void setDescriptionFor(Completion item) { public void setDescriptionFor(Completion item) {
setDescriptionFor(item, false); setDescriptionFor(item, false);
} }
/** /**
* Sets the description displayed in this window. * Sets the description displayed in this window.
* *
* @param item * @param item The item whose description you want to display.
* The item whose description you want to display. * @param addToHistory Whether to add this page to the page history (as opposed
* @param addToHistory * to clearing it and starting anew).
* Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
*/ */
protected void setDescriptionFor(Completion item, boolean addToHistory) { protected void setDescriptionFor(Completion item, boolean addToHistory) {
setDescriptionFor(item, null, addToHistory); setDescriptionFor(item, null, addToHistory);
} }
/** /**
* Sets the description displayed in this window. * Sets the description displayed in this window.
* *
* @param item * @param item The item whose description you want to display.
* The item whose description you want to display.
* @parma anchor The anchor to jump to, or <code>null</code> if none. * @parma anchor The anchor to jump to, or <code>null</code> if none.
* @param addToHistory * @param addToHistory Whether to add this page to the page history (as opposed
* Whether to add this page to the page history * to clearing it and starting anew).
* (as opposed to clearing it and starting anew).
*/ */
protected void setDescriptionFor(Completion item, String anchor, boolean addToHistory) { protected void setDescriptionFor(Completion item, String anchor, boolean addToHistory) {
timer.stop(); timer.stop();
timerAction.setCompletion(item, anchor, addToHistory); timerAction.setCompletion(item, anchor, addToHistory);
timer.start(); timer.start();
} }
private void setDisplayedDesc(Completion completion, final String anchor, boolean addToHistory) { private void setDisplayedDesc(Completion completion, final String anchor, boolean addToHistory) {
String desc = completion == null ? null : completion.getSummary(); String desc = completion == null ? null : completion.getSummary();
if (desc == null) { if (desc == null) {
@@ -372,7 +360,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
descArea.setText(desc); descArea.setText(desc);
if (anchor != null) { if (anchor != null) {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
descArea.scrollToReference(anchor); descArea.scrollToReference(anchor);
@@ -388,7 +376,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
} }
addToHistory(new HistoryEntry(completion, desc, null)); addToHistory(new HistoryEntry(completion, desc, null));
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -399,21 +387,19 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
} }
super.setVisible(visible); super.setVisible(visible);
} }
/** /**
* Callback for custom <code>ExternalURLHandler</code>s. * Callback for custom <code>ExternalURLHandler</code>s.
* *
* @param completion * @param completion The completion to display.
* The completion to display. * @param anchor The anchor in the HTML to jump to, or <code>null</code> if
* @param anchor * none.
* The anchor in the HTML to jump to, or <code>null</code>
* if none.
*/ */
@Override @Override
public void showSummaryFor(Completion completion, String anchor) { public void showSummaryFor(Completion completion, String anchor) {
setDescriptionFor(completion, anchor, true); setDescriptionFor(completion, anchor, true);
} }
/** /**
* Called by the parent completion popup window the LookAndFeel is updated. * 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()); scrollPane.getViewport().setBackground(descArea.getBackground());
((JPanel) getContentPane()).setBorder(TipUtil.getToolTipBorder()); ((JPanel) getContentPane()).setBorder(TipUtil.getToolTipBorder());
} }
/** /**
* A completion and its cached summary text. * A completion and its cached summary text.
*/ */
private static class HistoryEntry { private static class HistoryEntry {
public Completion completion; public Completion completion;
public String summary; public String summary;
public String anchor; public String anchor;
public HistoryEntry(Completion completion, String summary, String anchor) { public HistoryEntry(Completion completion, String summary, String anchor) {
this.completion = completion; this.completion = completion;
this.summary = summary; this.summary = summary;
this.anchor = anchor; this.anchor = anchor;
} }
/** /**
* Overridden to display a short name for the completion, since it's * Overridden to display a short name for the completion, since it's used in the
* used in the tool tips for the "back" and "forward" buttons. * tool tips for the "back" and "forward" buttons.
* *
* @return A string representation of this history entry. * @return A string representation of this history entry.
*/ */
@@ -452,16 +438,16 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
return completion.getInputText(); return completion.getInputText();
} }
} }
/** /**
* Action that actually updates the summary text displayed. * Action that actually updates the summary text displayed.
*/ */
private class TimerAction extends AbstractAction { private class TimerAction extends AbstractAction {
private Completion completion; private Completion completion;
private String anchor; private String anchor;
private boolean addToHistory; private boolean addToHistory;
/** /**
* Called when the timer is fired. * Called when the timer is fired.
*/ */
@@ -469,26 +455,26 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
setDisplayedDesc(completion, anchor, addToHistory); setDisplayedDesc(completion, anchor, addToHistory);
} }
public void setCompletion(Completion c, String anchor, boolean addToHistory) { public void setCompletion(Completion c, String anchor, boolean addToHistory) {
this.completion = c; this.completion = c;
this.anchor = anchor; this.anchor = anchor;
this.addToHistory = addToHistory; this.addToHistory = addToHistory;
} }
} }
/** /**
* Action that moves to the previous description displayed. * Action that moves to the previous description displayed.
*/ */
class ToolBarBackAction extends AbstractAction { class ToolBarBackAction extends AbstractAction {
public ToolBarBackAction(boolean ltr) { public ToolBarBackAction(boolean ltr) {
String img = "org/fife/ui/autocomplete/arrow_" + (ltr ? "left.png" : "right.png"); String img = "org/fife/ui/autocomplete/arrow_" + (ltr ? "left.png" : "right.png");
ClassLoader cl = getClass().getClassLoader(); ClassLoader cl = getClass().getClassLoader();
Icon icon = new ImageIcon(cl.getResource(img)); Icon icon = new ImageIcon(cl.getResource(img));
putValue(Action.SMALL_ICON, icon); putValue(Action.SMALL_ICON, icon);
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (historyPos > 0) { if (historyPos > 0) {
@@ -504,19 +490,19 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
} }
} }
} }
/** /**
* Action that moves to the previous description displayed. * Action that moves to the previous description displayed.
*/ */
class ToolBarForwardAction extends AbstractAction { class ToolBarForwardAction extends AbstractAction {
public ToolBarForwardAction(boolean ltr) { public ToolBarForwardAction(boolean ltr) {
String img = "org/fife/ui/autocomplete/arrow_" + (ltr ? "right.png" : "left.png"); String img = "org/fife/ui/autocomplete/arrow_" + (ltr ? "right.png" : "left.png");
ClassLoader cl = getClass().getClassLoader(); ClassLoader cl = getClass().getClassLoader();
Icon icon = new ImageIcon(cl.getResource(img)); Icon icon = new ImageIcon(cl.getResource(img));
putValue(Action.SMALL_ICON, icon); putValue(Action.SMALL_ICON, icon);
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (history != null && historyPos < history.size() - 1) { 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 * The actual popup window of choices. When visible, this window intercepts
* certain keystrokes in the parent text component and uses them to navigate * certain keystrokes in the parent text component and uses them to navigate the
* the completion choices instead. If Enter or Escape is pressed, the window * completion choices instead. If Enter or Escape is pressed, the window hides
* hides itself and notifies the {@link AutoCompletion} to insert the selected * itself and notifies the {@link AutoCompletion} to insert the selected text.
* text.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSelectionListener, MouseListener { class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSelectionListener, MouseListener {
/** /**
* The parent AutoCompletion instance. * The parent AutoCompletion instance.
*/ */
@@ -66,27 +65,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
*/ */
private CompletionListModel model; private CompletionListModel model;
/** /**
* A hack to work around the fact that we clear our completion model (and * A hack to work around the fact that we clear our completion model (and our
* our selection) when hiding the completion window. This allows us to * selection) when hiding the completion window. This allows us to still know
* still know what the user selected after the popup is hidden. * what the user selected after the popup is hidden.
*/ */
private Completion lastSelection; private Completion lastSelection;
/** /**
* Optional popup window containing a description of the currently * Optional popup window containing a description of the currently selected
* selected completion. * completion.
*/ */
private AutoCompleteDescWindow descWindow; private AutoCompleteDescWindow descWindow;
/** /**
* The preferred size of the optional description window. This field * The preferred size of the optional description window. This field only exists
* only exists because the user may (and usually will) set the size of * because the user may (and usually will) set the size of the description
* the description window before it exists (it must be parented to a * window before it exists (it must be parented to a Window).
* Window).
*/ */
private Dimension preferredDescWindowSize; private Dimension preferredDescWindowSize;
/** /**
* Whether the completion window and the optional description window * Whether the completion window and the optional description window should be
* should be displayed above the current caret position (as opposed to * displayed above the current caret position (as opposed to underneath it,
* underneath it, which is preferred unless there is not enough space). * which is preferred unless there is not enough space).
*/ */
private boolean aboveCaret; private boolean aboveCaret;
private int lastLine; private int lastLine;
@@ -103,7 +101,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
private KeyActionPair pageUpKap; private KeyActionPair pageUpKap;
private KeyActionPair pageDownKap; private KeyActionPair pageDownKap;
private KeyActionPair ctrlCKap; 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. * 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. * The class name of the Substance List UI.
*/ */
private static final String SUBSTANCE_LIST_UI = "org.pushingpixels.substance.internal.ui.SubstanceListUI"; private static final String SUBSTANCE_LIST_UI = "org.pushingpixels.substance.internal.ui.SubstanceListUI";
/** /**
* Constructor. * Constructor.
* *
* @param parent * @param parent The parent window (hosting the text component).
* The parent window (hosting the text component). * @param ac The auto-completion instance.
* @param ac
* The auto-completion instance.
*/ */
public AutoCompletePopupWindow(Window parent, final AutoCompletion ac) { public AutoCompletePopupWindow(Window parent, final AutoCompletion ac) {
super(parent); super(parent);
@@ -131,7 +128,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.addListSelectionListener(this); list.addListSelectionListener(this);
list.addMouseListener(this); list.addMouseListener(this);
JPanel contentPane = new JPanel(new BorderLayout()); 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 // In 1.4, JScrollPane.setCorner() has a bug where it won't accept
// JScrollPane.LOWER_TRAILING_CORNER, even though that constant is // JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
// defined. So we have to put the logic added in 1.5 to handle it // 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); setFocusableWindowState(false);
lastLine = -1; lastLine = -1;
} }
@Override @Override
public void caretUpdate(CaretEvent e) { public void caretUpdate(CaretEvent e) {
if (isVisible()) { // Should always be true if (isVisible()) { // Should always be true
@@ -170,7 +168,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
Thread.dumpStack(); Thread.dumpStack();
} }
} }
/** /**
* Creates the description window. * Creates the description window.
* *
@@ -186,10 +184,10 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
dw.setSize(size); dw.setSize(size);
return dw; return dw;
} }
/** /**
* Creates the mappings from keys to Actions we'll be putting into the * Creates the mappings from keys to Actions we'll be putting into the text
* text component's ActionMap and InputMap. * component's ActionMap and InputMap.
*/ */
private void createKeyActionPairs() { private void createKeyActionPairs() {
// Actions we'll install. // Actions we'll install.
@@ -220,11 +218,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
oldPageDown = new KeyActionPair(); oldPageDown = new KeyActionPair();
oldCtrlC = new KeyActionPair(); oldCtrlC = new KeyActionPair();
} }
protected void doAutocomplete() { protected void doAutocomplete() {
lastLine = ac.refreshPopupWindow(); lastLine = ac.refreshPopupWindow();
} }
/** /**
* Returns the copy keystroke to use for this platform. * 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(); int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
return KeyStroke.getKeyStroke(key, mask); return KeyStroke.getKeyStroke(key, mask);
} }
/** /**
* Returns the default list cell renderer used when a completion provider * Returns the default list cell renderer used when a completion provider does
* does not supply its own. * not supply its own.
* *
* @return The default list cell renderer. * @return The default list cell renderer.
* @see #setListCellRenderer(ListCellRenderer) * @see #setListCellRenderer(ListCellRenderer)
@@ -247,7 +245,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.getCellRenderer(); DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.getCellRenderer();
return dcr.getFallbackCellRenderer(); return dcr.getFallbackCellRenderer();
} }
/** /**
* Returns the selected value, or <code>null</code> if nothing is selected. * 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() { public Completion getSelection() {
return isShowing() ? (Completion) list.getSelectedValue() : lastSelection; return isShowing() ? (Completion) list.getSelectedValue() : lastSelection;
} }
/** /**
* Inserts the currently selected completion. * Inserts the currently selected completion.
* *
@@ -266,10 +264,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
Completion comp = getSelection(); Completion comp = getSelection();
ac.insertCompletion(comp); ac.insertCompletion(comp);
} }
/** /**
* Registers keyboard actions to listen for in the text component and * Registers keyboard actions to listen for in the text component and intercept.
* intercept.
* *
* @see #uninstallKeyBindings() * @see #uninstallKeyBindings()
*/ */
@@ -313,30 +310,34 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
comp.addCaretListener(this); comp.addCaretListener(this);
keyBindingsInstalled = true; keyBindingsInstalled = true;
} }
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) { if (e.getClickCount() == 2) {
insertSelectedCompletion(); insertSelectedCompletion();
} }
} }
@Override @Override
public void mouseEntered(MouseEvent e) {} public void mouseEntered(MouseEvent e) {
}
@Override @Override
public void mouseExited(MouseEvent e) {} public void mouseExited(MouseEvent e) {
}
@Override @Override
public void mousePressed(MouseEvent e) {} public void mousePressed(MouseEvent e) {
}
@Override @Override
public void mouseReleased(MouseEvent e) {} public void mouseReleased(MouseEvent e) {
}
/** /**
* Positions the description window relative to the completion choices * Positions the description window relative to the completion choices window.
* window. We assume there is room on one side of the other for this * We assume there is room on one side of the other for this entire window to
* entire window to fit. * fit.
*/ */
private void positionDescWindow() { private void positionDescWindow() {
boolean showDescWindow = descWindow != null && ac.getShowDescWindow(); boolean showDescWindow = descWindow != null && ac.getShowDescWindow();
@@ -372,42 +373,31 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
descWindow.setLocation(x, y); descWindow.setLocation(x, y);
} }
} }
/** /**
* "Puts back" the original key/Action pair for a keystroke. This is used * "Puts back" the original key/Action pair for a keystroke. This is used when
* when this popup is hidden. * this popup is hidden.
* *
* @param im * @param im The input map.
* The input map. * @param am The action map.
* @param am * @param key The keystroke whose key/Action pair to change.
* The action map. * @param kap The (original) key/Action pair.
* @param key * @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
* 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) { private void putBackAction(InputMap im, ActionMap am, int key, KeyActionPair kap) {
KeyStroke ks = KeyStroke.getKeyStroke(key, 0); KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
am.put(im.get(ks), kap.action); // Original action for the "new" key am.put(im.get(ks), kap.action); // Original action for the "new" key
im.put(ks, kap.key); // Original key for the keystroke. im.put(ks, kap.key); // Original key for the keystroke.
} }
/** /**
* Replaces a key/Action pair in an InputMap and ActionMap with a new * Replaces a key/Action pair in an InputMap and ActionMap with a new pair.
* pair.
* *
* @param im * @param im The input map.
* The input map. * @param am The action map.
* @param am * @param key The keystroke whose information to replace.
* The action map. * @param kap The new key/Action pair for <code>key</code>.
* @param key * @param old A buffer in which to place the old key/Action pair.
* 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) * @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
*/ */
private void replaceAction(InputMap im, ActionMap am, int key, KeyActionPair kap, KeyActionPair old) { 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); old.action = am.get(kap.key);
am.put(kap.key, kap.action); am.put(kap.key, kap.action);
} }
/** /**
* Selects the first item in the completion list. * Selects the first item in the completion list.
* *
@@ -429,7 +419,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.ensureIndexIsVisible(0); list.ensureIndexIsVisible(0);
} }
} }
/** /**
* Selects the last item in the completion list. * Selects the last item in the completion list.
* *
@@ -442,7 +432,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.ensureIndexIsVisible(index); list.ensureIndexIsVisible(index);
} }
} }
/** /**
* Selects the next item in the completion list. * Selects the next item in the completion list.
* *
@@ -456,10 +446,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.ensureIndexIsVisible(index); list.ensureIndexIsVisible(index);
} }
} }
/** /**
* Selects the completion item one "page down" from the currently selected * Selects the completion item one "page down" from the currently selected one.
* one.
* *
* @see #selectPageUpItem() * @see #selectPageUpItem()
*/ */
@@ -469,10 +458,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.setSelectedIndex(i); list.setSelectedIndex(i);
list.ensureIndexIsVisible(i); list.ensureIndexIsVisible(i);
} }
/** /**
* Selects the completion item one "page up" from the currently selected * Selects the completion item one "page up" from the currently selected one.
* one.
* *
* @see #selectPageDownItem() * @see #selectPageDownItem()
*/ */
@@ -482,7 +470,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.setSelectedIndex(i); list.setSelectedIndex(i);
list.ensureIndexIsVisible(i); list.ensureIndexIsVisible(i);
} }
/** /**
* Selects the previous item in the completion list. * Selects the previous item in the completion list.
* *
@@ -491,40 +479,38 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
private void selectPreviousItem() { private void selectPreviousItem() {
int index = list.getSelectedIndex(); int index = list.getSelectedIndex();
switch (index) { switch (index) {
case 0: case 0:
index = list.getModel().getSize() - 1; index = list.getModel().getSize() - 1;
break; break;
case -1: // Check for an empty list (would be an error) case -1: // Check for an empty list (would be an error)
index = list.getModel().getSize() - 1; index = list.getModel().getSize() - 1;
if (index == -1) { if (index == -1) {
return; return;
} }
break; break;
default: default:
index = index - 1; index = index - 1;
break; break;
} }
list.setSelectedIndex(index); list.setSelectedIndex(index);
list.ensureIndexIsVisible(index); list.ensureIndexIsVisible(index);
} }
/** /**
* Sets the completions to display in the choices list. The first * Sets the completions to display in the choices list. The first completion is
* completion is selected. * selected.
* *
* @param completions * @param completions The completions to display.
* The completions to display.
*/ */
public void setCompletions(List<Completion> completions) { public void setCompletions(List<Completion> completions) {
model.setContents(completions); model.setContents(completions);
selectFirstItem(); selectFirstItem();
} }
/** /**
* Sets the size of the description window. * Sets the size of the description window.
* *
* @param size * @param size The new size. This cannot be <code>null</code>.
* The new size. This cannot be <code>null</code>.
*/ */
public void setDescriptionWindowSize(Dimension size) { public void setDescriptionWindowSize(Dimension size) {
if (descWindow != null) { if (descWindow != null) {
@@ -533,28 +519,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
preferredDescWindowSize = size; preferredDescWindowSize = size;
} }
} }
/** /**
* Sets the default list cell renderer to use when a completion provider * Sets the default list cell renderer to use when a completion provider does
* does not supply its own. * not supply its own.
* *
* @param renderer * @param renderer The renderer to use. If this is <code>null</code>, a default
* The renderer to use. If this is <code>null</code>, * renderer is used.
* a default renderer is used.
* @see #getListCellRenderer() * @see #getListCellRenderer()
*/ */
public void setListCellRenderer(ListCellRenderer renderer) { public void setListCellRenderer(ListCellRenderer renderer) {
DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.getCellRenderer(); DelegatingCellRenderer dcr = (DelegatingCellRenderer) list.getCellRenderer();
dcr.setFallbackCellRenderer(renderer); dcr.setFallbackCellRenderer(renderer);
} }
/** /**
* Sets the location of this window to be "good" relative to the specified * Sets the location of this window to be "good" relative to the specified
* rectangle. That rectangle should be the location of the text * rectangle. That rectangle should be the location of the text component's
* component's caret, in screen coordinates. * caret, in screen coordinates.
* *
* @param r * @param r The text component's caret position, in screen coordinates.
* The text component's caret position, in screen coordinates.
*/ */
public void setLocationRelativeTo(Rectangle r) { public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and // Multi-monitor support - make sure the completion window (and
@@ -596,12 +580,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
positionDescWindow(); positionDescWindow();
} }
} }
/** /**
* Toggles the visibility of this popup window. * Toggles the visibility of this popup window.
* *
* @param visible * @param visible Whether this window should be visible.
* Whether this window should be visible.
*/ */
@Override @Override
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
@@ -649,7 +632,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
/** /**
* Stops intercepting certain keystrokes from the text component. * Stops intercepting certain keystrokes from the text component.
* *
@@ -683,10 +666,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
comp.removeCaretListener(this); comp.removeCaretListener(this);
keyBindingsInstalled = false; keyBindingsInstalled = false;
} }
/** /**
* Updates the <tt>LookAndFeel</tt> of this window and the description * Updates the <tt>LookAndFeel</tt> of this window and the description window.
* window.
*/ */
public void updateUI() { public void updateUI() {
SwingUtilities.updateComponentTreeUI(this); SwingUtilities.updateComponentTreeUI(this);
@@ -694,12 +676,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
descWindow.updateUI(); descWindow.updateUI();
} }
} }
/** /**
* Called when a new item is selected in the popup list. * Called when a new item is selected in the popup list.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void valueChanged(ListSelectionEvent e) { public void valueChanged(ListSelectionEvent e) {
@@ -711,9 +692,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class CopyAction extends AbstractAction { class CopyAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
boolean doNormalCopy = false; boolean doNormalCopy = false;
@@ -725,9 +706,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class DownAction extends AbstractAction { class DownAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -735,9 +716,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class EndAction extends AbstractAction { class EndAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -745,9 +726,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class EnterAction extends AbstractAction { class EnterAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -755,9 +736,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class EscapeAction extends AbstractAction { class EscapeAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -765,9 +746,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class HomeAction extends AbstractAction { class HomeAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -775,25 +756,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
/** /**
* A mapping from a key (an Object) to an Action. * A mapping from a key (an Object) to an Action.
*/ */
private static class KeyActionPair { private static class KeyActionPair {
public Object key; public Object key;
public Action action; public Action action;
public KeyActionPair() {} public KeyActionPair() {
}
public KeyActionPair(Object key, Action a) { public KeyActionPair(Object key, Action a) {
this.key = key; this.key = key;
this.action = a; this.action = a;
} }
} }
class LeftAction extends AbstractAction { class LeftAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -813,9 +795,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class PageDownAction extends AbstractAction { class PageDownAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -823,9 +805,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class PageUpAction extends AbstractAction { class PageUpAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -833,16 +815,16 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
/** /**
* The actual list of completion choices in this popup window. * The actual list of completion choices in this popup window.
*/ */
private class PopupList extends JList { private class PopupList extends JList {
public PopupList(CompletionListModel model) { public PopupList(CompletionListModel model) {
super(model); super(model);
} }
@Override @Override
public void setUI(ListUI ui) { public void setUI(ListUI ui) {
if (Util.getUseSubstanceRenderers() && SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) { if (Util.getUseSubstanceRenderers() && SUBSTANCE_LIST_UI.equals(ui.getClass().getName())) {
@@ -863,9 +845,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
super.setUI(ui); super.setUI(ui);
} }
} }
class RightAction extends AbstractAction { class RightAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { if (isVisible()) {
@@ -885,9 +867,9 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
} }
} }
} }
class UpAction extends AbstractAction { class UpAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isVisible()) { 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; import java.util.EventObject;
/** /**
* An event fired by an instance of {@link AutoCompletion}. This can be * An event fired by an instance of {@link AutoCompletion}. This can be used by
* used by applications that wish to be notified of the auto-complete popup * applications that wish to be notified of the auto-complete popup window
* window showing and hiding. * showing and hiding.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class AutoCompletionEvent extends EventObject { public class AutoCompletionEvent extends EventObject {
/** /**
* The type of this event. * The type of this event.
*/ */
private Type type; private Type type;
/** /**
* Constructor. * Constructor.
* *
* @param source * @param source The <code>AutoCompletion</code> instance that fired this event.
* The <code>AutoCompletion</code> instance that fired * @param type The event type.
* this event.
* @param type
* The event type.
*/ */
public AutoCompletionEvent(AutoCompletion source, Type type) { public AutoCompletionEvent(AutoCompletion source, Type type) {
super(source); super(source);
this.type = type; this.type = type;
} }
/** /**
* Returns the source <code>AutoCompletion</code> instance. This is just * Returns the source <code>AutoCompletion</code> instance. This is just
* shorthand for <code>return (AutoCompletion)getSource();</code>. * shorthand for <code>return (AutoCompletion)getSource();</code>.
@@ -45,7 +42,7 @@ public class AutoCompletionEvent extends EventObject {
public AutoCompletion getAutoCompletion() { public AutoCompletion getAutoCompletion() {
return (AutoCompletion) getSource(); return (AutoCompletion) getSource();
} }
/** /**
* Returns the type of this event. * Returns the type of this event.
* *
@@ -54,7 +51,7 @@ public class AutoCompletionEvent extends EventObject {
public Type getEventType() { public Type getEventType() {
return type; return type;
} }
/** /**
* Enumeration of the various types of this event. * Enumeration of the various types of this event.
*/ */
@@ -15,13 +15,12 @@ import java.util.EventListener;
* @version 1.0 * @version 1.0
*/ */
public interface AutoCompletionListener extends EventListener { public interface AutoCompletionListener extends EventListener {
/** /**
* Callback notified when a change to the <code>AutoCompletion</code>'s * Callback notified when a change to the <code>AutoCompletion</code>'s status
* status occurs. * occurs.
* *
* @param e * @param e The event.
* The event.
*/ */
void autoCompleteUpdate(AutoCompletionEvent e); void autoCompleteUpdate(AutoCompletionEvent e);
} }
@@ -15,32 +15,32 @@ import java.awt.Color;
* @version 1.0 * @version 1.0
*/ */
public class AutoCompletionStyleContext { public class AutoCompletionStyleContext {
/** /**
* The color used to denote the ending caret position for parameterized * The color used to denote the ending caret position for parameterized
* completions. * completions.
*/ */
private Color parameterizedCompletionCursorPositionColor; private Color parameterizedCompletionCursorPositionColor;
/** /**
* The color used to highlight copies of editable parameters in * The color used to highlight copies of editable parameters in parameterized
* parameterized completions. * completions.
*/ */
private Color parameterCopyColor; private Color parameterCopyColor;
/** /**
* The color of the outline highlight used to denote editable parameters * The color of the outline highlight used to denote editable parameters in
* in parameterized completions. * parameterized completions.
*/ */
private Color parameterOutlineColor; private Color parameterOutlineColor;
public AutoCompletionStyleContext() { public AutoCompletionStyleContext() {
setParameterOutlineColor(Color.gray); setParameterOutlineColor(Color.gray);
setParameterCopyColor(new Color(0xb4d7ff)); setParameterCopyColor(new Color(0xb4d7ff));
setParameterizedCompletionCursorPositionColor(new Color(0x00b400)); setParameterizedCompletionCursorPositionColor(new Color(0x00b400));
} }
/** /**
* Returns the color of the highlight painted on copies of editable * Returns the color of the highlight painted on copies of editable parameters
* parameters in parameterized completions. * in parameterized completions.
* *
* @return The color used. * @return The color used.
* @see #setParameterCopyColor(Color) * @see #setParameterCopyColor(Color)
@@ -48,10 +48,10 @@ public class AutoCompletionStyleContext {
public Color getParameterCopyColor() { public Color getParameterCopyColor() {
return parameterCopyColor; return parameterCopyColor;
} }
/** /**
* Returns the color used to denote the ending caret position for * Returns the color used to denote the ending caret position for parameterized
* parameterized completions. * completions.
* *
* @return The color used. * @return The color used.
* @see #setParameterizedCompletionCursorPositionColor(Color) * @see #setParameterizedCompletionCursorPositionColor(Color)
@@ -59,10 +59,10 @@ public class AutoCompletionStyleContext {
public Color getParameterizedCompletionCursorPositionColor() { public Color getParameterizedCompletionCursorPositionColor() {
return parameterizedCompletionCursorPositionColor; return parameterizedCompletionCursorPositionColor;
} }
/** /**
* Returns the color of the outline highlight used to denote editable * Returns the color of the outline highlight used to denote editable parameters
* parameters in parameterized completions. * in parameterized completions.
* *
* @return The color used. * @return The color used.
* @see #setParameterOutlineColor(Color) * @see #setParameterOutlineColor(Color)
@@ -70,37 +70,34 @@ public class AutoCompletionStyleContext {
public Color getParameterOutlineColor() { public Color getParameterOutlineColor() {
return parameterOutlineColor; return parameterOutlineColor;
} }
/** /**
* Sets the color of the highlight painted on copies of editable * Sets the color of the highlight painted on copies of editable parameters in
* parameters in parameterized completions. * parameterized completions.
* *
* @param color * @param color The color to use.
* The color to use.
* @see #setParameterCopyColor(Color) * @see #setParameterCopyColor(Color)
*/ */
public void setParameterCopyColor(Color color) { public void setParameterCopyColor(Color color) {
this.parameterCopyColor = color; this.parameterCopyColor = color;
} }
/** /**
* Sets the color used to denote the ending caret position for * Sets the color used to denote the ending caret position for parameterized
* parameterized completions. * completions.
* *
* @param color * @param color The color to use.
* The color to use.
* @see #getParameterizedCompletionCursorPositionColor() * @see #getParameterizedCompletionCursorPositionColor()
*/ */
public void setParameterizedCompletionCursorPositionColor(Color color) { public void setParameterizedCompletionCursorPositionColor(Color color) {
this.parameterizedCompletionCursorPositionColor = color; this.parameterizedCompletionCursorPositionColor = color;
} }
/** /**
* Sets the color of the outline highlight used to denote editable * Sets the color of the outline highlight used to denote editable parameters in
* parameters in parameterized completions. * parameterized completions.
* *
* @param color * @param color The color to use.
* The color to use.
* @see #getParameterOutlineColor() * @see #getParameterOutlineColor()
*/ */
public void setParameterOutlineColor(Color color) { public void setParameterOutlineColor(Color color) {
@@ -7,9 +7,9 @@
package org.fife.ui.autocomplete; package org.fife.ui.autocomplete;
/** /**
* A straightforward {@link Completion} implementation. This implementation * A straightforward {@link Completion} implementation. This implementation can
* can be used if you have a relatively short number of static completions * be used if you have a relatively short number of static completions with no
* with no (or short) summaries. * (or short) summaries.
* <p> * <p>
* *
* This implementation uses the replacement text as the input text. It also * This implementation uses the replacement text as the input text. It also
@@ -20,53 +20,44 @@ package org.fife.ui.autocomplete;
* @version 1.0 * @version 1.0
*/ */
public class BasicCompletion extends AbstractCompletion { public class BasicCompletion extends AbstractCompletion {
private String replacementText; private String replacementText;
private String shortDesc; private String shortDesc;
private String summary; private String summary;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The parent completion provider.
* The parent completion provider. * @param replacementText The text to replace.
* @param replacementText
* The text to replace.
*/ */
public BasicCompletion(CompletionProvider provider, String replacementText) { public BasicCompletion(CompletionProvider provider, String replacementText) {
this(provider, replacementText, null); this(provider, replacementText, null);
} }
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The parent completion provider.
* The parent completion provider. * @param replacementText The text to replace.
* @param replacementText * @param shortDesc A short description of the completion. This will be
* The text to replace. * displayed in the completion list. This may be
* @param shortDesc * <code>null</code>.
* 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) { public BasicCompletion(CompletionProvider provider, String replacementText, String shortDesc) {
this(provider, replacementText, shortDesc, null); this(provider, replacementText, shortDesc, null);
} }
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The parent completion provider.
* The parent completion provider. * @param replacementText The text to replace.
* @param replacementText * @param shortDesc A short description of the completion. This will be
* The text to replace. * displayed in the completion list. This may be
* @param shortDesc * <code>null</code>.
* A short description of the completion. This will be * @param summary The summary of this completion. This should be HTML.
* displayed in the completion list. This may be * This may be <code>null</code>.
* <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) { public BasicCompletion(CompletionProvider provider, String replacementText, String shortDesc, String summary) {
super(provider); super(provider);
@@ -74,7 +65,7 @@ public class BasicCompletion extends AbstractCompletion {
this.shortDesc = shortDesc; this.shortDesc = shortDesc;
this.summary = summary; this.summary = summary;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -82,10 +73,10 @@ public class BasicCompletion extends AbstractCompletion {
public String getReplacementText() { public String getReplacementText() {
return replacementText; return replacementText;
} }
/** /**
* Returns the short description of this completion, usually used in * Returns the short description of this completion, usually used in the
* the completion choices list. * completion choices list.
* *
* @return The short description, or <code>null</code> if there is none. * @return The short description, or <code>null</code> if there is none.
* @see #setShortDescription(String) * @see #setShortDescription(String)
@@ -93,7 +84,7 @@ public class BasicCompletion extends AbstractCompletion {
public String getShortDescription() { public String getShortDescription() {
return shortDesc; return shortDesc;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -101,32 +92,30 @@ public class BasicCompletion extends AbstractCompletion {
public String getSummary() { public String getSummary() {
return summary; return summary;
} }
/** /**
* Sets the short description of this completion. * Sets the short description of this completion.
* *
* @param shortDesc * @param shortDesc The short description of this completion.
* The short description of this completion.
* @see #getShortDescription() * @see #getShortDescription()
*/ */
public void setShortDescription(String shortDesc) { public void setShortDescription(String shortDesc) {
this.shortDesc = shortDesc; this.shortDesc = shortDesc;
} }
/** /**
* Sets the summary for this completion. * Sets the summary for this completion.
* *
* @param summary * @param summary The summary for this completion.
* The summary for this completion.
* @see #getSummary() * @see #getSummary()
*/ */
public void setSummary(String summary) { public void setSummary(String summary) {
this.summary = summary; this.summary = summary;
} }
/** /**
* Returns a string representation of this completion. If the short * Returns a string representation of this completion. If the short description
* description is not <code>null</code>, this method will return: * is not <code>null</code>, this method will return:
* *
* <code>getInputText() + " - " + shortDesc</code> * <code>getInputText() + " - " + shortDesc</code>
* *
+51 -59
View File
@@ -10,20 +10,20 @@ import javax.swing.Icon;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
/** /**
* Represents a completion choice. A {@link CompletionProvider} returns lists * Represents a completion choice. A {@link CompletionProvider} returns lists of
* of objects implementing this interface. A <tt>Completion</tt> contains the * objects implementing this interface. A <tt>Completion</tt> contains the
* following information: * following information:
* *
* <ul> * <ul>
* <li>The text the user must (begin to) input for this to be a completion * <li>The text the user must (begin to) input for this to be a completion
* choice. * choice.
* <li>The text that will be filled in if the user chooses this completion. * <li>The text that will be filled in if the user chooses this completion. Note
* Note that often, this is the same as the text the user must (begin to) * that often, this is the same as the text the user must (begin to) enter, but
* enter, but this doesn't have to be the case. * this doesn't have to be the case.
* <li>Summary HTML that describes this completion. This is information that * <li>Summary HTML that describes this completion. This is information that can
* can be displayed in a helper "tooltip"-style window beside the * be displayed in a helper "tooltip"-style window beside the completion list.
* completion list. This may be <code>null</code>. It may also be * This may be <code>null</code>. It may also be lazily generated to cut down on
* lazily generated to cut down on memory usage. * memory usage.
* <li>The <tt>CompletionProvider</tt> that returned this completion. * <li>The <tt>CompletionProvider</tt> that returned this completion.
* <li>Tool tip text that can be displayed when a mouse hovers over this * <li>Tool tip text that can be displayed when a mouse hovers over this
* completion in a text component. * completion in a text component.
@@ -34,112 +34,104 @@ import javax.swing.text.JTextComponent;
* @see AbstractCompletion * @see AbstractCompletion
*/ */
public interface Completion extends Comparable<Completion> { public interface Completion extends Comparable<Completion> {
/** /**
* Compares this completion to another one lexicographically, ignoring * Compares this completion to another one lexicographically, ignoring case.
* case.
* *
* @param other * @param other Another completion instance.
* Another completion instance.
* @return How this completion compares to the other one. * @return How this completion compares to the other one.
*/ */
@Override @Override
public int compareTo(Completion other); public int compareTo(Completion other);
/** /**
* Returns the portion of this completion that has already been entered * Returns the portion of this completion that has already been entered into the
* into the text component. The match is case-insensitive. * text component. The match is case-insensitive.
* <p> * <p>
* *
* This is a convenience method for: * This is a convenience method for:
* <code>getProvider().getAlreadyEnteredText(comp)</code>. * <code>getProvider().getAlreadyEnteredText(comp)</code>.
* *
* @param comp * @param comp The text component.
* The text component.
* @return The already-entered portion of this completion. * @return The already-entered portion of this completion.
*/ */
public String getAlreadyEntered(JTextComponent comp); public String getAlreadyEntered(JTextComponent comp);
/** /**
* Returns the icon to use for this completion. * Returns the icon to use for this completion.
* *
* @return The icon, or <code>null</code> for none. * @return The icon, or <code>null</code> for none.
*/ */
public Icon getIcon(); public Icon getIcon();
/** /**
* Returns the text that the user has to (start) typing for this completion * Returns the text that the user has to (start) typing for this completion to
* to be offered. Note that this will usually be the same value as * be offered. Note that this will usually be the same value as
* {@link #getReplacementText()}, but not always (a completion could be * {@link #getReplacementText()}, but not always (a completion could be a way to
* a way to implement shorthand, for example, "<code>sysout</code>" mapping * implement shorthand, for example, "<code>sysout</code>" mapping to
* to "<code>System.out.println(</code>"). * "<code>System.out.println(</code>").
* *
* @return The text the user has to (start) typing for this completion to * @return The text the user has to (start) typing for this completion to be
* be offered. * offered.
* @see #getReplacementText() * @see #getReplacementText()
*/ */
public String getInputText(); public String getInputText();
/** /**
* Returns the provider that returned this completion. * Returns the provider that returned this completion.
* *
* @return The provider. * @return The provider.
*/ */
public CompletionProvider getProvider(); public CompletionProvider getProvider();
/** /**
* Returns the "relevance" of this completion. This is used when sorting * Returns the "relevance" of this completion. This is used when sorting
* completions by their relevance. It is an abstract concept that may * completions by their relevance. It is an abstract concept that may mean
* mean different things to different languages, and may depend on the * different things to different languages, and may depend on the context of the
* context of the completion. * completion.
* <p> * <p>
* *
* By default, all completions have a relevance of <code>0</code>. The * By default, all completions have a relevance of <code>0</code>. The higher
* higher the value returned by this method, the higher up in the list * the value returned by this method, the higher up in the list this completion
* this completion will be; the lower the value returned, the lower it will * will be; the lower the value returned, the lower it will be.
* be. <code>Completion</code>s with equal relevance values will be * <code>Completion</code>s with equal relevance values will be sorted
* sorted alphabetically. * alphabetically.
* *
* @return The relevance of this completion. * @return The relevance of this completion.
*/ */
public int getRelevance(); public int getRelevance();
/** /**
* Returns the text to insert as the result of this auto-completion. This * Returns the text to insert as the result of this auto-completion. This is the
* is the "complete" text, including any text that replaces what the user * "complete" text, including any text that replaces what the user has already
* has already typed. * typed.
* *
* @return The replacement text. * @return The replacement text.
* @see #getInputText() * @see #getInputText()
*/ */
public String getReplacementText(); public String getReplacementText();
/** /**
* Returns the description of this auto-complete choice. This can be * Returns the description of this auto-complete choice. This can be used in a
* used in a popup "description window." * popup "description window."
* *
* @return This item's description. This should be HTML. It may be * @return This item's description. This should be HTML. It may be
* <code>null</code> if there is no description for this * <code>null</code> if there is no description for this completion.
* completion.
*/ */
public String getSummary(); public String getSummary();
/** /**
* Returns the tool tip text to display for mouse hovers over this * Returns the tool tip text to display for mouse hovers over this completion.
* completion.
* <p> * <p>
* *
* Note that for this functionality to be enabled, a * Note that for this functionality to be enabled, a <tt>JTextComponent</tt>
* <tt>JTextComponent</tt> must be registered with the * must be registered with the <tt>ToolTipManager</tt>, and the text component
* <tt>ToolTipManager</tt>, and the text component must know to search * must know to search for this value. In the case of an
* for this value. In the case of an * <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this can
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this * be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that calls into
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}. * {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
* *
* @return The tool tip text for this completion, or <code>null</code> if * @return The tool tip text for this completion, or <code>null</code> if none.
* none.
*/ */
public String getToolTipText(); public String getToolTipText();
} }
@@ -26,8 +26,8 @@ import javax.swing.text.View;
/** /**
* A cell renderer that adds some pizazz when rendering the standard * A cell renderer that adds some pizazz when rendering the standard
* {@link Completion} types, like Eclipse and NetBeans do. Specifically, * {@link Completion} types, like Eclipse and NetBeans do. Specifically, this
* this renderer handles: * renderer handles:
* *
* <ul> * <ul>
* <li>{@link FunctionCompletion}s</li> * <li>{@link FunctionCompletion}s</li>
@@ -41,20 +41,20 @@ import javax.swing.text.View;
* @version 1.0 * @version 1.0
*/ */
public class CompletionCellRenderer extends DefaultListCellRenderer { public class CompletionCellRenderer extends DefaultListCellRenderer {
/** /**
* The alternating background color, or <code>null</code> if alternating * The alternating background color, or <code>null</code> if alternating row
* row colors should not be used. * colors should not be used.
*/ */
private static Color altBG; private static Color altBG;
/** /**
* The font to use when rendering items, or <code>null</code> if the * The font to use when rendering items, or <code>null</code> if the list's
* list's default font should be used. * default font should be used.
*/ */
private Font font; private Font font;
/** /**
* Whether to display the types of fields and return types of functions * Whether to display the types of fields and return types of functions in the
* in the completion text. * completion text.
*/ */
private boolean showTypes; private boolean showTypes;
/** /**
@@ -92,33 +92,32 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
* words. * words.
*/ */
private static final String PREFIX = "<html><nobr>"; private static final String PREFIX = "<html><nobr>";
/** /**
* Constructor. * Constructor.
*/ */
public CompletionCellRenderer() { public CompletionCellRenderer() {
init(); init();
} }
/** /**
* Constructor. This is primarily a hook for Substance, or any other * Constructor. This is primarily a hook for Substance, or any other Look and
* Look and Feel whose renderers look drastically different than standard * Feel whose renderers look drastically different than standard
* <code>DefaultListCellRenderer</code>s. Everything except for the text * <code>DefaultListCellRenderer</code>s. Everything except for the text
* rendering will be done by the delegate. In almost all scenarios, you * rendering will be done by the delegate. In almost all scenarios, you will
* will want to use the no-argument constructor instead of this one. * want to use the no-argument constructor instead of this one.
* *
* @param delegate * @param delegate The delegate renderer.
* The delegate renderer.
* @see #delegateToSubstanceRenderer() * @see #delegateToSubstanceRenderer()
*/ */
public CompletionCellRenderer(DefaultListCellRenderer delegate) { public CompletionCellRenderer(DefaultListCellRenderer delegate) {
setDelegateRenderer(delegate); setDelegateRenderer(delegate);
init(); init();
} }
/** /**
* Creates the icon to use if no icon is found for a specific completion. * Creates the icon to use if no icon is found for a specific completion. The
* The default implementation returns a 16x16 empty icon. * default implementation returns a 16x16 empty icon.
* *
* @return The icon. * @return The icon.
* @see #getEmptyIcon() * @see #getEmptyIcon()
@@ -126,34 +125,32 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
protected Icon createEmptyIcon() { protected Icon createEmptyIcon() {
return new EmptyIcon(16); return new EmptyIcon(16);
} }
/** /**
* Returns a decent "parameter" color based on the current default * Returns a decent "parameter" color based on the current default foreground
* foreground color. * color.
* *
* @return The parameter color to use. * @return The parameter color to use.
*/ */
private String createParamColor() { private String createParamColor() {
return Util.isLightForeground(getForeground()) ? Util.getHexString(Util.getHyperlinkForeground()) : "#aa0077"; return Util.isLightForeground(getForeground()) ? Util.getHexString(Util.getHyperlinkForeground()) : "#aa0077";
} }
/** /**
* Returns a decent "type" color based on the current default foreground * Returns a decent "type" color based on the current default foreground color.
* color.
* *
* @return The type color to use. * @return The type color to use.
*/ */
private String createTypeColor() { private String createTypeColor() {
return "#808080"; return "#808080";
} }
/** /**
* Attempts to delegate rendering to a Substance cell renderer. This * Attempts to delegate rendering to a Substance cell renderer. This should only
* should only be called if Substance is known to be on the classpath. * be called if Substance is known to be on the classpath.
* *
* @throws Exception * @throws Exception If Substance is not on the classpath, or some other error
* If Substance is not on the classpath, or some other * occurs creating the Substance cell renderer.
* error occurs creating the Substance cell renderer.
* @see Util#getUseSubstanceRenderers() * @see Util#getUseSubstanceRenderers()
* @see #setDelegateRenderer(DefaultListCellRenderer) * @see #setDelegateRenderer(DefaultListCellRenderer)
*/ */
@@ -162,7 +159,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
DefaultListCellRenderer delegate = (DefaultListCellRenderer) clazz.newInstance(); DefaultListCellRenderer delegate = (DefaultListCellRenderer) clazz.newInstance();
setDelegateRenderer(delegate); setDelegateRenderer(delegate);
} }
/** /**
* Returns the background color to use on alternating lines. * Returns the background color to use on alternating lines.
* *
@@ -173,7 +170,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
public static Color getAlternateBackground() { public static Color getAlternateBackground() {
return altBG; return altBG;
} }
/** /**
* Returns the delegate renderer, or <code>null</code> if there is none. * Returns the delegate renderer, or <code>null</code> if there is none.
* *
@@ -183,21 +180,21 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
public DefaultListCellRenderer getDelegateRenderer() { public DefaultListCellRenderer getDelegateRenderer() {
return delegate; return delegate;
} }
/** /**
* Returns the font used when rendering completions. * Returns the font used when rendering completions.
* *
* @return The font. If this is <code>null</code>, then the default list * @return The font. If this is <code>null</code>, then the default list font is
* font is used. * used.
* @see #setDisplayFont(Font) * @see #setDisplayFont(Font)
*/ */
public Font getDisplayFont() { public Font getDisplayFont() {
return font; return font;
} }
/** /**
* Returns the icon to use if no icon is found for a specific completion. * Returns the icon to use if no icon is found for a specific completion. This
* This icon is lazily created if necessary. * icon is lazily created if necessary.
* *
* @return The icon. * @return The icon.
* @see #createEmptyIcon() * @see #createEmptyIcon()
@@ -208,13 +205,12 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
return emptyIcon; return emptyIcon;
} }
/** /**
* Returns an icon. * Returns an icon.
* *
* @param resource * @param resource The icon to retrieve. This should either be a file, or a
* The icon to retrieve. This should either be a file, * resource loadable by the current ClassLoader.
* or a resource loadable by the current ClassLoader.
* @return The icon. * @return The icon.
*/ */
protected Icon getIcon(String resource) { protected Icon getIcon(String resource) {
@@ -229,23 +225,19 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
return url != null ? new ImageIcon(url) : null; return url != null ? new ImageIcon(url) : null;
} }
/** /**
* Returns the renderer. * Returns the renderer.
* *
* @param list * @param list The list of choices being rendered.
* The list of choices being rendered. * @param value The {@link Completion} being rendered.
* @param value * @param index The index into <code>list</code> being rendered.
* The {@link Completion} being rendered. * @param selected Whether the item is selected.
* @param index * @param hasFocus Whether the item has focus.
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
*/ */
@Override @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); super.getListCellRendererComponent(list, value, index, selected, hasFocus);
if (font != null) { if (font != null) {
setFont(font); // Overrides super's setFont(list.getFont()). setFont(font); // Overrides super's setFont(list.getFont()).
@@ -282,10 +274,10 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
return this; return this;
} }
/** /**
* Returns whether the types of fields and return types of methods are * Returns whether the types of fields and return types of methods are shown in
* shown in the completion text. * the completion text.
* *
* @return Whether to show the types. * @return Whether to show the types.
* @see #setShowTypes(boolean) * @see #setShowTypes(boolean)
@@ -293,7 +285,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
public boolean getShowTypes() { public boolean getShowTypes() {
return showTypes; return showTypes;
} }
private void init() { private void init() {
// setDisplayFont(new Font("Monospaced", Font.PLAIN, 12)); // setDisplayFont(new Font("Monospaced", Font.PLAIN, 12));
setShowTypes(true); setShowTypes(true);
@@ -301,7 +293,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
paramColor = createParamColor(); paramColor = createParamColor();
paintTextR = new Rectangle(); paintTextR = new Rectangle();
} }
@Override @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
// super.paintComponent(g); // super.paintComponent(g);
@@ -342,22 +334,18 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
} }
} }
/** /**
* Prepares this renderer to display a function completion. * Prepares this renderer to display a function completion.
* *
* @param list * @param list The list of choices being rendered.
* The list of choices being rendered. * @param fc The completion to render.
* @param fc * @param index The index into <code>list</code> being rendered.
* The completion to render. * @param selected Whether the item is selected.
* @param index * @param hasFocus Whether the item has focus.
* 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); StringBuilder sb = new StringBuilder(PREFIX);
sb.append(fc.getName()); sb.append(fc.getName());
char paramListStart = fc.getProvider().getParameterListStart(); char paramListStart = fc.getProvider().getParameterListStart();
@@ -404,41 +392,32 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
setText(sb.toString()); setText(sb.toString());
} }
/** /**
* Prepares this renderer to display a markup tag completion. * Prepares this renderer to display a markup tag completion.
* *
* @param list * @param list The list of choices being rendered.
* The list of choices being rendered. * @param mc The completion to render.
* @param mc * @param index The index into <code>list</code> being rendered.
* The completion to render. * @param selected Whether the item is selected.
* @param index * @param hasFocus Whether the item has focus.
* 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); StringBuilder sb = new StringBuilder(PREFIX);
sb.append(mc.getName()); sb.append(mc.getName());
setText(sb.toString()); setText(sb.toString());
} }
/** /**
* Prepares this renderer to display a completion not specifically handled * Prepares this renderer to display a completion not specifically handled
* elsewhere. * elsewhere.
* *
* @param list * @param list The list of choices being rendered.
* The list of choices being rendered. * @param c The completion to render.
* @param c * @param index The index into <code>list</code> being rendered.
* The completion to render. * @param selected Whether the item is selected.
* @param index * @param hasFocus Whether the item has focus.
* 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) { protected void prepareForOtherCompletion(JList list, Completion c, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX); StringBuilder sb = new StringBuilder(PREFIX);
@@ -458,22 +437,18 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
setText(sb.toString()); setText(sb.toString());
} }
/** /**
* Prepares this renderer to display a template completion. * Prepares this renderer to display a template completion.
* *
* @param list * @param list The list of choices being rendered.
* The list of choices being rendered. * @param tc The completion to render.
* @param tc * @param index The index into <code>list</code> being rendered.
* The completion to render. * @param selected Whether the item is selected.
* @param index * @param hasFocus Whether the item has focus.
* 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); StringBuilder sb = new StringBuilder(PREFIX);
sb.append(tc.getInputText()); sb.append(tc.getInputText());
String definition = tc.getShortDescription(); String definition = tc.getShortDescription();
@@ -489,22 +464,18 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
setText(sb.toString()); setText(sb.toString());
} }
/** /**
* Prepares this renderer to display a variable completion. * Prepares this renderer to display a variable completion.
* *
* @param list * @param list The list of choices being rendered.
* The list of choices being rendered. * @param vc The completion to render.
* @param vc * @param index The index into <code>list</code> being rendered.
* The completion to render. * @param selected Whether the item is selected.
* @param index * @param hasFocus Whether the item has focus.
* 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); StringBuilder sb = new StringBuilder(PREFIX);
sb.append(vc.getName()); sb.append(vc.getName());
if (getShowTypes() && vc.getType() != null) { if (getShowTypes() && vc.getType() != null) {
@@ -519,81 +490,73 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
} }
setText(sb.toString()); setText(sb.toString());
} }
/** /**
* Sets the background color to use on alternating lines. * Sets the background color to use on alternating lines.
* *
* @param altBG * @param altBG The new alternate background color. If this is
* The new alternate background color. If this is * <code>null</code>, alternating lines will not use different
* <code>null</code>, alternating lines will not use different * background colors.
* background colors.
* @see #getAlternateBackground() * @see #getAlternateBackground()
*/ */
public static void setAlternateBackground(Color altBG) { public static void setAlternateBackground(Color altBG) {
CompletionCellRenderer.altBG = altBG; CompletionCellRenderer.altBG = altBG;
} }
/** /**
* Sets the delegate renderer. Most users will never use this method; it * Sets the delegate renderer. Most users will never use this method; it is
* is primarily a hook for Substance and other Look and Feels whose * primarily a hook for Substance and other Look and Feels whose renderers look
* renderers look drastically different from the standard * drastically different from the standard <code>DefaultListCellRenderer</code>.
* <code>DefaultListCellRenderer</code>.
* *
* @param delegate * @param delegate The new delegate renderer. If this is <code>null</code>, the
* The new delegate renderer. If this is <code>null</code>, * default rendering of this component is used.
* the default rendering of this component is used.
* @see #getDelegateRenderer() * @see #getDelegateRenderer()
* @see #delegateToSubstanceRenderer() * @see #delegateToSubstanceRenderer()
*/ */
public void setDelegateRenderer(DefaultListCellRenderer delegate) { public void setDelegateRenderer(DefaultListCellRenderer delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
/** /**
* Sets the font to use when rendering completion items. * Sets the font to use when rendering completion items.
* *
* @param font * @param font The font to use. If this is <code>null</code>, then the default
* The font to use. If this is <code>null</code>, then * list font is used.
* the default list font is used.
* @see #getDisplayFont() * @see #getDisplayFont()
*/ */
public void setDisplayFont(Font font) { public void setDisplayFont(Font font) {
this.font = font; this.font = font;
} }
/** /**
* Sets the icon to display based off of a completion, falling back to the * Sets the icon to display based off of a completion, falling back to the empty
* empty icon if the completion has no icon. * icon if the completion has no icon.
* *
* @param completion * @param completion The completion to check.
* The completion to check.
* @see #setIconWithDefault(Completion, Icon) * @see #setIconWithDefault(Completion, Icon)
*/ */
protected void setIconWithDefault(Completion completion) { protected void setIconWithDefault(Completion completion) {
setIconWithDefault(completion, getEmptyIcon()); setIconWithDefault(completion, getEmptyIcon());
} }
/** /**
* Sets the icon to display based off of a completion, falling back to a * Sets the icon to display based off of a completion, falling back to a default
* default icon if the completion has no icon. * icon if the completion has no icon.
* *
* @param completion * @param completion The completion to check.
* The completion to check. * @param defaultIcon The icon to use if <code>completion</code> does not
* @param defaultIcon * specify an icon.
* The icon to use if <code>completion</code> does not
* specify an icon.
* @see #setIconWithDefault(Completion) * @see #setIconWithDefault(Completion)
*/ */
protected void setIconWithDefault(Completion completion, Icon defaultIcon) { protected void setIconWithDefault(Completion completion, Icon defaultIcon) {
Icon icon = completion.getIcon(); Icon icon = completion.getIcon();
setIcon(icon != null ? icon : (defaultIcon != null ? defaultIcon : emptyIcon)); setIcon(icon != null ? icon : (defaultIcon != null ? defaultIcon : emptyIcon));
} }
/** /**
* Sets the color to use for function arguments. * Sets the color to use for function arguments.
* *
* @param color * @param color The color to use. This is ignored if <code>null</code>.
* The color to use. This is ignored if <code>null</code>.
* @see #setTypeColor(Color) * @see #setTypeColor(Color)
*/ */
public void setParamColor(Color color) { public void setParamColor(Color color) {
@@ -601,27 +564,25 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
paramColor = Util.getHexString(color); paramColor = Util.getHexString(color);
} }
} }
/** /**
* Sets whether the types of fields and return types of methods are * Sets whether the types of fields and return types of methods are shown in the
* shown in the completion text. * completion text.
* *
* @param show * @param show Whether to show the types.
* Whether to show the types.
* @see #getShowTypes() * @see #getShowTypes()
*/ */
public void setShowTypes(boolean show) { public void setShowTypes(boolean show) {
this.showTypes = show; this.showTypes = show;
} }
/** /**
* Sets the color to use for function/field types. Note that if * Sets the color to use for function/field types. Note that if
* {@link #getShowTypes()} returns <code>false</code>, this property * {@link #getShowTypes()} returns <code>false</code>, this property effectively
* effectively does nothing. * does nothing.
* *
* @param color * @param color The color to use for types. This is ignored if
* The color to use for types. This is ignored if * <code>null</code>.
* <code>null</code>.
* @see #setShowTypes(boolean) * @see #setShowTypes(boolean)
* @see #setParamColor(Color) * @see #setParamColor(Color)
*/ */
@@ -630,7 +591,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
typeColor = Util.getHexString(color); typeColor = Util.getHexString(color);
} }
} }
/** /**
* Overridden to update our delegate, if necessary. * Overridden to update our delegate, if necessary.
*/ */
@@ -13,30 +13,29 @@ import java.util.List;
import javax.swing.AbstractListModel; import javax.swing.AbstractListModel;
/** /**
* A list model implementation that allows the bulk addition of elements. * A list model implementation that allows the bulk addition of elements. This
* This is the only feature missing from <code>DefaultListModel</code> that * is the only feature missing from <code>DefaultListModel</code> that we need.
* we need.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class CompletionListModel extends AbstractListModel { class CompletionListModel extends AbstractListModel {
/** /**
* Container for items in this model. * Container for items in this model.
*/ */
private List<Completion> delegate; private List<Completion> delegate;
/** /**
* Constructor. * Constructor.
*/ */
public CompletionListModel() { public CompletionListModel() {
delegate = new ArrayList<Completion>(); delegate = new ArrayList<Completion>();
} }
/** /**
* Removes all of the elements from this list. The list will * Removes all of the elements from this list. The list will be empty after this
* be empty after this call returns (unless it throws an exception). * call returns (unless it throws an exception).
* *
* @see #setContents(Collection) * @see #setContents(Collection)
*/ */
@@ -47,7 +46,7 @@ class CompletionListModel extends AbstractListModel {
fireIntervalRemoved(this, 0, end); fireIntervalRemoved(this, 0, end);
} }
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -55,7 +54,7 @@ class CompletionListModel extends AbstractListModel {
public Object getElementAt(int index) { public Object getElementAt(int index) {
return delegate.get(index); return delegate.get(index);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -63,12 +62,11 @@ class CompletionListModel extends AbstractListModel {
public int getSize() { public int getSize() {
return delegate.size(); return delegate.size();
} }
/** /**
* Sets the contents of this model. All previous contents are removed. * Sets the contents of this model. All previous contents are removed.
* *
* @param contents * @param contents The new contents of this model.
* The new contents of this model.
*/ */
public void setContents(Collection<Completion> contents) { public void setContents(Collection<Completion> contents) {
clear(); clear();
@@ -25,7 +25,7 @@ import javax.swing.text.JTextComponent;
* @version 1.0 * @version 1.0
*/ */
public interface CompletionProvider { public interface CompletionProvider {
/** /**
* Clears the values used to identify and insert "parameterized completions" * Clears the values used to identify and insert "parameterized completions"
* (e.g. functions or methods). After this method is called, functions and * (e.g. functions or methods). After this method is called, functions and
@@ -34,111 +34,103 @@ public interface CompletionProvider {
* @see #setParameterizedCompletionParams(char, String, char) * @see #setParameterizedCompletionParams(char, String, char)
*/ */
public void clearParameterizedCompletionParams(); public void clearParameterizedCompletionParams();
/** /**
* Returns the text just before the current caret position that could be * Returns the text just before the current caret position that could be the
* the start of something auto-completable. * start of something auto-completable.
* *
* @param comp * @param comp The text component.
* The text component. * @return The text. A return value of <code>null</code> means nothing should be
* @return The text. A return value of <code>null</code> means nothing * auto-completed; a value of an empty string (<code>""</code>) means
* should be auto-completed; a value of an empty string * auto-completion should still be considered (i.e., all possible
* (<code>""</code>) means auto-completion should still be * choices are valid).
* considered (i.e., all possible choices are valid).
*/ */
public String getAlreadyEnteredText(JTextComponent comp); public String getAlreadyEnteredText(JTextComponent comp);
/** /**
* Gets the possible completions for the text component at the current * Gets the possible completions for the text component at the current caret
* caret position. * position.
* *
* @param comp * @param comp The text component.
* The text component. * @return The list of {@link Completion}s. If no completions are available,
* @return The list of {@link Completion}s. If no completions are * this method should return an empty list.
* available, this method should return an empty list.
*/ */
public List<Completion> getCompletions(JTextComponent comp); public List<Completion> getCompletions(JTextComponent comp);
/** /**
* Returns the completions that have been entered at the specified visual * Returns the completions that have been entered at the specified visual
* location. This can be used for tool tips when the user hovers the * location. This can be used for tool tips when the user hovers the mouse over
* mouse over completed text. * completed text.
* *
* @param comp * @param comp The text component.
* The text component. * @param p The position, usually from a <tt>MouseEvent</tt>.
* @param p
* The position, usually from a <tt>MouseEvent</tt>.
* @return The completions, or an empty list if there are none. * @return The completions, or an empty list if there are none.
*/ */
public List<Completion> getCompletionsAt(JTextComponent comp, Point p); public List<Completion> getCompletionsAt(JTextComponent comp, Point p);
/** /**
* Returns the cell renderer for completions returned from this provider. * Returns the cell renderer for completions returned from this provider.
* *
* @return The cell renderer, or <code>null</code> if the default should * @return The cell renderer, or <code>null</code> if the default should be
* be used. * used.
* @see #setListCellRenderer(ListCellRenderer) * @see #setListCellRenderer(ListCellRenderer)
*/ */
public ListCellRenderer getListCellRenderer(); public ListCellRenderer getListCellRenderer();
/** /**
* Returns an object that can return a list of completion choices for * Returns an object that can return a list of completion choices for
* parameters. This is used when a user code-completes a parameterized * parameters. This is used when a user code-completes a parameterized
* completion, such as a function or method. For any parameter to the * completion, such as a function or method. For any parameter to the
* function/method, this object can return possible completions. * function/method, this object can return possible completions.
* *
* @return The parameter choices provider, or <code>null</code> if * @return The parameter choices provider, or <code>null</code> if none is
* none is installed. * installed.
*/ */
public ParameterChoicesProvider getParameterChoicesProvider(); public ParameterChoicesProvider getParameterChoicesProvider();
/** /**
* Returns a list of parameterized completions that have been entered * Returns a list of parameterized completions that have been entered at the
* at the current caret position of a text component (and thus can have * current caret position of a text component (and thus can have their
* their completion choices displayed). * completion choices displayed).
* *
* @param tc * @param tc The text component.
* The text component. * @return The list of {@link ParameterizedCompletion}s. If no completions are
* @return The list of {@link ParameterizedCompletion}s. If no completions * available, this may be <code>null</code>.
* are available, this may be <code>null</code>.
*/ */
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc); public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc);
/** /**
* Returns the text that marks the end of a list of parameters to a * Returns the text that marks the end of a list of parameters to a function or
* function or method. * method.
* *
* @return The text for a parameter list end, for example, * @return The text for a parameter list end, for example, '<code>)</code>'.
* '<code>)</code>'.
* @see #getParameterListStart() * @see #getParameterListStart()
* @see #getParameterListSeparator() * @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char) * @see #setParameterizedCompletionParams(char, String, char)
*/ */
public char getParameterListEnd(); public char getParameterListEnd();
/** /**
* Returns the text that separates parameters to a function or method. * Returns the text that separates parameters to a function or method.
* *
* @return The text that separates parameters, for example, * @return The text that separates parameters, for example, "<code>, </code>".
* "<code>, </code>".
* @see #getParameterListStart() * @see #getParameterListStart()
* @see #getParameterListEnd() * @see #getParameterListEnd()
* @see #setParameterizedCompletionParams(char, String, char) * @see #setParameterizedCompletionParams(char, String, char)
*/ */
public String getParameterListSeparator(); public String getParameterListSeparator();
/** /**
* Returns the text that marks the start of a list of parameters to a * Returns the text that marks the start of a list of parameters to a function
* function or method. * or method.
* *
* @return The text for a parameter list start, for example, * @return The text for a parameter list start, for example, "<code>(</code>".
* "<code>(</code>".
* @see #getParameterListEnd() * @see #getParameterListEnd()
* @see #getParameterListSeparator() * @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char) * @see #setParameterizedCompletionParams(char, String, char)
*/ */
public char getParameterListStart(); public char getParameterListStart();
/** /**
* Returns the parent completion provider. * Returns the parent completion provider.
* *
@@ -146,59 +138,53 @@ public interface CompletionProvider {
* @see #setParent(CompletionProvider) * @see #setParent(CompletionProvider)
*/ */
public CompletionProvider getParent(); public CompletionProvider getParent();
/** /**
* This method is called if auto-activation is enabled in the parent * This method is called if auto-activation is enabled in the parent
* {@link AutoCompletion} after the user types a single character. This * {@link AutoCompletion} after the user types a single character. This provider
* provider should check the text at the current caret position of the * should check the text at the current caret position of the text component,
* text component, and decide whether auto-activation would be appropriate * and decide whether auto-activation would be appropriate here. For example, a
* here. For example, a <code>CompletionProvider</code> for Java might * <code>CompletionProvider</code> for Java might want to return
* want to return <code>true</code> for this method only if the last * <code>true</code> for this method only if the last character typed was a
* character typed was a '<code>.</code>'. * '<code>.</code>'.
* *
* @param tc * @param tc The text component.
* The text component.
* @return Whether auto-activation would be appropriate. * @return Whether auto-activation would be appropriate.
*/ */
public boolean isAutoActivateOkay(JTextComponent tc); public boolean isAutoActivateOkay(JTextComponent tc);
/** /**
* Sets the renderer to use when displaying completion choices. * Sets the renderer to use when displaying completion choices.
* *
* @param r * @param r The renderer to use.
* The renderer to use.
* @see #getListCellRenderer() * @see #getListCellRenderer()
*/ */
public void setListCellRenderer(ListCellRenderer r); public void setListCellRenderer(ListCellRenderer r);
/** /**
* Sets the values used to identify and insert "parameterized completions" * Sets the values used to identify and insert "parameterized completions" (e.g.
* (e.g. functions or methods). If this method isn't called, functions * functions or methods). If this method isn't called, functions and methods
* and methods will not have their parameters auto-completed. * will not have their parameters auto-completed.
* *
* @param listStart * @param listStart The character that marks the beginning of a list of
* The character that marks the beginning of a list of * parameters, such as '<tt>(</tt>' in C or Java.
* parameters, such as '<tt>(</tt>' in C or Java. * @param separator Text that should separate parameters in a parameter list
* @param separator * when one is inserted. For example, "<tt>, </tt>".
* Text that should separate parameters in a parameter * @param listEnd The character that marks the end of a list of parameters,
* list when one is inserted. For example, "<tt>, </tt>". * such as '<tt>)</tt>' in C or Java.
* @param listEnd * @throws IllegalArgumentException If either <tt>listStart</tt> or
* The character that marks the end of a list of parameters, * <tt>listEnd</tt> is not printable ASCII, or
* such as '<tt>)</tt>' in C or Java. * if <tt>separator</tt> is <code>null</code>
* @throws IllegalArgumentException * or an empty string.
* 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() * @see #clearParameterizedCompletionParams()
*/ */
public void setParameterizedCompletionParams(char listStart, String separator, char listEnd); public void setParameterizedCompletionParams(char listStart, String separator, char listEnd);
/** /**
* Sets the parent completion provider. * Sets the parent completion provider.
* *
* @param parent * @param parent The parent provider. <code>null</code> means there will be no
* The parent provider. <code>null</code> means there will * parent provider.
* be no parent provider.
* @see #getParent() * @see #getParent()
*/ */
public void setParent(CompletionProvider parent); public void setParent(CompletionProvider parent);
@@ -182,7 +182,8 @@ public abstract class CompletionProviderBase implements CompletionProvider {
} catch (BadLocationException ble) { // Never happens } catch (BadLocationException ble) { // Never happens
ble.printStackTrace(); 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; import org.xml.sax.helpers.DefaultHandler;
/** /**
* Parser for an XML file describing a procedural language such as C. XML * Parser for an XML file describing a procedural language such as C. XML files
* files will be validated against the <code>CompletionXml.dtd</code> DTD * will be validated against the <code>CompletionXml.dtd</code> DTD found in
* found in this package. * this package.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class CompletionXMLParser extends DefaultHandler { public class CompletionXMLParser extends DefaultHandler {
/** /**
* The completions found after parsing the XML. * The completions found after parsing the XML.
*/ */
@@ -68,36 +68,31 @@ public class CompletionXMLParser extends DefaultHandler {
*/ */
private String funcCompletionType; private String funcCompletionType;
/** /**
* The class loader to use to load custom completion classes, such as * The class loader to use to load custom completion classes, such as the one
* the one defined by {@link #funcCompletionType}. If this is * defined by {@link #funcCompletionType}. If this is <code>null</code>, then a
* <code>null</code>, then a default class loader is used. This field * default class loader is used. This field will usually be <code>null</code>.
* will usually be <code>null</code>.
*/ */
private static ClassLoader DEFAULT_COMPLETION_CLASS_LOADER; private static ClassLoader DEFAULT_COMPLETION_CLASS_LOADER;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider to get completions for.
* The provider to get completions for.
* @see #reset(CompletionProvider) * @see #reset(CompletionProvider)
*/ */
public CompletionXMLParser(CompletionProvider provider) { public CompletionXMLParser(CompletionProvider provider) {
this(provider, null); this(provider, null);
} }
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider to get completions for.
* The provider to get completions for. * @param cl The class loader to use, if necessary, when loading classes
* @param cl * from the XML (custom {@link FunctionCompletion}s, for
* The class loader to use, if necessary, when loading classes * example). This may be <code>null</code> if the default is to
* from the XML (custom {@link FunctionCompletion}s, for * be used, or if the XML does not define specific classes for
* example). * completion types.
* 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) * @see #reset(CompletionProvider)
*/ */
public CompletionXMLParser(CompletionProvider provider, ClassLoader cl) { public CompletionXMLParser(CompletionProvider provider, ClassLoader cl) {
@@ -115,7 +110,7 @@ public class CompletionXMLParser extends DefaultHandler {
paramStartChar = paramEndChar = 0; paramStartChar = paramEndChar = 0;
paramSeparator = null; paramSeparator = null;
} }
/** /**
* Called when character data inside an element is found. * Called when character data inside an element is found.
*/ */
@@ -129,7 +124,7 @@ public class CompletionXMLParser extends DefaultHandler {
returnValDesc.append(ch, start, length); returnValDesc.append(ch, start, length);
} }
} }
private FunctionCompletion createFunctionCompletion() { private FunctionCompletion createFunctionCompletion() {
FunctionCompletion fc = null; FunctionCompletion fc = null;
if (funcCompletionType != null) { if (funcCompletionType != null) {
@@ -163,7 +158,7 @@ public class CompletionXMLParser extends DefaultHandler {
} }
return fc; return fc;
} }
private BasicCompletion createOtherCompletion() { private BasicCompletion createOtherCompletion() {
BasicCompletion bc = new BasicCompletion(provider, name); BasicCompletion bc = new BasicCompletion(provider, name);
if (desc.length() > 0) { if (desc.length() > 0) {
@@ -172,7 +167,7 @@ public class CompletionXMLParser extends DefaultHandler {
} }
return bc; return bc;
} }
private MarkupTagCompletion createMarkupTagCompletion() { private MarkupTagCompletion createMarkupTagCompletion() {
MarkupTagCompletion mc = new MarkupTagCompletion(provider, name); MarkupTagCompletion mc = new MarkupTagCompletion(provider, name);
if (desc.length() > 0) { if (desc.length() > 0) {
@@ -183,7 +178,7 @@ public class CompletionXMLParser extends DefaultHandler {
mc.setDefinedIn(definedIn); mc.setDefinedIn(definedIn);
return mc; return mc;
} }
private VariableCompletion createVariableCompletion() { private VariableCompletion createVariableCompletion() {
VariableCompletion vc = new VariableCompletion(provider, name, returnType); VariableCompletion vc = new VariableCompletion(provider, name, returnType);
if (desc.length() > 0) { if (desc.length() > 0) {
@@ -193,7 +188,7 @@ public class CompletionXMLParser extends DefaultHandler {
vc.setDefinedIn(definedIn); vc.setDefinedIn(definedIn);
return vc; return vc;
} }
/** /**
* Called when an element is closed. * Called when an element is closed.
*/ */
@@ -246,12 +241,12 @@ public class CompletionXMLParser extends DefaultHandler {
} }
} }
} }
@Override @Override
public void error(SAXParseException e) throws SAXException { public void error(SAXParseException e) throws SAXException {
throw e; throw e;
} }
/** /**
* Returns the completions found after parsing the XML. * Returns the completions found after parsing the XML.
* *
@@ -260,7 +255,7 @@ public class CompletionXMLParser extends DefaultHandler {
public List<Completion> getCompletions() { public List<Completion> getCompletions() {
return completions; return completions;
} }
/** /**
* Returns the parameter end character specified. * Returns the parameter end character specified.
* *
@@ -269,7 +264,7 @@ public class CompletionXMLParser extends DefaultHandler {
public char getParamEndChar() { public char getParamEndChar() {
return paramEndChar; return paramEndChar;
} }
/** /**
* Returns the parameter end string specified. * Returns the parameter end string specified.
* *
@@ -278,7 +273,7 @@ public class CompletionXMLParser extends DefaultHandler {
public String getParamSeparator() { public String getParamSeparator() {
return paramSeparator; return paramSeparator;
} }
/** /**
* Returns the parameter start character specified. * Returns the parameter start character specified.
* *
@@ -287,16 +282,15 @@ public class CompletionXMLParser extends DefaultHandler {
public char getParamStartChar() { public char getParamStartChar() {
return paramStartChar; return paramStartChar;
} }
private static final char getSingleChar(String str) { private static final char getSingleChar(String str) {
return str.length() == 1 ? str.charAt(0) : 0; return str.length() == 1 ? str.charAt(0) : 0;
} }
/** /**
* Resets this parser to grab more completions. * Resets this parser to grab more completions.
* *
* @param provider * @param provider The new provider to get completions for.
* The new provider to get completions for.
*/ */
public void reset(CompletionProvider provider) { public void reset(CompletionProvider provider) {
this.provider = provider; this.provider = provider;
@@ -305,28 +299,26 @@ public class CompletionXMLParser extends DefaultHandler {
paramStartChar = paramEndChar = 0; paramStartChar = paramEndChar = 0;
paramSeparator = null; paramSeparator = null;
} }
@Override @Override
public InputSource resolveEntity(String publicID, String systemID) throws SAXException { public InputSource resolveEntity(String publicID, String systemID) throws SAXException {
return new InputSource(getClass().getResourceAsStream("CompletionXml.dtd")); return new InputSource(getClass().getResourceAsStream("CompletionXml.dtd"));
} }
/** /**
* Sets the class loader to use when loading custom classes to use for * Sets the class loader to use when loading custom classes to use for various
* various {@link Completion} types, such as {@link FunctionCompletion}s, * {@link Completion} types, such as {@link FunctionCompletion}s, from XML.
* from XML.
* <p> * <p>
* *
* Users should very rarely have a need to use this method. * Users should very rarely have a need to use this method.
* *
* @param cl * @param cl The class loader to use. If this is <code>null</code>, then a
* The class loader to use. If this is <code>null</code>, then * default is used.
* a default is used.
*/ */
public static void setDefaultCompletionClassLoader(ClassLoader cl) { public static void setDefaultCompletionClassLoader(ClassLoader cl) {
DEFAULT_COMPLETION_CLASS_LOADER = cl; DEFAULT_COMPLETION_CLASS_LOADER = cl;
} }
/** /**
* Called when an element starts. * Called when an element starts.
*/ */
@@ -375,7 +367,7 @@ public class CompletionXMLParser extends DefaultHandler {
} }
} }
} }
@Override @Override
public void warning(SAXParseException e) throws SAXException { public void warning(SAXParseException e) throws SAXException {
throw e; throw e;
@@ -28,29 +28,29 @@ import org.xml.sax.SAXException;
/** /**
* A basic completion provider implementation. This provider has no * A basic completion provider implementation. This provider has no
* understanding of language semantics. It simply checks the text entered up * understanding of language semantics. It simply checks the text entered up to
* to the caret position for a match against known completions. This is all * the caret position for a match against known completions. This is all that is
* that is needed in the majority of cases. * needed in the majority of cases.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class DefaultCompletionProvider extends AbstractCompletionProvider { public class DefaultCompletionProvider extends AbstractCompletionProvider {
protected Segment seg; protected Segment seg;
/** /**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}. * Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}.
*/ */
private String lastCompletionsAtText; private String lastCompletionsAtText;
/** /**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}, * Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}, since this
* since this may be called multiple times in succession (this is usually * may be called multiple times in succession (this is usually called by
* called by <tt>JTextComponent.getToolTipText()</tt>, and if the user * <tt>JTextComponent.getToolTipText()</tt>, and if the user wiggles the mouse
* wiggles the mouse while a tool tip is displayed, this method gets * while a tool tip is displayed, this method gets repeatedly called. It can be
* repeatedly called. It can be costly so we try to speed it up a tad). * costly so we try to speed it up a tad).
*/ */
private List<Completion> lastParameterizedCompletionsAt; private List<Completion> lastParameterizedCompletionsAt;
/** /**
* Constructor. The returned provider will not be aware of any completions. * Constructor. The returned provider will not be aware of any completions.
* *
@@ -59,28 +59,27 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
public DefaultCompletionProvider() { public DefaultCompletionProvider() {
init(); init();
} }
/** /**
* Creates a completion provider that provides completion for a simple * Creates a completion provider that provides completion for a simple list of
* list of words. * words.
* *
* @param words * @param words The words to offer as completion suggestions. If this is
* The words to offer as completion suggestions. If this is * <code>null</code>, no completions will be known.
* <code>null</code>, no completions will be known.
* @see BasicCompletion * @see BasicCompletion
*/ */
public DefaultCompletionProvider(String[] words) { public DefaultCompletionProvider(String[] words) {
init(); init();
addWordCompletions(words); addWordCompletions(words);
} }
/** /**
* Returns the text just before the current caret position that could be * Returns the text just before the current caret position that could be the
* the start of something auto-completable. * start of something auto-completable.
* <p> * <p>
* *
* This method returns all characters before the caret that are matched * This method returns all characters before the caret that are matched by
* by {@link #isValidChar(char)}. * {@link #isValidChar(char)}.
* *
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -108,7 +107,7 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
len = segEnd - start; len = segEnd - start;
return len == 0 ? EMPTY_STRING : new String(seg.array, start, len); return len == 0 ? EMPTY_STRING : new String(seg.array, start, len);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -156,7 +155,7 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
lastCompletionsAtText = null; lastCompletionsAtText = null;
return lastParameterizedCompletionsAt = null; return lastParameterizedCompletionsAt = null;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -212,36 +211,33 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
} }
return list; return list;
} }
/** /**
* Initializes this completion provider. * Initializes this completion provider.
*/ */
protected void init() { protected void init() {
seg = new Segment(); seg = new Segment();
} }
/** /**
* Returns whether the specified character is valid in an auto-completion. * Returns whether the specified character is valid in an auto-completion. The
* The default implementation is equivalent to * default implementation is equivalent to
* "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses * "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses can
* can override this method to change what characters are matched. * override this method to change what characters are matched.
* *
* @param ch * @param ch The character.
* The character.
* @return Whether the character is valid. * @return Whether the character is valid.
*/ */
protected boolean isValidChar(char ch) { protected boolean isValidChar(char ch) {
return Character.isLetterOrDigit(ch) || ch == '_'; return Character.isLetterOrDigit(ch) || ch == '_';
} }
/** /**
* Loads completions from an XML file. The XML should validate against * Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>. * <code>CompletionXml.dtd</code>.
* *
* @param file * @param file An XML file to load from.
* An XML file to load from. * @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/ */
public void loadFromXML(File file) throws IOException { public void loadFromXML(File file) throws IOException {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file)); BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file));
@@ -251,34 +247,28 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
bin.close(); bin.close();
} }
} }
/** /**
* Loads completions from an XML input stream. The XML should validate * Loads completions from an XML input stream. The XML should validate against
* against <code>CompletionXml.dtd</code>. * <code>CompletionXml.dtd</code>.
* *
* @param in * @param in The input stream to read from.
* The input stream to read from. * @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/ */
public void loadFromXML(InputStream in) throws IOException { public void loadFromXML(InputStream in) throws IOException {
loadFromXML(in, null); loadFromXML(in, null);
} }
/** /**
* Loads completions from an XML input stream. The XML should validate * Loads completions from an XML input stream. The XML should validate against
* against <code>CompletionXml.dtd</code>. * <code>CompletionXml.dtd</code>.
* *
* @param in * @param in The input stream to read from.
* The input stream to read from. * @param cl The class loader to use when loading any extra classes defined in
* @param cl * the XML, such as custom {@link FunctionCompletion}s. This may be
* The class loader to use when loading any extra classes defined * <code>null</code> if the default is to be used, or if no custom
* in the XML, such as custom {@link FunctionCompletion}s. This * completions are defined in the XML.
* may be <code>null</code> if the default is to be used, or if * @throws IOException If an IO error occurs.
* no
* custom completions are defined in the XML.
* @throws IOException
* If an IO error occurs.
*/ */
public void loadFromXML(InputStream in, ClassLoader cl) throws IOException { public void loadFromXML(InputStream in, ClassLoader cl) throws IOException {
// long start = System.currentTimeMillis(); // long start = System.currentTimeMillis();
@@ -309,15 +299,13 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
bin.close(); bin.close();
} }
} }
/** /**
* Loads completions from an XML file. The XML should validate against * Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>. * <code>CompletionXml.dtd</code>.
* *
* @param resource * @param resource A resource the current ClassLoader can get to.
* A resource the current ClassLoader can get to. * @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
*/ */
public void loadFromXML(String resource) throws IOException { public void loadFromXML(String resource) throws IOException {
ClassLoader cl = getClass().getClassLoader(); ClassLoader cl = getClass().getClassLoader();
@@ -15,22 +15,22 @@ import javax.swing.JList;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
/** /**
* List cell renderer that delegates to a {@link CompletionProvider}'s * List cell renderer that delegates to a {@link CompletionProvider}'s renderer,
* renderer, if it has one. If it doesn't, it calls into a fallback renderer. * if it has one. If it doesn't, it calls into a fallback renderer. If a
* If a fallback renderer isn't specified, it simply renders * fallback renderer isn't specified, it simply renders
* <code>(({@link Completion})value).toString()</code>. * <code>(({@link Completion})value).toString()</code>.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class DelegatingCellRenderer extends DefaultListCellRenderer { class DelegatingCellRenderer extends DefaultListCellRenderer {
/** /**
* The renderer to fall back on if one isn't specified by a provider. * The renderer to fall back on if one isn't specified by a provider. This is
* This is usually <tt>this</tt>. * usually <tt>this</tt>.
*/ */
private ListCellRenderer fallback; private ListCellRenderer fallback;
/** /**
* Returns the fallback cell renderer. * Returns the fallback cell renderer.
* *
@@ -40,12 +40,13 @@ class DelegatingCellRenderer extends DefaultListCellRenderer {
public ListCellRenderer getFallbackCellRenderer() { public ListCellRenderer getFallbackCellRenderer() {
return fallback; return fallback;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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; Completion c = (Completion) value;
CompletionProvider p = c.getProvider(); CompletionProvider p = c.getProvider();
ListCellRenderer r = p.getListCellRenderer(); ListCellRenderer r = p.getListCellRenderer();
@@ -57,19 +58,18 @@ class DelegatingCellRenderer extends DefaultListCellRenderer {
} }
return fallback.getListCellRendererComponent(list, value, index, selected, hasFocus); return fallback.getListCellRendererComponent(list, value, index, selected, hasFocus);
} }
/** /**
* Sets the fallback cell renderer. * Sets the fallback cell renderer.
* *
* @param fallback * @param fallback The fallback cell renderer. If this is <code>null</code>,
* The fallback cell renderer. If this is * <tt>this</tt> will be used.
* <code>null</code>, <tt>this</tt> will be used.
* @see #getFallbackCellRenderer() * @see #getFallbackCellRenderer()
*/ */
public void setFallbackCellRenderer(ListCellRenderer fallback) { public void setFallbackCellRenderer(ListCellRenderer fallback) {
this.fallback = fallback; this.fallback = fallback;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -14,15 +14,13 @@ package org.fife.ui.autocomplete;
* @see ExternalURLHandler * @see ExternalURLHandler
*/ */
public interface DescWindowCallback { public interface DescWindowCallback {
/** /**
* Callback allowing a new code completion's description to be displayed * Callback allowing a new code completion's description to be displayed in the
* in the description window. * description window.
* *
* @param completion * @param completion The new completion.
* The new completion. * @param anchor The anchor to scroll to, or <code>null</code> if none.
* @param anchor
* The anchor to scroll to, or <code>null</code> if none.
*/ */
public void showSummaryFor(Completion completion, String anchor); public void showSummaryFor(Completion completion, String anchor);
} }
+7 -6
View File
@@ -21,23 +21,24 @@ import javax.swing.Icon;
* @version 1.0 * @version 1.0
*/ */
public class EmptyIcon implements Icon, Serializable { public class EmptyIcon implements Icon, Serializable {
private int size; private int size;
public EmptyIcon(int size) { public EmptyIcon(int size) {
this.size = size; this.size = size;
} }
@Override @Override
public int getIconHeight() { public int getIconHeight() {
return size; return size;
} }
@Override @Override
public int getIconWidth() { public int getIconWidth() {
return size; return size;
} }
@Override @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; import javax.swing.event.HyperlinkEvent;
/** /**
* A callback for when an external URL is clicked in the description window. * A callback for when an external URL is clicked in the description window. If
* If no handler is installed, and if running in Java 6, the system default * no handler is installed, and if running in Java 6, the system default web
* web browser is used to open the URL. If not running Java 6, nothing will * browser is used to open the URL. If not running Java 6, nothing will happen.
* happen. If you want browser support for pre-Java 6 JRE's, you will need * If you want browser support for pre-Java 6 JRE's, you will need to register
* to register one of these callbacks on your {@link AutoCompletion}, and * one of these callbacks on your {@link AutoCompletion}, and open the URL in a
* open the URL in a web browser yourself. * web browser yourself.
* <p> * <p>
* *
* Alternatively, folks implementing robust code completion support for a * Alternatively, folks implementing robust code completion support for a
@@ -27,17 +27,13 @@ import javax.swing.event.HyperlinkEvent;
* @see AutoCompletion#setExternalURLHandler(ExternalURLHandler) * @see AutoCompletion#setExternalURLHandler(ExternalURLHandler)
*/ */
public interface ExternalURLHandler { public interface ExternalURLHandler {
/** /**
* Called when an external URL is clicked in the description window. * Called when an external URL is clicked in the description window.
* *
* @param e * @param e The event containing the hyperlink clicked.
* The event containing the hyperlink clicked. * @param c The completion currently being displayed.
* @param c * @param callback Allows you to display new content in the description window.
* 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); 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 * 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 * completion choices is "large," it does a fast estimate of the preferred width
* width and height of list items. This allows HTML renderers to be used (such * and height of list items. This allows HTML renderers to be used (such as
* as {@link CompletionCellRenderer}), with thousands of completion choices, * {@link CompletionCellRenderer}), with thousands of completion choices, with
* with no performance penalty. With standard BasicListUI subclasses, this can * no performance penalty. With standard BasicListUI subclasses, this can cause
* cause very poor performance <b>each time</b> the list is displayed, which * very poor performance <b>each time</b> the list is displayed, which is bad
* is bad for lists that are repeatedly hidden and re-displayed, such as * for lists that are repeatedly hidden and re-displayed, such as completion
* completion choices. This is all because the calculation to get the * choices. This is all because the calculation to get the preferred size of
* preferred size of each list item, when it is displayed with HTML, is slow. * each list item, when it is displayed with HTML, is slow.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class FastListUI extends BasicListUI { class FastListUI extends BasicListUI {
/** /**
* Whether the selection background was overridden (usually because of * Whether the selection background was overridden (usually because of Nimbus)
* Nimbus) so we know to manually uninstall the color we installed. * so we know to manually uninstall the color we installed.
*/ */
private boolean overriddenBackground; private boolean overriddenBackground;
/** /**
* Whether the selection foreground was overridden (usually because of * Whether the selection foreground was overridden (usually because of Nimbus)
* Nimbus) so we know to manually uninstall the color we installed. * so we know to manually uninstall the color we installed.
*/ */
private boolean overriddenForeground; private boolean overriddenForeground;
/** /**
* If there are more than this many completions in a single list, this * If there are more than this many completions in a single list, this UI will
* UI will estimate the cell width and height needed for each item instead * estimate the cell width and height needed for each item instead of computing
* of computing it, for performance reasons. * it, for performance reasons.
*/ */
private static final int ESTIMATION_THRESHOLD = 200; private static final int ESTIMATION_THRESHOLD = 200;
private Color determineSelectionBackground() { private Color determineSelectionBackground() {
Color c = UIManager.getColor("List.selectionBackground"); Color c = UIManager.getColor("List.selectionBackground");
if (c == null) { if (c == null) {
@@ -68,7 +68,7 @@ class FastListUI extends BasicListUI {
// actually be a ColorUIResource // actually be a ColorUIResource
return new Color(c.getRGB());// new ColorUIResource(c); return new Color(c.getRGB());// new ColorUIResource(c);
} }
private Color determineSelectionForeground() { private Color determineSelectionForeground() {
Color c = UIManager.getColor("List.selectionForeground"); Color c = UIManager.getColor("List.selectionForeground");
if (c == null) { if (c == null) {
@@ -84,12 +84,11 @@ class FastListUI extends BasicListUI {
// may actually be a ColorUIResource // may actually be a ColorUIResource
return new Color(c.getRGB()); return new Color(c.getRGB());
} }
/** /**
* Overridden to ensure we have selection background/foreground colors * Overridden to ensure we have selection background/foreground colors defined,
* defined, even if we're in some weirdo LAF such as Nimbus which doesn't * even if we're in some weirdo LAF such as Nimbus which doesn't define them.
* define them. Since FastListUI extends BasicListUI, we need these values * Since FastListUI extends BasicListUI, we need these values to be defined.
* to be defined.
*/ */
@Override @Override
protected void installDefaults() { protected void installDefaults() {
@@ -103,7 +102,7 @@ class FastListUI extends BasicListUI {
overriddenForeground = true; overriddenForeground = true;
} }
} }
/** /**
* Overridden to work around a Nimbus issue. * Overridden to work around a Nimbus issue.
*/ */
@@ -117,12 +116,11 @@ class FastListUI extends BasicListUI {
list.setSelectionForeground(null); list.setSelectionForeground(null);
} }
} }
/** /**
* Recalculates the cell width and height of each cell in the list. This * Recalculates the cell width and height of each cell in the list. This method
* method is overridden to do a fast estimation if the completion list is * is overridden to do a fast estimation if the completion list is too long, to
* too long, to improve performance for lists with huge amounts of * improve performance for lists with huge amounts of completions.
* completions.
*/ */
@Override @Override
protected void updateLayoutState() { protected void updateLayoutState() {
@@ -20,7 +20,7 @@ import javax.swing.text.Position;
* @version 1.0 * @version 1.0
*/ */
public class FunctionCompletion extends VariableCompletion implements ParameterizedCompletion { public class FunctionCompletion extends VariableCompletion implements ParameterizedCompletion {
/** /**
* Parameters to the function. * Parameters to the function.
*/ */
@@ -33,33 +33,29 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
* Used to improve performance of sorting FunctionCompletions. * Used to improve performance of sorting FunctionCompletions.
*/ */
private String compareString; private String compareString;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The parent provider.
* The parent provider. * @param name The name of this function.
* @param name * @param returnType The return type of this function.
* The name of this function.
* @param returnType
* The return type of this function.
*/ */
public FunctionCompletion(CompletionProvider provider, String name, String returnType) { public FunctionCompletion(CompletionProvider provider, String name, String returnType) {
super(provider, name, returnType); super(provider, name, returnType);
} }
@Override @Override
protected void addDefinitionString(StringBuilder sb) { protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>"); sb.append("<html><b>");
sb.append(getDefinitionString()); sb.append(getDefinitionString());
sb.append("</b>"); sb.append("</b>");
} }
/** /**
* Adds HTML describing the parameters to this function to a buffer. * Adds HTML describing the parameters to this function to a buffer.
* *
* @param sb * @param sb The buffer to append to.
* The buffer to append to.
*/ */
protected void addParameters(StringBuilder sb) { protected void addParameters(StringBuilder sb) {
// TODO: Localize me // TODO: Localize me
@@ -86,12 +82,11 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
sb.append("</td></tr></table></center><br><br>"); sb.append("</td></tr></table></center><br><br>");
} }
} }
/** /**
* Overridden to compare methods by their comparison strings. * Overridden to compare methods by their comparison strings.
* *
* @param c2 * @param c2 A <code>Completion</code> to compare to.
* A <code>Completion</code> to compare to.
* @return The sort order. * @return The sort order.
*/ */
@Override @Override
@@ -106,7 +101,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
} }
return rc; return rc;
} }
/** /**
* Returns a string used to compare this method completion to another. * 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() { private String getCompareString() {
/* /*
* This string compares the following parts of methods in this order, * This string compares the following parts of methods in this order, to
* to optimize sort order in completion lists. * optimize sort order in completion lists. 1. First, by name 2. Next, by number
* 1. First, by name * of parameters. 3. Finally, by parameter type.
* 2. Next, by number of parameters.
* 3. Finally, by parameter type.
*/ */
if (compareString == null) { if (compareString == null) {
StringBuilder sb = new StringBuilder(getName()); StringBuilder sb = new StringBuilder(getName());
@@ -139,10 +132,10 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
} }
return compareString; return compareString;
} }
/** /**
* Returns the "definition string" for this function completion. For * Returns the "definition string" for this function completion. For example,
* example, for the C "<code>printf</code>" function, this would return * for the C "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>". * "<code>int printf(const char *, ...)</code>".
* *
* @return The definition string. * @return The definition string.
@@ -186,7 +179,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
} }
return sb.toString(); return sb.toString();
} }
@Override @Override
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces) { public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces) {
ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo(); ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo();
@@ -238,7 +231,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
info.setTextToInsert(sb.toString()); info.setTextToInsert(sb.toString());
return info; return info;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -246,7 +239,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public Parameter getParam(int index) { public Parameter getParam(int index) {
return params.get(index); return params.get(index);
} }
/** /**
* Returns the number of parameters to this function. * Returns the number of parameters to this function.
* *
@@ -257,7 +250,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public int getParamCount() { public int getParamCount() {
return params == null ? 0 : params.size(); return params == null ? 0 : params.size();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -265,12 +258,11 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public boolean getShowParameterToolTip() { public boolean getShowParameterToolTip() {
return true; return true;
} }
/** /**
* Returns the text to insert for a parameter. * Returns the text to insert for a parameter.
* *
* @param param * @param param The parameter.
* The parameter.
* @return The text. * @return The text.
*/ */
private String getParamText(ParameterizedCompletion.Parameter param) { private String getParamText(ParameterizedCompletion.Parameter param) {
@@ -283,7 +275,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
} }
return text; return text;
} }
/** /**
* Returns the description of the return value of this function. * Returns the description of the return value of this function.
* *
@@ -293,7 +285,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
public String getReturnValueDescription() { public String getReturnValueDescription() {
return returnValDesc; return returnValDesc;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -308,7 +300,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
possiblyAddDefinedIn(sb); possiblyAddDefinedIn(sb);
return sb.toString(); return sb.toString();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -320,13 +312,12 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
} }
return text; return text;
} }
/** /**
* Sets the parameters to this function. * Sets the parameters to this function.
* *
* @param params * @param params The parameters. This should be a list of
* The parameters. This should be a list of * {@link ParameterizedCompletion.Parameter}s.
* {@link ParameterizedCompletion.Parameter}s.
* @see #getParam(int) * @see #getParam(int)
* @see #getParamCount() * @see #getParamCount()
*/ */
@@ -336,12 +327,11 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
this.params = new ArrayList<Parameter>(params); this.params = new ArrayList<Parameter>(params);
} }
} }
/** /**
* Sets the description of the return value of this function. * Sets the description of the return value of this function.
* *
* @param desc * @param desc The description.
* The description.
* @see #getReturnValueDescription() * @see #getReturnValueDescription()
*/ */
public void setReturnValueDescription(String desc) { public void setReturnValueDescription(String desc) {
@@ -33,13 +33,13 @@ import org.fife.ui.rtextarea.ToolTipSupplier;
* <li>A documentation comment</li> * <li>A documentation comment</li>
* </ul> * </ul>
* *
* This allows for different completion choices in comments than in code, * This allows for different completion choices in comments than in code, for
* for example. * example.
* <p> * <p>
* *
* This provider also implements the * This provider also implements the
* <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it * <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it to
* to display tooltips for completion choices. Thus the standard * display tooltips for completion choices. Thus the standard
* {@link VariableCompletion} and {@link FunctionCompletion} completions should * {@link VariableCompletion} and {@link FunctionCompletion} completions should
* be able to display tooltips with the variable declaration or function * be able to display tooltips with the variable declaration or function
* definition (provided the <tt>RSyntaxTextArea</tt> was registered with the * definition (provided the <tt>RSyntaxTextArea</tt> was registered with the
@@ -49,10 +49,9 @@ import org.fife.ui.rtextarea.ToolTipSupplier;
* @version 1.0 * @version 1.0
*/ */
public class LanguageAwareCompletionProvider extends CompletionProviderBase implements ToolTipSupplier { public class LanguageAwareCompletionProvider extends CompletionProviderBase implements ToolTipSupplier {
/** /**
* The provider to use when no provider is assigned to a particular token * The provider to use when no provider is assigned to a particular token type.
* type.
*/ */
private CompletionProvider defaultProvider; private CompletionProvider defaultProvider;
/** /**
@@ -67,41 +66,40 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
* The provider to use while in documentation comments. * The provider to use while in documentation comments.
*/ */
private CompletionProvider docCommentCompletionProvider; private CompletionProvider docCommentCompletionProvider;
/** /**
* Constructor subclasses can use when they don't have their default * Constructor subclasses can use when they don't have their default provider
* provider created at construction time. They should call * created at construction time. They should call
* {@link #setDefaultCompletionProvider(CompletionProvider)} in this * {@link #setDefaultCompletionProvider(CompletionProvider)} in this
* constructor. * constructor.
*/ */
protected LanguageAwareCompletionProvider() {} protected LanguageAwareCompletionProvider() {
}
/** /**
* Constructor. * Constructor.
* *
* @param defaultProvider * @param defaultProvider The provider to use when no provider is assigned to a
* The provider to use when no provider is assigned * particular token type. This cannot be
* to a particular token type. This cannot be <code>null</code>. * <code>null</code>.
*/ */
public LanguageAwareCompletionProvider(CompletionProvider defaultProvider) { public LanguageAwareCompletionProvider(CompletionProvider defaultProvider) {
setDefaultCompletionProvider(defaultProvider); setDefaultCompletionProvider(defaultProvider);
} }
/** /**
* Calling this method will result in an * Calling this method will result in an {@link UnsupportedOperationException}
* {@link UnsupportedOperationException} being thrown. To set the * being thrown. To set the parameter completion parameters, do so on the
* parameter completion parameters, do so on the provider returned by * provider returned by {@link #getDefaultCompletionProvider()}.
* {@link #getDefaultCompletionProvider()}.
* *
* @throws UnsupportedOperationException * @throws UnsupportedOperationException Always.
* Always.
* @see #setParameterizedCompletionParams(char, String, char) * @see #setParameterizedCompletionParams(char, String, char)
*/ */
@Override @Override
public void clearParameterizedCompletionParams() { public void clearParameterizedCompletionParams() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -113,7 +111,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
CompletionProvider provider = getProviderFor(comp); CompletionProvider provider = getProviderFor(comp);
return provider != null ? provider.getAlreadyEnteredText(comp) : null; return provider != null ? provider.getAlreadyEnteredText(comp) : null;
} }
/** /**
* Returns the completion provider to use for comments. * Returns the completion provider to use for comments.
* *
@@ -123,7 +121,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getCommentCompletionProvider() { public CompletionProvider getCommentCompletionProvider() {
return commentCompletionProvider; return commentCompletionProvider;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -131,14 +129,12 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public List<Completion> getCompletionsAt(JTextComponent tc, Point p) { public List<Completion> getCompletionsAt(JTextComponent tc, Point p) {
return defaultProvider == null ? null : defaultProvider.getCompletionsAt(tc, p); return defaultProvider == null ? null : defaultProvider.getCompletionsAt(tc, p);
} }
/** /**
* Does the dirty work of creating a list of completions. * Does the dirty work of creating a list of completions.
* *
* @param comp * @param comp The text component to look in.
* The text component to look in. * @return The list of possible completions, or an empty list if there are none.
* @return The list of possible completions, or an empty list if there
* are none.
*/ */
@Override @Override
protected List<Completion> getCompletionsImpl(JTextComponent comp) { protected List<Completion> getCompletionsImpl(JTextComponent comp) {
@@ -150,10 +146,10 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
} }
return Collections.emptyList(); return Collections.emptyList();
} }
/** /**
* Returns the completion provider used when one isn't defined for a * Returns the completion provider used when one isn't defined for a particular
* particular token type. * token type.
* *
* @return The completion provider to use. * @return The completion provider to use.
* @see #setDefaultCompletionProvider(CompletionProvider) * @see #setDefaultCompletionProvider(CompletionProvider)
@@ -161,7 +157,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getDefaultCompletionProvider() { public CompletionProvider getDefaultCompletionProvider() {
return defaultProvider; return defaultProvider;
} }
/** /**
* Returns the completion provider to use for documentation comments. * Returns the completion provider to use for documentation comments.
* *
@@ -171,7 +167,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getDocCommentCompletionProvider() { public CompletionProvider getDocCommentCompletionProvider() {
return docCommentCompletionProvider; return docCommentCompletionProvider;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -183,7 +179,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
CompletionProvider provider = getProviderFor(tc); CompletionProvider provider = getProviderFor(tc);
return provider == defaultProvider ? provider.getParameterizedCompletions(tc) : null; return provider == defaultProvider ? provider.getParameterizedCompletions(tc) : null;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -191,7 +187,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public char getParameterListEnd() { public char getParameterListEnd() {
return defaultProvider.getParameterListEnd(); return defaultProvider.getParameterListEnd();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -199,7 +195,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public String getParameterListSeparator() { public String getParameterListSeparator() {
return defaultProvider.getParameterListSeparator(); return defaultProvider.getParameterListSeparator();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -207,13 +203,12 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public char getParameterListStart() { public char getParameterListStart() {
return defaultProvider.getParameterListStart(); return defaultProvider.getParameterListStart();
} }
/** /**
* Returns the completion provider to use at the current caret position in * Returns the completion provider to use at the current caret position in a
* a text component. * text component.
* *
* @param comp * @param comp The text component to check.
* The text component to check.
* @return The completion provider to use. * @return The completion provider to use.
*/ */
private CompletionProvider getProviderFor(JTextComponent comp) { private CompletionProvider getProviderFor(JTextComponent comp) {
@@ -242,15 +237,15 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
type = doc.getClosestStandardTokenTypeForInternalType(type); type = doc.getClosestStandardTokenTypeForInternalType(type);
} }
switch (type) { switch (type) {
case Token.ERROR_STRING_DOUBLE: case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider(); return getStringCompletionProvider();
case Token.COMMENT_EOL: case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE: case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider(); return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION: case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider(); return getDocCommentCompletionProvider();
default: default:
return getDefaultCompletionProvider(); return getDefaultCompletionProvider();
} }
} }
// FIXME: This isn't always a safe assumption. // FIXME: This isn't always a safe assumption.
@@ -261,27 +256,27 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
return getDefaultCompletionProvider(); return getDefaultCompletionProvider();
} }
switch (curToken.getType()) { switch (curToken.getType()) {
case Token.LITERAL_STRING_DOUBLE_QUOTE: case Token.LITERAL_STRING_DOUBLE_QUOTE:
case Token.ERROR_STRING_DOUBLE: case Token.ERROR_STRING_DOUBLE:
return getStringCompletionProvider(); return getStringCompletionProvider();
case Token.COMMENT_EOL: case Token.COMMENT_EOL:
case Token.COMMENT_MULTILINE: case Token.COMMENT_MULTILINE:
return getCommentCompletionProvider(); return getCommentCompletionProvider();
case Token.COMMENT_DOCUMENTATION: case Token.COMMENT_DOCUMENTATION:
return getDocCommentCompletionProvider(); return getDocCommentCompletionProvider();
case Token.NULL: case Token.NULL:
case Token.WHITESPACE: case Token.WHITESPACE:
case Token.IDENTIFIER: case Token.IDENTIFIER:
case Token.VARIABLE: case Token.VARIABLE:
case Token.PREPROCESSOR: case Token.PREPROCESSOR:
case Token.DATA_TYPE: case Token.DATA_TYPE:
case Token.FUNCTION: case Token.FUNCTION:
case Token.OPERATOR: case Token.OPERATOR:
return getDefaultCompletionProvider(); return getDefaultCompletionProvider();
} }
return null; // In a token type we can't auto-complete from. return null; // In a token type we can't auto-complete from.
} }
/** /**
* Returns the completion provider to use for strings. * Returns the completion provider to use for strings.
* *
@@ -291,7 +286,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
public CompletionProvider getStringCompletionProvider() { public CompletionProvider getStringCompletionProvider() {
return stringCompletionProvider; return stringCompletionProvider;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -300,24 +295,22 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
CompletionProvider provider = getProviderFor(tc); CompletionProvider provider = getProviderFor(tc);
return provider != null ? provider.isAutoActivateOkay(tc) : false; return provider != null ? provider.isAutoActivateOkay(tc) : false;
} }
/** /**
* Sets the comment completion provider. * Sets the comment completion provider.
* *
* @param provider * @param provider The provider to use in comments.
* The provider to use in comments.
* @see #getCommentCompletionProvider() * @see #getCommentCompletionProvider()
*/ */
public void setCommentCompletionProvider(CompletionProvider provider) { public void setCommentCompletionProvider(CompletionProvider provider) {
this.commentCompletionProvider = provider; this.commentCompletionProvider = provider;
} }
/** /**
* Sets the default completion provider. * Sets the default completion provider.
* *
* @param provider * @param provider The provider to use when no provider is assigned to a
* The provider to use when no provider is assigned to a * particular token type. This cannot be <code>null</code>.
* particular token type. This cannot be <code>null</code>.
* @see #getDefaultCompletionProvider() * @see #getDefaultCompletionProvider()
*/ */
public void setDefaultCompletionProvider(CompletionProvider provider) { public void setDefaultCompletionProvider(CompletionProvider provider) {
@@ -326,59 +319,53 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
} }
this.defaultProvider = provider; this.defaultProvider = provider;
} }
/** /**
* Sets the documentation comment completion provider. * Sets the documentation comment completion provider.
* *
* @param provider * @param provider The provider to use in comments.
* The provider to use in comments.
* @see #getDocCommentCompletionProvider() * @see #getDocCommentCompletionProvider()
*/ */
public void setDocCommentCompletionProvider(CompletionProvider provider) { public void setDocCommentCompletionProvider(CompletionProvider provider) {
this.docCommentCompletionProvider = provider; this.docCommentCompletionProvider = provider;
} }
/** /**
* Calling this method will result in an * Calling this method will result in an {@link UnsupportedOperationException}
* {@link UnsupportedOperationException} being thrown. To set the * being thrown. To set the parameter completion parameters, do so on the
* parameter completion parameters, do so on the provider returned by * provider returned by {@link #getDefaultCompletionProvider()}.
* {@link #getDefaultCompletionProvider()}.
* *
* @throws UnsupportedOperationException * @throws UnsupportedOperationException Always.
* Always.
* @see #clearParameterizedCompletionParams() * @see #clearParameterizedCompletionParams()
*/ */
@Override @Override
public void setParameterizedCompletionParams(char listStart, String separator, char listEnd) { public void setParameterizedCompletionParams(char listStart, String separator, char listEnd) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
/** /**
* Sets the completion provider to use while in a string. * Sets the completion provider to use while in a string.
* *
* @param provider * @param provider The provider to use.
* The provider to use.
* @see #getStringCompletionProvider() * @see #getStringCompletionProvider()
*/ */
public void setStringCompletionProvider(CompletionProvider provider) { public void setStringCompletionProvider(CompletionProvider provider) {
stringCompletionProvider = provider; stringCompletionProvider = provider;
} }
/** /**
* Returns the tool tip to display for a mouse event. * Returns the tool tip to display for a mouse event.
* <p> * <p>
* *
* For this method to be called, the <tt>RSyntaxTextArea</tt> must be * For this method to be called, the <tt>RSyntaxTextArea</tt> must be registered
* registered with the <tt>javax.swing.ToolTipManager</tt> like so: * with the <tt>javax.swing.ToolTipManager</tt> like so:
* *
* <pre> * <pre>
* ToolTipManager.sharedInstance().registerComponent(textArea); * ToolTipManager.sharedInstance().registerComponent(textArea);
* </pre> * </pre>
* *
* @param textArea * @param textArea The text area.
* The text area. * @param e The mouse event.
* @param e
* The mouse event.
* @return The tool tip text, or <code>null</code> if none. * @return The tool tip text, or <code>null</code> if none.
*/ */
@Override @Override
@@ -8,14 +8,13 @@ package org.fife.ui.autocomplete;
import java.net.URL; import java.net.URL;
/** /**
* Possibly redirects one URL to another. Useful if you want "external" URL's * Possibly redirects one URL to another. Useful if you want "external" URL's in
* in code completion documentation to point to a local copy instead, for * code completion documentation to point to a local copy instead, for example.
* example.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public interface LinkRedirector { public interface LinkRedirector {
public URL possiblyRedirect(URL original); public URL possiblyRedirect(URL original);
} }
@@ -19,7 +19,7 @@ import org.fife.ui.autocomplete.ParameterizedCompletion.Parameter;
* @version 1.0 * @version 1.0
*/ */
public class MarkupTagCompletion extends AbstractCompletion { public class MarkupTagCompletion extends AbstractCompletion {
private String name; private String name;
private String desc; private String desc;
private String definedIn; private String definedIn;
@@ -27,25 +27,22 @@ public class MarkupTagCompletion extends AbstractCompletion {
* Attributes of the tag. * Attributes of the tag.
*/ */
private List<Parameter> attrs; private List<Parameter> attrs;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The parent provider instance.
* The parent provider instance. * @param name The name of the tag.
* @param name
* The name of the tag.
*/ */
public MarkupTagCompletion(CompletionProvider provider, String name) { public MarkupTagCompletion(CompletionProvider provider, String name) {
super(provider); super(provider);
this.name = name; this.name = name;
} }
/** /**
* Adds HTML describing the attributes of this tag to a buffer. * Adds HTML describing the attributes of this tag to a buffer.
* *
* @param sb * @param sb The buffer to append to.
* The buffer to append to.
*/ */
protected void addAttributes(StringBuilder sb) { protected void addAttributes(StringBuilder sb) {
// TODO: Localize me. // TODO: Localize me.
@@ -67,11 +64,11 @@ public class MarkupTagCompletion extends AbstractCompletion {
sb.append("</td></tr></table></center><br><br>"); sb.append("</td></tr></table></center><br><br>");
} }
} }
protected void addDefinitionString(StringBuilder sb) { protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>").append(name).append("</b>"); sb.append("<html><b>").append(name).append("</b>");
} }
/** /**
* Returns all attributes of this tag. * Returns all attributes of this tag.
* *
@@ -82,19 +79,18 @@ public class MarkupTagCompletion extends AbstractCompletion {
public List<Parameter> getAttributes() { public List<Parameter> getAttributes() {
return attrs; return attrs;
} }
/** /**
* Returns the specified {@link ParameterizedCompletion.Parameter}. * Returns the specified {@link ParameterizedCompletion.Parameter}.
* *
* @param index * @param index The index of the attribute to retrieve.
* The index of the attribute to retrieve.
* @return The attribute. * @return The attribute.
* @see #getAttributeCount() * @see #getAttributeCount()
*/ */
public Parameter getAttribute(int index) { public Parameter getAttribute(int index) {
return attrs.get(index); return attrs.get(index);
} }
/** /**
* Returns the number of attributes of this tag. * Returns the number of attributes of this tag.
* *
@@ -104,7 +100,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
public int getAttributeCount() { public int getAttributeCount() {
return attrs == null ? 0 : attrs.size(); return attrs == null ? 0 : attrs.size();
} }
/** /**
* Returns where this variable is defined. * Returns where this variable is defined.
* *
@@ -114,19 +110,17 @@ public class MarkupTagCompletion extends AbstractCompletion {
public String getDefinedIn() { public String getDefinedIn() {
return definedIn; return definedIn;
} }
/** /**
* Returns a short description of this variable. This should be an * Returns a short description of this variable. This should be an HTML snippet.
* HTML snippet.
* *
* @return A short description of this variable. This may be * @return A short description of this variable. This may be <code>null</code>.
* <code>null</code>.
* @see #setDescription(String) * @see #setDescription(String)
*/ */
public String getDescription() { public String getDescription() {
return desc; return desc;
} }
/** /**
* Returns the name of this tag. * Returns the name of this tag.
* *
@@ -135,7 +129,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
public String getName() { public String getName() {
return name; return name;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -143,7 +137,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
public String getReplacementText() { public String getReplacementText() {
return getName(); return getName();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -156,13 +150,12 @@ public class MarkupTagCompletion extends AbstractCompletion {
possiblyAddDefinedIn(sb); possiblyAddDefinedIn(sb);
return sb.toString(); return sb.toString();
} }
/** /**
* Adds some HTML describing where this variable is defined, if this * Adds some HTML describing where this variable is defined, if this information
* information is known. * is known.
* *
* @param sb * @param sb The buffer to append to.
* The buffer to append to.
*/ */
protected void possiblyAddDefinedIn(StringBuilder sb) { protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn != null) { if (definedIn != null) {
@@ -170,13 +163,11 @@ public class MarkupTagCompletion extends AbstractCompletion {
sb.append(" <em>").append(definedIn).append("</em>"); sb.append(" <em>").append(definedIn).append("</em>");
} }
} }
/** /**
* Adds the description text as HTML to a buffer, if a description is * Adds the description text as HTML to a buffer, if a description is defined.
* defined.
* *
* @param sb * @param sb The buffer to append to.
* The buffer to append to.
*/ */
protected void possiblyAddDescription(StringBuilder sb) { protected void possiblyAddDescription(StringBuilder sb) {
if (desc != null) { if (desc != null) {
@@ -185,36 +176,31 @@ public class MarkupTagCompletion extends AbstractCompletion {
sb.append("<br><br><br>"); sb.append("<br><br><br>");
} }
} }
/** /**
* Sets where this variable is defined. * Sets where this variable is defined.
* *
* @param definedIn * @param definedIn Where this variable is defined.
* Where this variable is defined.
* @see #getDefinedIn() * @see #getDefinedIn()
*/ */
public void setDefinedIn(String definedIn) { public void setDefinedIn(String definedIn) {
this.definedIn = definedIn; this.definedIn = definedIn;
} }
/** /**
* Sets the short description of this tag. This should be an * Sets the short description of this tag. This should be an HTML snippet.
* HTML snippet.
* *
* @param desc * @param desc A short description of this tag. This may be <code>null</code>.
* A short description of this tag. This may be
* <code>null</code>.
* @see #getDescription() * @see #getDescription()
*/ */
public void setDescription(String desc) { public void setDescription(String desc) {
this.desc = desc; this.desc = desc;
} }
/** /**
* Sets the attributes of this tag. * Sets the attributes of this tag.
* *
* @param attrs * @param attrs The attributes.
* The attributes.
* @see #getAttribute(int) * @see #getAttribute(int)
* @see #getAttributeCount() * @see #getAttributeCount()
*/ */
@@ -19,40 +19,39 @@ import javax.swing.text.Position;
import javax.swing.text.View; import javax.swing.text.View;
/** /**
* Highlight painter that draws an outline around the text. This is used to * Highlight painter that draws an outline around the text. This is used to draw
* draw bounds around function/method parameters. * bounds around function/method parameters.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
/* /*
* NOTE: Whenever you see text like "Workaround for Java Highlight issues", * NOTE: Whenever you see text like "Workaround for Java Highlight issues", this
* this is because highlighted text in a JTextComponent gets "pushed" forward * is because highlighted text in a JTextComponent gets "pushed" forward when
* when the caret is at the Highlight's start, when we need it to instead get * 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 * 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 * 1 char too long (1 char earlier than where it should really start), but only
* paint the Highlight from the 2nd char on. * paint the Highlight from the 2nd char on.
*/ */
class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter { class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter {
/** /**
* DefaultHighlightPainter doesn't allow changing color, so we must cache * DefaultHighlightPainter doesn't allow changing color, so we must cache ours
* ours here. * here.
*/ */
private Color color; private Color color;
/** /**
* Constructor. * Constructor.
* *
* @param color * @param color The color to draw the bounding boxes with. This cannot be
* The color to draw the bounding boxes with. This cannot * <code>null</code>.
* be <code>null</code>.
*/ */
public OutlineHighlightPainter(Color color) { public OutlineHighlightPainter(Color color) {
super(color); super(color);
setColor(color); setColor(color);
} }
/** /**
* Returns the color to paint bounding boxes with. * Returns the color to paint bounding boxes with.
* *
@@ -63,7 +62,7 @@ class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter
public Color getColor() { public Color getColor() {
return color; return color;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -110,12 +109,11 @@ class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter
return null; return null;
} }
} }
/** /**
* Sets the color to paint the bounding boxes with. * Sets the color to paint the bounding boxes with.
* *
* @param color * @param color The new color. This cannot be <code>null</code>.
* The new color. This cannot be <code>null</code>.
* @see #getColor() * @see #getColor()
*/ */
public void setColor(Color color) { public void setColor(Color color) {
@@ -12,25 +12,23 @@ import java.util.List;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
/** /**
* Provides completions for a {@link ParameterizedCompletion}'s parameters. * Provides completions for a {@link ParameterizedCompletion}'s parameters. So,
* So, for example, if the user code-completes a function or method, if * for example, if the user code-completes a function or method, if a
* a <code>ParameterChoicesProvider</code> is installed, it can return possible * <code>ParameterChoicesProvider</code> is installed, it can return possible
* completions for the parameters to that function or method. * completions for the parameters to that function or method.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public interface ParameterChoicesProvider { public interface ParameterChoicesProvider {
/** /**
* Returns a list of choices for a specific parameter. * Returns a list of choices for a specific parameter.
* *
* @param tc * @param tc The text component.
* The text component. * @param param The currently focused parameter.
* @param param * @return The list of parameters. This may be <code>null</code> for "no
* The currently focused parameter. * parameters," but might also be an empty list.
* @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); public List<Completion> getParameterChoices(JTextComponent tc, ParameterizedCompletion.Parameter param);
} }
@@ -15,26 +15,25 @@ import javax.swing.text.JTextComponent;
* @version 1.0 * @version 1.0
*/ */
public interface ParameterizedCompletion extends Completion { public interface ParameterizedCompletion extends Completion {
/** /**
* Returns the "definition string" for this completion. For example, * Returns the "definition string" for this completion. For example, for the C
* for the C "<code>printf</code>" function, this would return * "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>". * "<code>int printf(const char *, ...)</code>".
* *
* @return The definition string. * @return The definition string.
*/ */
public String getDefinitionString(); public String getDefinitionString();
/** /**
* Returns the specified {@link Parameter}. * Returns the specified {@link Parameter}.
* *
* @param index * @param index The index of the parameter to retrieve.
* The index of the parameter to retrieve.
* @return The parameter. * @return The parameter.
* @see #getParamCount() * @see #getParamCount()
*/ */
public Parameter getParam(int index); public Parameter getParam(int index);
/** /**
* Returns the number of parameters this completion takes. * Returns the number of parameters this completion takes.
* *
@@ -42,82 +41,73 @@ public interface ParameterizedCompletion extends Completion {
* @see #getParam(int) * @see #getParam(int)
*/ */
public int getParamCount(); public int getParamCount();
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces); public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces);
/** /**
* Returns whether a tool tip displaying assistance for each parameter * Returns whether a tool tip displaying assistance for each parameter while it
* while it is being edited is appropriate for this completion. * is being edited is appropriate for this completion.
* *
* @return Whether the tool tip is appropriate to display. * @return Whether the tool tip is appropriate to display.
*/ */
public boolean getShowParameterToolTip(); public boolean getShowParameterToolTip();
/** /**
* A parameter passed to a parameterized {@link Completion}. * A parameter passed to a parameterized {@link Completion}.
*/ */
public static class Parameter { public static class Parameter {
private String name; private String name;
private Object type; private Object type;
private String desc; private String desc;
private boolean isEndParam; private boolean isEndParam;
/** /**
* Constructor. * Constructor.
* *
* @param type * @param type The type of this parameter. This may be <code>null</code> for
* The type of this parameter. This may be * languages without specific types, dynamic typing, etc. Usually
* <code>null</code> for languages without specific types, * you'll pass a String for this value, but you may pass any object
* dynamic typing, etc. Usually you'll pass a String for this * representing a type in your language, as long as its
* value, but you may pass any object representing a type in * <code>toString()</code> method returns a string representation of
* your language, as long as its <code>toString()</code> * the type.
* method * @param name The name of the parameter.
* returns a string representation of the type.
* @param name
* The name of the parameter.
*/ */
public Parameter(Object type, String name) { public Parameter(Object type, String name) {
this(type, name, false); this(type, name, false);
} }
/** /**
* Constructor. * Constructor.
* *
* @param type * @param type The type of this parameter. This may be <code>null</code> for
* The type of this parameter. This may be * languages without specific types, dynamic typing, etc.
* <code>null</code> for languages without specific types, * Usually you'll pass a String for this value, but you may pass
* dynamic typing, etc. Usually you'll pass a String for this * any object representing a type in your language, as long as
* value, but you may pass any object representing a type in * its <code>toString()</code> method returns a string
* your language, as long as its <code>toString()</code> * representation of the type.
* method * @param name The name of the parameter.
* returns a string representation of the type. * @param endParam Whether this parameter is an "ending parameter;" that is,
* @param name * whether this parameter is at a logical "ending point" in the
* The name of the parameter. * completion text. If the user types in a parameter that is an
* @param endParam * ending point, parameter completion mode terminates. Set this
* Whether this parameter is an "ending parameter;" * to <code>true</code> for a trailing parameter after a
* that is, whether this parameter is at a logical "ending * function call's closing ')', for example.
* 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) { public Parameter(Object type, String name, boolean endParam) {
this.name = name; this.name = name;
this.type = type; this.type = type;
this.isEndParam = endParam; this.isEndParam = endParam;
} }
public String getDescription() { public String getDescription() {
return desc; return desc;
} }
public String getName() { public String getName() {
return name; return name;
} }
/** /**
* Returns the type of this parameter, as a string. * Returns the type of this parameter, as a string.
* *
@@ -126,7 +116,7 @@ public interface ParameterizedCompletion extends Completion {
public String getType() { public String getType() {
return type == null ? null : type.toString(); return type == null ? null : type.toString();
} }
/** /**
* Returns the object used to describe the type of this parameter. * Returns the object used to describe the type of this parameter.
* *
@@ -135,22 +125,21 @@ public interface ParameterizedCompletion extends Completion {
public Object getTypeObject() { public Object getTypeObject() {
return type; return type;
} }
/** /**
* @return Whether this parameter is an "ending parameter;" * @return Whether this parameter is an "ending parameter;" that is, whether
* that is, whether this parameter is at a logical "ending * this parameter is at a logical "ending point" in the completion text.
* point" in the completion text. If the user types in a * If the user types in a parameter that is an ending point, parameter
* parameter that is an ending point, parameter completion mode * completion mode terminates.
* terminates.
*/ */
public boolean isEndParam() { public boolean isEndParam() {
return isEndParam; return isEndParam;
} }
public void setDescription(String desc) { public void setDescription(String desc) {
this.desc = desc; this.desc = desc;
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -28,17 +28,17 @@ import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator; import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/** /**
* A small popup window offering a list of likely choices for a parameter * A small popup window offering a list of likely choices for a parameter when
* when the user has code-completed a parameterized completion. For example, * the user has code-completed a parameterized completion. For example, if they
* if they have just code-completed the C function "<code>fprintf</code>", * have just code-completed the C function "<code>fprintf</code>", when entering
* when entering the file name, this popup might display all local variables * the file name, this popup might display all local variables of type
* of type "<code>char *</code>". * "<code>char *</code>".
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class ParameterizedCompletionChoicesWindow extends JWindow { public class ParameterizedCompletionChoicesWindow extends JWindow {
/** /**
* The parent AutoCompletion instance. * The parent AutoCompletion instance.
*/ */
@@ -60,22 +60,20 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
*/ */
private JScrollPane sp; private JScrollPane sp;
/** /**
* Comparator used to sort completions by their relevance before sorting * Comparator used to sort completions by their relevance before sorting them
* them lexicographically. * lexicographically.
*/ */
private static final Comparator<Completion> sortByRelevanceComparator = new SortByRelevanceComparator(); private static final Comparator<Completion> sortByRelevanceComparator = new SortByRelevanceComparator();
/** /**
* Constructor. * Constructor.
* *
* @param parent * @param parent The parent window (hosting the text component).
* The parent window (hosting the text component). * @param ac The auto-completion instance.
* @param ac * @param context The completion context.
* 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); super(parent);
this.ac = ac; this.ac = ac;
ComponentOrientation o = ac.getTextComponentOrientation(); ComponentOrientation o = ac.getTextComponentOrientation();
@@ -85,7 +83,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
list.setCellRenderer(ac.getParamChoicesRenderer()); list.setCellRenderer(ac.getParamChoicesRenderer());
} }
list.addMouseListener(new MouseAdapter() { list.addMouseListener(new MouseAdapter() {
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) { if (e.getClickCount() == 2) {
@@ -103,23 +101,21 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
decorator.decorate(this); decorator.decorate(this);
} }
} }
/** /**
* Returns the selected value. * Returns the selected value.
* *
* @return The selected value, or <code>null</code> if nothing is * @return The selected value, or <code>null</code> if nothing is selected.
* selected.
*/ */
public String getSelectedChoice() { public String getSelectedChoice() {
Completion c = (Completion) list.getSelectedValue(); Completion c = (Completion) list.getSelectedValue();
return c == null ? null : c.toString(); return c == null ? null : c.toString();
} }
/** /**
* Changes the selected index. * Changes the selected index.
* *
* @param amount * @param amount The amount by which to change the selected index.
* The amount by which to change the selected index.
*/ */
public void incSelection(int amount) { public void incSelection(int amount) {
int selection = list.getSelectedIndex(); int selection = list.getSelectedIndex();
@@ -133,14 +129,12 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
list.setSelectedIndex(selection); list.setSelectedIndex(selection);
list.ensureIndexIsVisible(selection); list.ensureIndexIsVisible(selection);
} }
/** /**
* Initializes this window to offer suggestions for the parameters of * Initializes this window to offer suggestions for the parameters of a specific
* a specific completion. * completion.
* *
* @param pc * @param pc The completion whose parameters we should offer suggestions for.
* The completion whose parameters we should offer suggestions
* for.
*/ */
public void initialize(ParameterizedCompletion pc) { public void initialize(ParameterizedCompletion pc) {
CompletionProvider provider = pc.getProvider(); CompletionProvider provider = pc.getProvider();
@@ -158,12 +152,11 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
choicesListList.add(choices); choicesListList.add(choices);
} }
} }
/** /**
* Sets the location of this window relative to the given rectangle. * Sets the location of this window relative to the given rectangle.
* *
* @param r * @param r The visual position of the caret (in screen coordinates).
* The visual position of the caret (in screen coordinates).
*/ */
public void setLocationRelativeTo(Rectangle r) { public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and // Multi-monitor support - make sure the completion window (and
@@ -187,18 +180,16 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
} }
setLocation(x, y); setLocation(x, y);
} }
/** /**
* Displays the choices for the specified parameter matching the given * Displays the choices for the specified parameter matching the given text.
* text. This will display or hide this popup window as necessary. * This will display or hide this popup window as necessary.
* *
* @param param * @param param The index of the parameter the caret is currently in. This may
* The index of the parameter the caret is currently in. * be <code>-1</code> if not in a parameter (i.e., on the comma
* This may be <code>-1</code> if not in a parameter (i.e., on * between parameters).
* the comma between parameters). * @param prefix Text in the parameter before the dot. This may be
* @param prefix * <code>null</code> to represent the empty string.
* 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) { public void setParameter(int param, String prefix) {
model.clear(); model.clear();
@@ -250,12 +241,11 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
setVisible(false); setVisible(false);
} }
} }
/** /**
* Toggles the visibility of this popup window. * Toggles the visibility of this popup window.
* *
* @param visible * @param visible Whether this window should be visible.
* Whether this window should be visible.
*/ */
@Override @Override
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
@@ -268,7 +258,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
super.setVisible(visible); super.setVisible(visible);
} }
} }
/** /**
* Updates the <tt>LookAndFeel</tt> of this window. * 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 * Manages UI and state specific to parameterized completions - the parameter
* description tool tip, the parameter completion choices list, the actual * description tool tip, the parameter completion choices list, the actual
* highlights in the editor, etc. This component installs new key bindings * highlights in the editor, etc. This component installs new key bindings when
* when appropriate to allow the user to cycle through the parameters of the * appropriate to allow the user to cycle through the parameters of the
* completion, and optionally cycle through completion choices for those * completion, and optionally cycle through completion choices for those
* parameters. * parameters.
* *
@@ -57,7 +57,7 @@ import org.fife.ui.rtextarea.ChangeableHighlightPainter;
* @version 1.0 * @version 1.0
*/ */
class ParameterizedCompletionContext { class ParameterizedCompletionContext {
/** /**
* The parent window. * The parent window.
*/ */
@@ -95,13 +95,13 @@ class ParameterizedCompletionContext {
*/ */
private Listener listener; private Listener listener;
/** /**
* The minimum offset into the document that the caret can move to * The minimum offset into the document that the caret can move to before this
* before this tool tip disappears. * tool tip disappears.
*/ */
private int minPos; private int minPos;
/** /**
* The maximum offset into the document that the caret can move to * The maximum offset into the document that the caret can move to before this
* before this tool tip disappears. * tool tip disappears.
*/ */
private Position maxPos; // Moves with text inserted. private Position maxPos; // Moves with text inserted.
private Position defaultEndOffs; private Position defaultEndOffs;
@@ -140,7 +140,7 @@ class ParameterizedCompletionContext {
private static final String IM_KEY_ESCAPE = "ParamCompKey.Escape"; private static final String IM_KEY_ESCAPE = "ParamCompKey.Escape";
private static final String IM_KEY_ENTER = "ParamCompKey.Enter"; private static final String IM_KEY_ENTER = "ParamCompKey.Enter";
private static final String IM_KEY_CLOSING = "ParamCompKey.Closing"; private static final String IM_KEY_CLOSING = "ParamCompKey.Closing";
/** /**
* Constructor. * Constructor.
*/ */
@@ -156,7 +156,7 @@ class ParameterizedCompletionContext {
tags = new ArrayList<Object>(1); // Usually small tags = new ArrayList<Object>(1); // Usually small
paramCopyInfos = new ArrayList<ParamCopyInfo>(1); paramCopyInfos = new ArrayList<ParamCopyInfo>(1);
} }
/** /**
* Activates parameter completion support. * Activates parameter completion support.
* *
@@ -195,7 +195,7 @@ class ParameterizedCompletionContext {
prepareParamChoicesWindow(); prepareParamChoicesWindow();
paramChoicesWindow.setVisible(true); paramChoicesWindow.setVisible(true);
} }
/** /**
* Creates the completion window offering suggestions for parameters. * Creates the completion window offering suggestions for parameters.
* *
@@ -206,10 +206,9 @@ class ParameterizedCompletionContext {
pcw.initialize(pc); pcw.initialize(pc);
return pcw; return pcw;
} }
/** /**
* Hides any popup windows and terminates parameterized completion * Hides any popup windows and terminates parameterized completion assistance.
* assistance.
* *
* @see #activate() * @see #activate()
*/ */
@@ -226,15 +225,13 @@ class ParameterizedCompletionContext {
paramChoicesWindow.setVisible(false); paramChoicesWindow.setVisible(false);
} }
} }
/** /**
* Returns the text inserted for the parameter containing the specified * Returns the text inserted for the parameter containing the specified offset.
* offset.
* *
* @param offs * @param offs The offset into the document.
* The offset into the document. * @return The text of the parameter containing the offset, or <code>null</code>
* @return The text of the parameter containing the offset, or * if the offset is not in a parameter.
* <code>null</code> if the offset is not in a parameter.
*/ */
public String getArgumentText(int offs) { public String getArgumentText(int offs) {
List<Highlight> paramHighlights = getParameterHighlights(); List<Highlight> paramHighlights = getParameterHighlights();
@@ -258,12 +255,12 @@ class ParameterizedCompletionContext {
} }
return null; return null;
} }
/** /**
* Returns the highlight of the current parameter. * Returns the highlight of the current parameter.
* *
* @return The current parameter's highlight, or <code>null</code> if * @return The current parameter's highlight, or <code>null</code> if the caret
* the caret is not in a parameter's bounds. * is not in a parameter's bounds.
* @see #getCurrentParameterStartOffset() * @see #getCurrentParameterStartOffset()
*/ */
private Highlight getCurrentParameterHighlight() { private Highlight getCurrentParameterHighlight() {
@@ -280,7 +277,7 @@ class ParameterizedCompletionContext {
} }
return null; return null;
} }
private int getCurrentParameterIndex() { private int getCurrentParameterIndex() {
JTextComponent tc = ac.getTextComponent(); JTextComponent tc = ac.getTextComponent();
int dot = tc.getCaretPosition(); int dot = tc.getCaretPosition();
@@ -296,27 +293,26 @@ class ParameterizedCompletionContext {
} }
return -1; return -1;
} }
/** /**
* Returns the starting offset of the current parameter. * Returns the starting offset of the current parameter.
* *
* @return The current parameter's starting offset, or <code>-1</code> if * @return The current parameter's starting offset, or <code>-1</code> if the
* the caret is not in a parameter's bounds. * caret is not in a parameter's bounds.
* @see #getCurrentParameterHighlight() * @see #getCurrentParameterHighlight()
*/ */
private int getCurrentParameterStartOffset() { private int getCurrentParameterStartOffset() {
Highlight h = getCurrentParameterHighlight(); Highlight h = getCurrentParameterHighlight();
return h != null ? h.getStartOffset() + 1 : -1; return h != null ? h.getStartOffset() + 1 : -1;
} }
/** /**
* Returns the highlight from a list that comes "first" in a list. Even * Returns the highlight from a list that comes "first" in a list. Even though
* though most parameter highlights are ordered, sometimes they aren't * most parameter highlights are ordered, sometimes they aren't (e.g. the
* (e.g. the "cursor" parameter in a template completion is always last, * "cursor" parameter in a template completion is always last, even though it
* even though it can be anywhere in the template). * can be anywhere in the template).
* *
* @param highlights * @param highlights The list of highlights. Assumed to be non-empty.
* The list of highlights. Assumed to be non-empty.
* @return The highlight that comes first in the document. * @return The highlight that comes first in the document.
* @see #getLastHighlight(List) * @see #getLastHighlight(List)
*/ */
@@ -332,15 +328,14 @@ class ParameterizedCompletionContext {
} }
return first; return first;
} }
/** /**
* Returns the highlight from a list that comes "last" in that list. Even * Returns the highlight from a list that comes "last" in that list. Even though
* though most parameter highlights are ordered, sometimes they aren't * most parameter highlights are ordered, sometimes they aren't (e.g. the
* (e.g. the "cursor" parameter in a template completion is always last, * "cursor" parameter in a template completion is always last, even though it
* even though it can be anywhere in the template. * can be anywhere in the template.
* *
* @param highlights * @param highlights The list of highlights. Assumed to be non-empty.
* The list of highlights. Assumed to be non-empty.
* @return The highlight that comes last in the document. * @return The highlight that comes last in the document.
* @see #getFirstHighlight(List) * @see #getFirstHighlight(List)
*/ */
@@ -356,7 +351,7 @@ class ParameterizedCompletionContext {
} }
return last; return last;
} }
public List<Highlight> getParameterHighlights() { public List<Highlight> getParameterHighlights() {
List<Highlight> paramHighlights = new ArrayList<Highlight>(2); List<Highlight> paramHighlights = new ArrayList<Highlight>(2);
JTextComponent tc = ac.getTextComponent(); JTextComponent tc = ac.getTextComponent();
@@ -369,12 +364,12 @@ class ParameterizedCompletionContext {
} }
return paramHighlights; return paramHighlights;
} }
/** /**
* Inserts the choice selected in the parameter choices window. * Inserts the choice selected in the parameter choices window.
* *
* @return Whether the choice was inserted. This will be <code>false</code> * @return Whether the choice was inserted. This will be <code>false</code> if
* if the window is not visible, or no choice is selected. * the window is not visible, or no choice is selected.
*/ */
boolean insertSelectedChoice() { boolean insertSelectedChoice() {
if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) { if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) {
@@ -396,10 +391,10 @@ class ParameterizedCompletionContext {
} }
return false; return false;
} }
/** /**
* Installs key bindings on the text component that facilitate the user * Installs key bindings on the text component that facilitate the user editing
* editing this completion's parameters. * this completion's parameters.
* *
* @see #uninstallKeyBindings() * @see #uninstallKeyBindings()
*/ */
@@ -447,7 +442,7 @@ class ParameterizedCompletionContext {
oldClosingAction = am.get(IM_KEY_CLOSING); oldClosingAction = am.get(IM_KEY_CLOSING);
am.put(IM_KEY_CLOSING, new ClosingAction()); am.put(IM_KEY_CLOSING, new ClosingAction());
} }
/** /**
* Moves to and selects the next parameter. * Moves to and selects the next parameter.
* *
@@ -486,7 +481,7 @@ class ParameterizedCompletionContext {
tc.setSelectionEnd(currentNext.getEndOffset()); tc.setSelectionEnd(currentNext.getEndOffset());
updateToolTipText(pos); updateToolTipText(pos);
} }
/** /**
* Moves to and selects the previous parameter. * Moves to and selects the previous parameter.
* *
@@ -507,8 +502,8 @@ class ParameterizedCompletionContext {
List<Highlight> highlights = getParameterHighlights(); List<Highlight> highlights = getParameterHighlights();
for (int i = 0; i < highlights.size(); i++) { for (int i = 0; i < highlights.size(); i++) {
Highlight h = highlights.get(i); Highlight h = highlights.get(i);
if (currentPrev == null || currentPrev.getStartOffset() >= dot if (currentPrev == null || currentPrev.getStartOffset() >= dot || (h.getStartOffset() < selStart
|| (h.getStartOffset() < selStart && (h.getStartOffset() > currentPrev.getStartOffset() || pos == lastSelectedParam))) { && (h.getStartOffset() > currentPrev.getStartOffset() || pos == lastSelectedParam))) {
currentPrev = h; currentPrev = h;
pos = i; pos = i;
} }
@@ -533,7 +528,7 @@ class ParameterizedCompletionContext {
deactivate(); deactivate();
} }
} }
private void possiblyUpdateParamCopies(Document doc) { private void possiblyUpdateParamCopies(Document doc) {
int index = getCurrentParameterIndex(); int index = getCurrentParameterIndex();
// FunctionCompletions add an extra param at end of inserted text // FunctionCompletions add an extra param at end of inserted text
@@ -567,7 +562,7 @@ class ParameterizedCompletionContext {
deactivate(); deactivate();
} }
} }
/** /**
* Updates the optional window listing likely completion choices, * Updates the optional window listing likely completion choices,
*/ */
@@ -595,7 +590,7 @@ class ParameterizedCompletionContext {
paramChoicesWindow.setParameter(lastSelectedParam, paramPrefix); paramChoicesWindow.setParameter(lastSelectedParam, paramPrefix);
} }
} }
/** /**
* Removes the bounding boxes around parameters. * Removes the bounding boxes around parameters.
*/ */
@@ -611,19 +606,16 @@ class ParameterizedCompletionContext {
} }
paramCopyInfos.clear(); paramCopyInfos.clear();
} }
/** /**
* Replaces highlighted text with new text. Takes special care so that * Replaces highlighted text with new text. Takes special care so that the
* the highlight stays just around the newly-highlighted text, since * highlight stays just around the newly-highlighted text, since Swing's
* Swing's <code>Highlight</code> classes are funny about insertions at * <code>Highlight</code> classes are funny about insertions at their start
* their start offsets. * offsets.
* *
* @param doc * @param doc The document.
* The document. * @param h The highlight whose text to change.
* @param h * @param replacement The new text to be in the highlight.
* The highlight whose text to change.
* @param replacement
* The new text to be in the highlight.
* @return The replacement highlight for <code>h</code>. * @return The replacement highlight for <code>h</code>.
*/ */
private Highlight replaceHighlightedText(Document doc, Highlight h, String replacement) { private Highlight replaceHighlightedText(Document doc, Highlight h, String replacement) {
@@ -646,7 +638,7 @@ class ParameterizedCompletionContext {
} }
return null; return null;
} }
/** /**
* Removes the key bindings we installed. * Removes the key bindings we installed.
* *
@@ -682,11 +674,10 @@ class ParameterizedCompletionContext {
im.put(ks, oldClosingKey); im.put(ks, oldClosingKey);
am.put(IM_KEY_CLOSING, oldClosingAction); am.put(IM_KEY_CLOSING, oldClosingAction);
} }
/** /**
* Updates the text in the tool tip to have the current parameter * Updates the text in the tool tip to have the current parameter displayed in
* displayed in bold. The "current parameter" is determined from the * bold. The "current parameter" is determined from the current caret position.
* current caret position.
* *
* @return The "prefix" of text in the caret's parameter before the caret. * @return The "prefix" of text in the caret's parameter before the caret.
*/ */
@@ -718,7 +709,7 @@ class ParameterizedCompletionContext {
updateToolTipText(index); updateToolTipText(index);
return paramPrefix; return paramPrefix;
} }
private void updateToolTipText(int selectedParam) { private void updateToolTipText(int selectedParam) {
if (selectedParam != lastSelectedParam) { if (selectedParam != lastSelectedParam) {
if (tip != null) { if (tip != null) {
@@ -727,7 +718,7 @@ class ParameterizedCompletionContext {
this.lastSelectedParam = selectedParam; this.lastSelectedParam = selectedParam;
} }
} }
/** /**
* Updates the <code>LookAndFeel</code> of all popup windows this context * Updates the <code>LookAndFeel</code> of all popup windows this context
* manages. * manages.
@@ -740,12 +731,12 @@ class ParameterizedCompletionContext {
paramChoicesWindow.updateUI(); paramChoicesWindow.updateUI();
} }
} }
/** /**
* Called when the user presses Enter while entering parameters. * Called when the user presses Enter while entering parameters.
*/ */
private class GotoEndAction extends AbstractAction { private class GotoEndAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
// If the param choices window is visible and something is chosen, // If the param choices window is visible and something is chosen,
@@ -771,19 +762,19 @@ class ParameterizedCompletionContext {
} }
} }
} }
private Action getDefaultEnterAction(JTextComponent tc) { private Action getDefaultEnterAction(JTextComponent tc) {
ActionMap am = tc.getActionMap(); ActionMap am = tc.getActionMap();
return am.get(DefaultEditorKit.insertBreakAction); return am.get(DefaultEditorKit.insertBreakAction);
} }
} }
/** /**
* Called when the user types the character marking the closing of the * Called when the user types the character marking the closing of the parameter
* parameter list, such as '<code>)</code>'. * list, such as '<code>)</code>'.
*/ */
private class ClosingAction extends AbstractAction { private class ClosingAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
JTextComponent tc = ac.getTextComponent(); JTextComponent tc = ac.getTextComponent();
@@ -812,7 +803,7 @@ class ParameterizedCompletionContext {
tc.replaceSelection(Character.toString(end)); tc.replaceSelection(Character.toString(end));
} }
} }
public int getCount(String text, char ch) { public int getCount(String text, char ch) {
int count = 0; int count = 0;
int old = 0; int old = 0;
@@ -824,12 +815,12 @@ class ParameterizedCompletionContext {
return count; return count;
} }
} }
/** /**
* Action performed when the user hits the escape key. * Action performed when the user hits the escape key.
*/ */
private class HideAction extends AbstractAction { private class HideAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
// On first escape press, if the param choices window is visible, // 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 * Listens for various events in the text component while this tool tip is
* is visible. * visible.
*/ */
private class Listener implements FocusListener, CaretListener, DocumentListener { private class Listener implements FocusListener, CaretListener, DocumentListener {
private boolean markOccurrencesEnabled; private boolean markOccurrencesEnabled;
/** /**
* Called when the text component's caret moves. * Called when the text component's caret moves.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void caretUpdate(CaretEvent e) { public void caretUpdate(CaretEvent e) {
@@ -875,37 +865,36 @@ class ParameterizedCompletionContext {
prepareParamChoicesWindow(); prepareParamChoicesWindow();
} }
} }
@Override @Override
public void changedUpdate(DocumentEvent e) {} public void changedUpdate(DocumentEvent e) {
}
/** /**
* Called when the text component gains focus. * Called when the text component gains focus.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void focusGained(FocusEvent e) { public void focusGained(FocusEvent e) {
// Do nothing // Do nothing
} }
/** /**
* Called when the text component loses focus. * Called when the text component loses focus.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void focusLost(FocusEvent e) { public void focusLost(FocusEvent e) {
deactivate(); deactivate();
} }
private void handleDocumentEvent(final DocumentEvent e) { private void handleDocumentEvent(final DocumentEvent e) {
if (!ignoringDocumentEvents) { if (!ignoringDocumentEvents) {
ignoringDocumentEvents = true; ignoringDocumentEvents = true;
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
@Override @Override
public void run() { public void run() {
possiblyUpdateParamCopies(e.getDocument()); possiblyUpdateParamCopies(e.getDocument());
@@ -914,17 +903,16 @@ class ParameterizedCompletionContext {
}); });
} }
} }
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
handleDocumentEvent(e); handleDocumentEvent(e);
} }
/** /**
* Installs this listener onto a text component. * Installs this listener onto a text component.
* *
* @param tc * @param tc The text component to install onto.
* The text component to install onto.
* @see #uninstall() * @see #uninstall()
*/ */
public void install(JTextComponent tc) { public void install(JTextComponent tc) {
@@ -950,7 +938,8 @@ class ParameterizedCompletionContext {
} }
for (int i = 0; i < info.getReplacementCopyCount(); i++) { for (int i = 0; i < info.getReplacementCopyCount(); i++) {
ReplacementCopy rc = info.getReplacementCopy(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. // Go back and start at the first parameter.
tc.setCaretPosition(info.getSelectionStart()); tc.setCaretPosition(info.getSelectionStart());
@@ -981,12 +970,12 @@ class ParameterizedCompletionContext {
tc.addFocusListener(this); tc.addFocusListener(this);
installKeyBindings(); installKeyBindings();
} }
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
handleDocumentEvent(e); handleDocumentEvent(e);
} }
/** /**
* Uninstalls this listener from the current text component. * Uninstalls this listener from the current text component.
*/ */
@@ -1005,21 +994,21 @@ class ParameterizedCompletionContext {
removeParameterHighlights(); removeParameterHighlights();
} }
} }
/** /**
* Action performed when the user presses the up or down arrow keys and * Action performed when the user presses the up or down arrow keys and the
* the parameter completion choices popup is visible. * parameter completion choices popup is visible.
*/ */
private class NextChoiceAction extends AbstractAction { private class NextChoiceAction extends AbstractAction {
private Action oldAction; private Action oldAction;
private int amount; private int amount;
public NextChoiceAction(int amount, Action oldAction) { public NextChoiceAction(int amount, Action oldAction) {
this.amount = amount; this.amount = amount;
this.oldAction = oldAction; this.oldAction = oldAction;
} }
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) { if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) {
@@ -1031,34 +1020,34 @@ class ParameterizedCompletionContext {
} }
} }
} }
/** /**
* Action performed when the user hits the tab key. * Action performed when the user hits the tab key.
*/ */
private class NextParamAction extends AbstractAction { private class NextParamAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
moveToNextParam(); moveToNextParam();
} }
} }
private static class ParamCopyInfo { private static class ParamCopyInfo {
private String paramName; private String paramName;
private Highlight h; private Highlight h;
public ParamCopyInfo(String paramName, Highlight h) { public ParamCopyInfo(String paramName, Highlight h) {
this.paramName = paramName; this.paramName = paramName;
this.h = h; this.h = h;
} }
} }
/** /**
* Action performed when the user hits shift+tab. * Action performed when the user hits shift+tab.
*/ */
private class PrevParamAction extends AbstractAction { private class PrevParamAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
moveToPreviousParam(); moveToPreviousParam();
@@ -28,7 +28,7 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* @version 1.0 * @version 1.0
*/ */
class ParameterizedCompletionDescriptionToolTip { class ParameterizedCompletionDescriptionToolTip {
/** /**
* The actual tool tip. * The actual tool tip.
*/ */
@@ -41,23 +41,21 @@ class ParameterizedCompletionDescriptionToolTip {
* The completion being described. * The completion being described.
*/ */
private ParameterizedCompletion pc; private ParameterizedCompletion pc;
/** /**
* Constructor. * Constructor.
* *
* @param owner * @param owner The parent window.
* The parent window. * @param ac The parent auto-completion.
* @param ac * @param pc The completion being described.
* The parent auto-completion.
* @param pc
* The completion being described.
*/ */
public ParameterizedCompletionDescriptionToolTip(Window owner, ParameterizedCompletionContext context, AutoCompletion ac, public ParameterizedCompletionDescriptionToolTip(Window owner, ParameterizedCompletionContext context,
ParameterizedCompletion pc) { AutoCompletion ac, ParameterizedCompletion pc) {
tooltip = new JWindow(owner); tooltip = new JWindow(owner);
this.pc = pc; this.pc = pc;
descLabel = new JLabel(); 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.setOpaque(true);
descLabel.setBackground(TipUtil.getToolTipBackground()); descLabel.setBackground(TipUtil.getToolTipBackground());
// It appears that if a JLabel is set as a content pane directly, when // It appears that if a JLabel is set as a content pane directly, when
@@ -77,7 +75,7 @@ class ParameterizedCompletionDescriptionToolTip {
updateText(0); updateText(0);
tooltip.setFocusableWindowState(false); tooltip.setFocusableWindowState(false);
} }
/** /**
* Returns whether this tool tip is visible. * Returns whether this tool tip is visible.
* *
@@ -87,12 +85,11 @@ class ParameterizedCompletionDescriptionToolTip {
public boolean isVisible() { public boolean isVisible() {
return tooltip.isVisible(); return tooltip.isVisible();
} }
/** /**
* Sets the location of this tool tip relative to the given rectangle. * Sets the location of this tool tip relative to the given rectangle.
* *
* @param r * @param r The visual position of the caret (in screen coordinates).
* The visual position of the caret (in screen coordinates).
*/ */
public void setLocationRelativeTo(Rectangle r) { public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and // Multi-monitor support - make sure the completion window (and
@@ -119,24 +116,22 @@ class ParameterizedCompletionDescriptionToolTip {
} }
tooltip.setLocation(x, y); tooltip.setLocation(x, y);
} }
/** /**
* Toggles the visibility of this tool tip. * Toggles the visibility of this tool tip.
* *
* @param visible * @param visible Whether this tool tip should be visible.
* Whether this tool tip should be visible.
* @see #isVisible() * @see #isVisible()
*/ */
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
tooltip.setVisible(visible); tooltip.setVisible(visible);
} }
/** /**
* Updates the text in the tool tip to have the current parameter * Updates the text in the tool tip to have the current parameter displayed in
* displayed in bold. * bold.
* *
* @param selectedParam * @param selectedParam The index of the selected parameter.
* The index of the selected parameter.
* @return Whether the text needed to be updated. * @return Whether the text needed to be updated.
*/ */
public boolean updateText(int selectedParam) { public boolean updateText(int selectedParam) {
@@ -170,10 +165,9 @@ class ParameterizedCompletionDescriptionToolTip {
tooltip.pack(); tooltip.pack();
return true; return true;
} }
/** /**
* Updates the <tt>LookAndFeel</tt> of this window and the description * Updates the <tt>LookAndFeel</tt> of this window and the description window.
* window.
*/ */
public void updateUI() { public void updateUI() {
SwingUtilities.updateComponentTreeUI(tooltip); SwingUtilities.updateComponentTreeUI(tooltip);
@@ -22,7 +22,7 @@ import org.fife.ui.rsyntaxtextarea.DocumentRange;
* @version 1.0 * @version 1.0
*/ */
class ParameterizedCompletionInsertionInfo { class ParameterizedCompletionInsertionInfo {
private int minOffs; private int minOffs;
private Position maxOffs; private Position maxOffs;
private int defaultEnd; private int defaultEnd;
@@ -31,26 +31,24 @@ class ParameterizedCompletionInsertionInfo {
private String textToInsert; private String textToInsert;
private List<DocumentRange> replacementLocations; private List<DocumentRange> replacementLocations;
private List<ReplacementCopy> replacementCopies; private List<ReplacementCopy> replacementCopies;
public ParameterizedCompletionInsertionInfo() { public ParameterizedCompletionInsertionInfo() {
defaultEnd = -1; defaultEnd = -1;
} }
public void addReplacementCopy(String id, int start, int end) { public void addReplacementCopy(String id, int start, int end) {
if (replacementCopies == null) { if (replacementCopies == null) {
replacementCopies = new ArrayList<ReplacementCopy>(1); replacementCopies = new ArrayList<ReplacementCopy>(1);
} }
replacementCopies.add(new ReplacementCopy(id, start, end)); replacementCopies.add(new ReplacementCopy(id, start, end));
} }
/** /**
* Marks a region of the replacement text as representing a variable name * Marks a region of the replacement text as representing a variable name or
* or some other construct that the user should replace. * some other construct that the user should replace.
* *
* @param start * @param start The start offset.
* The start offset. * @param end The end offset.
* @param end
* The end offset.
* @see #getReplacementCount() * @see #getReplacementCount()
* @see #getReplacementLocation(int) * @see #getReplacementLocation(int)
*/ */
@@ -60,14 +58,14 @@ class ParameterizedCompletionInsertionInfo {
} }
replacementLocations.add(new DocumentRange(start, end)); replacementLocations.add(new DocumentRange(start, end));
} }
public int getDefaultEndOffs() { public int getDefaultEndOffs() {
return defaultEnd > -1 ? defaultEnd : getMaxOffset().getOffset(); return defaultEnd > -1 ? defaultEnd : getMaxOffset().getOffset();
} }
/** /**
* Returns the maximum offset the caret can move to before being outside * Returns the maximum offset the caret can move to before being outside of the
* of the text inserted for this completion. * text inserted for this completion.
* *
* @return The maximum offset. * @return The maximum offset.
* @see #getMinOffset() * @see #getMinOffset()
@@ -75,10 +73,10 @@ class ParameterizedCompletionInsertionInfo {
public Position getMaxOffset() { public Position getMaxOffset() {
return maxOffs; return maxOffs;
} }
/** /**
* Returns the minimum offset the caret can move to before being outside * Returns the minimum offset the caret can move to before being outside of the
* of the text inserted for this completion. * text inserted for this completion.
* *
* @return The minimum offset. * @return The minimum offset.
* @see #getMaxOffset() * @see #getMaxOffset()
@@ -86,11 +84,11 @@ class ParameterizedCompletionInsertionInfo {
public int getMinOffset() { public int getMinOffset() {
return minOffs; return minOffs;
} }
public int getReplacementCopyCount() { public int getReplacementCopyCount() {
return replacementCopies == null ? 0 : replacementCopies.size(); return replacementCopies == null ? 0 : replacementCopies.size();
} }
/** /**
* Returns the number of replacements in the completion. * Returns the number of replacements in the completion.
* *
@@ -99,28 +97,27 @@ class ParameterizedCompletionInsertionInfo {
public int getReplacementCount() { public int getReplacementCount() {
return replacementLocations == null ? 0 : replacementLocations.size(); return replacementLocations == null ? 0 : replacementLocations.size();
} }
public ReplacementCopy getReplacementCopy(int index) { public ReplacementCopy getReplacementCopy(int index) {
return replacementCopies.get(index); return replacementCopies.get(index);
} }
/** /**
* Returns the starting- and ending-offsets of the replacement regions * Returns the starting- and ending-offsets of the replacement regions in the
* in the completion. * completion.
* *
* @param index * @param index The replacement region.
* The replacement region.
* @return The range in the document of that replacement region. * @return The range in the document of that replacement region.
* @see #getReplacementCount() * @see #getReplacementCount()
*/ */
public DocumentRange getReplacementLocation(int index) { public DocumentRange getReplacementLocation(int index) {
return replacementLocations.get(index); return replacementLocations.get(index);
} }
/** /**
* Returns the offset that should be the end of the initially selected * Returns the offset that should be the end of the initially selected text when
* text when the completion is inserted (i.e., the end offset of the first * the completion is inserted (i.e., the end offset of the first replacement
* replacement region). * region).
* *
* @return The end offset for the initial selection. * @return The end offset for the initial selection.
* @see #getSelectionStart() * @see #getSelectionStart()
@@ -128,11 +125,11 @@ class ParameterizedCompletionInsertionInfo {
public int getSelectionEnd() { public int getSelectionEnd() {
return selEnd; return selEnd;
} }
/** /**
* Returns the offset that should be the start of the initially selected * Returns the offset that should be the start of the initially selected text
* text when the completion is inserted (i.e., the start offset of the * when the completion is inserted (i.e., the start offset of the first
* first replacement region). * replacement region).
* *
* @return The start offset for the initial selection. * @return The start offset for the initial selection.
* @see #getSelectionEnd() * @see #getSelectionEnd()
@@ -140,7 +137,7 @@ class ParameterizedCompletionInsertionInfo {
public int getSelectionStart() { public int getSelectionStart() {
return selStart; return selStart;
} }
/** /**
* Returns the actual text to insert when the completion is selected. * Returns the actual text to insert when the completion is selected.
* *
@@ -150,24 +147,22 @@ class ParameterizedCompletionInsertionInfo {
public String getTextToInsert() { public String getTextToInsert() {
return textToInsert; return textToInsert;
} }
/** /**
* Returns whether or not there is an initial selected region for the * Returns whether or not there is an initial selected region for the completion
* completion (i.e., whether the completion actually has any parameters). * (i.e., whether the completion actually has any parameters).
* *
* @return Whether there is a region to initially select for the completion. * @return Whether there is a region to initially select for the completion.
*/ */
public boolean hasSelection() { public boolean hasSelection() {
return selEnd != selStart; return selEnd != selStart;
} }
/** /**
* Sets the initially selected region for the completion. * Sets the initially selected region for the completion.
* *
* @param selStart * @param selStart The selection start.
* The selection start. * @param selEnd The selection end.
* @param selEnd
* The selection end.
* @see #getSelectionEnd() * @see #getSelectionEnd()
* @see #getSelectionStart() * @see #getSelectionStart()
*/ */
@@ -175,16 +170,14 @@ class ParameterizedCompletionInsertionInfo {
this.selStart = selStart; this.selStart = selStart;
this.selEnd = selEnd; this.selEnd = selEnd;
} }
/** /**
* Sets the document range the caret can move around in before being * Sets the document range the caret can move around in before being outside of
* outside of the text inserted for the completion. * the text inserted for the completion.
* *
* @param minOffs * @param minOffs The minimum offset.
* The minimum offset. * @param maxOffs The maximum offset, that will track its location as the
* @param maxOffs * document is modified.
* The maximum offset, that will track its location as the
* document is modified.
* @see #getMinOffset() * @see #getMinOffset()
* @see #getMaxOffset() * @see #getMaxOffset()
*/ */
@@ -192,42 +185,41 @@ class ParameterizedCompletionInsertionInfo {
this.minOffs = minOffs; this.minOffs = minOffs;
this.maxOffs = maxOffs; this.maxOffs = maxOffs;
} }
public void setDefaultEndOffs(int end) { public void setDefaultEndOffs(int end) {
defaultEnd = end; defaultEnd = end;
} }
/** /**
* Sets the text to insert for the completion. * Sets the text to insert for the completion.
* *
* @param text * @param text The text to insert.
* The text to insert.
* @see #getTextToInsert() * @see #getTextToInsert()
*/ */
public void setTextToInsert(String text) { public void setTextToInsert(String text) {
this.textToInsert = text; this.textToInsert = text;
} }
public static class ReplacementCopy { public static class ReplacementCopy {
private String id; private String id;
private int start; private int start;
private int end; private int end;
public ReplacementCopy(String id, int start, int end) { public ReplacementCopy(String id, int start, int end) {
this.id = id; this.id = id;
this.start = start; this.start = start;
this.end = end; this.end = end;
} }
public int getEnd() { public int getEnd() {
return end; return end;
} }
public String getId() { public String getId() {
return id; return id;
} }
public int getStart() { public int getStart() {
return start; return start;
} }
@@ -14,8 +14,8 @@ import javax.swing.Action;
/** /**
* An <code>AutoCompletion</code> that adds the ability to cycle through a set * 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 * of <code>CompletionProvider</code>s via the trigger key. This allows the
* application to logically "group together" completions of similar kinds; * application to logically "group together" completions of similar kinds; for
* for example, Java code completions vs. template completions. * example, Java code completions vs. template completions.
* <p> * <p>
* *
* Usage: * Usage:
@@ -33,15 +33,14 @@ import javax.swing.Action;
* @author mschlegel * @author mschlegel
*/ */
public class RoundRobinAutoCompletion extends AutoCompletion { public class RoundRobinAutoCompletion extends AutoCompletion {
/** The List of CompletionProviders to use */ /** The List of CompletionProviders to use */
private List<CompletionProvider> cycle = new ArrayList<CompletionProvider>(); private List<CompletionProvider> cycle = new ArrayList<CompletionProvider>();
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider A single completion provider.
* A single completion provider.
* @see #addCompletionProvider(CompletionProvider) * @see #addCompletionProvider(CompletionProvider)
*/ */
public RoundRobinAutoCompletion(CompletionProvider provider) { public RoundRobinAutoCompletion(CompletionProvider provider) {
@@ -58,25 +57,24 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
// chosen. // chosen.
setAutoCompleteSingleChoices(false); setAutoCompleteSingleChoices(false);
} }
/** /**
* Adds an additional <code>CompletionProvider</code> to the list to * Adds an additional <code>CompletionProvider</code> to the list to cycle
* cycle through. * through.
* *
* @param provider * @param provider The new completion provider.
* The new completion provider.
*/ */
public void addCompletionProvider(CompletionProvider provider) { public void addCompletionProvider(CompletionProvider provider) {
cycle.add(provider); cycle.add(provider);
} }
/** /**
* Moves to the next Provider internally. Needs refresh of the popup window * Moves to the next Provider internally. Needs refresh of the popup window to
* to display the changes. * display the changes.
* *
* @return true if the next provider was the default one (thus returned to * @return true if the next provider was the default one (thus returned to the
* the default view). May be used in case you like to hide the * default view). May be used in case you like to hide the popup in this
* popup in this case. * case.
*/ */
public boolean advanceProvider() { public boolean advanceProvider() {
CompletionProvider currentProvider = getCompletionProvider(); CompletionProvider currentProvider = getCompletionProvider();
@@ -84,7 +82,7 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
setCompletionProvider(cycle.get(i)); setCompletionProvider(cycle.get(i));
return i == 0; return i == 0;
} }
/** /**
* Overridden to provide our own implementation of the action. * Overridden to provide our own implementation of the action.
*/ */
@@ -92,7 +90,7 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
protected Action createAutoCompleteAction() { protected Action createAutoCompleteAction() {
return new CycleAutoCompleteAction(); return new CycleAutoCompleteAction();
} }
/** /**
* Resets the cycle to use the default provider on next refresh. * Resets the cycle to use the default provider on next refresh.
*/ */
@@ -103,14 +101,14 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
setCompletionProvider(defaultProvider); setCompletionProvider(defaultProvider);
} }
} }
/** /**
* An implementation of the auto-complete action that ensures the proper * An implementation of the auto-complete action that ensures the proper
* <code>CompletionProvider</code> is displayed based on the context in * <code>CompletionProvider</code> is displayed based on the context in which
* which the user presses the trigger key. * the user presses the trigger key.
*/ */
private class CycleAutoCompleteAction extends AutoCompleteAction { private class CycleAutoCompleteAction extends AutoCompleteAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
if (isAutoCompleteEnabled()) { if (isAutoCompleteEnabled()) {
@@ -17,68 +17,58 @@ package org.fife.ui.autocomplete;
* @version 1.0 * @version 1.0
*/ */
public class ShorthandCompletion extends BasicCompletion { public class ShorthandCompletion extends BasicCompletion {
/** /**
* The text the user can start typing that will match this completion. * The text the user can start typing that will match this completion.
*/ */
private String inputText; private String inputText;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider that returns this completion.
* The provider that returns this completion. * @param inputText The text the user inputs to get this completion.
* @param inputText * @param replacementText The replacement text of the completion.
* 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) { public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText) {
super(provider, replacementText); super(provider, replacementText);
this.inputText = inputText; this.inputText = inputText;
} }
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider that returns this completion.
* The provider that returns this completion. * @param inputText The text the user inputs to get this completion.
* @param inputText * @param replacementText The replacement text of the completion.
* The text the user inputs to get this completion. * @param shortDesc A short description of the completion. This will be
* @param replacementText * displayed in the completion list. This may be
* The replacement text of the completion. * <code>null</code>.
* @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); super(provider, replacementText, shortDesc);
this.inputText = inputText; this.inputText = inputText;
} }
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The provider that returns this completion.
* The provider that returns this completion. * @param inputText The text the user inputs to get this completion.
* @param inputText * @param replacementText The replacement text of the completion.
* The text the user inputs to get this completion. * @param shortDesc A short description of the completion. This will be
* @param replacementText * displayed in the completion list. This may be
* The replacement text of the completion. * <code>null</code>.
* @param shortDesc * @param summary The summary of this completion. This should be HTML.
* A short description of the completion. This will be * This may be <code>null</code>.
* 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); super(provider, replacementText, shortDesc, summary);
this.inputText = inputText; this.inputText = inputText;
} }
/** /**
* Returns the text the user must start typing to get this completion. * Returns the text the user must start typing to get this completion.
* *
@@ -88,13 +78,12 @@ public class ShorthandCompletion extends BasicCompletion {
public String getInputText() { public String getInputText() {
return inputText; return inputText;
} }
/** /**
* If a summary has been set, that summary is returned. Otherwise, the * If a summary has been set, that summary is returned. Otherwise, the
* replacement text is returned. * replacement text is returned.
* *
* @return A description of this completion (the text that will be * @return A description of this completion (the text that will be inserted).
* inserted).
* @see #getReplacementText() * @see #getReplacementText()
*/ */
@Override @Override
@@ -102,11 +91,11 @@ public class ShorthandCompletion extends BasicCompletion {
String summary = super.getSummary(); String summary = super.getSummary();
return summary != null ? summary : ("<html><body>" + getSummaryBody()); return summary != null ? summary : ("<html><body>" + getSummaryBody());
} }
/** /**
* Returns the "body" of the HTML returned by {@link #getSummary()} when * Returns the "body" of the HTML returned by {@link #getSummary()} when no
* no summary text has been set. This is defined to return the replacement * summary text has been set. This is defined to return the replacement text in
* text in a monospaced font. * a monospaced font.
* *
* @return The summary text's body, if no other summary has been defined. * @return The summary text's body, if no other summary has been defined.
* @see #getReplacementText() * @see #getReplacementText()
+21 -24
View File
@@ -37,32 +37,31 @@ import javax.swing.event.MouseInputAdapter;
* @version 1.0 * @version 1.0
*/ */
class SizeGrip extends JPanel { class SizeGrip extends JPanel {
/** /**
* The size grip to use if we're on OS X. * The size grip to use if we're on OS X.
*/ */
private Image osxSizeGrip; private Image osxSizeGrip;
public SizeGrip() { public SizeGrip() {
MouseHandler adapter = new MouseHandler(); MouseHandler adapter = new MouseHandler();
addMouseListener(adapter); addMouseListener(adapter);
addMouseMotionListener(adapter); addMouseMotionListener(adapter);
setPreferredSize(new Dimension(16, 16)); setPreferredSize(new Dimension(16, 16));
} }
/** /**
* Overridden to ensure that the cursor for this component is appropriate * Overridden to ensure that the cursor for this component is appropriate for
* for the orientation. * the orientation.
* *
* @param o * @param o The new orientation.
* The new orientation.
*/ */
@Override @Override
public void applyComponentOrientation(ComponentOrientation o) { public void applyComponentOrientation(ComponentOrientation o) {
possiblyFixCursor(o.isLeftToRight()); possiblyFixCursor(o.isLeftToRight());
super.applyComponentOrientation(o); super.applyComponentOrientation(o);
} }
/** /**
* Creates and returns the OS X size grip image. * Creates and returns the OS X size grip image.
* *
@@ -94,12 +93,11 @@ class SizeGrip extends JPanel {
} }
return image; return image;
} }
/** /**
* Paints this panel. * Paints this panel.
* *
* @param g * @param g The graphics context.
* The graphics context.
*/ */
@Override @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
@@ -147,13 +145,12 @@ class SizeGrip extends JPanel {
g.fillRect(2, height - 9, 2, 2); g.fillRect(2, height - 9, 2, 2);
} }
} }
/** /**
* Ensures that the cursor for this component is appropriate for the * Ensures that the cursor for this component is appropriate for the
* orientation. * orientation.
* *
* @param ltr * @param ltr Whether the current component orientation is LTR.
* Whether the current component orientation is LTR.
*/ */
protected void possiblyFixCursor(boolean ltr) { protected void possiblyFixCursor(boolean ltr) {
int cursor = Cursor.NE_RESIZE_CURSOR; int cursor = Cursor.NE_RESIZE_CURSOR;
@@ -164,7 +161,7 @@ class SizeGrip extends JPanel {
setCursor(Cursor.getPredefinedCursor(cursor)); setCursor(Cursor.getPredefinedCursor(cursor));
} }
} }
@Override @Override
public void updateUI() { public void updateUI() {
super.updateUI(); super.updateUI();
@@ -178,21 +175,21 @@ class SizeGrip extends JPanel {
osxSizeGrip = null; osxSizeGrip = null;
} }
} }
/** /**
* Listens for mouse events on this panel and resizes the parent window * Listens for mouse events on this panel and resizes the parent window
* appropriately. * appropriately.
*/ */
/* /*
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using * NOTE: We use SwingUtilities.convertPointToScreen() instead of just using the
* the locations relative to the corner component because the latter proved * locations relative to the corner component because the latter proved buggy -
* buggy - stretch the window too wide and some kind of arithmetic error * stretch the window too wide and some kind of arithmetic error started
* started happening somewhere - our window would grow way too large. * happening somewhere - our window would grow way too large.
*/ */
private class MouseHandler extends MouseInputAdapter { private class MouseHandler extends MouseInputAdapter {
private Point origPos; private Point origPos;
@Override @Override
public void mouseDragged(MouseEvent e) { public void mouseDragged(MouseEvent e) {
Point newPos = e.getPoint(); Point newPos = e.getPoint();
@@ -222,13 +219,13 @@ class SizeGrip extends JPanel {
} }
origPos.setLocation(newPos); origPos.setLocation(newPos);
} }
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
origPos = e.getPoint(); origPos = e.getPoint();
SwingUtilities.convertPointToScreen(origPos, SizeGrip.this); SwingUtilities.convertPointToScreen(origPos, SizeGrip.this);
} }
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
origPos = null; origPos = null;
@@ -10,14 +10,14 @@ package org.fife.ui.autocomplete;
import java.util.Comparator; import java.util.Comparator;
/** /**
* Compares two <code>Completion</code>s by their relevance before * Compares two <code>Completion</code>s by their relevance before sorting them
* sorting them lexicographically. * lexicographically.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class SortByRelevanceComparator implements Comparator<Completion> { public class SortByRelevanceComparator implements Comparator<Completion> {
@Override @Override
public int compare(Completion c1, Completion c2) { public int compare(Completion c1, Completion c2) {
int rel1 = c1.getRelevance(); int rel1 = c1.getRelevance();
@@ -37,12 +37,12 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* } * }
* </pre> * </pre>
* *
* In the above example, the first <code>${i}</code> is a parameter for the * In the above example, the first <code>${i}</code> is a parameter for the user
* user to type into; all the other <code>${i}</code> instances are * to type into; all the other <code>${i}</code> instances are automatically
* automatically changed to what the user types in the first one. The parameter * changed to what the user types in the first one. The parameter named
* named <code>${cursor}</code> is the "ending position" of the template. It's * <code>${cursor}</code> is the "ending position" of the template. It's where
* where the caret moves after it cycles through all other parameters. If the * the caret moves after it cycles through all other parameters. If the user
* user types into it, template mode terminates. If more than one * types into it, template mode terminates. If more than one
* <code>${cursor}</code> parameter is specified, behavior is undefined. * <code>${cursor}</code> parameter is specified, behavior is undefined.
* <p> * <p>
* *
@@ -51,15 +51,15 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* fault-tolerant. * fault-tolerant.
* <p> * <p>
* *
* Leading whitespace is automatically added to lines if the template spans * Leading whitespace is automatically added to lines if the template spans more
* more than one line, and if used with a text component using a * than one line, and if used with a text component using a
* <code>PlainDocument</code>, tabs will be converted to spaces if requested. * <code>PlainDocument</code>, tabs will be converted to spaces if requested.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class TemplateCompletion extends AbstractCompletion implements ParameterizedCompletion { public class TemplateCompletion extends AbstractCompletion implements ParameterizedCompletion {
private List<TemplatePiece> pieces; private List<TemplatePiece> pieces;
private String inputText; private String inputText;
private String definitionString; private String definitionString;
@@ -69,11 +69,11 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
* The template's parameters. * The template's parameters.
*/ */
private List<Parameter> params; private List<Parameter> params;
public TemplateCompletion(CompletionProvider provider, String inputText, String definitionString, String template) { public TemplateCompletion(CompletionProvider provider, String inputText, String definitionString, String template) {
this(provider, inputText, definitionString, template, null, null); this(provider, inputText, definitionString, template, null, null);
} }
public TemplateCompletion(CompletionProvider provider, String inputText, String definitionString, String template, public TemplateCompletion(CompletionProvider provider, String inputText, String definitionString, String template,
String shortDescription, String summary) { String shortDescription, String summary) {
super(provider); super(provider);
@@ -85,7 +85,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
params = new ArrayList<Parameter>(3); params = new ArrayList<Parameter>(3);
parse(template); parse(template);
} }
private void addTemplatePiece(TemplatePiece piece) { private void addTemplatePiece(TemplatePiece piece) {
pieces.add(piece); pieces.add(piece);
if (piece instanceof Param && !"cursor".equals(piece.getText())) { if (piece instanceof Param && !"cursor".equals(piece.getText())) {
@@ -94,12 +94,12 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
params.add(param); params.add(param);
} }
} }
@Override @Override
public String getInputText() { public String getInputText() {
return inputText; return inputText;
} }
private String getPieceText(int index, String leadingWS) { private String getPieceText(int index, String leadingWS) {
TemplatePiece piece = pieces.get(index); TemplatePiece piece = pieces.get(index);
String text = piece.getText(); String text = piece.getText();
@@ -108,10 +108,10 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
} }
return text; return text;
} }
/** /**
* Returns <code>null</code>; template completions insert all of their * Returns <code>null</code>; template completions insert all of their text via
* text via <code>getInsertionInfo()</code>. * <code>getInsertionInfo()</code>.
* *
* @return <code>null</code> always. * @return <code>null</code> always.
*/ */
@@ -119,21 +119,21 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public String getReplacementText() { public String getReplacementText() {
return null; return null;
} }
@Override @Override
public String getSummary() { public String getSummary() {
return summary; return summary;
} }
@Override @Override
public String getDefinitionString() { public String getDefinitionString() {
return definitionString; return definitionString;
} }
public String getShortDescription() { public String getShortDescription() {
return shortDescription; return shortDescription;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -141,7 +141,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public boolean getShowParameterToolTip() { public boolean getShowParameterToolTip() {
return false; return false;
} }
@Override @Override
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces) { public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces) {
ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo(); ParameterizedCompletionInsertionInfo info = new ParameterizedCompletionInsertionInfo();
@@ -214,7 +214,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
info.setTextToInsert(sb.toString()); info.setTextToInsert(sb.toString());
return info; return info;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -222,7 +222,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public Parameter getParam(int index) { public Parameter getParam(int index) {
return params.get(index); return params.get(index);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -230,12 +230,11 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
public int getParamCount() { public int getParamCount() {
return params == null ? 0 : params.size(); return params == null ? 0 : params.size();
} }
/** /**
* Returns whether a parameter is already defined with a specific name. * Returns whether a parameter is already defined with a specific name.
* *
* @param name * @param name The name.
* The name.
* @return Whether a parameter is defined with that name. * @return Whether a parameter is defined with that name.
*/ */
private boolean isParamDefined(String name) { private boolean isParamDefined(String name) {
@@ -247,12 +246,11 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
} }
return false; return false;
} }
/** /**
* Parses a template string into logical pieces used by this class. * Parses a template string into logical pieces used by this class.
* *
* @param template * @param template The template to parse.
* The template to parse.
*/ */
private void parse(String template) { private void parse(String template) {
int offs = 0; int offs = 0;
@@ -260,23 +258,23 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
while ((offs = template.indexOf('$', lastOffs)) > -1 && offs < template.length() - 1) { while ((offs = template.indexOf('$', lastOffs)) > -1 && offs < template.length() - 1) {
char next = template.charAt(offs + 1); char next = template.charAt(offs + 1);
switch (next) { switch (next) {
case '$': // "$$" => escaped single dollar sign case '$': // "$$" => escaped single dollar sign
addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs + 1))); addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs + 1)));
lastOffs = offs + 2; lastOffs = offs + 2;
break; break;
case '{': // "${...}" => variable case '{': // "${...}" => variable
int closingCurly = template.indexOf('}', offs + 2); int closingCurly = template.indexOf('}', offs + 2);
if (closingCurly > -1) { if (closingCurly > -1) {
addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs))); addTemplatePiece(new TemplatePiece.Text(template.substring(lastOffs, offs)));
String varName = template.substring(offs + 2, closingCurly); String varName = template.substring(offs + 2, closingCurly);
if (!"cursor".equals(varName) && isParamDefined(varName)) { if (!"cursor".equals(varName) && isParamDefined(varName)) {
addTemplatePiece(new TemplatePiece.ParamCopy(varName)); addTemplatePiece(new TemplatePiece.ParamCopy(varName));
} else { } else {
addTemplatePiece(new TemplatePiece.Param(varName)); addTemplatePiece(new TemplatePiece.Param(varName));
}
lastOffs = closingCurly + 1;
} }
break; lastOffs = closingCurly + 1;
}
break;
} }
} }
if (lastOffs < template.length()) { if (lastOffs < template.length()) {
@@ -284,7 +282,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
addTemplatePiece(new TemplatePiece.Text(text)); addTemplatePiece(new TemplatePiece.Text(text));
} }
} }
private int possiblyReplaceTabsWithSpaces(StringBuilder sb, String text, JTextComponent tc, int start) { private int possiblyReplaceTabsWithSpaces(StringBuilder sb, String text, JTextComponent tc, int start) {
int tab = text.indexOf('\t'); int tab = text.indexOf('\t');
if (tab > -1) { if (tab > -1) {
@@ -315,18 +313,17 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
} }
return start; return start;
} }
/** /**
* Sets the short description of this template completion. * Sets the short description of this template completion.
* *
* @param shortDesc * @param shortDesc The new short description.
* The new short description.
* @see #getShortDescription() * @see #getShortDescription()
*/ */
public void setShortDescription(String shortDesc) { public void setShortDescription(String shortDesc) {
this.shortDescription = shortDesc; this.shortDescription = shortDesc;
} }
@Override @Override
public String toString() { public String toString() {
return getDefinitionString(); return getDefinitionString();
+18 -18
View File
@@ -7,68 +7,68 @@
package org.fife.ui.autocomplete; package org.fife.ui.autocomplete;
/** /**
* A piece of a <code>TemplateCompletion</code>. You add instances of this * A piece of a <code>TemplateCompletion</code>. You add instances of this class
* class to template completions to define them. * to template completions to define them.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
* @see TemplateCompletion * @see TemplateCompletion
*/ */
interface TemplatePiece { interface TemplatePiece {
String getText(); String getText();
public class Text implements TemplatePiece { public class Text implements TemplatePiece {
private String text; private String text;
public Text(String text) { public Text(String text) {
this.text = text; this.text = text;
} }
@Override @Override
public String getText() { public String getText() {
return text; return text;
} }
@Override @Override
public String toString() { public String toString() {
return "[TemplatePiece.Text: text=" + text + "]"; return "[TemplatePiece.Text: text=" + text + "]";
} }
} }
public class Param implements TemplatePiece { public class Param implements TemplatePiece {
String text; String text;
public Param(String text) { public Param(String text) {
this.text = text; this.text = text;
} }
@Override @Override
public String getText() { public String getText() {
return text; return text;
} }
@Override @Override
public String toString() { public String toString() {
return "[TemplatePiece.Param: param=" + text + "]"; return "[TemplatePiece.Param: param=" + text + "]";
} }
} }
public class ParamCopy implements TemplatePiece { public class ParamCopy implements TemplatePiece {
private String text; private String text;
public ParamCopy(String text) { public ParamCopy(String text) {
this.text = text; this.text = text;
} }
@Override @Override
public String getText() { public String getText() {
return text; return text;
} }
@Override @Override
public String toString() { public String toString() {
return "[TemplatePiece.ParamCopy: param=" + text + "]"; 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. * Static utility methods for homemade tool tips.
* <p> * <p>
* *
* This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class * This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class of
* of the same name, but isn't re-used to prevent a hard dependency on the * the same name, but isn't re-used to prevent a hard dependency on the RSTA
* RSTA library. * library.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class TipUtil { class TipUtil {
private TipUtil() {} private TipUtil() {
}
/** /**
* Returns the default background color to use for tool tip windows. * Returns the default background color to use for tool tip windows.
* *
@@ -55,7 +56,7 @@ class TipUtil {
} }
return c; return c;
} }
/** /**
* Returns the border used by tool tips in this look and feel. * Returns the border used by tool tips in this look and feel.
* *
@@ -71,21 +72,20 @@ class TipUtil {
} }
return border; return border;
} }
/** /**
* Returns whether a color is a Nimbus DerivedColor, which is troublesome * Returns whether a color is a Nimbus DerivedColor, which is troublesome in
* in that it doesn't use its RGB values (uses HSB instead?) and so * that it doesn't use its RGB values (uses HSB instead?) and so querying them
* querying them is useless. * is useless.
* *
* @param c * @param c The color to check.
* The color to check.
* @return Whether it is a DerivedColor * @return Whether it is a DerivedColor
*/ */
private static final boolean isDerivedColor(Color c) { private static final boolean isDerivedColor(Color c) {
return c != null return c != null && (c.getClass().getName().endsWith(".DerivedColor")
&& (c.getClass().getName().endsWith(".DerivedColor") || c.getClass().getName().endsWith(".DerivedColor$UIResource")); || c.getClass().getName().endsWith(".DerivedColor$UIResource"));
} }
/** /**
* Returns whether the Nimbus Look and Feel is installed. * Returns whether the Nimbus Look and Feel is installed.
* *
@@ -94,14 +94,13 @@ class TipUtil {
private static final boolean isNimbusLookAndFeel() { private static final boolean isNimbusLookAndFeel() {
return UIManager.getLookAndFeel().getName().equals("Nimbus"); return UIManager.getLookAndFeel().getName().equals("Nimbus");
} }
/** /**
* Tweaks a <code>JEditorPane</code> so it can be used to render the * Tweaks a <code>JEditorPane</code> so it can be used to render the content in
* content in a focusable pseudo-tool tip. It is assumed that the editor * a focusable pseudo-tool tip. It is assumed that the editor pane is using an
* pane is using an <code>HTMLDocument</code>. * <code>HTMLDocument</code>.
* *
* @param textArea * @param textArea The editor pane to tweak.
* The editor pane to tweak.
*/ */
public static void tweakTipEditorPane(JEditorPane textArea) { public static void tweakTipEditorPane(JEditorPane textArea) {
// Jump through a few hoops to get things looking nice in Nimbus // 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); font = new Font("SansSerif", Font.PLAIN, 12);
} }
HTMLDocument doc = (HTMLDocument) textArea.getDocument(); HTMLDocument doc = (HTMLDocument) textArea.getDocument();
doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt" + "; color: " doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt"
+ Util.getHexString(fg) + "; }"); + "; color: " + Util.getHexString(fg) + "; }");
// Always add link foreground rule. Unfortunately these CSS rules // Always add link foreground rule. Unfortunately these CSS rules
// stack each time the LaF is changed (how can we overwrite them // stack each time the LaF is changed (how can we overwrite them
// without clearing out the important "standard" ones?). // without clearing out the important "standard" ones?).
+59 -67
View File
@@ -28,31 +28,31 @@ import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
* @version 1.0 * @version 1.0
*/ */
public class Util { public class Util {
/** /**
* If a system property is defined with this name and set, ignoring case, * If a system property is defined with this name and set, ignoring case, to
* to <code>true</code>, this library will not attempt to use Substance * <code>true</code>, this library will not attempt to use Substance renderers.
* renderers. Otherwise, if a Substance Look and Feel is installed, we * Otherwise, if a Substance Look and Feel is installed, we will attempt to use
* will attempt to use Substance cell renderers in all of our dropdowns. * Substance cell renderers in all of our dropdowns.
* <p> * <p>
* *
* Note that we do not have a build dependency on Substance, so all access * Note that we do not have a build dependency on Substance, so all access to
* to Substance stuff is done via reflection. We will fall back onto * Substance stuff is done via reflection. We will fall back onto default
* default renderers if something goes horribly wrong. * renderers if something goes horribly wrong.
*/ */
public static final String PROPERTY_DONT_USE_SUBSTANCE_RENDERERS = "org.fife.ui.autocomplete.DontUseSubstanceRenderers"; 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 * If this system property is <code>true</code>, then even the "main" two
* auto-complete windows will allow window decorations via * auto-complete windows will allow window decorations via
* {@link PopupWindowDecorator}. If this property is undefined or * {@link PopupWindowDecorator}. If this property is undefined or
* <code>false</code>, they won't honor such decorations. This is due to * <code>false</code>, they won't honor such decorations. This is due to certain
* certain performance issues with translucent windows (used for drop * performance issues with translucent windows (used for drop shadows), even as
* shadows), even as of Java 7u2. * of Java 7u2.
*/ */
public static final String PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS = "org.fife.ui.autocomplete.allowDecoratedAutoCompleteWindows"; 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 * Used for the color of hyperlinks when a LookAndFeel uses light text against a
* against a dark background. * dark background.
*/ */
private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff); private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);
private static final Pattern TAG_PATTERN = Pattern.compile("<[^>]*>"); private static final Pattern TAG_PATTERN = Pattern.compile("<[^>]*>");
@@ -60,15 +60,14 @@ public class Util {
private static boolean desktopCreationAttempted; private static boolean desktopCreationAttempted;
private static Object desktop; private static Object desktop;
private static final Object LOCK_DESKTOP_CREATION = new Object(); private static final Object LOCK_DESKTOP_CREATION = new Object();
/** /**
* Attempts to open a web browser to the specified URI. * Attempts to open a web browser to the specified URI.
* *
* @param uri * @param uri The URI to open. If this is <code>null</code>, nothing happens and
* The URI to open. If this is <code>null</code>, nothing * this method returns <code>false</code>.
* happens and this method returns <code>false</code>. * @return Whether the operation was successful. This will be <code>false</code>
* @return Whether the operation was successful. This will be * on JRE's older than 1.6.
* <code>false</code> on JRE's older than 1.6.
*/ */
public static boolean browse(URI uri) { public static boolean browse(URI uri) {
boolean success = false; boolean success = false;
@@ -88,11 +87,11 @@ public class Util {
} }
return success; return success;
} }
/** /**
* Returns the singleton <code>java.awt.Desktop</code> instance, or * Returns the singleton <code>java.awt.Desktop</code> instance, or
* <code>null</code> if it is unsupported on this platform (or the JRE * <code>null</code> if it is unsupported on this platform (or the JRE is older
* is older than 1.6). * than 1.6).
* *
* @return The desktop, as an {@link Object}. * @return The desktop, as an {@link Object}.
*/ */
@@ -117,14 +116,13 @@ public class Util {
} }
return desktop; return desktop;
} }
/** /**
* Returns a hex string for the specified color, suitable for HTML. * Returns a hex string for the specified color, suitable for HTML.
* *
* @param c * @param c The color.
* The color. * @return The string representation, in the form "<code>#rrggbb</code>", or
* @return The string representation, in the form "<code>#rrggbb</code>", * <code>null</code> if <code>c</code> is <code>null</code>.
* or <code>null</code> if <code>c</code> is <code>null</code>.
*/ */
public static String getHexString(Color c) { public static String getHexString(Color c) {
if (c == null) { if (c == null) {
@@ -150,12 +148,12 @@ public class Util {
sb.append(Integer.toHexString(b)); sb.append(Integer.toHexString(b));
return sb.toString(); return sb.toString();
} }
/** /**
* Returns the color to use for hyperlink-style components. This method * Returns the color to use for hyperlink-style components. This method will
* will return <code>Color.blue</code> unless it appears that the current * return <code>Color.blue</code> unless it appears that the current LookAndFeel
* LookAndFeel uses light text on a dark background, in which case a * uses light text on a dark background, in which case a brighter alternative is
* brighter alternative is returned. * returned.
* *
* @return The color to use for hyperlinks. * @return The color to use for hyperlinks.
*/ */
@@ -168,16 +166,14 @@ public class Util {
} }
return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue; return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue;
} }
/** /**
* Returns the screen coordinates for the monitor that contains the * Returns the screen coordinates for the monitor that contains the specified
* specified point. This is useful for setups with multiple monitors, * point. This is useful for setups with multiple monitors, to ensure that popup
* to ensure that popup windows are positioned properly. * windows are positioned properly.
* *
* @param x * @param x The x-coordinate, in screen coordinates.
* The x-coordinate, in screen coordinates. * @param y The y-coordinate, in screen coordinates.
* @param y
* The y-coordinate, in screen coordinates.
* @return The bounds of the monitor that contains the specified point. * @return The bounds of the monitor that contains the specified point.
*/ */
public static Rectangle getScreenBoundsForPoint(int x, int y) { 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 (?) // If point is outside all monitors, default to default monitor (?)
return env.getMaximumWindowBounds(); return env.getMaximumWindowBounds();
} }
/** /**
* Give apps a chance to decorate us with drop shadows, etc. Since very * Give apps a chance to decorate us with drop shadows, etc. Since very scrolly
* scrolly things such as lists (of e.g. completions) are *very* slow when * things such as lists (of e.g. completions) are *very* slow when in per-pixel
* in per-pixel translucent windows, even as of Java 7u2, we force the user * translucent windows, even as of Java 7u2, we force the user to specify an
* to specify an extra option for the two "main" auto-complete windows. * extra option for the two "main" auto-complete windows.
* *
* @return Whether to allow decorating the main auto-complete windows. * @return Whether to allow decorating the main auto-complete windows.
* @see #PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS * @see #PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS
@@ -210,40 +206,37 @@ public class Util {
return false; return false;
} }
} }
/** /**
* Returns whether we should attempt to use Substance cell renderers and * Returns whether we should attempt to use Substance cell renderers and styles
* styles for things such as completion choices, if a Substance Look and * for things such as completion choices, if a Substance Look and Feel is
* Feel is installed. If this is <code>false</code>, we'll use our * installed. If this is <code>false</code>, we'll use our standard rendering
* standard rendering for completions, even when Substance is being used. * for completions, even when Substance is being used.
* *
* @return Whether to use Substance renderers if Substance is installed. * @return Whether to use Substance renderers if Substance is installed.
*/ */
public static boolean getUseSubstanceRenderers() { public static boolean getUseSubstanceRenderers() {
return useSubstanceRenderers; return useSubstanceRenderers;
} }
/** /**
* Returns whether the specified color is "light" to use as a foreground. * Returns whether the specified color is "light" to use as a foreground. Colors
* Colors that return <code>true</code> indicate that the current Look and * that return <code>true</code> indicate that the current Look and Feel
* Feel probably uses light text colors on a dark background. * probably uses light text colors on a dark background.
* *
* @param fg * @param fg The foreground color.
* The foreground color.
* @return Whether it is a "light" foreground color. * @return Whether it is a "light" foreground color.
*/ */
public static final boolean isLightForeground(Color fg) { public static final boolean isLightForeground(Color fg) {
return fg.getRed() > 0xa0 && fg.getGreen() > 0xa0 && fg.getBlue() > 0xa0; return fg.getRed() > 0xa0 && fg.getGreen() > 0xa0 && fg.getBlue() > 0xa0;
} }
/** /**
* Returns whether a string starts with a specified prefix, ignoring case. * Returns whether a string starts with a specified prefix, ignoring case. This
* This method does not support characters outside of the BMP. * method does not support characters outside of the BMP.
* *
* @param str * @param str The string to check. This cannot be {@code null}.
* The string to check. This cannot be {@code null}. * @param prefix The prefix to check for. 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. * @return Whether {@code str} starts with {@code prefix}, ignoring case.
*/ */
public static boolean startsWithIgnoreCase(String str, String prefix) { public static boolean startsWithIgnoreCase(String str, String prefix) {
@@ -253,13 +246,12 @@ public class Util {
} }
return false; return false;
} }
/** /**
* Strips any HTML from a string. The string must start with * Strips any HTML from a string. The string must start with
* "<code>&lt;html&gt;</code>" for markup tags to be stripped. * "<code>&lt;html&gt;</code>" for markup tags to be stripped.
* *
* @param text * @param text The string.
* The string.
* @return The string, with any HTML stripped. * @return The string, with any HTML stripped.
*/ */
public static String stripHtml(String text) { public static String stripHtml(String text) {
@@ -269,7 +261,7 @@ public class Util {
// TODO: Micro-optimize me, might be called in renderers and loops // TODO: Micro-optimize me, might be called in renderers and loops
return TAG_PATTERN.matcher(text).replaceAll(""); return TAG_PATTERN.matcher(text).replaceAll("");
} }
static { static {
boolean use = true; boolean use = true;
try { try {
@@ -13,16 +13,16 @@ import javax.swing.text.JTextComponent;
* <p> * <p>
* *
* This completion type uses its <tt>shortDescription</tt> property as part of * This completion type uses its <tt>shortDescription</tt> property as part of
* its summary returned by {@link #getSummary()}; for this reason, it may be * its summary returned by {@link #getSummary()}; for this reason, it may be a
* a little longer (even much longer), if desired, than what is recommended * little longer (even much longer), if desired, than what is recommended for
* for <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used * <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used in
* in {@link #toString()} for <tt>ListCellRenderers</tt>). * {@link #toString()} for <tt>ListCellRenderers</tt>).
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class VariableCompletion extends BasicCompletion { public class VariableCompletion extends BasicCompletion {
/** /**
* The variable's type. * The variable's type.
*/ */
@@ -31,27 +31,24 @@ public class VariableCompletion extends BasicCompletion {
* What library (for example) this variable is defined in. * What library (for example) this variable is defined in.
*/ */
private String definedIn; private String definedIn;
/** /**
* Constructor. * Constructor.
* *
* @param provider * @param provider The parent provider.
* The parent provider. * @param name The name of this variable.
* @param name * @param type The type of this variable (e.g. "<code>int</code>",
* The name of this variable. * "<code>String</code>", etc.).
* @param type
* The type of this variable (e.g. "<code>int</code>",
* "<code>String</code>", etc.).
*/ */
public VariableCompletion(CompletionProvider provider, String name, String type) { public VariableCompletion(CompletionProvider provider, String name, String type) {
super(provider, name); super(provider, name);
this.type = type; this.type = type;
} }
protected void addDefinitionString(StringBuilder sb) { protected void addDefinitionString(StringBuilder sb) {
sb.append("<html><b>").append(getDefinitionString()).append("</b>"); sb.append("<html><b>").append(getDefinitionString()).append("</b>");
} }
public String getDefinitionString() { public String getDefinitionString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
// Add the return type if applicable (C macros like NULL have no type). // 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()); sb.append(getName());
return sb.toString(); return sb.toString();
} }
/** /**
* Returns where this variable is defined. * Returns where this variable is defined.
* *
@@ -72,7 +69,7 @@ public class VariableCompletion extends BasicCompletion {
public String getDefinedIn() { public String getDefinedIn() {
return definedIn; return definedIn;
} }
/** /**
* Returns the name of this variable. * Returns the name of this variable.
* *
@@ -81,7 +78,7 @@ public class VariableCompletion extends BasicCompletion {
public String getName() { public String getName() {
return getReplacementText(); return getReplacementText();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -93,29 +90,25 @@ public class VariableCompletion extends BasicCompletion {
possiblyAddDefinedIn(sb); possiblyAddDefinedIn(sb);
return sb.toString(); return sb.toString();
} }
/** /**
* Returns the tool tip text to display for mouse hovers over this * Returns the tool tip text to display for mouse hovers over this completion.
* completion.
* <p> * <p>
* *
* Note that for this functionality to be enabled, a * Note that for this functionality to be enabled, a <tt>JTextComponent</tt>
* <tt>JTextComponent</tt> must be registered with the * must be registered with the <tt>ToolTipManager</tt>, and the text component
* <tt>ToolTipManager</tt>, and the text component must know to search * must know to search for this value. In the case of an
* for this value. In the case of an * <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this can
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this * be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that calls into
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}. * {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
* *
* @return The tool tip text for this completion, or <code>null</code> if * @return The tool tip text for this completion, or <code>null</code> if none.
* none.
*/ */
@Override @Override
public String getToolTipText() { public String getToolTipText() {
return getDefinitionString(); return getDefinitionString();
} }
/** /**
* Returns the type of this variable. * Returns the type of this variable.
* *
@@ -124,13 +117,12 @@ public class VariableCompletion extends BasicCompletion {
public String getType() { public String getType() {
return type; return type;
} }
/** /**
* Adds some HTML describing where this variable is defined, if this * Adds some HTML describing where this variable is defined, if this information
* information is known. * is known.
* *
* @param sb * @param sb The buffer to append to.
* The buffer to append to.
*/ */
protected void possiblyAddDefinedIn(StringBuilder sb) { protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn != null) { if (definedIn != null) {
@@ -138,13 +130,11 @@ public class VariableCompletion extends BasicCompletion {
sb.append(" <em>").append(definedIn).append("</em>"); sb.append(" <em>").append(definedIn).append("</em>");
} }
} }
/** /**
* Adds the description text as HTML to a buffer, if a description is * Adds the description text as HTML to a buffer, if a description is defined.
* defined.
* *
* @param sb * @param sb The buffer to append to.
* The buffer to append to.
* @return Whether there was a description to add. * @return Whether there was a description to add.
*/ */
protected boolean possiblyAddDescription(StringBuilder sb) { protected boolean possiblyAddDescription(StringBuilder sb) {
@@ -156,18 +146,17 @@ public class VariableCompletion extends BasicCompletion {
} }
return false; return false;
} }
/** /**
* Sets where this variable is defined. * Sets where this variable is defined.
* *
* @param definedIn * @param definedIn Where this variable is defined.
* Where this variable is defined.
* @see #getDefinedIn() * @see #getDefinedIn()
*/ */
public void setDefinedIn(String definedIn) { public void setDefinedIn(String definedIn) {
this.definedIn = definedIn; this.definedIn = definedIn;
} }
/** /**
* Overridden to return the name of the variable being completed. * Overridden to return the name of the variable being completed.
* *
@@ -30,18 +30,18 @@ import org.fife.ui.rtextarea.RTextArea;
* @version 1.0 * @version 1.0
*/ */
public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker { public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
private final Action INSERT_BREAK_ACTION; private final Action INSERT_BREAK_ACTION;
private static final Pattern MLC_PATTERN = Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)"); private static final Pattern MLC_PATTERN = Pattern.compile("([ \\t]*)(/?[\\*]+)([ \\t]*)");
protected AbstractJFlexCTokenMaker() { protected AbstractJFlexCTokenMaker() {
INSERT_BREAK_ACTION = createInsertBreakAction(); INSERT_BREAK_ACTION = createInsertBreakAction();
} }
/** /**
* Creates and returns the action to use when the user inserts a newline. * Creates and returns the action to use when the user inserts a newline. The
* The default implementation intelligently closes multi-line comments. * default implementation intelligently closes multi-line comments. Subclasses
* Subclasses can override. * can override.
* *
* @return The action. * @return The action.
* @see #getInsertBreakAction() * @see #getInsertBreakAction()
@@ -49,10 +49,10 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
protected Action createInsertBreakAction() { protected Action createInsertBreakAction() {
return new CStyleInsertBreakAction(); return new CStyleInsertBreakAction();
} }
/** /**
* Returns <code>true</code> always as C-style languages use curly braces * Returns <code>true</code> always as C-style languages use curly braces to
* to denote code blocks. * denote code blocks.
* *
* @return <code>true</code> always. * @return <code>true</code> always.
*/ */
@@ -60,11 +60,10 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) { public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return true; return true;
} }
/** /**
* Returns an action to handle "insert break" key presses (i.e. Enter). * Returns an action to handle "insert break" key presses (i.e. Enter). An
* An action is returned that handles newlines differently in multi-line * action is returned that handles newlines differently in multi-line comments.
* comments.
* *
* @return The action. * @return The action.
*/ */
@@ -72,7 +71,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public Action getInsertBreakAction() { public Action getInsertBreakAction() {
return INSERT_BREAK_ACTION; return INSERT_BREAK_ACTION;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -80,7 +79,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
public boolean getMarkOccurrencesOfTokenType(int type) { public boolean getMarkOccurrencesOfTokenType(int type) {
return type == Token.IDENTIFIER || type == Token.FUNCTION; return type == Token.IDENTIFIER || type == Token.FUNCTION;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -92,19 +91,17 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
} }
return false; return false;
} }
/** /**
* Returns whether a given token is an internal token type that represents * Returns whether a given token is an internal token type that represents an
* an MLC or documentation comment continuing on to the next line. This is * MLC or documentation comment continuing on to the next line. This is done by
* done by languages such as JavaScript that are a little more verbose * languages such as JavaScript that are a little more verbose than necessary so
* than necessary so that their code can be copy-and-pasted into other * that their code can be copy-and-pasted into other <code>TokenMaker</code>s
* <code>TokenMaker</code>s that use them as nested languages (such as * that use them as nested languages (such as HTML, JSP, etc.).
* HTML, JSP, etc.).
* *
* @param t * @param t The token to check. This cannot be <code>null</code>.
* The token to check. This cannot be <code>null</code>. * @return Whether the token is an internal token representing the end of a line
* @return Whether the token is an internal token representing the end of * for an MLC/doc comment continuing on to the next line.
* a line for an MLC/doc comment continuing on to the next line.
*/ */
private boolean isInternalEolTokenForMLCs(Token t) { private boolean isInternalEolTokenForMLCs(Token t) {
int type = t.getType(); int type = t.getType();
@@ -114,13 +111,13 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
} }
return false; return false;
} }
/** /**
* Action that knows how to special-case inserting a newline in a * Action that knows how to special-case inserting a newline in a multi-line
* multi-line comment for languages like C and Java. * comment for languages like C and Java.
*/ */
protected class CStyleInsertBreakAction extends RSyntaxTextAreaEditorKit.InsertBreakAction { protected class CStyleInsertBreakAction extends RSyntaxTextAreaEditorKit.InsertBreakAction {
@Override @Override
public void actionPerformedImpl(ActionEvent e, RTextArea textArea) { public void actionPerformedImpl(ActionEvent e, RTextArea textArea) {
if (!textArea.isEditable() || !textArea.isEnabled()) { if (!textArea.isEditable() || !textArea.isEnabled()) {
@@ -141,13 +138,12 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
handleInsertBreak(rsta, true); handleInsertBreak(rsta, true);
} }
} }
/** /**
* Returns whether the MLC token containing <code>offs</code> appears * Returns whether the MLC token containing <code>offs</code> appears to have a
* to have a "nested" comment (i.e., contains "<code>/*</code>" * "nested" comment (i.e., contains "<code>/*</code>" somewhere inside of it).
* somewhere inside of it). This implies that it is likely a "new" MLC * This implies that it is likely a "new" MLC and needs to be closed. While not
* and needs to be closed. While not foolproof, this is usually good * foolproof, this is usually good enough of a sign.
* enough of a sign.
* *
* @param textArea * @param textArea
* @param line * @param line
@@ -183,7 +179,7 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
} }
return true; // No match - MLC goes to the end of the file return true; // No match - MLC goes to the end of the file
} }
private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea, int line) { private void insertBreakInMLC(ActionEvent e, RSyntaxTextArea textArea, int line) {
Matcher m = null; Matcher m = null;
int start = -1; int start = -1;
@@ -11,35 +11,32 @@ import javax.swing.text.Segment;
/** /**
* Base class for JFlex-generated token makers. This class attempts to factor * 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 * out all common code from these classes. Many methods <em>almost</em> could be
* be factored out into this class, but cannot because they reference JFlex * factored out into this class, but cannot because they reference JFlex
* variables that we cannot access from this class. * variables that we cannot access from this class.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 0.1 * @version 0.1
*/ */
public abstract class AbstractJFlexTokenMaker extends TokenMakerBase { public abstract class AbstractJFlexTokenMaker extends TokenMakerBase {
protected Segment s; protected Segment s;
protected int start; // Just for states. protected int start; // Just for states.
protected int offsetShift; // As parser always starts at 0, but our line protected int offsetShift; // As parser always starts at 0, but our line
// doesn't. // doesn't.
/** /**
* Declared here so we can define overloads that refer to this method. * Declared here so we can define overloads that refer to this method.
* *
* @param newState * @param newState The new JFlex state to enter.
* The new JFlex state to enter.
*/ */
public abstract void yybegin(int newState); public abstract void yybegin(int newState);
/** /**
* Starts a new JFlex state and changes the current language index. * Starts a new JFlex state and changes the current language index.
* *
* @param state * @param state The new JFlex state to enter.
* The new JFlex state to enter. * @param languageIndex The new language index.
* @param languageIndex
* The new language index.
*/ */
protected void yybegin(int state, int languageIndex) { protected void yybegin(int state, int languageIndex) {
yybegin(state); yybegin(state);
@@ -8,9 +8,9 @@ package org.fife.ui.rsyntaxtextarea;
/** /**
* An abstract implementation of the * An abstract implementation of the
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should * {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should be
* be overridden for every language for which you want to provide * overridden for every language for which you want to provide syntax
* syntax highlighting. * highlighting.
* <p> * <p>
* *
* @see Token * @see Token
@@ -19,34 +19,33 @@ package org.fife.ui.rsyntaxtextarea;
* @version 0.2 * @version 0.2
*/ */
public abstract class AbstractTokenMaker extends TokenMakerBase { public abstract class AbstractTokenMaker extends TokenMakerBase {
/** /**
* Hash table of words to highlight and what token type they are. * Hash table of words to highlight and what token type they are. The keys are
* The keys are the words to highlight, and their values are the * the words to highlight, and their values are the token types, for example,
* token types, for example, <code>Token.RESERVED_WORD</code> or * <code>Token.RESERVED_WORD</code> or <code>Token.FUNCTION</code>.
* <code>Token.FUNCTION</code>.
*/ */
protected TokenMap wordsToHighlight; protected TokenMap wordsToHighlight;
/** /**
* Constructor. * Constructor.
*/ */
public AbstractTokenMaker() { public AbstractTokenMaker() {
wordsToHighlight = getWordsToHighlight(); wordsToHighlight = getWordsToHighlight();
} }
/** /**
* Returns the words to highlight for this programming language. * Returns the words to highlight for this programming language.
* *
* @return A <code>TokenMap</code> containing the words to highlight for * @return A <code>TokenMap</code> containing the words to highlight for this
* this programming language. * programming language.
*/ */
public abstract TokenMap getWordsToHighlight(); public abstract TokenMap getWordsToHighlight();
/** /**
* Removes the token last added from the linked list of tokens. The * Removes the token last added from the linked list of tokens. The programmer
* programmer should never have to call this directly; it can be called * should never have to call this directly; it can be called by subclasses of
* by subclasses of <code>TokenMaker</code> if necessary. * <code>TokenMaker</code> if necessary.
*/ */
public void removeLastToken() { public void removeLastToken() {
if (previousToken == null) { if (previousToken == null) {
@@ -18,15 +18,14 @@ import java.util.Set;
* @version 1.0 * @version 1.0
*/ */
public abstract class AbstractTokenMakerFactory extends TokenMakerFactory { public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/** /**
* A mapping from keys to the names of {@link TokenMaker} implementation * A mapping from keys to the names of {@link TokenMaker} implementation class
* class names. When {@link #getTokenMaker(String)} is called with a key * names. When {@link #getTokenMaker(String)} is called with a key defined in
* defined in this map, a <code>TokenMaker</code> of the corresponding type * this map, a <code>TokenMaker</code> of the corresponding type is returned.
* is returned.
*/ */
private Map<String, Object> tokenMakerMap; private Map<String, Object> tokenMakerMap;
/** /**
* Constructor. * Constructor.
*/ */
@@ -34,14 +33,13 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
tokenMakerMap = new HashMap<String, Object>(); tokenMakerMap = new HashMap<String, Object>();
initTokenMakerMap(); initTokenMakerMap();
} }
/** /**
* Returns a {@link TokenMaker} for the specified key. * Returns a {@link TokenMaker} for the specified key.
* *
* @param key * @param key The key.
* The key. * @return The corresponding <code>TokenMaker</code>, or <code>null</code> if
* @return The corresponding <code>TokenMaker</code>, or <code>null</code> * none matches the specified key.
* if none matches the specified key.
*/ */
@Override @Override
protected TokenMaker getTokenMakerImpl(String key) { protected TokenMaker getTokenMakerImpl(String key) {
@@ -57,18 +55,18 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
} }
return null; return null;
} }
/** /**
* Populates the mapping from keys to instances of * Populates the mapping from keys to instances of
* <code>TokenMakerCreator</code>s. Subclasses should override this method * <code>TokenMakerCreator</code>s. Subclasses should override this method and
* and call one of the <code>putMapping</code> overloads to register * call one of the <code>putMapping</code> overloads to register
* {@link TokenMaker}s for syntax constants. * {@link TokenMaker}s for syntax constants.
* *
* @see #putMapping(String, String) * @see #putMapping(String, String)
* @see #putMapping(String, String, ClassLoader) * @see #putMapping(String, String, ClassLoader)
*/ */
protected abstract void initTokenMakerMap(); protected abstract void initTokenMakerMap();
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -76,50 +74,45 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
public Set<String> keySet() { public Set<String> keySet() {
return tokenMakerMap.keySet(); return tokenMakerMap.keySet();
} }
/** /**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation * Adds a mapping from a key to a <code>TokenMaker</code> implementation class
* class name. * name.
* *
* @param key * @param key The key.
* The key. * @param className The <code>TokenMaker</code> class name.
* @param className
* The <code>TokenMaker</code> class name.
* @see #putMapping(String, String, ClassLoader) * @see #putMapping(String, String, ClassLoader)
*/ */
public void putMapping(String key, String className) { public void putMapping(String key, String className) {
putMapping(key, className, null); putMapping(key, className, null);
} }
/** /**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation * Adds a mapping from a key to a <code>TokenMaker</code> implementation class
* class name. * name.
* *
* @param key * @param key The key.
* The key. * @param className The <code>TokenMaker</code> class name.
* @param className * @param cl The class loader to use when loading the class.
* The <code>TokenMaker</code> class name.
* @param cl
* The class loader to use when loading the class.
* @see #putMapping(String, String) * @see #putMapping(String, String)
*/ */
public void putMapping(String key, String className, ClassLoader cl) { public void putMapping(String key, String className, ClassLoader cl) {
tokenMakerMap.put(key, new TokenMakerCreator(className, cl)); tokenMakerMap.put(key, new TokenMakerCreator(className, cl));
} }
/** /**
* Wrapper that handles the creation of TokenMaker instances. * Wrapper that handles the creation of TokenMaker instances.
*/ */
private static class TokenMakerCreator { private static class TokenMakerCreator {
private String className; private String className;
private ClassLoader cl; private ClassLoader cl;
public TokenMakerCreator(String className, ClassLoader cl) { public TokenMakerCreator(String className, ClassLoader cl) {
this.className = className; this.className = className;
this.cl = cl != null ? cl : getClass().getClassLoader(); this.cl = cl != null ? cl : getClass().getClassLoader();
} }
public TokenMaker create() throws Exception { public TokenMaker create() throws Exception {
return (TokenMaker) Class.forName(className, true, cl).newInstance(); return (TokenMaker) Class.forName(className, true, cl).newInstance();
} }
@@ -17,44 +17,39 @@ import java.util.EventObject;
* @version 1.0 * @version 1.0
*/ */
public class ActiveLineRangeEvent extends EventObject { public class ActiveLineRangeEvent extends EventObject {
private int min; private int min;
private int max; private int max;
/** /**
* Constructor. * Constructor.
* *
* @param source * @param source The text area.
* The text area. * @param min The first line in the active line range, or <code>-1</code> if
* @param min * the line range is being cleared.
* The first line in the active line range, or * @param max The last line in the active line range, or <code>-1</code> if
* <code>-1</code> if the line range is being cleared. * 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) { public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
super(source); super(source);
this.min = min; this.min = min;
this.max = max; this.max = max;
} }
/** /**
* Returns the last line in the active line range. * Returns the last line in the active line range.
* *
* @return The last line, or <code>-1</code> if the range is being * @return The last line, or <code>-1</code> if the range is being cleared.
* cleared.
* @see #getMin() * @see #getMin()
*/ */
public int getMax() { public int getMax() {
return max; return max;
} }
/** /**
* Returns the first line in the active line range. * Returns the first line in the active line range.
* *
* @return The first line, or <code>-1</code> if the range is being * @return The first line, or <code>-1</code> if the range is being cleared.
* cleared.
* @see #getMax() * @see #getMax()
*/ */
public int getMin() { public int getMin() {
@@ -13,29 +13,27 @@ import java.util.EventListener;
* Listens for "active line range" events from an <code>RSyntaxTextArea</code>. * Listens for "active line range" events from an <code>RSyntaxTextArea</code>.
* If a text area contains some semantic knowledge of the programming language * If a text area contains some semantic knowledge of the programming language
* being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the * being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the
* caret moves into a new "block" of code. Listeners can listen for these * caret moves into a new "block" of code. Listeners can listen for these events
* events and respond accordingly. * and respond accordingly.
* <p> * <p>
* *
* See the <code>RSTALanguageSupport</code> project at * See the <code>RSTALanguageSupport</code> project at
* <a href="http://fifesoft.com">http://fifesoft.com</a> for some * <a href="http://fifesoft.com">http://fifesoft.com</a> for some
* <code>LanguageSupport</code> implementations that may broadcast these * <code>LanguageSupport</code> implementations that may broadcast these events.
* events. Note that if an RSTA/LanguageSupport does not support broadcasting * Note that if an RSTA/LanguageSupport does not support broadcasting these
* these events, the listener will simply never receive any notifications. * events, the listener will simply never receive any notifications.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public interface ActiveLineRangeListener extends EventListener { public interface ActiveLineRangeListener extends EventListener {
/** /**
* Called whenever the "active line range" changes. * Called whenever the "active line range" changes.
* *
* @param e * @param e Information about the line range change. If there is no longer an
* Information about the line range change. If there is no longer * "active line range," the "minimum" and "maximum" line values should
* an "active line range," the "minimum" and "maximum" line * both be <code>-1</code>.
* values
* should both be <code>-1</code>.
*/ */
void activeLineRangeChanged(ActiveLineRangeEvent e); void activeLineRangeChanged(ActiveLineRangeEvent e);
} }
@@ -13,14 +13,14 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter; import org.fife.ui.rtextarea.SmartHighlightPainter;
/** /**
* The default implementation of {@link OccurrenceMarker}. It goes through * The default implementation of {@link OccurrenceMarker}. It goes through the
* the document and marks all instances of the specified token. * document and marks all instances of the specified token.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
class DefaultOccurrenceMarker implements OccurrenceMarker { class DefaultOccurrenceMarker implements OccurrenceMarker {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -45,7 +45,7 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
} }
return t; return t;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -53,7 +53,7 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) { public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType()); return textArea.getMarkOccurrencesOfTokenType(t.getType());
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -61,21 +61,18 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
public void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) { public void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
markOccurrencesOfToken(doc, t, h, p); markOccurrencesOfToken(doc, t, h, p);
} }
/** /**
* Highlights all instances of tokens identical to <code>t</code> in the * Highlights all instances of tokens identical to <code>t</code> in the
* specified document. * specified document.
* *
* @param doc * @param doc The document.
* The document. * @param t The document whose relevant occurrences should be marked.
* @param t * @param h The highlighter to add the highlights to.
* The document whose relevant occurrences should be marked. * @param p The painter for the highlights.
* @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(); char[] lexeme = t.getLexeme().toCharArray();
int type = t.getType(); int type = t.getType();
int lineCount = doc.getDefaultRootElement().getElementCount(); 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 * This class generates tokens for a {@link TokenMaker}. This class is here
* because it reuses tokens when they aren't needed anymore to prevent * because it reuses tokens when they aren't needed anymore to prevent This
* This class doesn't actually create new tokens every time * class doesn't actually create new tokens every time <code>createToken</code>
* <code>createToken</code> is called. Instead, it internally keeps a stack of * is called. Instead, it internally keeps a stack of available already-created
* available already-created tokens. When more tokens are needed to properly * tokens. When more tokens are needed to properly display a line, more tokens
* display a line, more tokens are added to the available stack. This saves * are added to the available stack. This saves from needless repetitive memory
* from needless repetitive memory allocation. However, it makes it IMPERATIVE * allocation. However, it makes it IMPERATIVE that users call
* that users call <code>resetTokenList</code> when creating a new token list so * <code>resetTokenList</code> when creating a new token list so that the token
* that the token maker can keep an accurate list of available tokens. * maker can keep an accurate list of available tokens.
* <p> * <p>
* *
* NOTE: This class should only be used by {@link TokenMaker}; nobody else * NOTE: This class should only be used by {@link TokenMaker}; nobody else needs
* needs it! * it!
* *
* @author Robert Futrell * @author Robert Futrell
* @version 0.1 * @version 0.1
*/ */
class DefaultTokenFactory implements TokenFactory { class DefaultTokenFactory implements TokenFactory {
private int size; private int size;
private int increment; private int increment;
private TokenImpl[] tokenList; private TokenImpl[] tokenList;
private int currentFreeToken; private int currentFreeToken;
protected static final int DEFAULT_START_SIZE = 30; protected static final int DEFAULT_START_SIZE = 30;
protected static final int DEFAULT_INCREMENT = 10; protected static final int DEFAULT_INCREMENT = 10;
/** /**
* Constructor. * Constructor.
*/ */
DefaultTokenFactory() { DefaultTokenFactory() {
this(DEFAULT_START_SIZE, DEFAULT_INCREMENT); this(DEFAULT_START_SIZE, DEFAULT_INCREMENT);
} }
/** /**
* Constructor. * Constructor.
* *
* @param size * @param size The initial number of tokens in this factory.
* The initial number of tokens in this factory. * @param increment How many tokens to increment by when the stack gets empty.
* @param increment
* How many tokens to increment by when the stack gets
* empty.
*/ */
DefaultTokenFactory(int size, int increment) { DefaultTokenFactory(int size, int increment) {
this.size = size; this.size = size;
@@ -61,10 +58,10 @@ class DefaultTokenFactory implements TokenFactory {
tokenList[i] = new TokenImpl(); tokenList[i] = new TokenImpl();
} }
} }
/** /**
* Adds tokens to the internal token list. This is called whenever a * Adds tokens to the internal token list. This is called whenever a request is
* request is made and no more tokens are available. * made and no more tokens are available.
*/ */
private void augmentTokenList() { private void augmentTokenList() {
TokenImpl[] temp = new TokenImpl[size + increment]; TokenImpl[] temp = new TokenImpl[size + increment];
@@ -76,37 +73,37 @@ class DefaultTokenFactory implements TokenFactory {
} }
// System.err.println("... size up to: " + size); // System.err.println("... size up to: " + size);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public TokenImpl createToken() { public TokenImpl createToken() {
TokenImpl token = tokenList[currentFreeToken]; TokenImpl token = tokenList[currentFreeToken];
token.text = null; token.reset();
token.setType(Token.NULL); if (++currentFreeToken == size) {
token.setOffset(-1);
token.setNextToken(null);
currentFreeToken++;
if (currentFreeToken == size) {
augmentTokenList(); augmentTokenList();
} }
return token; return token;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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); return createToken(line.array, beg, end, startOffset, type);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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]; TokenImpl token = tokenList[currentFreeToken];
token.set(line, beg, end, startOffset, type); token.set(line, beg, end, startOffset, type);
currentFreeToken++; currentFreeToken++;
@@ -115,11 +112,11 @@ class DefaultTokenFactory implements TokenFactory {
} }
return token; return token;
} }
/** /**
* Resets the state of this token maker. This method should be called * Resets the state of this token maker. This method should be called by the
* by the <code>TokenMaker</code> every time a token list is generated for * <code>TokenMaker</code> every time a token list is generated for a new line
* a new line so the tokens can be reused. * so the tokens can be reused.
*/ */
@Override @Override
public void resetAllTokens() { public void resetAllTokens() {
@@ -15,7 +15,7 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0 * @version 1.0
*/ */
class DefaultTokenMakerFactory extends AbstractTokenMakerFactory implements SyntaxConstants { class DefaultTokenMakerFactory extends AbstractTokenMakerFactory implements SyntaxConstants {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -20,22 +20,22 @@ import javax.swing.text.TabExpander;
* @version 1.0 * @version 1.0
*/ */
class DefaultTokenPainter implements TokenPainter { class DefaultTokenPainter implements TokenPainter {
/** /**
* Rectangle used for filling token backgrounds. * Rectangle used for filling token backgrounds.
*/ */
private Rectangle2D.Float bgRect; private Rectangle2D.Float bgRect;
/** /**
* Micro-optimization; buffer used to compute tab width. If the width is * Micro-optimization; buffer used to compute tab width. If the width is correct
* correct it's not re-allocated, to prevent lots of very small garbage. * it's not re-allocated, to prevent lots of very small garbage. Only used when
* Only used when painting tab lines. * painting tab lines.
*/ */
private static char[] tabBuf; private static char[] tabBuf;
DefaultTokenPainter() { DefaultTokenPainter() {
bgRect = new Rectangle2D.Float(); bgRect = new Rectangle2D.Float();
} }
/** /**
* {@inheritDoc} * {@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) { 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); return paint(token, g, x, y, host, e, 0);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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); return paintImpl(token, g, x, y, host, e, clipStart, false, false);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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); return paintImpl(token, g, x, y, host, e, clipStart, !paintBG, false);
} }
/** /**
* Paints the background of a token. * Paints the background of a token.
* *
* @param x * @param x The x-coordinate of the token.
* The x-coordinate of the token. * @param y The y-coordinate of the token.
* @param y * @param width The width of the token (actually, the width of the part of
* The y-coordinate of the token. * the token to paint).
* @param width * @param height The height of the token.
* The width of the token (actually, the width of the part of * @param g The graphics context with which to paint.
* the token to paint). * @param fontAscent The ascent of the token's font.
* @param height * @param host The text area.
* The height of the token. * @param color The color with which to paint.
* @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, protected void paintBackground(float x, float y, float width, float height, Graphics2D g, int fontAscent,
Color color) { RSyntaxTextArea host, Color color) {
g.setColor(color); g.setColor(color);
bgRect.setRect(x, y - fontAscent, width, height); bgRect.setRect(x, y - fontAscent, width, height);
// g.fill(bgRect); // g.fill(bgRect);
g.fillRect((int) x, (int) (y - fontAscent), (int) width, (int) height); g.fillRect((int) x, (int) (y - fontAscent), (int) width, (int) height);
} }
/** /**
* Does the dirty-work of actually painting the token. * 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, protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
boolean selected, boolean useSTC) { float clipStart, boolean selected, boolean useSTC) {
int origX = (int) x; int origX = (int) x;
int textOffs = token.getTextOffset(); int textOffs = token.getTextOffset();
char[] text = token.getTextArray(); char[] text = token.getTextArray();
@@ -107,22 +101,22 @@ class DefaultTokenPainter implements TokenPainter {
FontMetrics fm = host.getFontMetricsForTokenType(token.getType()); FontMetrics fm = host.getFontMetricsForTokenType(token.getType());
for (int i = textOffs; i < end; i++) { for (int i = textOffs; i < end; i++) {
switch (text[i]) { switch (text[i]) {
case '\t': case '\t':
nextX = e.nextTabStop(x + fm.charsWidth(text, flushIndex, flushLen), 0); nextX = e.nextTabStop(x + fm.charsWidth(text, flushIndex, flushLen), 0);
if (bg != null) { if (bg != null) {
paintBackground(x, y, nextX - x, fm.getHeight(), g, fm.getAscent(), host, bg); paintBackground(x, y, nextX - x, fm.getHeight(), g, fm.getAscent(), host, bg);
} }
if (flushLen > 0) { if (flushLen > 0) {
g.setColor(fg); g.setColor(fg);
g.drawChars(text, flushIndex, flushLen, (int) x, (int) y); g.drawChars(text, flushIndex, flushLen, (int) x, (int) y);
flushLen = 0; flushLen = 0;
} }
flushIndex = i + 1; flushIndex = i + 1;
x = nextX; x = nextX;
break; break;
default: default:
flushLen += 1; flushLen += 1;
break; break;
} }
} }
nextX = x + fm.charsWidth(text, flushIndex, flushLen); nextX = x + fm.charsWidth(text, flushIndex, flushLen);
@@ -152,48 +146,43 @@ class DefaultTokenPainter implements TokenPainter {
} }
return nextX; return nextX;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @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); return paintSelected(token, g, x, y, host, e, 0, useSTC);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart, public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
boolean useSTC) { float clipStart, boolean useSTC) {
return paintImpl(token, g, x, y, host, e, clipStart, true, useSTC); return paintImpl(token, g, x, y, host, e, clipStart, true, useSTC);
} }
/** /**
* Paints dotted "tab" lines; that is, lines that show where your caret * Paints dotted "tab" lines; that is, lines that show where your caret would go
* would go to on the line if you hit "tab". This visual effect is usually * to on the line if you hit "tab". This visual effect is usually done in the
* done in the leading whitespace token(s) of lines. * leading whitespace token(s) of lines.
* *
* @param token * @param token The token to render.
* The token to render. * @param x The starting x-offset of this token. It is assumed that this is
* @param x * the left margin of the text area (may be non-zero due to
* The starting x-offset of this token. It is assumed that this * insets), since tab lines are only painted for leading
* is the left margin of the text area (may be non-zero due to * whitespace.
* insets), since tab lines are only painted for leading * @param y The baseline where this token was painted.
* whitespace. * @param endX The ending x-offset of this token.
* @param y * @param g The graphics context.
* The baseline where this token was painted. * @param e Used to expand tabs.
* @param endX * @param host The text area.
* 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, // 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 // i.e. for MLC's and Token.IDENTIFIERS (for TokenMakers that return
// whitespace as identifiers for performance). But we only paint tab // whitespace as identifiers for performance). But we only paint tab
@@ -13,30 +13,27 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0 * @version 1.0
*/ */
public class DocumentRange implements Comparable<DocumentRange> { public class DocumentRange implements Comparable<DocumentRange> {
private int startOffs; private int startOffs;
private int endOffs; private int endOffs;
/** /**
* Constructor. * Constructor.
* *
* @param startOffs * @param startOffs The starting offset in the document, inclusive.
* The starting offset in the document, inclusive. * @param endOffs The ending offset in the document, exclusive.
* @param endOffs * @throws IllegalArgumentException If <code>endOffs</code> is less than
* The ending offset in the document, exclusive. * <code>startOffs</code>, or either argument
* @throws IllegalArgumentException * is less than zero.
* If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument is less than zero.
*/ */
public DocumentRange(int startOffs, int endOffs) { public DocumentRange(int startOffs, int endOffs) {
set(startOffs, endOffs); set(startOffs, endOffs);
} }
/** /**
* Compares this document range to another. * Compares this document range to another.
* *
* @param other * @param other Another document range.
* Another document range.
* @return How the two should be sorted relative to each other. * @return How the two should be sorted relative to each other.
*/ */
@Override @Override
@@ -50,14 +47,13 @@ public class DocumentRange implements Comparable<DocumentRange> {
} }
return endOffs - other.endOffs; return endOffs - other.endOffs;
} }
/** /**
* Returns whether this document range is equal to another one. * Returns whether this document range is equal to another one.
* *
* @param other * @param other Another object, presumably a document range.
* Another object, presumably a document range. * @return Whether <code>other</code> is also a document range, and equal to
* @return Whether <code>other</code> is also a document range, and equal * this one.
* to this one.
*/ */
@Override @Override
public boolean equals(Object other) { public boolean equals(Object other) {
@@ -69,7 +65,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
} }
return false; return false;
} }
/** /**
* Gets the end offset of the range. * Gets the end offset of the range.
* *
@@ -79,7 +75,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int getEndOffset() { public int getEndOffset() {
return endOffs; return endOffs;
} }
/** /**
* Gets the starting offset of the range. * Gets the starting offset of the range.
* *
@@ -89,7 +85,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int getStartOffset() { public int getStartOffset() {
return startOffs; return startOffs;
} }
/** /**
* Overridden simply as a best practice, since {@link #equals(Object)} is * Overridden simply as a best practice, since {@link #equals(Object)} is
* overridden. * overridden.
@@ -100,29 +96,26 @@ public class DocumentRange implements Comparable<DocumentRange> {
public int hashCode() { public int hashCode() {
return startOffs + endOffs; return startOffs + endOffs;
} }
/** /**
* Returns whether this document range has zero length. This can happen, * Returns whether this document range has zero length. This can happen, for
* for example, with regex searches of forms like * example, with regex searches of forms like <code>"foo|"</code>, where the
* <code>"foo|"</code>, where the right-hand sub-expression matches empty * right-hand sub-expression matches empty strings.
* strings.
* *
* @return Whether this document range has zero length. * @return Whether this document range has zero length.
*/ */
public boolean isZeroLength() { public boolean isZeroLength() {
return startOffs == endOffs; return startOffs == endOffs;
} }
/** /**
* Sets the document range. * Sets the document range.
* *
* @param start * @param start The new start value, inclusive.
* The new start value, inclusive. * @param end The new end value, exclusive.
* @param end * @throws IllegalArgumentException If <code>end</code> is less than
* The new end value, exclusive. * <code>start</code>, or either argument is
* @throws IllegalArgumentException * less than zero.
* If <code>end</code> is less than
* <code>start</code>, or either argument is less than zero.
*/ */
public void set(int start, int end) { public void set(int start, int end) {
if (start < 0 || end < 0) { if (start < 0 || end < 0) {
@@ -134,7 +127,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
this.startOffs = start; this.startOffs = start;
this.endOffs = end; this.endOffs = end;
} }
/** /**
* Returns a string representation of this object. * Returns a string representation of this object.
* *
@@ -144,12 +137,11 @@ public class DocumentRange implements Comparable<DocumentRange> {
public String toString() { public String toString() {
return "[DocumentRange: " + startOffs + "-" + endOffs + "]"; return "[DocumentRange: " + startOffs + "-" + endOffs + "]";
} }
/** /**
* Translates this document range by a given amount. * Translates this document range by a given amount.
* *
* @param amount * @param amount The amount to translate this range by.
* The amount to translate this range by.
* @return This (modified) range. * @return This (modified) range.
*/ */
public DocumentRange translate(int amount) { 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; import org.fife.ui.rtextarea.RTextArea;
/** /**
* A component to sit alongside an {@link RSyntaxTextArea} that displays * A component to sit alongside an {@link RSyntaxTextArea} that displays colored
* colored markers for locations of interest (parser errors, marked * markers for locations of interest (parser errors, marked occurrences, etc.).
* occurrences, etc.).
* <p> * <p>
* *
* <code>ErrorStrip</code>s display <code>ParserNotice</code>s from * <code>ErrorStrip</code>s display <code>ParserNotice</code>s from
* {@link Parser}s. Currently, the only way to get lines flagged in this * {@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 * 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. * return <code>ParserNotice</code>s for each line to display an icon for. The
* The severity of each notice must be at least the threshold set by * severity of each notice must be at least the threshold set by
* {@link #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)} * {@link #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)}
* to be displayed in this error strip. The default threshold is * to be displayed in this error strip. The default threshold is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}. * {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
@@ -76,7 +75,7 @@ import org.fife.ui.rtextarea.RTextArea;
// are now "empty"). // are now "empty").
// //
public class ErrorStrip extends JPanel { public class ErrorStrip extends JPanel {
/** /**
* The text area. * The text area.
*/ */
@@ -86,23 +85,23 @@ public class ErrorStrip extends JPanel {
*/ */
private transient Listener listener; private transient Listener listener;
/** /**
* Whether "marked occurrences" in the text area should be shown in this * Whether "marked occurrences" in the text area should be shown in this error
* error strip. * strip.
*/ */
private boolean showMarkedOccurrences; private boolean showMarkedOccurrences;
/** /**
* Whether markers for "mark all" highlights should be shown in this * Whether markers for "mark all" highlights should be shown in this error
* error strip. * strip.
*/ */
private boolean showMarkAll; private boolean showMarkAll;
/** /**
* Mapping of colors to brighter colors. This is kept to prevent * Mapping of colors to brighter colors. This is kept to prevent unnecessary
* unnecessary creation of the same Colors over and over. * creation of the same Colors over and over.
*/ */
private Map<Color, Color> brighterColors; private Map<Color, Color> brighterColors;
/** /**
* Only notices of this severity (or worse) will be displayed in this * Only notices of this severity (or worse) will be displayed in this error
* error strip. * strip.
*/ */
private ParserNotice.Level levelThreshold; private ParserNotice.Level levelThreshold;
/** /**
@@ -130,12 +129,11 @@ public class ErrorStrip extends JPanel {
*/ */
private static final int PREFERRED_WIDTH = 14; private static final int PREFERRED_WIDTH = 14;
private static final ResourceBundle MSG = ResourceBundle.getBundle("org.fife.ui.rsyntaxtextarea.ErrorStrip"); private static final ResourceBundle MSG = ResourceBundle.getBundle("org.fife.ui.rsyntaxtextarea.ErrorStrip");
/** /**
* Constructor. * Constructor.
* *
* @param textArea * @param textArea The text area we are examining.
* The text area we are examining.
*/ */
public ErrorStrip(RSyntaxTextArea textArea) { public ErrorStrip(RSyntaxTextArea textArea) {
this.textArea = textArea; this.textArea = textArea;
@@ -150,10 +148,10 @@ public class ErrorStrip extends JPanel {
setCaretMarkerColor(Color.BLACK); setCaretMarkerColor(Color.BLACK);
setMarkerToolTipProvider(null); // Install default setMarkerToolTipProvider(null); // Install default
} }
/** /**
* Overridden so we only start listening for parser notices when this * Overridden so we only start listening for parser notices when this component
* component (and presumably the text area) are visible. * (and presumably the text area) are visible.
*/ */
@Override @Override
public void addNotify() { public void addNotify() {
@@ -165,7 +163,7 @@ public class ErrorStrip extends JPanel {
textArea.addPropertyChangeListener(RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener); textArea.addPropertyChangeListener(RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);
refreshMarkers(); refreshMarkers();
} }
/** /**
* Manually manages layout since this component uses no layout manager. * 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 listener.caretUpdate(null); // Force recalculation of caret line pos
} }
/** /**
* Returns a "brighter" color. * Returns a "brighter" color.
* *
* @param c * @param c The color.
* The color.
* @return A brighter color. * @return A brighter color.
*/ */
private Color getBrighterColor(Color c) { private Color getBrighterColor(Color c) {
@@ -201,7 +198,7 @@ public class ErrorStrip extends JPanel {
} }
return brighter; return brighter;
} }
/** /**
* returns the color to use when painting the caret marker. * returns the color to use when painting the caret marker.
* *
@@ -211,7 +208,7 @@ public class ErrorStrip extends JPanel {
public Color getCaretMarkerColor() { public Color getCaretMarkerColor() {
return caretMarkerColor; return caretMarkerColor;
} }
/** /**
* Returns whether the caret's position should be drawn. * Returns whether the caret's position should be drawn.
* *
@@ -221,7 +218,7 @@ public class ErrorStrip extends JPanel {
public boolean getFollowCaret() { public boolean getFollowCaret() {
return followCaret; return followCaret;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -230,11 +227,11 @@ public class ErrorStrip extends JPanel {
int height = textArea.getPreferredScrollableViewportSize().height; int height = textArea.getPreferredScrollableViewportSize().height;
return new Dimension(PREFERRED_WIDTH, height); return new Dimension(PREFERRED_WIDTH, height);
} }
/** /**
* Returns the minimum severity a parser notice must be for it to be * Returns the minimum severity a parser notice must be for it to be displayed
* displayed in this error strip. This will be one of the constants * in this error strip. This will be one of the constants defined in the
* defined in the <code>ParserNotice</code> class. * <code>ParserNotice</code> class.
* *
* @return The minimum severity. * @return The minimum severity.
* @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level) * @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)
@@ -242,7 +239,7 @@ public class ErrorStrip extends JPanel {
public ParserNotice.Level getLevelThreshold() { public ParserNotice.Level getLevelThreshold() {
return levelThreshold; return levelThreshold;
} }
/** /**
* Returns whether "mark all" highlights are shown in this error strip. * Returns whether "mark all" highlights are shown in this error strip.
* *
@@ -252,7 +249,7 @@ public class ErrorStrip extends JPanel {
public boolean getShowMarkAll() { public boolean getShowMarkAll() {
return showMarkAll; return showMarkAll;
} }
/** /**
* Returns whether marked occurrences are shown in this error strip. * Returns whether marked occurrences are shown in this error strip.
* *
@@ -262,7 +259,7 @@ public class ErrorStrip extends JPanel {
public boolean getShowMarkedOccurrences() { public boolean getShowMarkedOccurrences() {
return showMarkedOccurrences; return showMarkedOccurrences;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -276,13 +273,12 @@ public class ErrorStrip extends JPanel {
} }
return text; return text;
} }
/** /**
* Returns the y-offset in this component corresponding to a line in the * Returns the y-offset in this component corresponding to a line in the text
* text component. * component.
* *
* @param line * @param line The line.
* The line.
* @return The y-offset. * @return The y-offset.
* @see #yToLine(int) * @see #yToLine(int)
*/ */
@@ -291,12 +287,11 @@ public class ErrorStrip extends JPanel {
float lineCount = textArea.getLineCount(); float lineCount = textArea.getLineCount();
return (int) (((line - 1) / (lineCount - 1)) * (h - 2)); return (int) (((line - 1) / (lineCount - 1)) * (h - 2));
} }
/** /**
* Overridden to (possibly) draw the caret's position. * Overridden to (possibly) draw the caret's position.
* *
* @param g * @param g The graphics context.
* The graphics context.
*/ */
@Override @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
@@ -306,12 +301,11 @@ public class ErrorStrip extends JPanel {
g.fillRect(0, caretLineY, getWidth(), 2); g.fillRect(0, caretLineY, getWidth(), 2);
} }
} }
/** /**
* Returns a possibly brighter component for a color. * Returns a possibly brighter component for a color.
* *
* @param i * @param i An RGB component for a color (0-255).
* An RGB component for a color (0-255).
* @return A possibly brighter value for the component. * @return A possibly brighter value for the component.
*/ */
private static int possiblyBrighter(int i) { private static int possiblyBrighter(int i) {
@@ -320,7 +314,7 @@ public class ErrorStrip extends JPanel {
} }
return i; return i;
} }
/** /**
* Refreshes the markers displayed in this error strip. * Refreshes the markers displayed in this error strip.
*/ */
@@ -354,16 +348,13 @@ public class ErrorStrip extends JPanel {
revalidate(); revalidate();
repaint(); repaint();
} }
/** /**
* Adds markers for a list of ranges in the document. * Adds markers for a list of ranges in the document.
* *
* @param ranges * @param ranges The list of ranges in the document.
* The list of ranges in the document. * @param markerMap A mapping from line number to <code>Marker</code>.
* @param markerMap * @param color The color to use for the markers.
* 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) { private void addMarkersForRanges(List<DocumentRange> ranges, Map<Integer, Marker> markerMap, Color color) {
for (DocumentRange range : ranges) { for (DocumentRange range : ranges) {
@@ -388,7 +379,7 @@ public class ErrorStrip extends JPanel {
} }
} }
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -401,12 +392,11 @@ public class ErrorStrip extends JPanel {
textArea.removePropertyChangeListener(RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener); textArea.removePropertyChangeListener(RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);
textArea.removePropertyChangeListener(RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener); textArea.removePropertyChangeListener(RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);
} }
/** /**
* Sets the color to use when painting the caret marker. * Sets the color to use when painting the caret marker.
* *
* @param color * @param color The new caret marker color.
* The new caret marker color.
* @see #getCaretMarkerColor() * @see #getCaretMarkerColor()
*/ */
public void setCaretMarkerColor(Color color) { public void setCaretMarkerColor(Color color) {
@@ -415,12 +405,11 @@ public class ErrorStrip extends JPanel {
listener.caretUpdate(null); // Force repaint listener.caretUpdate(null); // Force repaint
} }
} }
/** /**
* Toggles whether the caret's current location should be drawn. * Toggles whether the caret's current location should be drawn.
* *
* @param follow * @param follow Whether the caret's current location should be followed.
* Whether the caret's current location should be followed.
* @see #getFollowCaret() * @see #getFollowCaret()
*/ */
public void setFollowCaret(boolean follow) { public void setFollowCaret(boolean follow) {
@@ -434,15 +423,14 @@ public class ErrorStrip extends JPanel {
listener.caretUpdate(null); // Possibly repaint listener.caretUpdate(null); // Possibly repaint
} }
} }
/** /**
* Sets the minimum severity a parser notice must be for it to be displayed * Sets the minimum severity a parser notice must be for it to be displayed in
* in this error strip. This should be one of the constants defined in * this error strip. This should be one of the constants defined in the
* the <code>ParserNotice</code> class. The default value is * <code>ParserNotice</code> class. The default value is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}. * {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
* *
* @param level * @param level The new severity threshold.
* The new severity threshold.
* @see #getLevelThreshold() * @see #getLevelThreshold()
* @see ParserNotice * @see ParserNotice
*/ */
@@ -452,25 +440,23 @@ public class ErrorStrip extends JPanel {
refreshMarkers(); refreshMarkers();
} }
} }
/** /**
* Sets the provider of tool tips for markers in this error strip. * Sets the provider of tool tips for markers in this error strip. Applications
* Applications can use this method to control the content and format of * can use this method to control the content and format of the tool tip
* the tool tip descriptions of line markers. * descriptions of line markers.
* *
* @param provider * @param provider The provider. If this is <code>null</code>, a default
* The provider. If this is <code>null</code>, a default * implementation will be used.
* implementation will be used.
*/ */
public void setMarkerToolTipProvider(ErrorStripMarkerToolTipProvider provider) { public void setMarkerToolTipProvider(ErrorStripMarkerToolTipProvider provider) {
markerToolTipProvider = provider != null ? provider : new DefaultErrorStripMarkerToolTipProvider(); markerToolTipProvider = provider != null ? provider : new DefaultErrorStripMarkerToolTipProvider();
} }
/** /**
* Sets whether "mark all" highlights are shown in this error strip. * Sets whether "mark all" highlights are shown in this error strip.
* *
* @param show * @param show Whether to show markers for "mark all" highlights.
* Whether to show markers for "mark all" highlights.
* @see #getShowMarkAll() * @see #getShowMarkAll()
*/ */
public void setShowMarkAll(boolean show) { public void setShowMarkAll(boolean show) {
@@ -481,12 +467,11 @@ public class ErrorStrip extends JPanel {
} }
} }
} }
/** /**
* Sets whether marked occurrences are shown in this error strip. * Sets whether marked occurrences are shown in this error strip.
* *
* @param show * @param show Whether to show marked occurrences.
* Whether to show marked occurrences.
* @see #getShowMarkedOccurrences() * @see #getShowMarkedOccurrences()
*/ */
public void setShowMarkedOccurrences(boolean show) { 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 * Returns the line in the text area corresponding to a y-offset in this
* component. * component.
* *
* @param y * @param y The y-offset.
* The y-offset.
* @return The line. * @return The line.
* @see #lineToY(int) * @see #lineToY(int)
*/ */
@@ -516,15 +500,15 @@ public class ErrorStrip extends JPanel {
} }
return line; return line;
} }
/** /**
* The default implementation of the provider of tool tips for markers in * The default implementation of the provider of tool tips for markers in an
* an error strip. * error strip.
* *
* @author predi * @author predi
*/ */
private static class DefaultErrorStripMarkerToolTipProvider implements ErrorStripMarkerToolTipProvider { private static class DefaultErrorStripMarkerToolTipProvider implements ErrorStripMarkerToolTipProvider {
@Override @Override
public String getToolTipText(List<ParserNotice> notices) { public String getToolTipText(List<ParserNotice> notices) {
String text = null; String text = null;
@@ -545,34 +529,33 @@ public class ErrorStrip extends JPanel {
return text; return text;
} }
} }
/** /**
* Returns tool tip text for the markers in an {@link ErrorStrip} that * Returns tool tip text for the markers in an {@link ErrorStrip} that denote
* denote one or more parser notices. * one or more parser notices.
* *
* @author predi * @author predi
*/ */
public interface ErrorStripMarkerToolTipProvider { public interface ErrorStripMarkerToolTipProvider {
/** /**
* Returns the tool tip text for a marker in an <code>ErrorStrip</code> * Returns the tool tip text for a marker in an <code>ErrorStrip</code> that
* that denotes a given list of parser notices. * denotes a given list of parser notices.
* *
* @param notices * @param notices The list of parser notices.
* The list of parser notices. * @return The tool tip text. This may be HTML. Returning <code>null</code> will
* @return The tool tip text. This may be HTML. Returning * result in no tool tip being displayed.
* <code>null</code> will result in no tool tip being displayed.
*/ */
String getToolTipText(List<ParserNotice> notices); String getToolTipText(List<ParserNotice> notices);
} }
/** /**
* Listens for events in the error strip and its markers. * Listens for events in the error strip and its markers.
*/ */
private class Listener extends MouseAdapter implements PropertyChangeListener, CaretListener { private class Listener extends MouseAdapter implements PropertyChangeListener, CaretListener {
private Rectangle visibleRect = new Rectangle(); private Rectangle visibleRect = new Rectangle();
@Override @Override
public void caretUpdate(CaretEvent e) { public void caretUpdate(CaretEvent e) {
if (getFollowCaret()) { if (getFollowCaret()) {
@@ -587,7 +570,7 @@ public class ErrorStrip extends JPanel {
} }
} }
} }
@Override @Override
public void mouseClicked(MouseEvent e) { public void mouseClicked(MouseEvent e) {
Component source = (Component) e.getSource(); Component source = (Component) e.getSource();
@@ -605,7 +588,7 @@ public class ErrorStrip extends JPanel {
} }
} }
} }
@Override @Override
public void propertyChange(PropertyChangeEvent e) { public void propertyChange(PropertyChangeEvent e) {
String propName = e.getPropertyName(); String propName = e.getPropertyName();
@@ -636,30 +619,30 @@ public class ErrorStrip extends JPanel {
} }
} }
} }
/** /**
* A notice that wraps a "marked occurrence" instance. * A notice that wraps a "marked occurrence" instance.
*/ */
private class MarkedOccurrenceNotice implements ParserNotice { private class MarkedOccurrenceNotice implements ParserNotice {
private DocumentRange range; private DocumentRange range;
private Color color; private Color color;
MarkedOccurrenceNotice(DocumentRange range, Color color) { MarkedOccurrenceNotice(DocumentRange range, Color color) {
this.range = range; this.range = range;
this.color = color; this.color = color;
} }
@Override @Override
public int compareTo(ParserNotice other) { public int compareTo(ParserNotice other) {
return 0; // Value doesn't matter return 0; // Value doesn't matter
} }
@Override @Override
public boolean containsPosition(int pos) { public boolean containsPosition(int pos) {
return pos >= range.getStartOffset() && pos < range.getEndOffset(); return pos >= range.getStartOffset() && pos < range.getEndOffset();
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
// FindBugs - Define equals() when defining compareTo() // FindBugs - Define equals() when defining compareTo()
@@ -668,12 +651,12 @@ public class ErrorStrip extends JPanel {
} }
return compareTo((ParserNotice) o) == 0; return compareTo((ParserNotice) o) == 0;
} }
@Override @Override
public Color getColor() { public Color getColor() {
return color; return color;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -681,17 +664,17 @@ public class ErrorStrip extends JPanel {
public boolean getKnowsOffsetAndLength() { public boolean getKnowsOffsetAndLength() {
return true; return true;
} }
@Override @Override
public int getLength() { public int getLength() {
return range.getEndOffset() - range.getStartOffset(); return range.getEndOffset() - range.getStartOffset();
} }
@Override @Override
public Level getLevel() { public Level getLevel() {
return Level.INFO; // Won't matter return Level.INFO; // Won't matter
} }
@Override @Override
public int getLine() { public int getLine() {
try { try {
@@ -700,7 +683,7 @@ public class ErrorStrip extends JPanel {
return 0; return 0;
} }
} }
@Override @Override
public String getMessage() { public String getMessage() {
String text = null; String text = null;
@@ -713,40 +696,40 @@ public class ErrorStrip extends JPanel {
} }
return text; return text;
} }
@Override @Override
public int getOffset() { public int getOffset() {
return range.getStartOffset(); return range.getStartOffset();
} }
@Override @Override
public Parser getParser() { public Parser getParser() {
return null; return null;
} }
@Override @Override
public boolean getShowInEditor() { public boolean getShowInEditor() {
return false; // Value doesn't matter return false; // Value doesn't matter
} }
@Override @Override
public String getToolTipText() { public String getToolTipText() {
return null; return null;
} }
@Override @Override
public int hashCode() { // FindBugs, since we override equals() public int hashCode() { // FindBugs, since we override equals()
return 0; // Value doesn't matter for us. return 0; // Value doesn't matter for us.
} }
} }
/** /**
* A "marker" in this error strip, representing one or more notices. * A "marker" in this error strip, representing one or more notices.
*/ */
private class Marker extends JComponent { private class Marker extends JComponent {
private List<ParserNotice> notices; private List<ParserNotice> notices;
Marker(ParserNotice notice) { Marker(ParserNotice notice) {
notices = new ArrayList<ParserNotice>(1); // Usually just 1 notices = new ArrayList<ParserNotice>(1); // Usually just 1
addNotice(notice); addNotice(notice);
@@ -754,11 +737,11 @@ public class ErrorStrip extends JPanel {
setSize(getPreferredSize()); setSize(getPreferredSize());
ToolTipManager.sharedInstance().registerComponent(this); ToolTipManager.sharedInstance().registerComponent(this);
} }
public void addNotice(ParserNotice notice) { public void addNotice(ParserNotice notice) {
notices.add(notice); notices.add(notice);
} }
public boolean containsMarkedOccurence() { public boolean containsMarkedOccurence() {
boolean result = false; boolean result = false;
for (int i = 0; i < notices.size(); i++) { for (int i = 0; i < notices.size(); i++) {
@@ -769,7 +752,7 @@ public class ErrorStrip extends JPanel {
} }
return result; return result;
} }
public Color getColor() { public Color getColor() {
// Return the color for the highest-level parser. // Return the color for the highest-level parser.
Color c = null; Color c = null;
@@ -782,18 +765,18 @@ public class ErrorStrip extends JPanel {
} }
return c; return c;
} }
@Override @Override
public Dimension getPreferredSize() { public Dimension getPreferredSize() {
int w = PREFERRED_WIDTH - 4; // 2-pixel empty border int w = PREFERRED_WIDTH - 4; // 2-pixel empty border
return new Dimension(w, 5); return new Dimension(w, 5);
} }
@Override @Override
public String getToolTipText() { public String getToolTipText() {
return markerToolTipProvider.getToolTipText(Collections.unmodifiableList(notices)); return markerToolTipProvider.getToolTipText(Collections.unmodifiableList(notices));
} }
protected void mouseClicked(MouseEvent e) { protected void mouseClicked(MouseEvent e) {
ParserNotice pn = notices.get(0); ParserNotice pn = notices.get(0);
int offs = pn.getOffset(); int offs = pn.getOffset();
@@ -812,7 +795,7 @@ public class ErrorStrip extends JPanel {
} }
} }
} }
@Override @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
// TODO: Give "priorities" and always pick color of a notice with // TODO: Give "priorities" and always pick color of a notice with
@@ -829,14 +812,14 @@ public class ErrorStrip extends JPanel {
g.setColor(borderColor); g.setColor(borderColor);
g.drawRect(0, 0, w - 1, h - 1); g.drawRect(0, 0, w - 1, h - 1);
} }
@Override @Override
public void removeNotify() { public void removeNotify() {
super.removeNotify(); super.removeNotify();
ToolTipManager.sharedInstance().unregisterComponent(this); ToolTipManager.sharedInstance().unregisterComponent(this);
removeMouseListener(listener); removeMouseListener(listener);
} }
public void updateLocation() { public void updateLocation() {
int line = notices.get(0).getLine(); int line = notices.get(0).getLine();
int y = lineToY(line); int y = lineToY(line);
@@ -20,17 +20,16 @@ import java.io.OutputStream;
* @version 1.0 * @version 1.0
*/ */
class FileFileLocation extends FileLocation { class FileFileLocation extends FileLocation {
/** /**
* The file. This may or may not actually exist. * The file. This may or may not actually exist.
*/ */
private File file; private File file;
/** /**
* Constructor. * Constructor.
* *
* @param file * @param file The local file.
* The local file.
*/ */
FileFileLocation(File file) { FileFileLocation(File file) {
try { try {
@@ -40,7 +39,7 @@ class FileFileLocation extends FileLocation {
this.file = file; this.file = file;
} }
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -48,7 +47,7 @@ class FileFileLocation extends FileLocation {
protected long getActualLastModified() { protected long getActualLastModified() {
return file.lastModified(); return file.lastModified();
} }
/** /**
* Returns the full path to the file. * Returns the full path to the file.
* *
@@ -59,7 +58,7 @@ class FileFileLocation extends FileLocation {
public String getFileFullPath() { public String getFileFullPath() {
return file.getAbsolutePath(); return file.getAbsolutePath();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -67,7 +66,7 @@ class FileFileLocation extends FileLocation {
public String getFileName() { public String getFileName() {
return file.getName(); return file.getName();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -75,7 +74,7 @@ class FileFileLocation extends FileLocation {
protected InputStream getInputStream() throws IOException { protected InputStream getInputStream() throws IOException {
return new FileInputStream(file); return new FileInputStream(file);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -83,7 +82,7 @@ class FileFileLocation extends FileLocation {
protected OutputStream getOutputStream() throws IOException { protected OutputStream getOutputStream() throws IOException {
return new FileOutputStream(file); return new FileOutputStream(file);
} }
/** /**
* Always returns <code>true</code>. * Always returns <code>true</code>.
* *
@@ -94,10 +93,10 @@ class FileFileLocation extends FileLocation {
public boolean isLocal() { public boolean isLocal() {
return true; return true;
} }
/** /**
* Since file locations of this type are guaranteed to be local, this * Since file locations of this type are guaranteed to be local, this method
* method returns whether the file exists. * returns whether the file exists.
* *
* @return Whether this local file actually exists. * @return Whether this local file actually exists.
* @see #isLocal() * @see #isLocal()
@@ -14,23 +14,23 @@ import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
/** /**
* Holds the location of a local or remote file. This provides a common way * Holds the location of a local or remote file. This provides a common way to
* to read, write, and check properties of both local and remote files. * read, write, and check properties of both local and remote files.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public abstract class FileLocation { public abstract class FileLocation {
/** /**
* Creates a {@link FileLocation} instance for the specified local file. * Creates a {@link FileLocation} instance for the specified local file.
* *
* @param fileFullPath * @param fileFullPath The full path to a local file.
* The full path to a local file.
* @return The file's location. * @return The file's location.
*/ */
public static FileLocation create(String fileFullPath) { 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 { try {
return new URLFileLocation(new URL(fileFullPath)); return new URLFileLocation(new URL(fileFullPath));
} catch (MalformedURLException mue) { } catch (MalformedURLException mue) {
@@ -39,23 +39,21 @@ public abstract class FileLocation {
} }
return new FileFileLocation(new File(fileFullPath)); return new FileFileLocation(new File(fileFullPath));
} }
/** /**
* Creates a {@link FileLocation} instance for the specified local file. * Creates a {@link FileLocation} instance for the specified local file.
* *
* @param file * @param file A local file.
* A local file.
* @return The file's location. * @return The file's location.
*/ */
public static FileLocation create(File file) { public static FileLocation create(File file) {
return new FileFileLocation(file); return new FileFileLocation(file);
} }
/** /**
* Creates a {@link FileLocation} instance for the specified file. * Creates a {@link FileLocation} instance for the specified file.
* *
* @param url * @param url The URL of a file.
* The URL of a file.
* @return The file's location. * @return The file's location.
*/ */
public static FileLocation create(URL url) { public static FileLocation create(URL url) {
@@ -64,25 +62,25 @@ public abstract class FileLocation {
} }
return new URLFileLocation(url); return new URLFileLocation(url);
} }
/** /**
* Returns the last time this file was modified, or * Returns the last time this file was modified, or
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be * {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be computed
* computed (such as for a remote file). * (such as for a remote file).
* *
* @return The last time this file was modified. * @return The last time this file was modified.
*/ */
protected abstract long getActualLastModified(); protected abstract long getActualLastModified();
/** /**
* Returns the full path to the file. This will be stripped of * Returns the full path to the file. This will be stripped of sensitive
* sensitive information such as passwords for remote files. * information such as passwords for remote files.
* *
* @return The full path to the file. * @return The full path to the file.
* @see #getFileName() * @see #getFileName()
*/ */
public abstract String getFileFullPath(); public abstract String getFileFullPath();
/** /**
* Returns the name of the file. * Returns the name of the file.
* *
@@ -90,26 +88,24 @@ public abstract class FileLocation {
* @see #getFileFullPath() * @see #getFileFullPath()
*/ */
public abstract String getFileName(); public abstract String getFileName();
/** /**
* Opens an input stream for reading from this file. * Opens an input stream for reading from this file.
* *
* @return The input stream. * @return The input stream.
* @throws IOException * @throws IOException If the file does not exist, or some other IO error
* If the file does not exist, or some other IO error * occurs.
* occurs.
*/ */
protected abstract InputStream getInputStream() throws IOException; protected abstract InputStream getInputStream() throws IOException;
/** /**
* Opens an output stream for writing this file. * Opens an output stream for writing this file.
* *
* @return An output stream. * @return An output stream.
* @throws IOException * @throws IOException If an IO error occurs.
* If an IO error occurs.
*/ */
protected abstract OutputStream getOutputStream() throws IOException; protected abstract OutputStream getOutputStream() throws IOException;
/** /**
* Returns whether this file location is a local file. * Returns whether this file location is a local file.
* *
@@ -117,16 +113,15 @@ public abstract class FileLocation {
* @see #isLocalAndExists() * @see #isLocalAndExists()
*/ */
public abstract boolean isLocal(); public abstract boolean isLocal();
/** /**
* Returns whether this file location is a local file that already * Returns whether this file location is a local file that already exists.
* exists.
* *
* @return Whether this file is local and actually exists. * @return Whether this file is local and actually exists.
* @see #isLocal() * @see #isLocal()
*/ */
public abstract boolean isLocalAndExists(); public abstract boolean isLocalAndExists();
/** /**
* Returns whether this file location is a remote location. * Returns whether this file location is a remote location.
* *
@@ -28,17 +28,16 @@ import org.fife.ui.rtextarea.IconRowHeader;
* @version 1.0 * @version 1.0
*/ */
public class FoldingAwareIconRowHeader extends IconRowHeader { public class FoldingAwareIconRowHeader extends IconRowHeader {
/** /**
* Constructor. * Constructor.
* *
* @param textArea * @param textArea The parent text area.
* The parent text area.
*/ */
public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) { public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
super(textArea); super(textArea);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -160,11 +159,11 @@ public class FoldingAwareIconRowHeader extends IconRowHeader {
} }
} }
} }
/** /**
* Paints icons when line wrapping is enabled. Note that this does not * Paints icons when line wrapping is enabled. Note that this does not override
* override the parent class's implementation to avoid this version being * the parent class's implementation to avoid this version being called when
* called when line wrapping is disabled. * line wrapping is disabled.
*/ */
private void paintComponentWrapped(Graphics g) { private void paintComponentWrapped(Graphics g) {
// The variables we use are as follows: // The variables we use are as follows:
@@ -20,49 +20,49 @@ import javax.swing.text.Element;
import org.fife.ui.rtextarea.SmartHighlightPainter; import org.fife.ui.rtextarea.SmartHighlightPainter;
/** /**
* Marks occurrences of the current token for HTML. Tags that require a * Marks occurrences of the current token for HTML. Tags that require a closing
* closing tag have their "opposite" tag closed. * tag have their "opposite" tag closed.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class HtmlOccurrenceMarker implements OccurrenceMarker { public class HtmlOccurrenceMarker implements OccurrenceMarker {
private static final char[] CLOSE_TAG_START = { '<', '/' }; private static final char[] CLOSE_TAG_START = { '<', '/' };
private static final char[] TAG_SELF_CLOSE = { '/', '>' }; private static final char[] TAG_SELF_CLOSE = { '/', '>' };
private static final Set<String> TAGS_REQUIRING_CLOSING = getRequiredClosingTags(); private static final Set<String> TAGS_REQUIRING_CLOSING = getRequiredClosingTags();
public static final Set<String> getRequiredClosingTags() { public static final Set<String> getRequiredClosingTags() {
final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav", "article", "aside", "h1", final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav",
"h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog", "blockquote", "ol", "ul", "dl", "a", "q", "article", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog",
"cite", "em", "strong", "small", "mark", "dfn", "abbr", "time", "progress", "meter", "code", "var", "samp", "kbd", "sub", "blockquote", "ol", "ul", "dl", "a", "q", "cite", "em", "strong", "small", "mark", "dfn", "abbr",
"sup", "span", "i", "b", "bdo", "ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas", "time", "progress", "meter", "code", "var", "samp", "kbd", "sub", "sup", "span", "i", "b", "bdo",
"map", "table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output", "details", "ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas", "map",
"bb", "menu", "legend", "div", "table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output",
"details", "bb", "menu", "legend", "div",
// Obsolete elements // Obsolete elements
"acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing", "marquee", "nobr", "acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing",
"noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", }; "marquee", "nobr", "noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", };
return new HashSet<String>(Arrays.asList(tags)); return new HashSet<String>(Arrays.asList(tags));
} }
/** /**
* If the caret is inside of a tag, this method returns the token * If the caret is inside of a tag, this method returns the token representing
* representing the tag name; otherwise, <code>null</code> is returned. * the tag name; otherwise, <code>null</code> is returned.
* <p> * <p>
* *
* Currently, this method only checks for tag names on the same line as * Currently, this method only checks for tag names on the same line as the
* the caret, for simplicity. In the future it could check prior lines * caret, for simplicity. In the future it could check prior lines until the tag
* until the tag name is found. * name is found.
* *
* @param textArea * @param textArea The text area.
* The text area. * @param occurrenceMarker The occurrence marker.
* @param occurrenceMarker * @return The token to mark occurrences of. Note that, if the specified
* The occurrence marker. * occurrence marker identifies tokens other than tag names, these other
* @return The token to mark occurrences of. Note that, if the * element types may be returned.
* 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. // Get the tag name token.
// For now, we only check for tags on the current line, for simplicity. // For now, we only check for tags on the current line, for simplicity.
int dot = textArea.getCaretPosition(); int dot = textArea.getCaretPosition();
@@ -93,7 +93,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
} }
return toMark; return toMark;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -101,7 +101,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
public Token getTokenToMark(RSyntaxTextArea textArea) { public Token getTokenToMark(RSyntaxTextArea textArea) {
return getTagNameTokenForCaretOffset(textArea, this); return getTagNameTokenForCaretOffset(textArea, this);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -109,7 +109,7 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
public boolean isValidType(RSyntaxTextArea textArea, Token t) { public boolean isValidType(RSyntaxTextArea textArea, Token t) {
return textArea.getMarkOccurrencesOfTokenType(t.getType()); return textArea.getMarkOccurrencesOfTokenType(t.getType());
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -254,15 +254,15 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
} while (curLine >= 0); } while (curLine >= 0);
} }
} }
/** /**
* Used internally when searching backward for a matching "open" tag. * Used internally when searching backward for a matching "open" tag.
*/ */
private static class Entry { private static class Entry {
private boolean open; private boolean open;
private Token t; private Token t;
Entry(boolean open, Token t) { Entry(boolean open, Token t) {
this.open = open; this.open = open;
this.t = t; this.t = t;
@@ -28,17 +28,14 @@ package org.fife.ui.rsyntaxtextarea;
* @version 1.0 * @version 1.0
*/ */
public interface LinkGenerator { public interface LinkGenerator {
/** /**
* If a region of text under the mouse position should be considered a * If a region of text under the mouse position should be considered a
* hyperlink, a result object is returned. This object describes what * hyperlink, a result object is returned. This object describes what region of
* region of text is the link, and what action to perform if the link is * text is the link, and what action to perform if the link is clicked.
* clicked.
* *
* @param textArea * @param textArea The text component.
* The text component. * @param offs The offset in the document under the mouse position.
* @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 * @return The link information, or <code>null</code> if no link is at the
* specified offset. * specified offset.
*/ */
@@ -23,19 +23,19 @@ import javax.swing.event.HyperlinkEvent;
* @see SelectRegionLinkGeneratorResult * @see SelectRegionLinkGeneratorResult
*/ */
public interface LinkGeneratorResult { public interface LinkGeneratorResult {
/** /**
* Executes the action associated with this object. If the result is a * Executes the action associated with this object. If the result is a URL to
* URL to open, a standard hyperlink event can be returned. Alternatively, * open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this * <code>null</code> can be returned and the action performed in this method
* method itself. * itself.
* *
* @return The hyperlink event to broadcast from the text area, or * @return The hyperlink event to broadcast from the text area, or
* <code>null</code> if the action's behavior occurs in this method * <code>null</code> if the action's behavior occurs in this method
* directly. * directly.
*/ */
HyperlinkEvent execute(); HyperlinkEvent execute();
/** /**
* Returns the starting offset of the link specified by the parent * Returns the starting offset of the link specified by the parent
* <code>LinkGenerator</code>. * <code>LinkGenerator</code>.
@@ -19,15 +19,15 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter; import org.fife.ui.rtextarea.SmartHighlightPainter;
/** /**
* Marks all occurrences of the token at the current caret position, if it is * Marks all occurrences of the token at the current caret position, if it is an
* an identifier. * identifier.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
* @see OccurrenceMarker * @see OccurrenceMarker
*/ */
class MarkOccurrencesSupport implements CaretListener, ActionListener { class MarkOccurrencesSupport implements CaretListener, ActionListener {
private RSyntaxTextArea textArea; private RSyntaxTextArea textArea;
private Timer timer; private Timer timer;
private SmartHighlightPainter p; private SmartHighlightPainter p;
@@ -39,36 +39,33 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
* The default delay. * The default delay.
*/ */
static final int DEFAULT_DELAY_MS = 1000; static final int DEFAULT_DELAY_MS = 1000;
/** /**
* Constructor. Creates a listener with a 1 second delay. * Constructor. Creates a listener with a 1 second delay.
*/ */
MarkOccurrencesSupport() { MarkOccurrencesSupport() {
this(DEFAULT_DELAY_MS); this(DEFAULT_DELAY_MS);
} }
/** /**
* Constructor. * Constructor.
* *
* @param delay * @param delay The delay between when the caret last moves and when the text
* The delay between when the caret last moves and when the * should be scanned for matching occurrences. This should be in
* text should be scanned for matching occurrences. This should * milliseconds.
* be in milliseconds.
*/ */
MarkOccurrencesSupport(int delay) { MarkOccurrencesSupport(int delay) {
this(delay, DEFAULT_COLOR); this(delay, DEFAULT_COLOR);
} }
/** /**
* Constructor. * Constructor.
* *
* @param delay * @param delay The delay between when the caret last moves and when the text
* The delay between when the caret last moves and when the * should be scanned for matching occurrences. This should be in
* text should be scanned for matching occurrences. This should * milliseconds.
* be in milliseconds. * @param color The color to use to mark the occurrences. This cannot be
* @param color * <code>null</code>.
* The color to use to mark the occurrences. This cannot be
* <code>null</code>.
*/ */
MarkOccurrencesSupport(int delay, Color color) { MarkOccurrencesSupport(int delay, Color color) {
timer = new Timer(delay, this); timer = new Timer(delay, this);
@@ -76,18 +73,17 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
p = new SmartHighlightPainter(); p = new SmartHighlightPainter();
setColor(color); setColor(color);
} }
/** /**
* Called after the caret has been moved and a fixed time delay has * Called after the caret has been moved and a fixed time delay has elapsed.
* elapsed. This locates and highlights all occurrences of the identifier * This locates and highlights all occurrences of the identifier at the caret
* at the caret position, if any. * position, if any.
* <p> * <p>
* *
* Callers should not call this method directly, but should rather prefer * Callers should not call this method directly, but should rather prefer
* {@link #doMarkOccurrences()} to mark occurrences. * {@link #doMarkOccurrences()} to mark occurrences.
* *
* @param e * @param e The event.
* The event.
* @see #doMarkOccurrences() * @see #doMarkOccurrences()
*/ */
@Override @Override
@@ -124,18 +120,17 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
textArea.fireMarkedOccurrencesChanged(); textArea.fireMarkedOccurrencesChanged();
} }
} }
/** /**
* Called when the caret moves in the text area. * Called when the caret moves in the text area.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void caretUpdate(CaretEvent e) { public void caretUpdate(CaretEvent e) {
timer.restart(); timer.restart();
} }
/** /**
* Removes all highlights added to the text area by this listener. * Removes all highlights added to the text area by this listener.
*/ */
@@ -145,16 +140,15 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
h.clearMarkOccurrencesHighlights(); h.clearMarkOccurrencesHighlights();
} }
} }
/** /**
* Immediately marks all occurrences of the token at the current caret * Immediately marks all occurrences of the token at the current caret position.
* position.
*/ */
public void doMarkOccurrences() { public void doMarkOccurrences() {
timer.stop(); timer.stop();
actionPerformed(null); actionPerformed(null);
} }
/** /**
* Returns the color being used to mark occurrences. * Returns the color being used to mark occurrences.
* *
@@ -164,7 +158,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public Color getColor() { public Color getColor() {
return (Color) p.getPaint(); return (Color) p.getPaint();
} }
/** /**
* Returns the delay, in milliseconds. * Returns the delay, in milliseconds.
* *
@@ -174,7 +168,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public int getDelay() { public int getDelay() {
return timer.getDelay(); return timer.getDelay();
} }
/** /**
* Returns whether a border is painted around marked occurrences. * Returns whether a border is painted around marked occurrences.
* *
@@ -185,13 +179,12 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
public boolean getPaintBorder() { public boolean getPaintBorder() {
return p.getPaintBorder(); return p.getPaintBorder();
} }
/** /**
* Installs this listener on a text area. If it is already installed on * Installs this listener on a text area. If it is already installed on another
* another text area, it is uninstalled first. * text area, it is uninstalled first.
* *
* @param textArea * @param textArea The text area to install on.
* The text area to install on.
*/ */
public void install(RSyntaxTextArea textArea) { public void install(RSyntaxTextArea textArea) {
if (this.textArea != null) { if (this.textArea != null) {
@@ -203,12 +196,11 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
setColor(textArea.getMarkOccurrencesColor()); setColor(textArea.getMarkOccurrencesColor());
} }
} }
/** /**
* Sets the color to use when marking occurrences. * Sets the color to use when marking occurrences.
* *
* @param color * @param color The color to use.
* The color to use.
* @see #getColor() * @see #getColor()
* @see #setPaintBorder(boolean) * @see #setPaintBorder(boolean)
*/ */
@@ -219,25 +211,23 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
caretUpdate(null); // Force a highlight repaint. caretUpdate(null); // Force a highlight repaint.
} }
} }
/** /**
* Sets the delay between the last caret position change and when the * Sets the delay between the last caret position change and when the text is
* text is scanned for matching identifiers. A delay is needed to prevent * scanned for matching identifiers. A delay is needed to prevent repeated
* repeated scanning while the user is typing. * scanning while the user is typing.
* *
* @param delay * @param delay The new delay.
* The new delay.
* @see #getDelay() * @see #getDelay()
*/ */
public void setDelay(int delay) { public void setDelay(int delay) {
timer.setInitialDelay(delay); timer.setInitialDelay(delay);
} }
/** /**
* Toggles whether a border is painted around marked highlights. * Toggles whether a border is painted around marked highlights.
* *
* @param paint * @param paint Whether to paint a border.
* Whether to paint a border.
* @see #getPaintBorder() * @see #getPaintBorder()
* @see #setColor(Color) * @see #setColor(Color)
*/ */
@@ -249,10 +239,10 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
} }
} }
} }
/** /**
* Uninstalls this listener from the current text area. Does nothing if * Uninstalls this listener from the current text area. Does nothing if it not
* it not currently installed on any text area. * currently installed on any text area.
* *
* @see #install(RSyntaxTextArea) * @see #install(RSyntaxTextArea)
*/ */
@@ -41,11 +41,11 @@ import org.fife.ui.rsyntaxtextarea.focusabletip.TipUtil;
* @version 1.0 * @version 1.0
*/ */
class MatchedBracketPopup extends JWindow { class MatchedBracketPopup extends JWindow {
private RSyntaxTextArea textArea; private RSyntaxTextArea textArea;
private transient Listener listener; private transient Listener listener;
private static final int LEFT_EMPTY_BORDER = 5; private static final int LEFT_EMPTY_BORDER = 5;
MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int offsToRender) { MatchedBracketPopup(Window parent, RSyntaxTextArea textArea, int offsToRender) {
super(parent); super(parent);
this.textArea = textArea; this.textArea = textArea;
@@ -59,7 +59,7 @@ class MatchedBracketPopup extends JWindow {
listener = new Listener(); listener = new Listener();
setLocation(); setLocation();
} }
/** /**
* Overridden to ensure this popup stays in a specific size range. * Overridden to ensure this popup stays in a specific size range.
*/ */
@@ -71,7 +71,7 @@ class MatchedBracketPopup extends JWindow {
} }
return size; return size;
} }
private String getText(int offsToRender) { private String getText(int offsToRender) {
int line = 0; int line = 0;
try { try {
@@ -110,7 +110,7 @@ class MatchedBracketPopup extends JWindow {
} }
return sb.toString(); return sb.toString();
} }
/** /**
* Adds key bindings to this popup. * Adds key bindings to this popup.
*/ */
@@ -121,10 +121,9 @@ class MatchedBracketPopup extends JWindow {
im.put(escapeKS, "onEscape"); im.put(escapeKS, "onEscape");
am.put("onEscape", new EscapeAction()); am.put("onEscape", new EscapeAction());
} }
/** /**
* Positions this popup to be in the top right-hand corner of the parent * Positions this popup to be in the top right-hand corner of the parent editor.
* editor.
*/ */
private void setLocation() { private void setLocation() {
Point topLeft = textArea.getVisibleRect().getLocation(); Point topLeft = textArea.getVisibleRect().getLocation();
@@ -132,23 +131,23 @@ class MatchedBracketPopup extends JWindow {
topLeft.y = Math.max(topLeft.y - 24, 0); topLeft.y = Math.max(topLeft.y - 24, 0);
setLocation(topLeft.x - LEFT_EMPTY_BORDER, topLeft.y); setLocation(topLeft.x - LEFT_EMPTY_BORDER, topLeft.y);
} }
/** /**
* Action performed when Escape is pressed in this popup. * Action performed when Escape is pressed in this popup.
*/ */
private class EscapeAction extends AbstractAction { private class EscapeAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
listener.uninstallAndHide(); listener.uninstallAndHide();
} }
} }
/** /**
* Listens for events in this popup. * Listens for events in this popup.
*/ */
private class Listener extends WindowAdapter implements ComponentListener { private class Listener extends WindowAdapter implements ComponentListener {
Listener() { Listener() {
addWindowFocusListener(this); addWindowFocusListener(this);
// If anything happens to the "parent" window, hide this popup // If anything happens to the "parent" window, hide this popup
@@ -157,42 +156,42 @@ class MatchedBracketPopup extends JWindow {
parent.addWindowListener(this); parent.addWindowListener(this);
parent.addComponentListener(this); parent.addComponentListener(this);
} }
@Override @Override
public void componentResized(ComponentEvent e) { public void componentResized(ComponentEvent e) {
uninstallAndHide(); uninstallAndHide();
} }
@Override @Override
public void componentMoved(ComponentEvent e) { public void componentMoved(ComponentEvent e) {
uninstallAndHide(); uninstallAndHide();
} }
@Override @Override
public void componentShown(ComponentEvent e) { public void componentShown(ComponentEvent e) {
uninstallAndHide(); uninstallAndHide();
} }
@Override @Override
public void componentHidden(ComponentEvent e) { public void componentHidden(ComponentEvent e) {
uninstallAndHide(); uninstallAndHide();
} }
@Override @Override
public void windowActivated(WindowEvent e) { public void windowActivated(WindowEvent e) {
checkForParentWindowEvent(e); checkForParentWindowEvent(e);
} }
@Override @Override
public void windowLostFocus(WindowEvent e) { public void windowLostFocus(WindowEvent e) {
uninstallAndHide(); uninstallAndHide();
} }
@Override @Override
public void windowIconified(WindowEvent e) { public void windowIconified(WindowEvent e) {
checkForParentWindowEvent(e); checkForParentWindowEvent(e);
} }
private boolean checkForParentWindowEvent(WindowEvent e) { private boolean checkForParentWindowEvent(WindowEvent e) {
if (e.getSource() == getParent()) { if (e.getSource() == getParent()) {
uninstallAndHide(); uninstallAndHide();
@@ -200,7 +199,7 @@ class MatchedBracketPopup extends JWindow {
} }
return false; return false;
} }
private void uninstallAndHide() { private void uninstallAndHide() {
Window parent = (Window) getParent(); Window parent = (Window) getParent();
parent.removeWindowFocusListener(this); 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 * An <code>OccurrenceMarker</code> is called when the caret stops moving after
* a short period. If the current {@link TokenMaker} returns an instance of * a short period. If the current {@link TokenMaker} returns an instance of this
* this class, it is told to mark all occurrences of the identifier at the * class, it is told to mark all occurrences of the identifier at the caret
* caret position. * position.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public interface OccurrenceMarker { public interface OccurrenceMarker {
/** /**
* Returns the token to mark occurrences, of, provided it matches the * Returns the token to mark occurrences, of, provided it matches the criteria
* criteria put forth by {@link #isValidType(RSyntaxTextArea, Token)}. * put forth by {@link #isValidType(RSyntaxTextArea, Token)}. For most
* For most languages, this method should return the token at the caret * languages, this method should return the token at the caret position.
* position.
* *
* @param textArea * @param textArea The text area.
* The text area. * @return The token to (possibly) mark occurrences of, or <code>null</code> if
* @return The token to (possibly) mark occurrences of, or * none.
* <code>null</code> if none.
*/ */
Token getTokenToMark(RSyntaxTextArea textArea); Token getTokenToMark(RSyntaxTextArea textArea);
/** /**
* Returns whether the specified token is a type that we can do a * Returns whether the specified token is a type that we can do a "mark
* "mark occurrences" of. Typically, this will delegate to * occurrences" of. Typically, this will delegate to
* {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}. * {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}.
* *
* @param textArea * @param textArea The text area.
* The text area. * @param t The token.
* @param t
* The token.
* @return Whether we should mark all occurrences of this token. * @return Whether we should mark all occurrences of this token.
*/ */
boolean isValidType(RSyntaxTextArea textArea, Token t); boolean isValidType(RSyntaxTextArea textArea, Token t);
/** /**
* Called when occurrences of a token should be marked. * Called when occurrences of a token should be marked.
* *
* @param doc * @param doc The document.
* The document. * @param t The document whose relevant occurrences should be marked.
* @param t * @param h The highlighter to add the highlights to.
* The document whose relevant occurrences should be marked. * @param p The painter for the highlights.
* @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); 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 * @version 0.9
*/ */
class ParserManager implements DocumentListener, ActionListener, HyperlinkListener, PropertyChangeListener { class ParserManager implements DocumentListener, ActionListener, HyperlinkListener, PropertyChangeListener {
private RSyntaxTextArea textArea; private RSyntaxTextArea textArea;
private List<Parser> parsers; private List<Parser> parsers;
private Timer timer; private Timer timer;
@@ -55,20 +55,21 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
private Position firstOffsetModded; private Position firstOffsetModded;
private Position lastOffsetModded; private Position lastOffsetModded;
/** /**
* Mapping of notices to their highlights in the editor. Can't use a Map * Mapping of notices to their highlights in the editor. Can't use a Map since
* since parsers could return two <code>ParserNotice</code>s that compare * parsers could return two <code>ParserNotice</code>s that compare equally via
* equally via <code>equals()</code>. Real-world example: The Perl * <code>equals()</code>. Real-world example: The Perl compiler will return 2+
* compiler will return 2+ identical error messages if the same error is * identical error messages if the same error is committed in a single line more
* committed in a single line more than once. * than once.
*/ */
private List<NoticeHighlightPair> noticeHighlightPairs; private List<NoticeHighlightPair> noticeHighlightPairs;
/** /**
* Painter used to underline errors. * 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 * If this system property is set to <code>true</code>, debug messages will be
* will be printed to stdout to help diagnose parsing issues. * printed to stdout to help diagnose parsing issues.
*/ */
private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing"; 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; private static final boolean DEBUG_PARSING;
/** /**
* The default delay between the last key press and when the document * The default delay between the last key press and when the document is parsed,
* is parsed, in milliseconds. * in milliseconds.
*/ */
private static final int DEFAULT_DELAY_MS = 1250; private static final int DEFAULT_DELAY_MS = 1250;
/** /**
* Constructor. * Constructor.
* *
* @param textArea * @param textArea The text area whose document the parser will be parsing.
* The text area whose document the parser will be
* parsing.
*/ */
ParserManager(RSyntaxTextArea textArea) { ParserManager(RSyntaxTextArea textArea) {
this(DEFAULT_DELAY_MS, textArea); this(DEFAULT_DELAY_MS, textArea);
} }
/** /**
* Constructor. * Constructor.
* *
* @param delay * @param delay The delay between the last key press and when the document is
* The delay between the last key press and when the document * parsed.
* is parsed. * @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(int delay, RSyntaxTextArea textArea) { ParserManager(int delay, RSyntaxTextArea textArea) {
this.textArea = textArea; this.textArea = textArea;
@@ -111,12 +107,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.setRepeats(false); timer.setRepeats(false);
running = true; running = true;
} }
/** /**
* Called when the timer fires (e.g. it's time to parse the document). * Called when the timer fires (e.g. it's time to parse the document).
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@@ -132,7 +127,8 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument(); RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
Element root = doc.getDefaultRootElement(); Element root = doc.getDefaultRootElement();
int firstLine = firstOffsetModded == null ? 0 : root.getElementIndex(firstOffsetModded.getOffset()); 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; firstOffsetModded = lastOffsetModded = null;
if (DEBUG_PARSING) { if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine); 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"); System.out.println("Total parsing time: " + time + " seconds");
} }
} }
/** /**
* Adds a parser for the text area. * Adds a parser for the text area.
* *
* @param parser * @param parser The new parser. If this is <code>null</code>, nothing happens.
* The new parser. If this is <code>null</code>, nothing
* happens.
* @see #getParser(int) * @see #getParser(int)
* @see #removeParser(Parser) * @see #removeParser(Parser)
*/ */
@@ -183,13 +177,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
} }
} }
/** /**
* Adds highlights for a list of parser notices. Any current notices * Adds highlights for a list of parser notices. Any current notices from the
* from the same Parser, in the same parsed range, are removed. * same Parser, in the same parsed range, are removed.
* *
* @param res * @param res The result of a parsing.
* The result of a parsing.
* @see #clearParserNoticeHighlights() * @see #clearParserNoticeHighlights()
*/ */
private void addParserNoticeHighlights(ParseResult res) { 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()); System.out.println("[DEBUG]: Done adding parser notices from " + res.getParser());
} }
} }
/** /**
* Called when the document is modified. * Called when the document is modified.
* *
* @param e * @param e The document event.
* The document event.
*/ */
@Override @Override
public void changedUpdate(DocumentEvent e) {} public void changedUpdate(DocumentEvent e) {
}
private void clearParserNoticeHighlights() { private void clearParserNoticeHighlights() {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter(); RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
if (h != null) { if (h != null) {
@@ -246,12 +239,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
noticeHighlightPairs.clear(); noticeHighlightPairs.clear();
} }
} }
/** /**
* Removes all parser notice highlights for a specific parser. * Removes all parser notice highlights for a specific parser.
* *
* @param parser * @param parser The parser whose highlights to remove.
* The parser whose highlights to remove.
*/ */
private void clearParserNoticeHighlights(Parser parser) { private void clearParserNoticeHighlights(Parser parser) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter(); RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
@@ -268,7 +260,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
} }
} }
/** /**
* Removes all parsers and any highlights they have created. * Removes all parsers and any highlights they have created.
* *
@@ -280,20 +272,18 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
parsers.clear(); parsers.clear();
textArea.fireParserNoticesChange(); textArea.fireParserNoticesChange();
} }
/** /**
* Forces the given {@link Parser} to re-parse the content of this text * Forces the given {@link Parser} to re-parse the content of this text area.
* area.
* <p> * <p>
* *
* This method can be useful when a <code>Parser</code> can be configured * This method can be useful when a <code>Parser</code> can be configured as to
* as to what notices it returns. For example, if a Java language parser * what notices it returns. For example, if a Java language parser can be
* can be configured to set whether no serialVersionUID is a warning, * configured to set whether no serialVersionUID is a warning, error, or
* error, or ignored, this method can be called after changing the expected * ignored, this method can be called after changing the expected notice type to
* notice type to have the document re-parsed. * have the document re-parsed.
* *
* @param parser * @param parser The index of the <code>Parser</code> to re-run.
* The index of the <code>Parser</code> to re-run.
* @see #getParser(int) * @see #getParser(int)
*/ */
public void forceReparsing(int parser) { public void forceReparsing(int parser) {
@@ -313,10 +303,10 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
doc.readUnlock(); doc.readUnlock();
} }
} }
/** /**
* Returns the delay between the last "concurrent" edit and when the * Returns the delay between the last "concurrent" edit and when the document is
* document is re-parsed. * re-parsed.
* *
* @return The delay, in milliseconds. * @return The delay, in milliseconds.
* @see #setDelay(int) * @see #setDelay(int)
@@ -324,12 +314,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
public int getDelay() { public int getDelay() {
return timer.getDelay(); return timer.getDelay();
} }
/** /**
* Returns the specified parser. * Returns the specified parser.
* *
* @param index * @param index The index of the parser.
* The index of the parser.
* @return The parser. * @return The parser.
* @see #getParserCount() * @see #getParserCount()
* @see #addParser(Parser) * @see #addParser(Parser)
@@ -338,7 +327,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
public Parser getParser(int index) { public Parser getParser(int index) {
return parsers.get(index); return parsers.get(index);
} }
/** /**
* Returns the number of registered parsers. * Returns the number of registered parsers.
* *
@@ -347,14 +336,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
public int getParserCount() { public int getParserCount() {
return parsers.size(); return parsers.size();
} }
/** /**
* Returns a list of the current parser notices for this text area. * Returns a list of the current parser notices for this text area. This method
* This method (like most Swing methods) should only be called on the * (like most Swing methods) should only be called on the EDT.
* EDT.
* *
* @return The list of notices. This will be an empty list if there are * @return The list of notices. This will be an empty list if there are none.
* none.
*/ */
public List<ParserNotice> getParserNotices() { public List<ParserNotice> getParserNotices() {
List<ParserNotice> notices = new ArrayList<ParserNotice>(); List<ParserNotice> notices = new ArrayList<ParserNotice>();
@@ -365,15 +352,13 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
return notices; return notices;
} }
/** /**
* Returns the tool tip to display for a mouse event at the given * Returns the tool tip to display for a mouse event at the given location. This
* location. This method is overridden to give a registered parser a * method is overridden to give a registered parser a chance to display a tool
* chance to display a tool tip (such as an error description when the * tip (such as an error description when the mouse is over an error highlight).
* mouse is over an error highlight).
* *
* @param e * @param e The mouse event.
* The mouse event.
* @return The tool tip to display, and possibly a hyperlink event handler. * @return The tool tip to display, and possibly a hyperlink event handler.
*/ */
public ToolTipInfo getToolTipText(MouseEvent e) { public ToolTipInfo getToolTipText(MouseEvent e) {
@@ -385,19 +370,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
int pos = textArea.viewToModel(p); int pos = textArea.viewToModel(p);
/* /*
* Highlighter.Highlight[] highlights = textArea.getHighlighter(). * Highlighter.Highlight[] highlights = textArea.getHighlighter().
* getHighlights(); * getHighlights(); for (int i=0; i<highlights.length; i++) {
* for (int i=0; i<highlights.length; i++) { * Highlighter.Highlight h = highlights[i]; //if (h instanceof
* Highlighter.Highlight h = highlights[i]; * ParserNoticeHighlight) { // ParserNoticeHighlight pnh =
* //if (h instanceof ParserNoticeHighlight) { * (ParserNoticeHighlight)h; int start = h.getStartOffset(); int end =
* // ParserNoticeHighlight pnh = (ParserNoticeHighlight)h; * h.getEndOffset(); if (start<=pos && end>=pos) { //return pnh.getMessage();
* int start = h.getStartOffset(); * return textArea.getText(start, end-start); } //} }
* int end = h.getEndOffset();
* if (start<=pos && end>=pos) {
* //return pnh.getMessage();
* return textArea.getText(start, end-start);
* }
* //}
* }
*/ */
if (noticeHighlightPairs != null) { if (noticeHighlightPairs != null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) { for (NoticeHighlightPair pair : noticeHighlightPairs) {
@@ -418,25 +396,23 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
URL imageBase = parserForTip == null ? null : parserForTip.getImageBase(); URL imageBase = parserForTip == null ? null : parserForTip.getImageBase();
return new ToolTipInfo(tip, listener, imageBase); return new ToolTipInfo(tip, listener, imageBase);
} }
/** /**
* Called when the document is modified. * Called when the document is modified.
* *
* @param e * @param e The document event.
* The document event.
*/ */
public void handleDocumentEvent(DocumentEvent e) { public void handleDocumentEvent(DocumentEvent e) {
if (running && parsers.size() > 0) { if (running && parsers.size() > 0) {
timer.restart(); timer.restart();
} }
} }
/** /**
* Called when the user clicks a hyperlink in a * Called when the user clicks a hyperlink in a
* {@link org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip}. * {@link org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip}.
* *
* @param e * @param e The event.
* The event.
*/ */
@Override @Override
public void hyperlinkUpdate(HyperlinkEvent e) { public void hyperlinkUpdate(HyperlinkEvent e) {
@@ -444,12 +420,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
parserForTip.getHyperlinkListener().linkClicked(textArea, e); parserForTip.getHyperlinkListener().linkClicked(textArea, e);
} }
} }
/** /**
* Called when the document is modified. * Called when the document is modified.
* *
* @param e * @param e The document event.
* The document event.
*/ */
@Override @Override
public void insertUpdate(DocumentEvent e) { public void insertUpdate(DocumentEvent e) {
@@ -469,14 +444,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
handleDocumentEvent(e); handleDocumentEvent(e);
} }
/** /**
* Returns whether a parser notice contains the specified offset. * Returns whether a parser notice contains the specified offset.
* *
* @param notice * @param notice The notice.
* The notice. * @param offs The offset.
* @param offs
* The offset.
* @return Whether the notice contains the offset. * @return Whether the notice contains the offset.
*/ */
private boolean noticeContainsPosition(ParserNotice notice, int offs) { private boolean noticeContainsPosition(ParserNotice notice, int offs) {
@@ -492,20 +465,18 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
Element elem = root.getElement(line); Element elem = root.getElement(line);
return elem != null && offs >= elem.getStartOffset() && offs < elem.getEndOffset(); return elem != null && offs >= elem.getStartOffset() && offs < elem.getEndOffset();
} }
/** /**
* Since <code>viewToModel()</code> returns the <em>closest</em> model * Since <code>viewToModel()</code> returns the <em>closest</em> model position,
* position, and the position doesn't <em>necessarily</em> contain the * and the position doesn't <em>necessarily</em> contain the point passed in as
* point passed in as an argument, this method checks whether the point is * an argument, this method checks whether the point is indeed contained in the
* indeed contained in the view rectangle for the specified offset. * view rectangle for the specified offset.
* *
* @param notice * @param notice The parser notice.
* The parser notice. * @param p The point possibly contained in the view range of the parser
* @param p * notice.
* The point possibly contained in the view range of the * @return Whether the parser notice actually contains the specified point in
* parser notice. * the view.
* @return Whether the parser notice actually contains the specified point
* in the view.
*/ */
private boolean noticeContainsPointInView(ParserNotice notice, Point p) { private boolean noticeContainsPointInView(ParserNotice notice, Point p) {
try { try {
@@ -543,12 +514,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
return true; return true;
} }
} }
/** /**
* Called when a property we're interested in changes. * Called when a property we're interested in changes.
* *
* @param e * @param e The property change event.
* The property change event.
*/ */
@Override @Override
public void propertyChange(PropertyChangeEvent e) { public void propertyChange(PropertyChangeEvent e) {
@@ -565,12 +535,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
} }
} }
/** /**
* Removes a parser. * Removes a parser.
* *
* @param parser * @param parser The parser to remove.
* The parser to remove.
* @return Whether the parser was found. * @return Whether the parser was found.
* @see #addParser(Parser) * @see #addParser(Parser)
* @see #getParser(int) * @see #getParser(int)
@@ -583,13 +552,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
return removed; return removed;
} }
/** /**
* Removes all parser notices (and clears highlights in the editor) from * Removes all parser notices (and clears highlights in the editor) from a
* a particular parser. * particular parser.
* *
* @param parser * @param parser The parser.
* The parser.
*/ */
private void removeParserNotices(Parser parser) { private void removeParserNotices(Parser parser) {
if (noticeHighlightPairs != null) { if (noticeHighlightPairs != null) {
@@ -603,14 +571,13 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
} }
} }
/** /**
* Removes any currently stored notices (and the corresponding highlights * Removes any currently stored notices (and the corresponding highlights from
* from the editor) from the same Parser, and in the given line range, * the editor) from the same Parser, and in the given line range, as in the
* as in the results. * results.
* *
* @param res * @param res The results.
* The results.
*/ */
private void removeParserNotices(ParseResult res) { private void removeParserNotices(ParseResult res) {
if (noticeHighlightPairs != null) { if (noticeHighlightPairs != null) {
@@ -632,12 +599,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
} }
} }
/** /**
* Called when the document is modified. * Called when the document is modified.
* *
* @param e * @param e The document event.
* The document event.
*/ */
@Override @Override
public void removeUpdate(DocumentEvent e) { public void removeUpdate(DocumentEvent e) {
@@ -658,7 +624,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
} }
handleDocumentEvent(e); handleDocumentEvent(e);
} }
/** /**
* Restarts parsing the document. * Restarts parsing the document.
* *
@@ -668,14 +634,13 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.restart(); timer.restart();
running = true; running = true;
} }
/** /**
* Sets the delay between the last "concurrent" edit and when the document * Sets the delay between the last "concurrent" edit and when the document is
* is re-parsed. * re-parsed.
* *
* @param millis * @param millis The new delay, in milliseconds. This must be greater than
* The new delay, in milliseconds. This must be greater * <code>0</code>.
* than <code>0</code>.
* @see #getDelay() * @see #getDelay()
*/ */
public void setDelay(int millis) { public void setDelay(int millis) {
@@ -688,20 +653,18 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.start(); timer.start();
} }
} }
/** /**
* Returns whether a parser notice should be removed, based on a parse * Returns whether a parser notice should be removed, based on a parse result.
* result.
* *
* @param notice * @param notice The notice in question.
* The notice in question. * @param res The result.
* @param res
* The result.
* @return Whether the notice should be removed. * @return Whether the notice should be removed.
*/ */
private boolean shouldRemoveNotice(ParserNotice notice, ParseResult res) { private boolean shouldRemoveNotice(ParserNotice notice, ParseResult res) {
if (DEBUG_PARSING) { 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 // NOTE: We must currently remove all notices for the parser. Parser
// implementors are required to parse the entire document each parsing // 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 same_parser && (in_reparsed_range || in_deleted_end_of_doc)
return notice.getParser() == res.getParser(); return notice.getParser() == res.getParser();
} }
/** /**
* Stops parsing the document. * Stops parsing the document.
* *
@@ -722,21 +685,21 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
timer.stop(); timer.stop();
running = false; running = false;
} }
/** /**
* Mapping of a parser notice to its highlight in the editor. * Mapping of a parser notice to its highlight in the editor.
*/ */
private static class NoticeHighlightPair { private static class NoticeHighlightPair {
private ParserNotice notice; private ParserNotice notice;
private HighlightInfo highlight; private HighlightInfo highlight;
NoticeHighlightPair(ParserNotice notice, HighlightInfo highlight) { NoticeHighlightPair(ParserNotice notice, HighlightInfo highlight) {
this.notice = notice; this.notice = notice;
this.highlight = highlight; this.highlight = highlight;
} }
} }
static { static {
boolean debugParsing = false; boolean debugParsing = false;
try { try {
@@ -12,48 +12,45 @@ import javax.swing.JWindow;
/** /**
* A hook allowing hosting applications to decorate JWindows created by the * A hook allowing hosting applications to decorate JWindows created by the
* AutoComplete library. For example, you could use the * AutoComplete library. For example, you could use the
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows * <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows to
* to the windows. * the windows.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public abstract class PopupWindowDecorator { public abstract class PopupWindowDecorator {
/** /**
* The singleton instance of this class. * The singleton instance of this class.
*/ */
private static PopupWindowDecorator decorator; private static PopupWindowDecorator decorator;
/** /**
* Callback called whenever an appropriate JWindow is created by the * Callback called whenever an appropriate JWindow is created by the
* AutoComplete library. Implementations can decorate the window however * AutoComplete library. Implementations can decorate the window however they
* they see fit. * see fit.
* *
* @param window * @param window The newly-created window.
* The newly-created window.
*/ */
public abstract void decorate(JWindow window); public abstract void decorate(JWindow window);
/** /**
* Returns the singleton instance of this class. This should only be * Returns the singleton instance of this class. This should only be called on
* called on the EDT. * the EDT.
* *
* @return The singleton instance of this class, or <code>null</code> * @return The singleton instance of this class, or <code>null</code> for none.
* for none.
* @see #set(PopupWindowDecorator) * @see #set(PopupWindowDecorator)
*/ */
public static PopupWindowDecorator get() { public static PopupWindowDecorator get() {
return decorator; return decorator;
} }
/** /**
* Sets the singleton instance of this class. This should only be called * Sets the singleton instance of this class. This should only be called on the
* on the EDT. * EDT.
* *
* @param decorator * @param decorator The new instance of this class. This may be
* The new instance of this class. This may be * <code>null</code>.
* <code>null</code>.
* @see #get() * @see #get()
*/ */
public static void set(PopupWindowDecorator decorator) { public static void set(PopupWindowDecorator decorator) {
+20 -28
View File
@@ -17,46 +17,38 @@ import javax.swing.text.BadLocationException;
* @version 1.0 * @version 1.0
*/ */
interface RSTAView { interface RSTAView {
/** /**
* Returns the y-coordinate of the specified line. * Returns the y-coordinate of the specified line.
* <p> * <p>
* *
* This method is quicker than using traditional * This method is quicker than using traditional <code>modelToView(int)</code>
* <code>modelToView(int)</code> calls, as the entire bounding box isn't * calls, as the entire bounding box isn't computed.
* computed.
* *
* @param alloc * @param alloc The area the text area can render into.
* The area the text area can render into. * @param line The line number.
* @param line * @return The y-coordinate of the top of the line, or <code>-1</code> if this
* The line number. * text area doesn't yet have a positive size or the line is hidden
* @return The y-coordinate of the top of the line, or <code>-1</code> if * (i.e. from folding).
* this text area doesn't yet have a positive size or the line is * @throws BadLocationException If <code>line</code> isn't a valid line number
* hidden (i.e. from folding). * for this document.
* @throws BadLocationException
* If <code>line</code> isn't a valid line
* number for this document.
*/ */
int yForLine(Rectangle alloc, int line) throws BadLocationException; int yForLine(Rectangle alloc, int line) throws BadLocationException;
/** /**
* Returns the y-coordinate of the line containing a specified offset. * Returns the y-coordinate of the line containing a specified offset.
* <p> * <p>
* *
* This method is quicker than using traditional * This method is quicker than using traditional <code>modelToView(int)</code>
* <code>modelToView(int)</code> calls, as the entire bounding box isn't * calls, as the entire bounding box isn't computed.
* computed.
* *
* @param alloc * @param alloc The area the text area can render into.
* The area the text area can render into. * @param offs The offset info the document.
* @param offs * @return The y-coordinate of the top of the offset, or <code>-1</code> if this
* The offset info the document. * text area doesn't yet have a positive size or the line is hidden
* @return The y-coordinate of the top of the offset, or <code>-1</code> if * (i.e. from folding).
* this text area doesn't yet have a positive size or the line is * @throws BadLocationException If <code>offs</code> isn't a valid offset into
* hidden (i.e. from folding). * the document.
* @throws BadLocationException
* If <code>offs</code> isn't a valid offset
* into the document.
*/ */
int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException; int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException;
} }
@@ -49,10 +49,9 @@ import me.ByteEdit.edit.JavaAssemblyTokenMaker;
* @version 1.0 * @version 1.0
*/ */
public class RSyntaxDocument extends RDocument implements Iterable<Token>, SyntaxConstants { public class RSyntaxDocument extends RDocument implements Iterable<Token>, SyntaxConstants {
/** /**
* Creates a {@link TokenMaker} appropriate for a given programming * Creates a {@link TokenMaker} appropriate for a given programming language.
* language.
*/ */
private transient TokenMakerFactory tokenMakerFactory; private transient TokenMakerFactory tokenMakerFactory;
/** /**
@@ -64,8 +63,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
*/ */
private String syntaxStyle; private String syntaxStyle;
/** /**
* Array of values representing the "last token type" on each line. This is * Array of values representing the "last token type" on each line. This is used
* used
* in cases such as multi-line comments: if the previous line ended with an * 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 * (unclosed) multi-line comment, we can use this knowledge and start the
* current line's syntax highlighting in multi-line comment state. * 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 int tokenRetrievalCount = 0;
private transient Segment s; private transient Segment s;
/** /**
* If this is set to <code>true</code>, debug information about how much * If this is set to <code>true</code>, debug information about how much token
* token
* caching is helping is printed to stdout. * caching is helping is printed to stdout.
*/ */
private static final boolean DEBUG_TOKEN_CACHING = false; private static final boolean DEBUG_TOKEN_CACHING = false;
/** /**
* Constructs a plain text document. A default root element is created, and * Constructs a plain text document. A default root element is created, and the
* the
* tab size set to 5. * tab size set to 5.
* *
* @param syntaxStyle * @param syntaxStyle The syntax highlighting scheme to use.
* The syntax highlighting scheme to use.
*/ */
public RSyntaxDocument(String syntaxStyle) { public RSyntaxDocument(String syntaxStyle) {
this(null, syntaxStyle); this(null, syntaxStyle);
} }
/** /**
* Constructs a plain text document. A default root element is created, and * Constructs a plain text document. A default root element is created, and the
* the
* tab size set to 5. * tab size set to 5.
* *
* @param tmf * @param tmf The <code>TokenMakerFactory</code> for this document. If
* The <code>TokenMakerFactory</code> for this document. If this * this is <code>null</code>, a default factory is used.
* is * @param syntaxStyle The syntax highlighting scheme to use.
* <code>null</code>, a default factory is used.
* @param syntaxStyle
* The syntax highlighting scheme to use.
*/ */
public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) { public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
putProperty(tabSizeAttribute, Integer.valueOf(5)); putProperty(tabSizeAttribute, Integer.valueOf(5));
@@ -115,27 +106,23 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
setTokenMakerFactory(tmf); setTokenMakerFactory(tmf);
setSyntaxStyle(syntaxStyle); setSyntaxStyle(syntaxStyle);
} }
/** /**
* Alerts all listeners to this document of an insertion. This is overridden * Alerts all listeners to this document of an insertion. This is overridden so
* so
* we can update our syntax highlighting stuff. * we can update our syntax highlighting stuff.
* <p> * <p>
* The syntax highlighting stuff has to be here instead of in * The syntax highlighting stuff has to be here instead of in
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called * <code>insertUpdate</code> because <code>insertUpdate</code> is not called by
* by
* the undo/redo actions, but this method is. * the undo/redo actions, but this method is.
* *
* @param e * @param e The change.
* The change.
*/ */
@Override @Override
protected void fireInsertUpdate(DocumentEvent e) { protected void fireInsertUpdate(DocumentEvent e) {
cachedTokenList = null; cachedTokenList = null;
/* /*
* Now that the text is actually inserted into the content and element * Now that the text is actually inserted into the content and element
* structure, we can update our token elements and * structure, we can update our token elements and "last tokens on lines"
* "last tokens on lines"
* structure. * structure.
*/ */
Element lineMap = getDefaultRootElement(); Element lineMap = getDefaultRootElement();
@@ -174,23 +161,20 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
// Let all listeners know about the insertion. // Let all listeners know about the insertion.
super.fireInsertUpdate(e); super.fireInsertUpdate(e);
} }
/** /**
* This method is called AFTER the content has been inserted into the * This method is called AFTER the content has been inserted into the document
* document
* and the element structure has been updated. * and the element structure has been updated.
* <p> * <p>
* The syntax-highlighting updates need to be done here (as opposed to an * The syntax-highlighting updates need to be done here (as opposed to an
* override of <code>postRemoveUpdate</code>) as this method is called in * override of <code>postRemoveUpdate</code>) as this method is called in
* response to undo/redo events, whereas <code>postRemoveUpdate</code> is * response to undo/redo events, whereas <code>postRemoveUpdate</code> is not.
* not.
* <p> * <p>
* Now that the text is actually inserted into the content and element * 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. * structure.
* *
* @param chng * @param chng The change that occurred.
* The change that occurred.
* @see #removeUpdate * @see #removeUpdate
*/ */
@Override @Override
@@ -240,26 +224,22 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
// Let all of our listeners know about the removal. // Let all of our listeners know about the removal.
super.fireRemoveUpdate(chng); super.fireRemoveUpdate(chng);
} }
/** /**
* Returns the closest {@link TokenTypes "standard" token type} for a given * Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>). * "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
* *
* @param type * @param type The token type.
* The token type. * @return The closest "standard" token type. If a mapping is not defined for
* @return The closest "standard" token type. If a mapping is not defined
* for
* this language, then <code>type</code> is returned. * this language, then <code>type</code> is returned.
*/ */
public int getClosestStandardTokenTypeForInternalType(int type) { public int getClosestStandardTokenTypeForInternalType(int type) {
return tokenMaker.getClosestStandardTokenTypeForInternalType(type); return tokenMaker.getClosestStandardTokenTypeForInternalType(type);
} }
/** /**
* Returns whether closing markup tags should be automatically completed. * Returns whether closing markup tags should be automatically completed. This
* This * method only returns <code>true</code> if {@link #getLanguageIsMarkup()} also
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()}
* also
* returns <code>true</code>. * returns <code>true</code>.
* *
* @return Whether markup closing tags should be automatically completed. * @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 // TODO: Remove terrible dependency on AbstractMarkupTokenMaker
return getLanguageIsMarkup() && ((AbstractMarkupTokenMaker) tokenMaker).getCompleteCloseTags(); return getLanguageIsMarkup() && ((AbstractMarkupTokenMaker) tokenMaker).getCompleteCloseTags();
} }
/** /**
* Returns whether the current programming language uses curly braces * Returns whether the current programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks. * ('<code>{</code>' and '<code>}</code>') to denote code blocks.
* *
* @param languageIndex * @param languageIndex The language index at the offset in question. Since some
* The language index at the offset in question. Since some * <code>TokenMaker</code>s effectively have nested
* <code>TokenMaker</code>s effectively have nested languages * languages (such as JavaScript in HTML), this parameter
* (such * tells the <code>TokenMaker</code> what sub-language to
* as JavaScript in HTML), this parameter tells the * look at.
* <code>TokenMaker</code> what sub-language to look at.
* @return Whether curly braces denote code blocks. * @return Whether curly braces denote code blocks.
*/ */
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) { public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
return tokenMaker.getCurlyBracesDenoteCodeBlocks(languageIndex); return tokenMaker.getCurlyBracesDenoteCodeBlocks(languageIndex);
} }
/** /**
* Returns whether the current language is a markup language, such as HTML, * Returns whether the current language is a markup language, such as HTML, XML
* XML
* or PHP. * or PHP.
* *
* @return Whether the current language is a markup language. * @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() { public boolean getLanguageIsMarkup() {
return tokenMaker.isMarkupLanguage(); return tokenMaker.isMarkupLanguage();
} }
/** /**
* Returns the token type of the last token on the given line. * Returns the token type of the last token on the given line.
* *
* @param line * @param line The line to inspect.
* The line to inspect. * @return The token type of the last token on the specified line. If the line
* @return The token type of the last token on the specified line. If the
* line
* is invalid, an exception is thrown. * is invalid, an exception is thrown.
*/ */
public int getLastTokenTypeOnLine(int line) { public int getLastTokenTypeOnLine(int line) {
return lastTokensOnLines.get(line); return lastTokensOnLines.get(line);
} }
/** /**
* Returns the text to place at the beginning and end of a line to "comment" * Returns the text to place at the beginning and end of a line to "comment" it
* it
* in this programming language. * in this programming language.
* *
* @return The start and end strings to add to a line to "comment" it out. A * @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 * <code>null</code> value for either means there is no string to add
* add * for that part. A value of <code>null</code> for the array means this
* for that part. A value of <code>null</code> for the array means
* this
* language does not support commenting/uncommenting lines. * language does not support commenting/uncommenting lines.
*/ */
public String[] getLineCommentStartAndEnd(int languageIndex) { public String[] getLineCommentStartAndEnd(int languageIndex) {
return tokenMaker.getLineCommentStartAndEnd(languageIndex); return tokenMaker.getLineCommentStartAndEnd(languageIndex);
} }
/** /**
* Returns whether tokens of the specified type should have "mark * Returns whether tokens of the specified type should have "mark occurrences"
* occurrences"
* enabled for the current programming language. * enabled for the current programming language.
* *
* @param type * @param type The token type.
* The token type. * @return Whether tokens of this type should have "mark occurrences" enabled.
* @return Whether tokens of this type should have "mark occurrences"
* enabled.
*/ */
boolean getMarkOccurrencesOfTokenType(int type) { boolean getMarkOccurrencesOfTokenType(int type) {
return tokenMaker.getMarkOccurrencesOfTokenType(type); return tokenMaker.getMarkOccurrencesOfTokenType(type);
} }
/** /**
* Returns the occurrence marker for the current language. * Returns the occurrence marker for the current language.
* *
@@ -348,13 +318,12 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
OccurrenceMarker getOccurrenceMarker() { OccurrenceMarker getOccurrenceMarker() {
return tokenMaker.getOccurrenceMarker(); return tokenMaker.getOccurrenceMarker();
} }
/** /**
* This method returns whether auto indentation should be done if Enter is * This method returns whether auto indentation should be done if Enter is
* pressed at the end of the specified line. * pressed at the end of the specified line.
* *
* @param line * @param line The line to check.
* The line to check.
* @return Whether an extra indentation should be done. * @return Whether an extra indentation should be done.
*/ */
public boolean getShouldIndentNextLine(int line) { public boolean getShouldIndentNextLine(int line) {
@@ -362,7 +331,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
t = t.getLastNonCommentNonWhitespaceToken(); t = t.getLastNonCommentNonWhitespaceToken();
return tokenMaker.getShouldIndentNextLineAfter(t); return tokenMaker.getShouldIndentNextLineAfter(t);
} }
/** /**
* Returns the syntax style being used. * Returns the syntax style being used.
* *
@@ -372,16 +341,14 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public String getSyntaxStyle() { public String getSyntaxStyle() {
return syntaxStyle; return syntaxStyle;
} }
/** /**
* Returns a token list for the specified segment of text representing the * Returns a token list for the specified segment of text representing the
* specified line number. This method is basically a wrapper for * specified line number. This method is basically a wrapper for
* <code>tokenMaker.getTokenList</code> that takes into account the last * <code>tokenMaker.getTokenList</code> that takes into account the last token
* token
* on the previous line to assure token accuracy. * on the previous line to assure token accuracy.
* *
* @param line * @param line The line number of <code>text</code> in the document, &gt;= 0.
* The line number of <code>text</code> in the document, &gt;= 0.
* @return A token list representing the specified line. * @return A token list representing the specified line.
*/ */
public final Token getTokenListForLine(int 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); cachedTokenList = tokenMaker.getTokenList(s, initialTokenType, startOffset);
return cachedTokenList; return cachedTokenList;
} }
boolean insertBreakSpecialHandling(ActionEvent e) { boolean insertBreakSpecialHandling(ActionEvent e) {
Action a = tokenMaker.getInsertBreakAction(); Action a = tokenMaker.getInsertBreakAction();
if (a != null) { if (a != null) {
@@ -421,28 +388,23 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
} }
return false; return false;
} }
/** /**
* Returns whether a character could be part of an "identifier" token in a * 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 * specific language. This is used to identify such things as the bounds of the
* the
* "word" to select on double-clicking. * "word" to select on double-clicking.
* *
* @param languageIndex * @param languageIndex The language index the character was found in.
* The language index the character was found in. * @param ch The character.
* @param ch
* The character.
* @return Whether the character could be part of an "identifier" token. * @return Whether the character could be part of an "identifier" token.
*/ */
public boolean isIdentifierChar(int languageIndex, char ch) { public boolean isIdentifierChar(int languageIndex, char ch) {
return tokenMaker.isIdentifierChar(languageIndex, ch); return tokenMaker.isIdentifierChar(languageIndex, ch);
} }
/** /**
* Returns an iterator over the paintable tokens in this document. Results * Returns an iterator over the paintable tokens in this document. Results are
* are * undefined if this document is modified while the iterator is being iterated
* undefined if this document is modified while the iterator is being
* iterated
* through, so this should only be used on the EDT. * through, so this should only be used on the EDT.
* <p> * <p>
* *
@@ -455,12 +417,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
public Iterator<Token> iterator() { public Iterator<Token> iterator() {
return new TokenIterator(this); return new TokenIterator(this);
} }
/** /**
* Deserializes a document. * Deserializes a document.
* *
* @param in * @param in The stream to read from.
* The stream to read from.
* @throws ClassNotFoundException * @throws ClassNotFoundException
* @throws IOException * @throws IOException
*/ */
@@ -476,16 +437,13 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
lastTokensOnLines = new DynamicIntArray(lineCount); lastTokensOnLines = new DynamicIntArray(lineCount);
setSyntaxStyle(syntaxStyle); // Actually install (transient) TokenMaker setSyntaxStyle(syntaxStyle); // Actually install (transient) TokenMaker
} }
/** /**
* Makes our private <code>Segment s</code> point to the text in our * Makes our private <code>Segment s</code> point to the text in our document
* document * referenced by the specified element. Note that <code>line</code> MUST be a
* referenced by the specified element. Note that <code>line</code> MUST be
* a
* valid line number in the document. * valid line number in the document.
* *
* @param line * @param line The line number you want to get.
* The line number you want to get.
*/ */
private void setSharedSegment(int line) { private void setSharedSegment(int line) {
Element map = getDefaultRootElement(); 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); throw new InternalError("Text range not in document: " + startOffset + "-" + endOffset);
} }
} }
/** /**
* Sets the syntax style being used for syntax highlighting in this * Sets the syntax style being used for syntax highlighting in this document.
* document.
* What styles are supported by a document is determined by its * What styles are supported by a document is determined by its
* {@link TokenMakerFactory}. By default, all <code>RSyntaxDocument</code>s * {@link TokenMakerFactory}. By default, all <code>RSyntaxDocument</code>s
* support all languages built into <code>RSyntaxTextArea</code>. * support all languages built into <code>RSyntaxTextArea</code>.
* *
* @param styleKey * @param styleKey The new style to use, such as
* The new style to use, such as * {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is * not known or supported by this document, then
* not * {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* known or supported by this document, then
* {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* @see #setSyntaxStyle(TokenMaker) * @see #setSyntaxStyle(TokenMaker)
* @see #getSyntaxStyle() * @see #getSyntaxStyle()
*/ */
@@ -530,15 +485,13 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
updateSyntaxHighlightingInformation(); updateSyntaxHighlightingInformation();
this.syntaxStyle = styleKey; this.syntaxStyle = styleKey;
} }
/** /**
* Sets the syntax style being used for syntax highlighting in this * Sets the syntax style being used for syntax highlighting in this document.
* document.
* You should call this method if you've created a custom token maker for a * You should call this method if you've created a custom token maker for a
* language not normally supported by <code>RSyntaxTextArea</code>. * language not normally supported by <code>RSyntaxTextArea</code>.
* *
* @param tokenMaker * @param tokenMaker The new token maker to use.
* The new token maker to use.
* @see #setSyntaxStyle(String) * @see #setSyntaxStyle(String)
*/ */
public void setSyntaxStyle(TokenMaker tokenMaker) { public void setSyntaxStyle(TokenMaker tokenMaker) {
@@ -546,35 +499,28 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
updateSyntaxHighlightingInformation(); updateSyntaxHighlightingInformation();
this.syntaxStyle = "text/unknown"; // TODO: Make me public? this.syntaxStyle = "text/unknown"; // TODO: Make me public?
} }
/** /**
* Sets the token maker factory used by this document. * Sets the token maker factory used by this document.
* *
* @param tmf * @param tmf The <code>TokenMakerFactory</code> for this document. If this is
* The <code>TokenMakerFactory</code> for this document. If this
* is
* <code>null</code>, a default factory is used. * <code>null</code>, a default factory is used.
*/ */
public void setTokenMakerFactory(TokenMakerFactory tmf) { public void setTokenMakerFactory(TokenMakerFactory tmf) {
tokenMakerFactory = tmf != null ? tmf : TokenMakerFactory.getDefaultInstance(); tokenMakerFactory = tmf != null ? tmf : TokenMakerFactory.getDefaultInstance();
} }
/** /**
* Loops through the last-tokens-on-lines array from a specified point * Loops through the last-tokens-on-lines array from a specified point onward,
* onward, * updating last-token values until they stop changing. This should be called
* updating last-token values until they stop changing. This should be * when lines are updated/inserted/removed, as doing so may cause lines below to
* called
* when lines are updated/inserted/removed, as doing so may cause lines
* below to
* change color. * change color.
* *
* @param line * @param line The first line to check for a change in last-token
* The first line to check for a change in last-token value. * value.
* @param numLines * @param numLines The number of lines in the document.
* The number of lines in the document. * @param previousTokenType The last-token value of the line just before
* @param previousTokenType * <code>line</code>.
* The last-token value of the line just before
* <code>line</code>.
* @return The last line that needs repainting. * @return The last line that needs repainting.
*/ */
private int updateLastTokensBelow(int line, int numLines, int previousTokenType) { private int updateLastTokensBelow(int line, int numLines, int previousTokenType) {
@@ -627,7 +573,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
} }
return line; return line;
} }
/** /**
* Updates internal state information; e.g. the "last tokens on lines" data. * 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 * After this, a changed update is fired to let listeners know that the
File diff suppressed because it is too large. Load diff
Loaded 100 of 279 files, more files were not shown because too many files have changed in this diff. Show more