26 Commits
Author SHA1 Message Date
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
275 changed files with 9521 additions and 105700 deletions

No files matched your search

+1 -5
View File
@@ -2,8 +2,6 @@
A simple, text based Java Bytecode Editor.
It uses ObjectWeb ASM (http://asm.ow2.org/) and the RSyntaxTextArea from https://github.com/bobbylight/RSyntaxTextArea
Get the precompiled version from here: https://github.com/SuspiciousActivity/Random-Java-Stuff/blob/master/ByteEdit.jar
To open up a file, drag it onto the left side. To save a class, press CTRL + S on the right side, to save the jar file, press on the left side. With F5 you can reload either the file or only the class.
To search/replace in a file, press CTRL + F.
@@ -12,12 +10,10 @@ To open a class, press CTRL + T.
To change settings like auto-computation of stack frames and max locals/stack press CTRL + O.
To go to the field/method definition in the selected line press CTRL + G.
To (un-)escape unicode characters, press CTRL + U.
To rename a field/method with attention to references in the selected line, press CTRL + R.
To quickly compile some java code, press CTRL + E.
To switch between light and dark theme, press CTRL + B.
To switch between themes, press CTRL + B.
+13 -4
View File
@@ -32,7 +32,8 @@ import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
import me.ByteEdit.edit.Disassembler;
import me.ByteEdit.decompiler.EnumDecompiler;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
import me.ByteEdit.main.ThemeManager;
@@ -105,6 +106,8 @@ public class CompilationBox extends JFrame {
null, Arrays.asList(new JavaSourceFromString("Compiled", source))).call();
String res = out.toString();
if (res.isEmpty()) {
SingleThreadedExecutor.submit(() -> {
try {
File clazz = new File(tmpFolder, "Compiled.class");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream is = new FileInputStream(clazz);
@@ -114,10 +117,12 @@ public class CompilationBox extends JFrame {
baos.write(tmp, 0, r);
}
is.close();
ClassReader read = new ClassReader(baos.toByteArray());
ClassNode node = new ClassNode();
read.accept(node, 0);
String dis = Disassembler.disassemble(node);
EnumDecompiler decompiler = EnumDecompiler.BYTEEDIT;
String dis = decompiler.getDecompiler().decompile(node);
File[] files = tmpFolder.listFiles();
if (files != null)
for (File f : files) {
@@ -132,10 +137,14 @@ public class CompilationBox extends JFrame {
read = new ClassReader(baos.toByteArray());
node = new ClassNode();
read.accept(node, 0);
dis += "\n" + Disassembler.disassemble(node);
dis += "\n" + decompiler.getDecompiler().decompile(node);
}
compSuccess.textArea.setText(dis);
compSuccess.setVisible(true);
} catch (IOException e2) {
e2.printStackTrace();
}
});
} else {
JOptionPane.showMessageDialog(CompilationBox.this, res, "Error!", JOptionPane.ERROR_MESSAGE);
}
@@ -150,7 +159,7 @@ public class CompilationBox extends JFrame {
}, ctrlS, JComponent.WHEN_FOCUSED);
}
class JavaSourceFromString extends SimpleJavaFileObject {
private static class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
public JavaSourceFromString(String name, String code) {
+21 -40
View File
@@ -1,8 +1,6 @@
package me.ByteEdit.boxes;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
@@ -18,6 +16,7 @@ import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
@@ -59,13 +58,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblFind);
txtString = new JTextField();
KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
txtString.registerKeyboardAction(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
find();
}
}, enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtString.registerKeyboardAction(e -> find(), enter, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtString.setBounds(91, 22, 149, 20);
contentPane.add(txtString);
txtString.setColumns(10);
@@ -73,12 +66,7 @@ public class GlobalSearchBox extends JFrame {
chckbxCaseSensitive.setBounds(10, 199, 114, 23);
contentPane.add(chckbxCaseSensitive);
JButton btnFind = new JButton("Find");
btnFind.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
btnFind.addActionListener(e -> find());
btnFind.setBounds(130, 199, 110, 23);
contentPane.add(btnFind);
@@ -91,12 +79,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblOwner);
txtOwner = new JTextField();
txtOwner.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
txtOwner.addActionListener(e -> find());
txtOwner.setColumns(10);
txtOwner.setBounds(91, 78, 149, 20);
contentPane.add(txtOwner);
@@ -106,12 +89,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblName);
txtName = new JTextField();
txtName.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
txtName.addActionListener(e -> find());
txtName.setColumns(10);
txtName.setBounds(91, 106, 149, 20);
contentPane.add(txtName);
@@ -121,12 +99,7 @@ public class GlobalSearchBox extends JFrame {
contentPane.add(lblDesc);
txtDesc = new JTextField();
txtDesc.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
find();
}
});
txtDesc.addActionListener(e -> find());
txtDesc.setColumns(10);
txtDesc.setBounds(91, 134, 149, 20);
contentPane.add(txtDesc);
@@ -137,10 +110,11 @@ public class GlobalSearchBox extends JFrame {
openBox.model.clear();
Set<Info> set = new HashSet<>();
if (!txtString.getText().isEmpty()) {
String str = UnicodeUtils.unescape(txtString.getText());
String str = UnicodeUtils.unescape(null, txtString.getText(), true);
if (ignoreCase) {
str = str.toLowerCase();
}
String fstr = str;
for (ClassNode cn : Main.classNodes.values()) {
for (MethodNode mn : cn.methods) {
for (AbstractInsnNode insn : mn.instructions.toArray()) {
@@ -154,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 {
String owner = UnicodeUtils.unescape(txtOwner.getText());
String owner = UnicodeUtils.unescape(null, txtOwner.getText(), true);
boolean wildCardOwner = owner.isEmpty();
String name = UnicodeUtils.unescape(txtName.getText());
String name = UnicodeUtils.unescape(null, txtName.getText(), true);
boolean wildCardName = name.isEmpty();
String desc = UnicodeUtils.unescape(txtDesc.getText());
String desc = UnicodeUtils.unescape(null, txtDesc.getText(), true);
boolean wildCardDesc = desc.isEmpty();
for (ClassNode cn : Main.classNodes.values()) {
for (MethodNode mn : cn.methods) {
@@ -183,9 +166,7 @@ public class GlobalSearchBox extends JFrame {
}
}
}
for (Info s : set) {
openBox.model.addElement(s);
}
set.forEach(openBox.model::addElement);
openBox.setVisible(true);
}
}
+4 -10
View File
@@ -11,10 +11,10 @@ import javax.swing.ListSelectionModel;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.BadLocationException;
import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
public class GlobalSearchOpenBox extends JFrame {
@@ -36,19 +36,13 @@ public class GlobalSearchOpenBox extends JFrame {
model = new DefaultListModel<>();
JList<Info> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
SingleThreadedExecutor.submit(() -> {
Info val = list.getSelectedValue();
if (val != null && !val.owner.equals(Main.currentNodeName)) {
int lineFound = Main.selectFileWithSearch(val.nodePath, val.mn);
if (lineFound != -1) {
try {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
Main.selectFile(val.nodePath);
}
});
}
});
+8 -21
View File
@@ -20,7 +20,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.edit.Disassembler;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.UnicodeUtils;
@@ -84,8 +84,8 @@ public class RenameBox extends JFrame {
JOptionPane.showMessageDialog(null, "ClassNode not found!", "Error!", JOptionPane.ERROR_MESSAGE);
return;
}
String newName = UnicodeUtils.unescape(txtName.getText());
String newDesc = UnicodeUtils.unescape(txtDesc.getText());
String newName = UnicodeUtils.unescape(null, txtName.getText(), true);
String newDesc = UnicodeUtils.unescape(null, txtDesc.getText(), true);
if (ClassUtil.isObjectClassMethod(newName, newDesc, true)) {
JOptionPane.showMessageDialog(null, "Invalid name!", "Error!", JOptionPane.ERROR_MESSAGE);
return;
@@ -210,27 +210,14 @@ public class RenameBox extends JFrame {
return;
}
}
try { // refresh
ClassNode classNode = Main.classNodes.get(Main.currentNodeName);
int prev = Main.txtByteEditView.getCaretPosition();
String dis = Disassembler.disassemble(classNode);
String substr = Main.currentNodeName.substring(0, Main.currentNodeName.length() - 6);
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
if (split[0].equals(substr)) {
dis += "\n" + Disassembler.disassemble(Main.classNodes.get(key));
}
}
}
Main.txtByteEditView.setText(dis);
Main.txtByteEditView.setCaretPosition(prev);
} catch (Exception e2) {
e2.printStackTrace();
}
SingleThreadedExecutor.submit(() -> {
// refresh
Main.decompileCurrentNode();
name = newName;
desc = newDesc;
RenameBox.this.setVisible(false);
});
}
});
btnRename.setBounds(345, 237, 89, 23);
+4 -1
View File
@@ -123,13 +123,16 @@ public class SearchBox extends JFrame {
}
public void replace() {
String txt = Main.txtByteEditView.getText();
if (!Main.decompiler.isEditable())
return;
int startPos = Main.txtByteEditView.getSelectionStart();
Main.txtByteEditView.replaceSelection(txtReplace.getText());
Main.txtByteEditView.select(startPos, startPos + txtReplace.getText().length());
}
public void replaceAll() {
if (!Main.decompiler.isEditable())
return;
String toFind = txtFind.getText();
String toReplace = txtReplace.getText();
String txt = Main.txtByteEditView.getText();
+4 -3
View File
@@ -17,6 +17,7 @@ import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.main.Main;
import me.ByteEdit.utils.UnicodeUtils;
@@ -39,11 +40,11 @@ public class TypeOpenBox extends JFrame {
DefaultListModel<String> model = new DefaultListModel<>();
JList<String> list = new JList(model);
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
String val = list.getSelectedValue();
if (val != null && !val.equals(Main.currentNodeName)) {
Main.selectFile(val);
SingleThreadedExecutor.submit(() -> Main.selectFile(val));
}
}
});
@@ -76,7 +77,7 @@ public class TypeOpenBox extends JFrame {
if (txtSearch.getText().isEmpty()) {
return;
}
String search = UnicodeUtils.unescape(txtSearch.getText()).toLowerCase();
String search = UnicodeUtils.unescape(null, txtSearch.getText(), true).toLowerCase();
if (search.contains("/")) {
for (String s : Main.classNodes.keySet()) {
String className = s.substring(0, s.length() - 6);
+2 -2
View File
@@ -36,7 +36,7 @@ public class UnicodeBox extends JFrame {
public void actionPerformed(ActionEvent e) {
try {
textArea.setText(UnicodeUtils.escape(textArea.getText()));
textArea.setText(UnicodeUtils.escape(null, textArea.getText(), true));
} catch (IllegalArgumentException ex) {
Main.showError(ex.getMessage());
}
@@ -50,7 +50,7 @@ public class UnicodeBox extends JFrame {
public void actionPerformed(ActionEvent e) {
try {
textArea.setText(UnicodeUtils.unescape(textArea.getText()));
textArea.setText(UnicodeUtils.unescape(null, textArea.getText(), true));
} catch (IllegalArgumentException ex) {
Main.showError(ex.getMessage());
}
@@ -0,0 +1,117 @@
package me.ByteEdit.decompiler;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Collection;
import java.util.HashMap;
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.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");
}
@Override
public String decompile(ClassNode cn) {
return doDecompilation(cn, getBytes(cn));
}
private static String doDecompilation(ClassNode cn, byte[] b) {
try {
HashMap<String, String> ops = options;
ClassFileSource cfs = new ClassFileSource() {
@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);
}
return null; // cfr loads unnecessary classes
}
@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,45 @@
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()),
FERNFLOWER("FernFlower", new FernflowerDecompiler()), 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,143 @@
package me.ByteEdit.decompiler;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.jar.Manifest;
import org.jetbrains.java.decompiler.main.Fernflower;
import org.jetbrains.java.decompiler.main.extern.IBytecodeProvider;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
import org.jetbrains.java.decompiler.main.extern.IResultSaver;
import org.jetbrains.java.decompiler.struct.ContextUnit;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructContext;
import org.jetbrains.java.decompiler.struct.lazy.LazyLoader;
import org.objectweb.asm.tree.ClassNode;
public class FernflowerDecompiler implements IBytecodeProvider, IResultSaver, IDecompiler {
private byte[] bytes;
private String returned;
public static final HashMap<String, Boolean> options = new HashMap<>();
static {
options.put("rbr", true);
options.put("rsy", false);
options.put("din", true);
options.put("dc4", true);
options.put("das", true);
options.put("hes", true);
options.put("hdc", true);
options.put("dgs", false);
options.put("ner", true);
options.put("den", true);
options.put("rgn", true);
options.put("lit", false);
options.put("asc", true);
options.put("bto", true);
options.put("nns", false);
options.put("uto", true);
options.put("udv", true);
options.put("rer", true);
options.put("fdi", true);
options.put("ren", false);
options.put("inn", true);
options.put("lac", false);
}
@Override
public String decompile(ClassNode cn) {
return doDecompilation(cn, getBytes(cn));
}
public String doDecompilation(ClassNode cn, byte[] b) {
try {
// TODO decompile method only
this.bytes = b;
HashMap<String, Object> map = new HashMap<>();
for (Entry<String, Boolean> entry : options.entrySet()) {
map.put(entry.getKey(), entry.getValue() ? "1" : "0");
}
map.put("ind", " ");
Fernflower f = new Fernflower(this, this, map, new IFernflowerLogger() {
@Override
public void writeMessage(String message, Throwable t) {
}
@Override
public void writeMessage(String message, Severity severity) {
}
@Override
public boolean accepts(Severity severity) {
return false;
}
});
StructContext sc = f.getStructContext();
StructClass cl = new StructClass(b, true, sc.getLoader());
sc.getClasses().put(cn.name, cl);
// instead of loading a file use custom bridge, created a few getters
String fakePath = new File("none.class").getAbsolutePath();
ContextUnit unit = new ContextUnit(ContextUnit.TYPE_FOLDER, null, fakePath, true, sc.getSaver(),
sc.getDecompiledData());
sc.getUnits().put(fakePath, unit);
unit.addClass(cl, "none.class");
sc.getLoader().addClassLink(cn.name, new LazyLoader.Link(LazyLoader.Link.CLASS, fakePath, null));
f.decompileContext();
return returned;
} catch (Exception e) {
e.printStackTrace();
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
return sw.toString();
}
}
@Override
public byte[] getBytecode(String externalPath, String internalPath) throws IOException {
return bytes;
}
// we can ignore most of those methods because we do not want to save the output
// as a file
@Override
public void saveFolder(String path) {
}
@Override
public void copyFile(String source, String path, String entryName) {
}
@Override
public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) {
this.returned = content;
}
@Override
public void createArchive(String path, String archiveName, Manifest manifest) {
}
@Override
public void saveDirEntry(String path, String archiveName, String entryName) {
}
@Override
public void copyEntry(String source, String path, String archiveName, String entry) {
}
@Override
public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName,
String content) {
}
@Override
public void closeArchive(String path, String archiveName) {
}
}
@@ -0,0 +1,16 @@
package me.ByteEdit.decompiler;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
public interface IDecompiler {
String decompile(ClassNode cn);
default byte[] getBytes(ClassNode cn) {
ClassWriter classWriter = new ClassWriter(0);
cn.accept(classWriter);
return classWriter.toByteArray();
}
}
@@ -0,0 +1,130 @@
package me.ByteEdit.decompiler;
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.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import me.ByteEdit.main.Main;
public class JDDecompiler implements Loader, Printer, IDecompiler {
@Override
public String decompile(ClassNode cn) {
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) {
return Main.classNodes.containsKey(internalName + ".class");
}
@Override
public byte[] load(String internalName) throws LoaderException {
ClassNode cl = Main.classNodes.get(internalName + ".class");
ClassWriter cw = new ClassWriter(0);
cl.accept(cw);
return cw.toByteArray();
}
protected static final String TAB = " ";
protected static final String NEWLINE = "\n";
protected int indentationCount = 0;
protected StringBuilder sb = new StringBuilder();
@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,74 @@
package me.ByteEdit.decompiler;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
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) {
return doDecompilation(cn, getBytes(cn));
}
private static String doDecompilation(ClassNode cn, byte[] b) {
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;
}
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();
}
}
}
+193 -195
View File
@@ -1,5 +1,6 @@
package me.ByteEdit.edit;
import java.io.BufferedReader;
import java.io.StringReader;
import java.math.BigInteger;
import java.util.ArrayList;
@@ -9,6 +10,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.regex.Pattern;
import javax.swing.text.BadLocationException;
@@ -51,30 +53,48 @@ import me.ByteEdit.utils.UnicodeUtils;
public class Assembler {
public static final Pattern SPACE = Pattern.compile(" ");
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
public static ClassNode assemble(String input) {
Main.txtByteEditView.removeAllLineHighlights();
CustomBufferedReader read = null;
try {
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));
String s;
while (!(s = read.readLine()).startsWith("// #Class v:")) {
if (clazz.visibleAnnotations == null) {
clazz.visibleAnnotations = new ArrayList<>();
}
clazz.visibleAnnotations.add(parseAnnotation(s));
clazz.visibleAnnotations.add(parseAnnotation(hsr, s));
}
clazz.version = Integer.parseInt(s.substring(12));
while ((s = read.readLine()).startsWith("// ")) {
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: ")) {
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: ")) {
String[] split = s.split(" ");
if (split.length > 3) {
clazz.outerMethod = UnicodeUtils.unescape(split[2]);
clazz.outerMethodDesc = UnicodeUtils.unescape(split[3]);
clazz.outerMethod = UnicodeUtils.unescape(hsr, split[2]);
clazz.outerMethodDesc = UnicodeUtils.unescape(hsr, split[3]);
}
} else if (s.equals("// #InnerClasses:")) {
clazz.innerClasses = new ArrayList<>();
@@ -137,9 +157,9 @@ public class Assembler {
access ^= ClassUtil.ACC_MANDATED;
}
}
clazz.innerClasses.add(new InnerClassNode(UnicodeUtils.unescape(split[1]),
split[2].equals("null") ? null : UnicodeUtils.unescape(split[2]),
split[3].equals("null") ? null : UnicodeUtils.unescape(split[3]), access));
clazz.innerClasses.add(new InnerClassNode(UnicodeUtils.unescape(hsr, split[1]),
split[2].equals("null") ? null : UnicodeUtils.unescape(hsr, split[2]),
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);
clazz.interfaces = new ArrayList<>();
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 "));
}
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 "));
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;
switch (split[split.length - 2]) {
case "class":
@@ -192,7 +212,7 @@ public class Assembler {
}
}
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")) {
}
clazz.fields = new ArrayList<>();
@@ -207,7 +227,7 @@ public class Assembler {
annotationsForNext.add(s);
} else {
if (s.startsWith("// #Signature: ")) {
signature = UnicodeUtils.unescape(s.substring(15));
signature = UnicodeUtils.unescape(hsr, s.substring(15));
} else {
String[] split = s.split(" ");
boolean hasValue = false;
@@ -218,9 +238,9 @@ public class Assembler {
if (hasValue) {
split = s.substring(0, s.indexOf(" = ")).split(" ");
}
String name = UnicodeUtils.unescape(split[split.length - 1]);
String desc = UnicodeUtils.unescape(split[split.length - 2]);
value = getValue((String) value, desc);
String name = UnicodeUtils.unescape(hsr, split[split.length - 1]);
String desc = UnicodeUtils.unescape(hsr, split[split.length - 2]);
value = getValue(hsr, (String) value, desc);
int access = 0;
if (split.length > 2) {
String cons = consolidateStrings(split, 0, split.length - 2);
@@ -283,7 +303,7 @@ public class Assembler {
node.visibleAnnotations = new ArrayList<>();
}
for (String anno : annotationsForNext) {
node.visibleAnnotations.add(parseAnnotation(anno));
node.visibleAnnotations.add(parseAnnotation(hsr, anno));
}
annotationsForNext.clear();
}
@@ -319,7 +339,9 @@ public class Assembler {
tryCatchBlocksToParse.clear();
stage = 0;
} 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:")) {
stage = 1;
} else if (s.equals("// #LocalVars:")) {
@@ -332,12 +354,14 @@ public class Assembler {
localVarsToParse.add(s);
}
} else if (s.equals("}")) {
if (node == null)
throw new IllegalStateException("node is null");
if (!annotationsForNext.isEmpty()) {
if (node.visibleAnnotations == null) {
node.visibleAnnotations = new ArrayList<>();
}
for (String anno : annotationsForNext) {
node.visibleAnnotations.add(parseAnnotation(anno));
node.visibleAnnotations.add(parseAnnotation(hsr, anno));
}
annotationsForNext.clear();
}
@@ -345,7 +369,7 @@ public class Assembler {
String[] sp = st.split(":");
int start = Integer.parseInt(sp[3].substring(0, sp[3].length() - 2));
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 _end = null;
for (Entry<LabelNode, Integer> entry : methodLabelMap.entrySet()) {
@@ -356,9 +380,9 @@ public class Assembler {
_end = entry.getKey();
}
}
node.localVariables.add(new LocalVariableNode(UnicodeUtils.unescape(sp[0]),
UnicodeUtils.unescape(sp[1].substring(1, sp[1].length() - 2)), signat, _start, _end,
Integer.parseInt(sp[2].substring(0, sp[2].length() - 2))));
node.localVariables.add(new LocalVariableNode(UnicodeUtils.unescape(hsr, sp[0]),
UnicodeUtils.unescape(hsr, sp[1].substring(1, sp[1].length() - 2)), signat, _start,
_end, Integer.parseInt(sp[2].substring(0, sp[2].length() - 2))));
}
for (String st : tryCatchBlocksToParse) {
String[] sp = st.split(" ");
@@ -380,21 +404,23 @@ public class Assembler {
}
}
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);
} else if (!s.startsWith("\t")) {
if (node == null)
throw new IllegalStateException("node is null");
s = s.substring(0, s.length() - 2);
if (s.contains(" throws ")) {
String exceptions = s.substring(s.lastIndexOf(" throws ") + 8);
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 "));
}
String[] split = s.split(" ");
node.desc = UnicodeUtils.unescape(split[split.length - 1]);
node.name = UnicodeUtils.unescape(split[split.length - 2]);
node.desc = UnicodeUtils.unescape(hsr, split[split.length - 1]);
node.name = UnicodeUtils.unescape(hsr, split[split.length - 2]);
int access = 0;
if (split.length > 2) {
String cons = consolidateStrings(split, 0, split.length - 2);
@@ -457,8 +483,10 @@ public class Assembler {
s = s.substring(1);
if (!temp.isEmpty()) {
if (s.equals("]")) {
if (node == null)
throw new IllegalStateException("node is null");
temp += s;
node.instructions.add(getNode(temp, methodLabelMap));
node.instructions.add(getNode(hsr, temp, methodLabelMap));
temp = "";
} else {
temp += s + "\n";
@@ -467,7 +495,9 @@ public class Assembler {
if (s.endsWith("[")) {
temp += s + "\n";
} else {
node.instructions.add(getNode(s, methodLabelMap));
if (node == null)
throw new IllegalStateException("node is null");
node.instructions.add(getNode(hsr, s, methodLabelMap));
}
}
}
@@ -475,17 +505,20 @@ public class Assembler {
}
return clazz;
} catch (Throwable e) {
if (read != null) {
System.err.println("Error at line: " + read.currentLine);
try {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(read.currentLine));
} catch (BadLocationException e1) {
System.err.println("Can't show line!");
}
}
Main.showError(e.getMessage());
e.printStackTrace();
return null;
} finally {
try {
if (read != null)
read.close();
} catch (Exception e) {
}
@@ -496,31 +529,37 @@ public class Assembler {
ArrayList<String> ret = new ArrayList<>();
String[] split = s.split(", ");
String building = null;
boolean inString = false;
for (int i = 0; i < split.length; i++) {
if (split[i].startsWith("(")) {
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)
ret.add(split[i]);
else
ret.add(building + ", " + split[i]);
building = null;
} else {
} else if (inString || split[i].startsWith("\"")) {
inString = true;
if (building == null)
building = split[i];
else
building += ", " + split[i];
} else {
ret.add(split[i]);
}
}
return ret.toArray(new String[0]);
}
private static AnnotationNode parseAnnotation(String s) {
private static AnnotationNode parseAnnotation(HugeStringsRev hsr, String s) {
s = s.substring(1);
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 (node.values == null) {
node.values = new ArrayList<>();
@@ -538,7 +577,7 @@ public class Assembler {
String split3[] = splitWithStrings(value.substring(2, value.length() - 2));
List<Object> list = new ArrayList<>();
for (String rofl : split3) {
rofl = UnicodeUtils.unescape(rofl);
rofl = UnicodeUtils.unescape(hsr, rofl);
if (rofl.startsWith("\"")) {
int index = rofl.lastIndexOf("\"");
list.add(rofl.substring(1, index));
@@ -552,7 +591,7 @@ public class Assembler {
}
node.values.add(list);
} else {
value = UnicodeUtils.unescape(value);
value = UnicodeUtils.unescape(hsr, value);
if (value.startsWith("\"")) {
int index = value.lastIndexOf("\"");
node.values.add(value.substring(1, index));
@@ -574,29 +613,31 @@ public class Assembler {
if (args.length == 0) {
return null;
}
String ret = args[start];
StringBuilder sb = new StringBuilder(args[start]);
if (args.length > start + 1) {
for (int i = start + 1; i < args.length; i++) {
ret = ret + " " + args[i];
sb.append(' ');
sb.append(args[i]);
}
}
return ret;
return sb.toString();
}
private static String consolidateStrings(String[] args, int start, int end) {
if (end == 0) {
return null;
}
String ret = args[start];
StringBuilder sb = new StringBuilder(args[start]);
if (end > start + 1) {
for (int i = start + 1; i < end; i++) {
ret = ret + " " + args[i];
sb.append(' ');
sb.append(args[i]);
}
}
return ret;
return sb.toString();
}
private static Object getValue(String s, String to) {
private static Object getValue(HugeStringsRev hsr, String s, String to) {
if (s == null) {
return null;
}
@@ -618,14 +659,15 @@ public class Assembler {
case "S":
return Short.parseShort(s);
case "Ljava/lang/String;":
return UnicodeUtils.unescape(s.substring(1, s.length() - 1));
return UnicodeUtils.unescape(hsr, s.substring(1, s.length() - 1));
default: {
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 ")) {
int labelNr = Integer.parseInt(s.substring(9));
for (Map.Entry<LabelNode, Integer> entry : labels.entrySet()) {
@@ -648,124 +690,41 @@ public class Assembler {
labels.put(ln, labelNr);
return new LineNumberNode(Integer.parseInt(s.split(" ")[0]), ln);
} else if (s.startsWith("// frame ")) {
String str1 = s.substring(7);
String str = str1.substring(str1.indexOf(' ', 3) + 1);
Object[] local;
Object[] stack;
ArrayList<Object> arr = new ArrayList<>();
if (str.startsWith("l:null")) {
local = new Object[0];
} else {
String l = str.substring(3, str.indexOf("s:") - 2);
if (l.isEmpty()) {
local = new Object[0];
} else {
for (String asd : l.split(", ")) {
if (!asd.startsWith("(")) {
int frameType = ClassUtil.getFrameTypeByName(asd);
if (frameType != -1)
arr.add(new Integer(frameType));
else {
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++;
}
}
}
String str1 = s.substring(9);
String[] split = str1.split(" ", 2);
int type = 0;
switch (str1.substring(str1.indexOf(' ', 1) + 1, str1.indexOf(' ', 3))) {
case "NEW":
type = Opcodes.F_NEW;
break;
case "FULL":
type = Opcodes.F_FULL;
break;
case "APPEND":
type = Opcodes.F_APPEND;
break;
case "CHOP":
type = Opcodes.F_CHOP;
break;
Object[] local = null;
Object[] stack = null;
int localLen = 0;
int stackLen = 0;
switch (split[0].toUpperCase()) {
case "SAME":
type = Opcodes.F_SAME;
break;
case "SAME1":
type = Opcodes.F_SAME1;
stack = parseFrameList(hsr, split[1], labels);
stackLen = stack.length;
break;
case "CHOP":
type = Opcodes.F_CHOP;
localLen = Integer.parseInt(split[1]);
break;
case "APPEND":
type = Opcodes.F_APPEND;
local = parseFrameList(hsr, split[1], labels);
localLen = local.length;
break;
case "FULL":
type = Opcodes.F_FULL;
split = split[1].split("\\] \\[", 2);
local = parseFrameList(hsr, split[0] + "]", labels);
localLen = local.length;
stack = parseFrameList(hsr, "[" + split[1], labels);
stackLen = stack.length;
break;
}
return new FrameNode(type, local.length, local, stack.length, stack);
return new FrameNode(type, localLen, local, stackLen, stack);
} else {
String[] split = s.split(" ");
String command = split[0];
@@ -803,10 +762,10 @@ public class Assembler {
case "dconst_1":
return new InsnNode(15);
case "bipush": {
return new IntInsnNode(16, Integer.parseInt(split[1]));
return new IntInsnNode(16, (byte) Integer.parseInt(split[1]));
}
case "sipush": {
return new IntInsnNode(17, Integer.parseInt(split[1]));
return new IntInsnNode(17, (short) Integer.parseInt(split[1]));
}
case "ldc": {
String str = consolidateStrings(split, 1);
@@ -818,10 +777,12 @@ public class Assembler {
String type = split2[0].substring(7);
// Reflection
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)));
} 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")) {
val = Long.parseLong(str.substring(0, str.length() - 1));
} else if (str.endsWith("f")) {
@@ -1437,50 +1398,54 @@ public class Assembler {
case "getstatic": {
String target = split[2];
int index = target.lastIndexOf("/");
return new FieldInsnNode(178, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]));
return new FieldInsnNode(178, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
}
case "putstatic": {
String target = split[2];
int index = target.lastIndexOf("/");
return new FieldInsnNode(179, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]));
return new FieldInsnNode(179, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
}
case "getfield": {
String target = split[2];
int index = target.lastIndexOf("/");
return new FieldInsnNode(180, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]));
return new FieldInsnNode(180, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
}
case "putfield": {
String target = split[2];
int index = target.lastIndexOf("/");
return new FieldInsnNode(181, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]));
return new FieldInsnNode(181, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]));
}
case "invokevirtual": {
String target = split[2];
int index = target.lastIndexOf("/");
return new MethodInsnNode(182, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false);
return new MethodInsnNode(182, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
false);
}
case "invokespecial": {
String target = split[2];
int index = target.lastIndexOf("/");
return new MethodInsnNode(183, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false);
return new MethodInsnNode(183, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
false);
}
case "invokestatic": {
String target = split[2];
int index = target.lastIndexOf("/");
return new MethodInsnNode(184, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), false);
return new MethodInsnNode(184, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
false);
}
case "invokeinterface": {
String target = split[2];
int index = target.lastIndexOf("/");
return new MethodInsnNode(185, UnicodeUtils.unescape(target.substring(0, index)),
UnicodeUtils.unescape(target.substring(index + 1)), UnicodeUtils.unescape(split[1]), true);
return new MethodInsnNode(185, UnicodeUtils.unescape(hsr, target.substring(0, index)),
UnicodeUtils.unescape(hsr, target.substring(index + 1)), UnicodeUtils.unescape(hsr, split[1]),
true);
}
case "invokedynamic": {
String str = s.substring(16, s.length() - 2).replace("", "");
@@ -1495,7 +1460,7 @@ public class Assembler {
try {
// Reflection
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(2).substring(5))));
} catch (Exception e) {
@@ -1503,9 +1468,9 @@ public class Assembler {
}
} else if (stage == 2) {
args.add(new Handle(OpcodesReverse.getHandleOpcode(vals.get(4).substring(5)),
UnicodeUtils.unescape(vals.get(1).substring(7)),
UnicodeUtils.unescape(vals.get(0).substring(6)),
UnicodeUtils.unescape(vals.get(2).substring(6)),
UnicodeUtils.unescape(hsr, vals.get(1).substring(7)),
UnicodeUtils.unescape(hsr, vals.get(0).substring(6)),
UnicodeUtils.unescape(hsr, vals.get(2).substring(6)),
Boolean.parseBoolean(vals.get(3).substring(13))));
vals.clear();
}
@@ -1523,7 +1488,7 @@ public class Assembler {
vals.add(st);
} else if (stage == 0) {
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")) {
args.add(Long.parseLong(st.substring(0, st.length() - 1)));
} else if (st.endsWith("f")) {
@@ -1535,27 +1500,23 @@ public class Assembler {
}
}
}
Object[] _args = new Object[args.size()];
stage = 0;
for (Object o : args) {
_args[stage] = o;
stage++;
}
return new InvokeDynamicInsnNode(UnicodeUtils.unescape(sp[0].substring(7)),
UnicodeUtils.unescape(sp[1].substring(6)),
return new InvokeDynamicInsnNode(UnicodeUtils.unescape(hsr, sp[0].substring(7)),
UnicodeUtils.unescape(hsr, sp[1].substring(6)),
new Handle(OpcodesReverse.getHandleOpcode(sp[7].substring(6)),
UnicodeUtils.unescape(sp[4].substring(8)), UnicodeUtils.unescape(sp[3].substring(7)),
UnicodeUtils.unescape(sp[5].substring(7)), Boolean.parseBoolean(sp[6].substring(14))),
_args);
UnicodeUtils.unescape(hsr, sp[4].substring(8)),
UnicodeUtils.unescape(hsr, sp[3].substring(7)),
UnicodeUtils.unescape(hsr, sp[5].substring(7)),
Boolean.parseBoolean(sp[6].substring(14))),
args.toArray(new Object[args.size()]));
}
case "new": {
return new TypeInsnNode(187, UnicodeUtils.unescape(split[1]));
return new TypeInsnNode(187, UnicodeUtils.unescape(hsr, split[1]));
}
case "newarray": {
return new IntInsnNode(188, ClassUtil.getArrayIDByType(split[1]));
}
case "anewarray": {
return new TypeInsnNode(189, UnicodeUtils.unescape(split[1]));
return new TypeInsnNode(189, UnicodeUtils.unescape(hsr, split[1]));
}
case "arraylength": {
return new InsnNode(190);
@@ -1564,10 +1525,10 @@ public class Assembler {
return new InsnNode(191);
}
case "checkcast": {
return new TypeInsnNode(192, UnicodeUtils.unescape(split[1]));
return new TypeInsnNode(192, UnicodeUtils.unescape(hsr, split[1]));
}
case "instanceof": {
return new TypeInsnNode(193, UnicodeUtils.unescape(split[1]));
return new TypeInsnNode(193, UnicodeUtils.unescape(hsr, split[1]));
}
case "monitorenter": {
return new InsnNode(194);
@@ -1576,7 +1537,7 @@ public class Assembler {
return new InsnNode(195);
}
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": {
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()]);
}
}
+252 -380
View File
@@ -1,5 +1,22 @@
package me.ByteEdit.edit;
import static org.objectweb.asm.tree.AbstractInsnNode.FIELD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.FRAME;
import static org.objectweb.asm.tree.AbstractInsnNode.IINC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INT_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.INVOKE_DYNAMIC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.JUMP_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LABEL;
import static org.objectweb.asm.tree.AbstractInsnNode.LDC_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.LINE;
import static org.objectweb.asm.tree.AbstractInsnNode.LOOKUPSWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.METHOD_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.MULTIANEWARRAY_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TABLESWITCH_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.TYPE_INSN;
import static org.objectweb.asm.tree.AbstractInsnNode.VAR_INSN;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -9,7 +26,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.Label;
@@ -23,7 +40,7 @@ import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
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.InvokeDynamicInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
@@ -40,111 +57,95 @@ import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import me.ByteEdit.decompiler.IDecompiler;
import me.ByteEdit.main.Main;
import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.OpcodesReverse;
import me.ByteEdit.utils.UnicodeUtils;
public class Disassembler {
public class Disassembler implements IDecompiler {
private static final ExecutorService exec = Executors
.newFixedThreadPool((Runtime.getRuntime().availableProcessors() + 1) / 2);
public static String disassemble(ClassNode classNode) {
return disassemble(classNode, null).getDisassembly();
@Override
public String decompile(ClassNode cn) {
return disassemble(cn);
}
public static class DisassembleTuple {
private final String dis;
private final int line;
public DisassembleTuple(String dis) {
this.dis = dis;
this.line = -1;
}
public DisassembleTuple(String dis, int line) {
this.dis = dis;
this.line = line;
}
public String getDisassembly() {
return dis;
}
public int getLine() {
return line;
}
}
public static DisassembleTuple disassemble(ClassNode classNode, Object nodeToFind) {
private static String disassemble(ClassNode classNode) {
if (classNode == null) {
return new DisassembleTuple("ClassNode is null! This is not a valid java class file!");
return "ClassNode is null! This is not a valid java class file!";
}
try {
AtomicInteger lineFound = new AtomicInteger(-1);
StringContext ctx = new StringContext(16);
HugeStrings hs = new HugeStrings();
StringContext ctx = new StringContext(18);
ctx.next("// #Annotations:\n");
ctx.next(doAnnotations(classNode.visibleAnnotations));
ctx.next(doAnnotations(classNode.visibleAnnotations, hs, ""));
ctx.next("// #Class v:" + classNode.version + "\n");
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)
ctx.next("// #OuterMethod: " + (classNode.outerMethod == null ? "null"
: (UnicodeUtils.escapeWithSpaces(classNode.outerMethod) + " "
+ UnicodeUtils.escapeWithSpaces(classNode.outerMethodDesc)))
ctx.next(
"// #OuterMethod: "
+ (classNode.outerMethod == null ? "null"
: (UnicodeUtils.escapeWithSpaces(hs, classNode.outerMethod) + " "
+ UnicodeUtils.escapeWithSpaces(hs, classNode.outerMethodDesc)))
+ "\n");
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");
if (classNode.innerClasses != null) {
String ics = "";
for (InnerClassNode icn : classNode.innerClasses) {
ics += "// " + UnicodeUtils.escapeWithSpaces(icn.name) + " "
+ UnicodeUtils.escapeWithSpaces(icn.outerName) + " "
+ UnicodeUtils.escapeWithSpaces(icn.innerName)
ics += "// " + UnicodeUtils.escapeWithSpaces(hs, icn.name) + " "
+ UnicodeUtils.escapeWithSpaces(hs, icn.outerName) + " "
+ UnicodeUtils.escapeWithSpaces(hs, icn.innerName)
+ (icn.access == 0 ? "" : " " + ClassUtil.getAccessFlagsFull(icn.access).trim()) + "\n";
}
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()) {
String interfaceStr = "";
for (String interfc : classNode.interfaces) {
if (interfaceStr.isEmpty()) {
interfaceStr += UnicodeUtils.escapeWithSpaces(interfc);
interfaceStr += UnicodeUtils.escapeWithSpaces(hs, interfc);
} else {
interfaceStr += ", " + UnicodeUtils.escapeWithSpaces(interfc);
interfaceStr += ", " + UnicodeUtils.escapeWithSpaces(hs, interfc);
}
}
if (!interfaceStr.isEmpty())
ctx.next("implements " + interfaceStr + " ");
}
ctx.next("{\n// #SourceFile: "
+ (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(classNode.sourceFile))
+ "\n\n// #Fields\n");
ctx.next(doFields(classNode.fields, nodeToFind, lineFound));
+ (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(hs, classNode.sourceFile)) + "\n");
int hugeIdx = ctx.next("");
ctx.next("\n// #Fields\n");
ctx.next(doFields(classNode.fields, hs));
ctx.next("\n// #Methods\n");
ctx.next(doMethods(classNode.methods, classNode.name, nodeToFind, lineFound));
ctx.next(doMethods(classNode.methods, hs));
ctx.next("}\n");
return new DisassembleTuple(ctx.finish(), lineFound.get());
if (!hs.isEmpty())
ctx.set(hugeIdx, hs.makeStringsList());
return ctx.finish();
} catch (Throwable e) {
return new DisassembleTuple("Class couldn't be decompiled:\n" + e.getClass().getName() + ": "
+ e.getMessage() + "\n"
return "Class couldn't be decompiled:\n" + e.getClass().getName() + ": " + e.getMessage() + "\n"
+ Arrays.toString(e.getStackTrace()).replace(", ", "\n\tat ").replace("[", "\tat ").replace("]", "")
+ "\n");
+ "\n";
}
}
private static String doAnnotations(List<AnnotationNode> annotations) {
private static String doAnnotations(List<AnnotationNode> annotations, HugeStrings hs, String linePrefix) {
if (annotations == null)
return "";
StringContext ctx = new StringContext(annotations.size());
for (AnnotationNode annotationNode : annotations) {
String s = "@" + UnicodeUtils.escapeWithSpaces(annotationNode.desc);
String s = linePrefix + "@" + UnicodeUtils.escapeWithSpaces(hs, annotationNode.desc);
if (annotationNode.values != null && !annotationNode.values.isEmpty()) {
s += " (";
boolean valBefore = true;
@@ -153,16 +154,16 @@ public class Disassembler {
s += o + " = [";
valBefore = false;
continue;
} else {
}
if (o instanceof String[]) {
String[] arr = (String[]) o;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(rofl) + "/";
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(rofl) + "]";
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "]";
}
}
s += ", ";
@@ -175,27 +176,26 @@ public class Disassembler {
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
s += UnicodeUtils.escapeWithSpaces(rofl) + "/";
s += UnicodeUtils.escapeWithSpaces(hs, rofl) + "/";
w8ing = true;
} else {
s += UnicodeUtils.escapeWithSpaces(rofl);
s += UnicodeUtils.escapeWithSpaces(hs, rofl);
}
}
} else {
s += "\"" + UnicodeUtils.escape(String.valueOf(obj)) + "\"";
s += "\"" + UnicodeUtils.escape(hs, String.valueOf(obj)) + "\"";
}
s += ", ";
}
s = s.substring(0, s.length() - 2);
s += " }]";
s += " }], ";
} else if (o instanceof String) {
s += "\"" + UnicodeUtils.escapeWithSpaces((String) o) + "\"], ";
s += "\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"], ";
} else {
s += "(" + o.getClass().getName().replace(".", "/") + ") " + o + "], ";
}
valBefore = true;
}
}
if (s.endsWith(", "))
s = s.substring(0, s.length() - 2);
s += ")";
@@ -206,93 +206,35 @@ public class Disassembler {
return ctx.finish();
}
private static String doMethods(List<MethodNode> methods, String className, Object nodeToFind,
AtomicInteger lineFound) throws InterruptedException, ExecutionException {
private static String doMethods(List<MethodNode> methods, HugeStrings hs)
throws InterruptedException, ExecutionException {
boolean multithreaded = Main.INSTANCE.mntmMultithreaded.isSelected();
Future<String>[] futures = new Future[methods.size()];
for (int i = 0; i < futures.length; i++) {
MethodNode mn = methods.get(i);
futures[i] = exec.submit(new Callable<String>() {
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
String ms = "";
try {
ms += "\t// #Max: l:" + mn.maxLocals + " s:" + mn.maxStack + "\n";
if (mn.signature != null)
ms += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(mn.signature) + "\n";
String[] dis = disassembleMethod(className, mn);
ms += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, mn.signature) + "\n";
String[] dis = disassembleMethod(mn, hs);
ms += dis[2];
ms += dis[1];
if (mn.visibleAnnotations != null && !mn.visibleAnnotations.isEmpty()) {
for (AnnotationNode annotationNode : mn.visibleAnnotations) {
ms += "\t@" + annotationNode.desc;
if (annotationNode.values != null && !annotationNode.values.isEmpty()) {
ms += " (";
boolean valBefore = true;
for (Object o : annotationNode.values) {
if (valBefore) {
ms += o + " = [";
valBefore = false;
continue;
} else {
if (o instanceof String[]) {
String[] arr = (String[]) o;
boolean w8ing = false;
for (String rofl : arr) {
if (!w8ing) {
ms += UnicodeUtils.escapeWithSpaces(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.visibleAnnotations != null && !mn.visibleAnnotations.isEmpty())
ms += doAnnotations(mn.visibleAnnotations, hs, "\t");
ms += "\t" + ClassUtil.getAccessFlagsFull(mn.access)
+ UnicodeUtils.escapeWithSpaces(hs, mn.name) + " "
+ UnicodeUtils.escapeWithSpaces(hs, mn.desc) + " ";
if (mn.exceptions != null && !mn.exceptions.isEmpty()) {
String exceptionStr = "";
for (String exc : mn.exceptions) {
if (exceptionStr.isEmpty()) {
exceptionStr += UnicodeUtils.escapeWithSpaces(exc);
exceptionStr += UnicodeUtils.escapeWithSpaces(hs, exc);
} else {
exceptionStr += ", " + UnicodeUtils.escapeWithSpaces(exc);
exceptionStr += ", " + UnicodeUtils.escapeWithSpaces(hs, exc);
}
}
if (!exceptionStr.isEmpty())
@@ -309,10 +251,13 @@ public class Disassembler {
}
return ms;
}
});
};
futures[i] = multithreaded ? exec.submit(callable) : new FutureTask<>(callable);
}
StringContext ctx = new StringContext(futures.length);
for (Future<String> f : futures) {
if (f instanceof FutureTask)
((FutureTask) f).run();
ctx.next(f.get());
}
String s = ctx.finish();
@@ -321,78 +266,18 @@ public class Disassembler {
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());
for (FieldNode fn : fields) {
String s = "";
if (fn.signature != null)
s += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(fn.signature) + "\n";
if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty()) {
for (AnnotationNode annotationNode : fn.visibleAnnotations) {
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// #Signature: " + UnicodeUtils.escapeWithSpaces(hs, fn.signature) + "\n";
if (fn.visibleAnnotations != null && !fn.visibleAnnotations.isEmpty())
s += doAnnotations(fn.visibleAnnotations, hs, "\t");
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) {
s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc, true);
s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc, true, hs);
}
s += "\n";
ctx.next(s);
@@ -400,13 +285,22 @@ public class Disassembler {
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>();
String localVarTable;
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) {
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()) {
@@ -414,10 +308,10 @@ public class Disassembler {
StringContext ctx = new StringContext(list.size() + 1);
ctx.next("\t// #LocalVars:\n");
for (LocalVariableNode lvn : list) {
ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(lvn.name) + ": "
+ UnicodeUtils.escapeWithSpaces(lvn.desc) + " i:" + lvn.index + " s:"
ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(hs, lvn.name) + ": "
+ UnicodeUtils.escapeWithSpaces(hs, lvn.desc) + " i:" + lvn.index + " s:"
+ 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();
} else
@@ -427,123 +321,75 @@ public class Disassembler {
StringContext ctx = new StringContext(list.size() + 1);
ctx.next("\t// #TryCatch:\n");
for (TryCatchBlockNode tcbn : mn.tryCatchBlocks) {
ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(tcbn.type) + " s:" + labels.get(tcbn.start.getLabel())
+ " e:" + labels.get(tcbn.end.getLabel()) + " h:" + labels.get(tcbn.handler.getLabel()) + "\n");
ctx.next("\t// " + UnicodeUtils.escapeWithSpaces(hs, tcbn.type) + " s:"
+ labels.get(tcbn.start.getLabel()) + " e:" + labels.get(tcbn.end.getLabel()) + " h:"
+ labels.get(tcbn.handler.getLabel()) + "\n");
}
tryCatchTable = ctx.finish();
} else
tryCatchTable = "";
AbstractInsnNode[] insns = mn.instructions.toArray();
StringContext ctx = new StringContext(insns.length);
for (AbstractInsnNode n : insns) {
ctx.next(disassembleInstruction(n, labels));
if (deobfNumbers) {
StackBasedCalculator calc = new StackBasedCalculator(insnList);
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 };
}
public static final HashMap<Class, InstructionInterface> iiMap = new HashMap<>();
static {
iiMap.put(FieldInsnNode.class, (n, labels) -> {
public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels, HugeStrings hs) {
switch (n.getType()) {
case INSN: {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + "\n";
}
case INT_INSN: {
IntInsnNode node = (IntInsnNode) n;
if (n.getOpcode() == Opcodes.NEWARRAY) {
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " "
+ ClassUtil.getArrayTypeByID(node.operand) + "\n";
}
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.operand + "\n";
}
case VAR_INSN: {
VarInsnNode node = (VarInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + "\n";
}
case TYPE_INSN: {
TypeInsnNode node = (TypeInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + "\n";
}
case FIELD_INSN: {
FieldInsnNode node = (FieldInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(node.desc) + " " + UnicodeUtils.escapeWithSpaces(node.owner) + "/"
+ UnicodeUtils.escapeWithSpaces(node.name).replace("/", "\\u002F") + "\n";
});
iiMap.put(LabelNode.class, (n, labels) -> {
LabelNode node = (LabelNode) n;
return "\t\t// label " + labels.get(node.getLabel()) + "\n";
});
iiMap.put(FrameNode.class, (n, labels) -> {
FrameNode node = (FrameNode) n;
String type = "";
switch (node.type) {
case Opcodes.F_NEW:
type = "NEW";
break;
case Opcodes.F_FULL:
type = "FULL";
break;
case Opcodes.F_APPEND:
type = "APPEND";
break;
case Opcodes.F_CHOP:
type = "CHOP";
break;
case Opcodes.F_SAME:
type = "SAME";
break;
case Opcodes.F_SAME1:
type = "SAME1";
break;
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
+ "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
}
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);
case METHOD_INSN: {
MethodInsnNode node = (MethodInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + UnicodeUtils.escapeWithSpaces(hs, node.owner)
+ "/" + UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n";
}
}
}
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) -> {
case INVOKE_DYNAMIC_INSN: {
InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(node.name).replace("/", "\\u002F") + "\n\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(node.desc) + "\n\t\t\tHandle: [\n\t\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(node.bsm.getName()) + "\n\t\t\t\towner: "
+ UnicodeUtils.escapeWithSpaces(node.bsm.getOwner()) + "\n\t\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(node.bsm.getDesc()) + "\n\t\t\t\tisInterface: "
+ UnicodeUtils.escapeWithSpaces(hs, node.name).replace("/", "\\u002F") + "\n\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + "\n\t\t\tHandle: [\n\t\t\t\tname: "
+ UnicodeUtils.escapeWithSpaces(hs, node.bsm.getName()) + "\n\t\t\t\towner: "
+ UnicodeUtils.escapeWithSpaces(hs, node.bsm.getOwner()) + "\n\t\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(hs, node.bsm.getDesc()) + "\n\t\t\t\tisInterface: "
+ node.bsm.isInterface() + "\n\t\t\t\ttag: " + OpcodesReverse.reverseHandleOpcode(node.bsm.getTag())
+ "\n\t\t\t]\n\t\t\targs: [\n";
for (Object l : node.bsmArgs) {
@@ -555,40 +401,34 @@ public class Disassembler {
// Reflection
s += "\t\t\t\tType: [\n\t\t\t\t\ttype: " + ClassUtil.getClassNameFromType(type)
+ "\n\t\t\t\t\tstart: " + valueBegin + "\n\t\t\t\t\tend: " + valueEnd + "\n\t\t\t\t\tbuf: "
+ ClassUtil.getDecompiledValue(buf, "");
+ ClassUtil.getDecompiledValue(buf, "", hs);
s += "\n\t\t\t\t]\n";
} else if (l instanceof Handle) {
Handle h = (Handle) l;
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(h.getOwner()) + "\n\t\t\t\t\tdesc: "
+ UnicodeUtils.escapeWithSpaces(h.getDesc()) + "\n\t\t\t\t\tisInterface: " + h.isInterface()
+ "\n\t\t\t\t\ttag: " + OpcodesReverse.reverseHandleOpcode(h.getTag());
+ UnicodeUtils.escapeWithSpaces(hs, h.getName()).replace("/", "\\u002F")
+ "\n\t\t\t\t\towner: " + UnicodeUtils.escapeWithSpaces(hs, h.getOwner())
+ "\n\t\t\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(hs, h.getDesc())
+ "\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";
} 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";
return s;
});
iiMap.put(MethodInsnNode.class, (n, labels) -> {
MethodInsnNode node = (MethodInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(node.desc) + " " + UnicodeUtils.escapeWithSpaces(node.owner) + "/"
+ UnicodeUtils.escapeWithSpaces(node.name).replace("/", "\\u002F") + "\n";
});
iiMap.put(TypeInsnNode.class, (n, labels) -> {
TypeInsnNode node = (TypeInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(node.desc) + "\n";
});
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) -> {
}
case JUMP_INSN: {
JumpInsnNode node = (JumpInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + labels.get(node.label.getLabel())
+ "\n";
}
case LABEL: {
LabelNode node = (LabelNode) n;
return "\t\t// label " + labels.get(node.getLabel()) + "\n";
}
case LDC_INSN: {
LdcInsnNode node = (LdcInsnNode) n;
if (node.cst instanceof Type) {
Type type = (Type) node.cst;
@@ -598,37 +438,24 @@ public class Disassembler {
// Reflection
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " Type: [\n\t\t\ttype: "
+ ClassUtil.getClassNameFromType(type) + "\n\t\t\tstart: " + valueBegin + "\n\t\t\tend: "
+ valueEnd + "\n\t\t\tbuf: " + ClassUtil.getDecompiledValue(buf, "");
+ valueEnd + "\n\t\t\tbuf: " + ClassUtil.getDecompiledValue(buf, "", hs);
s += "\n\t\t]\n";
return s;
} else {
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ ClassUtil.getDecompiledValue(node.cst, "") + "\n";
}
});
iiMap.put(VarInsnNode.class, (n, labels) -> {
VarInsnNode node = (VarInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + "\n";
});
iiMap.put(InsnNode.class, (n, labels) -> {
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";
String decomp = ClassUtil.getDecompiledValue(node.cst, "", hs);
if (node.cst instanceof String) {
if (decomp.startsWith("\"#") && decomp.endsWith("\""))
decomp = decomp.substring(1, decomp.length() - 1);
else if (decomp.startsWith("\"\\u0023"))
decomp = "\"#" + decomp.substring(7);
}
});
iiMap.put(JumpInsnNode.class, (n, labels) -> {
JumpInsnNode node = (JumpInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + labels.get(node.label.getLabel())
+ "\n";
});
iiMap.put(TableSwitchInsnNode.class, (n, labels) -> {
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + decomp + "\n";
}
case IINC_INSN: {
IincInsnNode node = (IincInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + " " + node.incr + "\n";
}
case TABLESWITCH_INSN: {
TableSwitchInsnNode node = (TableSwitchInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tmin: " + node.min
+ "\n\t\t\tmax: " + node.max + "\n\t\t\tdefault: " + labels.get(node.dflt.getLabel())
@@ -638,8 +465,8 @@ public class Disassembler {
}
s += "\t\t\t]\n\t\t]\n";
return s;
});
iiMap.put(LookupSwitchInsnNode.class, (n, labels) -> {
}
case LOOKUPSWITCH_INSN: {
LookupSwitchInsnNode node = (LookupSwitchInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " [\n\t\t\tdefault: "
+ labels.get(node.dflt.getLabel()) + "\n\t\t\tkeys: [\n";
@@ -652,24 +479,69 @@ public class Disassembler {
}
s += "\t\t\t]\n\t\t]\n";
return s;
});
iiMap.put(IincInsnNode.class, (n, labels) -> {
IincInsnNode node = (IincInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + node.var + " " + node.incr + "\n";
});
}
static interface InstructionInterface {
String apply(AbstractInsnNode n, HashMap<Label, Integer> labels);
case MULTIANEWARRAY_INSN: {
MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " "
+ UnicodeUtils.escapeWithSpaces(hs, node.desc) + " " + node.dims + "\n";
}
public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels) {
InstructionInterface ii = iiMap.get(n.getClass());
if (ii != null)
return ii.apply(n, labels);
else
case FRAME: {
FrameNode node = (FrameNode) n;
String out = "";
switch (node.type) {
case Opcodes.F_SAME:
out = "SAME";
break;
case Opcodes.F_SAME1:
out = "SAME1 " + getFrameArray(hs, node.stack, labels);
break;
case Opcodes.F_CHOP:
out = "CHOP " + Integer.toString(node.local.size());
break;
case Opcodes.F_APPEND:
out = "APPEND " + getFrameArray(hs, node.local, labels);
break;
case Opcodes.F_FULL:
out = "FULL " + getFrameArray(hs, node.local, labels) + " " + getFrameArray(hs, node.stack, labels);
break;
}
return "\t\t// frame " + out + "\n";
}
case LINE: {
LineNumberNode node = (LineNumberNode) n;
return "\t\t// line " + node.line + " " + labels.get(node.start.getLabel()) + "\n";
}
default: {
return "\t\tNOT HANDLED! " + OpcodesReverse.reverseOpcode(n.getOpcode()) + " "
+ n.getClass().getSimpleName() + "\n";
}
}
}
private static String getFrameArray(HugeStrings hs, List<Object> list, HashMap<Label, Integer> labels) {
ArrayList<String> arr = new ArrayList<>();
if (list != null) {
for (Object o : list) {
if (o == null) {
arr.add(null);
} else {
if (o instanceof String) {
arr.add("\"" + UnicodeUtils.escapeWithSpaces(hs, (String) o) + "\"");
} else if (o instanceof LabelNode) {
arr.add("(label) " + labels.get(((LabelNode) o).getLabel()));
} else if (o instanceof Integer) {
String frameType = ClassUtil.getFrameTypeByID(((Integer) o).intValue());
if (frameType != null)
arr.add(frameType);
else
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
} else {
arr.add("(" + o.getClass().getName().replace(".", "/") + ") " + o);
}
}
}
return arr.toString();
}
return "null";
}
}
+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];
}
public void next(String s) {
public int next(String s) {
int i = idx;
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() {
-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 = true;
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(Main.getFullName(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(Main.getFullName(node.name));
}
} 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;
}
}
+234 -411
View File
@@ -1,5 +1,6 @@
package me.ByteEdit.main;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
@@ -15,28 +16,29 @@ import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
@@ -46,21 +48,16 @@ import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.border.EmptyBorder;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.fife.ui.autocomplete.AutoCompletion;
@@ -73,23 +70,21 @@ import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rsyntaxtextarea.folding.CurlyFoldParser;
import org.fife.ui.rsyntaxtextarea.folding.FoldParserManager;
import org.fife.ui.rtextarea.RTextScrollPane;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
import me.ByteEdit.boxes.CompilationBox;
import me.ByteEdit.boxes.GlobalSearchBox;
import me.ByteEdit.boxes.OptionBox;
import me.ByteEdit.boxes.RenameBox;
import me.ByteEdit.boxes.SearchBox;
import me.ByteEdit.boxes.ThemeBox;
import me.ByteEdit.boxes.TypeOpenBox;
import me.ByteEdit.boxes.UnicodeBox;
import me.ByteEdit.decompiler.EnumDecompiler;
import me.ByteEdit.decompiler.SingleThreadedExecutor;
import me.ByteEdit.edit.Assembler;
import me.ByteEdit.edit.Disassembler;
import me.ByteEdit.edit.Disassembler.DisassembleTuple;
import me.ByteEdit.edit.HugeStrings;
import me.ByteEdit.utils.ClassUtil;
import me.ByteEdit.utils.OpcodesReverse;
import me.ByteEdit.utils.UnicodeUtils;
@@ -97,17 +92,23 @@ import me.ByteEdit.utils.UnicodeUtils;
public class Main extends JFrame {
public static Main INSTANCE;
public static final Pattern patternSlash = Pattern.compile("/");
private static final Pattern patternRenameableField = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*");
private static final Pattern patternRenameableMethod = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{");
private JPanel contentPane;
private volatile boolean isChangingFile;
public static File jarFile;
public static HashMap<String, byte[]> OTHER_FILES = new HashMap<>();
public static HashMap<String, ClassNode> classNodes = new HashMap<>();
public static final HashMap<String, ClassNode> classNodes = new HashMap<>();
public static final HashMap<String, byte[]> otherFiles = new HashMap<>();
public static String currentNodeName;
public static RSyntaxTextArea txtByteEditView;
public static GlobalSearchBox globalSearchBox;
public static SearchBox searchBox;
public static TypeOpenBox typeOpenBox;
public static OptionBox optionBox;
public static UnicodeBox unicodeBox;
public static RenameBox renameBox;
public static CompilationBox compilationBox;
@@ -116,15 +117,42 @@ public class Main extends JFrame {
public static RTextScrollPane scrollPane_ByteEdit;
public static File saveFolder;
private JMenuBar menuBar;
private JMenu mnFile;
private JMenuItem mntmOpenJar;
private JMenuItem mntmSaveBytecode;
private JMenuItem mntmSaveJar;
private JMenu mnWindow;
private JMenuItem mntmThemes;
private JMenuItem mntmSearchMembers;
private JMenuItem mntmSearch;
private JMenuItem mntmOpenType;
private JMenuItem mntmUnicode;
private JMenuItem mntmCompiler;
private JMenu mnSearch;
private JMenu mnOptions;
private JCheckBoxMenuItem mntmComputeFrames;
private JCheckBoxMenuItem mntmComputeMax;
private JMenu mnStringLimit;
private JRadioButtonMenuItem rdbtnmntm0;
private JMenuItem rdbtnmntm100;
private JMenuItem rdbtnmntm1000;
private JMenuItem rdbtnmntm500;
private JMenuItem rdbtnmntm5000;
private JMenuItem rdbtnmntm10000;
public JCheckBoxMenuItem mntmMultithreaded;
private JMenu mnDeobfuscation;
public JCheckBoxMenuItem mntmNumbers;
public static final Object treeLock = new Object();
public static EnumDecompiler decompiler = EnumDecompiler.BYTEEDIT;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
EventQueue.invokeLater(() -> {
try {
Main frame = new Main();
frame.setVisible(true);
@@ -132,15 +160,13 @@ public class Main extends JFrame {
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create a simple provider that adds some Java-related completions.
*/
private CompletionProvider createCompletionProvider() {
private static CompletionProvider createCompletionProvider() {
DefaultCompletionProvider provider = new DefaultCompletionProvider();
for (int i = 0; i < 200; i++) {
String s = OpcodesReverse.reverseOpcode(i);
@@ -196,7 +222,17 @@ public class Main extends JFrame {
"// #Max: l:0 s:0\n\t// #TryCatch:\n\t// #LocalVars:\n\tpublic static method ()V {\n\t\treturn\n\t}",
null, "<html><b><u>method</u></b><br>Creates an example method</html>"));
provider.addCompletion(new ShorthandCompletion(provider, "frame", "frame FULL l:[] s:[]", null,
"<html><b><u>frame</u></b><br>Creates a stack map frame</html>"));
"<html><b><u>frame</u></b><br>Creates a stack map frame<br><br><b>Possible frames:</b><br><ul><li><code>SAME: same locals, empty stack</code></li><li><code>SAME1: same locals, stack has 1 entry</code></li><li><code>CHOP: same locals - k, empty stack</code></li><li><code>APPEND: same locals + k, empty stack</code></li><li><code>FULL: define locals, define stack</code></li></ul></html>"));
provider.addCompletion(new BasicCompletion(provider, "SAME", null,
"<html><b><u>Frames:</u></b><br><code>SAME</code><br><br><u>Locals:</u> same as previous<br><u>Stack:</u> empty</html>"));
provider.addCompletion(new BasicCompletion(provider, "SAME1", null,
"<html><b><u>Frames:</u></b><br><code>SAME1 [(type)]</code><br><br><u>Locals:</u> same as previous<br><u>Stack:</u> 1 entry</html>"));
provider.addCompletion(new BasicCompletion(provider, "CHOP", null,
"<html><b><u>Frames:</u></b><br><code>CHOP (int:k)</code><br><br><u>Locals:</u> removes the top <i>k</i> elements<br><u>Stack:</u> empty</html>"));
provider.addCompletion(new BasicCompletion(provider, "APPEND", null,
"<html><b><u>Frames:</u></b><br><code>APPEND [(type), (type), ...]</code><br><br><u>Locals:</u> adds the elements to the top<br><u>Stack:</u> empty</html>"));
provider.addCompletion(new BasicCompletion(provider, "FULL", null,
"<html><b><u>Frames:</u></b><br><code>FULL [(type), (type), ...] [(type), (type), ...]</code><br><i>first locals, then stack</i><br><br><u>Locals:</u> sets the elements<br><u>Stack:</u> sets the elements</html>"));
return provider;
}
@@ -209,7 +245,6 @@ public class Main extends JFrame {
globalSearchBox = new GlobalSearchBox();
searchBox = new SearchBox();
typeOpenBox = new TypeOpenBox();
optionBox = new OptionBox();
unicodeBox = new UnicodeBox();
renameBox = new RenameBox();
compilationBox = new CompilationBox();
@@ -217,7 +252,6 @@ public class Main extends JFrame {
ThemeManager.registerFrames(globalSearchBox);
ThemeManager.registerFrames(searchBox);
ThemeManager.registerFrames(typeOpenBox);
ThemeManager.registerFrames(optionBox);
ThemeManager.registerFrames(unicodeBox);
ThemeManager.registerFrames(renameBox);
ThemeManager.registerFrames(compilationBox);
@@ -254,7 +288,6 @@ public class Main extends JFrame {
if (action == 0) {
final File file = fileChooser.getSelectedFile();
saveFolder = file.getParentFile();
boolean shouldSave = false;
if (file.exists()) {
synchronized (treeLock) {
jarFile = file;
@@ -268,8 +301,9 @@ public class Main extends JFrame {
new Thread(new Runnable() {
public void run() {
try {
ArchiveTreeModel model = new ArchiveTreeModel(new JarFile(jarFile));
EventQueue.invokeLater(new Runnable() {
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile), classNodes,
otherFiles);
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
tree.setModel(model);
@@ -278,7 +312,6 @@ public class Main extends JFrame {
globalSearchBox.setVisible(false);
searchBox.setVisible(false);
typeOpenBox.setVisible(false);
optionBox.setVisible(false);
unicodeBox.setVisible(false);
renameBox.setVisible(false);
compilationBox.setVisible(false);
@@ -287,7 +320,7 @@ public class Main extends JFrame {
}
}
});
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -336,6 +369,62 @@ public class Main extends JFrame {
mntmCompiler = new JMenuItem("Compiler");
mntmCompiler.addActionListener(e -> compilationBox.setVisible(true));
mnWindow.add(mntmCompiler);
mnOptions = new JMenu("Options");
menuBar.add(mnOptions);
mntmComputeFrames = new JCheckBoxMenuItem("Compute Frames");
mnOptions.add(mntmComputeFrames);
mntmComputeMax = new JCheckBoxMenuItem("Compute Max");
mnOptions.add(mntmComputeMax);
mntmMultithreaded = new JCheckBoxMenuItem("Multithreaded");
mntmMultithreaded.setSelected(true);
mnOptions.add(mntmMultithreaded);
mnStringLimit = new JMenu("Huge String Limit");
mnOptions.add(mnStringLimit);
rdbtnmntm0 = new JRadioButtonMenuItem("0");
rdbtnmntm0.addActionListener(e -> HugeStrings.THRESHOLD = 0);
mnStringLimit.add(rdbtnmntm0);
rdbtnmntm100 = new JRadioButtonMenuItem("100");
rdbtnmntm100.addActionListener(e -> HugeStrings.THRESHOLD = 100);
mnStringLimit.add(rdbtnmntm100);
rdbtnmntm500 = new JRadioButtonMenuItem("500");
rdbtnmntm500.addActionListener(e -> HugeStrings.THRESHOLD = 500);
mnStringLimit.add(rdbtnmntm500);
rdbtnmntm1000 = new JRadioButtonMenuItem("1000");
rdbtnmntm1000.addActionListener(e -> HugeStrings.THRESHOLD = 1000);
rdbtnmntm1000.setSelected(true);
mnStringLimit.add(rdbtnmntm1000);
rdbtnmntm5000 = new JRadioButtonMenuItem("5000");
rdbtnmntm5000.addActionListener(e -> HugeStrings.THRESHOLD = 5000);
mnStringLimit.add(rdbtnmntm5000);
rdbtnmntm10000 = new JRadioButtonMenuItem("10000");
rdbtnmntm10000.addActionListener(e -> HugeStrings.THRESHOLD = 10000);
mnStringLimit.add(rdbtnmntm10000);
ButtonGroup group = new ButtonGroup();
group.add(rdbtnmntm0);
group.add(rdbtnmntm100);
group.add(rdbtnmntm500);
group.add(rdbtnmntm1000);
group.add(rdbtnmntm5000);
group.add(rdbtnmntm10000);
mnDeobfuscation = new JMenu("Deobfuscation");
menuBar.add(mnDeobfuscation);
mntmNumbers = new JCheckBoxMenuItem("Numbers");
mnDeobfuscation.add(mntmNumbers);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
@@ -352,9 +441,8 @@ public class Main extends JFrame {
new CurlyFoldParser(false, true));
tree = new JTree(new DefaultTreeModel(null));
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener());
tree.addTreeExpansionListener(new FurtherExpandingTreeExpansionListener(tree));
tree.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 116 && jarFile != null) { // F5
@@ -368,8 +456,9 @@ public class Main extends JFrame {
new Thread(new Runnable() {
public void run() {
try {
ArchiveTreeModel model = new ArchiveTreeModel(new JarFile(jarFile));
EventQueue.invokeLater(new Runnable() {
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile), classNodes,
otherFiles);
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
tree.setModel(model);
@@ -378,7 +467,7 @@ public class Main extends JFrame {
}
}
});
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -423,16 +512,8 @@ public class Main extends JFrame {
}
});
scrollPane.setViewportView(tree);
tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
synchronized (treeLock) {
if (!isChangingFile) {
selectFile(((ByteEditTreeNode) e.getPath().getLastPathComponent()).path);
}
}
}
});
tree.addTreeSelectionListener(e -> SingleThreadedExecutor
.submit(() -> selectFile(((ByteEditTreeNode) e.getPath().getLastPathComponent()).path)));
new DropTarget(tree, new DropTargetListener() {
@Override
@@ -448,7 +529,7 @@ public class Main extends JFrame {
for (int i = 0; i < flavors.length; i++) {
if (flavors[i].isFlavorJavaFileListType()) {
dtde.acceptDrop(dtde.getDropAction());
java.util.List<File> files = (java.util.List<File>) tr.getTransferData(flavors[i]);
List<File> files = (List<File>) tr.getTransferData(flavors[i]);
final File file = files.get(0);
if (file.getName().endsWith(".jar")) {
jarFile = file;
@@ -462,8 +543,9 @@ public class Main extends JFrame {
new Thread(new Runnable() {
public void run() {
try {
ArchiveTreeModel model = new ArchiveTreeModel(new JarFile(jarFile));
EventQueue.invokeLater(new Runnable() {
ArchiveTreeModel model = new ArchiveTreeModel(new ZipFile(jarFile),
classNodes, otherFiles);
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
tree.setModel(model);
@@ -472,7 +554,6 @@ public class Main extends JFrame {
globalSearchBox.setVisible(false);
searchBox.setVisible(false);
typeOpenBox.setVisible(false);
optionBox.setVisible(false);
unicodeBox.setVisible(false);
renameBox.setVisible(false);
compilationBox.setVisible(false);
@@ -481,7 +562,7 @@ public class Main extends JFrame {
}
}
});
} catch (IOException e) {
} catch (Exception e) {
e.printStackTrace();
}
}
@@ -520,30 +601,12 @@ public class Main extends JFrame {
txtByteEditView = new RSyntaxTextArea();
ac.install(txtByteEditView);
txtByteEditView.setCodeFoldingEnabled(true);
txtByteEditView.setToolTipSupplier(new HugeStringsToolTips(txtByteEditView));
txtByteEditView.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 116 && currentNodeName != null) { // F5
try {
ClassNode classNode = classNodes.get(currentNodeName);
int prev = txtByteEditView.getCaretPosition();
String dis = Disassembler.disassemble(classNode);
String substr = currentNodeName.substring(0, currentNodeName.length() - 6);
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
if (split[0].equals(substr)) {
dis += "\n" + Disassembler.disassemble(classNodes.get(key));
}
}
}
txtByteEditView.setText(dis);
if (dis.length() > prev)
txtByteEditView.setCaretPosition(prev);
} catch (Exception e2) {
e2.printStackTrace();
}
SingleThreadedExecutor.submit(Main::decompileCurrentNode);
}
}
});
@@ -551,7 +614,6 @@ public class Main extends JFrame {
KeyStroke ctrlT = KeyStroke.getKeyStroke(KeyEvent.VK_T, KeyEvent.CTRL_DOWN_MASK);
KeyStroke ctrlS = KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK);
KeyStroke ctrlG = KeyStroke.getKeyStroke(KeyEvent.VK_G, KeyEvent.CTRL_DOWN_MASK);
KeyStroke ctrlO = KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK);
KeyStroke ctrlU = KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK);
KeyStroke ctrlR = KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK);
KeyStroke ctrlE = KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK);
@@ -574,8 +636,6 @@ public class Main extends JFrame {
typeOpenBox.txtSearch.requestFocusInWindow();
typeOpenBox.txtSearch.select(0, typeOpenBox.txtSearch.getText().length());
}, ctrlT, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtByteEditView.registerKeyboardAction(e -> optionBox.setVisible(true), ctrlO,
JComponent.WHEN_IN_FOCUSED_WINDOW);
txtByteEditView.registerKeyboardAction(e -> unicodeBox.setVisible(true), ctrlU,
JComponent.WHEN_IN_FOCUSED_WINDOW);
txtByteEditView.registerKeyboardAction(e -> compilationBox.setVisible(true), ctrlE,
@@ -583,114 +643,63 @@ public class Main extends JFrame {
// specific
tree.registerKeyboardAction(e -> save(), ctrlS, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> saveCurrentClassNode(), ctrlS, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> {
try {
goToSelected();
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}, ctrlG, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> {
try {
renameSelected();
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}, ctrlR, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(e -> renameSelected(), ctrlR, JComponent.WHEN_FOCUSED);
txtByteEditView.setEditable(true);
txtByteEditView.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JAVA_DISASSEMBLE);
txtByteEditView.setCodeFoldingEnabled(true);
scrollPane_ByteEdit = new RTextScrollPane();
ThemeManager.registerTextArea(txtByteEditView);
splitPane.setRightComponent(scrollPane_ByteEdit);
JPanel panel = new JPanel(new BorderLayout());
panel.add(scrollPane_ByteEdit, BorderLayout.CENTER);
final JComboBox<String> decompilerMode = new JComboBox<>();
DefaultComboBoxModel model = new DefaultComboBoxModel();
Stream.of(EnumDecompiler.values()).forEach(model::addElement);
decompilerMode.setModel(model);
decompilerMode.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
EnumDecompiler selected = (EnumDecompiler) e.getItem();
SingleThreadedExecutor.submit(() -> {
decompiler = selected;
decompileCurrentNode();
try {
EventQueue.invokeAndWait(() -> {
synchronized (treeLock) {
txtByteEditView.setCaretPosition(0);
try {
txtByteEditView.setSyntaxEditingStyle(decompiler.getSyntaxStyle());
} catch (Throwable t) {
}
txtByteEditView.setEditable(decompiler.isEditable());
}
});
} catch (Exception e1) {
e1.printStackTrace();
}
});
}
}
});
panel.add(decompilerMode, BorderLayout.NORTH);
splitPane.setRightComponent(panel);
scrollPane_ByteEdit.setViewportView(txtByteEditView);
scrollPane_ByteEdit.setLineNumbersEnabled(true);
scrollPane_ByteEdit.setFoldIndicatorEnabled(true);
}
private final Pattern jumpableInstructionPattern = Pattern.compile("^\t\t(?!//|\t+).+ .+");
public void goToSelected() throws BadLocationException {
public void renameSelected() {
try {
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
if (!jumpableInstructionPattern.matcher(line).matches()) {
return;
}
line = line.substring(2);
if (line.startsWith("invoke")) {
String[] split = line.split(" ");
String desc = UnicodeUtils.unescape(split[1]);
String target = split[2];
int index = target.lastIndexOf("/");
String className = UnicodeUtils.unescape(target.substring(0, index));
String methodName = UnicodeUtils.unescape(target.substring(index + 1));
ClassNode classNode = classNodes.get(getFullName(className));
if (classNode == null) {
return;
}
for (MethodNode mn : classNode.methods) {
if (mn.name.equals(methodName) && mn.desc.equals(desc)) {
while (classNode.outerClass != null) {
classNode = classNodes.get(getFullName(classNode.outerClass));
}
int lineFound = selectFileWithSearch(getFullName(classNode.name), mn);
if (lineFound != -1) {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
}
break;
}
}
} else if (line.startsWith("new ")) {
String[] split = line.split(" ");
String className = UnicodeUtils.unescape(split[1]);
ClassNode classNode = classNodes.get(getFullName(className));
if (classNode == null) {
return;
}
selectFile(getFullName(className));
} else if (line.startsWith("getstatic ") || line.startsWith("putstatic ") || line.startsWith("getfield ")
|| line.startsWith("putfield ")) {
String[] split = line.split(" ");
String desc = UnicodeUtils.unescape(split[1]);
String target = split[2];
int index = target.lastIndexOf("/");
String className = UnicodeUtils.unescape(target.substring(0, index));
String fieldName = UnicodeUtils.unescape(target.substring(index + 1));
ClassNode classNode = classNodes.get(getFullName(className));
if (classNode == null) {
return;
}
for (FieldNode fn : classNode.fields) {
if (fn.name.equals(fieldName) && fn.desc.equals(desc)) {
while (classNode.outerClass != null) {
classNode = classNodes.get(getFullName(classNode.outerClass));
}
int lineFound = selectFileWithSearch(getFullName(classNode.name), fn);
if (lineFound != -1) {
Main.txtByteEditView.setCaretPosition(Main.txtByteEditView.getLineStartOffset(lineFound));
}
break;
}
}
}
}
private final Pattern renameableFieldPattern = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?(\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;)) ([^ ]+) ?.*");
private final Pattern renameableMethodPattern = Pattern
.compile("^\t(?:[a-z]+ |0x[0-9a-fA-F]+ )*?([^ ]+) (\\((?:\\[*(?:V|Z|C|B|S|I|F|J|D|L.+?;))*\\)[^ ]+) .*\\{");
public void renameSelected() throws BadLocationException {
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
String line = txtByteEditView.getText(lineStart, lineEnd - lineStart);
Matcher m = renameableFieldPattern.matcher(line);
Matcher m = patternRenameableField.matcher(line);
if (m.find()) {
setRenameInfo(m, lineEnd);
} else {
m = renameableMethodPattern.matcher(line);
m = patternRenameableMethod.matcher(line);
if (m.find()) {
if (ClassUtil.isObjectClassMethod(m.group(1), m.group(2), false)) {
JOptionPane.showMessageDialog(null, "This method can not be renamed!", "Error!",
@@ -700,6 +709,9 @@ public class Main extends JFrame {
setRenameInfo(m, lineEnd);
}
}
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
public void setRenameInfo(Matcher m, int lineEnd) throws BadLocationException {
@@ -721,10 +733,12 @@ public class Main extends JFrame {
if (s.contains(" extends "))
s = s.substring(0, s.lastIndexOf(" extends "));
String[] split = s.split(" ");
String className = UnicodeUtils.unescape(split[split.length - 1]);
String className = UnicodeUtils.unescape(null, split[split.length - 1], true);
renameBox.className = className;
String name = UnicodeUtils.unescape(m.pattern() == renameableFieldPattern ? m.group(2) : m.group(1));
String desc = UnicodeUtils.unescape(m.pattern() == renameableFieldPattern ? m.group(1) : m.group(2));
String name = UnicodeUtils.unescape(null, m.pattern() == patternRenameableField ? m.group(2) : m.group(1),
true);
String desc = UnicodeUtils.unescape(null, m.pattern() == patternRenameableField ? m.group(1) : m.group(2),
true);
renameBox.name = name;
renameBox.desc = desc;
renameBox.txtName.setText(name);
@@ -734,7 +748,7 @@ public class Main extends JFrame {
}
}
private void saveCurrentClassNode() {
private static void saveCurrentClassNode() {
String txt = txtByteEditView.getText();
for (String s : txt.split("\\/\\/ #Annotations:\n")) {
if (s.isEmpty())
@@ -747,7 +761,7 @@ public class Main extends JFrame {
classNodes.put(getFullName(node.name), node);
ArchiveTreeModel model;
if (tree.getModel().getClass().equals(DefaultTreeModel.class)) {
tree.setModel(model = new ArchiveTreeModel());
tree.setModel(model = new ArchiveTreeModel(classNodes, otherFiles));
} else {
model = (ArchiveTreeModel) tree.getModel();
}
@@ -764,41 +778,45 @@ public class Main extends JFrame {
}
}
public static int selectFileWithSearch(String s, Object nodeToFind) {
if (s != null && s.endsWith(".class")) {
currentNodeName = s;
int lineFound = -1;
ClassNode classNode = classNodes.get(s);
if (classNode == null)
return -1;
String dis = "";
DisassembleTuple tuple = Disassembler.disassemble(classNode, nodeToFind);
if (tuple.getLine() != -1) {
lineFound = dis.split("\\n").length + tuple.getLine() - 1;
}
dis += tuple.getDisassembly();
String substr = s.substring(0, s.length() - 6);
for (String key : Main.classNodes.keySet()) {
if (key.contains("$")) {
String[] split = key.split("\\$");
public static void decompileCurrentNode() {
if (currentNodeName == null)
return;
try {
EnumDecompiler decompiler = Main.decompiler;
String current = currentNodeName;
ClassNode classNode = classNodes.get(current);
int prev = txtByteEditView.getCaretPosition();
String dis = decompiler.getDecompiler().decompile(classNode);
String substr = current.substring(0, current.length() - 6);
synchronized (Main.classNodes) {
for (Entry<String, ClassNode> entry : Main.classNodes.entrySet()) {
if (entry.getKey().contains("$")) {
String[] split = entry.getKey().split("\\$");
if (split[0].equals(substr)) {
tuple = Disassembler.disassemble(classNodes.get(key), nodeToFind);
if (tuple.getLine() != -1) {
lineFound = dis.split("\\n").length + tuple.getLine() + 1;
}
dis += "\n" + tuple.getDisassembly();
dis += "\n" + decompiler.getDecompiler().decompile(entry.getValue());
}
}
}
txtByteEditView.setText(dis);
txtByteEditView.setCaretPosition(0);
return lineFound;
}
return -1;
String fdis = dis;
EventQueue.invokeAndWait(() -> {
synchronized (treeLock) {
txtByteEditView.setText(fdis);
if (fdis.length() > prev)
txtByteEditView.setCaretPosition(prev);
}
});
} catch (Exception e2) {
e2.printStackTrace();
}
}
public static int selectFile(String s) {
return selectFileWithSearch(s, null);
public static void selectFile(String s) {
if (s != null && s.endsWith(".class")) {
currentNodeName = s;
decompileCurrentNode();
txtByteEditView.setCaretPosition(0);
}
}
public void save() {
@@ -824,7 +842,6 @@ public class Main extends JFrame {
int dialogResult = JOptionPane.showConfirmDialog(null, "This file already exists! Overwrite it?",
"Warning", JOptionPane.YES_NO_OPTION);
if (dialogResult == JOptionPane.YES_OPTION) {
save(file, classNodes.values());
shouldSave = true;
}
} else {
@@ -847,7 +864,8 @@ public class Main extends JFrame {
public void run() {
synchronized (treeLock) {
save(file, classNodes.values());
EventQueue.invokeLater(new Runnable() {
try {
EventQueue.invokeAndWait(new Runnable() {
public void run() {
synchronized (treeLock) {
isChangingFile = false;
@@ -855,6 +873,9 @@ public class Main extends JFrame {
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
@@ -868,7 +889,7 @@ public class Main extends JFrame {
public void save(File jar, Collection<ClassNode> classes) {
try {
final JarOutputStream output = new JarOutputStream(new FileOutputStream(jar));
final ZipOutputStream output = new ZipOutputStream(new FileOutputStream(jar));
if (tree.getModel().getClass().equals(DefaultTreeModel.class)
|| ((ArchiveTreeModel) tree.getModel()).newCreated) {
int acc = ClassUtil.ACC_PUBLIC | ClassUtil.ACC_STATIC;
@@ -887,28 +908,28 @@ public class Main extends JFrame {
if (name != null && !name.isEmpty()) {
String val = "Manifest-Version: 1.0\n" + "Class-Path: .\n" + "Main-Class: " + name.replace('/', '.')
+ "\n\n";
OTHER_FILES.put("META-INF/MANIFEST.MF", val.getBytes());
otherFiles.put("META-INF/MANIFEST.MF", val.getBytes());
}
if (tree.getModel() instanceof ArchiveTreeModel)
((ArchiveTreeModel) tree.getModel()).newCreated = false;
}
for (Entry<String, byte[]> entry : OTHER_FILES.entrySet()) {
JarEntry ent = new JarEntry(entry.getKey());
for (Entry<String, byte[]> entry : otherFiles.entrySet()) {
ZipEntry ent = new ZipEntry(entry.getKey());
output.putNextEntry(ent);
output.write(entry.getValue());
output.closeEntry();
}
int computeFlags = 0;
if (optionBox.chckbxComputeFrames.isSelected()) {
if (mntmComputeFrames.isSelected()) {
computeFlags |= ClassWriter.COMPUTE_FRAMES;
}
if (optionBox.chckbxComputeMax.isSelected()) {
if (mntmComputeMax.isSelected()) {
computeFlags |= ClassWriter.COMPUTE_MAXS;
}
for (ClassNode node : classes) {
ClassWriter writer = new ClassWriter(computeFlags);
node.accept(writer);
output.putNextEntry(new JarEntry(getFullName(node.name)));
output.putNextEntry(new ZipEntry(getFullName(node.name)));
output.write(writer.toByteArray());
output.closeEntry();
}
@@ -938,202 +959,4 @@ public class Main extends JFrame {
public static String getFullName(String s) {
return s + ".class";
}
private static final Pattern SLASH = Pattern.compile("/");
private JMenuBar menuBar;
private JMenu mnFile;
private JMenuItem mntmOpenJar;
private JMenuItem mntmSaveBytecode;
private JMenuItem mntmSaveJar;
private JMenu mnWindow;
private JMenuItem mntmThemes;
private JMenuItem mntmSearchMembers;
private JMenuItem mntmSearch;
private JMenuItem mntmOpenType;
private JMenuItem mntmUnicode;
private JMenuItem mntmCompiler;
private JMenu mnSearch;
class ArchiveTreeModel extends DefaultTreeModel {
public ArrayList<String> paths = new ArrayList<>();
public boolean newCreated = false;
public ArchiveTreeModel() {
super(new ByteEditTreeNode("New"));
newCreated = true;
}
public ArchiveTreeModel(JarFile jar) {
super(new ByteEditTreeNode(jar.getName().split(File.separator.equals("\\") ? "\\\\"
: File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length
- 1]));
try {
classNodes.clear();
OTHER_FILES.clear();
Enumeration<JarEntry> enumeration = jar.entries();
while (enumeration.hasMoreElements()) {
JarEntry next = enumeration.nextElement();
byte[] data = toByteArray(jar.getInputStream(next));
if (next.getSize() != 0 && !next.getName().startsWith("META-INF")
&& (next.getName().endsWith(".class") || next.getName().endsWith(".class/"))) {
try {
ClassReader reader = new ClassReader(data);
ClassNode node = new ClassNode();
reader.accept(node, 0);
classNodes.put(getFullName(node.name), node);
if ((next.getName().contains("/")
? (!SLASH.split(next.getName())[SLASH.split(next.getName()).length - 1]
.contains("$"))
: (!next.getName().contains("$")))
|| (next.getName().startsWith("$") || next.getName().contains("$$")
|| next.getName().endsWith("$"))) {
paths.add(getFullName(node.name));
}
} catch (Exception e) {
OTHER_FILES.put(next.getName(), data);
}
} else {
OTHER_FILES.put(next.getName(), data);
}
}
jar.close();
refresh();
} catch (Throwable e) {
classNodes.clear();
OTHER_FILES.clear();
e.printStackTrace();
showError(e);
}
}
private int getSlashCount(String s) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '/')
count++;
}
return count;
}
public void refresh() {
setRoot(new ByteEditTreeNode(((ByteEditTreeNode) getRoot()).toString()));
Collections.sort(paths, String.CASE_INSENSITIVE_ORDER);
Collections.sort(paths, new Comparator<String>() {
public int compare(String s1, String s2) {
return getSlashCount(s2) - getSlashCount(s1);
}
});
// Windows-like sorting (like file explorer)
List<String> tmp = new ArrayList<>(paths);
List<String> folders = tmp.stream().filter(s -> {
int c = countSlashes(s, 50);
return c > 0 && c < 50;
}).map(s -> {
int idx = s.lastIndexOf('/');
if (idx == s.length() - 1)
idx = s.lastIndexOf('/', idx - 1);
if (idx == -1)
return s;
return s.substring(0, idx);
}).distinct().sorted(new Comparator<String>() {
@Override
public int compare(String s, String s2) {
// From String#compareToIgnoreCase(String)
int n = s.length();
int n2 = s2.length();
int n3 = Math.min(n, n2);
for (int i = 0; i < n3; ++i) {
char c;
char c2 = s.charAt(i);
if (c2 == (c = s2.charAt(i))
|| (c2 = Character.toUpperCase(c2)) == (c = Character.toUpperCase(c))
|| (c2 = Character.toLowerCase(c2)) == (c = Character.toLowerCase(c)))
continue;
return c2 - c;
}
// Other way around
return n2 - n;
}
}).collect(Collectors.toList());
tmp.removeAll(folders);
tmp.addAll(0, folders);
for (String s : tmp) {
String[] elements = SLASH.split(s, 50);
if (elements.length >= 50) {
((ByteEditTreeNode) getRoot()).add(new ByteEditTreeNode(s, s));
} else {
ByteEditTreeNode currentNode = (ByteEditTreeNode) getRoot();
for (int i = 0; i < elements.length; i++) {
String token = elements[i];
ByteEditTreeNode nextNode = findNode(currentNode, token);
if (nextNode == null) {
nextNode = new ByteEditTreeNode(token, s);
currentNode.add(nextNode);
}
currentNode = nextNode;
}
}
}
}
private ByteEditTreeNode findNode(ByteEditTreeNode parent, String name) {
Enumeration<?> e = parent.children();
while (e.hasMoreElements()) {
ByteEditTreeNode element = (ByteEditTreeNode) e.nextElement();
if (element.getUserObject().equals(name)) {
return element;
}
}
return null;
}
private int countSlashes(String s, int max) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '/') {
count++;
if (count == max)
break;
}
}
return count;
}
}
private static class FurtherExpandingTreeExpansionListener implements TreeExpansionListener {
public void treeExpanded(TreeExpansionEvent event) {
TreePath treePath = event.getPath();
Object expandedTreePathObject = treePath.getLastPathComponent();
if (!(expandedTreePathObject instanceof TreeNode))
return;
TreeNode expandedTreeNode = (TreeNode) expandedTreePathObject;
if (expandedTreeNode.getChildCount() == 1) {
TreeNode descendantTreeNode = expandedTreeNode.getChildAt(0);
if (descendantTreeNode.isLeaf())
return;
TreePath nextTreePath = treePath.pathByAddingChild(descendantTreeNode);
tree.expandPath(nextTreePath);
}
}
public void treeCollapsed(TreeExpansionEvent event) {
}
}
public static byte[] toByteArray(final InputStream input) throws IOException {
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int read;
while ((read = input.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
return output.toByteArray();
}
}
}
+14 -14
View File
@@ -6,6 +6,8 @@ import java.util.HashSet;
import org.objectweb.asm.Type;
import me.ByteEdit.edit.HugeStrings;
public class ClassUtil {
public static final int ACC_PUBLIC = 0x0001;
@@ -318,12 +320,10 @@ public class ClassUtil {
s += "enum ";
acc ^= ACC_ENUM;
}
if (acc == access) {
if (acc == access)
return s;
} else {
return "0x" + Integer.toHexString(access) + " ";
}
}
/**
* @return { JavaType, Import }
@@ -525,28 +525,28 @@ public class ClassUtil {
public static final HashMap<Class, DecompilationInterface> diMap = new HashMap<>();
static {
diMap.put(String.class, (o, desc, escapeSpaces) -> "\""
+ (escapeSpaces ? UnicodeUtils.escapeWithSpaces((String) o) : UnicodeUtils.escape((String) o)) + "\"");
diMap.put(String.class, (o, desc, escapeSpaces, hs) -> "\""
+ (escapeSpaces ? UnicodeUtils.escapeWithSpaces(hs, (String) o) : UnicodeUtils.escape(hs, (String) o))
+ "\"");
diMap.put(Integer.class,
(o, desc, escape) -> desc.equals("Z") ? (((int) o) == 0 ? "false" : "true") : o.toString());
diMap.put(Long.class, (o, desc, escape) -> o.toString() + "l");
diMap.put(Float.class, (o, desc, escape) -> o.toString() + "f");
(o, desc, escape, hs) -> desc.equals("Z") ? (((int) o) == 0 ? "false" : "true") : o.toString());
diMap.put(Long.class, (o, desc, escape, hs) -> o.toString() + "l");
diMap.put(Float.class, (o, desc, escape, hs) -> o.toString() + "f");
}
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());
if (di != null)
return di.apply(o, desc, escapeSpaces);
else
return di.apply(o, desc, escapeSpaces, hs);
return o.toString();
}
public static String getDecompiledValue(Object o, String desc) {
return getDecompiledValue(o, desc, false);
public static String getDecompiledValue(Object o, String desc, HugeStrings hs) {
return getDecompiledValue(o, desc, false, hs);
}
public static Object getCastedValue(String o, String className) {
+41 -8
View File
@@ -1,11 +1,26 @@
package me.ByteEdit.utils;
import me.ByteEdit.edit.HugeStrings;
import me.ByteEdit.edit.HugeStringsRev;
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) {
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();
char unicodeBuffer = 0;
int stage = 0;
@@ -69,19 +84,20 @@ public class UnicodeUtils {
sb.append(c);
}
if (stage != 0) {
if (unicodeBuffer != 0) {
if (unicodeBuffer != 0)
throw new IllegalArgumentException("The unicode at the end is incomplete.");
} else {
throw new IllegalArgumentException("'\\' at the end is invalid.");
}
}
return sb.toString();
}
public static String escapeWithSpaces(String s) {
public static String escapeWithSpaces(HugeStrings hs, String s) {
if (s == null) {
return null;
}
if (s.length() > HugeStrings.THRESHOLD) {
return hs.onString(s);
}
StringBuilder sb = new StringBuilder();
char[] arr = s.toCharArray();
int i = 0;
@@ -92,6 +108,9 @@ public class UnicodeUtils {
if (arr[0] == '/') {
sb.append("\\u002F");
i++;
} else if (arr[0] == '#') {
sb.append("\\u0023");
i++;
}
if (arr[len - 1] == '/') {
addSlash = true;
@@ -158,12 +177,26 @@ public class UnicodeUtils {
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) {
return null;
}
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
if (!force && s.length() > HugeStrings.THRESHOLD) {
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) {
case 'Ä':
case 'ä':
+1 -1
View File
@@ -12,7 +12,7 @@
<baseFont size="12"/>
<!-- General editor colors. -->
<background color="ffffff"/>
<background color="c0c0c0"/>
<caret color="000000"/>
<selection bg="dddddd" fg="000000"/>
<currentLineHighlight color="dddddd" fade="false"/>
+13 -13
View File
@@ -12,10 +12,10 @@
<baseFont size="12"/>
<!-- General editor colors. -->
<background color="000000"/>
<background color="2f2f2f"/>
<caret color="eeeeee"/>
<selection bg="222222" fg="eeeeee"/>
<currentLineHighlight color="222222" fade="false"/>
<selection bg="555555" fg="eeeeee"/>
<currentLineHighlight color="111111" fade="false"/>
<marginLine fg="4f4b46"/>
<markAllHighlight color="947e76"/> <!-- TODO: Fix me -->
<markOccurrencesHighlight color="2b2b2b" border="false"/>
@@ -36,23 +36,23 @@
<!-- Syntax tokens. -->
<tokenStyles>
<style token="IDENTIFIER" fg="eeeeee"/>
<style token="RESERVED_WORD" fg="dd02d6" bold="true"/>
<style token="RESERVED_WORD_2" fg="dd02d6" bold="true"/>
<style token="RESERVED_WORD" fg="cc6c1d" bold="true"/>
<style token="RESERVED_WORD_2" fg="cc6c1d" bold="true"/>
<style token="ANNOTATION" fg="808080"/>
<style token="COMMENT_DOCUMENTATION" fg="279627"/>
<style token="COMMENT_EOL" fg="279627"/>
<style token="COMMENT_MULTILINE" fg="279627"/>
<style token="COMMENT_KEYWORD" fg="806040" bold="true"/>
<style token="COMMENT_MARKUP" fg="808060"/>
<style token="DATA_TYPE" fg="dd02d6" bold="true"/>
<style token="DATA_TYPE" fg="cc6c1d"/>
<style token="FUNCTION" fg="eeeeee"/>
<style token="LITERAL_BOOLEAN" fg="dd02d6" bold="true"/>
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="eeeeee"/>
<style token="LITERAL_NUMBER_FLOAT" fg="eeeeee"/>
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="eeeeee"/>
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="00aeff"/>
<style token="LITERAL_CHAR" fg="00aeff"/>
<style token="LITERAL_BACKQUOTE" fg="00aeff"/>
<style token="LITERAL_BOOLEAN" fg="cc6c1d"/>
<style token="LITERAL_NUMBER_DECIMAL_INT" fg="6897bb"/>
<style token="LITERAL_NUMBER_FLOAT" fg="6897bb"/>
<style token="LITERAL_NUMBER_HEXADECIMAL" fg="6897bb"/>
<style token="LITERAL_STRING_DOUBLE_QUOTE" fg="17c6a3"/>
<style token="LITERAL_CHAR" fg="17c6a3"/>
<style token="LITERAL_BACKQUOTE" fg="17c6a3"/>
<style token="MARKUP_TAG_DELIMITER" fg="008080"/>
<style token="MARKUP_TAG_NAME" fg="3f7f7f"/>
<style token="MARKUP_TAG_ATTRIBUTE" fg="7f007f"/>
+26 -34
View File
@@ -41,8 +41,7 @@ public class DocumentReader extends Reader {
/**
* Constructor.
*
* @param document
* The document we're 'reading'.
* @param document The document we're 'reading'.
*/
public DocumentReader(Document document) {
position = 0;
@@ -55,14 +54,14 @@ public class DocumentReader extends Reader {
* This currently does nothing...
*/
@Override
public void close() {}
public void close() {
}
/**
* Marks the present position in the stream. Subsequent calls to
* <code>reset()</code> will reposition the stream to this point.
*
* @param readAheadLimit
* Ignored.
* @param readAheadLimit Ignored.
*/
@Override
public void mark(int readAheadLimit) {
@@ -70,8 +69,8 @@ public class DocumentReader extends Reader {
}
/**
* Tells whether this reader supports the <code>mark</code> operation.
* This always returns <code>true</code> for <code>DocumentReader</code>.
* Tells whether this reader supports the <code>mark</code> operation. This
* always returns <code>true</code> for <code>DocumentReader</code>.
*/
@Override
public boolean markSupported() {
@@ -98,11 +97,10 @@ public class DocumentReader extends Reader {
}
/**
* Read <code>array.length</code> characters from the beginning
* of the document into <code>array</code>.
* Read <code>array.length</code> characters from the beginning of the document
* into <code>array</code>.
*
* @param array
* The array to read characters into.
* @param array The array to read characters into.
* @return The number of characters read.
*/
@Override
@@ -113,14 +111,11 @@ public class DocumentReader extends Reader {
/**
* Reads characters into a portion of an array.
*
* @param cbuf
* The destination buffer.
* @param off
* Offset at which to start storing characters.
* @param len
* Maximum number of characters to read.
* @return The number of characters read, or <code>-1</code> if the
* end of the stream (document) has been reached.
* @param cbuf The destination buffer.
* @param off Offset at which to start storing characters.
* @param len Maximum number of characters to read.
* @return The number of characters read, or <code>-1</code> if the end of the
* stream (document) has been reached.
*/
@Override
public int read(char[] cbuf, int off, int len) {
@@ -147,12 +142,11 @@ public class DocumentReader extends Reader {
}
/**
* Tells whether this reader is ready to be read without
* blocking for input. <code>DocumentReader</code> will
* always return true.
* Tells whether this reader is ready to be read without blocking for input.
* <code>DocumentReader</code> will always return true.
*
* @return <code>true</code> if the next read operation will
* return without blocking.
* @return <code>true</code> if the next read operation will return without
* blocking.
*/
@Override
public boolean ready() {
@@ -160,9 +154,9 @@ public class DocumentReader extends Reader {
}
/**
* Resets the stream. If the stream has been marked, then attempt to
* reposition it at the mark. If the stream has not been marked, then
* move it to the beginning of the document.
* Resets the stream. If the stream has been marked, then attempt to reposition
* it at the mark. If the stream has not been marked, then move it to the
* beginning of the document.
*/
@Override
public void reset() {
@@ -177,8 +171,7 @@ public class DocumentReader extends Reader {
/**
* Skips characters. This will not 'skip' past the end of the document.
*
* @param n
* The number of characters to skip.
* @param n The number of characters to skip.
* @return The number of characters actually skipped.
*/
@Override
@@ -193,12 +186,11 @@ public class DocumentReader extends Reader {
}
/**
* Move to the specified position in the document. If <code>pos</code>
* is greater than the document's length, the stream's position is moved
* to the end of the document.
* Move to the specified position in the document. If <code>pos</code> is
* greater than the document's length, the stream's position is moved to the end
* of the document.
*
* @param pos
* The position in the document to move to.
* @param pos The position in the document to move to.
*/
public void seek(long pos) {
position = Math.min(pos, document.getLength());
+34 -60
View File
@@ -30,10 +30,9 @@ import java.io.Writer;
public class UnicodeWriter extends Writer {
/**
* If this system property evaluates to "<code>false</code>", ignoring
* case, files written out as UTF-8 will not have a BOM written for them.
* Otherwise (even if the property is not set), UTF-8 files will have a
* BOM written.
* If this system property evaluates to "<code>false</code>", ignoring case,
* files written out as UTF-8 will not have a BOM written for them. Otherwise
* (even if the property is not set), UTF-8 files will have a BOM written.
*/
public static final String PROPERTY_WRITE_UTF8_BOM = "UnicodeWriter.writeUtf8BOM";
/**
@@ -47,30 +46,24 @@ public class UnicodeWriter extends Writer {
private static final byte[] UTF32BE_BOM = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0xFE, (byte) 0xFF };
/**
* This is a utility constructor since the vast majority of the time, this
* class will be used to write Unicode files.
* This is a utility constructor since the vast majority of the time, this class
* will be used to write Unicode files.
*
* @param fileName
* The file to which to write the Unicode output.
* @param encoding
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
* @param fileName The file to which to write the Unicode output.
* @param encoding The encoding to use.
* @throws IOException If an IO exception occurs.
*/
public UnicodeWriter(String fileName, String encoding) throws IOException {
this(new FileOutputStream(fileName), encoding);
}
/**
* This is a utility constructor since the vast majority of the time, this
* class will be used to write Unicode files.
* This is a utility constructor since the vast majority of the time, this class
* will be used to write Unicode files.
*
* @param file
* The file to which to write the Unicode output.
* @param encoding
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
* @param file The file to which to write the Unicode output.
* @param encoding The encoding to use.
* @throws IOException If an IO exception occurs.
*/
public UnicodeWriter(File file, String encoding) throws IOException {
this(new FileOutputStream(file), encoding);
@@ -79,12 +72,9 @@ public class UnicodeWriter extends Writer {
/**
* Creates a new writer.
*
* @param out
* The output stream to write.
* @param encoding
* The encoding to use.
* @throws IOException
* If an IO exception occurs.
* @param out The output stream to write.
* @param encoding The encoding to use.
* @throws IOException If an IO exception occurs.
*/
public UnicodeWriter(OutputStream out, String encoding) throws IOException {
init(out, encoding);
@@ -93,8 +83,7 @@ public class UnicodeWriter extends Writer {
/**
* Closes this writer.
*
* @throws IOException
* If an IO exception occurs.
* @throws IOException If an IO exception occurs.
*/
@Override
public void close() throws IOException {
@@ -104,8 +93,7 @@ public class UnicodeWriter extends Writer {
/**
* Flushes the stream.
*
* @throws IOException
* If an IO exception occurs.
* @throws IOException If an IO exception occurs.
*/
@Override
public void flush() throws IOException {
@@ -139,15 +127,12 @@ public class UnicodeWriter extends Writer {
}
/**
* Initializes the internal output stream and writes the BOM if the
* specified encoding is a Unicode encoding.
* Initializes the internal output stream and writes the BOM if the specified
* encoding is a Unicode encoding.
*
* @param out
* The output stream we are writing.
* @param encoding
* The encoding in which to write.
* @throws IOException
* If an I/O error occurs while writing a BOM.
* @param out The output stream we are writing.
* @param encoding The encoding in which to write.
* @throws IOException If an I/O error occurs while writing a BOM.
*/
private void init(OutputStream out, String encoding) throws IOException {
internalOut = new OutputStreamWriter(out, encoding);
@@ -173,8 +158,7 @@ public class UnicodeWriter extends Writer {
/**
* Sets whether UTF-8 files should have a BOM written in them.
*
* @param write
* Whether to write a BOM.
* @param write Whether to write a BOM.
* @see #getWriteUtf8BOM()
* @see UnicodeWriter
*/
@@ -185,14 +169,10 @@ public class UnicodeWriter extends Writer {
/**
* Writes a portion of an array of characters.
*
* @param cbuf
* The buffer of characters.
* @param off
* The offset from which to start writing characters.
* @param len
* The number of characters to write.
* @throws IOException
* If an I/O error occurs.
* @param cbuf The buffer of characters.
* @param off The offset from which to start writing characters.
* @param len The number of characters to write.
* @throws IOException If an I/O error occurs.
*/
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
@@ -202,10 +182,8 @@ public class UnicodeWriter extends Writer {
/**
* Writes a single character.
*
* @param c
* An integer specifying the character to write.
* @throws IOException
* If an IO error occurs.
* @param c An integer specifying the character to write.
* @throws IOException If an IO error occurs.
*/
@Override
public void write(int c) throws IOException {
@@ -215,14 +193,10 @@ public class UnicodeWriter extends Writer {
/**
* Writes a portion of a string.
*
* @param str
* The string from which to write.
* @param off
* The offset from which to start writing characters.
* @param len
* The number of characters to write.
* @throws IOException
* If an IO error occurs.
* @param str The string from which to write.
* @param off The offset from which to start writing characters.
* @param len The number of characters to write.
* @throws IOException If an IO error occurs.
*/
@Override
public void write(String str, int off, int len) throws IOException {
+55 -78
View File
@@ -55,24 +55,18 @@ public abstract class RPrintUtilities {
/**
* Returns the position closest to, but before, position
* <code>maxCharsPerLine</code> in
* <code>line</code> of one of the chars in <code>breakChars</code>, or
* simply returns
* <code>maxCharsPerLine-1</code> if none of the <code>breakChars</code>
* comes before
* that position. This position represents the logical line break for this
* <code>java.lang.String</code>
* if it is being printed in a monospaced font when lines can only be
* <code>maxCharsPerLine</code>
* characters long.
* <code>maxCharsPerLine</code> in <code>line</code> of one of the chars in
* <code>breakChars</code>, or simply returns <code>maxCharsPerLine-1</code> if
* none of the <code>breakChars</code> comes before that position. This position
* represents the logical line break for this <code>java.lang.String</code> if
* it is being printed in a monospaced font when lines can only be
* <code>maxCharsPerLine</code> characters long.
*
* @param line
* The text being printed.
* @param maxCharsPerLine
* Only up-to this many characters from
* <code>line</code> can be printed on one line.
* @return The logical position at which to stop printing <code>line</code>
* to simulate word wrap.
* @param line The text being printed.
* @param maxCharsPerLine Only up-to this many characters from <code>line</code>
* can be printed on one line.
* @return The logical position at which to stop printing <code>line</code> to
* simulate word wrap.
*/
private static int getLineBreakPoint(String line, final int maxCharsPerLine) {
int breakPoint = -1;
@@ -87,27 +81,20 @@ public abstract class RPrintUtilities {
/**
* Prints a <code>Document</code> using a monospaced font, and does no word
* wrapping (ie,
* words will wrap mid-word to the next line). This method is expected to be
* called from
* Printable 'print(Graphics g)' functions.
* wrapping (ie, words will wrap mid-word to the next line). This method is
* expected to be called from Printable 'print(Graphics g)' functions.
*
* @param g
* The graphics context to write to.
* @param doc
* The <code>javax.swing.text.Document</code> to print.
* @param fontSize
* the point size to use for the monospaced font.
* @param pageIndex
* The page number to print.
* @param pageFormat
* The format to print the page with.
* @param tabSize
* The number of spaces to expand tabs to.
* @param g The graphics context to write to.
* @param doc The <code>javax.swing.text.Document</code> to print.
* @param fontSize the point size to use for the monospaced font.
* @param pageIndex The page number to print.
* @param pageFormat The format to print the page with.
* @param tabSize The number of spaces to expand tabs to.
*
* @see #printDocumentMonospacedWordWrap
*/
public static int printDocumentMonospaced(Graphics g, Document doc, int fontSize, int pageIndex, PageFormat pageFormat, int tabSize) {
public static int printDocumentMonospaced(Graphics g, Document doc, int fontSize, int pageIndex,
PageFormat pageFormat, int tabSize) {
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
// Initialize our static variables (these are used by our tab expander
@@ -204,27 +191,21 @@ public abstract class RPrintUtilities {
}
/**
* Prints a <code>Document</code> using a monospaced font, word wrapping on
* the characters ' ', '\t', '\n', ',', '.', and ';'. This method is
* expected to be called from Printable 'print(Graphics g)' functions.
* Prints a <code>Document</code> using a monospaced font, word wrapping on the
* characters ' ', '\t', '\n', ',', '.', and ';'. This method is expected to be
* called from Printable 'print(Graphics g)' functions.
*
* @param g
* The graphics context to write to.
* @param doc
* The <code>javax.swing.text.Document</code> to print.
* @param fontSize
* the point size to use for the monospaced font.
* @param pageIndex
* The page number to print.
* @param pageFormat
* The format to print the page with.
* @param tabSize
* The number of spaces to expand tabs to.
* @param g The graphics context to write to.
* @param doc The <code>javax.swing.text.Document</code> to print.
* @param fontSize the point size to use for the monospaced font.
* @param pageIndex The page number to print.
* @param pageFormat The format to print the page with.
* @param tabSize The number of spaces to expand tabs to.
*
* @see #printDocumentMonospaced
*/
public static int printDocumentMonospacedWordWrap(Graphics g, Document doc, int fontSize, int pageIndex, PageFormat pageFormat,
int tabSize) {
public static int printDocumentMonospacedWordWrap(Graphics g, Document doc, int fontSize, int pageIndex,
PageFormat pageFormat, int tabSize) {
g.setColor(Color.BLACK);
g.setFont(new Font("Monospaced", Font.PLAIN, fontSize));
// Initialize our static variables (these are used by our tab expander
@@ -322,28 +303,22 @@ public abstract class RPrintUtilities {
}
/**
* Prints a <code>Document</code> using the specified font, word wrapping
* on the characters ' ', '\t', '\n', ',', '.', and ';'. This method is
* expected to be called from Printable 'print(Graphics g)' functions.
* Prints a <code>Document</code> using the specified font, word wrapping on the
* characters ' ', '\t', '\n', ',', '.', and ';'. This method is expected to be
* called from Printable 'print(Graphics g)' functions.
*
* @param g
* The graphics context to write to.
* @param textComponent
* The <code>javax.swing.text.JTextComponent</code>
* whose text you're printing.
* @param font
* The font to use for printing. If <code>null</code>, then
* @param g The graphics context to write to.
* @param textComponent The <code>javax.swing.text.JTextComponent</code> whose
* text you're printing.
* @param font The font to use for printing. If <code>null</code>, then
* <code>textComponent</code>'s font is used.
* @param pageIndex
* The page number to print.
* @param pageFormat
* The format to print the page with.
* @param tabSize
* The number of spaces to convert tabs to.
* @param pageIndex The page number to print.
* @param pageFormat The format to print the page with.
* @param tabSize The number of spaces to convert tabs to.
*
*/
public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent, Font font, int pageIndex, PageFormat pageFormat,
int tabSize) {
public static int printDocumentWordWrap(Graphics g, JTextComponent textComponent, Font font, int pageIndex,
PageFormat pageFormat, int tabSize) {
// Initialize our graphics object.
g.setColor(Color.BLACK);
g.setFont(font != null ? font : textComponent.getFont());
@@ -389,7 +364,8 @@ public abstract class RPrintUtilities {
// we'd start after the part
// that has already been printed).
try {
doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset), currentLineSeg);
doc.getText(currentLineStart + startingOffset, currentLineEnd - (currentLineStart + startingOffset),
currentLineSeg);
} catch (BadLocationException ble) {
System.err.println("BadLocationException in print (where there shouldn't be one!): " + ble);
return Printable.NO_SUCH_PAGE;
@@ -452,7 +428,8 @@ public abstract class RPrintUtilities {
System.err.println(ble);
return Printable.NO_SUCH_PAGE;
}
currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander, 0);
currentLineLengthInPixels = Utilities.getTabbedTextWidth(currentLineSeg, fm, 0, tabExpander,
0);
} while (currentLineLengthInPixels <= lineLengthInPixels);
currentPos--;
}
@@ -460,8 +437,8 @@ public abstract class RPrintUtilities {
doc.getText((currentLineStart + startingOffset), currentPos, currentLineSeg);
} catch (BadLocationException ble) {
System.err.println("BadLocationException in print (a):");
System.err.println("==> currentLineStart: " + currentLineStart + "; startingOffset: " + startingOffset
+ "; currentPos: " + currentPos);
System.err.println("==> currentLineStart: " + currentLineStart + "; startingOffset: "
+ startingOffset + "; currentPos: " + currentPos);
System.err.println("==> Range: " + (currentLineStart + startingOffset) + " - "
+ (currentLineStart + startingOffset + currentPos));
ble.printStackTrace();
@@ -495,8 +472,7 @@ public abstract class RPrintUtilities {
/**
* Removes any spaces or tabs from the end of the segment.
*
* @param segment
* The segment from which to remove tailing whitespace.
* @param segment The segment from which to remove tailing whitespace.
* @return <code>segment</code> with trailing whitespace removed.
*/
private static Segment removeEndingWhitespace(Segment segment) {
@@ -512,12 +488,13 @@ public abstract class RPrintUtilities {
}
/**
* A tab expander for the document currently being printed with the
* font being used for the printing.
* A tab expander for the document currently being printed with the font being
* used for the printing.
*/
private static class RPrintTabExpander implements TabExpander {
RPrintTabExpander() {}
RPrintTabExpander() {
}
@Override
public float nextTabStop(float x, int tabOffset) {
@@ -17,11 +17,11 @@ import javax.swing.text.JTextComponent;
* lexicographically (ignoring case).
* <p>
*
* This implementation assumes the input text and replacement text are the
* same value. It also returns the input text from its {@link #toString()}
* method (which is what <code>DefaultListCellRenderer</code> uses to render
* objects). Subclasses that wish to override any of this behavior can simply
* override the corresponding method(s) needed to do so.
* This implementation assumes the input text and replacement text are the same
* value. It also returns the input text from its {@link #toString()} method
* (which is what <code>DefaultListCellRenderer</code> uses to render objects).
* Subclasses that wish to override any of this behavior can simply override the
* corresponding method(s) needed to do so.
*
* @author Robert Futrell
* @version 1.0
@@ -38,17 +38,16 @@ public abstract class AbstractCompletion implements Completion {
private Icon icon;
/**
* The relevance of this completion. Completion instances with higher
* "relevance" values are inserted higher into the list of possible
* completions than those with lower values. Completion instances with
* equal relevance values are sorted alphabetically.
* "relevance" values are inserted higher into the list of possible completions
* than those with lower values. Completion instances with equal relevance
* values are sorted alphabetically.
*/
private int relevance;
/**
* Constructor.
*
* @param provider
* The provider that created this completion.
* @param provider The provider that created this completion.
*/
protected AbstractCompletion(CompletionProvider provider) {
this.provider = provider;
@@ -57,10 +56,8 @@ public abstract class AbstractCompletion implements Completion {
/**
* Constructor.
*
* @param provider
* The provider that created this completion.
* @param icon
* The icon for this completion.
* @param provider The provider that created this completion.
* @param icon The icon for this completion.
*/
protected AbstractCompletion(CompletionProvider provider, Icon icon) {
this(provider);
@@ -97,8 +94,8 @@ public abstract class AbstractCompletion implements Completion {
}
/**
* Returns the text the user has to (start) typing for this completion
* to be offered. The default implementation simply returns
* Returns the text the user has to (start) typing for this completion to be
* offered. The default implementation simply returns
* {@link #getReplacementText()}.
*
* @return The text the user has to (start) typing for this completion.
@@ -126,8 +123,8 @@ public abstract class AbstractCompletion implements Completion {
}
/**
* The default implementation returns <code>null</code>. Subclasses
* can override this method.
* The default implementation returns <code>null</code>. Subclasses can override
* this method.
*
* @return The tool tip text.
*/
@@ -139,8 +136,7 @@ public abstract class AbstractCompletion implements Completion {
/**
* Sets the icon to use for this completion.
*
* @param icon
* The icon to use.
* @param icon The icon to use.
* @see #getIcon()
*/
public void setIcon(Icon icon) {
@@ -150,8 +146,7 @@ public abstract class AbstractCompletion implements Completion {
/**
* Sets the relevance of this completion.
*
* @param relevance
* The new relevance of this completion.
* @param relevance The new relevance of this completion.
* @see #getRelevance()
*/
public void setRelevance(int relevance) {
@@ -43,8 +43,8 @@ import javax.swing.event.HyperlinkListener;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* The optional "description" window that describes the currently selected
* item in the auto-completion window.
* The optional "description" window that describes the currently selected item
* in the auto-completion window.
*
* @author Robert Futrell
* @version 1.0
@@ -89,9 +89,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
private int historyPos;
/**
* Provides a slight delay between asking to set a description and actually
* displaying it, so that if the user is scrolling quickly through
* completions, those with slow-to-calculate summaries won't bog down the
* scrolling.
* displaying it, so that if the user is scrolling quickly through completions,
* those with slow-to-calculate summaries won't bog down the scrolling.
*/
private Timer timer;
/**
@@ -103,10 +102,9 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
*/
private ResourceBundle bundle;
/**
* The amount of time to wait after the user changes the selected
* completion to refresh the description. This delay is in place to help
* performance for {@link Completion}s that may be slow to compute their
* summary text.
* The amount of time to wait after the user changes the selected completion to
* refresh the description. This delay is in place to help performance for
* {@link Completion}s that may be slow to compute their summary text.
*/
private static final int INITIAL_TIMER_DELAY = 120;
/**
@@ -117,10 +115,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Constructor.
*
* @param owner
* The parent window.
* @param ac
* The parent auto-completion.
* @param owner The parent window.
* @param ac The parent auto-completion.
*/
public AutoCompleteDescWindow(Window owner, AutoCompletion ac) {
super(owner);
@@ -183,8 +179,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Sets the currently displayed description and updates the history.
*
* @param historyItem
* The item to add to the history.
* @param historyItem The item to add to the history.
*/
private void addToHistory(HistoryEntry historyItem) {
history.add(++historyPos, historyItem);
@@ -214,8 +209,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
/**
* Copies from the description text area, if it is visible and there is
* a selection.
* Copies from the description text area, if it is visible and there is a
* selection.
*
* @return Whether a copy occurred.
*/
@@ -230,8 +225,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Returns the localized message for the specified key.
*
* @param key
* The key.
* @param key The key.
* @return The localized message.
*/
private String getString(String key) {
@@ -244,8 +238,7 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Called when a hyperlink is clicked.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
@@ -261,7 +254,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
URL newUrl = redirector.possiblyRedirect(url);
if (newUrl != null && newUrl != url) {
url = newUrl;
e = new HyperlinkEvent(e.getSource(), e.getEventType(), newUrl, e.getDescription(), e.getSourceElement());
e = new HyperlinkEvent(e.getSource(), e.getEventType(), newUrl, e.getDescription(),
e.getSourceElement());
}
}
}
@@ -325,11 +319,9 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
/**
* Sets the description displayed in this window. This clears the
* history.
* Sets the description displayed in this window. This clears the history.
*
* @param item
* The item whose description you want to display.
* @param item The item whose description you want to display.
*/
public void setDescriptionFor(Completion item) {
setDescriptionFor(item, false);
@@ -338,11 +330,9 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Sets the description displayed in this window.
*
* @param item
* The item whose description you want to display.
* @param addToHistory
* Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
* @param item The item whose description you want to display.
* @param addToHistory Whether to add this page to the page history (as opposed
* to clearing it and starting anew).
*/
protected void setDescriptionFor(Completion item, boolean addToHistory) {
setDescriptionFor(item, null, addToHistory);
@@ -351,12 +341,10 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Sets the description displayed in this window.
*
* @param item
* The item whose description you want to display.
* @param item The item whose description you want to display.
* @parma anchor The anchor to jump to, or <code>null</code> if none.
* @param addToHistory
* Whether to add this page to the page history
* (as opposed to clearing it and starting anew).
* @param addToHistory Whether to add this page to the page history (as opposed
* to clearing it and starting anew).
*/
protected void setDescriptionFor(Completion item, String anchor, boolean addToHistory) {
timer.stop();
@@ -403,11 +391,9 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
/**
* Callback for custom <code>ExternalURLHandler</code>s.
*
* @param completion
* The completion to display.
* @param anchor
* The anchor in the HTML to jump to, or <code>null</code>
* if none.
* @param completion The completion to display.
* @param anchor The anchor in the HTML to jump to, or <code>null</code> if
* none.
*/
@Override
public void showSummaryFor(Completion completion, String anchor) {
@@ -442,8 +428,8 @@ class AutoCompleteDescWindow extends JWindow implements HyperlinkListener, DescW
}
/**
* Overridden to display a short name for the completion, since it's
* used in the tool tips for the "back" and "forward" buttons.
* Overridden to display a short name for the completion, since it's used in the
* tool tips for the "back" and "forward" buttons.
*
* @return A string representation of this history entry.
*/
@@ -43,10 +43,9 @@ import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* The actual popup window of choices. When visible, this window intercepts
* certain keystrokes in the parent text component and uses them to navigate
* the completion choices instead. If Enter or Escape is pressed, the window
* hides itself and notifies the {@link AutoCompletion} to insert the selected
* text.
* certain keystrokes in the parent text component and uses them to navigate the
* completion choices instead. If Enter or Escape is pressed, the window hides
* itself and notifies the {@link AutoCompletion} to insert the selected text.
*
* @author Robert Futrell
* @version 1.0
@@ -66,27 +65,26 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
*/
private CompletionListModel model;
/**
* A hack to work around the fact that we clear our completion model (and
* our selection) when hiding the completion window. This allows us to
* still know what the user selected after the popup is hidden.
* A hack to work around the fact that we clear our completion model (and our
* selection) when hiding the completion window. This allows us to still know
* what the user selected after the popup is hidden.
*/
private Completion lastSelection;
/**
* Optional popup window containing a description of the currently
* selected completion.
* Optional popup window containing a description of the currently selected
* completion.
*/
private AutoCompleteDescWindow descWindow;
/**
* The preferred size of the optional description window. This field
* only exists because the user may (and usually will) set the size of
* the description window before it exists (it must be parented to a
* Window).
* The preferred size of the optional description window. This field only exists
* because the user may (and usually will) set the size of the description
* window before it exists (it must be parented to a Window).
*/
private Dimension preferredDescWindowSize;
/**
* Whether the completion window and the optional description window
* should be displayed above the current caret position (as opposed to
* underneath it, which is preferred unless there is not enough space).
* Whether the completion window and the optional description window should be
* displayed above the current caret position (as opposed to underneath it,
* which is preferred unless there is not enough space).
*/
private boolean aboveCaret;
private int lastLine;
@@ -103,7 +101,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
private KeyActionPair pageUpKap;
private KeyActionPair pageDownKap;
private KeyActionPair ctrlCKap;
private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight, oldEnter, oldTab, oldHome, oldEnd, oldPageUp, oldPageDown, oldCtrlC;
private KeyActionPair oldEscape, oldUp, oldDown, oldLeft, oldRight, oldEnter, oldTab, oldHome, oldEnd, oldPageUp,
oldPageDown, oldCtrlC;
/**
* The space between the caret and the completion popup.
*/
@@ -116,10 +115,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
/**
* Constructor.
*
* @param parent
* The parent window (hosting the text component).
* @param ac
* The auto-completion instance.
* @param parent The parent window (hosting the text component).
* @param ac The auto-completion instance.
*/
public AutoCompletePopupWindow(Window parent, final AutoCompletion ac) {
super(parent);
@@ -131,7 +128,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
list.addListSelectionListener(this);
list.addMouseListener(this);
JPanel contentPane = new JPanel(new BorderLayout());
JScrollPane sp = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
JScrollPane sp = new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
// In 1.4, JScrollPane.setCorner() has a bug where it won't accept
// JScrollPane.LOWER_TRAILING_CORNER, even though that constant is
// defined. So we have to put the logic added in 1.5 to handle it
@@ -188,8 +186,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Creates the mappings from keys to Actions we'll be putting into the
* text component's ActionMap and InputMap.
* Creates the mappings from keys to Actions we'll be putting into the text
* component's ActionMap and InputMap.
*/
private void createKeyActionPairs() {
// Actions we'll install.
@@ -237,8 +235,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Returns the default list cell renderer used when a completion provider
* does not supply its own.
* Returns the default list cell renderer used when a completion provider does
* not supply its own.
*
* @return The default list cell renderer.
* @see #setListCellRenderer(ListCellRenderer)
@@ -268,8 +266,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Registers keyboard actions to listen for in the text component and
* intercept.
* Registers keyboard actions to listen for in the text component and intercept.
*
* @see #uninstallKeyBindings()
*/
@@ -322,21 +319,25 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
@Override
public void mouseEntered(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {}
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {
}
/**
* Positions the description window relative to the completion choices
* window. We assume there is room on one side of the other for this
* entire window to fit.
* Positions the description window relative to the completion choices window.
* We assume there is room on one side of the other for this entire window to
* fit.
*/
private void positionDescWindow() {
boolean showDescWindow = descWindow != null && ac.getShowDescWindow();
@@ -374,19 +375,14 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* "Puts back" the original key/Action pair for a keystroke. This is used
* when this popup is hidden.
* "Puts back" the original key/Action pair for a keystroke. This is used when
* this popup is hidden.
*
* @param im
* The input map.
* @param am
* The action map.
* @param key
* The keystroke whose key/Action pair to change.
* @param kap
* The (original) key/Action pair.
* @see #replaceAction(InputMap, ActionMap, int, KeyActionPair,
* KeyActionPair)
* @param im The input map.
* @param am The action map.
* @param key The keystroke whose key/Action pair to change.
* @param kap The (original) key/Action pair.
* @see #replaceAction(InputMap, ActionMap, int, KeyActionPair, KeyActionPair)
*/
private void putBackAction(InputMap im, ActionMap am, int key, KeyActionPair kap) {
KeyStroke ks = KeyStroke.getKeyStroke(key, 0);
@@ -395,19 +391,13 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Replaces a key/Action pair in an InputMap and ActionMap with a new
* pair.
* Replaces a key/Action pair in an InputMap and ActionMap with a new pair.
*
* @param im
* The input map.
* @param am
* The action map.
* @param key
* The keystroke whose information to replace.
* @param kap
* The new key/Action pair for <code>key</code>.
* @param old
* A buffer in which to place the old key/Action pair.
* @param im The input map.
* @param am The action map.
* @param key The keystroke whose information to replace.
* @param kap The new key/Action pair for <code>key</code>.
* @param old A buffer in which to place the old key/Action pair.
* @see #putBackAction(InputMap, ActionMap, int, KeyActionPair)
*/
private void replaceAction(InputMap im, ActionMap am, int key, KeyActionPair kap, KeyActionPair old) {
@@ -458,8 +448,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Selects the completion item one "page down" from the currently selected
* one.
* Selects the completion item one "page down" from the currently selected one.
*
* @see #selectPageUpItem()
*/
@@ -471,8 +460,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Selects the completion item one "page up" from the currently selected
* one.
* Selects the completion item one "page up" from the currently selected one.
*
* @see #selectPageDownItem()
*/
@@ -509,11 +497,10 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Sets the completions to display in the choices list. The first
* completion is selected.
* Sets the completions to display in the choices list. The first completion is
* selected.
*
* @param completions
* The completions to display.
* @param completions The completions to display.
*/
public void setCompletions(List<Completion> completions) {
model.setContents(completions);
@@ -523,8 +510,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
/**
* Sets the size of the description window.
*
* @param size
* The new size. This cannot be <code>null</code>.
* @param size The new size. This cannot be <code>null</code>.
*/
public void setDescriptionWindowSize(Dimension size) {
if (descWindow != null) {
@@ -535,12 +521,11 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Sets the default list cell renderer to use when a completion provider
* does not supply its own.
* Sets the default list cell renderer to use when a completion provider does
* not supply its own.
*
* @param renderer
* The renderer to use. If this is <code>null</code>,
* a default renderer is used.
* @param renderer The renderer to use. If this is <code>null</code>, a default
* renderer is used.
* @see #getListCellRenderer()
*/
public void setListCellRenderer(ListCellRenderer renderer) {
@@ -550,11 +535,10 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
/**
* Sets the location of this window to be "good" relative to the specified
* rectangle. That rectangle should be the location of the text
* component's caret, in screen coordinates.
* rectangle. That rectangle should be the location of the text component's
* caret, in screen coordinates.
*
* @param r
* The text component's caret position, in screen coordinates.
* @param r The text component's caret position, in screen coordinates.
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
@@ -600,8 +584,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
/**
* Toggles the visibility of this popup window.
*
* @param visible
* Whether this window should be visible.
* @param visible Whether this window should be visible.
*/
@Override
public void setVisible(boolean visible) {
@@ -685,8 +668,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
}
/**
* Updates the <tt>LookAndFeel</tt> of this window and the description
* window.
* Updates the <tt>LookAndFeel</tt> of this window and the description window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(this);
@@ -698,8 +680,7 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
/**
* Called when a new item is selected in the popup list.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void valueChanged(ListSelectionEvent e) {
@@ -784,7 +765,8 @@ class AutoCompletePopupWindow extends JWindow implements CaretListener, ListSele
public Object key;
public Action action;
public KeyActionPair() {}
public KeyActionPair() {
}
public KeyActionPair(Object key, Action a) {
this.key = key;
+158 -198
View File
@@ -49,8 +49,8 @@ import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
/**
* Adds auto-completion to a text component. Provides a popup window with a
* list of auto-complete choices on a given keystroke, such as Crtrl+Space.
* Adds auto-completion to a text component. Provides a popup window with a list
* of auto-complete choices on a given keystroke, such as Crtrl+Space.
* <p>
*
* Depending on the {@link CompletionProvider} installed, the following
@@ -58,14 +58,14 @@ import javax.swing.text.JTextComponent;
*
* <ul>
* <li>An auto-complete choices list made visible via e.g. Ctrl+Space</li>
* <li>A "description" window displayed alongside the choices list that
* provides documentation on the currently selected completion choice
* (as seen in Eclipse and NetBeans).</li>
* <li>A "description" window displayed alongside the choices list that provides
* documentation on the currently selected completion choice (as seen in Eclipse
* and NetBeans).</li>
* <li>Parameter assistance. If this is enabled, if the user enters a
* "parameterized" completion, such as a method or a function, then they
* will receive a tool tip describing the arguments they have to enter to
* the completion. Also, the arguments can be navigated via tab and
* shift+tab (a la Eclipse and NetBeans).</li>
* "parameterized" completion, such as a method or a function, then they will
* receive a tool tip describing the arguments they have to enter to the
* completion. Also, the arguments can be navigated via tab and shift+tab (a la
* Eclipse and NetBeans).</li>
* </ul>
*
* @author Robert Futrell
@@ -93,14 +93,14 @@ public class AutoCompletion {
private AutoCompletePopupWindow popupWindow;
/**
* The preferred size of the completion choices window. This field exists
* because the user will likely set the preferred size of the window before
* it is actually created.
* because the user will likely set the preferred size of the window before it
* is actually created.
*/
private Dimension preferredChoicesWindowSize;
/**
* The preferred size of the optional description window. This field only
* exists because the user may (and usually will) set the size of the
* description window before it exists (it must be parented to a Window).
* The preferred size of the optional description window. This field only exists
* because the user may (and usually will) set the size of the description
* window before it exists (it must be parented to a Window).
*/
private Dimension preferredDescWindowSize;
/**
@@ -112,13 +112,12 @@ public class AutoCompletion {
*/
private CompletionProvider provider;
/**
* The renderer to use for the completion choices. If this is
* <code>null</code>, then a default renderer is used.
* The renderer to use for the completion choices. If this is <code>null</code>,
* then a default renderer is used.
*/
private ListCellRenderer renderer;
/**
* The handler to use when an external URL is clicked in the help
* documentation.
* The handler to use when an external URL is clicked in the help documentation.
*/
private ExternalURLHandler externalURLHandler;
/**
@@ -127,8 +126,8 @@ public class AutoCompletion {
*/
private static LinkRedirector linkRedirector;
/**
* Whether the description window should be displayed along with the
* completion choice window.
* Whether the description window should be displayed along with the completion
* choice window.
*/
private boolean showDescWindow;
/**
@@ -136,14 +135,14 @@ public class AutoCompletion {
*/
private boolean autoCompleteEnabled;
/**
* Whether the auto-activation of auto-complete (after a delay, after the
* user types an appropriate character) is enabled.
* Whether the auto-activation of auto-complete (after a delay, after the user
* types an appropriate character) is enabled.
*/
private boolean autoActivationEnabled;
/**
* Whether or not, when there is only a single auto-complete option that
* matches the text at the current text position, that text should be
* auto-inserted, instead of the completion window displaying.
* Whether or not, when there is only a single auto-complete option that matches
* the text at the current text position, that text should be auto-inserted,
* instead of the completion window displaying.
*/
private boolean autoCompleteSingleChoices;
/**
@@ -166,8 +165,8 @@ public class AutoCompletion {
*/
private Object oldTriggerKey;
/**
* The action previously assigned to {@link #trigger}, so we can reset it if
* the user disables auto-completion.
* The action previously assigned to {@link #trigger}, so we can reset it if the
* user disables auto-completion.
*/
private Action oldTriggerAction;
/**
@@ -186,8 +185,8 @@ public class AutoCompletion {
*/
private ParentWindowListener parentWindowListener;
/**
* Listens for events from the text component that affect the visibility of
* the popup windows.
* Listens for events from the text component that affect the visibility of the
* popup windows.
*/
private TextComponentListener textComponentListener;
/**
@@ -210,14 +209,14 @@ public class AutoCompletion {
private EventListenerList listeners;
/**
* Whether or not the popup should be hidden when user types a space (or any
* character that resets the completion list to "all completions"). Defaults
* to true.
* character that resets the completion list to "all completions"). Defaults to
* true.
*/
private boolean hideOnNoText;
/**
* Whether or not the popup should be hidden when the CompletionProvider
* changes. If set to false, caller has to ensure refresh of the popup
* content. Defaults to true.
* changes. If set to false, caller has to ensure refresh of the popup content.
* Defaults to true.
*/
private boolean hideOnCompletionProviderChange;
/**
@@ -229,21 +228,18 @@ public class AutoCompletion {
*/
private static final String PARAM_COMPLETE_KEY = "AutoCompletion.FunctionStart";
/**
* Stores how to render auto-completion-specific highlights in text
* components.
* Stores how to render auto-completion-specific highlights in text components.
*/
private static final AutoCompletionStyleContext styleContext = new AutoCompletionStyleContext();
/**
* Whether debug messages should be printed to stdout as AutoCompletion
* runs.
* Whether debug messages should be printed to stdout as AutoCompletion runs.
*/
private static final boolean DEBUG = initDebug();
/**
* Constructor.
*
* @param provider
* The completion provider. This cannot be <code>null</code>
* @param provider The completion provider. This cannot be <code>null</code>
*/
public AutoCompletion(CompletionProvider provider) {
setChoicesWindowSize(350, 200);
@@ -267,8 +263,7 @@ public class AutoCompletion {
/**
* Adds a listener interested in popup window events from this instance.
*
* @param l
* The listener to add.
* @param l The listener to add.
* @see #removeAutoCompletionListener(AutoCompletionListener)
*/
public void addAutoCompletionListener(AutoCompletionListener l) {
@@ -286,8 +281,7 @@ public class AutoCompletion {
/**
* Fires an {@link AutoCompletionEvent} of the specified type.
*
* @param type
* The type of event to fire.
* @param type The type of event to fire.
*/
protected void fireAutoCompletionEvent(AutoCompletionEvent.Type type) {
// Guaranteed to return a non-null array
@@ -306,8 +300,8 @@ public class AutoCompletion {
}
/**
* Returns the delay between when the user types a character and when the
* code completion popup should automatically appear (if applicable).
* Returns the delay between when the user types a character and when the code
* completion popup should automatically appear (if applicable).
*
* @return The delay, in milliseconds.
* @see #setAutoActivationDelay(int)
@@ -317,8 +311,8 @@ public class AutoCompletion {
}
/**
* Returns whether, if a single auto-complete choice is available, it should
* be automatically inserted, without displaying the popup menu.
* Returns whether, if a single auto-complete choice is available, it should be
* automatically inserted, without displaying the popup menu.
*
* @return Whether to auto-complete single choices.
* @see #setAutoCompleteSingleChoices(boolean)
@@ -346,8 +340,8 @@ public class AutoCompletion {
}
/**
* Returns the default auto-complete "trigger key" for this OS. For Windows,
* for example, it is Ctrl+Space.
* Returns the default auto-complete "trigger key" for this OS. For Windows, for
* example, it is Ctrl+Space.
*
* @return The default auto-complete trigger key.
*/
@@ -358,8 +352,8 @@ public class AutoCompletion {
}
/**
* Returns the handler to use when an external URL is clicked in the
* description window.
* Returns the handler to use when an external URL is clicked in the description
* window.
*
* @return The handler.
* @see #setExternalURLHandler(ExternalURLHandler)
@@ -386,8 +380,8 @@ public class AutoCompletion {
}
/**
* Returns the default list cell renderer used when a completion provider
* does not supply its own.
* Returns the default list cell renderer used when a completion provider does
* not supply its own.
*
* @return The default list cell renderer.
* @see #setListCellRenderer(ListCellRenderer)
@@ -397,10 +391,10 @@ public class AutoCompletion {
}
/**
* Returns the renderer to use for {@link Completion}s in the optional
* parameter choices popup window (displayed when a
* {@link ParameterizedCompletion} is code-completed). If this returns
* <code>null</code>, a default renderer is used.
* Returns the renderer to use for {@link Completion}s in the optional parameter
* choices popup window (displayed when a {@link ParameterizedCompletion} is
* code-completed). If this returns <code>null</code>, a default renderer is
* used.
*
* @return The renderer to use.
* @see #setParamChoicesRenderer(ListCellRenderer)
@@ -417,14 +411,10 @@ public class AutoCompletion {
* <tt>c.getReplacementText()</tt>. You usually will not need to modify this
* method.
*
* @param c
* The completion being inserted.
* @param doc
* The document being modified.
* @param start
* The start of the text being replaced.
* @param len
* The length of the text being replaced.
* @param c The completion being inserted.
* @param doc The document being modified.
* @param start The start of the text being replaced.
* @param len The length of the text being replaced.
* @return The text to replace with.
*/
protected String getReplacementText(Completion c, Document doc, int start, int len) {
@@ -443,8 +433,8 @@ public class AutoCompletion {
}
/**
* Returns the style context describing how auto-completion related
* highlights in the editor are rendered.
* Returns the style context describing how auto-completion related highlights
* in the editor are rendered.
*
* @return The style context.
*/
@@ -466,8 +456,8 @@ public class AutoCompletion {
/**
* Returns the orientation of the text component we're installed to.
*
* @return The orientation of the text component, or <code>null</code> if we
* are not installed on one.
* @return The orientation of the text component, or <code>null</code> if we are
* not installed on one.
*/
ComponentOrientation getTextComponentOrientation() {
return textComponent == null ? null : textComponent.getComponentOrientation();
@@ -525,8 +515,8 @@ public class AutoCompletion {
}
/**
* Determines whether debug should be enabled for the AutoCompletion
* library. This method checks a system property, but takes care of
* Determines whether debug should be enabled for the AutoCompletion library.
* This method checks a system property, but takes care of
* {@link SecurityException}s in case we're in an applet or WebStart.
*
* @return Whether debug should be enabled.
@@ -545,8 +535,7 @@ public class AutoCompletion {
* Inserts a completion. Any time a code completion event occurs, the actual
* text insertion happens through this method.
*
* @param c
* A completion to insert. This cannot be <code>null</code>.
* @param c A completion to insert. This cannot be <code>null</code>.
*/
protected final void insertCompletion(Completion c) {
insertCompletion(c, false);
@@ -556,11 +545,11 @@ public class AutoCompletion {
* Inserts a completion. Any time a code completion event occurs, the actual
* text insertion happens through this method.
*
* @param c
* A completion to insert. This cannot be <code>null</code>.
* @param typedParamListStartChar
* Whether the parameterized completion start
* character was typed (typically <code>'('</code>).
* @param c A completion to insert. This cannot be
* <code>null</code>.
* @param typedParamListStartChar Whether the parameterized completion start
* character was typed (typically
* <code>'('</code>).
*/
protected void insertCompletion(Completion c, boolean typedParamListStartChar) {
JTextComponent textComp = getTextComponent();
@@ -582,11 +571,10 @@ public class AutoCompletion {
/**
* Installs this auto-completion on a text component. If this
* {@link AutoCompletion} is already installed on another text component,
* it is uninstalled first.
* {@link AutoCompletion} is already installed on another text component, it is
* uninstalled first.
*
* @param c
* The text component.
* @param c The text component.
* @see #uninstall()
*/
public void install(JTextComponent c) {
@@ -624,8 +612,7 @@ public class AutoCompletion {
/**
* Installs a "trigger key" action onto the current text component.
*
* @param ks
* The keystroke that should trigger the action.
* @param ks The keystroke that should trigger the action.
* @see #uninstallTriggerKey()
*/
private void installTriggerKey(KeyStroke ks) {
@@ -639,9 +626,9 @@ public class AutoCompletion {
/**
* Creates and returns the action to call when the user presses the
* auto-completion trigger key (e.g. ctrl+space). This is a hook for
* subclasses that want to provide their own behavior in this scenario.
* The default implementation returns an {@link AutoCompleteAction}.
* auto-completion trigger key (e.g. ctrl+space). This is a hook for subclasses
* that want to provide their own behavior in this scenario. The default
* implementation returns an {@link AutoCompleteAction}.
*
* @return The action to use.
* @see AutoCompleteAction
@@ -651,10 +638,10 @@ public class AutoCompletion {
}
/**
* Returns whether auto-activation is enabled (that is, whether the
* completion popup will automatically appear after a delay when the user
* types an appropriate character). Note that this parameter will be ignored
* if auto-completion is disabled.
* Returns whether auto-activation is enabled (that is, whether the completion
* popup will automatically appear after a delay when the user types an
* appropriate character). Note that this parameter will be ignored if
* auto-completion is disabled.
*
* @return Whether auto-activation is enabled.
* @see #setAutoActivationEnabled(boolean)
@@ -677,8 +664,7 @@ public class AutoCompletion {
/**
* Whether or not the popup should be hidden when the CompletionProvider
* changes. If set to false, caller has to ensure refresh of the popup
* content.
* changes. If set to false, caller has to ensure refresh of the popup content.
*
* @return Whether the popup should be hidden when the completion provider
* changes.
@@ -689,11 +675,11 @@ public class AutoCompletion {
}
/**
* Whether or not the popup should be hidden when user types a space (or
* any character that resets the completion list to "all completions").
* Whether or not the popup should be hidden when user types a space (or any
* character that resets the completion list to "all completions").
*
* @return Whether the popup should be hidden when the completion list is
* reset to show all completions.
* @return Whether the popup should be hidden when the completion list is reset
* to show all completions.
* @see #setHideOnNoText(boolean)
*/
protected boolean isHideOnNoText() {
@@ -720,12 +706,12 @@ public class AutoCompletion {
}
/**
* Refreshes the popup window. First, this method gets the possible
* completions for the current caret position. If there are none, and the
* popup is visible, it is hidden. If there are some completions and the
* popup is hidden, it is made visible and made to display the completions.
* If there are some completions and the popup is visible, its list is
* updated to the current set of completions.
* Refreshes the popup window. First, this method gets the possible completions
* for the current caret position. If there are none, and the popup is visible,
* it is hidden. If there are some completions and the popup is hidden, it is
* made visible and made to display the completions. If there are some
* completions and the popup is visible, its list is updated to the current set
* of completions.
*
* @return The current line number of the caret.
*/
@@ -749,7 +735,8 @@ public class AutoCompletion {
final List<Completion> completions = provider.getCompletions(textComponent);
int count = completions == null ? 0 : completions.size();
if (count > 1 || (count == 1 && (isPopupVisible() || text.equals(completions.get(0).getInputText())))
|| (count == 1 && (isPopupVisible() || textLen == 0)) || (count == 1 && !getAutoCompleteSingleChoices())) {
|| (count == 1 && (isPopupVisible() || textLen == 0))
|| (count == 1 && !getAutoCompleteSingleChoices())) {
if (popupWindow == null) {
popupWindow = new AutoCompletePopupWindow(parentWindow, this);
popupWindowListener.install(popupWindow);
@@ -800,8 +787,7 @@ public class AutoCompletion {
/**
* Removes a listener interested in popup window events from this instance.
*
* @param l
* The listener to remove.
* @param l The listener to remove.
* @see #addAutoCompletionListener(AutoCompletionListener)
*/
public void removeAutoCompletionListener(AutoCompletionListener l) {
@@ -812,8 +798,7 @@ public class AutoCompletion {
* Sets the delay between when the user types a character and when the code
* completion popup should automatically appear (if applicable).
*
* @param ms
* The delay, in milliseconds. This should be greater than zero.
* @param ms The delay, in milliseconds. This should be greater than zero.
* @see #getAutoActivationDelay()
*/
public void setAutoActivationDelay(int ms) {
@@ -823,11 +808,10 @@ public class AutoCompletion {
}
/**
* Toggles whether auto-activation is enabled. Note that auto-activation
* also depends on auto-completion itself being enabled.
* Toggles whether auto-activation is enabled. Note that auto-activation also
* depends on auto-completion itself being enabled.
*
* @param enabled
* Whether auto-activation is enabled.
* @param enabled Whether auto-activation is enabled.
* @see #isAutoActivationEnabled()
* @see #setAutoActivationDelay(int)
*/
@@ -847,8 +831,7 @@ public class AutoCompletion {
/**
* Sets whether auto-completion is enabled.
*
* @param enabled
* Whether auto-completion is enabled.
* @param enabled Whether auto-completion is enabled.
* @see #isAutoCompleteEnabled()
*/
public void setAutoCompleteEnabled(boolean enabled) {
@@ -862,8 +845,7 @@ public class AutoCompletion {
* Sets whether, if a single auto-complete choice is available, it should be
* automatically inserted, without displaying the popup menu.
*
* @param autoComplete
* Whether to auto-complete single choices.
* @param autoComplete Whether to auto-complete single choices.
* @see #getAutoCompleteSingleChoices()
*/
public void setAutoCompleteSingleChoices(boolean autoComplete) {
@@ -873,11 +855,9 @@ public class AutoCompletion {
/**
* Sets the completion provider being used.
*
* @param provider
* The new completion provider. This cannot be
* @param provider The new completion provider. This cannot be
* <code>null</code>.
* @throws IllegalArgumentException
* If <code>provider</code> is
* @throws IllegalArgumentException If <code>provider</code> is
* <code>null</code>.
*/
public void setCompletionProvider(CompletionProvider provider) {
@@ -893,10 +873,8 @@ public class AutoCompletion {
/**
* Sets the size of the completion choices window.
*
* @param w
* The new width.
* @param h
* The new height.
* @param w The new width.
* @param h The new height.
* @see #setDescriptionWindowSize(int, int)
*/
public void setChoicesWindowSize(int w, int h) {
@@ -909,10 +887,8 @@ public class AutoCompletion {
/**
* Sets the size of the description window.
*
* @param w
* The new width.
* @param h
* The new height.
* @param w The new width.
* @param h The new height.
* @see #setChoicesWindowSize(int, int)
*/
public void setDescriptionWindowSize(int w, int h) {
@@ -923,15 +899,14 @@ public class AutoCompletion {
}
/**
* Sets the handler to use when an external URL is clicked in the
* description window. This handler can perform some action, such as open
* the URL in a web browser. The default implementation will open the URL in
* a browser, but only if running in Java 6. If you want browser support for
* Java 5 and below, or otherwise want to respond to hyperlink clicks, you
* will have to install your own handler to do so.
* Sets the handler to use when an external URL is clicked in the description
* window. This handler can perform some action, such as open the URL in a web
* browser. The default implementation will open the URL in a browser, but only
* if running in Java 6. If you want browser support for Java 5 and below, or
* otherwise want to respond to hyperlink clicks, you will have to install your
* own handler to do so.
*
* @param handler
* The new handler.
* @param handler The new handler.
* @see #getExternalURLHandler()
*/
public void setExternalURLHandler(ExternalURLHandler handler) {
@@ -939,13 +914,11 @@ public class AutoCompletion {
}
/**
* Sets whether or not the popup should be hidden when the
* CompletionProvider changes. If set to false, caller has to ensure refresh
* of the popup content.
* Sets whether or not the popup should be hidden when the CompletionProvider
* changes. If set to false, caller has to ensure refresh of the popup content.
*
* @param hideOnCompletionProviderChange
* Whether the popup should be hidden
* when the completion provider changes.
* @param hideOnCompletionProviderChange Whether the popup should be hidden when
* the completion provider changes.
* @see #isHideOnCompletionProviderChange()
*/
protected void setHideOnCompletionProviderChange(boolean hideOnCompletionProviderChange) {
@@ -953,12 +926,11 @@ public class AutoCompletion {
}
/**
* Sets whether or not the popup should be hidden when user types a space
* (or any character that resets the completion list to "all completions").
* Sets whether or not the popup should be hidden when user types a space (or
* any character that resets the completion list to "all completions").
*
* @param hideOnNoText
* Whether the popup sh ould be hidden when the
* completion list is reset to show all completions.
* @param hideOnNoText Whether the popup sh ould be hidden when the completion
* list is reset to show all completions.
* @see #isHideOnNoText()
*/
protected void setHideOnNoText(boolean hideOnNoText) {
@@ -967,12 +939,10 @@ public class AutoCompletion {
/**
* Sets the redirector for external URL's found in code completion
* documentation. When a non-local link in completion popups is clicked,
* this redirector is given the chance to modify the URL fetched and
* displayed.
* documentation. When a non-local link in completion popups is clicked, this
* redirector is given the chance to modify the URL fetched and displayed.
*
* @param linkRedirector
* The link redirector, or <code>null</code> for none.
* @param linkRedirector The link redirector, or <code>null</code> for none.
* @see #getLinkRedirector()
*/
public static void setLinkRedirector(LinkRedirector linkRedirector) {
@@ -980,12 +950,11 @@ public class AutoCompletion {
}
/**
* Sets the default list cell renderer to use when a completion provider
* does not supply its own.
* Sets the default list cell renderer to use when a completion provider does
* not supply its own.
*
* @param renderer
* The renderer to use. If this is <code>null</code>, a
* default renderer is used.
* @param renderer The renderer to use. If this is <code>null</code>, a default
* renderer is used.
* @see #getListCellRenderer()
*/
public void setListCellRenderer(ListCellRenderer renderer) {
@@ -997,13 +966,11 @@ public class AutoCompletion {
}
/**
* Sets the renderer to use for {@link Completion}s in the optional
* parameter choices popup window (displayed when a
* {@link ParameterizedCompletion} is code-completed). If this isn't set, a
* default renderer is used.
* Sets the renderer to use for {@link Completion}s in the optional parameter
* choices popup window (displayed when a {@link ParameterizedCompletion} is
* code-completed). If this isn't set, a default renderer is used.
*
* @param r
* The renderer to use.
* @param r The renderer to use.
* @see #getParamChoicesRenderer()
* @see #setParameterAssistanceEnabled(boolean)
*/
@@ -1013,13 +980,12 @@ public class AutoCompletion {
/**
* Sets whether parameter assistance is enabled. If parameter assistance is
* enabled, and a "parameterized" completion (such as a function or method)
* is inserted, the user will get "assistance" in inserting the parameters
* in the form of a popup window with documentation and easy tabbing through
* the arguments (as seen in Eclipse and NetBeans).
* enabled, and a "parameterized" completion (such as a function or method) is
* inserted, the user will get "assistance" in inserting the parameters in the
* form of a popup window with documentation and easy tabbing through the
* arguments (as seen in Eclipse and NetBeans).
*
* @param enabled
* Whether parameter assistance should be enabled.
* @param enabled Whether parameter assistance should be enabled.
* @see #isParameterAssistanceEnabled()
*/
public void setParameterAssistanceEnabled(boolean enabled) {
@@ -1027,11 +993,10 @@ public class AutoCompletion {
}
/**
* Toggles the visibility of the auto-completion popup window. This fires
* an {@link AutoCompletionEvent} of the appropriate type.
* Toggles the visibility of the auto-completion popup window. This fires an
* {@link AutoCompletionEvent} of the appropriate type.
*
* @param visible
* Whether the window should be made visible or hidden.
* @param visible Whether the window should be made visible or hidden.
* @see #isPopupVisible()
*/
protected void setPopupVisible(boolean visible) {
@@ -1041,11 +1006,10 @@ public class AutoCompletion {
}
/**
* Sets whether the "description window" should be shown beside the
* completion window.
* Sets whether the "description window" should be shown beside the completion
* window.
*
* @param show
* Whether to show the description window.
* @param show Whether to show the description window.
* @see #getShowDescWindow()
*/
public void setShowDescWindow(boolean show) {
@@ -1057,10 +1021,8 @@ public class AutoCompletion {
* Sets the keystroke that should be used to trigger the auto-complete popup
* window.
*
* @param ks
* The keystroke.
* @throws IllegalArgumentException
* If <code>ks</code> is <code>null</code>.
* @param ks The keystroke.
* @throws IllegalArgumentException If <code>ks</code> is <code>null</code>.
* @see #getTriggerKey()
*/
public void setTriggerKey(KeyStroke ks) {
@@ -1081,10 +1043,8 @@ public class AutoCompletion {
/**
* Displays a "tool tip" detailing the inputs to the function just entered.
*
* @param pc
* The completion.
* @param typedParamListStartChar
* Whether the parameterized completion list
* @param pc The completion.
* @param typedParamListStartChar Whether the parameterized completion list
* starting character was typed.
*/
private void startParameterizedCompletionAssistance(ParameterizedCompletion pc, boolean typedParamListStartChar) {
@@ -1159,8 +1119,7 @@ public class AutoCompletion {
/**
* Updates the LookAndFeel of the popup window. Applications can call this
* method as appropriate if they support changing the LookAndFeel at
* runtime.
* method as appropriate if they support changing the LookAndFeel at runtime.
*/
private void updateUI() {
if (popupWindow != null) {
@@ -1176,10 +1135,11 @@ public class AutoCompletion {
}
/**
* Listens for events in the text component to auto-activate the code
* completion popup.
* Listens for events in the text component to auto-activate the code completion
* popup.
*/
private class AutoActivationListener extends FocusAdapter implements DocumentListener, CaretListener, ActionListener {
private class AutoActivationListener extends FocusAdapter
implements DocumentListener, CaretListener, ActionListener {
private Timer timer;
private boolean justInserted;
@@ -1250,8 +1210,8 @@ public class AutoCompletion {
}
/**
* The <code>Action</code> that displays the popup window if auto-completion
* is enabled.
* The <code>Action</code> that displays the popup window if auto-completion is
* enabled.
*/
protected class AutoCompleteAction extends AbstractAction {
@@ -1340,7 +1300,8 @@ public class AutoCompletion {
}
@Override
public void windowGainedFocus(WindowEvent e) {}
public void windowGainedFocus(WindowEvent e) {
}
@Override
public void windowLostFocus(WindowEvent e) {
@@ -1393,12 +1354,11 @@ public class AutoCompletion {
}
/**
* Called when the component hierarchy for our text component changes.
* When the text component is added to a new {@link Window}, this method
* registers listeners on that <code>Window</code>.
* Called when the component hierarchy for our text component changes. When the
* text component is added to a new {@link Window}, this method registers
* listeners on that <code>Window</code>.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void hierarchyChanged(HierarchyEvent e) {
@@ -8,9 +8,9 @@ package org.fife.ui.autocomplete;
import java.util.EventObject;
/**
* An event fired by an instance of {@link AutoCompletion}. This can be
* used by applications that wish to be notified of the auto-complete popup
* window showing and hiding.
* An event fired by an instance of {@link AutoCompletion}. This can be used by
* applications that wish to be notified of the auto-complete popup window
* showing and hiding.
*
* @author Robert Futrell
* @version 1.0
@@ -25,11 +25,8 @@ public class AutoCompletionEvent extends EventObject {
/**
* Constructor.
*
* @param source
* The <code>AutoCompletion</code> instance that fired
* this event.
* @param type
* The event type.
* @param source The <code>AutoCompletion</code> instance that fired this event.
* @param type The event type.
*/
public AutoCompletionEvent(AutoCompletion source, Type type) {
super(source);
@@ -17,11 +17,10 @@ import java.util.EventListener;
public interface AutoCompletionListener extends EventListener {
/**
* Callback notified when a change to the <code>AutoCompletion</code>'s
* status occurs.
* Callback notified when a change to the <code>AutoCompletion</code>'s status
* occurs.
*
* @param e
* The event.
* @param e The event.
*/
void autoCompleteUpdate(AutoCompletionEvent e);
}
@@ -22,13 +22,13 @@ public class AutoCompletionStyleContext {
*/
private Color parameterizedCompletionCursorPositionColor;
/**
* The color used to highlight copies of editable parameters in
* parameterized completions.
* The color used to highlight copies of editable parameters in parameterized
* completions.
*/
private Color parameterCopyColor;
/**
* The color of the outline highlight used to denote editable parameters
* in parameterized completions.
* The color of the outline highlight used to denote editable parameters in
* parameterized completions.
*/
private Color parameterOutlineColor;
@@ -39,8 +39,8 @@ public class AutoCompletionStyleContext {
}
/**
* Returns the color of the highlight painted on copies of editable
* parameters in parameterized completions.
* Returns the color of the highlight painted on copies of editable parameters
* in parameterized completions.
*
* @return The color used.
* @see #setParameterCopyColor(Color)
@@ -50,8 +50,8 @@ public class AutoCompletionStyleContext {
}
/**
* Returns the color used to denote the ending caret position for
* parameterized completions.
* Returns the color used to denote the ending caret position for parameterized
* completions.
*
* @return The color used.
* @see #setParameterizedCompletionCursorPositionColor(Color)
@@ -61,8 +61,8 @@ public class AutoCompletionStyleContext {
}
/**
* Returns the color of the outline highlight used to denote editable
* parameters in parameterized completions.
* Returns the color of the outline highlight used to denote editable parameters
* in parameterized completions.
*
* @return The color used.
* @see #setParameterOutlineColor(Color)
@@ -72,11 +72,10 @@ public class AutoCompletionStyleContext {
}
/**
* Sets the color of the highlight painted on copies of editable
* parameters in parameterized completions.
* Sets the color of the highlight painted on copies of editable parameters in
* parameterized completions.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #setParameterCopyColor(Color)
*/
public void setParameterCopyColor(Color color) {
@@ -84,11 +83,10 @@ public class AutoCompletionStyleContext {
}
/**
* Sets the color used to denote the ending caret position for
* parameterized completions.
* Sets the color used to denote the ending caret position for parameterized
* completions.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #getParameterizedCompletionCursorPositionColor()
*/
public void setParameterizedCompletionCursorPositionColor(Color color) {
@@ -96,11 +94,10 @@ public class AutoCompletionStyleContext {
}
/**
* Sets the color of the outline highlight used to denote editable
* parameters in parameterized completions.
* Sets the color of the outline highlight used to denote editable parameters in
* parameterized completions.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #getParameterOutlineColor()
*/
public void setParameterOutlineColor(Color color) {
@@ -7,9 +7,9 @@
package org.fife.ui.autocomplete;
/**
* A straightforward {@link Completion} implementation. This implementation
* can be used if you have a relatively short number of static completions
* with no (or short) summaries.
* A straightforward {@link Completion} implementation. This implementation can
* be used if you have a relatively short number of static completions with no
* (or short) summaries.
* <p>
*
* This implementation uses the replacement text as the input text. It also
@@ -28,10 +28,8 @@ public class BasicCompletion extends AbstractCompletion {
/**
* Constructor.
*
* @param provider
* The parent completion provider.
* @param replacementText
* The text to replace.
* @param provider The parent completion provider.
* @param replacementText The text to replace.
*/
public BasicCompletion(CompletionProvider provider, String replacementText) {
this(provider, replacementText, null);
@@ -40,12 +38,9 @@ public class BasicCompletion extends AbstractCompletion {
/**
* Constructor.
*
* @param provider
* The parent completion provider.
* @param replacementText
* The text to replace.
* @param shortDesc
* A short description of the completion. This will be
* @param provider The parent completion provider.
* @param replacementText The text to replace.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
*/
@@ -56,16 +51,12 @@ public class BasicCompletion extends AbstractCompletion {
/**
* Constructor.
*
* @param provider
* The parent completion provider.
* @param replacementText
* The text to replace.
* @param shortDesc
* A short description of the completion. This will be
* @param provider The parent completion provider.
* @param replacementText The text to replace.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param summary
* The summary of this completion. This should be HTML.
* @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) {
@@ -84,8 +75,8 @@ public class BasicCompletion extends AbstractCompletion {
}
/**
* Returns the short description of this completion, usually used in
* the completion choices list.
* Returns the short description of this completion, usually used in the
* completion choices list.
*
* @return The short description, or <code>null</code> if there is none.
* @see #setShortDescription(String)
@@ -105,8 +96,7 @@ public class BasicCompletion extends AbstractCompletion {
/**
* Sets the short description of this completion.
*
* @param shortDesc
* The short description of this completion.
* @param shortDesc The short description of this completion.
* @see #getShortDescription()
*/
public void setShortDescription(String shortDesc) {
@@ -116,8 +106,7 @@ public class BasicCompletion extends AbstractCompletion {
/**
* Sets the summary for this completion.
*
* @param summary
* The summary for this completion.
* @param summary The summary for this completion.
* @see #getSummary()
*/
public void setSummary(String summary) {
@@ -125,8 +114,8 @@ public class BasicCompletion extends AbstractCompletion {
}
/**
* Returns a string representation of this completion. If the short
* description is not <code>null</code>, this method will return:
* Returns a string representation of this completion. If the short description
* is not <code>null</code>, this method will return:
*
* <code>getInputText() + " - " + shortDesc</code>
*
+42 -50
View File
@@ -10,20 +10,20 @@ import javax.swing.Icon;
import javax.swing.text.JTextComponent;
/**
* Represents a completion choice. A {@link CompletionProvider} returns lists
* of objects implementing this interface. A <tt>Completion</tt> contains the
* Represents a completion choice. A {@link CompletionProvider} returns lists of
* objects implementing this interface. A <tt>Completion</tt> contains the
* following information:
*
* <ul>
* <li>The text the user must (begin to) input for this to be a completion
* choice.
* <li>The text that will be filled in if the user chooses this completion.
* Note that often, this is the same as the text the user must (begin to)
* enter, but this doesn't have to be the case.
* <li>Summary HTML that describes this completion. This is information that
* can be displayed in a helper "tooltip"-style window beside the
* completion list. This may be <code>null</code>. It may also be
* lazily generated to cut down on memory usage.
* <li>The text that will be filled in if the user chooses this completion. Note
* that often, this is the same as the text the user must (begin to) enter, but
* this doesn't have to be the case.
* <li>Summary HTML that describes this completion. This is information that can
* be displayed in a helper "tooltip"-style window beside the completion list.
* This may be <code>null</code>. It may also be lazily generated to cut down on
* memory usage.
* <li>The <tt>CompletionProvider</tt> that returned this completion.
* <li>Tool tip text that can be displayed when a mouse hovers over this
* completion in a text component.
@@ -36,26 +36,23 @@ import javax.swing.text.JTextComponent;
public interface Completion extends Comparable<Completion> {
/**
* Compares this completion to another one lexicographically, ignoring
* case.
* Compares this completion to another one lexicographically, ignoring case.
*
* @param other
* Another completion instance.
* @param other Another completion instance.
* @return How this completion compares to the other one.
*/
@Override
public int compareTo(Completion other);
/**
* Returns the portion of this completion that has already been entered
* into the text component. The match is case-insensitive.
* Returns the portion of this completion that has already been entered into the
* text component. The match is case-insensitive.
* <p>
*
* This is a convenience method for:
* <code>getProvider().getAlreadyEnteredText(comp)</code>.
*
* @param comp
* The text component.
* @param comp The text component.
* @return The already-entered portion of this completion.
*/
public String getAlreadyEntered(JTextComponent comp);
@@ -68,14 +65,14 @@ public interface Completion extends Comparable<Completion> {
public Icon getIcon();
/**
* Returns the text that the user has to (start) typing for this completion
* to be offered. Note that this will usually be the same value as
* {@link #getReplacementText()}, but not always (a completion could be
* a way to implement shorthand, for example, "<code>sysout</code>" mapping
* to "<code>System.out.println(</code>").
* Returns the text that the user has to (start) typing for this completion to
* be offered. Note that this will usually be the same value as
* {@link #getReplacementText()}, but not always (a completion could be a way to
* implement shorthand, for example, "<code>sysout</code>" mapping to
* "<code>System.out.println(</code>").
*
* @return The text the user has to (start) typing for this completion to
* be offered.
* @return The text the user has to (start) typing for this completion to be
* offered.
* @see #getReplacementText()
*/
public String getInputText();
@@ -89,25 +86,25 @@ public interface Completion extends Comparable<Completion> {
/**
* Returns the "relevance" of this completion. This is used when sorting
* completions by their relevance. It is an abstract concept that may
* mean different things to different languages, and may depend on the
* context of the completion.
* completions by their relevance. It is an abstract concept that may mean
* different things to different languages, and may depend on the context of the
* completion.
* <p>
*
* By default, all completions have a relevance of <code>0</code>. The
* higher the value returned by this method, the higher up in the list
* this completion will be; the lower the value returned, the lower it will
* be. <code>Completion</code>s with equal relevance values will be
* sorted alphabetically.
* By default, all completions have a relevance of <code>0</code>. The higher
* the value returned by this method, the higher up in the list this completion
* will be; the lower the value returned, the lower it will be.
* <code>Completion</code>s with equal relevance values will be sorted
* alphabetically.
*
* @return The relevance of this completion.
*/
public int getRelevance();
/**
* Returns the text to insert as the result of this auto-completion. This
* is the "complete" text, including any text that replaces what the user
* has already typed.
* Returns the text to insert as the result of this auto-completion. This is the
* "complete" text, including any text that replaces what the user has already
* typed.
*
* @return The replacement text.
* @see #getInputText()
@@ -115,31 +112,26 @@ public interface Completion extends Comparable<Completion> {
public String getReplacementText();
/**
* Returns the description of this auto-complete choice. This can be
* used in a popup "description window."
* Returns the description of this auto-complete choice. This can be used in a
* popup "description window."
*
* @return This item's description. This should be HTML. It may be
* <code>null</code> if there is no description for this
* completion.
* <code>null</code> if there is no description for this completion.
*/
public String getSummary();
/**
* Returns the tool tip text to display for mouse hovers over this
* completion.
* Returns the tool tip text to display for mouse hovers over this completion.
* <p>
*
* Note that for this functionality to be enabled, a
* <tt>JTextComponent</tt> must be registered with the
* <tt>ToolTipManager</tt>, and the text component must know to search
* for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* Note that for this functionality to be enabled, a <tt>JTextComponent</tt>
* must be registered with the <tt>ToolTipManager</tt>, and the text component
* must know to search for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this can
* be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
*
* @return The tool tip text for this completion, or <code>null</code> if
* none.
* @return The tool tip text for this completion, or <code>null</code> if none.
*/
public String getToolTipText();
}
@@ -26,8 +26,8 @@ import javax.swing.text.View;
/**
* A cell renderer that adds some pizazz when rendering the standard
* {@link Completion} types, like Eclipse and NetBeans do. Specifically,
* this renderer handles:
* {@link Completion} types, like Eclipse and NetBeans do. Specifically, this
* renderer handles:
*
* <ul>
* <li>{@link FunctionCompletion}s</li>
@@ -43,18 +43,18 @@ import javax.swing.text.View;
public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* The alternating background color, or <code>null</code> if alternating
* row colors should not be used.
* The alternating background color, or <code>null</code> if alternating row
* colors should not be used.
*/
private static Color altBG;
/**
* The font to use when rendering items, or <code>null</code> if the
* list's default font should be used.
* The font to use when rendering items, or <code>null</code> if the list's
* default font should be used.
*/
private Font font;
/**
* Whether to display the types of fields and return types of functions
* in the completion text.
* Whether to display the types of fields and return types of functions in the
* completion text.
*/
private boolean showTypes;
/**
@@ -101,14 +101,13 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Constructor. This is primarily a hook for Substance, or any other
* Look and Feel whose renderers look drastically different than standard
* Constructor. This is primarily a hook for Substance, or any other Look and
* Feel whose renderers look drastically different than standard
* <code>DefaultListCellRenderer</code>s. Everything except for the text
* rendering will be done by the delegate. In almost all scenarios, you
* will want to use the no-argument constructor instead of this one.
* rendering will be done by the delegate. In almost all scenarios, you will
* want to use the no-argument constructor instead of this one.
*
* @param delegate
* The delegate renderer.
* @param delegate The delegate renderer.
* @see #delegateToSubstanceRenderer()
*/
public CompletionCellRenderer(DefaultListCellRenderer delegate) {
@@ -117,8 +116,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Creates the icon to use if no icon is found for a specific completion.
* The default implementation returns a 16x16 empty icon.
* Creates the icon to use if no icon is found for a specific completion. The
* default implementation returns a 16x16 empty icon.
*
* @return The icon.
* @see #getEmptyIcon()
@@ -128,8 +127,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Returns a decent "parameter" color based on the current default
* foreground color.
* Returns a decent "parameter" color based on the current default foreground
* color.
*
* @return The parameter color to use.
*/
@@ -138,8 +137,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Returns a decent "type" color based on the current default foreground
* color.
* Returns a decent "type" color based on the current default foreground color.
*
* @return The type color to use.
*/
@@ -148,12 +146,11 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Attempts to delegate rendering to a Substance cell renderer. This
* should only be called if Substance is known to be on the classpath.
* Attempts to delegate rendering to a Substance cell renderer. This should only
* be called if Substance is known to be on the classpath.
*
* @throws Exception
* If Substance is not on the classpath, or some other
* error occurs creating the Substance cell renderer.
* @throws Exception If Substance is not on the classpath, or some other error
* occurs creating the Substance cell renderer.
* @see Util#getUseSubstanceRenderers()
* @see #setDelegateRenderer(DefaultListCellRenderer)
*/
@@ -187,8 +184,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Returns the font used when rendering completions.
*
* @return The font. If this is <code>null</code>, then the default list
* font is used.
* @return The font. If this is <code>null</code>, then the default list font is
* used.
* @see #setDisplayFont(Font)
*/
public Font getDisplayFont() {
@@ -196,8 +193,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Returns the icon to use if no icon is found for a specific completion.
* This icon is lazily created if necessary.
* Returns the icon to use if no icon is found for a specific completion. This
* icon is lazily created if necessary.
*
* @return The icon.
* @see #createEmptyIcon()
@@ -212,9 +209,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Returns an icon.
*
* @param resource
* The icon to retrieve. This should either be a file,
* or a resource loadable by the current ClassLoader.
* @param resource The icon to retrieve. This should either be a file, or a
* resource loadable by the current ClassLoader.
* @return The icon.
*/
protected Icon getIcon(String resource) {
@@ -233,19 +229,15 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Returns the renderer.
*
* @param list
* The list of choices being rendered.
* @param value
* The {@link Completion} being rendered.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param value The {@link Completion} being rendered.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected,
boolean hasFocus) {
super.getListCellRendererComponent(list, value, index, selected, hasFocus);
if (font != null) {
setFont(font); // Overrides super's setFont(list.getFont()).
@@ -284,8 +276,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Returns whether the types of fields and return types of methods are
* shown in the completion text.
* Returns whether the types of fields and return types of methods are shown in
* the completion text.
*
* @return Whether to show the types.
* @see #setShowTypes(boolean)
@@ -346,18 +338,14 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Prepares this renderer to display a function completion.
*
* @param list
* The list of choices being rendered.
* @param fc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param fc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForFunctionCompletion(JList list, FunctionCompletion fc, int index, boolean selected, boolean hasFocus) {
protected void prepareForFunctionCompletion(JList list, FunctionCompletion fc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(fc.getName());
char paramListStart = fc.getProvider().getParameterListStart();
@@ -408,18 +396,14 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Prepares this renderer to display a markup tag completion.
*
* @param list
* The list of choices being rendered.
* @param mc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param mc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForMarkupTagCompletion(JList list, MarkupTagCompletion mc, int index, boolean selected, boolean hasFocus) {
protected void prepareForMarkupTagCompletion(JList list, MarkupTagCompletion mc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(mc.getName());
setText(sb.toString());
@@ -429,16 +413,11 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
* Prepares this renderer to display a completion not specifically handled
* elsewhere.
*
* @param list
* The list of choices being rendered.
* @param c
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param c The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForOtherCompletion(JList list, Completion c, int index, boolean selected, boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
@@ -462,18 +441,14 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Prepares this renderer to display a template completion.
*
* @param list
* The list of choices being rendered.
* @param tc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param tc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForTemplateCompletion(JList list, TemplateCompletion tc, int index, boolean selected, boolean hasFocus) {
protected void prepareForTemplateCompletion(JList list, TemplateCompletion tc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(tc.getInputText());
String definition = tc.getShortDescription();
@@ -493,18 +468,14 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Prepares this renderer to display a variable completion.
*
* @param list
* The list of choices being rendered.
* @param vc
* The completion to render.
* @param index
* The index into <code>list</code> being rendered.
* @param selected
* Whether the item is selected.
* @param hasFocus
* Whether the item has focus.
* @param list The list of choices being rendered.
* @param vc The completion to render.
* @param index The index into <code>list</code> being rendered.
* @param selected Whether the item is selected.
* @param hasFocus Whether the item has focus.
*/
protected void prepareForVariableCompletion(JList list, VariableCompletion vc, int index, boolean selected, boolean hasFocus) {
protected void prepareForVariableCompletion(JList list, VariableCompletion vc, int index, boolean selected,
boolean hasFocus) {
StringBuilder sb = new StringBuilder(PREFIX);
sb.append(vc.getName());
if (getShowTypes() && vc.getType() != null) {
@@ -523,8 +494,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Sets the background color to use on alternating lines.
*
* @param altBG
* The new alternate background color. If this is
* @param altBG The new alternate background color. If this is
* <code>null</code>, alternating lines will not use different
* background colors.
* @see #getAlternateBackground()
@@ -534,14 +504,12 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Sets the delegate renderer. Most users will never use this method; it
* is primarily a hook for Substance and other Look and Feels whose
* renderers look drastically different from the standard
* <code>DefaultListCellRenderer</code>.
* Sets the delegate renderer. Most users will never use this method; it is
* primarily a hook for Substance and other Look and Feels whose renderers look
* drastically different from the standard <code>DefaultListCellRenderer</code>.
*
* @param delegate
* The new delegate renderer. If this is <code>null</code>,
* the default rendering of this component is used.
* @param delegate The new delegate renderer. If this is <code>null</code>, the
* default rendering of this component is used.
* @see #getDelegateRenderer()
* @see #delegateToSubstanceRenderer()
*/
@@ -552,9 +520,8 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Sets the font to use when rendering completion items.
*
* @param font
* The font to use. If this is <code>null</code>, then
* the default list font is used.
* @param font The font to use. If this is <code>null</code>, then the default
* list font is used.
* @see #getDisplayFont()
*/
public void setDisplayFont(Font font) {
@@ -562,11 +529,10 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Sets the icon to display based off of a completion, falling back to the
* empty icon if the completion has no icon.
* Sets the icon to display based off of a completion, falling back to the empty
* icon if the completion has no icon.
*
* @param completion
* The completion to check.
* @param completion The completion to check.
* @see #setIconWithDefault(Completion, Icon)
*/
protected void setIconWithDefault(Completion completion) {
@@ -574,13 +540,11 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Sets the icon to display based off of a completion, falling back to a
* default icon if the completion has no icon.
* Sets the icon to display based off of a completion, falling back to a default
* icon if the completion has no icon.
*
* @param completion
* The completion to check.
* @param defaultIcon
* The icon to use if <code>completion</code> does not
* @param completion The completion to check.
* @param defaultIcon The icon to use if <code>completion</code> does not
* specify an icon.
* @see #setIconWithDefault(Completion)
*/
@@ -592,8 +556,7 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Sets the color to use for function arguments.
*
* @param color
* The color to use. This is ignored if <code>null</code>.
* @param color The color to use. This is ignored if <code>null</code>.
* @see #setTypeColor(Color)
*/
public void setParamColor(Color color) {
@@ -603,11 +566,10 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
}
/**
* Sets whether the types of fields and return types of methods are
* shown in the completion text.
* Sets whether the types of fields and return types of methods are shown in the
* completion text.
*
* @param show
* Whether to show the types.
* @param show Whether to show the types.
* @see #getShowTypes()
*/
public void setShowTypes(boolean show) {
@@ -616,11 +578,10 @@ public class CompletionCellRenderer extends DefaultListCellRenderer {
/**
* Sets the color to use for function/field types. Note that if
* {@link #getShowTypes()} returns <code>false</code>, this property
* effectively does nothing.
* {@link #getShowTypes()} returns <code>false</code>, this property effectively
* does nothing.
*
* @param color
* The color to use for types. This is ignored if
* @param color The color to use for types. This is ignored if
* <code>null</code>.
* @see #setShowTypes(boolean)
* @see #setParamColor(Color)
@@ -13,9 +13,8 @@ import java.util.List;
import javax.swing.AbstractListModel;
/**
* A list model implementation that allows the bulk addition of elements.
* This is the only feature missing from <code>DefaultListModel</code> that
* we need.
* A list model implementation that allows the bulk addition of elements. This
* is the only feature missing from <code>DefaultListModel</code> that we need.
*
* @author Robert Futrell
* @version 1.0
@@ -35,8 +34,8 @@ class CompletionListModel extends AbstractListModel {
}
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns (unless it throws an exception).
* Removes all of the elements from this list. The list will be empty after this
* call returns (unless it throws an exception).
*
* @see #setContents(Collection)
*/
@@ -67,8 +66,7 @@ class CompletionListModel extends AbstractListModel {
/**
* Sets the contents of this model. All previous contents are removed.
*
* @param contents
* The new contents of this model.
* @param contents The new contents of this model.
*/
public void setContents(Collection<Completion> contents) {
clear();
@@ -36,38 +36,34 @@ public interface CompletionProvider {
public void clearParameterizedCompletionParams();
/**
* Returns the text just before the current caret position that could be
* the start of something auto-completable.
* Returns the text just before the current caret position that could be the
* start of something auto-completable.
*
* @param comp
* The text component.
* @return The text. A return value of <code>null</code> means nothing
* should be auto-completed; a value of an empty string
* (<code>""</code>) means auto-completion should still be
* considered (i.e., all possible choices are valid).
* @param comp The text component.
* @return The text. A return value of <code>null</code> means nothing should be
* auto-completed; a value of an empty string (<code>""</code>) means
* auto-completion should still be considered (i.e., all possible
* choices are valid).
*/
public String getAlreadyEnteredText(JTextComponent comp);
/**
* Gets the possible completions for the text component at the current
* caret position.
* Gets the possible completions for the text component at the current caret
* position.
*
* @param comp
* The text component.
* @return The list of {@link Completion}s. If no completions are
* available, this method should return an empty list.
* @param comp The text component.
* @return The list of {@link Completion}s. If no completions are available,
* this method should return an empty list.
*/
public List<Completion> getCompletions(JTextComponent comp);
/**
* Returns the completions that have been entered at the specified visual
* location. This can be used for tool tips when the user hovers the
* mouse over completed text.
* location. This can be used for tool tips when the user hovers the mouse over
* completed text.
*
* @param comp
* The text component.
* @param p
* The position, usually from a <tt>MouseEvent</tt>.
* @param comp The text component.
* @param p The position, usually from a <tt>MouseEvent</tt>.
* @return The completions, or an empty list if there are none.
*/
public List<Completion> getCompletionsAt(JTextComponent comp, Point p);
@@ -75,8 +71,8 @@ public interface CompletionProvider {
/**
* Returns the cell renderer for completions returned from this provider.
*
* @return The cell renderer, or <code>null</code> if the default should
* be used.
* @return The cell renderer, or <code>null</code> if the default should be
* used.
* @see #setListCellRenderer(ListCellRenderer)
*/
public ListCellRenderer getListCellRenderer();
@@ -87,29 +83,27 @@ public interface CompletionProvider {
* completion, such as a function or method. For any parameter to the
* function/method, this object can return possible completions.
*
* @return The parameter choices provider, or <code>null</code> if
* none is installed.
* @return The parameter choices provider, or <code>null</code> if none is
* installed.
*/
public ParameterChoicesProvider getParameterChoicesProvider();
/**
* Returns a list of parameterized completions that have been entered
* at the current caret position of a text component (and thus can have
* their completion choices displayed).
* Returns a list of parameterized completions that have been entered at the
* current caret position of a text component (and thus can have their
* completion choices displayed).
*
* @param tc
* The text component.
* @return The list of {@link ParameterizedCompletion}s. If no completions
* are available, this may be <code>null</code>.
* @param tc The text component.
* @return The list of {@link ParameterizedCompletion}s. If no completions are
* available, this may be <code>null</code>.
*/
public List<ParameterizedCompletion> getParameterizedCompletions(JTextComponent tc);
/**
* Returns the text that marks the end of a list of parameters to a
* function or method.
* Returns the text that marks the end of a list of parameters to a function or
* method.
*
* @return The text for a parameter list end, for example,
* '<code>)</code>'.
* @return The text for a parameter list end, for example, '<code>)</code>'.
* @see #getParameterListStart()
* @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char)
@@ -119,8 +113,7 @@ public interface CompletionProvider {
/**
* Returns the text that separates parameters to a function or method.
*
* @return The text that separates parameters, for example,
* "<code>, </code>".
* @return The text that separates parameters, for example, "<code>, </code>".
* @see #getParameterListStart()
* @see #getParameterListEnd()
* @see #setParameterizedCompletionParams(char, String, char)
@@ -128,11 +121,10 @@ public interface CompletionProvider {
public String getParameterListSeparator();
/**
* Returns the text that marks the start of a list of parameters to a
* function or method.
* Returns the text that marks the start of a list of parameters to a function
* or method.
*
* @return The text for a parameter list start, for example,
* "<code>(</code>".
* @return The text for a parameter list start, for example, "<code>(</code>".
* @see #getParameterListEnd()
* @see #getParameterListSeparator()
* @see #setParameterizedCompletionParams(char, String, char)
@@ -149,15 +141,14 @@ public interface CompletionProvider {
/**
* This method is called if auto-activation is enabled in the parent
* {@link AutoCompletion} after the user types a single character. This
* provider should check the text at the current caret position of the
* text component, and decide whether auto-activation would be appropriate
* here. For example, a <code>CompletionProvider</code> for Java might
* want to return <code>true</code> for this method only if the last
* character typed was a '<code>.</code>'.
* {@link AutoCompletion} after the user types a single character. This provider
* should check the text at the current caret position of the text component,
* and decide whether auto-activation would be appropriate here. For example, a
* <code>CompletionProvider</code> for Java might want to return
* <code>true</code> for this method only if the last character typed was a
* '<code>.</code>'.
*
* @param tc
* The text component.
* @param tc The text component.
* @return Whether auto-activation would be appropriate.
*/
public boolean isAutoActivateOkay(JTextComponent tc);
@@ -165,30 +156,26 @@ public interface CompletionProvider {
/**
* Sets the renderer to use when displaying completion choices.
*
* @param r
* The renderer to use.
* @param r The renderer to use.
* @see #getListCellRenderer()
*/
public void setListCellRenderer(ListCellRenderer r);
/**
* Sets the values used to identify and insert "parameterized completions"
* (e.g. functions or methods). If this method isn't called, functions
* and methods will not have their parameters auto-completed.
* Sets the values used to identify and insert "parameterized completions" (e.g.
* functions or methods). If this method isn't called, functions and methods
* will not have their parameters auto-completed.
*
* @param listStart
* The character that marks the beginning of a list of
* @param listStart The character that marks the beginning of a list of
* parameters, such as '<tt>(</tt>' in C or Java.
* @param separator
* Text that should separate parameters in a parameter
* list when one is inserted. For example, "<tt>, </tt>".
* @param listEnd
* The character that marks the end of a list of parameters,
* @param separator Text that should separate parameters in a parameter list
* when one is inserted. For example, "<tt>, </tt>".
* @param listEnd The character that marks the end of a list of parameters,
* such as '<tt>)</tt>' in C or Java.
* @throws IllegalArgumentException
* If either <tt>listStart</tt> or
* <tt>listEnd</tt> is not printable ASCII, or if
* <tt>separator</tt> is <code>null</code> or an empty string.
* @throws IllegalArgumentException If either <tt>listStart</tt> or
* <tt>listEnd</tt> is not printable ASCII, or
* if <tt>separator</tt> is <code>null</code>
* or an empty string.
* @see #clearParameterizedCompletionParams()
*/
public void setParameterizedCompletionParams(char listStart, String separator, char listEnd);
@@ -196,9 +183,8 @@ public interface CompletionProvider {
/**
* Sets the parent completion provider.
*
* @param parent
* The parent provider. <code>null</code> means there will
* be no parent provider.
* @param parent The parent provider. <code>null</code> means there will be no
* parent provider.
* @see #getParent()
*/
public void setParent(CompletionProvider parent);
@@ -182,7 +182,8 @@ public abstract class CompletionProviderBase implements CompletionProvider {
} catch (BadLocationException ble) { // Never happens
ble.printStackTrace();
}
return (autoActivateAfterLetters && Character.isLetter(ch)) || (autoActivateChars != null && autoActivateChars.indexOf(ch) > -1);
return (autoActivateAfterLetters && Character.isLetter(ch))
|| (autoActivateChars != null && autoActivateChars.indexOf(ch) > -1);
}
/**
@@ -18,9 +18,9 @@ import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
/**
* Parser for an XML file describing a procedural language such as C. XML
* files will be validated against the <code>CompletionXml.dtd</code> DTD
* found in this package.
* Parser for an XML file describing a procedural language such as C. XML files
* will be validated against the <code>CompletionXml.dtd</code> DTD found in
* this package.
*
* @author Robert Futrell
* @version 1.0
@@ -68,18 +68,16 @@ public class CompletionXMLParser extends DefaultHandler {
*/
private String funcCompletionType;
/**
* The class loader to use to load custom completion classes, such as
* the one defined by {@link #funcCompletionType}. If this is
* <code>null</code>, then a default class loader is used. This field
* will usually be <code>null</code>.
* The class loader to use to load custom completion classes, such as the one
* defined by {@link #funcCompletionType}. If this is <code>null</code>, then a
* default class loader is used. This field will usually be <code>null</code>.
*/
private static ClassLoader DEFAULT_COMPLETION_CLASS_LOADER;
/**
* Constructor.
*
* @param provider
* The provider to get completions for.
* @param provider The provider to get completions for.
* @see #reset(CompletionProvider)
*/
public CompletionXMLParser(CompletionProvider provider) {
@@ -89,15 +87,12 @@ public class CompletionXMLParser extends DefaultHandler {
/**
* Constructor.
*
* @param provider
* The provider to get completions for.
* @param cl
* The class loader to use, if necessary, when loading classes
* @param provider The provider to get completions for.
* @param cl The class loader to use, if necessary, when loading classes
* from the XML (custom {@link FunctionCompletion}s, for
* example).
* This may be <code>null</code> if the default is to be used, or
* if the XML does not define specific classes for completion
* types.
* example). This may be <code>null</code> if the default is to
* be used, or if the XML does not define specific classes for
* completion types.
* @see #reset(CompletionProvider)
*/
public CompletionXMLParser(CompletionProvider provider, ClassLoader cl) {
@@ -295,8 +290,7 @@ public class CompletionXMLParser extends DefaultHandler {
/**
* Resets this parser to grab more completions.
*
* @param provider
* The new provider to get completions for.
* @param provider The new provider to get completions for.
*/
public void reset(CompletionProvider provider) {
this.provider = provider;
@@ -312,16 +306,14 @@ public class CompletionXMLParser extends DefaultHandler {
}
/**
* Sets the class loader to use when loading custom classes to use for
* various {@link Completion} types, such as {@link FunctionCompletion}s,
* from XML.
* Sets the class loader to use when loading custom classes to use for various
* {@link Completion} types, such as {@link FunctionCompletion}s, from XML.
* <p>
*
* Users should very rarely have a need to use this method.
*
* @param cl
* The class loader to use. If this is <code>null</code>, then
* a default is used.
* @param cl The class loader to use. If this is <code>null</code>, then a
* default is used.
*/
public static void setDefaultCompletionClassLoader(ClassLoader cl) {
DEFAULT_COMPLETION_CLASS_LOADER = cl;
@@ -28,9 +28,9 @@ import org.xml.sax.SAXException;
/**
* A basic completion provider implementation. This provider has no
* understanding of language semantics. It simply checks the text entered up
* to the caret position for a match against known completions. This is all
* that is needed in the majority of cases.
* understanding of language semantics. It simply checks the text entered up to
* the caret position for a match against known completions. This is all that is
* needed in the majority of cases.
*
* @author Robert Futrell
* @version 1.0
@@ -43,11 +43,11 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
*/
private String lastCompletionsAtText;
/**
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)},
* since this may be called multiple times in succession (this is usually
* called by <tt>JTextComponent.getToolTipText()</tt>, and if the user
* wiggles the mouse while a tool tip is displayed, this method gets
* repeatedly called. It can be costly so we try to speed it up a tad).
* Used to speed up {@link #getCompletionsAt(JTextComponent, Point)}, since this
* may be called multiple times in succession (this is usually called by
* <tt>JTextComponent.getToolTipText()</tt>, and if the user wiggles the mouse
* while a tool tip is displayed, this method gets repeatedly called. It can be
* costly so we try to speed it up a tad).
*/
private List<Completion> lastParameterizedCompletionsAt;
@@ -61,11 +61,10 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
}
/**
* Creates a completion provider that provides completion for a simple
* list of words.
* Creates a completion provider that provides completion for a simple list of
* words.
*
* @param words
* The words to offer as completion suggestions. If this is
* @param words The words to offer as completion suggestions. If this is
* <code>null</code>, no completions will be known.
* @see BasicCompletion
*/
@@ -75,12 +74,12 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
}
/**
* Returns the text just before the current caret position that could be
* the start of something auto-completable.
* Returns the text just before the current caret position that could be the
* start of something auto-completable.
* <p>
*
* This method returns all characters before the caret that are matched
* by {@link #isValidChar(char)}.
* This method returns all characters before the caret that are matched by
* {@link #isValidChar(char)}.
*
* {@inheritDoc}
*/
@@ -221,13 +220,12 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
}
/**
* Returns whether the specified character is valid in an auto-completion.
* The default implementation is equivalent to
* "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses
* can override this method to change what characters are matched.
* Returns whether the specified character is valid in an auto-completion. The
* default implementation is equivalent to
* "<code>Character.isLetterOrDigit(ch) || ch=='_'</code>". Subclasses can
* override this method to change what characters are matched.
*
* @param ch
* The character.
* @param ch The character.
* @return Whether the character is valid.
*/
protected boolean isValidChar(char ch) {
@@ -238,10 +236,8 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
* Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param file
* An XML file to load from.
* @throws IOException
* If an IO error occurs.
* @param file An XML file to load from.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(File file) throws IOException {
BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file));
@@ -253,32 +249,26 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
}
/**
* Loads completions from an XML input stream. The XML should validate
* against <code>CompletionXml.dtd</code>.
* Loads completions from an XML input stream. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param in
* The input stream to read from.
* @throws IOException
* If an IO error occurs.
* @param in The input stream to read from.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(InputStream in) throws IOException {
loadFromXML(in, null);
}
/**
* Loads completions from an XML input stream. The XML should validate
* against <code>CompletionXml.dtd</code>.
* Loads completions from an XML input stream. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param in
* The input stream to read from.
* @param cl
* The class loader to use when loading any extra classes defined
* in the XML, such as custom {@link FunctionCompletion}s. This
* may be <code>null</code> if the default is to be used, or if
* no
* custom completions are defined in the XML.
* @throws IOException
* If an IO error occurs.
* @param in The input stream to read from.
* @param cl The class loader to use when loading any extra classes defined in
* the XML, such as custom {@link FunctionCompletion}s. This may be
* <code>null</code> if the default is to be used, or if no custom
* completions are defined in the XML.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(InputStream in, ClassLoader cl) throws IOException {
// long start = System.currentTimeMillis();
@@ -314,10 +304,8 @@ public class DefaultCompletionProvider extends AbstractCompletionProvider {
* Loads completions from an XML file. The XML should validate against
* <code>CompletionXml.dtd</code>.
*
* @param resource
* A resource the current ClassLoader can get to.
* @throws IOException
* If an IO error occurs.
* @param resource A resource the current ClassLoader can get to.
* @throws IOException If an IO error occurs.
*/
public void loadFromXML(String resource) throws IOException {
ClassLoader cl = getClass().getClassLoader();
@@ -15,9 +15,9 @@ import javax.swing.JList;
import javax.swing.ListCellRenderer;
/**
* List cell renderer that delegates to a {@link CompletionProvider}'s
* renderer, if it has one. If it doesn't, it calls into a fallback renderer.
* If a fallback renderer isn't specified, it simply renders
* List cell renderer that delegates to a {@link CompletionProvider}'s renderer,
* if it has one. If it doesn't, it calls into a fallback renderer. If a
* fallback renderer isn't specified, it simply renders
* <code>(({@link Completion})value).toString()</code>.
*
* @author Robert Futrell
@@ -26,8 +26,8 @@ import javax.swing.ListCellRenderer;
class DelegatingCellRenderer extends DefaultListCellRenderer {
/**
* The renderer to fall back on if one isn't specified by a provider.
* This is usually <tt>this</tt>.
* The renderer to fall back on if one isn't specified by a provider. This is
* usually <tt>this</tt>.
*/
private ListCellRenderer fallback;
@@ -45,7 +45,8 @@ class DelegatingCellRenderer extends DefaultListCellRenderer {
* {@inheritDoc}
*/
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected,
boolean hasFocus) {
Completion c = (Completion) value;
CompletionProvider p = c.getProvider();
ListCellRenderer r = p.getListCellRenderer();
@@ -61,9 +62,8 @@ class DelegatingCellRenderer extends DefaultListCellRenderer {
/**
* Sets the fallback cell renderer.
*
* @param fallback
* The fallback cell renderer. If this is
* <code>null</code>, <tt>this</tt> will be used.
* @param fallback The fallback cell renderer. If this is <code>null</code>,
* <tt>this</tt> will be used.
* @see #getFallbackCellRenderer()
*/
public void setFallbackCellRenderer(ListCellRenderer fallback) {
@@ -16,13 +16,11 @@ package org.fife.ui.autocomplete;
public interface DescWindowCallback {
/**
* Callback allowing a new code completion's description to be displayed
* in the description window.
* Callback allowing a new code completion's description to be displayed in the
* description window.
*
* @param completion
* The new completion.
* @param anchor
* The anchor to scroll to, or <code>null</code> if none.
* @param completion The new completion.
* @param anchor The anchor to scroll to, or <code>null</code> if none.
*/
public void showSummaryFor(Completion completion, String anchor);
}
+2 -1
View File
@@ -39,5 +39,6 @@ public class EmptyIcon implements Icon, Serializable {
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {}
public void paintIcon(Component c, Graphics g, int x, int y) {
}
}
@@ -10,12 +10,12 @@ package org.fife.ui.autocomplete;
import javax.swing.event.HyperlinkEvent;
/**
* A callback for when an external URL is clicked in the description window.
* If no handler is installed, and if running in Java 6, the system default
* web browser is used to open the URL. If not running Java 6, nothing will
* happen. If you want browser support for pre-Java 6 JRE's, you will need
* to register one of these callbacks on your {@link AutoCompletion}, and
* open the URL in a web browser yourself.
* A callback for when an external URL is clicked in the description window. If
* no handler is installed, and if running in Java 6, the system default web
* browser is used to open the URL. If not running Java 6, nothing will happen.
* If you want browser support for pre-Java 6 JRE's, you will need to register
* one of these callbacks on your {@link AutoCompletion}, and open the URL in a
* web browser yourself.
* <p>
*
* Alternatively, folks implementing robust code completion support for a
@@ -31,13 +31,9 @@ public interface ExternalURLHandler {
/**
* Called when an external URL is clicked in the description window.
*
* @param e
* The event containing the hyperlink clicked.
* @param c
* The completion currently being displayed.
* @param callback
* Allows you to display new content in the description
* window.
* @param e The event containing the hyperlink clicked.
* @param c The completion currently being displayed.
* @param callback Allows you to display new content in the description window.
*/
public void urlClicked(HyperlinkEvent e, Completion c, DescWindowCallback callback);
}
+21 -23
View File
@@ -20,14 +20,14 @@ import javax.swing.plaf.basic.BasicListUI;
/**
* A custom list UI, used by the completion choices list. If the number of
* completion choices is "large," it does a fast estimate of the preferred
* width and height of list items. This allows HTML renderers to be used (such
* as {@link CompletionCellRenderer}), with thousands of completion choices,
* with no performance penalty. With standard BasicListUI subclasses, this can
* cause very poor performance <b>each time</b> the list is displayed, which
* is bad for lists that are repeatedly hidden and re-displayed, such as
* completion choices. This is all because the calculation to get the
* preferred size of each list item, when it is displayed with HTML, is slow.
* completion choices is "large," it does a fast estimate of the preferred width
* and height of list items. This allows HTML renderers to be used (such as
* {@link CompletionCellRenderer}), with thousands of completion choices, with
* no performance penalty. With standard BasicListUI subclasses, this can cause
* very poor performance <b>each time</b> the list is displayed, which is bad
* for lists that are repeatedly hidden and re-displayed, such as completion
* choices. This is all because the calculation to get the preferred size of
* each list item, when it is displayed with HTML, is slow.
*
* @author Robert Futrell
* @version 1.0
@@ -35,19 +35,19 @@ import javax.swing.plaf.basic.BasicListUI;
class FastListUI extends BasicListUI {
/**
* Whether the selection background was overridden (usually because of
* Nimbus) so we know to manually uninstall the color we installed.
* Whether the selection background was overridden (usually because of Nimbus)
* so we know to manually uninstall the color we installed.
*/
private boolean overriddenBackground;
/**
* Whether the selection foreground was overridden (usually because of
* Nimbus) so we know to manually uninstall the color we installed.
* Whether the selection foreground was overridden (usually because of Nimbus)
* so we know to manually uninstall the color we installed.
*/
private boolean overriddenForeground;
/**
* If there are more than this many completions in a single list, this
* UI will estimate the cell width and height needed for each item instead
* of computing it, for performance reasons.
* If there are more than this many completions in a single list, this UI will
* estimate the cell width and height needed for each item instead of computing
* it, for performance reasons.
*/
private static final int ESTIMATION_THRESHOLD = 200;
@@ -86,10 +86,9 @@ class FastListUI extends BasicListUI {
}
/**
* Overridden to ensure we have selection background/foreground colors
* defined, even if we're in some weirdo LAF such as Nimbus which doesn't
* define them. Since FastListUI extends BasicListUI, we need these values
* to be defined.
* Overridden to ensure we have selection background/foreground colors defined,
* even if we're in some weirdo LAF such as Nimbus which doesn't define them.
* Since FastListUI extends BasicListUI, we need these values to be defined.
*/
@Override
protected void installDefaults() {
@@ -119,10 +118,9 @@ class FastListUI extends BasicListUI {
}
/**
* Recalculates the cell width and height of each cell in the list. This
* method is overridden to do a fast estimation if the completion list is
* too long, to improve performance for lists with huge amounts of
* completions.
* Recalculates the cell width and height of each cell in the list. This method
* is overridden to do a fast estimation if the completion list is too long, to
* improve performance for lists with huge amounts of completions.
*/
@Override
protected void updateLayoutState() {
@@ -37,12 +37,9 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
/**
* Constructor.
*
* @param provider
* The parent provider.
* @param name
* The name of this function.
* @param returnType
* The return type of this function.
* @param provider The parent provider.
* @param name The name of this function.
* @param returnType The return type of this function.
*/
public FunctionCompletion(CompletionProvider provider, String name, String returnType) {
super(provider, name, returnType);
@@ -58,8 +55,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
/**
* Adds HTML describing the parameters to this function to a buffer.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void addParameters(StringBuilder sb) {
// TODO: Localize me
@@ -90,8 +86,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
/**
* Overridden to compare methods by their comparison strings.
*
* @param c2
* A <code>Completion</code> to compare to.
* @param c2 A <code>Completion</code> to compare to.
* @return The sort order.
*/
@Override
@@ -114,11 +109,9 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
*/
private String getCompareString() {
/*
* This string compares the following parts of methods in this order,
* to optimize sort order in completion lists.
* 1. First, by name
* 2. Next, by number of parameters.
* 3. Finally, by parameter type.
* This string compares the following parts of methods in this order, to
* optimize sort order in completion lists. 1. First, by name 2. Next, by number
* of parameters. 3. Finally, by parameter type.
*/
if (compareString == null) {
StringBuilder sb = new StringBuilder(getName());
@@ -141,8 +134,8 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
}
/**
* Returns the "definition string" for this function completion. For
* example, for the C "<code>printf</code>" function, this would return
* Returns the "definition string" for this function completion. For example,
* for the C "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>".
*
* @return The definition string.
@@ -269,8 +262,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
/**
* Returns the text to insert for a parameter.
*
* @param param
* The parameter.
* @param param The parameter.
* @return The text.
*/
private String getParamText(ParameterizedCompletion.Parameter param) {
@@ -324,8 +316,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
/**
* Sets the parameters to this function.
*
* @param params
* The parameters. This should be a list of
* @param params The parameters. This should be a list of
* {@link ParameterizedCompletion.Parameter}s.
* @see #getParam(int)
* @see #getParamCount()
@@ -340,8 +331,7 @@ public class FunctionCompletion extends VariableCompletion implements Parameteri
/**
* Sets the description of the return value of this function.
*
* @param desc
* The description.
* @param desc The description.
* @see #getReturnValueDescription()
*/
public void setReturnValueDescription(String desc) {
@@ -33,13 +33,13 @@ import org.fife.ui.rtextarea.ToolTipSupplier;
* <li>A documentation comment</li>
* </ul>
*
* This allows for different completion choices in comments than in code,
* for example.
* This allows for different completion choices in comments than in code, for
* example.
* <p>
*
* This provider also implements the
* <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it
* to display tooltips for completion choices. Thus the standard
* <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> interface, which allows it to
* display tooltips for completion choices. Thus the standard
* {@link VariableCompletion} and {@link FunctionCompletion} completions should
* be able to display tooltips with the variable declaration or function
* definition (provided the <tt>RSyntaxTextArea</tt> was registered with the
@@ -51,8 +51,7 @@ import org.fife.ui.rtextarea.ToolTipSupplier;
public class LanguageAwareCompletionProvider extends CompletionProviderBase implements ToolTipSupplier {
/**
* The provider to use when no provider is assigned to a particular token
* type.
* The provider to use when no provider is assigned to a particular token type.
*/
private CompletionProvider defaultProvider;
/**
@@ -69,32 +68,31 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
private CompletionProvider docCommentCompletionProvider;
/**
* Constructor subclasses can use when they don't have their default
* provider created at construction time. They should call
* Constructor subclasses can use when they don't have their default provider
* created at construction time. They should call
* {@link #setDefaultCompletionProvider(CompletionProvider)} in this
* constructor.
*/
protected LanguageAwareCompletionProvider() {}
protected LanguageAwareCompletionProvider() {
}
/**
* Constructor.
*
* @param defaultProvider
* The provider to use when no provider is assigned
* to a particular token type. This cannot be <code>null</code>.
* @param defaultProvider The provider to use when no provider is assigned to a
* particular token type. This cannot be
* <code>null</code>.
*/
public LanguageAwareCompletionProvider(CompletionProvider defaultProvider) {
setDefaultCompletionProvider(defaultProvider);
}
/**
* Calling this method will result in an
* {@link UnsupportedOperationException} being thrown. To set the
* parameter completion parameters, do so on the provider returned by
* {@link #getDefaultCompletionProvider()}.
* Calling this method will result in an {@link UnsupportedOperationException}
* being thrown. To set the parameter completion parameters, do so on the
* provider returned by {@link #getDefaultCompletionProvider()}.
*
* @throws UnsupportedOperationException
* Always.
* @throws UnsupportedOperationException Always.
* @see #setParameterizedCompletionParams(char, String, char)
*/
@Override
@@ -135,10 +133,8 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
/**
* Does the dirty work of creating a list of completions.
*
* @param comp
* The text component to look in.
* @return The list of possible completions, or an empty list if there
* are none.
* @param comp The text component to look in.
* @return The list of possible completions, or an empty list if there are none.
*/
@Override
protected List<Completion> getCompletionsImpl(JTextComponent comp) {
@@ -152,8 +148,8 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
}
/**
* Returns the completion provider used when one isn't defined for a
* particular token type.
* Returns the completion provider used when one isn't defined for a particular
* token type.
*
* @return The completion provider to use.
* @see #setDefaultCompletionProvider(CompletionProvider)
@@ -209,11 +205,10 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
}
/**
* Returns the completion provider to use at the current caret position in
* a text component.
* Returns the completion provider to use at the current caret position in a
* text component.
*
* @param comp
* The text component to check.
* @param comp The text component to check.
* @return The completion provider to use.
*/
private CompletionProvider getProviderFor(JTextComponent comp) {
@@ -304,8 +299,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
/**
* Sets the comment completion provider.
*
* @param provider
* The provider to use in comments.
* @param provider The provider to use in comments.
* @see #getCommentCompletionProvider()
*/
public void setCommentCompletionProvider(CompletionProvider provider) {
@@ -315,8 +309,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
/**
* Sets the default completion provider.
*
* @param provider
* The provider to use when no provider is assigned to a
* @param provider The provider to use when no provider is assigned to a
* particular token type. This cannot be <code>null</code>.
* @see #getDefaultCompletionProvider()
*/
@@ -330,8 +323,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
/**
* Sets the documentation comment completion provider.
*
* @param provider
* The provider to use in comments.
* @param provider The provider to use in comments.
* @see #getDocCommentCompletionProvider()
*/
public void setDocCommentCompletionProvider(CompletionProvider provider) {
@@ -339,13 +331,11 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
}
/**
* Calling this method will result in an
* {@link UnsupportedOperationException} being thrown. To set the
* parameter completion parameters, do so on the provider returned by
* {@link #getDefaultCompletionProvider()}.
* Calling this method will result in an {@link UnsupportedOperationException}
* being thrown. To set the parameter completion parameters, do so on the
* provider returned by {@link #getDefaultCompletionProvider()}.
*
* @throws UnsupportedOperationException
* Always.
* @throws UnsupportedOperationException Always.
* @see #clearParameterizedCompletionParams()
*/
@Override
@@ -356,8 +346,7 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
/**
* Sets the completion provider to use while in a string.
*
* @param provider
* The provider to use.
* @param provider The provider to use.
* @see #getStringCompletionProvider()
*/
public void setStringCompletionProvider(CompletionProvider provider) {
@@ -368,17 +357,15 @@ public class LanguageAwareCompletionProvider extends CompletionProviderBase impl
* Returns the tool tip to display for a mouse event.
* <p>
*
* For this method to be called, the <tt>RSyntaxTextArea</tt> must be
* registered with the <tt>javax.swing.ToolTipManager</tt> like so:
* For this method to be called, the <tt>RSyntaxTextArea</tt> must be registered
* with the <tt>javax.swing.ToolTipManager</tt> like so:
*
* <pre>
* ToolTipManager.sharedInstance().registerComponent(textArea);
* </pre>
*
* @param textArea
* The text area.
* @param e
* The mouse event.
* @param textArea The text area.
* @param e The mouse event.
* @return The tool tip text, or <code>null</code> if none.
*/
@Override
@@ -8,9 +8,8 @@ package org.fife.ui.autocomplete;
import java.net.URL;
/**
* Possibly redirects one URL to another. Useful if you want "external" URL's
* in code completion documentation to point to a local copy instead, for
* example.
* Possibly redirects one URL to another. Useful if you want "external" URL's in
* code completion documentation to point to a local copy instead, for example.
*
* @author Robert Futrell
* @version 1.0
@@ -31,10 +31,8 @@ public class MarkupTagCompletion extends AbstractCompletion {
/**
* Constructor.
*
* @param provider
* The parent provider instance.
* @param name
* The name of the tag.
* @param provider The parent provider instance.
* @param name The name of the tag.
*/
public MarkupTagCompletion(CompletionProvider provider, String name) {
super(provider);
@@ -44,8 +42,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
/**
* Adds HTML describing the attributes of this tag to a buffer.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void addAttributes(StringBuilder sb) {
// TODO: Localize me.
@@ -86,8 +83,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
/**
* Returns the specified {@link ParameterizedCompletion.Parameter}.
*
* @param index
* The index of the attribute to retrieve.
* @param index The index of the attribute to retrieve.
* @return The attribute.
* @see #getAttributeCount()
*/
@@ -116,11 +112,9 @@ public class MarkupTagCompletion extends AbstractCompletion {
}
/**
* Returns a short description of this variable. This should be an
* HTML snippet.
* Returns a short description of this variable. This should be an HTML snippet.
*
* @return A short description of this variable. This may be
* <code>null</code>.
* @return A short description of this variable. This may be <code>null</code>.
* @see #setDescription(String)
*/
public String getDescription() {
@@ -158,11 +152,10 @@ public class MarkupTagCompletion extends AbstractCompletion {
}
/**
* Adds some HTML describing where this variable is defined, if this
* information is known.
* Adds some HTML describing where this variable is defined, if this information
* is known.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn != null) {
@@ -172,11 +165,9 @@ public class MarkupTagCompletion extends AbstractCompletion {
}
/**
* Adds the description text as HTML to a buffer, if a description is
* defined.
* Adds the description text as HTML to a buffer, if a description is defined.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void possiblyAddDescription(StringBuilder sb) {
if (desc != null) {
@@ -189,8 +180,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
/**
* Sets where this variable is defined.
*
* @param definedIn
* Where this variable is defined.
* @param definedIn Where this variable is defined.
* @see #getDefinedIn()
*/
public void setDefinedIn(String definedIn) {
@@ -198,12 +188,9 @@ public class MarkupTagCompletion extends AbstractCompletion {
}
/**
* Sets the short description of this tag. This should be an
* HTML snippet.
* Sets the short description of this tag. This should be an HTML snippet.
*
* @param desc
* A short description of this tag. This may be
* <code>null</code>.
* @param desc A short description of this tag. This may be <code>null</code>.
* @see #getDescription()
*/
public void setDescription(String desc) {
@@ -213,8 +200,7 @@ public class MarkupTagCompletion extends AbstractCompletion {
/**
* Sets the attributes of this tag.
*
* @param attrs
* The attributes.
* @param attrs The attributes.
* @see #getAttribute(int)
* @see #getAttributeCount()
*/
@@ -19,16 +19,16 @@ import javax.swing.text.Position;
import javax.swing.text.View;
/**
* Highlight painter that draws an outline around the text. This is used to
* draw bounds around function/method parameters.
* Highlight painter that draws an outline around the text. This is used to draw
* bounds around function/method parameters.
*
* @author Robert Futrell
* @version 1.0
*/
/*
* NOTE: Whenever you see text like "Workaround for Java Highlight issues",
* this is because highlighted text in a JTextComponent gets "pushed" forward
* when the caret is at the Highlight's start, when we need it to instead get
* NOTE: Whenever you see text like "Workaround for Java Highlight issues", this
* is because highlighted text in a JTextComponent gets "pushed" forward when
* the caret is at the Highlight's start, when we need it to instead get
* prepended to. For this reason, the auto-complete package adds its Highlights
* 1 char too long (1 char earlier than where it should really start), but only
* paint the Highlight from the 2nd char on.
@@ -36,17 +36,16 @@ import javax.swing.text.View;
class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter {
/**
* DefaultHighlightPainter doesn't allow changing color, so we must cache
* ours here.
* DefaultHighlightPainter doesn't allow changing color, so we must cache ours
* here.
*/
private Color color;
/**
* Constructor.
*
* @param color
* The color to draw the bounding boxes with. This cannot
* be <code>null</code>.
* @param color The color to draw the bounding boxes with. This cannot be
* <code>null</code>.
*/
public OutlineHighlightPainter(Color color) {
super(color);
@@ -114,8 +113,7 @@ class OutlineHighlightPainter extends DefaultHighlighter.DefaultHighlightPainter
/**
* Sets the color to paint the bounding boxes with.
*
* @param color
* The new color. This cannot be <code>null</code>.
* @param color The new color. This cannot be <code>null</code>.
* @see #getColor()
*/
public void setColor(Color color) {
@@ -12,9 +12,9 @@ import java.util.List;
import javax.swing.text.JTextComponent;
/**
* Provides completions for a {@link ParameterizedCompletion}'s parameters.
* So, for example, if the user code-completes a function or method, if
* a <code>ParameterChoicesProvider</code> is installed, it can return possible
* Provides completions for a {@link ParameterizedCompletion}'s parameters. So,
* for example, if the user code-completes a function or method, if a
* <code>ParameterChoicesProvider</code> is installed, it can return possible
* completions for the parameters to that function or method.
*
* @author Robert Futrell
@@ -25,12 +25,10 @@ public interface ParameterChoicesProvider {
/**
* Returns a list of choices for a specific parameter.
*
* @param tc
* The text component.
* @param param
* The currently focused parameter.
* @return The list of parameters. This may be <code>null</code> for
* "no parameters," but might also be an empty list.
* @param tc The text component.
* @param param The currently focused parameter.
* @return The list of parameters. This may be <code>null</code> for "no
* parameters," but might also be an empty list.
*/
public List<Completion> getParameterChoices(JTextComponent tc, ParameterizedCompletion.Parameter param);
}
@@ -17,8 +17,8 @@ import javax.swing.text.JTextComponent;
public interface ParameterizedCompletion extends Completion {
/**
* Returns the "definition string" for this completion. For example,
* for the C "<code>printf</code>" function, this would return
* Returns the "definition string" for this completion. For example, for the C
* "<code>printf</code>" function, this would return
* "<code>int printf(const char *, ...)</code>".
*
* @return The definition string.
@@ -28,8 +28,7 @@ public interface ParameterizedCompletion extends Completion {
/**
* Returns the specified {@link Parameter}.
*
* @param index
* The index of the parameter to retrieve.
* @param index The index of the parameter to retrieve.
* @return The parameter.
* @see #getParamCount()
*/
@@ -46,8 +45,8 @@ public interface ParameterizedCompletion extends Completion {
public ParameterizedCompletionInsertionInfo getInsertionInfo(JTextComponent tc, boolean replaceTabsWithSpaces);
/**
* Returns whether a tool tip displaying assistance for each parameter
* while it is being edited is appropriate for this completion.
* Returns whether a tool tip displaying assistance for each parameter while it
* is being edited is appropriate for this completion.
*
* @return Whether the tool tip is appropriate to display.
*/
@@ -66,16 +65,13 @@ public interface ParameterizedCompletion extends Completion {
/**
* Constructor.
*
* @param type
* The type of this parameter. This may be
* <code>null</code> for languages without specific types,
* dynamic typing, etc. Usually you'll pass a String for this
* value, but you may pass any object representing a type in
* your language, as long as its <code>toString()</code>
* method
* returns a string representation of the type.
* @param name
* The name of the parameter.
* @param type The type of this parameter. This may be <code>null</code> for
* languages without specific types, dynamic typing, etc. Usually
* you'll pass a String for this value, but you may pass any object
* representing a type in your language, as long as its
* <code>toString()</code> method returns a string representation of
* the type.
* @param name The name of the parameter.
*/
public Parameter(Object type, String name) {
this(type, name, false);
@@ -84,25 +80,19 @@ public interface ParameterizedCompletion extends Completion {
/**
* Constructor.
*
* @param type
* The type of this parameter. This may be
* <code>null</code> for languages without specific types,
* dynamic typing, etc. Usually you'll pass a String for this
* value, but you may pass any object representing a type in
* your language, as long as its <code>toString()</code>
* method
* returns a string representation of the type.
* @param name
* The name of the parameter.
* @param endParam
* Whether this parameter is an "ending parameter;"
* that is, whether this parameter is at a logical "ending
* point" in the completion text. If the user types in a
* parameter that is an ending point, parameter completion
* mode
* terminates. Set this to <code>true</code> for a trailing
* parameter after a function call's closing ')', for
* example.
* @param type The type of this parameter. This may be <code>null</code> for
* languages without specific types, dynamic typing, etc.
* Usually you'll pass a String for this value, but you may pass
* any object representing a type in your language, as long as
* its <code>toString()</code> method returns a string
* representation of the type.
* @param name The name of the parameter.
* @param endParam Whether this parameter is an "ending parameter;" that is,
* whether this parameter is at a logical "ending point" in the
* completion text. If the user types in a parameter that is an
* ending point, parameter completion mode terminates. Set this
* to <code>true</code> for a trailing parameter after a
* function call's closing ')', for example.
*/
public Parameter(Object type, String name, boolean endParam) {
this.name = name;
@@ -137,11 +127,10 @@ public interface ParameterizedCompletion extends Completion {
}
/**
* @return Whether this parameter is an "ending parameter;"
* that is, whether this parameter is at a logical "ending
* point" in the completion text. If the user types in a
* parameter that is an ending point, parameter completion mode
* terminates.
* @return Whether this parameter is an "ending parameter;" that is, whether
* this parameter is at a logical "ending point" in the completion text.
* If the user types in a parameter that is an ending point, parameter
* completion mode terminates.
*/
public boolean isEndParam() {
return isEndParam;
@@ -28,11 +28,11 @@ import javax.swing.text.JTextComponent;
import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
/**
* A small popup window offering a list of likely choices for a parameter
* when the user has code-completed a parameterized completion. For example,
* if they have just code-completed the C function "<code>fprintf</code>",
* when entering the file name, this popup might display all local variables
* of type "<code>char *</code>".
* A small popup window offering a list of likely choices for a parameter when
* the user has code-completed a parameterized completion. For example, if they
* have just code-completed the C function "<code>fprintf</code>", when entering
* the file name, this popup might display all local variables of type
* "<code>char *</code>".
*
* @author Robert Futrell
* @version 1.0
@@ -60,22 +60,20 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
*/
private JScrollPane sp;
/**
* Comparator used to sort completions by their relevance before sorting
* them lexicographically.
* Comparator used to sort completions by their relevance before sorting them
* lexicographically.
*/
private static final Comparator<Completion> sortByRelevanceComparator = new SortByRelevanceComparator();
/**
* Constructor.
*
* @param parent
* The parent window (hosting the text component).
* @param ac
* The auto-completion instance.
* @param context
* The completion context.
* @param parent The parent window (hosting the text component).
* @param ac The auto-completion instance.
* @param context The completion context.
*/
public ParameterizedCompletionChoicesWindow(Window parent, AutoCompletion ac, final ParameterizedCompletionContext context) {
public ParameterizedCompletionChoicesWindow(Window parent, AutoCompletion ac,
final ParameterizedCompletionContext context) {
super(parent);
this.ac = ac;
ComponentOrientation o = ac.getTextComponentOrientation();
@@ -107,8 +105,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* Returns the selected value.
*
* @return The selected value, or <code>null</code> if nothing is
* selected.
* @return The selected value, or <code>null</code> if nothing is selected.
*/
public String getSelectedChoice() {
Completion c = (Completion) list.getSelectedValue();
@@ -118,8 +115,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* Changes the selected index.
*
* @param amount
* The amount by which to change the selected index.
* @param amount The amount by which to change the selected index.
*/
public void incSelection(int amount) {
int selection = list.getSelectedIndex();
@@ -135,12 +131,10 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
}
/**
* Initializes this window to offer suggestions for the parameters of
* a specific completion.
* Initializes this window to offer suggestions for the parameters of a specific
* completion.
*
* @param pc
* The completion whose parameters we should offer suggestions
* for.
* @param pc The completion whose parameters we should offer suggestions for.
*/
public void initialize(ParameterizedCompletion pc) {
CompletionProvider provider = pc.getProvider();
@@ -162,8 +156,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* Sets the location of this window relative to the given rectangle.
*
* @param r
* The visual position of the caret (in screen coordinates).
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
@@ -189,16 +182,14 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
}
/**
* Displays the choices for the specified parameter matching the given
* text. This will display or hide this popup window as necessary.
* Displays the choices for the specified parameter matching the given text.
* This will display or hide this popup window as necessary.
*
* @param param
* The index of the parameter the caret is currently in.
* This may be <code>-1</code> if not in a parameter (i.e., on
* the comma between parameters).
* @param prefix
* Text in the parameter before the dot. This may
* be <code>null</code> to represent the empty string.
* @param param The index of the parameter the caret is currently in. This may
* be <code>-1</code> if not in a parameter (i.e., on the comma
* between parameters).
* @param prefix Text in the parameter before the dot. This may be
* <code>null</code> to represent the empty string.
*/
public void setParameter(int param, String prefix) {
model.clear();
@@ -254,8 +245,7 @@ public class ParameterizedCompletionChoicesWindow extends JWindow {
/**
* Toggles the visibility of this popup window.
*
* @param visible
* Whether this window should be visible.
* @param visible Whether this window should be visible.
*/
@Override
public void setVisible(boolean visible) {
@@ -48,8 +48,8 @@ import org.fife.ui.rtextarea.ChangeableHighlightPainter;
/**
* Manages UI and state specific to parameterized completions - the parameter
* description tool tip, the parameter completion choices list, the actual
* highlights in the editor, etc. This component installs new key bindings
* when appropriate to allow the user to cycle through the parameters of the
* highlights in the editor, etc. This component installs new key bindings when
* appropriate to allow the user to cycle through the parameters of the
* completion, and optionally cycle through completion choices for those
* parameters.
*
@@ -95,13 +95,13 @@ class ParameterizedCompletionContext {
*/
private Listener listener;
/**
* The minimum offset into the document that the caret can move to
* before this tool tip disappears.
* The minimum offset into the document that the caret can move to before this
* tool tip disappears.
*/
private int minPos;
/**
* The maximum offset into the document that the caret can move to
* before this tool tip disappears.
* The maximum offset into the document that the caret can move to before this
* tool tip disappears.
*/
private Position maxPos; // Moves with text inserted.
private Position defaultEndOffs;
@@ -208,8 +208,7 @@ class ParameterizedCompletionContext {
}
/**
* Hides any popup windows and terminates parameterized completion
* assistance.
* Hides any popup windows and terminates parameterized completion assistance.
*
* @see #activate()
*/
@@ -228,13 +227,11 @@ class ParameterizedCompletionContext {
}
/**
* Returns the text inserted for the parameter containing the specified
* offset.
* Returns the text inserted for the parameter containing the specified offset.
*
* @param offs
* The offset into the document.
* @return The text of the parameter containing the offset, or
* <code>null</code> if the offset is not in a parameter.
* @param offs The offset into the document.
* @return The text of the parameter containing the offset, or <code>null</code>
* if the offset is not in a parameter.
*/
public String getArgumentText(int offs) {
List<Highlight> paramHighlights = getParameterHighlights();
@@ -262,8 +259,8 @@ class ParameterizedCompletionContext {
/**
* Returns the highlight of the current parameter.
*
* @return The current parameter's highlight, or <code>null</code> if
* the caret is not in a parameter's bounds.
* @return The current parameter's highlight, or <code>null</code> if the caret
* is not in a parameter's bounds.
* @see #getCurrentParameterStartOffset()
*/
private Highlight getCurrentParameterHighlight() {
@@ -300,8 +297,8 @@ class ParameterizedCompletionContext {
/**
* Returns the starting offset of the current parameter.
*
* @return The current parameter's starting offset, or <code>-1</code> if
* the caret is not in a parameter's bounds.
* @return The current parameter's starting offset, or <code>-1</code> if the
* caret is not in a parameter's bounds.
* @see #getCurrentParameterHighlight()
*/
private int getCurrentParameterStartOffset() {
@@ -310,13 +307,12 @@ class ParameterizedCompletionContext {
}
/**
* Returns the highlight from a list that comes "first" in a list. Even
* though most parameter highlights are ordered, sometimes they aren't
* (e.g. the "cursor" parameter in a template completion is always last,
* even though it can be anywhere in the template).
* Returns the highlight from a list that comes "first" in a list. Even though
* most parameter highlights are ordered, sometimes they aren't (e.g. the
* "cursor" parameter in a template completion is always last, even though it
* can be anywhere in the template).
*
* @param highlights
* The list of highlights. Assumed to be non-empty.
* @param highlights The list of highlights. Assumed to be non-empty.
* @return The highlight that comes first in the document.
* @see #getLastHighlight(List)
*/
@@ -334,13 +330,12 @@ class ParameterizedCompletionContext {
}
/**
* Returns the highlight from a list that comes "last" in that list. Even
* though most parameter highlights are ordered, sometimes they aren't
* (e.g. the "cursor" parameter in a template completion is always last,
* even though it can be anywhere in the template.
* Returns the highlight from a list that comes "last" in that list. Even though
* most parameter highlights are ordered, sometimes they aren't (e.g. the
* "cursor" parameter in a template completion is always last, even though it
* can be anywhere in the template.
*
* @param highlights
* The list of highlights. Assumed to be non-empty.
* @param highlights The list of highlights. Assumed to be non-empty.
* @return The highlight that comes last in the document.
* @see #getFirstHighlight(List)
*/
@@ -373,8 +368,8 @@ class ParameterizedCompletionContext {
/**
* Inserts the choice selected in the parameter choices window.
*
* @return Whether the choice was inserted. This will be <code>false</code>
* if the window is not visible, or no choice is selected.
* @return Whether the choice was inserted. This will be <code>false</code> if
* the window is not visible, or no choice is selected.
*/
boolean insertSelectedChoice() {
if (paramChoicesWindow != null && paramChoicesWindow.isVisible()) {
@@ -398,8 +393,8 @@ class ParameterizedCompletionContext {
}
/**
* Installs key bindings on the text component that facilitate the user
* editing this completion's parameters.
* Installs key bindings on the text component that facilitate the user editing
* this completion's parameters.
*
* @see #uninstallKeyBindings()
*/
@@ -507,8 +502,8 @@ class ParameterizedCompletionContext {
List<Highlight> highlights = getParameterHighlights();
for (int i = 0; i < highlights.size(); i++) {
Highlight h = highlights.get(i);
if (currentPrev == null || currentPrev.getStartOffset() >= dot
|| (h.getStartOffset() < selStart && (h.getStartOffset() > currentPrev.getStartOffset() || pos == lastSelectedParam))) {
if (currentPrev == null || currentPrev.getStartOffset() >= dot || (h.getStartOffset() < selStart
&& (h.getStartOffset() > currentPrev.getStartOffset() || pos == lastSelectedParam))) {
currentPrev = h;
pos = i;
}
@@ -613,17 +608,14 @@ class ParameterizedCompletionContext {
}
/**
* Replaces highlighted text with new text. Takes special care so that
* the highlight stays just around the newly-highlighted text, since
* Swing's <code>Highlight</code> classes are funny about insertions at
* their start offsets.
* Replaces highlighted text with new text. Takes special care so that the
* highlight stays just around the newly-highlighted text, since Swing's
* <code>Highlight</code> classes are funny about insertions at their start
* offsets.
*
* @param doc
* The document.
* @param h
* The highlight whose text to change.
* @param replacement
* The new text to be in the highlight.
* @param doc The document.
* @param h The highlight whose text to change.
* @param replacement The new text to be in the highlight.
* @return The replacement highlight for <code>h</code>.
*/
private Highlight replaceHighlightedText(Document doc, Highlight h, String replacement) {
@@ -684,9 +676,8 @@ class ParameterizedCompletionContext {
}
/**
* Updates the text in the tool tip to have the current parameter
* displayed in bold. The "current parameter" is determined from the
* current caret position.
* Updates the text in the tool tip to have the current parameter displayed in
* bold. The "current parameter" is determined from the current caret position.
*
* @return The "prefix" of text in the caret's parameter before the caret.
*/
@@ -779,8 +770,8 @@ class ParameterizedCompletionContext {
}
/**
* Called when the user types the character marking the closing of the
* parameter list, such as '<code>)</code>'.
* Called when the user types the character marking the closing of the parameter
* list, such as '<code>)</code>'.
*/
private class ClosingAction extends AbstractAction {
@@ -846,8 +837,8 @@ class ParameterizedCompletionContext {
}
/**
* Listens for various events in the text component while this tool tip
* is visible.
* Listens for various events in the text component while this tool tip is
* visible.
*/
private class Listener implements FocusListener, CaretListener, DocumentListener {
@@ -856,8 +847,7 @@ class ParameterizedCompletionContext {
/**
* Called when the text component's caret moves.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void caretUpdate(CaretEvent e) {
@@ -877,13 +867,13 @@ class ParameterizedCompletionContext {
}
@Override
public void changedUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {
}
/**
* Called when the text component gains focus.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void focusGained(FocusEvent e) {
@@ -893,8 +883,7 @@ class ParameterizedCompletionContext {
/**
* Called when the text component loses focus.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void focusLost(FocusEvent e) {
@@ -923,8 +912,7 @@ class ParameterizedCompletionContext {
/**
* Installs this listener onto a text component.
*
* @param tc
* The text component to install onto.
* @param tc The text component to install onto.
* @see #uninstall()
*/
public void install(JTextComponent tc) {
@@ -950,7 +938,8 @@ class ParameterizedCompletionContext {
}
for (int i = 0; i < info.getReplacementCopyCount(); i++) {
ReplacementCopy rc = info.getReplacementCopy(i);
paramCopyInfos.add(new ParamCopyInfo(rc.getId(), (Highlight) h.addHighlight(rc.getStart(), rc.getEnd(), paramCopyP)));
paramCopyInfos.add(new ParamCopyInfo(rc.getId(),
(Highlight) h.addHighlight(rc.getStart(), rc.getEnd(), paramCopyP)));
}
// Go back and start at the first parameter.
tc.setCaretPosition(info.getSelectionStart());
@@ -1007,8 +996,8 @@ class ParameterizedCompletionContext {
}
/**
* Action performed when the user presses the up or down arrow keys and
* the parameter completion choices popup is visible.
* Action performed when the user presses the up or down arrow keys and the
* parameter completion choices popup is visible.
*/
private class NextChoiceAction extends AbstractAction {
@@ -45,19 +45,17 @@ class ParameterizedCompletionDescriptionToolTip {
/**
* Constructor.
*
* @param owner
* The parent window.
* @param ac
* The parent auto-completion.
* @param pc
* The completion being described.
* @param owner The parent window.
* @param ac The parent auto-completion.
* @param pc The completion being described.
*/
public ParameterizedCompletionDescriptionToolTip(Window owner, ParameterizedCompletionContext context, AutoCompletion ac,
ParameterizedCompletion pc) {
public ParameterizedCompletionDescriptionToolTip(Window owner, ParameterizedCompletionContext context,
AutoCompletion ac, ParameterizedCompletion pc) {
tooltip = new JWindow(owner);
this.pc = pc;
descLabel = new JLabel();
descLabel.setBorder(BorderFactory.createCompoundBorder(TipUtil.getToolTipBorder(), BorderFactory.createEmptyBorder(2, 5, 2, 5)));
descLabel.setBorder(BorderFactory.createCompoundBorder(TipUtil.getToolTipBorder(),
BorderFactory.createEmptyBorder(2, 5, 2, 5)));
descLabel.setOpaque(true);
descLabel.setBackground(TipUtil.getToolTipBackground());
// It appears that if a JLabel is set as a content pane directly, when
@@ -91,8 +89,7 @@ class ParameterizedCompletionDescriptionToolTip {
/**
* Sets the location of this tool tip relative to the given rectangle.
*
* @param r
* The visual position of the caret (in screen coordinates).
* @param r The visual position of the caret (in screen coordinates).
*/
public void setLocationRelativeTo(Rectangle r) {
// Multi-monitor support - make sure the completion window (and
@@ -123,8 +120,7 @@ class ParameterizedCompletionDescriptionToolTip {
/**
* Toggles the visibility of this tool tip.
*
* @param visible
* Whether this tool tip should be visible.
* @param visible Whether this tool tip should be visible.
* @see #isVisible()
*/
public void setVisible(boolean visible) {
@@ -132,11 +128,10 @@ class ParameterizedCompletionDescriptionToolTip {
}
/**
* Updates the text in the tool tip to have the current parameter
* displayed in bold.
* Updates the text in the tool tip to have the current parameter displayed in
* bold.
*
* @param selectedParam
* The index of the selected parameter.
* @param selectedParam The index of the selected parameter.
* @return Whether the text needed to be updated.
*/
public boolean updateText(int selectedParam) {
@@ -172,8 +167,7 @@ class ParameterizedCompletionDescriptionToolTip {
}
/**
* Updates the <tt>LookAndFeel</tt> of this window and the description
* window.
* Updates the <tt>LookAndFeel</tt> of this window and the description window.
*/
public void updateUI() {
SwingUtilities.updateComponentTreeUI(tooltip);
@@ -44,13 +44,11 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Marks a region of the replacement text as representing a variable name
* or some other construct that the user should replace.
* Marks a region of the replacement text as representing a variable name or
* some other construct that the user should replace.
*
* @param start
* The start offset.
* @param end
* The end offset.
* @param start The start offset.
* @param end The end offset.
* @see #getReplacementCount()
* @see #getReplacementLocation(int)
*/
@@ -66,8 +64,8 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Returns the maximum offset the caret can move to before being outside
* of the text inserted for this completion.
* Returns the maximum offset the caret can move to before being outside of the
* text inserted for this completion.
*
* @return The maximum offset.
* @see #getMinOffset()
@@ -77,8 +75,8 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Returns the minimum offset the caret can move to before being outside
* of the text inserted for this completion.
* Returns the minimum offset the caret can move to before being outside of the
* text inserted for this completion.
*
* @return The minimum offset.
* @see #getMaxOffset()
@@ -105,11 +103,10 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Returns the starting- and ending-offsets of the replacement regions
* in the completion.
* Returns the starting- and ending-offsets of the replacement regions in the
* completion.
*
* @param index
* The replacement region.
* @param index The replacement region.
* @return The range in the document of that replacement region.
* @see #getReplacementCount()
*/
@@ -118,9 +115,9 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Returns the offset that should be the end of the initially selected
* text when the completion is inserted (i.e., the end offset of the first
* replacement region).
* Returns the offset that should be the end of the initially selected text when
* the completion is inserted (i.e., the end offset of the first replacement
* region).
*
* @return The end offset for the initial selection.
* @see #getSelectionStart()
@@ -130,9 +127,9 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Returns the offset that should be the start of the initially selected
* text when the completion is inserted (i.e., the start offset of the
* first replacement region).
* Returns the offset that should be the start of the initially selected text
* when the completion is inserted (i.e., the start offset of the first
* replacement region).
*
* @return The start offset for the initial selection.
* @see #getSelectionEnd()
@@ -152,8 +149,8 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Returns whether or not there is an initial selected region for the
* completion (i.e., whether the completion actually has any parameters).
* Returns whether or not there is an initial selected region for the completion
* (i.e., whether the completion actually has any parameters).
*
* @return Whether there is a region to initially select for the completion.
*/
@@ -164,10 +161,8 @@ class ParameterizedCompletionInsertionInfo {
/**
* Sets the initially selected region for the completion.
*
* @param selStart
* The selection start.
* @param selEnd
* The selection end.
* @param selStart The selection start.
* @param selEnd The selection end.
* @see #getSelectionEnd()
* @see #getSelectionStart()
*/
@@ -177,13 +172,11 @@ class ParameterizedCompletionInsertionInfo {
}
/**
* Sets the document range the caret can move around in before being
* outside of the text inserted for the completion.
* Sets the document range the caret can move around in before being outside of
* the text inserted for the completion.
*
* @param minOffs
* The minimum offset.
* @param maxOffs
* The maximum offset, that will track its location as the
* @param minOffs The minimum offset.
* @param maxOffs The maximum offset, that will track its location as the
* document is modified.
* @see #getMinOffset()
* @see #getMaxOffset()
@@ -200,8 +193,7 @@ class ParameterizedCompletionInsertionInfo {
/**
* Sets the text to insert for the completion.
*
* @param text
* The text to insert.
* @param text The text to insert.
* @see #getTextToInsert()
*/
public void setTextToInsert(String text) {
@@ -14,8 +14,8 @@ import javax.swing.Action;
/**
* An <code>AutoCompletion</code> that adds the ability to cycle through a set
* of <code>CompletionProvider</code>s via the trigger key. This allows the
* application to logically "group together" completions of similar kinds;
* for example, Java code completions vs. template completions.
* application to logically "group together" completions of similar kinds; for
* example, Java code completions vs. template completions.
* <p>
*
* Usage:
@@ -40,8 +40,7 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
/**
* Constructor.
*
* @param provider
* A single completion provider.
* @param provider A single completion provider.
* @see #addCompletionProvider(CompletionProvider)
*/
public RoundRobinAutoCompletion(CompletionProvider provider) {
@@ -60,23 +59,22 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
}
/**
* Adds an additional <code>CompletionProvider</code> to the list to
* cycle through.
* Adds an additional <code>CompletionProvider</code> to the list to cycle
* through.
*
* @param provider
* The new completion provider.
* @param provider The new completion provider.
*/
public void addCompletionProvider(CompletionProvider provider) {
cycle.add(provider);
}
/**
* Moves to the next Provider internally. Needs refresh of the popup window
* to display the changes.
* Moves to the next Provider internally. Needs refresh of the popup window to
* display the changes.
*
* @return true if the next provider was the default one (thus returned to
* the default view). May be used in case you like to hide the
* popup in this case.
* @return true if the next provider was the default one (thus returned to the
* default view). May be used in case you like to hide the popup in this
* case.
*/
public boolean advanceProvider() {
CompletionProvider currentProvider = getCompletionProvider();
@@ -106,8 +104,8 @@ public class RoundRobinAutoCompletion extends AutoCompletion {
/**
* An implementation of the auto-complete action that ensures the proper
* <code>CompletionProvider</code> is displayed based on the context in
* which the user presses the trigger key.
* <code>CompletionProvider</code> is displayed based on the context in which
* the user presses the trigger key.
*/
private class CycleAutoCompleteAction extends AutoCompleteAction {
@@ -26,12 +26,9 @@ public class ShorthandCompletion extends BasicCompletion {
/**
* Constructor.
*
* @param provider
* The provider that returns this completion.
* @param inputText
* The text the user inputs to get this completion.
* @param replacementText
* The replacement text of the completion.
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText) {
super(provider, replacementText);
@@ -41,18 +38,15 @@ public class ShorthandCompletion extends BasicCompletion {
/**
* Constructor.
*
* @param provider
* The provider that returns this completion.
* @param inputText
* The text the user inputs to get this completion.
* @param replacementText
* The replacement text of the completion.
* @param shortDesc
* A short description of the completion. This will be
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText, String shortDesc) {
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText,
String shortDesc) {
super(provider, replacementText, shortDesc);
this.inputText = inputText;
}
@@ -60,21 +54,17 @@ public class ShorthandCompletion extends BasicCompletion {
/**
* Constructor.
*
* @param provider
* The provider that returns this completion.
* @param inputText
* The text the user inputs to get this completion.
* @param replacementText
* The replacement text of the completion.
* @param shortDesc
* A short description of the completion. This will be
* @param provider The provider that returns this completion.
* @param inputText The text the user inputs to get this completion.
* @param replacementText The replacement text of the completion.
* @param shortDesc A short description of the completion. This will be
* displayed in the completion list. This may be
* <code>null</code>.
* @param summary
* The summary of this completion. This should be HTML.
* @param summary The summary of this completion. This should be HTML.
* This may be <code>null</code>.
*/
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText, String shortDesc, String summary) {
public ShorthandCompletion(CompletionProvider provider, String inputText, String replacementText, String shortDesc,
String summary) {
super(provider, replacementText, shortDesc, summary);
this.inputText = inputText;
}
@@ -93,8 +83,7 @@ public class ShorthandCompletion extends BasicCompletion {
* If a summary has been set, that summary is returned. Otherwise, the
* replacement text is returned.
*
* @return A description of this completion (the text that will be
* inserted).
* @return A description of this completion (the text that will be inserted).
* @see #getReplacementText()
*/
@Override
@@ -104,9 +93,9 @@ public class ShorthandCompletion extends BasicCompletion {
}
/**
* Returns the "body" of the HTML returned by {@link #getSummary()} when
* no summary text has been set. This is defined to return the replacement
* text in a monospaced font.
* Returns the "body" of the HTML returned by {@link #getSummary()} when no
* summary text has been set. This is defined to return the replacement text in
* a monospaced font.
*
* @return The summary text's body, if no other summary has been defined.
* @see #getReplacementText()
+9 -12
View File
@@ -51,11 +51,10 @@ class SizeGrip extends JPanel {
}
/**
* Overridden to ensure that the cursor for this component is appropriate
* for the orientation.
* Overridden to ensure that the cursor for this component is appropriate for
* the orientation.
*
* @param o
* The new orientation.
* @param o The new orientation.
*/
@Override
public void applyComponentOrientation(ComponentOrientation o) {
@@ -98,8 +97,7 @@ class SizeGrip extends JPanel {
/**
* Paints this panel.
*
* @param g
* The graphics context.
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
@@ -152,8 +150,7 @@ class SizeGrip extends JPanel {
* Ensures that the cursor for this component is appropriate for the
* orientation.
*
* @param ltr
* Whether the current component orientation is LTR.
* @param ltr Whether the current component orientation is LTR.
*/
protected void possiblyFixCursor(boolean ltr) {
int cursor = Cursor.NE_RESIZE_CURSOR;
@@ -184,10 +181,10 @@ class SizeGrip extends JPanel {
* appropriately.
*/
/*
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using
* the locations relative to the corner component because the latter proved
* buggy - stretch the window too wide and some kind of arithmetic error
* started happening somewhere - our window would grow way too large.
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just using the
* locations relative to the corner component because the latter proved buggy -
* stretch the window too wide and some kind of arithmetic error started
* happening somewhere - our window would grow way too large.
*/
private class MouseHandler extends MouseInputAdapter {
@@ -10,8 +10,8 @@ package org.fife.ui.autocomplete;
import java.util.Comparator;
/**
* Compares two <code>Completion</code>s by their relevance before
* sorting them lexicographically.
* Compares two <code>Completion</code>s by their relevance before sorting them
* lexicographically.
*
* @author Robert Futrell
* @version 1.0
@@ -37,12 +37,12 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* }
* </pre>
*
* In the above example, the first <code>${i}</code> is a parameter for the
* user to type into; all the other <code>${i}</code> instances are
* automatically changed to what the user types in the first one. The parameter
* named <code>${cursor}</code> is the "ending position" of the template. It's
* where the caret moves after it cycles through all other parameters. If the
* user types into it, template mode terminates. If more than one
* In the above example, the first <code>${i}</code> is a parameter for the user
* to type into; all the other <code>${i}</code> instances are automatically
* changed to what the user types in the first one. The parameter named
* <code>${cursor}</code> is the "ending position" of the template. It's where
* the caret moves after it cycles through all other parameters. If the user
* types into it, template mode terminates. If more than one
* <code>${cursor}</code> parameter is specified, behavior is undefined.
* <p>
*
@@ -51,8 +51,8 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* fault-tolerant.
* <p>
*
* Leading whitespace is automatically added to lines if the template spans
* more than one line, and if used with a text component using a
* Leading whitespace is automatically added to lines if the template spans more
* than one line, and if used with a text component using a
* <code>PlainDocument</code>, tabs will be converted to spaces if requested.
*
* @author Robert Futrell
@@ -110,8 +110,8 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
}
/**
* Returns <code>null</code>; template completions insert all of their
* text via <code>getInsertionInfo()</code>.
* Returns <code>null</code>; template completions insert all of their text via
* <code>getInsertionInfo()</code>.
*
* @return <code>null</code> always.
*/
@@ -234,8 +234,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
/**
* Returns whether a parameter is already defined with a specific name.
*
* @param name
* The name.
* @param name The name.
* @return Whether a parameter is defined with that name.
*/
private boolean isParamDefined(String name) {
@@ -251,8 +250,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
/**
* Parses a template string into logical pieces used by this class.
*
* @param template
* The template to parse.
* @param template The template to parse.
*/
private void parse(String template) {
int offs = 0;
@@ -319,8 +317,7 @@ public class TemplateCompletion extends AbstractCompletion implements Parameteri
/**
* Sets the short description of this template completion.
*
* @param shortDesc
* The new short description.
* @param shortDesc The new short description.
* @see #getShortDescription()
*/
public void setShortDescription(String shortDesc) {
@@ -7,8 +7,8 @@
package org.fife.ui.autocomplete;
/**
* A piece of a <code>TemplateCompletion</code>. You add instances of this
* class to template completions to define them.
* A piece of a <code>TemplateCompletion</code>. You add instances of this class
* to template completions to define them.
*
* @author Robert Futrell
* @version 1.0
+17 -18
View File
@@ -22,16 +22,17 @@ import javax.swing.text.html.HTMLDocument;
* Static utility methods for homemade tool tips.
* <p>
*
* This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class
* of the same name, but isn't re-used to prevent a hard dependency on the
* RSTA library.
* This is blatantly ripped off from RSyntaxTextArea's "FocusableTips" class of
* the same name, but isn't re-used to prevent a hard dependency on the RSTA
* library.
*
* @author Robert Futrell
* @version 1.0
*/
class TipUtil {
private TipUtil() {}
private TipUtil() {
}
/**
* Returns the default background color to use for tool tip windows.
@@ -73,17 +74,16 @@ class TipUtil {
}
/**
* Returns whether a color is a Nimbus DerivedColor, which is troublesome
* in that it doesn't use its RGB values (uses HSB instead?) and so
* querying them is useless.
* Returns whether a color is a Nimbus DerivedColor, which is troublesome in
* that it doesn't use its RGB values (uses HSB instead?) and so querying them
* is useless.
*
* @param c
* The color to check.
* @param c The color to check.
* @return Whether it is a DerivedColor
*/
private static final boolean isDerivedColor(Color c) {
return c != null
&& (c.getClass().getName().endsWith(".DerivedColor") || c.getClass().getName().endsWith(".DerivedColor$UIResource"));
return c != null && (c.getClass().getName().endsWith(".DerivedColor")
|| c.getClass().getName().endsWith(".DerivedColor$UIResource"));
}
/**
@@ -96,12 +96,11 @@ class TipUtil {
}
/**
* Tweaks a <code>JEditorPane</code> so it can be used to render the
* content in a focusable pseudo-tool tip. It is assumed that the editor
* pane is using an <code>HTMLDocument</code>.
* Tweaks a <code>JEditorPane</code> so it can be used to render the content in
* a focusable pseudo-tool tip. It is assumed that the editor pane is using an
* <code>HTMLDocument</code>.
*
* @param textArea
* The editor pane to tweak.
* @param textArea The editor pane to tweak.
*/
public static void tweakTipEditorPane(JEditorPane textArea) {
// Jump through a few hoops to get things looking nice in Nimbus
@@ -134,8 +133,8 @@ class TipUtil {
font = new Font("SansSerif", Font.PLAIN, 12);
}
HTMLDocument doc = (HTMLDocument) textArea.getDocument();
doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt" + "; color: "
+ Util.getHexString(fg) + "; }");
doc.getStyleSheet().addRule("body { font-family: " + font.getFamily() + "; font-size: " + font.getSize() + "pt"
+ "; color: " + Util.getHexString(fg) + "; }");
// Always add link foreground rule. Unfortunately these CSS rules
// stack each time the LaF is changed (how can we overwrite them
// without clearing out the important "standard" ones?).
+47 -55
View File
@@ -30,29 +30,29 @@ import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
public class Util {
/**
* If a system property is defined with this name and set, ignoring case,
* to <code>true</code>, this library will not attempt to use Substance
* renderers. Otherwise, if a Substance Look and Feel is installed, we
* will attempt to use Substance cell renderers in all of our dropdowns.
* If a system property is defined with this name and set, ignoring case, to
* <code>true</code>, this library will not attempt to use Substance renderers.
* Otherwise, if a Substance Look and Feel is installed, we will attempt to use
* Substance cell renderers in all of our dropdowns.
* <p>
*
* Note that we do not have a build dependency on Substance, so all access
* to Substance stuff is done via reflection. We will fall back onto
* default renderers if something goes horribly wrong.
* Note that we do not have a build dependency on Substance, so all access to
* Substance stuff is done via reflection. We will fall back onto default
* renderers if something goes horribly wrong.
*/
public static final String PROPERTY_DONT_USE_SUBSTANCE_RENDERERS = "org.fife.ui.autocomplete.DontUseSubstanceRenderers";
/**
* If this system property is <code>true</code>, then even the "main" two
* auto-complete windows will allow window decorations via
* {@link PopupWindowDecorator}. If this property is undefined or
* <code>false</code>, they won't honor such decorations. This is due to
* certain performance issues with translucent windows (used for drop
* shadows), even as of Java 7u2.
* <code>false</code>, they won't honor such decorations. This is due to certain
* performance issues with translucent windows (used for drop shadows), even as
* of Java 7u2.
*/
public static final String PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS = "org.fife.ui.autocomplete.allowDecoratedAutoCompleteWindows";
/**
* Used for the color of hyperlinks when a LookAndFeel uses light text
* against a dark background.
* Used for the color of hyperlinks when a LookAndFeel uses light text against a
* dark background.
*/
private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);
private static final Pattern TAG_PATTERN = Pattern.compile("<[^>]*>");
@@ -64,11 +64,10 @@ public class Util {
/**
* Attempts to open a web browser to the specified URI.
*
* @param uri
* The URI to open. If this is <code>null</code>, nothing
* happens and this method returns <code>false</code>.
* @return Whether the operation was successful. This will be
* <code>false</code> on JRE's older than 1.6.
* @param uri The URI to open. If this is <code>null</code>, nothing happens and
* this method returns <code>false</code>.
* @return Whether the operation was successful. This will be <code>false</code>
* on JRE's older than 1.6.
*/
public static boolean browse(URI uri) {
boolean success = false;
@@ -91,8 +90,8 @@ public class Util {
/**
* Returns the singleton <code>java.awt.Desktop</code> instance, or
* <code>null</code> if it is unsupported on this platform (or the JRE
* is older than 1.6).
* <code>null</code> if it is unsupported on this platform (or the JRE is older
* than 1.6).
*
* @return The desktop, as an {@link Object}.
*/
@@ -121,10 +120,9 @@ public class Util {
/**
* Returns a hex string for the specified color, suitable for HTML.
*
* @param c
* The color.
* @return The string representation, in the form "<code>#rrggbb</code>",
* or <code>null</code> if <code>c</code> is <code>null</code>.
* @param c The color.
* @return The string representation, in the form "<code>#rrggbb</code>", or
* <code>null</code> if <code>c</code> is <code>null</code>.
*/
public static String getHexString(Color c) {
if (c == null) {
@@ -152,10 +150,10 @@ public class Util {
}
/**
* Returns the color to use for hyperlink-style components. This method
* will return <code>Color.blue</code> unless it appears that the current
* LookAndFeel uses light text on a dark background, in which case a
* brighter alternative is returned.
* Returns the color to use for hyperlink-style components. This method will
* return <code>Color.blue</code> unless it appears that the current LookAndFeel
* uses light text on a dark background, in which case a brighter alternative is
* returned.
*
* @return The color to use for hyperlinks.
*/
@@ -170,14 +168,12 @@ public class Util {
}
/**
* Returns the screen coordinates for the monitor that contains the
* specified point. This is useful for setups with multiple monitors,
* to ensure that popup windows are positioned properly.
* Returns the screen coordinates for the monitor that contains the specified
* point. This is useful for setups with multiple monitors, to ensure that popup
* windows are positioned properly.
*
* @param x
* The x-coordinate, in screen coordinates.
* @param y
* The y-coordinate, in screen coordinates.
* @param x The x-coordinate, in screen coordinates.
* @param y The y-coordinate, in screen coordinates.
* @return The bounds of the monitor that contains the specified point.
*/
public static Rectangle getScreenBoundsForPoint(int x, int y) {
@@ -195,10 +191,10 @@ public class Util {
}
/**
* Give apps a chance to decorate us with drop shadows, etc. Since very
* scrolly things such as lists (of e.g. completions) are *very* slow when
* in per-pixel translucent windows, even as of Java 7u2, we force the user
* to specify an extra option for the two "main" auto-complete windows.
* Give apps a chance to decorate us with drop shadows, etc. Since very scrolly
* things such as lists (of e.g. completions) are *very* slow when in per-pixel
* translucent windows, even as of Java 7u2, we force the user to specify an
* extra option for the two "main" auto-complete windows.
*
* @return Whether to allow decorating the main auto-complete windows.
* @see #PROPERTY_ALLOW_DECORATED_AUTOCOMPLETE_WINDOWS
@@ -212,10 +208,10 @@ public class Util {
}
/**
* Returns whether we should attempt to use Substance cell renderers and
* styles for things such as completion choices, if a Substance Look and
* Feel is installed. If this is <code>false</code>, we'll use our
* standard rendering for completions, even when Substance is being used.
* Returns whether we should attempt to use Substance cell renderers and styles
* for things such as completion choices, if a Substance Look and Feel is
* installed. If this is <code>false</code>, we'll use our standard rendering
* for completions, even when Substance is being used.
*
* @return Whether to use Substance renderers if Substance is installed.
*/
@@ -224,12 +220,11 @@ public class Util {
}
/**
* Returns whether the specified color is "light" to use as a foreground.
* Colors that return <code>true</code> indicate that the current Look and
* Feel probably uses light text colors on a dark background.
* Returns whether the specified color is "light" to use as a foreground. Colors
* that return <code>true</code> indicate that the current Look and Feel
* probably uses light text colors on a dark background.
*
* @param fg
* The foreground color.
* @param fg The foreground color.
* @return Whether it is a "light" foreground color.
*/
public static final boolean isLightForeground(Color fg) {
@@ -237,13 +232,11 @@ public class Util {
}
/**
* Returns whether a string starts with a specified prefix, ignoring case.
* This method does not support characters outside of the BMP.
* Returns whether a string starts with a specified prefix, ignoring case. This
* method does not support characters outside of the BMP.
*
* @param str
* The string to check. This cannot be {@code null}.
* @param prefix
* The prefix to check for. This cannot be {@code null}.
* @param str The string to check. This cannot be {@code null}.
* @param prefix The prefix to check for. This cannot be {@code null}.
* @return Whether {@code str} starts with {@code prefix}, ignoring case.
*/
public static boolean startsWithIgnoreCase(String str, String prefix) {
@@ -258,8 +251,7 @@ public class Util {
* Strips any HTML from a string. The string must start with
* "<code>&lt;html&gt;</code>" for markup tags to be stripped.
*
* @param text
* The string.
* @param text The string.
* @return The string, with any HTML stripped.
*/
public static String stripHtml(String text) {
@@ -13,10 +13,10 @@ import javax.swing.text.JTextComponent;
* <p>
*
* This completion type uses its <tt>shortDescription</tt> property as part of
* its summary returned by {@link #getSummary()}; for this reason, it may be
* a little longer (even much longer), if desired, than what is recommended
* for <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used
* in {@link #toString()} for <tt>ListCellRenderers</tt>).
* its summary returned by {@link #getSummary()}; for this reason, it may be a
* little longer (even much longer), if desired, than what is recommended for
* <tt>BasicCompletion</tt>s (where the <tt>shortDescription</tt> is used in
* {@link #toString()} for <tt>ListCellRenderers</tt>).
*
* @author Robert Futrell
* @version 1.0
@@ -35,12 +35,9 @@ public class VariableCompletion extends BasicCompletion {
/**
* Constructor.
*
* @param provider
* The parent provider.
* @param name
* The name of this variable.
* @param type
* The type of this variable (e.g. "<code>int</code>",
* @param provider The parent provider.
* @param name The name of this variable.
* @param type The type of this variable (e.g. "<code>int</code>",
* "<code>String</code>", etc.).
*/
public VariableCompletion(CompletionProvider provider, String name, String type) {
@@ -95,21 +92,17 @@ public class VariableCompletion extends BasicCompletion {
}
/**
* Returns the tool tip text to display for mouse hovers over this
* completion.
* Returns the tool tip text to display for mouse hovers over this completion.
* <p>
*
* Note that for this functionality to be enabled, a
* <tt>JTextComponent</tt> must be registered with the
* <tt>ToolTipManager</tt>, and the text component must know to search
* for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this
* can be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that
* calls into
* Note that for this functionality to be enabled, a <tt>JTextComponent</tt>
* must be registered with the <tt>ToolTipManager</tt>, and the text component
* must know to search for this value. In the case of an
* <a href="http://fifesoft.com/rsyntaxtextarea">RSyntaxTextArea</a>, this can
* be done with a <tt>org.fife.ui.rtextarea.ToolTipSupplier</tt> that calls into
* {@link CompletionProvider#getCompletionsAt(JTextComponent, java.awt.Point)}.
*
* @return The tool tip text for this completion, or <code>null</code> if
* none.
* @return The tool tip text for this completion, or <code>null</code> if none.
*/
@Override
public String getToolTipText() {
@@ -126,11 +119,10 @@ public class VariableCompletion extends BasicCompletion {
}
/**
* Adds some HTML describing where this variable is defined, if this
* information is known.
* Adds some HTML describing where this variable is defined, if this information
* is known.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
*/
protected void possiblyAddDefinedIn(StringBuilder sb) {
if (definedIn != null) {
@@ -140,11 +132,9 @@ public class VariableCompletion extends BasicCompletion {
}
/**
* Adds the description text as HTML to a buffer, if a description is
* defined.
* Adds the description text as HTML to a buffer, if a description is defined.
*
* @param sb
* The buffer to append to.
* @param sb The buffer to append to.
* @return Whether there was a description to add.
*/
protected boolean possiblyAddDescription(StringBuilder sb) {
@@ -160,8 +150,7 @@ public class VariableCompletion extends BasicCompletion {
/**
* Sets where this variable is defined.
*
* @param definedIn
* Where this variable is defined.
* @param definedIn Where this variable is defined.
* @see #getDefinedIn()
*/
public void setDefinedIn(String definedIn) {
@@ -39,9 +39,9 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
/**
* Creates and returns the action to use when the user inserts a newline.
* The default implementation intelligently closes multi-line comments.
* Subclasses can override.
* Creates and returns the action to use when the user inserts a newline. The
* default implementation intelligently closes multi-line comments. Subclasses
* can override.
*
* @return The action.
* @see #getInsertBreakAction()
@@ -51,8 +51,8 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
/**
* Returns <code>true</code> always as C-style languages use curly braces
* to denote code blocks.
* Returns <code>true</code> always as C-style languages use curly braces to
* denote code blocks.
*
* @return <code>true</code> always.
*/
@@ -62,9 +62,8 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
/**
* Returns an action to handle "insert break" key presses (i.e. Enter).
* An action is returned that handles newlines differently in multi-line
* comments.
* Returns an action to handle "insert break" key presses (i.e. Enter). An
* action is returned that handles newlines differently in multi-line comments.
*
* @return The action.
*/
@@ -94,17 +93,15 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
/**
* Returns whether a given token is an internal token type that represents
* an MLC or documentation comment continuing on to the next line. This is
* done by languages such as JavaScript that are a little more verbose
* than necessary so that their code can be copy-and-pasted into other
* <code>TokenMaker</code>s that use them as nested languages (such as
* HTML, JSP, etc.).
* Returns whether a given token is an internal token type that represents an
* MLC or documentation comment continuing on to the next line. This is done by
* languages such as JavaScript that are a little more verbose than necessary so
* that their code can be copy-and-pasted into other <code>TokenMaker</code>s
* that use them as nested languages (such as HTML, JSP, etc.).
*
* @param t
* The token to check. This cannot be <code>null</code>.
* @return Whether the token is an internal token representing the end of
* a line for an MLC/doc comment continuing on to the next line.
* @param t The token to check. This cannot be <code>null</code>.
* @return Whether the token is an internal token representing the end of a line
* for an MLC/doc comment continuing on to the next line.
*/
private boolean isInternalEolTokenForMLCs(Token t) {
int type = t.getType();
@@ -116,8 +113,8 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
/**
* Action that knows how to special-case inserting a newline in a
* multi-line comment for languages like C and Java.
* Action that knows how to special-case inserting a newline in a multi-line
* comment for languages like C and Java.
*/
protected class CStyleInsertBreakAction extends RSyntaxTextAreaEditorKit.InsertBreakAction {
@@ -143,11 +140,10 @@ public abstract class AbstractJFlexCTokenMaker extends AbstractJFlexTokenMaker {
}
/**
* Returns whether the MLC token containing <code>offs</code> appears
* to have a "nested" comment (i.e., contains "<code>/*</code>"
* somewhere inside of it). This implies that it is likely a "new" MLC
* and needs to be closed. While not foolproof, this is usually good
* enough of a sign.
* Returns whether the MLC token containing <code>offs</code> appears to have a
* "nested" comment (i.e., contains "<code>/*</code>" somewhere inside of it).
* This implies that it is likely a "new" MLC and needs to be closed. While not
* foolproof, this is usually good enough of a sign.
*
* @param textArea
* @param line
@@ -11,8 +11,8 @@ import javax.swing.text.Segment;
/**
* Base class for JFlex-generated token makers. This class attempts to factor
* out all common code from these classes. Many methods <em>almost</em> could
* be factored out into this class, but cannot because they reference JFlex
* out all common code from these classes. Many methods <em>almost</em> could be
* factored out into this class, but cannot because they reference JFlex
* variables that we cannot access from this class.
*
* @author Robert Futrell
@@ -28,18 +28,15 @@ public abstract class AbstractJFlexTokenMaker extends TokenMakerBase {
/**
* Declared here so we can define overloads that refer to this method.
*
* @param newState
* The new JFlex state to enter.
* @param newState The new JFlex state to enter.
*/
public abstract void yybegin(int newState);
/**
* Starts a new JFlex state and changes the current language index.
*
* @param state
* The new JFlex state to enter.
* @param languageIndex
* The new language index.
* @param state The new JFlex state to enter.
* @param languageIndex The new language index.
*/
protected void yybegin(int state, int languageIndex) {
yybegin(state);
@@ -8,9 +8,9 @@ package org.fife.ui.rsyntaxtextarea;
/**
* An abstract implementation of the
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should
* be overridden for every language for which you want to provide
* syntax highlighting.
* {@link org.fife.ui.rsyntaxtextarea.TokenMaker} interface. It should be
* overridden for every language for which you want to provide syntax
* highlighting.
* <p>
*
* @see Token
@@ -21,10 +21,9 @@ package org.fife.ui.rsyntaxtextarea;
public abstract class AbstractTokenMaker extends TokenMakerBase {
/**
* Hash table of words to highlight and what token type they are.
* The keys are the words to highlight, and their values are the
* token types, for example, <code>Token.RESERVED_WORD</code> or
* <code>Token.FUNCTION</code>.
* Hash table of words to highlight and what token type they are. The keys are
* the words to highlight, and their values are the token types, for example,
* <code>Token.RESERVED_WORD</code> or <code>Token.FUNCTION</code>.
*/
protected TokenMap wordsToHighlight;
@@ -38,15 +37,15 @@ public abstract class AbstractTokenMaker extends TokenMakerBase {
/**
* Returns the words to highlight for this programming language.
*
* @return A <code>TokenMap</code> containing the words to highlight for
* this programming language.
* @return A <code>TokenMap</code> containing the words to highlight for this
* programming language.
*/
public abstract TokenMap getWordsToHighlight();
/**
* Removes the token last added from the linked list of tokens. The
* programmer should never have to call this directly; it can be called
* by subclasses of <code>TokenMaker</code> if necessary.
* Removes the token last added from the linked list of tokens. The programmer
* should never have to call this directly; it can be called by subclasses of
* <code>TokenMaker</code> if necessary.
*/
public void removeLastToken() {
if (previousToken == null) {
@@ -20,10 +20,9 @@ import java.util.Set;
public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/**
* A mapping from keys to the names of {@link TokenMaker} implementation
* class names. When {@link #getTokenMaker(String)} is called with a key
* defined in this map, a <code>TokenMaker</code> of the corresponding type
* is returned.
* A mapping from keys to the names of {@link TokenMaker} implementation class
* names. When {@link #getTokenMaker(String)} is called with a key defined in
* this map, a <code>TokenMaker</code> of the corresponding type is returned.
*/
private Map<String, Object> tokenMakerMap;
@@ -38,10 +37,9 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/**
* Returns a {@link TokenMaker} for the specified key.
*
* @param key
* The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code>
* if none matches the specified key.
* @param key The key.
* @return The corresponding <code>TokenMaker</code>, or <code>null</code> if
* none matches the specified key.
*/
@Override
protected TokenMaker getTokenMakerImpl(String key) {
@@ -60,8 +58,8 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
/**
* Populates the mapping from keys to instances of
* <code>TokenMakerCreator</code>s. Subclasses should override this method
* and call one of the <code>putMapping</code> overloads to register
* <code>TokenMakerCreator</code>s. Subclasses should override this method and
* call one of the <code>putMapping</code> overloads to register
* {@link TokenMaker}s for syntax constants.
*
* @see #putMapping(String, String)
@@ -78,13 +76,11 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
* Adds a mapping from a key to a <code>TokenMaker</code> implementation class
* name.
*
* @param key
* The key.
* @param className
* The <code>TokenMaker</code> class name.
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @see #putMapping(String, String, ClassLoader)
*/
public void putMapping(String key, String className) {
@@ -92,15 +88,12 @@ public abstract class AbstractTokenMakerFactory extends TokenMakerFactory {
}
/**
* Adds a mapping from a key to a <code>TokenMaker</code> implementation
* class name.
* Adds a mapping from a key to a <code>TokenMaker</code> implementation class
* name.
*
* @param key
* The key.
* @param className
* The <code>TokenMaker</code> class name.
* @param cl
* The class loader to use when loading the class.
* @param key The key.
* @param className The <code>TokenMaker</code> class name.
* @param cl The class loader to use when loading the class.
* @see #putMapping(String, String)
*/
public void putMapping(String key, String className, ClassLoader cl) {
@@ -24,14 +24,11 @@ public class ActiveLineRangeEvent extends EventObject {
/**
* Constructor.
*
* @param source
* The text area.
* @param min
* The first line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param max
* The last line in the active line range, or
* <code>-1</code> if the line range is being cleared.
* @param source The text area.
* @param min The first line in the active line range, or <code>-1</code> if
* the line range is being cleared.
* @param max The last line in the active line range, or <code>-1</code> if
* the line range is being cleared.
*/
public ActiveLineRangeEvent(RSyntaxTextArea source, int min, int max) {
super(source);
@@ -42,8 +39,7 @@ public class ActiveLineRangeEvent extends EventObject {
/**
* Returns the last line in the active line range.
*
* @return The last line, or <code>-1</code> if the range is being
* cleared.
* @return The last line, or <code>-1</code> if the range is being cleared.
* @see #getMin()
*/
public int getMax() {
@@ -53,8 +49,7 @@ public class ActiveLineRangeEvent extends EventObject {
/**
* Returns the first line in the active line range.
*
* @return The first line, or <code>-1</code> if the range is being
* cleared.
* @return The first line, or <code>-1</code> if the range is being cleared.
* @see #getMax()
*/
public int getMin() {
@@ -13,15 +13,15 @@ import java.util.EventListener;
* Listens for "active line range" events from an <code>RSyntaxTextArea</code>.
* If a text area contains some semantic knowledge of the programming language
* being edited, it may broadcast {@link ActiveLineRangeEvent}s whenever the
* caret moves into a new "block" of code. Listeners can listen for these
* events and respond accordingly.
* caret moves into a new "block" of code. Listeners can listen for these events
* and respond accordingly.
* <p>
*
* See the <code>RSTALanguageSupport</code> project at
* <a href="http://fifesoft.com">http://fifesoft.com</a> for some
* <code>LanguageSupport</code> implementations that may broadcast these
* events. Note that if an RSTA/LanguageSupport does not support broadcasting
* these events, the listener will simply never receive any notifications.
* <code>LanguageSupport</code> implementations that may broadcast these events.
* Note that if an RSTA/LanguageSupport does not support broadcasting these
* events, the listener will simply never receive any notifications.
*
* @author Robert Futrell
* @version 1.0
@@ -31,11 +31,9 @@ public interface ActiveLineRangeListener extends EventListener {
/**
* Called whenever the "active line range" changes.
*
* @param e
* Information about the line range change. If there is no longer
* an "active line range," the "minimum" and "maximum" line
* values
* should both be <code>-1</code>.
* @param e Information about the line range change. If there is no longer an
* "active line range," the "minimum" and "maximum" line values should
* both be <code>-1</code>.
*/
void activeLineRangeChanged(ActiveLineRangeEvent e);
}
@@ -13,8 +13,8 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* The default implementation of {@link OccurrenceMarker}. It goes through
* the document and marks all instances of the specified token.
* The default implementation of {@link OccurrenceMarker}. It goes through the
* document and marks all instances of the specified token.
*
* @author Robert Futrell
* @version 1.0
@@ -66,16 +66,13 @@ class DefaultOccurrenceMarker implements OccurrenceMarker {
* Highlights all instances of tokens identical to <code>t</code> in the
* specified document.
*
* @param doc
* The document.
* @param t
* The document whose relevant occurrences should be marked.
* @param h
* The highlighter to add the highlights to.
* @param p
* The painter for the highlights.
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
*/
public static final void markOccurrencesOfToken(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p) {
public static final void markOccurrencesOfToken(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h,
SmartHighlightPainter p) {
char[] lexeme = t.getLexeme().toCharArray();
int type = t.getType();
int lineCount = doc.getDefaultRootElement().getElementCount();
@@ -10,18 +10,18 @@ import javax.swing.text.Segment;
/**
* This class generates tokens for a {@link TokenMaker}. This class is here
* because it reuses tokens when they aren't needed anymore to prevent
* This class doesn't actually create new tokens every time
* <code>createToken</code> is called. Instead, it internally keeps a stack of
* available already-created tokens. When more tokens are needed to properly
* display a line, more tokens are added to the available stack. This saves
* from needless repetitive memory allocation. However, it makes it IMPERATIVE
* that users call <code>resetTokenList</code> when creating a new token list so
* that the token maker can keep an accurate list of available tokens.
* because it reuses tokens when they aren't needed anymore to prevent This
* class doesn't actually create new tokens every time <code>createToken</code>
* is called. Instead, it internally keeps a stack of available already-created
* tokens. When more tokens are needed to properly display a line, more tokens
* are added to the available stack. This saves from needless repetitive memory
* allocation. However, it makes it IMPERATIVE that users call
* <code>resetTokenList</code> when creating a new token list so that the token
* maker can keep an accurate list of available tokens.
* <p>
*
* NOTE: This class should only be used by {@link TokenMaker}; nobody else
* needs it!
* NOTE: This class should only be used by {@link TokenMaker}; nobody else needs
* it!
*
* @author Robert Futrell
* @version 0.1
@@ -45,11 +45,8 @@ class DefaultTokenFactory implements TokenFactory {
/**
* Constructor.
*
* @param size
* The initial number of tokens in this factory.
* @param increment
* How many tokens to increment by when the stack gets
* empty.
* @param size The initial number of tokens in this factory.
* @param increment How many tokens to increment by when the stack gets empty.
*/
DefaultTokenFactory(int size, int increment) {
this.size = size;
@@ -63,8 +60,8 @@ class DefaultTokenFactory implements TokenFactory {
}
/**
* Adds tokens to the internal token list. This is called whenever a
* request is made and no more tokens are available.
* Adds tokens to the internal token list. This is called whenever a request is
* made and no more tokens are available.
*/
private void augmentTokenList() {
TokenImpl[] temp = new TokenImpl[size + increment];
@@ -83,12 +80,8 @@ class DefaultTokenFactory implements TokenFactory {
@Override
public TokenImpl createToken() {
TokenImpl token = tokenList[currentFreeToken];
token.text = null;
token.setType(Token.NULL);
token.setOffset(-1);
token.setNextToken(null);
currentFreeToken++;
if (currentFreeToken == size) {
token.reset();
if (++currentFreeToken == size) {
augmentTokenList();
}
return token;
@@ -98,7 +91,8 @@ class DefaultTokenFactory implements TokenFactory {
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final Segment line, final int beg, final int end, final int startOffset, final int type) {
public TokenImpl createToken(final Segment line, final int beg, final int end, final int startOffset,
final int type) {
return createToken(line.array, beg, end, startOffset, type);
}
@@ -106,7 +100,10 @@ class DefaultTokenFactory implements TokenFactory {
* {@inheritDoc}
*/
@Override
public TokenImpl createToken(final char[] line, final int beg, final int end, final int startOffset, final int type) {
public TokenImpl createToken(final char[] line, final int beg, final int end, final int startOffset,
final int type) {
// if (tokenList.length - 1 > currentFreeToken)
// return null;
TokenImpl token = tokenList[currentFreeToken];
token.set(line, beg, end, startOffset, type);
currentFreeToken++;
@@ -117,9 +114,9 @@ class DefaultTokenFactory implements TokenFactory {
}
/**
* Resets the state of this token maker. This method should be called
* by the <code>TokenMaker</code> every time a token list is generated for
* a new line so the tokens can be reused.
* Resets the state of this token maker. This method should be called by the
* <code>TokenMaker</code> every time a token list is generated for a new line
* so the tokens can be reused.
*/
@Override
public void resetAllTokens() {
@@ -26,9 +26,9 @@ class DefaultTokenPainter implements TokenPainter {
*/
private Rectangle2D.Float bgRect;
/**
* Micro-optimization; buffer used to compute tab width. If the width is
* correct it's not re-allocated, to prevent lots of very small garbage.
* Only used when painting tab lines.
* Micro-optimization; buffer used to compute tab width. If the width is correct
* it's not re-allocated, to prevent lots of very small garbage. Only used when
* painting tab lines.
*/
private static char[] tabBuf;
@@ -48,7 +48,8 @@ class DefaultTokenPainter implements TokenPainter {
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart) {
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart) {
return paintImpl(token, g, x, y, host, e, clipStart, false, false);
}
@@ -56,33 +57,26 @@ class DefaultTokenPainter implements TokenPainter {
* {@inheritDoc}
*/
@Override
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart, boolean paintBG) {
public float paint(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart, boolean paintBG) {
return paintImpl(token, g, x, y, host, e, clipStart, !paintBG, false);
}
/**
* Paints the background of a token.
*
* @param x
* The x-coordinate of the token.
* @param y
* The y-coordinate of the token.
* @param width
* The width of the token (actually, the width of the part of
* @param x The x-coordinate of the token.
* @param y The y-coordinate of the token.
* @param width The width of the token (actually, the width of the part of
* the token to paint).
* @param height
* The height of the token.
* @param g
* The graphics context with which to paint.
* @param fontAscent
* The ascent of the token's font.
* @param host
* The text area.
* @param color
* The color with which to paint.
* @param height The height of the token.
* @param g The graphics context with which to paint.
* @param fontAscent The ascent of the token's font.
* @param host The text area.
* @param color The color with which to paint.
*/
protected void paintBackground(float x, float y, float width, float height, Graphics2D g, int fontAscent, RSyntaxTextArea host,
Color color) {
protected void paintBackground(float x, float y, float width, float height, Graphics2D g, int fontAscent,
RSyntaxTextArea host, Color color) {
g.setColor(color);
bgRect.setRect(x, y - fontAscent, width, height);
// g.fill(bgRect);
@@ -92,8 +86,8 @@ class DefaultTokenPainter implements TokenPainter {
/**
* Does the dirty-work of actually painting the token.
*/
protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean selected, boolean useSTC) {
protected float paintImpl(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart, boolean selected, boolean useSTC) {
int origX = (int) x;
int textOffs = token.getTextOffset();
char[] text = token.getTextArray();
@@ -157,7 +151,8 @@ class DefaultTokenPainter implements TokenPainter {
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, boolean useSTC) {
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
boolean useSTC) {
return paintSelected(token, g, x, y, host, e, 0, useSTC);
}
@@ -165,35 +160,29 @@ class DefaultTokenPainter implements TokenPainter {
* {@inheritDoc}
*/
@Override
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e, float clipStart,
boolean useSTC) {
public float paintSelected(Token token, Graphics2D g, float x, float y, RSyntaxTextArea host, TabExpander e,
float clipStart, boolean useSTC) {
return paintImpl(token, g, x, y, host, e, clipStart, true, useSTC);
}
/**
* Paints dotted "tab" lines; that is, lines that show where your caret
* would go to on the line if you hit "tab". This visual effect is usually
* done in the leading whitespace token(s) of lines.
* Paints dotted "tab" lines; that is, lines that show where your caret would go
* to on the line if you hit "tab". This visual effect is usually done in the
* leading whitespace token(s) of lines.
*
* @param token
* The token to render.
* @param x
* The starting x-offset of this token. It is assumed that this
* is the left margin of the text area (may be non-zero due to
* @param token The token to render.
* @param x The starting x-offset of this token. It is assumed that this is
* the left margin of the text area (may be non-zero due to
* insets), since tab lines are only painted for leading
* whitespace.
* @param y
* The baseline where this token was painted.
* @param endX
* The ending x-offset of this token.
* @param g
* The graphics context.
* @param e
* Used to expand tabs.
* @param host
* The text area.
* @param y The baseline where this token was painted.
* @param endX The ending x-offset of this token.
* @param g The graphics context.
* @param e Used to expand tabs.
* @param host The text area.
*/
protected void paintTabLines(Token token, int x, int y, int endX, Graphics2D g, TabExpander e, RSyntaxTextArea host) {
protected void paintTabLines(Token token, int x, int y, int endX, Graphics2D g, TabExpander e,
RSyntaxTextArea host) {
// We allow tab lines to be painted in more than just Token.WHITESPACE,
// i.e. for MLC's and Token.IDENTIFIERS (for TokenMakers that return
// whitespace as identifiers for performance). But we only paint tab
@@ -20,13 +20,11 @@ public class DocumentRange implements Comparable<DocumentRange> {
/**
* Constructor.
*
* @param startOffs
* The starting offset in the document, inclusive.
* @param endOffs
* The ending offset in the document, exclusive.
* @throws IllegalArgumentException
* If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument is less than zero.
* @param startOffs The starting offset in the document, inclusive.
* @param endOffs The ending offset in the document, exclusive.
* @throws IllegalArgumentException If <code>endOffs</code> is less than
* <code>startOffs</code>, or either argument
* is less than zero.
*/
public DocumentRange(int startOffs, int endOffs) {
set(startOffs, endOffs);
@@ -35,8 +33,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
/**
* Compares this document range to another.
*
* @param other
* Another document range.
* @param other Another document range.
* @return How the two should be sorted relative to each other.
*/
@Override
@@ -54,10 +51,9 @@ public class DocumentRange implements Comparable<DocumentRange> {
/**
* Returns whether this document range is equal to another one.
*
* @param other
* Another object, presumably a document range.
* @return Whether <code>other</code> is also a document range, and equal
* to this one.
* @param other Another object, presumably a document range.
* @return Whether <code>other</code> is also a document range, and equal to
* this one.
*/
@Override
public boolean equals(Object other) {
@@ -102,10 +98,9 @@ public class DocumentRange implements Comparable<DocumentRange> {
}
/**
* Returns whether this document range has zero length. This can happen,
* for example, with regex searches of forms like
* <code>"foo|"</code>, where the right-hand sub-expression matches empty
* strings.
* Returns whether this document range has zero length. This can happen, for
* example, with regex searches of forms like <code>"foo|"</code>, where the
* right-hand sub-expression matches empty strings.
*
* @return Whether this document range has zero length.
*/
@@ -116,13 +111,11 @@ public class DocumentRange implements Comparable<DocumentRange> {
/**
* Sets the document range.
*
* @param start
* The new start value, inclusive.
* @param end
* The new end value, exclusive.
* @throws IllegalArgumentException
* If <code>end</code> is less than
* <code>start</code>, or either argument is less than zero.
* @param start The new start value, inclusive.
* @param end The new end value, exclusive.
* @throws IllegalArgumentException If <code>end</code> is less than
* <code>start</code>, or either argument is
* less than zero.
*/
public void set(int start, int end) {
if (start < 0 || end < 0) {
@@ -148,8 +141,7 @@ public class DocumentRange implements Comparable<DocumentRange> {
/**
* Translates this document range by a given amount.
*
* @param amount
* The amount to translate this range by.
* @param amount The amount to translate this range by.
* @return This (modified) range.
*/
public DocumentRange translate(int amount) {
+49 -66
View File
@@ -39,16 +39,15 @@ import org.fife.ui.rsyntaxtextarea.parser.TaskTagParser.TaskNotice;
import org.fife.ui.rtextarea.RTextArea;
/**
* A component to sit alongside an {@link RSyntaxTextArea} that displays
* colored markers for locations of interest (parser errors, marked
* occurrences, etc.).
* A component to sit alongside an {@link RSyntaxTextArea} that displays colored
* markers for locations of interest (parser errors, marked occurrences, etc.).
* <p>
*
* <code>ErrorStrip</code>s display <code>ParserNotice</code>s from
* {@link Parser}s. Currently, the only way to get lines flagged in this
* component is to register a <code>Parser</code> on an RSyntaxTextArea and
* return <code>ParserNotice</code>s for each line to display an icon for.
* The severity of each notice must be at least the threshold set by
* return <code>ParserNotice</code>s for each line to display an icon for. The
* severity of each notice must be at least the threshold set by
* {@link #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)}
* to be displayed in this error strip. The default threshold is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
@@ -86,23 +85,23 @@ public class ErrorStrip extends JPanel {
*/
private transient Listener listener;
/**
* Whether "marked occurrences" in the text area should be shown in this
* error strip.
* Whether "marked occurrences" in the text area should be shown in this error
* strip.
*/
private boolean showMarkedOccurrences;
/**
* Whether markers for "mark all" highlights should be shown in this
* error strip.
* Whether markers for "mark all" highlights should be shown in this error
* strip.
*/
private boolean showMarkAll;
/**
* Mapping of colors to brighter colors. This is kept to prevent
* unnecessary creation of the same Colors over and over.
* Mapping of colors to brighter colors. This is kept to prevent unnecessary
* creation of the same Colors over and over.
*/
private Map<Color, Color> brighterColors;
/**
* Only notices of this severity (or worse) will be displayed in this
* error strip.
* Only notices of this severity (or worse) will be displayed in this error
* strip.
*/
private ParserNotice.Level levelThreshold;
/**
@@ -134,8 +133,7 @@ public class ErrorStrip extends JPanel {
/**
* Constructor.
*
* @param textArea
* The text area we are examining.
* @param textArea The text area we are examining.
*/
public ErrorStrip(RSyntaxTextArea textArea) {
this.textArea = textArea;
@@ -152,8 +150,8 @@ public class ErrorStrip extends JPanel {
}
/**
* Overridden so we only start listening for parser notices when this
* component (and presumably the text area) are visible.
* Overridden so we only start listening for parser notices when this component
* (and presumably the text area) are visible.
*/
@Override
public void addNotify() {
@@ -181,8 +179,7 @@ public class ErrorStrip extends JPanel {
/**
* Returns a "brighter" color.
*
* @param c
* The color.
* @param c The color.
* @return A brighter color.
*/
private Color getBrighterColor(Color c) {
@@ -232,9 +229,9 @@ public class ErrorStrip extends JPanel {
}
/**
* Returns the minimum severity a parser notice must be for it to be
* displayed in this error strip. This will be one of the constants
* defined in the <code>ParserNotice</code> class.
* Returns the minimum severity a parser notice must be for it to be displayed
* in this error strip. This will be one of the constants defined in the
* <code>ParserNotice</code> class.
*
* @return The minimum severity.
* @see #setLevelThreshold(org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level)
@@ -278,11 +275,10 @@ public class ErrorStrip extends JPanel {
}
/**
* Returns the y-offset in this component corresponding to a line in the
* text component.
* Returns the y-offset in this component corresponding to a line in the text
* component.
*
* @param line
* The line.
* @param line The line.
* @return The y-offset.
* @see #yToLine(int)
*/
@@ -295,8 +291,7 @@ public class ErrorStrip extends JPanel {
/**
* Overridden to (possibly) draw the caret's position.
*
* @param g
* The graphics context.
* @param g The graphics context.
*/
@Override
protected void paintComponent(Graphics g) {
@@ -310,8 +305,7 @@ public class ErrorStrip extends JPanel {
/**
* Returns a possibly brighter component for a color.
*
* @param i
* An RGB component for a color (0-255).
* @param i An RGB component for a color (0-255).
* @return A possibly brighter value for the component.
*/
private static int possiblyBrighter(int i) {
@@ -358,12 +352,9 @@ public class ErrorStrip extends JPanel {
/**
* Adds markers for a list of ranges in the document.
*
* @param ranges
* The list of ranges in the document.
* @param markerMap
* A mapping from line number to <code>Marker</code>.
* @param color
* The color to use for the markers.
* @param ranges The list of ranges in the document.
* @param markerMap A mapping from line number to <code>Marker</code>.
* @param color The color to use for the markers.
*/
private void addMarkersForRanges(List<DocumentRange> ranges, Map<Integer, Marker> markerMap, Color color) {
for (DocumentRange range : ranges) {
@@ -405,8 +396,7 @@ public class ErrorStrip extends JPanel {
/**
* Sets the color to use when painting the caret marker.
*
* @param color
* The new caret marker color.
* @param color The new caret marker color.
* @see #getCaretMarkerColor()
*/
public void setCaretMarkerColor(Color color) {
@@ -419,8 +409,7 @@ public class ErrorStrip extends JPanel {
/**
* Toggles whether the caret's current location should be drawn.
*
* @param follow
* Whether the caret's current location should be followed.
* @param follow Whether the caret's current location should be followed.
* @see #getFollowCaret()
*/
public void setFollowCaret(boolean follow) {
@@ -436,13 +425,12 @@ public class ErrorStrip extends JPanel {
}
/**
* Sets the minimum severity a parser notice must be for it to be displayed
* in this error strip. This should be one of the constants defined in
* the <code>ParserNotice</code> class. The default value is
* Sets the minimum severity a parser notice must be for it to be displayed in
* this error strip. This should be one of the constants defined in the
* <code>ParserNotice</code> class. The default value is
* {@link org.fife.ui.rsyntaxtextarea.parser.ParserNotice.Level#WARNING}.
*
* @param level
* The new severity threshold.
* @param level The new severity threshold.
* @see #getLevelThreshold()
* @see ParserNotice
*/
@@ -454,12 +442,11 @@ public class ErrorStrip extends JPanel {
}
/**
* Sets the provider of tool tips for markers in this error strip.
* Applications can use this method to control the content and format of
* the tool tip descriptions of line markers.
* Sets the provider of tool tips for markers in this error strip. Applications
* can use this method to control the content and format of the tool tip
* descriptions of line markers.
*
* @param provider
* The provider. If this is <code>null</code>, a default
* @param provider The provider. If this is <code>null</code>, a default
* implementation will be used.
*/
public void setMarkerToolTipProvider(ErrorStripMarkerToolTipProvider provider) {
@@ -469,8 +456,7 @@ public class ErrorStrip extends JPanel {
/**
* Sets whether "mark all" highlights are shown in this error strip.
*
* @param show
* Whether to show markers for "mark all" highlights.
* @param show Whether to show markers for "mark all" highlights.
* @see #getShowMarkAll()
*/
public void setShowMarkAll(boolean show) {
@@ -485,8 +471,7 @@ public class ErrorStrip extends JPanel {
/**
* Sets whether marked occurrences are shown in this error strip.
*
* @param show
* Whether to show marked occurrences.
* @param show Whether to show marked occurrences.
* @see #getShowMarkedOccurrences()
*/
public void setShowMarkedOccurrences(boolean show) {
@@ -502,8 +487,7 @@ public class ErrorStrip extends JPanel {
* Returns the line in the text area corresponding to a y-offset in this
* component.
*
* @param y
* The y-offset.
* @param y The y-offset.
* @return The line.
* @see #lineToY(int)
*/
@@ -518,8 +502,8 @@ public class ErrorStrip extends JPanel {
}
/**
* The default implementation of the provider of tool tips for markers in
* an error strip.
* The default implementation of the provider of tool tips for markers in an
* error strip.
*
* @author predi
*/
@@ -547,21 +531,20 @@ public class ErrorStrip extends JPanel {
}
/**
* Returns tool tip text for the markers in an {@link ErrorStrip} that
* denote one or more parser notices.
* Returns tool tip text for the markers in an {@link ErrorStrip} that denote
* one or more parser notices.
*
* @author predi
*/
public interface ErrorStripMarkerToolTipProvider {
/**
* Returns the tool tip text for a marker in an <code>ErrorStrip</code>
* that denotes a given list of parser notices.
* Returns the tool tip text for a marker in an <code>ErrorStrip</code> that
* denotes a given list of parser notices.
*
* @param notices
* The list of parser notices.
* @return The tool tip text. This may be HTML. Returning
* <code>null</code> will result in no tool tip being displayed.
* @param notices The list of parser notices.
* @return The tool tip text. This may be HTML. Returning <code>null</code> will
* result in no tool tip being displayed.
*/
String getToolTipText(List<ParserNotice> notices);
}
@@ -29,8 +29,7 @@ class FileFileLocation extends FileLocation {
/**
* Constructor.
*
* @param file
* The local file.
* @param file The local file.
*/
FileFileLocation(File file) {
try {
@@ -96,8 +95,8 @@ class FileFileLocation extends FileLocation {
}
/**
* Since file locations of this type are guaranteed to be local, this
* method returns whether the file exists.
* Since file locations of this type are guaranteed to be local, this method
* returns whether the file exists.
*
* @return Whether this local file actually exists.
* @see #isLocal()
@@ -14,8 +14,8 @@ import java.net.MalformedURLException;
import java.net.URL;
/**
* Holds the location of a local or remote file. This provides a common way
* to read, write, and check properties of both local and remote files.
* Holds the location of a local or remote file. This provides a common way to
* read, write, and check properties of both local and remote files.
*
* @author Robert Futrell
* @version 1.0
@@ -25,12 +25,12 @@ public abstract class FileLocation {
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param fileFullPath
* The full path to a local file.
* @param fileFullPath The full path to a local file.
* @return The file's location.
*/
public static FileLocation create(String fileFullPath) {
if (fileFullPath.startsWith("http://") || fileFullPath.startsWith("https://") || fileFullPath.startsWith("ftp://")) {
if (fileFullPath.startsWith("http://") || fileFullPath.startsWith("https://")
|| fileFullPath.startsWith("ftp://")) {
try {
return new URLFileLocation(new URL(fileFullPath));
} catch (MalformedURLException mue) {
@@ -43,8 +43,7 @@ public abstract class FileLocation {
/**
* Creates a {@link FileLocation} instance for the specified local file.
*
* @param file
* A local file.
* @param file A local file.
* @return The file's location.
*/
public static FileLocation create(File file) {
@@ -54,8 +53,7 @@ public abstract class FileLocation {
/**
* Creates a {@link FileLocation} instance for the specified file.
*
* @param url
* The URL of a file.
* @param url The URL of a file.
* @return The file's location.
*/
public static FileLocation create(URL url) {
@@ -67,16 +65,16 @@ public abstract class FileLocation {
/**
* Returns the last time this file was modified, or
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be
* computed (such as for a remote file).
* {@link TextEditorPane#LAST_MODIFIED_UNKNOWN} if this value cannot be computed
* (such as for a remote file).
*
* @return The last time this file was modified.
*/
protected abstract long getActualLastModified();
/**
* Returns the full path to the file. This will be stripped of
* sensitive information such as passwords for remote files.
* Returns the full path to the file. This will be stripped of sensitive
* information such as passwords for remote files.
*
* @return The full path to the file.
* @see #getFileName()
@@ -95,8 +93,7 @@ public abstract class FileLocation {
* Opens an input stream for reading from this file.
*
* @return The input stream.
* @throws IOException
* If the file does not exist, or some other IO error
* @throws IOException If the file does not exist, or some other IO error
* occurs.
*/
protected abstract InputStream getInputStream() throws IOException;
@@ -105,8 +102,7 @@ public abstract class FileLocation {
* Opens an output stream for writing this file.
*
* @return An output stream.
* @throws IOException
* If an IO error occurs.
* @throws IOException If an IO error occurs.
*/
protected abstract OutputStream getOutputStream() throws IOException;
@@ -119,8 +115,7 @@ public abstract class FileLocation {
public abstract boolean isLocal();
/**
* Returns whether this file location is a local file that already
* exists.
* Returns whether this file location is a local file that already exists.
*
* @return Whether this file is local and actually exists.
* @see #isLocal()
@@ -32,8 +32,7 @@ public class FoldingAwareIconRowHeader extends IconRowHeader {
/**
* Constructor.
*
* @param textArea
* The parent text area.
* @param textArea The parent text area.
*/
public FoldingAwareIconRowHeader(RSyntaxTextArea textArea) {
super(textArea);
@@ -162,9 +161,9 @@ public class FoldingAwareIconRowHeader extends IconRowHeader {
}
/**
* Paints icons when line wrapping is enabled. Note that this does not
* override the parent class's implementation to avoid this version being
* called when line wrapping is disabled.
* Paints icons when line wrapping is enabled. Note that this does not override
* the parent class's implementation to avoid this version being called when
* line wrapping is disabled.
*/
private void paintComponentWrapped(Graphics g) {
// The variables we use are as follows:
@@ -20,8 +20,8 @@ import javax.swing.text.Element;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks occurrences of the current token for HTML. Tags that require a
* closing tag have their "opposite" tag closed.
* Marks occurrences of the current token for HTML. Tags that require a closing
* tag have their "opposite" tag closed.
*
* @author Robert Futrell
* @version 1.0
@@ -33,36 +33,36 @@ public class HtmlOccurrenceMarker implements OccurrenceMarker {
private static final Set<String> TAGS_REQUIRING_CLOSING = getRequiredClosingTags();
public static final Set<String> getRequiredClosingTags() {
final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav", "article", "aside", "h1",
"h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog", "blockquote", "ol", "ul", "dl", "a", "q",
"cite", "em", "strong", "small", "mark", "dfn", "abbr", "time", "progress", "meter", "code", "var", "samp", "kbd", "sub",
"sup", "span", "i", "b", "bdo", "ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas",
"map", "table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output", "details",
"bb", "menu", "legend", "div",
final String[] tags = { "html", "head", "title", "style", "script", "noscript", "body", "section", "nav",
"article", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "pre", "dialog",
"blockquote", "ol", "ul", "dl", "a", "q", "cite", "em", "strong", "small", "mark", "dfn", "abbr",
"time", "progress", "meter", "code", "var", "samp", "kbd", "sub", "sup", "span", "i", "b", "bdo",
"ruby", "rt", "rp", "ins", "del", "figure", "iframe", "object", "video", "audio", "canvas", "map",
"table", "caption", "form", "fieldset", "label", "button", "select", "datalist", "textarea", "output",
"details", "bb", "menu", "legend", "div",
// Obsolete elements
"acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing", "marquee", "nobr",
"noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", };
"acronym", "applet", "big", "blink", "center", "dir", "font", "frame", "frameset", "isindex", "listing",
"marquee", "nobr", "noembed", "noframes", "plaintext", "s", "spacer", "strike", "tt", "u", "xmp", };
return new HashSet<String>(Arrays.asList(tags));
}
/**
* If the caret is inside of a tag, this method returns the token
* representing the tag name; otherwise, <code>null</code> is returned.
* If the caret is inside of a tag, this method returns the token representing
* the tag name; otherwise, <code>null</code> is returned.
* <p>
*
* Currently, this method only checks for tag names on the same line as
* the caret, for simplicity. In the future it could check prior lines
* until the tag name is found.
* Currently, this method only checks for tag names on the same line as the
* caret, for simplicity. In the future it could check prior lines until the tag
* name is found.
*
* @param textArea
* The text area.
* @param occurrenceMarker
* The occurrence marker.
* @return The token to mark occurrences of. Note that, if the
* specified occurrence marker identifies tokens other than
* tag names, these other element types may be returned.
* @param textArea The text area.
* @param occurrenceMarker The occurrence marker.
* @return The token to mark occurrences of. Note that, if the specified
* occurrence marker identifies tokens other than tag names, these other
* element types may be returned.
*/
public static final Token getTagNameTokenForCaretOffset(RSyntaxTextArea textArea, OccurrenceMarker occurrenceMarker) {
public static final Token getTagNameTokenForCaretOffset(RSyntaxTextArea textArea,
OccurrenceMarker occurrenceMarker) {
// Get the tag name token.
// For now, we only check for tags on the current line, for simplicity.
int dot = textArea.getCaretPosition();
@@ -31,14 +31,11 @@ public interface LinkGenerator {
/**
* If a region of text under the mouse position should be considered a
* hyperlink, a result object is returned. This object describes what
* region of text is the link, and what action to perform if the link is
* clicked.
* hyperlink, a result object is returned. This object describes what region of
* text is the link, and what action to perform if the link is clicked.
*
* @param textArea
* The text component.
* @param offs
* The offset in the document under the mouse position.
* @param textArea The text component.
* @param offs The offset in the document under the mouse position.
* @return The link information, or <code>null</code> if no link is at the
* specified offset.
*/
@@ -25,10 +25,10 @@ import javax.swing.event.HyperlinkEvent;
public interface LinkGeneratorResult {
/**
* Executes the action associated with this object. If the result is a
* URL to open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this
* method itself.
* Executes the action associated with this object. If the result is a URL to
* open, a standard hyperlink event can be returned. Alternatively,
* <code>null</code> can be returned and the action performed in this method
* itself.
*
* @return The hyperlink event to broadcast from the text area, or
* <code>null</code> if the action's behavior occurs in this method
@@ -19,8 +19,8 @@ import javax.swing.text.Caret;
import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* Marks all occurrences of the token at the current caret position, if it is
* an identifier.
* Marks all occurrences of the token at the current caret position, if it is an
* identifier.
*
* @author Robert Futrell
* @version 1.0
@@ -50,10 +50,9 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
/**
* Constructor.
*
* @param delay
* The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param delay The delay between when the caret last moves and when the text
* should be scanned for matching occurrences. This should be in
* milliseconds.
*/
MarkOccurrencesSupport(int delay) {
this(delay, DEFAULT_COLOR);
@@ -62,12 +61,10 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
/**
* Constructor.
*
* @param delay
* The delay between when the caret last moves and when the
* text should be scanned for matching occurrences. This should
* be in milliseconds.
* @param color
* The color to use to mark the occurrences. This cannot be
* @param delay The delay between when the caret last moves and when the text
* should be scanned for matching occurrences. This should be in
* milliseconds.
* @param color The color to use to mark the occurrences. This cannot be
* <code>null</code>.
*/
MarkOccurrencesSupport(int delay, Color color) {
@@ -78,16 +75,15 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
}
/**
* Called after the caret has been moved and a fixed time delay has
* elapsed. This locates and highlights all occurrences of the identifier
* at the caret position, if any.
* Called after the caret has been moved and a fixed time delay has elapsed.
* This locates and highlights all occurrences of the identifier at the caret
* position, if any.
* <p>
*
* Callers should not call this method directly, but should rather prefer
* {@link #doMarkOccurrences()} to mark occurrences.
*
* @param e
* The event.
* @param e The event.
* @see #doMarkOccurrences()
*/
@Override
@@ -128,8 +124,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
/**
* Called when the caret moves in the text area.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void caretUpdate(CaretEvent e) {
@@ -147,8 +142,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
}
/**
* Immediately marks all occurrences of the token at the current caret
* position.
* Immediately marks all occurrences of the token at the current caret position.
*/
public void doMarkOccurrences() {
timer.stop();
@@ -187,11 +181,10 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
}
/**
* Installs this listener on a text area. If it is already installed on
* another text area, it is uninstalled first.
* Installs this listener on a text area. If it is already installed on another
* text area, it is uninstalled first.
*
* @param textArea
* The text area to install on.
* @param textArea The text area to install on.
*/
public void install(RSyntaxTextArea textArea) {
if (this.textArea != null) {
@@ -207,8 +200,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
/**
* Sets the color to use when marking occurrences.
*
* @param color
* The color to use.
* @param color The color to use.
* @see #getColor()
* @see #setPaintBorder(boolean)
*/
@@ -221,12 +213,11 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
}
/**
* Sets the delay between the last caret position change and when the
* text is scanned for matching identifiers. A delay is needed to prevent
* repeated scanning while the user is typing.
* Sets the delay between the last caret position change and when the text is
* scanned for matching identifiers. A delay is needed to prevent repeated
* scanning while the user is typing.
*
* @param delay
* The new delay.
* @param delay The new delay.
* @see #getDelay()
*/
public void setDelay(int delay) {
@@ -236,8 +227,7 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
/**
* Toggles whether a border is painted around marked highlights.
*
* @param paint
* Whether to paint a border.
* @param paint Whether to paint a border.
* @see #getPaintBorder()
* @see #setColor(Color)
*/
@@ -251,8 +241,8 @@ class MarkOccurrencesSupport implements CaretListener, ActionListener {
}
/**
* Uninstalls this listener from the current text area. Does nothing if
* it not currently installed on any text area.
* Uninstalls this listener from the current text area. Does nothing if it not
* currently installed on any text area.
*
* @see #install(RSyntaxTextArea)
*/
@@ -123,8 +123,7 @@ class MatchedBracketPopup extends JWindow {
}
/**
* Positions this popup to be in the top right-hand corner of the parent
* editor.
* Positions this popup to be in the top right-hand corner of the parent editor.
*/
private void setLocation() {
Point topLeft = textArea.getVisibleRect().getLocation();
@@ -10,9 +10,9 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
/**
* An <code>OccurrenceMarker</code> is called when the caret stops moving after
* a short period. If the current {@link TokenMaker} returns an instance of
* this class, it is told to mark all occurrences of the identifier at the
* caret position.
* a short period. If the current {@link TokenMaker} returns an instance of this
* class, it is told to mark all occurrences of the identifier at the caret
* position.
*
* @author Robert Futrell
* @version 1.0
@@ -20,27 +20,23 @@ import org.fife.ui.rtextarea.SmartHighlightPainter;
public interface OccurrenceMarker {
/**
* Returns the token to mark occurrences, of, provided it matches the
* criteria put forth by {@link #isValidType(RSyntaxTextArea, Token)}.
* For most languages, this method should return the token at the caret
* position.
* Returns the token to mark occurrences, of, provided it matches the criteria
* put forth by {@link #isValidType(RSyntaxTextArea, Token)}. For most
* languages, this method should return the token at the caret position.
*
* @param textArea
* The text area.
* @return The token to (possibly) mark occurrences of, or
* <code>null</code> if none.
* @param textArea The text area.
* @return The token to (possibly) mark occurrences of, or <code>null</code> if
* none.
*/
Token getTokenToMark(RSyntaxTextArea textArea);
/**
* Returns whether the specified token is a type that we can do a
* "mark occurrences" of. Typically, this will delegate to
* Returns whether the specified token is a type that we can do a "mark
* occurrences" of. Typically, this will delegate to
* {@link RSyntaxTextArea#getMarkOccurrencesOfTokenType(int)}.
*
* @param textArea
* The text area.
* @param t
* The token.
* @param textArea The text area.
* @param t The token.
* @return Whether we should mark all occurrences of this token.
*/
boolean isValidType(RSyntaxTextArea textArea, Token t);
@@ -48,14 +44,10 @@ public interface OccurrenceMarker {
/**
* Called when occurrences of a token should be marked.
*
* @param doc
* The document.
* @param t
* The document whose relevant occurrences should be marked.
* @param h
* The highlighter to add the highlights to.
* @param p
* The painter for the highlights.
* @param doc The document.
* @param t The document whose relevant occurrences should be marked.
* @param h The highlighter to add the highlights to.
* @param p The painter for the highlights.
*/
void markOccurrences(RSyntaxDocument doc, Token t, RSyntaxTextAreaHighlighter h, SmartHighlightPainter p);
}
@@ -55,20 +55,21 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
private Position firstOffsetModded;
private Position lastOffsetModded;
/**
* Mapping of notices to their highlights in the editor. Can't use a Map
* since parsers could return two <code>ParserNotice</code>s that compare
* equally via <code>equals()</code>. Real-world example: The Perl
* compiler will return 2+ identical error messages if the same error is
* committed in a single line more than once.
* Mapping of notices to their highlights in the editor. Can't use a Map since
* parsers could return two <code>ParserNotice</code>s that compare equally via
* <code>equals()</code>. Real-world example: The Perl compiler will return 2+
* identical error messages if the same error is committed in a single line more
* than once.
*/
private List<NoticeHighlightPair> noticeHighlightPairs;
/**
* Painter used to underline errors.
*/
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter = new SquiggleUnderlineHighlightPainter(Color.RED);
private SquiggleUnderlineHighlightPainter parserErrorHighlightPainter = new SquiggleUnderlineHighlightPainter(
Color.RED);
/**
* If this system property is set to <code>true</code>, debug messages
* will be printed to stdout to help diagnose parsing issues.
* If this system property is set to <code>true</code>, debug messages will be
* printed to stdout to help diagnose parsing issues.
*/
private static final String PROPERTY_DEBUG_PARSING = "rsta.debugParsing";
/**
@@ -76,17 +77,15 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
*/
private static final boolean DEBUG_PARSING;
/**
* The default delay between the last key press and when the document
* is parsed, in milliseconds.
* The default delay between the last key press and when the document is parsed,
* in milliseconds.
*/
private static final int DEFAULT_DELAY_MS = 1250;
/**
* Constructor.
*
* @param textArea
* The text area whose document the parser will be
* parsing.
* @param textArea The text area whose document the parser will be parsing.
*/
ParserManager(RSyntaxTextArea textArea) {
this(DEFAULT_DELAY_MS, textArea);
@@ -95,12 +94,9 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Constructor.
*
* @param delay
* The delay between the last key press and when the document
* is parsed.
* @param textArea
* The text area whose document the parser will be
* parsing.
* @param delay The delay between the last key press and when the document is
* parsed.
* @param textArea The text area whose document the parser will be parsing.
*/
ParserManager(int delay, RSyntaxTextArea textArea) {
this.textArea = textArea;
@@ -115,8 +111,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Called when the timer fires (e.g. it's time to parse the document).
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void actionPerformed(ActionEvent e) {
@@ -132,7 +127,8 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
RSyntaxDocument doc = (RSyntaxDocument) textArea.getDocument();
Element root = doc.getDefaultRootElement();
int firstLine = firstOffsetModded == null ? 0 : root.getElementIndex(firstOffsetModded.getOffset());
int lastLine = lastOffsetModded == null ? root.getElementCount() - 1 : root.getElementIndex(lastOffsetModded.getOffset());
int lastLine = lastOffsetModded == null ? root.getElementCount() - 1
: root.getElementIndex(lastOffsetModded.getOffset());
firstOffsetModded = lastOffsetModded = null;
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: Minimum lines to parse: " + firstLine + "-" + lastLine);
@@ -162,9 +158,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Adds a parser for the text area.
*
* @param parser
* The new parser. If this is <code>null</code>, nothing
* happens.
* @param parser The new parser. If this is <code>null</code>, nothing happens.
* @see #getParser(int)
* @see #removeParser(Parser)
*/
@@ -185,11 +179,10 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Adds highlights for a list of parser notices. Any current notices
* from the same Parser, in the same parsed range, are removed.
* Adds highlights for a list of parser notices. Any current notices from the
* same Parser, in the same parsed range, are removed.
*
* @param res
* The result of a parsing.
* @param res The result of a parsing.
* @see #clearParserNoticeHighlights()
*/
private void addParserNoticeHighlights(ParseResult res) {
@@ -231,11 +224,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
@Override
public void changedUpdate(DocumentEvent e) {}
public void changedUpdate(DocumentEvent e) {
}
private void clearParserNoticeHighlights() {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
@@ -250,8 +243,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Removes all parser notice highlights for a specific parser.
*
* @param parser
* The parser whose highlights to remove.
* @param parser The parser whose highlights to remove.
*/
private void clearParserNoticeHighlights(Parser parser) {
RSyntaxTextAreaHighlighter h = (RSyntaxTextAreaHighlighter) textArea.getHighlighter();
@@ -282,18 +274,16 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Forces the given {@link Parser} to re-parse the content of this text
* area.
* Forces the given {@link Parser} to re-parse the content of this text area.
* <p>
*
* This method can be useful when a <code>Parser</code> can be configured
* as to what notices it returns. For example, if a Java language parser
* can be configured to set whether no serialVersionUID is a warning,
* error, or ignored, this method can be called after changing the expected
* notice type to have the document re-parsed.
* This method can be useful when a <code>Parser</code> can be configured as to
* what notices it returns. For example, if a Java language parser can be
* configured to set whether no serialVersionUID is a warning, error, or
* ignored, this method can be called after changing the expected notice type to
* have the document re-parsed.
*
* @param parser
* The index of the <code>Parser</code> to re-run.
* @param parser The index of the <code>Parser</code> to re-run.
* @see #getParser(int)
*/
public void forceReparsing(int parser) {
@@ -315,8 +305,8 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Returns the delay between the last "concurrent" edit and when the
* document is re-parsed.
* Returns the delay between the last "concurrent" edit and when the document is
* re-parsed.
*
* @return The delay, in milliseconds.
* @see #setDelay(int)
@@ -328,8 +318,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Returns the specified parser.
*
* @param index
* The index of the parser.
* @param index The index of the parser.
* @return The parser.
* @see #getParserCount()
* @see #addParser(Parser)
@@ -349,12 +338,10 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Returns a list of the current parser notices for this text area.
* This method (like most Swing methods) should only be called on the
* EDT.
* Returns a list of the current parser notices for this text area. This method
* (like most Swing methods) should only be called on the EDT.
*
* @return The list of notices. This will be an empty list if there are
* none.
* @return The list of notices. This will be an empty list if there are none.
*/
public List<ParserNotice> getParserNotices() {
List<ParserNotice> notices = new ArrayList<ParserNotice>();
@@ -367,13 +354,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Returns the tool tip to display for a mouse event at the given
* location. This method is overridden to give a registered parser a
* chance to display a tool tip (such as an error description when the
* mouse is over an error highlight).
* Returns the tool tip to display for a mouse event at the given location. This
* method is overridden to give a registered parser a chance to display a tool
* tip (such as an error description when the mouse is over an error highlight).
*
* @param e
* The mouse event.
* @param e The mouse event.
* @return The tool tip to display, and possibly a hyperlink event handler.
*/
public ToolTipInfo getToolTipText(MouseEvent e) {
@@ -385,19 +370,12 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
int pos = textArea.viewToModel(p);
/*
* Highlighter.Highlight[] highlights = textArea.getHighlighter().
* getHighlights();
* for (int i=0; i<highlights.length; i++) {
* Highlighter.Highlight h = highlights[i];
* //if (h instanceof ParserNoticeHighlight) {
* // ParserNoticeHighlight pnh = (ParserNoticeHighlight)h;
* int start = h.getStartOffset();
* int end = h.getEndOffset();
* if (start<=pos && end>=pos) {
* //return pnh.getMessage();
* return textArea.getText(start, end-start);
* }
* //}
* }
* getHighlights(); for (int i=0; i<highlights.length; i++) {
* Highlighter.Highlight h = highlights[i]; //if (h instanceof
* ParserNoticeHighlight) { // ParserNoticeHighlight pnh =
* (ParserNoticeHighlight)h; int start = h.getStartOffset(); int end =
* h.getEndOffset(); if (start<=pos && end>=pos) { //return pnh.getMessage();
* return textArea.getText(start, end-start); } //} }
*/
if (noticeHighlightPairs != null) {
for (NoticeHighlightPair pair : noticeHighlightPairs) {
@@ -422,8 +400,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
public void handleDocumentEvent(DocumentEvent e) {
if (running && parsers.size() > 0) {
@@ -435,8 +412,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
* Called when the user clicks a hyperlink in a
* {@link org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip}.
*
* @param e
* The event.
* @param e The event.
*/
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
@@ -448,8 +424,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
@Override
public void insertUpdate(DocumentEvent e) {
@@ -473,10 +448,8 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Returns whether a parser notice contains the specified offset.
*
* @param notice
* The notice.
* @param offs
* The offset.
* @param notice The notice.
* @param offs The offset.
* @return Whether the notice contains the offset.
*/
private boolean noticeContainsPosition(ParserNotice notice, int offs) {
@@ -494,18 +467,16 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Since <code>viewToModel()</code> returns the <em>closest</em> model
* position, and the position doesn't <em>necessarily</em> contain the
* point passed in as an argument, this method checks whether the point is
* indeed contained in the view rectangle for the specified offset.
* Since <code>viewToModel()</code> returns the <em>closest</em> model position,
* and the position doesn't <em>necessarily</em> contain the point passed in as
* an argument, this method checks whether the point is indeed contained in the
* view rectangle for the specified offset.
*
* @param notice
* The parser notice.
* @param p
* The point possibly contained in the view range of the
* parser notice.
* @return Whether the parser notice actually contains the specified point
* in the view.
* @param notice The parser notice.
* @param p The point possibly contained in the view range of the parser
* notice.
* @return Whether the parser notice actually contains the specified point in
* the view.
*/
private boolean noticeContainsPointInView(ParserNotice notice, Point p) {
try {
@@ -547,8 +518,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Called when a property we're interested in changes.
*
* @param e
* The property change event.
* @param e The property change event.
*/
@Override
public void propertyChange(PropertyChangeEvent e) {
@@ -569,8 +539,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Removes a parser.
*
* @param parser
* The parser to remove.
* @param parser The parser to remove.
* @return Whether the parser was found.
* @see #addParser(Parser)
* @see #getParser(int)
@@ -585,11 +554,10 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Removes all parser notices (and clears highlights in the editor) from
* a particular parser.
* Removes all parser notices (and clears highlights in the editor) from a
* particular parser.
*
* @param parser
* The parser.
* @param parser The parser.
*/
private void removeParserNotices(Parser parser) {
if (noticeHighlightPairs != null) {
@@ -605,12 +573,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Removes any currently stored notices (and the corresponding highlights
* from the editor) from the same Parser, and in the given line range,
* as in the results.
* Removes any currently stored notices (and the corresponding highlights from
* the editor) from the same Parser, and in the given line range, as in the
* results.
*
* @param res
* The results.
* @param res The results.
*/
private void removeParserNotices(ParseResult res) {
if (noticeHighlightPairs != null) {
@@ -636,8 +603,7 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
/**
* Called when the document is modified.
*
* @param e
* The document event.
* @param e The document event.
*/
@Override
public void removeUpdate(DocumentEvent e) {
@@ -670,12 +636,11 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Sets the delay between the last "concurrent" edit and when the document
* is re-parsed.
* Sets the delay between the last "concurrent" edit and when the document is
* re-parsed.
*
* @param millis
* The new delay, in milliseconds. This must be greater
* than <code>0</code>.
* @param millis The new delay, in milliseconds. This must be greater than
* <code>0</code>.
* @see #getDelay()
*/
public void setDelay(int millis) {
@@ -690,18 +655,16 @@ class ParserManager implements DocumentListener, ActionListener, HyperlinkListen
}
/**
* Returns whether a parser notice should be removed, based on a parse
* result.
* Returns whether a parser notice should be removed, based on a parse result.
*
* @param notice
* The notice in question.
* @param res
* The result.
* @param notice The notice in question.
* @param res The result.
* @return Whether the notice should be removed.
*/
private boolean shouldRemoveNotice(ParserNotice notice, ParseResult res) {
if (DEBUG_PARSING) {
System.out.println("[DEBUG]: ... ... shouldRemoveNotice " + notice + ": " + (notice.getParser() == res.getParser()));
System.out.println(
"[DEBUG]: ... ... shouldRemoveNotice " + notice + ": " + (notice.getParser() == res.getParser()));
}
// NOTE: We must currently remove all notices for the parser. Parser
// implementors are required to parse the entire document each parsing
@@ -12,8 +12,8 @@ import javax.swing.JWindow;
/**
* A hook allowing hosting applications to decorate JWindows created by the
* AutoComplete library. For example, you could use the
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows
* to the windows.
* <a href="http://jgoodies.com/">JGoodies</a> library to add drop shadows to
* the windows.
*
* @author Robert Futrell
* @version 1.0
@@ -27,20 +27,18 @@ public abstract class PopupWindowDecorator {
/**
* Callback called whenever an appropriate JWindow is created by the
* AutoComplete library. Implementations can decorate the window however
* they see fit.
* AutoComplete library. Implementations can decorate the window however they
* see fit.
*
* @param window
* The newly-created window.
* @param window The newly-created window.
*/
public abstract void decorate(JWindow window);
/**
* Returns the singleton instance of this class. This should only be
* called on the EDT.
* Returns the singleton instance of this class. This should only be called on
* the EDT.
*
* @return The singleton instance of this class, or <code>null</code>
* for none.
* @return The singleton instance of this class, or <code>null</code> for none.
* @see #set(PopupWindowDecorator)
*/
public static PopupWindowDecorator get() {
@@ -48,11 +46,10 @@ public abstract class PopupWindowDecorator {
}
/**
* Sets the singleton instance of this class. This should only be called
* on the EDT.
* Sets the singleton instance of this class. This should only be called on the
* EDT.
*
* @param decorator
* The new instance of this class. This may be
* @param decorator The new instance of this class. This may be
* <code>null</code>.
* @see #get()
*/
+18 -26
View File
@@ -22,20 +22,16 @@ interface RSTAView {
* Returns the y-coordinate of the specified line.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
* This method is quicker than using traditional <code>modelToView(int)</code>
* calls, as the entire bounding box isn't computed.
*
* @param alloc
* The area the text area can render into.
* @param line
* The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException
* If <code>line</code> isn't a valid line
* number for this document.
* @param alloc The area the text area can render into.
* @param line The line number.
* @return The y-coordinate of the top of the line, or <code>-1</code> if this
* text area doesn't yet have a positive size or the line is hidden
* (i.e. from folding).
* @throws BadLocationException If <code>line</code> isn't a valid line number
* for this document.
*/
int yForLine(Rectangle alloc, int line) throws BadLocationException;
@@ -43,20 +39,16 @@ interface RSTAView {
* Returns the y-coordinate of the line containing a specified offset.
* <p>
*
* This method is quicker than using traditional
* <code>modelToView(int)</code> calls, as the entire bounding box isn't
* computed.
* This method is quicker than using traditional <code>modelToView(int)</code>
* calls, as the entire bounding box isn't computed.
*
* @param alloc
* The area the text area can render into.
* @param offs
* The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if
* this text area doesn't yet have a positive size or the line is
* hidden (i.e. from folding).
* @throws BadLocationException
* If <code>offs</code> isn't a valid offset
* into the document.
* @param alloc The area the text area can render into.
* @param offs The offset info the document.
* @return The y-coordinate of the top of the offset, or <code>-1</code> if this
* text area doesn't yet have a positive size or the line is hidden
* (i.e. from folding).
* @throws BadLocationException If <code>offs</code> isn't a valid offset into
* the document.
*/
int yForLineContaining(Rectangle alloc, int offs) throws BadLocationException;
}
@@ -51,8 +51,7 @@ import me.ByteEdit.edit.JavaAssemblyTokenMaker;
public class RSyntaxDocument extends RDocument implements Iterable<Token>, SyntaxConstants {
/**
* Creates a {@link TokenMaker} appropriate for a given programming
* language.
* Creates a {@link TokenMaker} appropriate for a given programming language.
*/
private transient TokenMakerFactory tokenMakerFactory;
/**
@@ -64,8 +63,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
*/
private String syntaxStyle;
/**
* Array of values representing the "last token type" on each line. This is
* used
* Array of values representing the "last token type" on each line. This is used
* in cases such as multi-line comments: if the previous line ended with an
* (unclosed) multi-line comment, we can use this knowledge and start the
* current line's syntax highlighting in multi-line comment state.
@@ -77,35 +75,28 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
private transient int tokenRetrievalCount = 0;
private transient Segment s;
/**
* If this is set to <code>true</code>, debug information about how much
* token
* If this is set to <code>true</code>, debug information about how much token
* caching is helping is printed to stdout.
*/
private static final boolean DEBUG_TOKEN_CACHING = false;
/**
* Constructs a plain text document. A default root element is created, and
* the
* Constructs a plain text document. A default root element is created, and the
* tab size set to 5.
*
* @param syntaxStyle
* The syntax highlighting scheme to use.
* @param syntaxStyle The syntax highlighting scheme to use.
*/
public RSyntaxDocument(String syntaxStyle) {
this(null, syntaxStyle);
}
/**
* Constructs a plain text document. A default root element is created, and
* the
* Constructs a plain text document. A default root element is created, and the
* tab size set to 5.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this
* is
* <code>null</code>, a default factory is used.
* @param syntaxStyle
* The syntax highlighting scheme to use.
* @param tmf The <code>TokenMakerFactory</code> for this document. If
* this is <code>null</code>, a default factory is used.
* @param syntaxStyle The syntax highlighting scheme to use.
*/
public RSyntaxDocument(TokenMakerFactory tmf, String syntaxStyle) {
putProperty(tabSizeAttribute, Integer.valueOf(5));
@@ -117,25 +108,21 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Alerts all listeners to this document of an insertion. This is overridden
* so
* Alerts all listeners to this document of an insertion. This is overridden so
* we can update our syntax highlighting stuff.
* <p>
* The syntax highlighting stuff has to be here instead of in
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called
* by
* <code>insertUpdate</code> because <code>insertUpdate</code> is not called by
* the undo/redo actions, but this method is.
*
* @param e
* The change.
* @param e The change.
*/
@Override
protected void fireInsertUpdate(DocumentEvent e) {
cachedTokenList = null;
/*
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and
* "last tokens on lines"
* structure, we can update our token elements and "last tokens on lines"
* structure.
*/
Element lineMap = getDefaultRootElement();
@@ -176,21 +163,18 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* This method is called AFTER the content has been inserted into the
* document
* This method is called AFTER the content has been inserted into the document
* and the element structure has been updated.
* <p>
* The syntax-highlighting updates need to be done here (as opposed to an
* override of <code>postRemoveUpdate</code>) as this method is called in
* response to undo/redo events, whereas <code>postRemoveUpdate</code> is
* not.
* response to undo/redo events, whereas <code>postRemoveUpdate</code> is not.
* <p>
* Now that the text is actually inserted into the content and element
* structure, we can update our token elements and "last tokens on lines"
* structure.
*
* @param chng
* The change that occurred.
* @param chng The change that occurred.
* @see #removeUpdate
*/
@Override
@@ -245,10 +229,8 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* Returns the closest {@link TokenTypes "standard" token type} for a given
* "internal" token type (e.g. one whose value is <code>&lt; 0</code>).
*
* @param type
* The token type.
* @return The closest "standard" token type. If a mapping is not defined
* for
* @param type The token type.
* @return The closest "standard" token type. If a mapping is not defined for
* this language, then <code>type</code> is returned.
*/
public int getClosestStandardTokenTypeForInternalType(int type) {
@@ -256,10 +238,8 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Returns whether closing markup tags should be automatically completed.
* This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()}
* also
* Returns whether closing markup tags should be automatically completed. This
* method only returns <code>true</code> if {@link #getLanguageIsMarkup()} also
* returns <code>true</code>.
*
* @return Whether markup closing tags should be automatically completed.
@@ -274,12 +254,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* Returns whether the current programming language uses curly braces
* ('<code>{</code>' and '<code>}</code>') to denote code blocks.
*
* @param languageIndex
* The language index at the offset in question. Since some
* <code>TokenMaker</code>s effectively have nested languages
* (such
* as JavaScript in HTML), this parameter tells the
* <code>TokenMaker</code> what sub-language to look at.
* @param languageIndex The language index at the offset in question. Since some
* <code>TokenMaker</code>s effectively have nested
* languages (such as JavaScript in HTML), this parameter
* tells the <code>TokenMaker</code> what sub-language to
* look at.
* @return Whether curly braces denote code blocks.
*/
public boolean getCurlyBracesDenoteCodeBlocks(int languageIndex) {
@@ -287,8 +266,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Returns whether the current language is a markup language, such as HTML,
* XML
* Returns whether the current language is a markup language, such as HTML, XML
* or PHP.
*
* @return Whether the current language is a markup language.
@@ -300,10 +278,8 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
/**
* Returns the token type of the last token on the given line.
*
* @param line
* The line to inspect.
* @return The token type of the last token on the specified line. If the
* line
* @param line The line to inspect.
* @return The token type of the last token on the specified line. If the line
* is invalid, an exception is thrown.
*/
public int getLastTokenTypeOnLine(int line) {
@@ -311,15 +287,12 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Returns the text to place at the beginning and end of a line to "comment"
* it
* Returns the text to place at the beginning and end of a line to "comment" it
* in this programming language.
*
* @return The start and end strings to add to a line to "comment" it out. A
* <code>null</code> value for either means there is no string to
* add
* for that part. A value of <code>null</code> for the array means
* this
* <code>null</code> value for either means there is no string to add
* for that part. A value of <code>null</code> for the array means this
* language does not support commenting/uncommenting lines.
*/
public String[] getLineCommentStartAndEnd(int languageIndex) {
@@ -327,14 +300,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Returns whether tokens of the specified type should have "mark
* occurrences"
* Returns whether tokens of the specified type should have "mark occurrences"
* enabled for the current programming language.
*
* @param type
* The token type.
* @return Whether tokens of this type should have "mark occurrences"
* enabled.
* @param type The token type.
* @return Whether tokens of this type should have "mark occurrences" enabled.
*/
boolean getMarkOccurrencesOfTokenType(int type) {
return tokenMaker.getMarkOccurrencesOfTokenType(type);
@@ -353,8 +323,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
* This method returns whether auto indentation should be done if Enter is
* pressed at the end of the specified line.
*
* @param line
* The line to check.
* @param line The line to check.
* @return Whether an extra indentation should be done.
*/
public boolean getShouldIndentNextLine(int line) {
@@ -376,12 +345,10 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
/**
* Returns a token list for the specified segment of text representing the
* specified line number. This method is basically a wrapper for
* <code>tokenMaker.getTokenList</code> that takes into account the last
* token
* <code>tokenMaker.getTokenList</code> that takes into account the last token
* on the previous line to assure token accuracy.
*
* @param line
* The line number of <code>text</code> in the document, &gt;= 0.
* @param line The line number of <code>text</code> in the document, &gt;= 0.
* @return A token list representing the specified line.
*/
public final Token getTokenListForLine(int line) {
@@ -424,14 +391,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
/**
* Returns whether a character could be part of an "identifier" token in a
* specific language. This is used to identify such things as the bounds of
* the
* specific language. This is used to identify such things as the bounds of the
* "word" to select on double-clicking.
*
* @param languageIndex
* The language index the character was found in.
* @param ch
* The character.
* @param languageIndex The language index the character was found in.
* @param ch The character.
* @return Whether the character could be part of an "identifier" token.
*/
public boolean isIdentifierChar(int languageIndex, char ch) {
@@ -439,10 +403,8 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Returns an iterator over the paintable tokens in this document. Results
* are
* undefined if this document is modified while the iterator is being
* iterated
* Returns an iterator over the paintable tokens in this document. Results are
* undefined if this document is modified while the iterator is being iterated
* through, so this should only be used on the EDT.
* <p>
*
@@ -459,8 +421,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
/**
* Deserializes a document.
*
* @param in
* The stream to read from.
* @param in The stream to read from.
* @throws ClassNotFoundException
* @throws IOException
*/
@@ -478,14 +439,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Makes our private <code>Segment s</code> point to the text in our
* document
* referenced by the specified element. Note that <code>line</code> MUST be
* a
* Makes our private <code>Segment s</code> point to the text in our document
* referenced by the specified element. Note that <code>line</code> MUST be a
* valid line number in the document.
*
* @param line
* The line number you want to get.
* @param line The line number you want to get.
*/
private void setSharedSegment(int line) {
Element map = getDefaultRootElement();
@@ -506,17 +464,14 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Sets the syntax style being used for syntax highlighting in this
* document.
* Sets the syntax style being used for syntax highlighting in this document.
* What styles are supported by a document is determined by its
* {@link TokenMakerFactory}. By default, all <code>RSyntaxDocument</code>s
* support all languages built into <code>RSyntaxTextArea</code>.
*
* @param styleKey
* The new style to use, such as
* @param styleKey The new style to use, such as
* {@link SyntaxConstants#SYNTAX_STYLE_JAVA}. If this style is
* not
* known or supported by this document, then
* not known or supported by this document, then
* {@link SyntaxConstants#SYNTAX_STYLE_NONE} is used.
* @see #setSyntaxStyle(TokenMaker)
* @see #getSyntaxStyle()
@@ -532,13 +487,11 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Sets the syntax style being used for syntax highlighting in this
* document.
* Sets the syntax style being used for syntax highlighting in this document.
* You should call this method if you've created a custom token maker for a
* language not normally supported by <code>RSyntaxTextArea</code>.
*
* @param tokenMaker
* The new token maker to use.
* @param tokenMaker The new token maker to use.
* @see #setSyntaxStyle(String)
*/
public void setSyntaxStyle(TokenMaker tokenMaker) {
@@ -550,9 +503,7 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
/**
* Sets the token maker factory used by this document.
*
* @param tmf
* The <code>TokenMakerFactory</code> for this document. If this
* is
* @param tmf The <code>TokenMakerFactory</code> for this document. If this is
* <code>null</code>, a default factory is used.
*/
public void setTokenMakerFactory(TokenMakerFactory tmf) {
@@ -560,20 +511,15 @@ public class RSyntaxDocument extends RDocument implements Iterable<Token>, Synta
}
/**
* Loops through the last-tokens-on-lines array from a specified point
* onward,
* updating last-token values until they stop changing. This should be
* called
* when lines are updated/inserted/removed, as doing so may cause lines
* below to
* Loops through the last-tokens-on-lines array from a specified point onward,
* updating last-token values until they stop changing. This should be called
* when lines are updated/inserted/removed, as doing so may cause lines below to
* change color.
*
* @param line
* The first line to check for a change in last-token value.
* @param numLines
* The number of lines in the document.
* @param previousTokenType
* The last-token value of the line just before
* @param line The first line to check for a change in last-token
* value.
* @param numLines The number of lines in the document.
* @param previousTokenType The last-token value of the line just before
* <code>line</code>.
* @return The last line that needs repainting.
*/
File diff suppressed because it is too large. Load diff
Loaded 100 of 275 files, more files were not shown because too many files have changed in this diff. Show more