Updated to support Java 11, but only Java 8 features

This commit is contained in:
SuspiciousActivity committed 2018-09-26 17:19:03 +02:00
1 parent 58c8e90c81
commit 89f9753d8b
19 files changed
+1877 -2145

No files matched your search

+30 -33
View File
@@ -49,7 +49,7 @@ import me.ByteEdit.utils.SwitchIntContainer;
import me.ByteEdit.utils.UnicodeUtils; import me.ByteEdit.utils.UnicodeUtils;
public class Assembler { public class Assembler {
public static ClassNode assemble(String input) { public static ClassNode assemble(String input) {
Main.txtByteEditView.removeAllLineHighlights(); Main.txtByteEditView.removeAllLineHighlights();
CustomBufferedReader read = null; CustomBufferedReader read = null;
@@ -79,9 +79,9 @@ public class Assembler {
clazz.innerClasses = new ArrayList<>(); clazz.innerClasses = new ArrayList<>();
} else { } else {
String split[] = s.split(" "); String split[] = s.split(" ");
clazz.innerClasses.add(new InnerClassNode(UnicodeUtils.unescape(split[1]), clazz.innerClasses
split[2].equals("null") ? null : UnicodeUtils.unescape(split[2]), .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]), Integer.parseInt(split[4].substring(2), 16))); split[3].equals("null") ? null : UnicodeUtils.unescape(split[3]), Integer.parseInt(split[4].substring(2), 16)));
} }
} }
{ {
@@ -297,9 +297,8 @@ public class Assembler {
_end = new LabelNode(entry.getKey()); _end = new LabelNode(entry.getKey());
} }
} }
node.localVariables node.localVariables.add(new LocalVariableNode(sp[0], UnicodeUtils.unescape(sp[1].substring(1, sp[1].length() - 2)),
.add(new LocalVariableNode(sp[0], UnicodeUtils.unescape(sp[1].substring(1, sp[1].length() - 2)), signat, signat, _start, _end, Integer.parseInt(sp[2].substring(0, sp[2].length() - 2))));
_start, _end, Integer.parseInt(sp[2].substring(0, sp[2].length() - 2))));
} }
for (String st : tryCatchBlocksToParse) { for (String st : tryCatchBlocksToParse) {
String[] sp = st.split(" "); String[] sp = st.split(" ");
@@ -320,8 +319,8 @@ public class Assembler {
_handler = new LabelNode(entry.getKey()); _handler = new LabelNode(entry.getKey());
} }
} }
node.tryCatchBlocks.add(new TryCatchBlockNode(_start, _end, _handler, node.tryCatchBlocks
sp[0].equals("null") ? null : UnicodeUtils.unescape(sp[0]))); .add(new TryCatchBlockNode(_start, _end, _handler, sp[0].equals("null") ? null : UnicodeUtils.unescape(sp[0])));
} }
clazz.methods.add(node); clazz.methods.add(node);
// signature = null; // signature = null;
@@ -432,7 +431,7 @@ public class Assembler {
} catch (Exception e) {} } catch (Exception e) {}
} }
} }
private static AnnotationNode parseAnnotation(String s) { private static AnnotationNode parseAnnotation(String s) {
s = s.substring(1); s = s.substring(1);
String[] split = s.split(" "); String[] split = s.split(" ");
@@ -482,7 +481,7 @@ public class Assembler {
} }
return node; return node;
} }
private static String consolidateStrings(String[] args, int start) { private static String consolidateStrings(String[] args, int start) {
if (args.length == 0) { if (args.length == 0) {
return null; return null;
@@ -495,7 +494,7 @@ public class Assembler {
} }
return ret; return ret;
} }
private static String consolidateStrings(String[] args, int start, int end) { private static String consolidateStrings(String[] args, int start, int end) {
if (end == 0) { if (end == 0) {
return null; return null;
@@ -508,7 +507,7 @@ public class Assembler {
} }
return ret; return ret;
} }
private static Object getValue(String s, String to) { private static Object getValue(String s, String to) {
if (s == null) { if (s == null) {
return null; return null;
@@ -537,7 +536,7 @@ public class Assembler {
} }
} }
} }
private static AbstractInsnNode getNode(String s, HashMap<Label, Integer> labels) throws Exception { private static AbstractInsnNode getNode(String s, HashMap<Label, Integer> labels) throws Exception {
if (s.startsWith("// label ")) { if (s.startsWith("// label ")) {
int labelNr = Integer.parseInt(s.substring(9)); int labelNr = Integer.parseInt(s.substring(9));
@@ -643,8 +642,8 @@ public class Assembler {
} }
} }
} }
return new FrameNode(Integer.parseInt(str1.split(" ")[0]), local != null ? local.length : 0, local, return new FrameNode(Integer.parseInt(str1.split(" ")[0]), local != null ? local.length : 0, local, stack != null ? stack.length : 0,
stack != null ? stack.length : 0, stack); stack);
} else { } else {
String[] split = s.split(" "); String[] split = s.split(" ");
String command = split[0]; String command = split[0];
@@ -694,12 +693,12 @@ public class Assembler {
if (str.startsWith("Type: ")) { if (str.startsWith("Type: ")) {
str = str.substring(8, str.length() - 2); str = str.substring(8, str.length() - 2);
String[] split2 = str.split("\n\t"); String[] split2 = str.split("\n\t");
Constructor<Type> constr = Type.class.getDeclaredConstructor(int.class, char[].class, int.class, int.class); Constructor<Type> constr = Type.class.getDeclaredConstructor(int.class, String.class, int.class, int.class);
constr.setAccessible(true); constr.setAccessible(true);
String type = split2[0].substring(7); String type = split2[0].substring(7);
val = constr.newInstance(new Object[] { ClassUtil.getIDFromClassNameForType(type), val = constr.newInstance(
split2[3].substring(6, split2[3].length() - 1).toCharArray(), Integer.parseInt(split2[1].substring(5)), new Object[] { ClassUtil.getIDFromClassNameForType(type), split2[3].substring(6, split2[3].length() - 1),
Integer.parseInt(split2[2].substring(5)) }); Integer.parseInt(split2[1].substring(7)), Integer.parseInt(split2[2].substring(5)) });
} else if (str.startsWith("\"") && str.endsWith("\"")) { } else if (str.startsWith("\"") && str.endsWith("\"")) {
val = UnicodeUtils.unescape(str.substring(1, str.length() - 1)); val = UnicodeUtils.unescape(str.substring(1, str.length() - 1));
} else if (str.endsWith("l")) { } else if (str.endsWith("l")) {
@@ -1193,7 +1192,7 @@ public class Assembler {
labelIDs.put(BigInteger.valueOf(dfltLabelNr), dflt); labelIDs.put(BigInteger.valueOf(dfltLabelNr), dflt);
} }
labelMap.replaceAll(new BiFunction<SwitchIntContainer, LabelNode, LabelNode>() { labelMap.replaceAll(new BiFunction<SwitchIntContainer, LabelNode, LabelNode>() {
@Override @Override
public LabelNode apply(SwitchIntContainer t, LabelNode u) { public LabelNode apply(SwitchIntContainer t, LabelNode u) {
if (entry.getValue().intValue() == t.getId()) { if (entry.getValue().intValue() == t.getId()) {
@@ -1201,7 +1200,7 @@ public class Assembler {
} }
return u; return u;
} }
}); });
} }
if (dflt == null) { if (dflt == null) {
@@ -1226,8 +1225,7 @@ public class Assembler {
arr[counter] = ln; arr[counter] = ln;
counter++; counter++;
} }
return new TableSwitchInsnNode(Integer.parseInt(split2[0].substring(5)), Integer.parseInt(split2[1].substring(5)), dflt, return new TableSwitchInsnNode(Integer.parseInt(split2[0].substring(5)), Integer.parseInt(split2[1].substring(5)), dflt, arr);
arr);
} }
case "lookupswitch": { case "lookupswitch": {
String str = s.substring(15, s.length() - 2).replace("\t", ""); String str = s.substring(15, s.length() - 2).replace("\t", "");
@@ -1260,7 +1258,7 @@ public class Assembler {
labelIDs.put(BigInteger.valueOf(labelNr), dflt); labelIDs.put(BigInteger.valueOf(labelNr), dflt);
} }
labelMap.replaceAll(new BiFunction<SwitchIntContainer, LabelNode, LabelNode>() { labelMap.replaceAll(new BiFunction<SwitchIntContainer, LabelNode, LabelNode>() {
@Override @Override
public LabelNode apply(SwitchIntContainer t, LabelNode u) { public LabelNode apply(SwitchIntContainer t, LabelNode u) {
if (entry.getValue().intValue() == t.getId()) { if (entry.getValue().intValue() == t.getId()) {
@@ -1268,7 +1266,7 @@ public class Assembler {
} }
return u; return u;
} }
}); });
} }
if (dflt == null) { if (dflt == null) {
@@ -1375,19 +1373,18 @@ public class Assembler {
if (st.equals("]")) { if (st.equals("]")) {
if (stage == 1) { if (stage == 1) {
try { try {
Constructor<Type> constr = Type.class.getDeclaredConstructor(int.class, char[].class, int.class, Constructor<Type> constr = Type.class.getDeclaredConstructor(int.class, String.class, int.class, int.class);
int.class);
constr.setAccessible(true); constr.setAccessible(true);
args.add(constr.newInstance(new Object[] { Integer.parseInt(vals.get(0).substring(6)), args.add(constr.newInstance(new Object[] { Integer.parseInt(vals.get(0).substring(6)),
UnicodeUtils.unescape(vals.get(3).substring(6, vals.get(3).length() - 1)).toCharArray(), UnicodeUtils.unescape(vals.get(3).substring(6, vals.get(3).length() - 1)),
Integer.parseInt(vals.get(1).substring(5)), Integer.parseInt(vals.get(2).substring(5)) })); Integer.parseInt(vals.get(1).substring(7)), Integer.parseInt(vals.get(2).substring(5)) }));
} catch (Exception e) { } catch (Exception e) {
throw e; throw e;
} }
} else if (stage == 2) { } else if (stage == 2) {
args.add(new Handle(Integer.parseInt(vals.get(4).substring(5)), args.add(new Handle(Integer.parseInt(vals.get(4).substring(5)), UnicodeUtils.unescape(vals.get(1).substring(7)),
UnicodeUtils.unescape(vals.get(1).substring(7)), UnicodeUtils.unescape(vals.get(0).substring(6)), UnicodeUtils.unescape(vals.get(0).substring(6)), UnicodeUtils.unescape(vals.get(2).substring(6)),
UnicodeUtils.unescape(vals.get(2).substring(6)), Boolean.parseBoolean(vals.get(3).substring(13)))); Boolean.parseBoolean(vals.get(3).substring(13))));
vals.clear(); vals.clear();
} }
stage = 0; stage = 0;
+52 -54
View File
@@ -40,36 +40,36 @@ import me.ByteEdit.utils.OpcodesReverse;
import me.ByteEdit.utils.UnicodeUtils; import me.ByteEdit.utils.UnicodeUtils;
public class Disassembler { public class Disassembler {
public static String disassemble(ClassNode classNode) { public static String disassemble(ClassNode classNode) {
return disassemble(classNode, null).getDisassembly(); return disassemble(classNode, null).getDisassembly();
} }
public static class DisassembleTuple { public static class DisassembleTuple {
private final String dis; private final String dis;
private final int line; private final int line;
public DisassembleTuple(String dis) { public DisassembleTuple(String dis) {
this.dis = dis; this.dis = dis;
this.line = -1; this.line = -1;
} }
public DisassembleTuple(String dis, int line) { public DisassembleTuple(String dis, int line) {
this.dis = dis; this.dis = dis;
this.line = line; this.line = line;
} }
public String getDisassembly() { public String getDisassembly() {
return dis; return dis;
} }
public int getLine() { public int getLine() {
return line; return line;
} }
} }
public static DisassembleTuple disassemble(ClassNode classNode, MethodNode methodToFind) { public static DisassembleTuple disassemble(ClassNode classNode, MethodNode methodToFind) {
if (classNode == null) { if (classNode == null) {
return new DisassembleTuple("ClassNode is null! This is not a valid java class file!"); return new DisassembleTuple("ClassNode is null! This is not a valid java class file!");
@@ -139,8 +139,10 @@ public class Disassembler {
} }
s += "// #Class v:" + classNode.version + "\n"; s += "// #Class v:" + classNode.version + "\n";
s += "// #Signature: " + UnicodeUtils.escapeWithSpaces(classNode.signature) + "\n"; s += "// #Signature: " + UnicodeUtils.escapeWithSpaces(classNode.signature) + "\n";
s += "// #OuterMethod: " + (classNode.outerMethod == null ? "null" : (UnicodeUtils.escapeWithSpaces(classNode.outerMethod) + " " s += "// #OuterMethod: "
+ UnicodeUtils.escapeWithSpaces(classNode.outerMethodDesc))) + "\n"; + (classNode.outerMethod == null ? "null"
: (UnicodeUtils.escapeWithSpaces(classNode.outerMethod) + " " + UnicodeUtils.escapeWithSpaces(classNode.outerMethodDesc)))
+ "\n";
s += "// #OuterClass: " + UnicodeUtils.escapeWithSpaces(classNode.outerClass) + "\n"; s += "// #OuterClass: " + UnicodeUtils.escapeWithSpaces(classNode.outerClass) + "\n";
s += "// #InnerClasses:\n"; s += "// #InnerClasses:\n";
if (classNode.innerClasses != null) { if (classNode.innerClasses != null) {
@@ -163,8 +165,7 @@ public class Disassembler {
if (!interfaceStr.isEmpty()) if (!interfaceStr.isEmpty())
s += "implements " + interfaceStr + " "; s += "implements " + interfaceStr + " ";
} }
s += "{\n// #SourceFile: " + (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(classNode.sourceFile)) s += "{\n// #SourceFile: " + (classNode.sourceFile == null ? "null" : UnicodeUtils.escape(classNode.sourceFile)) + "\n\n// #Fields\n";
+ "\n\n// #Fields\n";
for (FieldNode fn : classNode.fields) { for (FieldNode fn : classNode.fields) {
if (fn.signature != null) if (fn.signature != null)
s += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(fn.signature) + "\n"; s += "\t// #Signature: " + UnicodeUtils.escapeWithSpaces(fn.signature) + "\n";
@@ -227,8 +228,8 @@ public class Disassembler {
s += "\n"; s += "\n";
} }
} }
s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient") + UnicodeUtils.escapeWithSpaces(fn.desc) s += "\t" + ClassUtil.getAccessFlagsFull(fn.access).replace("varargs", "transient") + UnicodeUtils.escapeWithSpaces(fn.desc) + " "
+ " " + UnicodeUtils.escapeWithSpaces(fn.name); + UnicodeUtils.escapeWithSpaces(fn.name);
if (fn.value != null) { if (fn.value != null) {
s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc, true); s += " = " + ClassUtil.getDecompiledValue(fn.value, fn.desc, true);
} }
@@ -323,8 +324,8 @@ public class Disassembler {
s += dis[0]; s += dis[0];
s += "\t}\n\n"; s += "\t}\n\n";
} catch (Exception e) { } catch (Exception e) {
s += "\t\t// Method couldn't be disassembled:\n\t\t// " + e.getClass().getName() + ": " + e.getMessage() + "\n" + Arrays s += "\t\t// Method couldn't be disassembled:\n\t\t// " + e.getClass().getName() + ": " + e.getMessage() + "\n"
.toString(e.getStackTrace()).replace(", ", "\n\t\t// \tat ").replace("[", "\t\t// \tat ").replace("]", "") + Arrays.toString(e.getStackTrace()).replace(", ", "\n\t\t// \tat ").replace("[", "\t\t// \tat ").replace("]", "")
+ "\n\t}\n\n"; + "\n\t}\n\n";
} }
} }
@@ -335,7 +336,7 @@ public class Disassembler {
+ Arrays.toString(e.getStackTrace()).replace(", ", "\n\tat ").replace("[", "\tat ").replace("]", "") + "\n"); + Arrays.toString(e.getStackTrace()).replace(", ", "\n\tat ").replace("[", "\tat ").replace("]", "") + "\n");
} }
} }
private static String[] disassembleMethod(String className, MethodNode mn) { private static String[] disassembleMethod(String className, MethodNode mn) {
HashMap<Label, Integer> labels = new HashMap<Label, Integer>(); HashMap<Label, Integer> labels = new HashMap<Label, Integer>();
String s = ""; String s = "";
@@ -364,7 +365,7 @@ public class Disassembler {
} }
return new String[] { s, localVarTable, tryCatchTable }; return new String[] { s, localVarTable, tryCatchTable };
} }
public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels) { public static String disassembleInstruction(AbstractInsnNode n, HashMap<Label, Integer> labels) {
switch (n.getClass().getSimpleName()) { switch (n.getClass().getSimpleName()) {
case "FieldInsnNode": { case "FieldInsnNode": {
@@ -427,38 +428,36 @@ public class Disassembler {
} }
case "InvokeDynamicInsnNode": { case "InvokeDynamicInsnNode": {
InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n; InvokeDynamicInsnNode node = (InvokeDynamicInsnNode) n;
String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: " String s = "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " [\n\t\t\tname: " + UnicodeUtils.escapeWithSpaces(node.name)
+ UnicodeUtils.escapeWithSpaces(node.name) + "\n\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(node.desc) + "\n\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(node.desc) + "\n\t\t\tHandle: [\n\t\t\t\tname: "
+ "\n\t\t\tHandle: [\n\t\t\t\tname: " + UnicodeUtils.escapeWithSpaces(node.bsm.getName()) + "\n\t\t\t\towner: " + UnicodeUtils.escapeWithSpaces(node.bsm.getName()) + "\n\t\t\t\towner: " + UnicodeUtils.escapeWithSpaces(node.bsm.getOwner())
+ UnicodeUtils.escapeWithSpaces(node.bsm.getOwner()) + "\n\t\t\t\tdesc: " + "\n\t\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(node.bsm.getDesc()) + "\n\t\t\t\tisInterface: " + node.bsm.isInterface()
+ UnicodeUtils.escapeWithSpaces(node.bsm.getDesc()) + "\n\t\t\t\tisInterface: " + node.bsm.isInterface()
+ "\n\t\t\t\ttag: " + node.bsm.getTag() + "\n\t\t\t]\n\t\t\targs: [\n"; + "\n\t\t\t\ttag: " + node.bsm.getTag() + "\n\t\t\t]\n\t\t\targs: [\n";
for (Object l : node.bsmArgs) { for (Object l : node.bsmArgs) {
if (l.getClass().getName().equals("org.objectweb.asm.Type")) { if (l.getClass().getName().equals("org.objectweb.asm.Type")) {
Type type = (Type) l; Type type = (Type) l;
int off = 0; int valueBegin = 0;
int len = 0; int valueEnd = 0;
char[] buf = null; String buf = null;
try { try {
Field f_off = Type.class.getDeclaredField("off"); Field f_off = Type.class.getDeclaredField("valueBegin");
f_off.setAccessible(true); f_off.setAccessible(true);
off = f_off.getInt(type); valueBegin = f_off.getInt(type);
Field f_len = Type.class.getDeclaredField("len"); Field f_len = Type.class.getDeclaredField("valueEnd");
f_len.setAccessible(true); f_len.setAccessible(true);
len = f_len.getInt(type); valueEnd = f_len.getInt(type);
Field f_buf = Type.class.getDeclaredField("buf"); Field f_buf = Type.class.getDeclaredField("valueBuffer");
f_buf.setAccessible(true); f_buf.setAccessible(true);
buf = (char[]) f_buf.get(type); buf = (String) f_buf.get(type);
} catch (Exception e) {} } catch (Exception e) {}
s += "\t\t\t\tType: [\n\t\t\t\t\ttype: " + ClassUtil.getClassNameFromType(type) + "\n\t\t\t\t\toff: " + off 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\tlen: " + len + "\n\t\t\t\t\tbuf: " + ClassUtil.getDecompiledValue(new String(buf), ""); + "\n\t\t\t\t\tend: " + valueEnd + "\n\t\t\t\t\tbuf: " + ClassUtil.getDecompiledValue(buf, "");
s += "\n\t\t\t\t]\n"; s += "\n\t\t\t\t]\n";
} else if (l.getClass().getName().equals("org.objectweb.asm.Handle")) { } else if (l.getClass().getName().equals("org.objectweb.asm.Handle")) {
Handle h = (Handle) l; Handle h = (Handle) l;
s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: " + UnicodeUtils.escapeWithSpaces(h.getName()) + "\n\t\t\t\t\towner: " s += "\t\t\t\tHandle: [\n\t\t\t\t\tname: " + UnicodeUtils.escapeWithSpaces(h.getName()) + "\n\t\t\t\t\towner: "
+ UnicodeUtils.escapeWithSpaces(h.getOwner()) + "\n\t\t\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(h.getOwner()) + "\n\t\t\t\t\tdesc: " + UnicodeUtils.escapeWithSpaces(h.getDesc())
+ UnicodeUtils.escapeWithSpaces(h.getDesc()) + "\n\t\t\t\t\tisInterface: " + h.isInterface() + "\n\t\t\t\t\tisInterface: " + h.isInterface() + "\n\t\t\t\t\ttag: " + h.getTag();
+ "\n\t\t\t\t\ttag: " + h.getTag();
s += "\n\t\t\t\t]\n"; s += "\n\t\t\t\t]\n";
} else { } else {
s += "\t\t\t\t" + ClassUtil.getDecompiledValue(l, "") + "\n"; s += "\t\t\t\t" + ClassUtil.getDecompiledValue(l, "") + "\n";
@@ -478,8 +477,8 @@ public class Disassembler {
} }
case "MultiANewArrayInsnNode": { case "MultiANewArrayInsnNode": {
MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n; MultiANewArrayInsnNode node = (MultiANewArrayInsnNode) n;
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + UnicodeUtils.escapeWithSpaces(node.desc) + " " return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + UnicodeUtils.escapeWithSpaces(node.desc) + " " + node.dims
+ node.dims + "\n"; + "\n";
} }
case "LdcInsnNode": { case "LdcInsnNode": {
LdcInsnNode node = (LdcInsnNode) n; LdcInsnNode node = (LdcInsnNode) n;
@@ -489,28 +488,27 @@ public class Disassembler {
case "Integer": case "Integer":
case "Float": case "Float":
case "Long": { case "Long": {
return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + ClassUtil.getDecompiledValue(node.cst, "") return "\t\t" + OpcodesReverse.reverseOpcode(node.getOpcode()) + " " + ClassUtil.getDecompiledValue(node.cst, "") + "\n";
+ "\n";
} }
case "Type": { case "Type": {
Type type = (Type) node.cst; Type type = (Type) node.cst;
int off = 0; int valueBegin = 0;
int len = 0; int valueEnd = 0;
char[] buf = null; String buf = null;
try { try {
Field f_off = Type.class.getDeclaredField("off"); Field f_off = Type.class.getDeclaredField("valueBegin");
f_off.setAccessible(true); f_off.setAccessible(true);
off = f_off.getInt(type); valueBegin = f_off.getInt(type);
Field f_len = Type.class.getDeclaredField("len"); Field f_len = Type.class.getDeclaredField("valueEnd");
f_len.setAccessible(true); f_len.setAccessible(true);
len = f_len.getInt(type); valueEnd = f_len.getInt(type);
Field f_buf = Type.class.getDeclaredField("buf"); Field f_buf = Type.class.getDeclaredField("valueBuffer");
f_buf.setAccessible(true); f_buf.setAccessible(true);
buf = (char[]) f_buf.get(type); buf = (String) f_buf.get(type);
} catch (Exception e) {} } catch (Exception e) {}
String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " Type: [\n\t\t\ttype: " String s = "\t\t" + OpcodesReverse.reverseOpcode(n.getOpcode()) + " Type: [\n\t\t\ttype: "
+ ClassUtil.getClassNameFromType(type) + "\n\t\t\toff: " + off + "\n\t\t\tlen: " + len + "\n\t\t\tbuf: " + ClassUtil.getClassNameFromType(type) + "\n\t\t\tstart: " + valueBegin + "\n\t\t\tend: " + valueEnd + "\n\t\t\tbuf: "
+ ClassUtil.getDecompiledValue(new String(buf), ""); + ClassUtil.getDecompiledValue(buf, "");
s += "\n\t\t]\n"; s += "\n\t\t]\n";
return s; return s;
} }
@@ -572,5 +570,5 @@ public class Disassembler {
return "\t\tNOT HANDLED! " + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + n.getClass().getSimpleName() + "\n"; return "\t\tNOT HANDLED! " + OpcodesReverse.reverseOpcode(n.getOpcode()) + " " + n.getClass().getSimpleName() + "\n";
} }
} }
} }
+38 -41
View File
@@ -81,7 +81,7 @@ import me.ByteEdit.utils.OpcodesReverse;
import me.ByteEdit.utils.UnicodeUtils; import me.ByteEdit.utils.UnicodeUtils;
public class Main extends JFrame { public class Main extends JFrame {
public static Main INSTANCE; public static Main INSTANCE;
private JPanel contentPane; private JPanel contentPane;
private boolean isChangingFile; private boolean isChangingFile;
@@ -97,13 +97,13 @@ public class Main extends JFrame {
public static JTree tree; public static JTree tree;
public static RTextScrollPane scrollPane_ByteEdit; public static RTextScrollPane scrollPane_ByteEdit;
public static Theme theme; public static Theme theme;
/** /**
* Launch the application. * Launch the application.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() { EventQueue.invokeLater(new Runnable() {
public void run() { public void run() {
try { try {
Main frame = new Main(); Main frame = new Main();
@@ -114,7 +114,7 @@ public class Main extends JFrame {
} }
}); });
} }
/** /**
* Create a simple provider that adds some Java-related completions. * Create a simple provider that adds some Java-related completions.
*/ */
@@ -159,7 +159,7 @@ public class Main extends JFrame {
"<html><b><u>method</u></b><br>Creates an example method</html>")); "<html><b><u>method</u></b><br>Creates an example method</html>"));
return provider; return provider;
} }
/** /**
* Create the frame. * Create the frame.
*/ */
@@ -200,7 +200,7 @@ public class Main extends JFrame {
} }
tree = new JTree(new DefaultTreeModel(null)); tree = new JTree(new DefaultTreeModel(null));
tree.addKeyListener(new KeyAdapter() { tree.addKeyListener(new KeyAdapter() {
@Override @Override
public void keyReleased(KeyEvent e) { public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 116 && jarFile != null) { if (e.getKeyCode() == 116 && jarFile != null) {
@@ -230,7 +230,7 @@ public class Main extends JFrame {
} }
scrollPane.setViewportView(tree); scrollPane.setViewportView(tree);
tree.addTreeSelectionListener(new TreeSelectionListener() { tree.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) { public void valueChanged(TreeSelectionEvent e) {
if (!isChangingFile) { if (!isChangingFile) {
String s = ""; String s = "";
@@ -248,7 +248,7 @@ public class Main extends JFrame {
} }
}); });
new DropTarget(tree, new DropTargetListener() { new DropTarget(tree, new DropTargetListener() {
@Override @Override
public void drop(DropTargetDropEvent dtde) { public void drop(DropTargetDropEvent dtde) {
try { try {
@@ -279,25 +279,25 @@ public class Main extends JFrame {
} }
dtde.rejectDrop(); dtde.rejectDrop();
} }
@Override @Override
public void dragEnter(DropTargetDragEvent dtde) {} public void dragEnter(DropTargetDragEvent dtde) {}
@Override @Override
public void dragOver(DropTargetDragEvent dtde) {} public void dragOver(DropTargetDragEvent dtde) {}
@Override @Override
public void dropActionChanged(DropTargetDragEvent dtde) {} public void dropActionChanged(DropTargetDragEvent dtde) {}
@Override @Override
public void dragExit(DropTargetEvent dte) {} public void dragExit(DropTargetEvent dte) {}
}); });
txtByteEditView = new RSyntaxTextArea(); txtByteEditView = new RSyntaxTextArea();
ac.install(txtByteEditView); ac.install(txtByteEditView);
txtByteEditView.setCodeFoldingEnabled(true); txtByteEditView.setCodeFoldingEnabled(true);
txtByteEditView.addKeyListener(new KeyAdapter() { txtByteEditView.addKeyListener(new KeyAdapter() {
@Override @Override
public void keyReleased(KeyEvent e) { public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == 116 && currentNodeName != null) { if (e.getKeyCode() == 116 && currentNodeName != null) {
@@ -328,7 +328,7 @@ public class Main extends JFrame {
KeyStroke ctrlU = KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK); KeyStroke ctrlU = KeyStroke.getKeyStroke(KeyEvent.VK_U, KeyEvent.CTRL_DOWN_MASK);
// global // global
txtByteEditView.registerKeyboardAction(new ActionListener() { txtByteEditView.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
searchBox.setVisible(true); searchBox.setVisible(true);
@@ -337,7 +337,7 @@ public class Main extends JFrame {
} }
}, ctrlF, JComponent.WHEN_IN_FOCUSED_WINDOW); }, ctrlF, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtByteEditView.registerKeyboardAction(new ActionListener() { txtByteEditView.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
typeOpenBox.setVisible(true); typeOpenBox.setVisible(true);
@@ -346,14 +346,14 @@ public class Main extends JFrame {
} }
}, ctrlT, JComponent.WHEN_IN_FOCUSED_WINDOW); }, ctrlT, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtByteEditView.registerKeyboardAction(new ActionListener() { txtByteEditView.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
optionBox.setVisible(true); optionBox.setVisible(true);
} }
}, ctrlO, JComponent.WHEN_IN_FOCUSED_WINDOW); }, ctrlO, JComponent.WHEN_IN_FOCUSED_WINDOW);
txtByteEditView.registerKeyboardAction(new ActionListener() { txtByteEditView.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
unicodeBox.setVisible(true); unicodeBox.setVisible(true);
@@ -361,21 +361,21 @@ public class Main extends JFrame {
}, ctrlU, JComponent.WHEN_IN_FOCUSED_WINDOW); }, ctrlU, JComponent.WHEN_IN_FOCUSED_WINDOW);
// specific // specific
tree.registerKeyboardAction(new ActionListener() { tree.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
save(); save();
} }
}, ctrlS, JComponent.WHEN_FOCUSED); }, ctrlS, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(new ActionListener() { txtByteEditView.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
saveCurrentClassNode(); saveCurrentClassNode();
} }
}, ctrlS, JComponent.WHEN_FOCUSED); }, ctrlS, JComponent.WHEN_FOCUSED);
txtByteEditView.registerKeyboardAction(new ActionListener() { txtByteEditView.registerKeyboardAction(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
try { try {
@@ -395,7 +395,7 @@ public class Main extends JFrame {
} catch (IOException e2) { } catch (IOException e2) {
e2.printStackTrace(); e2.printStackTrace();
} }
splitPane.setRightComponent(scrollPane_ByteEdit); splitPane.setRightComponent(scrollPane_ByteEdit);
txtByteEditView.setBackground(Color.LIGHT_GRAY); txtByteEditView.setBackground(Color.LIGHT_GRAY);
scrollPane_ByteEdit.setViewportView(txtByteEditView); scrollPane_ByteEdit.setViewportView(txtByteEditView);
@@ -403,9 +403,9 @@ public class Main extends JFrame {
scrollPane_ByteEdit.setFoldIndicatorEnabled(true); scrollPane_ByteEdit.setFoldIndicatorEnabled(true);
scrollPane_ByteEdit.getGutter().setBackground(Color.LIGHT_GRAY); scrollPane_ByteEdit.getGutter().setBackground(Color.LIGHT_GRAY);
} }
private final Pattern jumpableInstructionPattern = Pattern.compile("^\t\t(?!//|\t+).+ .+"); private final Pattern jumpableInstructionPattern = Pattern.compile("^\t\t(?!//|\t+).+ .+");
public void goToSelected() throws BadLocationException { public void goToSelected() throws BadLocationException {
int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine(); int lineStart = txtByteEditView.getLineStartOffsetOfCurrentLine();
int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1; int lineEnd = txtByteEditView.getLineEndOffsetOfCurrentLine() - 1;
@@ -447,7 +447,7 @@ public class Main extends JFrame {
selectFile(className + ".class"); selectFile(className + ".class");
} }
} }
private void saveCurrentClassNode() { private void saveCurrentClassNode() {
String txt = txtByteEditView.getText(); String txt = txtByteEditView.getText();
for (String s : txt.split("\\/\\/ #Annotations\n")) { for (String s : txt.split("\\/\\/ #Annotations\n")) {
@@ -462,7 +462,7 @@ public class Main extends JFrame {
} }
} }
} }
public static int selectFileWithSearch(String s, MethodNode methodToFind) { public static int selectFileWithSearch(String s, MethodNode methodToFind) {
if (s.endsWith(".class")) { if (s.endsWith(".class")) {
currentNodeName = s; currentNodeName = s;
@@ -493,16 +493,16 @@ public class Main extends JFrame {
} }
return -1; return -1;
} }
public static int selectFile(String s) { public static int selectFile(String s) {
return selectFileWithSearch(s, null); return selectFileWithSearch(s, null);
} }
public static File saveFolder = null; public static File saveFolder = null;
public void save() { public void save() {
final JFileChooser fileChooser = new JFileChooser(saveFolder) { final JFileChooser fileChooser = new JFileChooser(saveFolder) {
@Override @Override
protected JDialog createDialog(final Component parent) throws HeadlessException { protected JDialog createDialog(final Component parent) throws HeadlessException {
final JDialog dialog = super.createDialog(parent); final JDialog dialog = super.createDialog(parent);
@@ -529,7 +529,7 @@ public class Main extends JFrame {
} }
} }
} }
public void save(File jar, Collection<ClassNode> classes) { public void save(File jar, Collection<ClassNode> classes) {
try { try {
final JarOutputStream output = new JarOutputStream(new FileOutputStream(jar)); final JarOutputStream output = new JarOutputStream(new FileOutputStream(jar));
@@ -586,7 +586,7 @@ public class Main extends JFrame {
showError(e); showError(e);
} }
} }
public static void showError(Throwable e) { public static void showError(Throwable e) {
String s = e.toString() + "\n"; String s = e.toString() + "\n";
StackTraceElement[] stackTrace = e.getStackTrace(); StackTraceElement[] stackTrace = e.getStackTrace();
@@ -596,19 +596,16 @@ public class Main extends JFrame {
s += "\tat " + ste + "\n"; s += "\tat " + ste + "\n";
} }
} }
public static void showError(String s) { public static void showError(String s) {
JOptionPane.showMessageDialog(INSTANCE, s, "Error!", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(INSTANCE, s, "Error!", JOptionPane.ERROR_MESSAGE);
} }
class ArchiveTreeModel extends DefaultTreeModel { class ArchiveTreeModel extends DefaultTreeModel {
public ArchiveTreeModel(JarFile jar) { public ArchiveTreeModel(JarFile jar) {
super(new DefaultMutableTreeNode( super(new DefaultMutableTreeNode(jar.getName().split(File.separator.equals("\\") ? "\\\\"
jar.getName() : File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length - 1]));
.split(File.separator.equals("\\") ? "\\\\"
: File.separator)[jar.getName().split(File.separator.equals("\\") ? "\\\\" : File.separator).length
- 1]));
try { try {
ArrayList<String> paths = new ArrayList<>(); ArrayList<String> paths = new ArrayList<>();
classNodes.clear(); classNodes.clear();
@@ -662,7 +659,7 @@ public class Main extends JFrame {
showError(e); showError(e);
} }
} }
private DefaultMutableTreeNode findNode(DefaultMutableTreeNode parent, String name) { private DefaultMutableTreeNode findNode(DefaultMutableTreeNode parent, String name) {
Enumeration<?> e = parent.children(); Enumeration<?> e = parent.children();
while (e.hasMoreElements()) { while (e.hasMoreElements()) {
+68 -93
View File
@@ -25,8 +25,8 @@ import java.io.Reader;
* <li>UTF-32LE * <li>UTF-32LE
* <li>UTF-32BE * <li>UTF-32BE
* </ul> * </ul>
* If the stream is not found to be any of the above, then a default encoding * If the stream is not found to be any of the above, then a default encoding is
* is used for reading. The user can specify this default encoding, or a system * used for reading. The user can specify this default encoding, or a system
* default will be used. * default will be used.
* <p> * <p>
* *
@@ -40,121 +40,101 @@ import java.io.Reader;
* @author Robert Futrell * @author Robert Futrell
* @version 0.9 * @version 0.9
*/ */
@SuppressWarnings({ "checkstyle:magicnumber" })
public class UnicodeReader extends Reader { public class UnicodeReader extends Reader {
/** /**
* The input stream from which we're really reading. * The input stream from which we're really reading.
*/ */
private InputStreamReader internalIn = null; private InputStreamReader internalIn = null;
/** /**
* The encoding being used. We keep our own instead of using the string * The encoding being used. We keep our own instead of using the string returned
* returned by <code>java.io.InputStreamReader</code> since that class * by <code>java.io.InputStreamReader</code> since that class does not return
* does not return user-friendly names. * user-friendly names.
*/ */
private String encoding; private String encoding;
/** /**
* The size of a BOM. * The size of a BOM.
*/ */
private static final int BOM_SIZE = 4; private static final int BOM_SIZE = 4;
/** /**
* This utility constructor is here because you will usually use a * This utility constructor is here because you will usually use a
* <code>UnicodeReader</code> on files. * <code>UnicodeReader</code> on files.
* <p> * <p>
* Creates a reader using the encoding specified by the BOM in the file; * Creates a reader using the encoding specified by the BOM in the file; if
* if there is no recognized BOM, then a system default encoding is used. * there is no recognized BOM, then a system default encoding is used.
* *
* @param file * @param file The file from which you want to read.
* The file from which you want to read. * @throws IOException If an error occurs when checking for/reading the
* @throws IOException * BOM.
* If an error occurs when checking for/reading the * @throws SecurityException If a security manager exists and its checkRead
* BOM. * method denies read access to the file.
* @throws SecurityException
* If a security manager exists and its
* checkRead method denies read access to the file.
*/ */
public UnicodeReader(String file) throws IOException { public UnicodeReader(String file) throws IOException {
this(new File(file)); this(new File(file));
} }
/** /**
* This utility constructor is here because you will usually use a * This utility constructor is here because you will usually use a
* <code>UnicodeReader</code> on files. * <code>UnicodeReader</code> on files.
* <p> * <p>
* Creates a reader using the encoding specified by the BOM in the file; * Creates a reader using the encoding specified by the BOM in the file; if
* if there is no recognized BOM, then a system default encoding is used. * there is no recognized BOM, then a system default encoding is used.
* *
* @param file * @param file The file from which you want to read.
* The file from which you want to read. * @throws IOException If an error occurs when checking for/reading the
* @throws IOException * BOM.
* If an error occurs when checking for/reading the * @throws SecurityException If a security manager exists and its checkRead
* BOM. * method denies read access to the file.
* @throws SecurityException
* If a security manager exists and its
* checkRead method denies read access to the file.
*/ */
public UnicodeReader(File file) throws IOException { public UnicodeReader(File file) throws IOException {
this(new FileInputStream(file)); this(new FileInputStream(file));
} }
/** /**
* This utility constructor is here because you will usually use a * This utility constructor is here because you will usually use a
* <code>UnicodeReader</code> on files. * <code>UnicodeReader</code> on files.
* <p> * <p>
* Creates a reader using the encoding specified by the BOM in the file; * Creates a reader using the encoding specified by the BOM in the file; if
* if there is no recognized BOM, then a specified default encoding is * there is no recognized BOM, then a specified default encoding is used.
* used.
* *
* @param file * @param file The file from which you want to read.
* The file from which you want to read. * @param defaultEncoding The encoding to use if no BOM is found. If this value
* @param defaultEncoding * is <code>null</code>, a system default is used.
* The encoding to use if no BOM is found. If * @throws IOException If an error occurs when checking for/reading the
* this value is <code>null</code>, a system default is used. * BOM.
* @throws IOException * @throws SecurityException If a security manager exists and its checkRead
* If an error occurs when checking for/reading the * method denies read access to the file.
* BOM.
* @throws SecurityException
* If a security manager exists and its
* checkRead method denies read access to the file.
*/ */
public UnicodeReader(File file, String defaultEncoding) throws IOException { public UnicodeReader(File file, String defaultEncoding) throws IOException {
this(new FileInputStream(file), defaultEncoding); this(new FileInputStream(file), defaultEncoding);
} }
/** /**
* Creates a reader using the encoding specified by the BOM in the file; * Creates a reader using the encoding specified by the BOM in the file; if
* if there is no recognized BOM, then a system default encoding is used. * there is no recognized BOM, then a system default encoding is used.
* *
* @param in * @param in The input stream from which to read.
* The input stream from which to read. * @throws IOException If an error occurs when checking for/reading the BOM.
* @throws IOException
* If an error occurs when checking for/reading the
* BOM.
*/ */
public UnicodeReader(InputStream in) throws IOException { public UnicodeReader(InputStream in) throws IOException {
this(in, null); this(in, null);
} }
/** /**
* Creates a reader using the encoding specified by the BOM in the file; * Creates a reader using the encoding specified by the BOM in the file; if
* if there is no recognized BOM, then <code>defaultEncoding</code> is * there is no recognized BOM, then <code>defaultEncoding</code> is used.
* used.
* *
* @param in * @param in The input stream from which to read.
* The input stream from which to read. * @param defaultEncoding The encoding to use if no recognized BOM is found. If
* @param defaultEncoding * this value is <code>null</code>, a system default is
* The encoding to use if no recognized BOM is * used.
* found. If this value is <code>null</code>, a system default * @throws IOException If an error occurs when checking for/reading the BOM.
* is used.
* @throws IOException
* If an error occurs when checking for/reading the
* BOM.
*/ */
public UnicodeReader(InputStream in, String defaultEncoding) throws IOException { public UnicodeReader(InputStream in, String defaultEncoding) throws IOException {
init(in, defaultEncoding); init(in, defaultEncoding);
} }
/** /**
* Closes this reader. * Closes this reader.
*/ */
@@ -162,29 +142,27 @@ public class UnicodeReader extends Reader {
public void close() throws IOException { public void close() throws IOException {
internalIn.close(); internalIn.close();
} }
/** /**
* Returns the encoding being used to read this input stream (i.e., the * Returns the encoding being used to read this input stream (i.e., the encoding
* encoding of the file). If a BOM was recognized, then the specific * of the file). If a BOM was recognized, then the specific Unicode type is
* Unicode type is returned; otherwise, either the default encoding passed * returned; otherwise, either the default encoding passed into the constructor
* into the constructor or the system default is returned. * or the system default is returned.
* *
* @return The encoding of the stream. * @return The encoding of the stream.
*/ */
public String getEncoding() { public String getEncoding() {
return encoding; return encoding;
} }
/** /**
* Read-ahead four bytes and check for BOM marks. Extra bytes are * Read-ahead four bytes and check for BOM marks. Extra bytes are unread back to
* unread back to the stream, only BOM bytes are skipped. * the stream, only BOM bytes are skipped.
* *
* @param defaultEncoding * @param defaultEncoding The encoding to use if no BOM was recognized. If this
* The encoding to use if no BOM was recognized. If * value is <code>null</code>, then a system default is
* this value is <code>null</code>, then a system default is * used.
* used. * @throws IOException If an error occurs when trying to read a BOM.
* @throws IOException
* If an error occurs when trying to read a BOM.
*/ */
protected void init(InputStream in, String defaultEncoding) throws IOException { protected void init(InputStream in, String defaultEncoding) throws IOException {
PushbackInputStream tempIn = new PushbackInputStream(in, BOM_SIZE); PushbackInputStream tempIn = new PushbackInputStream(in, BOM_SIZE);
@@ -196,7 +174,8 @@ public class UnicodeReader extends Reader {
unread = n - 4; unread = n - 4;
} else if (n == BOM_SIZE && // Last 2 bytes are 0; could be an empty } else if (n == BOM_SIZE && // Last 2 bytes are 0; could be an empty
// UTF-16 // UTF-16
(bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) { (bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00)
&& (bom[3] == (byte) 0x00)) {
encoding = "UTF-32LE"; encoding = "UTF-32LE";
unread = n - 4; unread = n - 4;
} else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) { } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
@@ -226,21 +205,17 @@ public class UnicodeReader extends Reader {
internalIn = new InputStreamReader(tempIn, encoding); internalIn = new InputStreamReader(tempIn, encoding);
} }
} }
/** /**
* Read characters into a portion of an array. This method will block until * Read characters into a portion of an array. This method will block until some
* some input is available, an I/O error occurs, or the end of the stream * input is available, an I/O error occurs, or the end of the stream is reached.
* is reached.
* *
* @param cbuf * @param cbuf The buffer into which to read.
* The buffer into which to read. * @param off The offset at which to start storing characters.
* @param off * @param len The maximum number of characters to read.
* The offset at which to start storing characters.
* @param len
* The maximum number of characters to read.
* *
* @return The number of characters read, or <code>-1</code> if the end * @return The number of characters read, or <code>-1</code> if the end of the
* of the stream has been reached. * stream has been reached.
*/ */
@Override @Override
public int read(char[] cbuf, int off, int len) throws IOException { public int read(char[] cbuf, int off, int len) throws IOException {
@@ -15,26 +15,26 @@ import java.util.List;
import javax.swing.text.JTextComponent; import javax.swing.text.JTextComponent;
/** /**
* A base class for completion providers. {@link Completion}s are kept in * A base class for completion providers. {@link Completion}s are kept in a
* a sorted list. To get the list of completions that match a given input, * sorted list. To get the list of completions that match a given input, a
* a binary search is done to find the first matching completion, then all * binary search is done to find the first matching completion, then all
* succeeding completions that also match are also returned. * succeeding completions that also match are also returned.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public abstract class AbstractCompletionProvider extends CompletionProviderBase { public abstract class AbstractCompletionProvider extends CompletionProviderBase {
/** /**
* The completions this provider is aware of. Subclasses should ensure * The completions this provider is aware of. Subclasses should ensure that this
* that this list is sorted alphabetically (case-insensitively). * list is sorted alphabetically (case-insensitively).
*/ */
protected List<Completion> completions; protected List<Completion> completions;
/** /**
* Compares a {@link Completion} against a String. * Compares a {@link Completion} against a String.
*/ */
protected CaseInsensitiveComparator comparator; protected CaseInsensitiveComparator comparator;
/** /**
* Constructor. * Constructor.
*/ */
@@ -43,17 +43,15 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
clearParameterizedCompletionParams(); clearParameterizedCompletionParams();
completions = new ArrayList<Completion>(); completions = new ArrayList<Completion>();
} }
/** /**
* Adds a single completion to this provider. If you are adding multiple * Adds a single completion to this provider. If you are adding multiple
* completions to this provider, for efficiency reasons please consider * completions to this provider, for efficiency reasons please consider using
* using {@link #addCompletions(List)} instead. * {@link #addCompletions(List)} instead.
* *
* @param c * @param c The completion to add.
* The completion to add. * @throws IllegalArgumentException If the completion's provider isn't this
* @throws IllegalArgumentException * <tt>CompletionProvider</tt>.
* If the completion's provider isn't
* this <tt>CompletionProvider</tt>.
* @see #addCompletions(List) * @see #addCompletions(List)
* @see #removeCompletion(Completion) * @see #removeCompletion(Completion)
* @see #clear() * @see #clear()
@@ -62,16 +60,13 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
checkProviderAndAdd(c); checkProviderAndAdd(c);
Collections.sort(completions); Collections.sort(completions);
} }
/** /**
* Adds {@link Completion}s to this provider. * Adds {@link Completion}s to this provider.
* *
* @param completions * @param completions The completions to add. This cannot be <code>null</code>.
* The completions to add. This cannot be * @throws IllegalArgumentException If a completion's provider isn't this
* <code>null</code>. * <tt>CompletionProvider</tt>.
* @throws IllegalArgumentException
* If a completion's provider isn't
* this <tt>CompletionProvider</tt>.
* @see #addCompletion(Completion) * @see #addCompletion(Completion)
* @see #removeCompletion(Completion) * @see #removeCompletion(Completion)
* @see #clear() * @see #clear()
@@ -83,12 +78,11 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
} }
Collections.sort(this.completions); Collections.sort(this.completions);
} }
/** /**
* Adds simple completions for a list of words. * Adds simple completions for a list of words.
* *
* @param words * @param words The words.
* The words.
* @see BasicCompletion * @see BasicCompletion
*/ */
protected void addWordCompletions(String[] words) { protected void addWordCompletions(String[] words) {
@@ -98,17 +92,17 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
} }
Collections.sort(completions); Collections.sort(completions);
} }
protected void checkProviderAndAdd(Completion c) { protected void checkProviderAndAdd(Completion c) {
if (c.getProvider() != this) { if (c.getProvider() != this) {
throw new IllegalArgumentException("Invalid CompletionProvider"); throw new IllegalArgumentException("Invalid CompletionProvider");
} }
completions.add(c); completions.add(c);
} }
/** /**
* Removes all completions from this provider. This does not affect * Removes all completions from this provider. This does not affect the parent
* the parent <tt>CompletionProvider</tt>, if there is one. * <tt>CompletionProvider</tt>, if there is one.
* *
* @see #addCompletion(Completion) * @see #addCompletion(Completion)
* @see #addCompletions(List) * @see #addCompletions(List)
@@ -117,17 +111,15 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
public void clear() { public void clear() {
completions.clear(); completions.clear();
} }
/** /**
* Returns a list of <tt>Completion</tt>s in this provider with the * Returns a list of <tt>Completion</tt>s in this provider with the specified
* specified input text. * input text.
* *
* @param inputText * @param inputText The input text to search for.
* The input text to search for. * @return A list of {@link Completion}s, or <code>null</code> if there are no
* @return A list of {@link Completion}s, or <code>null</code> if there * matching <tt>Completion</tt>s.
* are no matching <tt>Completion</tt>s.
*/ */
@SuppressWarnings("unchecked")
public List<Completion> getCompletionByInputText(String inputText) { public List<Completion> getCompletionByInputText(String inputText) {
// Find any entry that matches this input text (there may be > 1). // Find any entry that matches this input text (there may be > 1).
int end = Collections.binarySearch(completions, inputText, comparator); int end = Collections.binarySearch(completions, inputText, comparator);
@@ -140,15 +132,15 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
start--; start--;
} }
int count = completions.size(); int count = completions.size();
while (++end < count && comparator.compare(completions.get(end), inputText) == 0); while (++end < count && comparator.compare(completions.get(end), inputText) == 0)
;
return completions.subList(start, end); // (inclusive, exclusive) return completions.subList(start, end); // (inclusive, exclusive)
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
@SuppressWarnings("unchecked")
protected List<Completion> getCompletionsImpl(JTextComponent comp) { protected List<Completion> getCompletionsImpl(JTextComponent comp) {
List<Completion> retVal = new ArrayList<Completion>(); List<Completion> retVal = new ArrayList<Completion>();
String text = getAlreadyEnteredText(comp); String text = getAlreadyEnteredText(comp);
@@ -188,13 +180,12 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
} }
return retVal; return retVal;
} }
/** /**
* Removes the specified completion from this provider. This method * Removes the specified completion from this provider. This method will not
* will not remove completions from the parent provider, if there is one. * remove completions from the parent provider, if there is one.
* *
* @param c * @param c The completion to remove.
* The completion to remove.
* @return <code>true</code> if this provider contained the specified * @return <code>true</code> if this provider contained the specified
* completion. * completion.
* @see #clear() * @see #clear()
@@ -210,14 +201,13 @@ public abstract class AbstractCompletionProvider extends CompletionProviderBase
completions.remove(index); completions.remove(index);
return true; return true;
} }
/** /**
* A comparator that compares the input text of a {@link Completion} * A comparator that compares the input text of a {@link Completion} against a
* against a String lexicographically, ignoring case. * String lexicographically, ignoring case.
*/ */
@SuppressWarnings("rawtypes")
public static class CaseInsensitiveComparator implements Comparator, Serializable { public static class CaseInsensitiveComparator implements Comparator, Serializable {
@Override @Override
public int compare(Object o1, Object o2) { public int compare(Object o1, Object o2) {
String s1 = o1 instanceof String ? (String) o1 : ((Completion) o1).getInputText(); String s1 = o1 instanceof String ? (String) o1 : ((Completion) o1).getInputText();
@@ -32,31 +32,30 @@ import org.fife.ui.rsyntaxtextarea.templates.CodeTemplate;
* Manages "code templates." * Manages "code templates."
* <p> * <p>
* *
* All methods in this class are synchronized for thread safety, but as a * All methods in this class are synchronized for thread safety, but as a best
* best practice, you should probably only modify the templates known to a * practice, you should probably only modify the templates known to a
* <code>CodeTemplateManager</code> on the EDT. Modifying a * <code>CodeTemplateManager</code> on the EDT. Modifying a
* <code>CodeTemplate</code> retrieved from a <code>CodeTemplateManager</code> * <code>CodeTemplate</code> retrieved from a <code>CodeTemplateManager</code>
* while <em>not</em> on the EDT could cause problems. * while <em>not</em> on the EDT could cause problems.
* <p> * <p>
* *
* For more flexible boilerplate code insertion, consider using the * For more flexible boilerplate code insertion, consider using the <a href=
* <a href=
* "http://javadoc.fifesoft.com/autocomplete/org/fife/ui/autocomplete/TemplateCompletion.html"> * "http://javadoc.fifesoft.com/autocomplete/org/fife/ui/autocomplete/TemplateCompletion.html">
* TemplateCompletion class</a> in the * TemplateCompletion class</a> in the
* <a href="https://github.com/bobbylight/AutoComplete">AutoComplete * <a href="https://github.com/bobbylight/AutoComplete">AutoComplete add-on
* add-on library</a>. * library</a>.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
public class CodeTemplateManager { public class CodeTemplateManager {
private int maxTemplateIDLength; private int maxTemplateIDLength;
private List<CodeTemplate> templates; private List<CodeTemplate> templates;
private Segment s; private Segment s;
private TemplateComparator comparator; private TemplateComparator comparator;
private File directory; private File directory;
/** /**
* Constructor. * Constructor.
*/ */
@@ -65,15 +64,13 @@ public class CodeTemplateManager {
comparator = new TemplateComparator(); comparator = new TemplateComparator();
templates = new ArrayList<CodeTemplate>(); templates = new ArrayList<CodeTemplate>();
} }
/** /**
* Registers the specified template with this template manager. * Registers the specified template with this template manager.
* *
* @param template * @param template The template to register.
* The template to register. * @throws IllegalArgumentException If <code>template</code> is
* @throws IllegalArgumentException * <code>null</code>.
* If <code>template</code> is
* <code>null</code>.
* @see #removeTemplate(CodeTemplate) * @see #removeTemplate(CodeTemplate)
* @see #removeTemplate(String) * @see #removeTemplate(String)
*/ */
@@ -84,13 +81,12 @@ public class CodeTemplateManager {
templates.add(template); templates.add(template);
sortTemplates(); sortTemplates();
} }
/** /**
* Returns the template that should be inserted at the current caret * Returns the template that should be inserted at the current caret position,
* position, assuming the trigger character was pressed. * assuming the trigger character was pressed.
* *
* @param textArea * @param textArea The text area that's getting text inserted into it.
* The text area that's getting text inserted into it.
* @return A template that should be inserted, if appropriate, or * @return A template that should be inserted, if appropriate, or
* <code>null</code> if no template should be inserted. * <code>null</code> if no template should be inserted.
*/ */
@@ -100,7 +96,6 @@ public class CodeTemplateManager {
try { try {
Document doc = textArea.getDocument(); Document doc = textArea.getDocument();
doc.getText(caretPos - charsToGet, charsToGet, s); doc.getText(caretPos - charsToGet, charsToGet, s);
@SuppressWarnings("unchecked")
int index = Collections.binarySearch(templates, s, comparator); int index = Collections.binarySearch(templates, s, comparator);
return index >= 0 ? (CodeTemplate) templates.get(index) : null; return index >= 0 ? (CodeTemplate) templates.get(index) : null;
} catch (BadLocationException ble) { } catch (BadLocationException ble) {
@@ -108,7 +103,7 @@ public class CodeTemplateManager {
throw new InternalError("Error in CodeTemplateManager"); throw new InternalError("Error in CodeTemplateManager");
} }
} }
/** /**
* Returns the number of templates this manager knows about. * Returns the number of templates this manager knows about.
* *
@@ -117,7 +112,7 @@ public class CodeTemplateManager {
public synchronized int getTemplateCount() { public synchronized int getTemplateCount() {
return templates.size(); return templates.size();
} }
/** /**
* Returns the templates currently available. * Returns the templates currently available.
* *
@@ -127,29 +122,26 @@ public class CodeTemplateManager {
CodeTemplate[] temp = new CodeTemplate[templates.size()]; CodeTemplate[] temp = new CodeTemplate[templates.size()];
return templates.toArray(temp); return templates.toArray(temp);
} }
/** /**
* Returns whether the specified character is a valid character for a * Returns whether the specified character is a valid character for a
* <code>CodeTemplate</code> id. * <code>CodeTemplate</code> id.
* *
* @param ch * @param ch The character to check.
* The character to check.
* @return Whether the character is a valid template character. * @return Whether the character is a valid template character.
*/ */
public static final boolean isValidChar(char ch) { public static final boolean isValidChar(char ch) {
return RSyntaxUtilities.isLetterOrDigit(ch) || ch == '_'; return RSyntaxUtilities.isLetterOrDigit(ch) || ch == '_';
} }
/** /**
* Returns the specified code template. * Returns the specified code template.
* *
* @param template * @param template The template to remove.
* The template to remove. * @return <code>true</code> if the template was removed, <code>false</code> if
* @return <code>true</code> if the template was removed, <code>false</code> * the template was not in this template manager.
* if the template was not in this template manager. * @throws IllegalArgumentException If <code>template</code> is
* @throws IllegalArgumentException * <code>null</code>.
* If <code>template</code> is
* <code>null</code>.
* @see #removeTemplate(String) * @see #removeTemplate(String)
* @see #addTemplate(CodeTemplate) * @see #addTemplate(CodeTemplate)
*/ */
@@ -160,16 +152,14 @@ public class CodeTemplateManager {
// TODO: Do a binary search // TODO: Do a binary search
return templates.remove(template); return templates.remove(template);
} }
/** /**
* Returns the code template with the specified id. * Returns the code template with the specified id.
* *
* @param id * @param id The id to check for.
* The id to check for. * @return The code template that was removed, or <code>null</code> if there was
* @return The code template that was removed, or <code>null</code> if * no template with the specified ID.
* there was no template with the specified ID. * @throws IllegalArgumentException If <code>id</code> is <code>null</code>.
* @throws IllegalArgumentException
* If <code>id</code> is <code>null</code>.
* @see #removeTemplate(CodeTemplate) * @see #removeTemplate(CodeTemplate)
* @see #addTemplate(CodeTemplate) * @see #addTemplate(CodeTemplate)
*/ */
@@ -187,14 +177,12 @@ public class CodeTemplateManager {
} }
return null; return null;
} }
/** /**
* Replaces the current set of available templates with the ones * Replaces the current set of available templates with the ones specified.
* specified.
* *
* @param newTemplates * @param newTemplates The new set of templates. Note that we will be taking a
* The new set of templates. Note that we will * shallow copy of these and sorting them.
* be taking a shallow copy of these and sorting them.
*/ */
public synchronized void replaceTemplates(CodeTemplate[] newTemplates) { public synchronized void replaceTemplates(CodeTemplate[] newTemplates) {
templates.clear(); templates.clear();
@@ -205,7 +193,7 @@ public class CodeTemplateManager {
} }
sortTemplates(); // Also recomputes maxTemplateIDLength. sortTemplates(); // Also recomputes maxTemplateIDLength.
} }
/** /**
* Saves all templates as XML files in the current template directory. * Saves all templates as XML files in the current template directory.
* *
@@ -243,14 +231,13 @@ public class CodeTemplateManager {
} }
return wasSuccessful; return wasSuccessful;
} }
/** /**
* Sets the directory in which to look for templates. Calling this * Sets the directory in which to look for templates. Calling this method adds
* method adds any new templates found in the specified directory to * any new templates found in the specified directory to the templates already
* the templates already registered. * registered.
* *
* @param dir * @param dir The new directory in which to look for templates.
* The new directory in which to look for templates.
* @return The new number of templates in this template manager, or * @return The new number of templates in this template manager, or
* <code>-1</code> if the specified directory does not exist. * <code>-1</code> if the specified directory does not exist.
*/ */
@@ -285,11 +272,10 @@ public class CodeTemplateManager {
} }
return -1; return -1;
} }
/** /**
* Removes any null entries in the current set of templates (if * Removes any null entries in the current set of templates (if any), sorts the
* any), sorts the remaining templates, and computes the new * remaining templates, and computes the new maximum template ID length.
* maximum template ID length.
*/ */
private synchronized void sortTemplates() { private synchronized void sortTemplates() {
// Get the maximum length of a template ID. // Get the maximum length of a template ID.
@@ -307,15 +293,14 @@ public class CodeTemplateManager {
} }
Collections.sort(templates); Collections.sort(templates);
} }
/** /**
* A comparator that takes a <code>CodeTemplate</code> as its first * A comparator that takes a <code>CodeTemplate</code> as its first parameter
* parameter and a <code>Segment</code> as its second, and knows * and a <code>Segment</code> as its second, and knows to compare the template's
* to compare the template's ID to the segment's text. * ID to the segment's text.
*/ */
@SuppressWarnings("rawtypes")
private static class TemplateComparator implements Comparator, Serializable { private static class TemplateComparator implements Comparator, Serializable {
@Override @Override
public int compare(Object template, Object segment) { public int compare(Object template, Object segment) {
// Get template start index (0) and length. // Get template start index (0) and length.
@@ -345,12 +330,12 @@ public class CodeTemplateManager {
return len1 - len2; return len1 - len2;
} }
} }
/** /**
* A file filter that accepts only XML files. * A file filter that accepts only XML files.
*/ */
private static class XMLFileFilter implements FileFilter { private static class XMLFileFilter implements FileFilter {
@Override @Override
public boolean accept(File f) { public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".xml"); return f.getName().toLowerCase().endsWith(".xml");
@@ -15,19 +15,18 @@ import javax.swing.KeyStroke;
import org.fife.ui.rtextarea.RTADefaultInputMap; import org.fife.ui.rtextarea.RTADefaultInputMap;
/** /**
* The default input map for an <code>RSyntaxTextArea</code>. * The default input map for an <code>RSyntaxTextArea</code>. Currently, the new
* Currently, the new key bindings include: * key bindings include:
* <ul> * <ul>
* <li>Shift+Tab indents the current line or currently selected lines * <li>Shift+Tab indents the current line or currently selected lines to the
* to the left. * left.
* </ul> * </ul>
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
@SuppressWarnings({ "checkstyle:linelength" })
public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap { public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap {
/** /**
* Constructs the default input map for an <code>RSyntaxTextArea</code>. * Constructs the default input map for an <code>RSyntaxTextArea</code>.
*/ */
@@ -60,18 +59,23 @@ public class RSyntaxTextAreaDefaultInputMap extends RTADefaultInputMap {
// (unfortunately) don't have a ToggleCommentAction for *nix // (unfortunately) don't have a ToggleCommentAction for *nix
// out-of-the-box. // out-of-the-box.
// Applications can add one easily enough if they want one. // Applications can add one easily enough if they want one.
put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod), RSyntaxTextAreaEditorKit.rstaToggleCommentAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_SLASH, defaultMod),
RSyntaxTextAreaEditorKit.rstaToggleCommentAction);
} }
put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod), RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_OPEN_BRACKET, defaultMod),
RSyntaxTextAreaEditorKit.rstaGoToMatchingBracketAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseFoldAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandFoldAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_ADD, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandFoldAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, defaultMod), RSyntaxTextAreaEditorKit.rstaCollapseAllFoldsAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_DIVIDE, defaultMod),
put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, defaultMod), RSyntaxTextAreaEditorKit.rstaExpandAllFoldsAction); RSyntaxTextAreaEditorKit.rstaCollapseAllFoldsAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_MULTIPLY, defaultMod),
RSyntaxTextAreaEditorKit.rstaExpandAllFoldsAction);
// NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second // NOTE: no modifiers => mapped to keyTyped. If we had "0" as a second
// second parameter, we'd get the template action (keyPressed) AND the // second parameter, we'd get the template action (keyPressed) AND the
// default space action (keyTyped). // default space action (keyTyped).
// put(KeyStroke.getKeyStroke(' '), // put(KeyStroke.getKeyStroke(' '),
// RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction); // RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, defaultShift), RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, defaultShift),
RSyntaxTextAreaEditorKit.rstaPossiblyInsertTemplateAction);
} }
} }
File diff suppressed because it is too large. Load diff
+34 -47
View File
@@ -28,9 +28,8 @@ import javax.swing.JPanel;
* @author Robert Futrell * @author Robert Futrell
* @version 0.6 * @version 0.6
*/ */
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public class Style implements Cloneable { public class Style implements Cloneable {
public static final Color DEFAULT_FOREGROUND = Color.BLACK; public static final Color DEFAULT_FOREGROUND = Color.BLACK;
public static final Color DEFAULT_BACKGROUND = null; public static final Color DEFAULT_BACKGROUND = null;
public static final Font DEFAULT_FONT = null; public static final Font DEFAULT_FONT = null;
@@ -39,62 +38,52 @@ public class Style implements Cloneable {
public boolean underline; public boolean underline;
public Font font; public Font font;
public FontMetrics fontMetrics; public FontMetrics fontMetrics;
/** /**
* Creates a new style defaulting to black foreground, no * Creates a new style defaulting to black foreground, no background, and no
* background, and no styling. * styling.
*/ */
public Style() { public Style() {
this(DEFAULT_FOREGROUND); this(DEFAULT_FOREGROUND);
} }
/** /**
* Creates a new style with the specified foreground and no styling. * Creates a new style with the specified foreground and no styling.
* *
* @param fg * @param fg The foreground color to use.
* The foreground color to use.
*/ */
public Style(Color fg) { public Style(Color fg) {
this(fg, DEFAULT_BACKGROUND); this(fg, DEFAULT_BACKGROUND);
} }
/** /**
* Creates a new style with the specified colors and no styling. * Creates a new style with the specified colors and no styling.
* *
* @param fg * @param fg The foreground color to use.
* The foreground color to use. * @param bg The background color to use.
* @param bg
* The background color to use.
*/ */
public Style(Color fg, Color bg) { public Style(Color fg, Color bg) {
this(fg, bg, DEFAULT_FONT); this(fg, bg, DEFAULT_FONT);
} }
/** /**
* Creates a new style. * Creates a new style.
* *
* @param fg * @param fg The foreground color to use.
* The foreground color to use. * @param bg The background color to use.
* @param bg * @param font The font for this syntax scheme.
* The background color to use.
* @param font
* The font for this syntax scheme.
*/ */
public Style(Color fg, Color bg, Font font) { public Style(Color fg, Color bg, Font font) {
this(fg, bg, font, false); this(fg, bg, font, false);
} }
/** /**
* Creates a new style. * Creates a new style.
* *
* @param fg * @param fg The foreground color to use.
* The foreground color to use. * @param bg The background color to use.
* @param bg * @param font The font for this syntax scheme.
* The background color to use. * @param underline Whether or not to underline tokens with this style.
* @param font
* The font for this syntax scheme.
* @param underline
* Whether or not to underline tokens with this style.
*/ */
public Style(Color fg, Color bg, Font font, boolean underline) { public Style(Color fg, Color bg, Font font, boolean underline) {
foreground = fg; foreground = fg;
@@ -106,15 +95,14 @@ public class Style implements Cloneable {
// rendering // rendering
// hints! // hints!
} }
/** /**
* Returns whether or not two (possibly <code>null</code>) objects are * Returns whether or not two (possibly <code>null</code>) objects are equal.
* equal.
*/ */
private boolean areEqual(Object o1, Object o2) { private boolean areEqual(Object o1, Object o2) {
return (o1 == null && o2 == null) || (o1 != null && o1.equals(o2)); return (o1 == null && o2 == null) || (o1 != null && o1.equals(o2));
} }
/** /**
* Returns a deep copy of this object. * Returns a deep copy of this object.
* *
@@ -136,34 +124,32 @@ public class Style implements Cloneable {
clone.fontMetrics = fontMetrics; clone.fontMetrics = fontMetrics;
return clone; return clone;
} }
/** /**
* Returns whether or not two syntax schemes are equal. * Returns whether or not two syntax schemes are equal.
* *
* @param o2 * @param o2 The object with which to compare this syntax scheme.
* The object with which to compare this syntax scheme. * @return Whether or not these two syntax schemes represent the same scheme.
* @return Whether or not these two syntax schemes represent the same
* scheme.
*/ */
@Override @Override
public boolean equals(Object o2) { public boolean equals(Object o2) {
if (o2 instanceof Style) { if (o2 instanceof Style) {
Style ss2 = (Style) o2; Style ss2 = (Style) o2;
if (this.underline == ss2.underline && areEqual(foreground, ss2.foreground) && areEqual(background, ss2.background) if (this.underline == ss2.underline && areEqual(foreground, ss2.foreground)
&& areEqual(font, ss2.font) && areEqual(fontMetrics, ss2.fontMetrics)) { && areEqual(background, ss2.background) && areEqual(font, ss2.font)
&& areEqual(fontMetrics, ss2.fontMetrics)) {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* Computes the hash code to use when adding this syntax scheme to * Computes the hash code to use when adding this syntax scheme to hash tables.
* hash tables.
* <p> * <p>
* *
* This method is implemented, since {@link #equals(Object)} is implemented, * This method is implemented, since {@link #equals(Object)} is implemented, to
* to keep FindBugs happy. * keep FindBugs happy.
* *
* @return The hash code. * @return The hash code.
*/ */
@@ -178,7 +164,7 @@ public class Style implements Cloneable {
} }
return hashCode; return hashCode;
} }
/** /**
* Returns a string representation of this style. * Returns a string representation of this style.
* *
@@ -186,6 +172,7 @@ public class Style implements Cloneable {
*/ */
@Override @Override
public String toString() { public String toString() {
return "[Style: foreground: " + foreground + ", background: " + background + ", underline: " + underline + ", font: " + font + "]"; return "[Style: foreground: " + foreground + ", background: " + background + ", underline: " + underline
+ ", font: " + font + "]";
} }
} }
+116 -145
View File
@@ -24,8 +24,8 @@ import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory; import org.xml.sax.helpers.XMLReaderFactory;
/** /**
* The set of colors and styles used by an <code>RSyntaxTextArea</code> to * The set of colors and styles used by an <code>RSyntaxTextArea</code> to color
* color tokens. * tokens.
* <p> * <p>
* You can use this class to programmatically set the fonts and colors used in * You can use this class to programmatically set the fonts and colors used in
* an RSyntaxTextArea, but for more powerful, externalized control, consider * an RSyntaxTextArea, but for more powerful, externalized control, consider
@@ -35,20 +35,18 @@ import org.xml.sax.helpers.XMLReaderFactory;
* @version 1.0 * @version 1.0
* @see Theme * @see Theme
*/ */
@SuppressWarnings({ "checkstyle:magicnumber" })
public class SyntaxScheme implements Cloneable, TokenTypes { public class SyntaxScheme implements Cloneable, TokenTypes {
private Style[] styles; private Style[] styles;
private static final String VERSION = "*ver1"; private static final String VERSION = "*ver1";
/** /**
* Creates a color scheme that either has all color values set to * Creates a color scheme that either has all color values set to a default
* a default value or set to <code>null</code>. * value or set to <code>null</code>.
* *
* @param useDefaults * @param useDefaults If <code>true</code>, all color values will be set to
* If <code>true</code>, all color values will * default colors; if <code>false</code>, all colors will be
* be set to default colors; if <code>false</code>, all colors * initially <code>null</code>.
* will be initially <code>null</code>.
*/ */
public SyntaxScheme(boolean useDefaults) { public SyntaxScheme(boolean useDefaults) {
styles = new Style[DEFAULT_NUM_TOKEN_TYPES]; styles = new Style[DEFAULT_NUM_TOKEN_TYPES];
@@ -56,50 +54,43 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
restoreDefaults(null); restoreDefaults(null);
} }
} }
/** /**
* Creates a default color scheme. * Creates a default color scheme.
* *
* @param baseFont * @param baseFont The base font to use. Keywords will be a bold version of this
* The base font to use. Keywords will be a bold version * font, and comments will be an italicized version of this
* of this font, and comments will be an italicized version of * font.
* this
* font.
*/ */
public SyntaxScheme(Font baseFont) { public SyntaxScheme(Font baseFont) {
this(baseFont, true); this(baseFont, true);
} }
/** /**
* Creates a default color scheme. * Creates a default color scheme.
* *
* @param baseFont * @param baseFont The base font to use. Keywords will be a bold version of
* The base font to use. Keywords will be a bold version * this font, and comments will be an italicized version of
* of this font, and comments will be an italicized version of * this font.
* this * @param fontStyles Whether bold and italic should be used in the scheme (vs.
* font. * all tokens using a plain font).
* @param fontStyles
* Whether bold and italic should be used in the scheme
* (vs. all tokens using a plain font).
*/ */
public SyntaxScheme(Font baseFont, boolean fontStyles) { public SyntaxScheme(Font baseFont, boolean fontStyles) {
styles = new Style[DEFAULT_NUM_TOKEN_TYPES]; styles = new Style[DEFAULT_NUM_TOKEN_TYPES];
restoreDefaults(baseFont, fontStyles); restoreDefaults(baseFont, fontStyles);
} }
/** /**
* Changes the "base font" for this syntax scheme. This is called by * Changes the "base font" for this syntax scheme. This is called by
* <code>RSyntaxTextArea</code> when its font changes via * <code>RSyntaxTextArea</code> when its font changes via
* <code>setFont()</code>. This looks for tokens that use a derivative of * <code>setFont()</code>. This looks for tokens that use a derivative of the
* the text area's old font (but bolded and/or italicized) and make them * text area's old font (but bolded and/or italicized) and make them use the new
* use the new font with those stylings instead. This is desirable because * font with those stylings instead. This is desirable because most programmers
* most programmers prefer a single font to be used in their text editor, * prefer a single font to be used in their text editor, but might want bold
* but might want bold (say for keywords) or italics. * (say for keywords) or italics.
* *
* @param old * @param old The old font of the text area.
* The old font of the text area. * @param font The new font of the text area.
* @param font
* The new font of the text area.
*/ */
void changeBaseFont(Font old, Font font) { void changeBaseFont(Font old, Font font) {
for (int i = 0; i < styles.length; i++) { for (int i = 0; i < styles.length; i++) {
@@ -113,7 +104,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
} }
} }
/** /**
* Returns a deep copy of this color scheme. * Returns a deep copy of this color scheme.
* *
@@ -137,15 +128,13 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
return shcs; return shcs;
} }
/** /**
* Tests whether this color scheme is the same as another color scheme. * Tests whether this color scheme is the same as another color scheme.
* *
* @param otherScheme * @param otherScheme The color scheme to compare to.
* The color scheme to compare to. * @return <code>true</code> if this color scheme and <code>otherScheme</code>
* @return <code>true</code> if this color scheme and * are the same scheme; <code>false</code> otherwise.
* <code>otherScheme</code> are the same scheme;
* <code>false</code> otherwise.
*/ */
@Override @Override
public boolean equals(Object otherScheme) { public boolean equals(Object otherScheme) {
@@ -167,24 +156,22 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
return true; return true;
} }
/** /**
* Returns a hex string representing an RGB color, of the form * Returns a hex string representing an RGB color, of the form
* <code>"$rrggbb"</code>. * <code>"$rrggbb"</code>.
* *
* @param c * @param c The color.
* The color.
* @return The string representation of the color. * @return The string representation of the color.
*/ */
private static String getHexString(Color c) { private static String getHexString(Color c) {
return "$" + Integer.toHexString((c.getRGB() & 0xffffff) + 0x1000000).substring(1); return "$" + Integer.toHexString((c.getRGB() & 0xffffff) + 0x1000000).substring(1);
} }
/** /**
* Returns the specified style. * Returns the specified style.
* *
* @param index * @param index The index of the style.
* The index of the style.
* @return The style. * @return The style.
* @see #setStyle(int, Style) * @see #setStyle(int, Style)
* @see #getStyleCount() * @see #getStyleCount()
@@ -192,7 +179,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public Style getStyle(int index) { public Style getStyle(int index) {
return styles[index]; return styles[index];
} }
/** /**
* Returns the number of styles. * Returns the number of styles.
* *
@@ -202,14 +189,14 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public int getStyleCount() { public int getStyleCount() {
return styles.length; return styles.length;
} }
/** /**
* Used by third party implementors e.g. SquirreL SQL. Most applications do * Used by third party implementors e.g. SquirreL SQL. Most applications do not
* not need to call this method. * need to call this method.
* <p> * <p>
* Note that the returned array is not a copy of the style data; editing the * Note that the returned array is not a copy of the style data; editing the
* array will modify the styles used by any <code>RSyntaxTextArea</code> * array will modify the styles used by any <code>RSyntaxTextArea</code> using
* using this scheme. * this scheme.
* *
* @return The style array. * @return The style array.
* @see #setStyles(Style[]) * @see #setStyles(Style[])
@@ -217,10 +204,10 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
public Style[] getStyles() { public Style[] getStyles() {
return styles; return styles;
} }
/** /**
* This is implemented to be consistent with {@link #equals(Object)}. * This is implemented to be consistent with {@link #equals(Object)}. This is a
* This is a requirement to keep FindBugs happy. * requirement to keep FindBugs happy.
* *
* @return The hash code for this object. * @return The hash code for this object.
*/ */
@@ -238,24 +225,20 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
return hashCode; return hashCode;
} }
/** /**
* Loads a syntax scheme from an input stream. * Loads a syntax scheme from an input stream.
* <p> * <p>
* *
* Consider using the {@link Theme} class for saving and loading RSTA * Consider using the {@link Theme} class for saving and loading RSTA styles
* styles rather than using this API. * rather than using this API.
* *
* @param baseFont * @param baseFont The font to use as the "base" for the syntax scheme. If this
* The font to use as the "base" for the syntax scheme. * is <code>null</code>, a default monospaced font is used.
* If this is <code>null</code>, a default monospaced font is * @param in The stream to load from. It is up to the caller to close this
* used. * stream when they are done.
* @param in
* The stream to load from. It is up to the caller to close this
* stream when they are done.
* @return The syntax scheme. * @return The syntax scheme.
* @throws IOException * @throws IOException If an IO error occurs.
* If an IO error occurs.
*/ */
public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException { public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException {
if (baseFont == null) { if (baseFont == null) {
@@ -263,42 +246,40 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
return SyntaxSchemeLoader.load(baseFont, in); return SyntaxSchemeLoader.load(baseFont, in);
} }
/** /**
* Loads a syntax highlighting color scheme from a string created from * Loads a syntax highlighting color scheme from a string created from
* <code>toCommaSeparatedString</code>. This method is useful for saving * <code>toCommaSeparatedString</code>. This method is useful for saving and
* and restoring color schemes. * restoring color schemes.
* <p> * <p>
* *
* Consider using the {@link Theme} class for saving and loading RSTA * Consider using the {@link Theme} class for saving and loading RSTA styles
* styles rather than using this API. * rather than using this API.
* *
* @param string * @param string A string generated from {@link #toCommaSeparatedString()}.
* A string generated from {@link #toCommaSeparatedString()}.
* @return A color scheme. * @return A color scheme.
* @see #toCommaSeparatedString() * @see #toCommaSeparatedString()
*/ */
public static SyntaxScheme loadFromString(String string) { public static SyntaxScheme loadFromString(String string) {
return loadFromString(string, DEFAULT_NUM_TOKEN_TYPES); return loadFromString(string, DEFAULT_NUM_TOKEN_TYPES);
} }
/** /**
* Loads a syntax highlighting color scheme from a string created from * Loads a syntax highlighting color scheme from a string created from
* <code>toCommaSeparatedString</code>. This method is useful for saving * <code>toCommaSeparatedString</code>. This method is useful for saving and
* and restoring color schemes. * restoring color schemes.
* <p> * <p>
* *
* Consider using the {@link Theme} class for saving and loading RSTA * Consider using the {@link Theme} class for saving and loading RSTA styles
* styles rather than using this API. * rather than using this API.
* *
* @param string * @param string A string generated from
* A string generated from {@link #toCommaSeparatedString()}. * {@link #toCommaSeparatedString()}.
* @param tokenTypeCount * @param tokenTypeCount The number of token types saved in this string. This
* The number of token types saved in this string. * should be the number of token types saved by your
* This should be the number of token types saved by your custom * custom SyntaxScheme subclass, or
* SyntaxScheme subclass, * {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES} if you used
* or {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES} if you used the * the standard implementation (which most people will).
* standard implementation (which most people will).
* @return A color scheme. * @return A color scheme.
* @see #loadFromString(String) * @see #loadFromString(String)
* @see #toCommaSeparatedString() * @see #toCommaSeparatedString()
@@ -314,7 +295,8 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
int tokenCount = tokenTypeCount * 7 + 1; // Version string int tokenCount = tokenTypeCount * 7 + 1; // Version string
if (tokens.length != tokenCount) { if (tokens.length != tokenCount) {
throw new Exception("Not enough tokens in packed color scheme: expected " + tokenCount + ", found " + tokens.length); throw new Exception("Not enough tokens in packed color scheme: expected " + tokenCount + ", found "
+ tokens.length);
} }
// Use StyleContext to create fonts to get composite fonts for // Use StyleContext to create fonts to get composite fonts for
// Asian glyphs. // Asian glyphs.
@@ -359,7 +341,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
return scheme; return scheme;
} }
void refreshFontMetrics(Graphics2D g2d) { void refreshFontMetrics(Graphics2D g2d) {
// It is assumed that any rendering hints are already applied to g2d. // It is assumed that any rendering hints are already applied to g2d.
for (int i = 0; i < styles.length; i++) { for (int i = 0; i < styles.length; i++) {
@@ -369,29 +351,24 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
} }
} }
/** /**
* Restores all colors and fonts to their default values. * Restores all colors and fonts to their default values.
* *
* @param baseFont * @param baseFont The base font to use when creating this scheme. If this is
* The base font to use when creating this scheme. If * <code>null</code>, then a default monospaced font is used.
* this is <code>null</code>, then a default monospaced font is
* used.
*/ */
public void restoreDefaults(Font baseFont) { public void restoreDefaults(Font baseFont) {
restoreDefaults(baseFont, true); restoreDefaults(baseFont, true);
} }
/** /**
* Restores all colors and fonts to their default values. * Restores all colors and fonts to their default values.
* *
* @param baseFont * @param baseFont The base font to use when creating this scheme. If this is
* The base font to use when creating this scheme. If * <code>null</code>, then a default monospaced font is used.
* this is <code>null</code>, then a default monospaced font is * @param fontStyles Whether bold and italic should be used in the scheme (vs.
* used. * all tokens using a plain font).
* @param fontStyles
* Whether bold and italic should be used in the scheme
* (vs. all tokens using a plain font).
*/ */
public void restoreDefaults(Font baseFont, boolean fontStyles) { public void restoreDefaults(Font baseFont, boolean fontStyles) {
// Colors used by tokens. // Colors used by tokens.
@@ -469,40 +446,36 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
} }
} }
/** /**
* Sets a style to use when rendering a token type. * Sets a style to use when rendering a token type.
* *
* @param type * @param type The token type.
* The token type. * @param style The style for the token type.
* @param style
* The style for the token type.
* @see #getStyle(int) * @see #getStyle(int)
*/ */
public void setStyle(int type, Style style) { public void setStyle(int type, Style style) {
styles[type] = style; styles[type] = style;
} }
/** /**
* Used by third party implementors e.g. SquirreL SQL. Most applications do * Used by third party implementors e.g. SquirreL SQL. Most applications do not
* not need to call this method; individual styles can be set via * need to call this method; individual styles can be set via
* {@link #setStyle(int, Style)}. * {@link #setStyle(int, Style)}.
* *
* @param styles * @param styles The new array of styles to use. Note that this should have
* The new array of styles to use. Note that this should * length of at least {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES}.
* have length of at least
* {@link TokenTypes#DEFAULT_NUM_TOKEN_TYPES}.
* @see #setStyle(int, Style) * @see #setStyle(int, Style)
* @see #getStyles() * @see #getStyles()
*/ */
public void setStyles(Style[] styles) { public void setStyles(Style[] styles) {
this.styles = styles; this.styles = styles;
} }
/** /**
* Returns the color represented by a string. If the first char in the * Returns the color represented by a string. If the first char in the string is
* string is '<code>$</code>', it is assumed to be in hex, otherwise it is * '<code>$</code>', it is assumed to be in hex, otherwise it is assumed to be
* assumed to be decimal. So, for example, both of these: * decimal. So, for example, both of these:
* *
* <pre> * <pre>
* "$00ff00" * "$00ff00"
@@ -511,8 +484,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
* *
* will return <code>new Color(0, 255, 0)</code>. * will return <code>new Color(0, 255, 0)</code>.
* *
* @param s * @param s The string to evaluate.
* The string to evaluate.
* @return The color. * @return The color.
*/ */
private static Color stringToColor(String s) { private static Color stringToColor(String s) {
@@ -521,28 +493,26 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
char ch = s.charAt(0); char ch = s.charAt(0);
return new Color((ch == '$' || ch == '#') ? Integer.parseInt(s.substring(1), 16) : Integer.parseInt(s)); return new Color((ch == '$' || ch == '#') ? Integer.parseInt(s.substring(1), 16) : Integer.parseInt(s));
} }
/** /**
* Returns this syntax highlighting scheme as a comma-separated list of * Returns this syntax highlighting scheme as a comma-separated list of values
* values as follows: * as follows:
* <ul> * <ul>
* <li>If a color is non-null, it is added as a 24-bit integer * <li>If a color is non-null, it is added as a 24-bit integer of the form
* of the form <code>((r&lt;*lt;16) | (g&lt;*lt;8) | (b))</code>; if * <code>((r&lt;*lt;16) | (g&lt;*lt;8) | (b))</code>; if it is
* it is <code>null</code>, it is added as "<i>-,</i>". * <code>null</code>, it is added as "<i>-,</i>".
* <li>The font and style (bold/italic) is added as an integer like so: * <li>The font and style (bold/italic) is added as an integer like so:
* "<i>family,</i> <i>style,</i> <i>size</i>". * "<i>family,</i> <i>style,</i> <i>size</i>".
* <li>The entire syntax highlighting scheme is thus one long string of * <li>The entire syntax highlighting scheme is thus one long string of color
* color schemes of the format "<i>i,[fg],[bg],uline,[style]</i>, * schemes of the format "<i>i,[fg],[bg],uline,[style]</i>, where:
* where:
* <ul> * <ul>
* <li><code>i</code> is the index of the syntax scheme. * <li><code>i</code> is the index of the syntax scheme.
* <li><i>fg</i> and <i>bg</i> are the foreground and background * <li><i>fg</i> and <i>bg</i> are the foreground and background colors for the
* colors for the scheme, and may be null (represented by * scheme, and may be null (represented by <code>-</code>).
* <code>-</code>). * <li><code>uline</code> is whether or not the font should be underlined, and
* <li><code>uline</code> is whether or not the font should be * is either <code>t</code> or <code>f</code>.
* underlined, and is either <code>t</code> or <code>f</code>. * <li><code>style</code> is the <code>family,style,size</code> triplet
* <li><code>style</code> is the <code>family,style,size</code> * described above.
* triplet described above.
* </ul> * </ul>
* </ul> * </ul>
* *
@@ -566,26 +536,27 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
sb.append(ss.underline ? "t," : "f,"); sb.append(ss.underline ? "t," : "f,");
Font font = ss.font; Font font = ss.font;
if (font != null) { if (font != null) {
sb.append(font.getFamily()).append(',').append(font.getStyle()).append(',').append(font.getSize()).append(','); sb.append(font.getFamily()).append(',').append(font.getStyle()).append(',').append(font.getSize())
.append(',');
} else { } else {
sb.append("-,,,"); sb.append("-,,,");
} }
} }
return sb.substring(0, sb.length() - 1); // Take off final ','. return sb.substring(0, sb.length() - 1); // Take off final ','.
} }
/** /**
* Loads a <code>SyntaxScheme</code> from an XML file. * Loads a <code>SyntaxScheme</code> from an XML file.
*/ */
private static class SyntaxSchemeLoader extends DefaultHandler { private static class SyntaxSchemeLoader extends DefaultHandler {
private Font baseFont; private Font baseFont;
private SyntaxScheme scheme; private SyntaxScheme scheme;
SyntaxSchemeLoader(Font baseFont) { SyntaxSchemeLoader(Font baseFont) {
scheme = new SyntaxScheme(baseFont); scheme = new SyntaxScheme(baseFont);
} }
public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException { public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException {
SyntaxSchemeLoader parser = null; SyntaxSchemeLoader parser = null;
try { try {
@@ -601,7 +572,7 @@ public class SyntaxScheme implements Cloneable, TokenTypes {
} }
return parser.scheme; return parser.scheme;
} }
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attrs) { public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("style".equals(qName)) { if ("style".equals(qName)) {
+60 -77
View File
@@ -62,17 +62,16 @@ import org.xml.sax.helpers.DefaultHandler;
* for various tasks. * for various tasks.
* <p> * <p>
* *
* Note that to save a <code>Theme</code> via {@link #save(OutputStream)}, * Note that to save a <code>Theme</code> via {@link #save(OutputStream)}, you
* you must currently create a <code>Theme</code> from a text area wrapped in * must currently create a <code>Theme</code> from a text area wrapped in an
* an <code>RTextScrollPane</code>, so that the color information for the * <code>RTextScrollPane</code>, so that the color information for the gutter
* gutter can be retrieved. * can be retrieved.
* *
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public class Theme { public class Theme {
public Font baseFont; public Font baseFont;
public Color bgColor; public Color bgColor;
public Color caretColor; public Color caretColor;
@@ -103,14 +102,13 @@ public class Theme {
public Color foldIndicatorFG; public Color foldIndicatorFG;
public Color foldBG; public Color foldBG;
public Color armedFoldBG; public Color armedFoldBG;
/** /**
* Private constructor, used when loading from a stream. * Private constructor, used when loading from a stream.
* *
* @param baseFont * @param baseFont The default font to use for any "base font" properties not
* The default font to use for any "base font" properties * specified in the theme XML. If this is <code>null</code>, a
* not specified in the theme XML. If this is <code>null</code>, * default monospaced font will be used.
* a default monospaced font will be used.
*/ */
private Theme(Font baseFont) { private Theme(Font baseFont) {
// Optional fields that require a default value. // Optional fields that require a default value.
@@ -118,13 +116,12 @@ public class Theme {
secondaryLanguages = new Color[3]; secondaryLanguages = new Color[3];
activeLineRangeColor = Gutter.DEFAULT_ACTIVE_LINE_RANGE_COLOR; activeLineRangeColor = Gutter.DEFAULT_ACTIVE_LINE_RANGE_COLOR;
} }
/** /**
* Creates a theme from an RSyntaxTextArea. It should be contained in * Creates a theme from an RSyntaxTextArea. It should be contained in an
* an <code>RTextScrollPane</code> to get all gutter color information. * <code>RTextScrollPane</code> to get all gutter color information.
* *
* @param textArea * @param textArea The text area.
* The text area.
*/ */
public Theme(RSyntaxTextArea textArea) { public Theme(RSyntaxTextArea textArea) {
baseFont = textArea.getFont(); baseFont = textArea.getFont();
@@ -165,12 +162,11 @@ public class Theme {
armedFoldBG = gutter.getArmedFoldBackground(); armedFoldBG = gutter.getArmedFoldBackground();
} }
} }
/** /**
* Applies this theme to a text area. * Applies this theme to a text area.
* *
* @param textArea * @param textArea The text area to apply this theme to.
* The text area to apply this theme to.
*/ */
public void apply(RSyntaxTextArea textArea) { public void apply(RSyntaxTextArea textArea) {
textArea.setFont(baseFont); textArea.setFont(baseFont);
@@ -212,7 +208,7 @@ public class Theme {
gutter.setArmedFoldBackground(armedFoldBG); gutter.setArmedFoldBackground(armedFoldBG);
} }
} }
private static String colorToString(Color c) { private static String colorToString(Color c) {
int color = c.getRGB() & 0xffffff; int color = c.getRGB() & 0xffffff;
StringBuilder stringBuilder = new StringBuilder(Integer.toHexString(color)); StringBuilder stringBuilder = new StringBuilder(Integer.toHexString(color));
@@ -221,7 +217,7 @@ public class Theme {
} }
return stringBuilder.toString(); return stringBuilder.toString();
} }
/** /**
* Returns the default selection background color to use if "default" is * Returns the default selection background color to use if "default" is
* specified in a theme. * specified in a theme.
@@ -243,7 +239,7 @@ public class Theme {
} }
return c; return c;
} }
/** /**
* Returns the default selection background color to use if "default" is * Returns the default selection background color to use if "default" is
* specified in a theme. * specified in a theme.
@@ -264,10 +260,10 @@ public class Theme {
} }
return c; return c;
} }
/** /**
* Returns the default "selected text" color to use if "default" is * Returns the default "selected text" color to use if "default" is specified in
* specified in a theme. * a theme.
* *
* @return The default selection foreground color to use. * @return The default selection foreground color to use.
* @see #getDefaultSelectionBG() * @see #getDefaultSelectionBG()
@@ -285,16 +281,13 @@ public class Theme {
} }
return c; return c;
} }
/** /**
* Returns the specified font. * Returns the specified font.
* *
* @param family * @param family The font family.
* The font family. * @param style The style of font.
* @param style * @param size The size of the font.
* The style of font.
* @param size
* The size of the font.
* @return The font. * @return The font.
*/ */
private static Font getFont(String family, int style, int size) { private static Font getFont(String family, int style, int size) {
@@ -302,35 +295,30 @@ public class Theme {
StyleContext sc = StyleContext.getDefaultStyleContext(); StyleContext sc = StyleContext.getDefaultStyleContext();
return sc.getFont(family, style, size); return sc.getFont(family, style, size);
} }
/** /**
* Loads a theme. * Loads a theme.
* *
* @param in * @param in The input stream to read from. This will be closed when this method
* The input stream to read from. This will be closed when this * returns.
* method returns.
* @return The theme. * @return The theme.
* @throws IOException * @throws IOException If an IO error occurs.
* If an IO error occurs.
* @see #save(OutputStream) * @see #save(OutputStream)
*/ */
public static Theme load(InputStream in) throws IOException { public static Theme load(InputStream in) throws IOException {
return load(in, null); return load(in, null);
} }
/** /**
* Loads a theme. * Loads a theme.
* *
* @param in * @param in The input stream to read from. This will be closed when this
* The input stream to read from. This will be closed when this * method returns.
* method returns. * @param baseFont The default font to use for any "base font" properties not
* @param baseFont * specified in the theme XML. If this is <code>null</code>, a
* The default font to use for any "base font" properties * default monospaced font will be used.
* not specified in the theme XML. If this is <code>null</code>,
* a default monospaced font will be used.
* @return The theme. * @return The theme.
* @throws IOException * @throws IOException If an IO error occurs.
* If an IO error occurs.
* @see #save(OutputStream) * @see #save(OutputStream)
*/ */
public static Theme load(InputStream in, Font baseFont) throws IOException { public static Theme load(InputStream in, Font baseFont) throws IOException {
@@ -343,14 +331,12 @@ public class Theme {
} }
return theme; return theme;
} }
/** /**
* Saves this theme to an output stream. * Saves this theme to an output stream.
* *
* @param out * @param out The output stream to write to.
* The output stream to write to. * @throws IOException If an IO error occurs.
* @throws IOException
* If an IO error occurs.
* @see #load(InputStream) * @see #load(InputStream)
*/ */
public void save(OutputStream out) throws IOException { public void save(OutputStream out) throws IOException {
@@ -495,11 +481,11 @@ public class Theme {
bout.close(); bout.close();
} }
} }
/** /**
* Returns the color represented by a string. The input is expected to * Returns the color represented by a string. The input is expected to be a
* be a 6-digit hex string, optionally prefixed by a '$'. For example, * 6-digit hex string, optionally prefixed by a '$'. For example, either of the
* either of the following: * following:
* *
* <pre> * <pre>
* "$00ff00" * "$00ff00"
@@ -508,18 +494,17 @@ public class Theme {
* *
* will return <code>new Color(0, 255, 0)</code>. * will return <code>new Color(0, 255, 0)</code>.
* *
* @param s * @param s The string to evaluate.
* The string to evaluate.
* @return The color. * @return The color.
*/ */
private static Color stringToColor(String s) { private static Color stringToColor(String s) {
return stringToColor(s, null); return stringToColor(s, null);
} }
/** /**
* Returns the color represented by a string. The input is expected to * Returns the color represented by a string. The input is expected to be a
* be a 6-digit hex string, optionally prefixed by a '$'. For example, * 6-digit hex string, optionally prefixed by a '$'. For example, either of the
* either of the following: * following:
* *
* <pre> * <pre>
* "$00ff00" * "$00ff00"
@@ -528,11 +513,9 @@ public class Theme {
* *
* will return <code>new Color(0, 255, 0)</code>. * will return <code>new Color(0, 255, 0)</code>.
* *
* @param s * @param s The string to evaluate.
* The string to evaluate. * @param defaultVal The color to use if <code>s</code> is
* @param defaultVal * "<code>default</code>".
* The color to use if <code>s</code> is
* "<code>default</code>".
* @return The color. * @return The color.
*/ */
private static Color stringToColor(String s, Color defaultVal) { private static Color stringToColor(String s, Color defaultVal) {
@@ -547,24 +530,24 @@ public class Theme {
} }
return null; return null;
} }
/** /**
* Loads a <code>SyntaxScheme</code> from an XML file. * Loads a <code>SyntaxScheme</code> from an XML file.
*/ */
private static class XmlHandler extends DefaultHandler { private static class XmlHandler extends DefaultHandler {
private Theme theme; private Theme theme;
@Override @Override
public void error(SAXParseException e) throws SAXException { public void error(SAXParseException e) throws SAXException {
throw e; throw e;
} }
@Override @Override
public void fatalError(SAXParseException e) throws SAXException { public void fatalError(SAXParseException e) throws SAXException {
throw e; throw e;
} }
public static void load(Theme theme, InputStream in) throws IOException { public static void load(Theme theme, InputStream in) throws IOException {
SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParserFactory spf = SAXParserFactory.newInstance();
spf.setValidating(true); spf.setValidating(true);
@@ -584,7 +567,7 @@ public class Theme {
throw new IOException(se.toString()); throw new IOException(se.toString());
} }
} }
private static int parseInt(Attributes attrs, String attr, int def) { private static int parseInt(Attributes attrs, String attr, int def) {
int value = def; int value = def;
String temp = attrs.getValue(attr); String temp = attrs.getValue(attr);
@@ -597,12 +580,12 @@ public class Theme {
} }
return value; return value;
} }
@Override @Override
public InputSource resolveEntity(String publicID, String systemID) throws SAXException { public InputSource resolveEntity(String publicID, String systemID) throws SAXException {
return new InputSource(getClass().getResourceAsStream("themes/theme.dtd")); return new InputSource(getClass().getResourceAsStream("themes/theme.dtd"));
} }
@Override @Override
public void startElement(String uri, String localName, String qName, Attributes attrs) { public void startElement(String uri, String localName, String qName, Attributes attrs) {
if ("background".equals(qName)) { if ("background".equals(qName)) {
@@ -787,7 +770,7 @@ public class Theme {
} }
} }
} }
@Override @Override
public void warning(SAXParseException e) throws SAXException { public void warning(SAXParseException e) throws SAXException {
throw e; throw e;
+207 -237
View File
@@ -22,19 +22,18 @@ import javax.swing.text.Utilities;
* <b>Note:</b> The instances of <code>Token</code> returned by * <b>Note:</b> The instances of <code>Token</code> returned by
* {@link RSyntaxDocument}s are pooled and should always be treated as * {@link RSyntaxDocument}s are pooled and should always be treated as
* immutable. They should not be cast to <code>TokenImpl</code> and modified. * immutable. They should not be cast to <code>TokenImpl</code> and modified.
* Modifying tokens you did not create yourself can and will result in * Modifying tokens you did not create yourself can and will result in rendering
* rendering issues and/or runtime exceptions. You have been warned! * issues and/or runtime exceptions. You have been warned!
* *
* @author Robert Futrell * @author Robert Futrell
* @version 0.3 * @version 0.3
*/ */
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public class TokenImpl implements Token { public class TokenImpl implements Token {
/** /**
* The text this token represents. This is implemented as a segment so we * The text this token represents. This is implemented as a segment so we can
* can point directly to the text in the document without having to make a * point directly to the text in the document without having to make a copy of
* copy of it. * it.
*/ */
public char[] text; public char[] text;
public int textOffset; public int textOffset;
@@ -59,11 +58,11 @@ public class TokenImpl implements Token {
* The language this token is in, <code>&gt;= 0</code>. * The language this token is in, <code>&gt;= 0</code>.
*/ */
private int languageIndex; private int languageIndex;
/** /**
* Creates a "null" token. The token itself is not null; rather, it * Creates a "null" token. The token itself is not null; rather, it signifies
* signifies that it is the last token in a linked list of tokens and * that it is the last token in a linked list of tokens and that it is not part
* that it is not part of a "multi-line token." * of a "multi-line token."
*/ */
public TokenImpl() { public TokenImpl() {
this.text = null; this.text = null;
@@ -74,69 +73,55 @@ public class TokenImpl implements Token {
hyperlink = false; hyperlink = false;
nextToken = null; nextToken = null;
} }
/** /**
* Constructor. * Constructor.
* *
* @param line * @param line The segment from which to get the token.
* The segment from which to get the token. * @param beg The first character's position in <code>line</code>.
* @param beg * @param end The last character's position in <code>line</code>.
* The first character's position in <code>line</code>. * @param startOffset The offset into the document at which this token begins.
* @param end * @param type A token type listed as "generic" above.
* The last character's position in <code>line</code>. * @param languageIndex The language index for this token.
* @param startOffset
* The offset into the document at which this
* token begins.
* @param type
* A token type listed as "generic" above.
* @param languageIndex
* The language index for this token.
*/ */
public TokenImpl(Segment line, int beg, int end, int startOffset, int type, int languageIndex) { public TokenImpl(Segment line, int beg, int end, int startOffset, int type, int languageIndex) {
this(line.array, beg, end, startOffset, type, languageIndex); this(line.array, beg, end, startOffset, type, languageIndex);
} }
/** /**
* Constructor. * Constructor.
* *
* @param line * @param line The segment from which to get the token.
* The segment from which to get the token. * @param beg The first character's position in <code>line</code>.
* @param beg * @param end The last character's position in <code>line</code>.
* The first character's position in <code>line</code>. * @param startOffset The offset into the document at which this token begins.
* @param end * @param type A token type listed as "generic" above.
* The last character's position in <code>line</code>. * @param languageIndex The language index for this token.
* @param startOffset
* The offset into the document at which this
* token begins.
* @param type
* A token type listed as "generic" above.
* @param languageIndex
* The language index for this token.
*/ */
public TokenImpl(char[] line, int beg, int end, int startOffset, int type, int languageIndex) { public TokenImpl(char[] line, int beg, int end, int startOffset, int type, int languageIndex) {
this(); this();
set(line, beg, end, startOffset, type); set(line, beg, end, startOffset, type);
setLanguageIndex(languageIndex); setLanguageIndex(languageIndex);
} }
/** /**
* Creates this token as a copy of the passed-in token. * Creates this token as a copy of the passed-in token.
* *
* @param t2 * @param t2 The token from which to make a copy.
* The token from which to make a copy.
*/ */
public TokenImpl(Token t2) { public TokenImpl(Token t2) {
this(); this();
copyFrom(t2); copyFrom(t2);
} }
@Override @Override
public StringBuilder appendHTMLRepresentation(StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily) { public StringBuilder appendHTMLRepresentation(StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily) {
return appendHTMLRepresentation(sb, textArea, fontFamily, false); return appendHTMLRepresentation(sb, textArea, fontFamily, false);
} }
@Override @Override
public StringBuilder appendHTMLRepresentation(StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily, boolean tabsToSpaces) { public StringBuilder appendHTMLRepresentation(StringBuilder sb, RSyntaxTextArea textArea, boolean fontFamily,
boolean tabsToSpaces) {
SyntaxScheme colorScheme = textArea.getSyntaxScheme(); SyntaxScheme colorScheme = textArea.getSyntaxScheme();
Style scheme = colorScheme.getStyle(getType()); Style scheme = colorScheme.getStyle(getType());
Font font = textArea.getFontForTokenType(getType());// scheme.font; Font font = textArea.getFontForTokenType(getType());// scheme.font;
@@ -177,18 +162,15 @@ public class TokenImpl implements Token {
} }
return sb; return sb;
} }
/** /**
* Appends an HTML version of the lexeme of this token (i.e. no style * Appends an HTML version of the lexeme of this token (i.e. no style HTML, but
* HTML, but replacing chars such as <code>\t</code>, <code>&lt;</code> * replacing chars such as <code>\t</code>, <code>&lt;</code> and
* and <code>&gt;</code> with their escapes). * <code>&gt;</code> with their escapes).
* *
* @param textArea * @param textArea The text area.
* The text area. * @param sb The buffer to append to.
* @param sb * @param tabsToSpaces Whether to convert tabs into spaces.
* The buffer to append to.
* @param tabsToSpaces
* Whether to convert tabs into spaces.
* @return The same buffer. * @return The same buffer.
*/ */
private StringBuilder appendHtmlLexeme(RSyntaxTextArea textArea, StringBuilder sb, boolean tabsToSpaces) { private StringBuilder appendHtmlLexeme(RSyntaxTextArea textArea, StringBuilder sb, boolean tabsToSpaces) {
@@ -199,64 +181,64 @@ public class TokenImpl implements Token {
while (i < textOffset + textCount) { while (i < textOffset + textCount) {
char ch = text[i]; char ch = text[i];
switch (ch) { switch (ch) {
case ' ': case ' ':
sb.append(text, lastI, i - lastI); sb.append(text, lastI, i - lastI);
lastI = i + 1; lastI = i + 1;
sb.append(lastWasSpace ? "&nbsp;" : " "); sb.append(lastWasSpace ? "&nbsp;" : " ");
lastWasSpace = true; lastWasSpace = true;
break; break;
case '\t': case '\t':
sb.append(text, lastI, i - lastI); sb.append(text, lastI, i - lastI);
lastI = i + 1; lastI = i + 1;
if (tabsToSpaces && tabStr == null) { if (tabsToSpaces && tabStr == null) {
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
for (int j = 0; j < textArea.getTabSize(); j++) { for (int j = 0; j < textArea.getTabSize(); j++) {
stringBuilder.append("&nbsp;"); stringBuilder.append("&nbsp;");
}
tabStr = stringBuilder.toString();
} }
sb.append(tabsToSpaces ? tabStr : "&#09;"); tabStr = stringBuilder.toString();
lastWasSpace = false; }
break; sb.append(tabsToSpaces ? tabStr : "&#09;");
case '&': lastWasSpace = false;
sb.append(text, lastI, i - lastI); break;
lastI = i + 1; case '&':
sb.append("&amp;"); sb.append(text, lastI, i - lastI);
lastWasSpace = false; lastI = i + 1;
break; sb.append("&amp;");
case '<': lastWasSpace = false;
sb.append(text, lastI, i - lastI); break;
lastI = i + 1; case '<':
sb.append("&lt;"); sb.append(text, lastI, i - lastI);
lastWasSpace = false; lastI = i + 1;
break; sb.append("&lt;");
case '>': lastWasSpace = false;
sb.append(text, lastI, i - lastI); break;
lastI = i + 1; case '>':
sb.append("&gt;"); sb.append(text, lastI, i - lastI);
lastWasSpace = false; lastI = i + 1;
break; sb.append("&gt;");
case '\'': lastWasSpace = false;
sb.append(text, lastI, i - lastI); break;
lastI = i + 1; case '\'':
sb.append("&#39;"); sb.append(text, lastI, i - lastI);
lastWasSpace = false; lastI = i + 1;
break; sb.append("&#39;");
case '"': lastWasSpace = false;
sb.append(text, lastI, i - lastI); break;
lastI = i + 1; case '"':
sb.append("&#34;"); sb.append(text, lastI, i - lastI);
lastWasSpace = false; lastI = i + 1;
break; sb.append("&#34;");
case '/': // OWASP-recommended to escape even though unnecessary lastWasSpace = false;
sb.append(text, lastI, i - lastI); break;
lastI = i + 1; case '/': // OWASP-recommended to escape even though unnecessary
sb.append("&#47;"); sb.append(text, lastI, i - lastI);
lastWasSpace = false; lastI = i + 1;
break; sb.append("&#47;");
default: lastWasSpace = false;
lastWasSpace = false; break;
break; default:
lastWasSpace = false;
break;
} }
i++; i++;
} }
@@ -265,23 +247,22 @@ public class TokenImpl implements Token {
} }
return sb; return sb;
} }
@Override @Override
public char charAt(int index) { public char charAt(int index) {
return text[textOffset + index]; return text[textOffset + index];
} }
@Override @Override
public boolean containsPosition(int pos) { public boolean containsPosition(int pos) {
return pos >= getOffset() && pos < getOffset() + textCount; return pos >= getOffset() && pos < getOffset() + textCount;
} }
/** /**
* Makes one token point to the same text segment, and have the same value * Makes one token point to the same text segment, and have the same value as
* as another token. * another token.
* *
* @param t2 * @param t2 The token from which to copy.
* The token from which to copy.
*/ */
public void copyFrom(Token t2) { public void copyFrom(Token t2) {
text = t2.getTextArray(); text = t2.getTextArray();
@@ -293,12 +274,12 @@ public class TokenImpl implements Token {
languageIndex = t2.getLanguageIndex(); languageIndex = t2.getLanguageIndex();
nextToken = t2.getNextToken(); nextToken = t2.getNextToken();
} }
@Override @Override
public int documentToToken(int pos) { public int documentToToken(int pos) {
return pos + (textOffset - getOffset()); return pos + (textOffset - getOffset());
} }
@Override @Override
public boolean endsWith(char[] ch) { public boolean endsWith(char[] ch) {
if (ch == null || ch.length > textCount) { if (ch == null || ch.length > textCount) {
@@ -312,7 +293,7 @@ public class TokenImpl implements Token {
} }
return true; return true;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) { if (obj == this) {
@@ -322,21 +303,21 @@ public class TokenImpl implements Token {
return false; return false;
} }
Token t2 = (Token) obj; Token t2 = (Token) obj;
return offset == t2.getOffset() && type == t2.getType() && languageIndex == t2.getLanguageIndex() && hyperlink == t2.isHyperlink() return offset == t2.getOffset() && type == t2.getType() && languageIndex == t2.getLanguageIndex()
&& ((getLexeme() == null && t2.getLexeme() == null) || (getLexeme() != null && getLexeme().equals(t2.getLexeme()))); && hyperlink == t2.isHyperlink() && ((getLexeme() == null && t2.getLexeme() == null)
|| (getLexeme() != null && getLexeme().equals(t2.getLexeme())));
} }
@Override @Override
public int getEndOffset() { public int getEndOffset() {
return offset + textCount; return offset + textCount;
} }
/** /**
* Returns a <code>String</code> of the form "#xxxxxx" good for use * Returns a <code>String</code> of the form "#xxxxxx" good for use in HTML,
* in HTML, representing the given color. * representing the given color.
* *
* @param color * @param color The color to get a string for.
* The color to get a string for.
* @return The HTML form of the color. If <code>color</code> is * @return The HTML form of the color. If <code>color</code> is
* <code>null</code>, <code>#000000</code> is returned. * <code>null</code>, <code>#000000</code> is returned.
*/ */
@@ -358,39 +339,39 @@ public class TokenImpl implements Token {
} }
return "#" + hexRed + hexGreen + hexBlue; return "#" + hexRed + hexGreen + hexBlue;
} }
@Override @Override
public String getHTMLRepresentation(RSyntaxTextArea textArea) { public String getHTMLRepresentation(RSyntaxTextArea textArea) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
appendHTMLRepresentation(buf, textArea, true); appendHTMLRepresentation(buf, textArea, true);
return buf.toString(); return buf.toString();
} }
@Override @Override
public int getLanguageIndex() { public int getLanguageIndex() {
return languageIndex; return languageIndex;
} }
@Override @Override
public Token getLastNonCommentNonWhitespaceToken() { public Token getLastNonCommentNonWhitespaceToken() {
Token last = null; Token last = null;
for (Token t = this; t != null && t.isPaintable(); t = t.getNextToken()) { for (Token t = this; t != null && t.isPaintable(); t = t.getNextToken()) {
switch (t.getType()) { switch (t.getType()) {
case COMMENT_DOCUMENTATION: case COMMENT_DOCUMENTATION:
case COMMENT_EOL: case COMMENT_EOL:
case COMMENT_MULTILINE: case COMMENT_MULTILINE:
case COMMENT_KEYWORD: case COMMENT_KEYWORD:
case COMMENT_MARKUP: case COMMENT_MARKUP:
case WHITESPACE: case WHITESPACE:
break; break;
default: default:
last = t; last = t;
break; break;
} }
} }
return last; return last;
} }
@Override @Override
public Token getLastPaintableToken() { public Token getLastPaintableToken() {
Token t = this; Token t = this;
@@ -403,12 +384,12 @@ public class TokenImpl implements Token {
} }
return null; return null;
} }
@Override @Override
public String getLexeme() { public String getLexeme() {
return text == null ? null : new String(text, textOffset, textCount); return text == null ? null : new String(text, textOffset, textCount);
} }
@Override @Override
public int getListOffset(RSyntaxTextArea textArea, TabExpander e, float x0, float x) { public int getListOffset(RSyntaxTextArea textArea, TabExpander e, float x0, float x) {
// If the coordinate in question is before this line's start, quit. // If the coordinate in question is before this line's start, quit.
@@ -449,17 +430,17 @@ public class TokenImpl implements Token {
// If we didn't find anything, return the end position of the text. // If we didn't find anything, return the end position of the text.
return last; return last;
} }
@Override @Override
public Token getNextToken() { public Token getNextToken() {
return nextToken; return nextToken;
} }
@Override @Override
public int getOffset() { public int getOffset() {
return offset; return offset;
} }
@Override @Override
public int getOffsetBeforeX(RSyntaxTextArea textArea, TabExpander e, float startX, float endBeforeX) { public int getOffsetBeforeX(RSyntaxTextArea textArea, TabExpander e, float startX, float endBeforeX) {
FontMetrics fm = textArea.getFontMetricsForTokenType(getType()); FontMetrics fm = textArea.getFontMetricsForTokenType(getType());
@@ -484,27 +465,27 @@ public class TokenImpl implements Token {
// If we got here, the whole token fit in (endBeforeX-startX) pixels. // If we got here, the whole token fit in (endBeforeX-startX) pixels.
return getOffset() + textCount - 1; return getOffset() + textCount - 1;
} }
@Override @Override
public char[] getTextArray() { public char[] getTextArray() {
return text; return text;
} }
@Override @Override
public int getTextOffset() { public int getTextOffset() {
return textOffset; return textOffset;
} }
@Override @Override
public int getType() { public int getType() {
return type; return type;
} }
@Override @Override
public float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0) { public float getWidth(RSyntaxTextArea textArea, TabExpander e, float x0) {
return getWidthUpTo(textCount, textArea, e, x0); return getWidthUpTo(textCount, textArea, e, x0);
} }
@Override @Override
public float getWidthUpTo(int numChars, RSyntaxTextArea textArea, TabExpander e, float x0) { public float getWidthUpTo(int numChars, RSyntaxTextArea textArea, TabExpander e, float x0) {
float width = x0; float width = x0;
@@ -535,12 +516,12 @@ public class TokenImpl implements Token {
} }
return width - x0; return width - x0;
} }
@Override @Override
public int hashCode() { public int hashCode() {
return offset + (getLexeme() == null ? 0 : getLexeme().hashCode()); return offset + (getLexeme() == null ? 0 : getLexeme().hashCode());
} }
@Override @Override
public boolean is(char[] lexeme) { public boolean is(char[] lexeme) {
if (textCount == lexeme.length) { if (textCount == lexeme.length) {
@@ -553,7 +534,7 @@ public class TokenImpl implements Token {
} }
return false; return false;
} }
@Override @Override
public boolean is(int type, char[] lexeme) { public boolean is(int type, char[] lexeme) {
if (this.getType() == type && textCount == lexeme.length) { if (this.getType() == type && textCount == lexeme.length) {
@@ -566,67 +547,67 @@ public class TokenImpl implements Token {
} }
return false; return false;
} }
@Override @Override
public boolean is(int type, String lexeme) { public boolean is(int type, String lexeme) {
return this.getType() == type && textCount == lexeme.length() && lexeme.equals(getLexeme()); return this.getType() == type && textCount == lexeme.length() && lexeme.equals(getLexeme());
} }
@Override @Override
public boolean isComment() { public boolean isComment() {
return getType() >= Token.COMMENT_EOL && getType() <= Token.COMMENT_MARKUP; return getType() >= Token.COMMENT_EOL && getType() <= Token.COMMENT_MARKUP;
} }
@Override @Override
public boolean isCommentOrWhitespace() { public boolean isCommentOrWhitespace() {
return isComment() || isWhitespace(); return isComment() || isWhitespace();
} }
@Override @Override
public boolean isHyperlink() { public boolean isHyperlink() {
return hyperlink; return hyperlink;
} }
@Override @Override
public boolean isIdentifier() { public boolean isIdentifier() {
return getType() == IDENTIFIER; return getType() == IDENTIFIER;
} }
@Override @Override
public boolean isLeftCurly() { public boolean isLeftCurly() {
return getType() == SEPARATOR && isSingleChar('{'); return getType() == SEPARATOR && isSingleChar('{');
} }
@Override @Override
public boolean isRightCurly() { public boolean isRightCurly() {
return getType() == SEPARATOR && isSingleChar('}'); return getType() == SEPARATOR && isSingleChar('}');
} }
@Override @Override
public boolean isPaintable() { public boolean isPaintable() {
return getType() > Token.NULL; return getType() > Token.NULL;
} }
@Override @Override
public boolean isSingleChar(char ch) { public boolean isSingleChar(char ch) {
return textCount == 1 && text[textOffset] == ch; return textCount == 1 && text[textOffset] == ch;
} }
@Override @Override
public boolean isSingleChar(int type, char ch) { public boolean isSingleChar(int type, char ch) {
return this.getType() == type && isSingleChar(ch); return this.getType() == type && isSingleChar(ch);
} }
@Override @Override
public boolean isWhitespace() { public boolean isWhitespace() {
return getType() == WHITESPACE; return getType() == WHITESPACE;
} }
@Override @Override
public int length() { public int length() {
return textCount; return textCount;
} }
@Override @Override
public Rectangle listOffsetToView(RSyntaxTextArea textArea, TabExpander e, int pos, int x0, Rectangle rect) { public Rectangle listOffsetToView(RSyntaxTextArea textArea, TabExpander e, int pos, int x0, Rectangle rect) {
int stableX = x0; // Cached ending x-coord. of last tab or token. int stableX = x0; // Cached ending x-coord. of last tab or token.
@@ -678,49 +659,46 @@ public class TokenImpl implements Token {
rect.width = 1; rect.width = 1;
return rect; return rect;
} }
/** /**
* Makes this token start at the specified offset into the document. * Makes this token start at the specified offset into the document.
* <p> * <p>
* *
* <b>Note:</b> You should not modify <code>Token</code> instances you * <b>Note:</b> You should not modify <code>Token</code> instances you did not
* did not create yourself (e.g., came from an * create yourself (e.g., came from an <code>RSyntaxDocument</code>). If you do,
* <code>RSyntaxDocument</code>). If you do, rendering issues and/or * rendering issues and/or runtime exceptions will likely occur. You have been
* runtime exceptions will likely occur. You have been warned! * warned!
* *
* @param pos * @param pos The offset into the document this token should start at. Note that
* The offset into the document this token should start at. * this token must already contain this position; if it doesn't, an
* Note that this token must already contain this position; if * exception is thrown.
* it doesn't, an exception is thrown. * @throws IllegalArgumentException If pos is not already contained by this
* @throws IllegalArgumentException * token.
* If pos is not already contained by
* this token.
* @see #moveOffset(int) * @see #moveOffset(int)
*/ */
public void makeStartAt(int pos) { public void makeStartAt(int pos) {
if (pos < getOffset() || pos >= (getOffset() + textCount)) { if (pos < getOffset() || pos >= (getOffset() + textCount)) {
throw new IllegalArgumentException("pos " + pos + " is not in range " + getOffset() + "-" + (getOffset() + textCount - 1)); throw new IllegalArgumentException(
"pos " + pos + " is not in range " + getOffset() + "-" + (getOffset() + textCount - 1));
} }
int shift = pos - getOffset(); int shift = pos - getOffset();
setOffset(pos); setOffset(pos);
textOffset += shift; textOffset += shift;
textCount -= shift; textCount -= shift;
} }
/** /**
* Moves the starting offset of this token. * Moves the starting offset of this token.
* <p> * <p>
* *
* <b>Note:</b> You should not modify <code>Token</code> instances you * <b>Note:</b> You should not modify <code>Token</code> instances you did not
* did not create yourself (e.g., came from an * create yourself (e.g., came from an <code>RSyntaxDocument</code>). If you do,
* <code>RSyntaxDocument</code>). If you do, rendering issues and/or * rendering issues and/or runtime exceptions will likely occur. You have been
* runtime exceptions will likely occur. You have been warned! * warned!
* *
* @param amt * @param amt The amount to move the starting offset. This should be between
* The amount to move the starting offset. This should be * <code>0</code> and <code>textCount</code>, inclusive.
* between <code>0</code> and <code>textCount</code>, inclusive. * @throws IllegalArgumentException If <code>amt</code> is an invalid value.
* @throws IllegalArgumentException
* If <code>amt</code> is an invalid value.
* @see #makeStartAt(int) * @see #makeStartAt(int)
*/ */
public void moveOffset(int amt) { public void moveOffset(int amt) {
@@ -731,21 +709,16 @@ public class TokenImpl implements Token {
textOffset += amt; textOffset += amt;
textCount -= amt; textCount -= amt;
} }
/** /**
* Sets the value of this token to a particular segment of a document. * Sets the value of this token to a particular segment of a document. The "next
* The "next token" value is cleared. * token" value is cleared.
* *
* @param line * @param line The segment from which to get the token.
* The segment from which to get the token. * @param beg The first character's position in <code>line</code>.
* @param beg * @param end The last character's position in <code>line</code>.
* The first character's position in <code>line</code>. * @param offset The offset into the document at which this token begins.
* @param end * @param type A token type listed as "generic" above.
* The last character's position in <code>line</code>.
* @param offset
* The offset into the document at which this token begins.
* @param type
* A token type listed as "generic" above.
*/ */
public void set(final char[] line, final int beg, final int end, final int offset, final int type) { public void set(final char[] line, final int beg, final int end, final int offset, final int type) {
this.text = line; this.text = line;
@@ -755,61 +728,56 @@ public class TokenImpl implements Token {
this.setOffset(offset); this.setOffset(offset);
nextToken = null; nextToken = null;
} }
/** /**
* Sets whether this token is a hyperlink. * Sets whether this token is a hyperlink.
* *
* @param hyperlink * @param hyperlink Whether this token is a hyperlink.
* Whether this token is a hyperlink.
* @see #isHyperlink() * @see #isHyperlink()
*/ */
@Override @Override
public void setHyperlink(boolean hyperlink) { public void setHyperlink(boolean hyperlink) {
this.hyperlink = hyperlink; this.hyperlink = hyperlink;
} }
/** /**
* Sets the language index for this token. If this value is positive, it * Sets the language index for this token. If this value is positive, it denotes
* denotes a specific "secondary" language this token represents (such as * a specific "secondary" language this token represents (such as JavaScript
* JavaScript code or CSS embedded in an HTML file). If this value is * code or CSS embedded in an HTML file). If this value is <code>0</code>, this
* <code>0</code>, this token is in the "main" language being edited. * token is in the "main" language being edited. Negative values are invalid and
* Negative values are invalid and treated as <code>0</code>. * treated as <code>0</code>.
* *
* @param languageIndex * @param languageIndex The new language index. A value of <code>0</code>
* The new language index. A value of * denotes the "main" language, any positive value denotes
* <code>0</code> denotes the "main" language, any positive value * a specific secondary language. Negative values will be
* denotes a specific secondary language. Negative values will * treated as <code>0</code>.
* be treated as <code>0</code>.
* @see #getLanguageIndex() * @see #getLanguageIndex()
*/ */
@Override @Override
public void setLanguageIndex(int languageIndex) { public void setLanguageIndex(int languageIndex) {
this.languageIndex = languageIndex; this.languageIndex = languageIndex;
} }
/** /**
* Sets the "next token" pointer of this token to point to the specified * Sets the "next token" pointer of this token to point to the specified token.
* token.
* *
* @param nextToken * @param nextToken The new next token.
* The new next token.
* @see #getNextToken() * @see #getNextToken()
*/ */
public void setNextToken(Token nextToken) { public void setNextToken(Token nextToken) {
this.nextToken = nextToken; this.nextToken = nextToken;
} }
/** /**
* Sets the offset into the document at which this token resides. * Sets the offset into the document at which this token resides.
* *
* @param offset * @param offset The new offset into the document.
* The new offset into the document.
* @see #getOffset() * @see #getOffset()
*/ */
public void setOffset(int offset) { public void setOffset(int offset) {
this.offset = offset; this.offset = offset;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -817,7 +785,7 @@ public class TokenImpl implements Token {
public void setType(int type) { public void setType(int type) {
this.type = type; this.type = type;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -833,7 +801,7 @@ public class TokenImpl implements Token {
} }
return false; return false;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -841,18 +809,20 @@ public class TokenImpl implements Token {
public int tokenToDocument(int pos) { public int tokenToDocument(int pos) {
return pos + (getOffset() - textOffset); return pos + (getOffset() - textOffset);
} }
/** /**
* Returns this token as a <code>String</code>, which is useful for * Returns this token as a <code>String</code>, which is useful for debugging.
* debugging.
* *
* @return A string describing this token. * @return A string describing this token.
*/ */
@Override @Override
public String toString() { public String toString() {
return "[Token: " + (getType() == Token.NULL ? "<null token>" return "[Token: "
: "text: '" + (text == null ? "<null>" : getLexeme() + "'; " + "offset: " + getOffset() + "; type: " + getType() + "; " + (getType() == Token.NULL ? "<null token>"
+ "isPaintable: " + isPaintable() + "; nextToken==null: " + (nextToken == null))) : "text: '" + (text == null ? "<null>"
: getLexeme() + "'; " + "offset: " + getOffset() + "; type: " + getType() + "; "
+ "isPaintable: " + isPaintable() + "; nextToken==null: "
+ (nextToken == null)))
+ "]"; + "]";
} }
} }
+57 -67
View File
@@ -9,12 +9,13 @@ import javax.swing.text.TabExpander;
* @version 1.0 * @version 1.0
*/ */
public final class TokenUtils { public final class TokenUtils {
private TokenUtils() {} private TokenUtils() {
}
/** /**
* Modifies the passed-in token list to start at the specified offset. * Modifies the passed-in token list to start at the specified offset. For
* For example, if the token list covered positions 20-60 in the document * example, if the token list covered positions 20-60 in the document
* (inclusive) like so: * (inclusive) like so:
* *
* <pre> * <pre>
@@ -22,50 +23,46 @@ public final class TokenUtils {
* 20 30 31 40 41 50 51 60 * 20 30 31 40 41 50 51 60
* </pre> * </pre>
* *
* and you used this method to make the token list start at position 44, * and you used this method to make the token list start at position 44, then
* then the token list would be modified to be the following: * the token list would be modified to be the following:
* *
* <pre> * <pre>
* [part-of-old-token3] -&gt; [token4] * [part-of-old-token3] -&gt; [token4]
* 44 50 51 60 * 44 50 51 60
* </pre> * </pre>
* *
* Tokens that come before the specified position are forever lost, and * Tokens that come before the specified position are forever lost, and the
* the token containing that position is made to begin at that position if * token containing that position is made to begin at that position if
* necessary. All token types remain the same as they were originally. * necessary. All token types remain the same as they were originally.
* <p> * <p>
* *
* This method can be useful if you are only interested in part of a token * This method can be useful if you are only interested in part of a token list
* list (i.e., the line it represents), but you don't want to modify the * (i.e., the line it represents), but you don't want to modify the token list
* token list yourself. * yourself.
* *
* @param tokenList * @param tokenList The list to make start at the specified position. This
* The list to make start at the specified position. * parameter is modified.
* This parameter is modified. * @param pos The position at which the new token list is to start. If
* @param pos * this position is not in the passed-in token list, returned
* The position at which the new token list is to start. If * token list will either be <code>null</code> or the
* this position is not in the passed-in token list, * unpaintable token(s) at the end of the passed-in token list.
* returned token list will either be <code>null</code> or the * @param e How to expand tabs.
* unpaintable token(s) at the end of the passed-in token list. * @param textArea The text area from which the token list came.
* @param e * @param x0 The initial x-pixel position of the old token list.
* How to expand tabs.
* @param textArea
* The text area from which the token list came.
* @param x0
* The initial x-pixel position of the old token list.
* @return Information about the "sub" token list. This will be * @return Information about the "sub" token list. This will be
* <code>null</code> if <code>pos</code> was not a valid offset * <code>null</code> if <code>pos</code> was not a valid offset into the
* into the token list. * token list.
* @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float, * @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float,
* TokenImpl) * TokenImpl)
*/ */
public static TokenSubList getSubTokenList(Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea, float x0) { public static TokenSubList getSubTokenList(Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea,
float x0) {
return getSubTokenList(tokenList, pos, e, textArea, x0, null); return getSubTokenList(tokenList, pos, e, textArea, x0, null);
} }
/** /**
* Modifies the passed-in token list to start at the specified offset. * Modifies the passed-in token list to start at the specified offset. For
* For example, if the token list covered positions 20-60 in the document * example, if the token list covered positions 20-60 in the document
* (inclusive) like so: * (inclusive) like so:
* *
* <pre> * <pre>
@@ -73,48 +70,42 @@ public final class TokenUtils {
* 20 30 31 40 41 50 51 60 * 20 30 31 40 41 50 51 60
* </pre> * </pre>
* *
* and you used this method to make the token list start at position 44, * and you used this method to make the token list start at position 44, then
* then the token list would be modified to be the following: * the token list would be modified to be the following:
* *
* <pre> * <pre>
* [part-of-old-token3] -&gt; [token4] * [part-of-old-token3] -&gt; [token4]
* 44 50 51 60 * 44 50 51 60
* </pre> * </pre>
* *
* Tokens that come before the specified position are forever lost, and * Tokens that come before the specified position are forever lost, and the
* the token containing that position is made to begin at that position if * token containing that position is made to begin at that position if
* necessary. All token types remain the same as they were originally. * necessary. All token types remain the same as they were originally.
* <p> * <p>
* *
* This method can be useful if you are only interested in part of a token * This method can be useful if you are only interested in part of a token list
* list (i.e., the line it represents), but you don't want to modify the * (i.e., the line it represents), but you don't want to modify the token list
* token list yourself. * yourself.
* *
* @param tokenList * @param tokenList The list to make start at the specified position. This
* The list to make start at the specified position. * parameter is modified.
* This parameter is modified. * @param pos The position at which the new token list is to start. If
* @param pos * this position is not in the passed-in token list, returned
* The position at which the new token list is to start. If * token list will either be <code>null</code> or the
* this position is not in the passed-in token list, * unpaintable token(s) at the end of the passed-in token list.
* returned token list will either be <code>null</code> or the * @param e How to expand tabs.
* unpaintable token(s) at the end of the passed-in token list. * @param textArea The text area from which the token list came.
* @param e * @param x0 The initial x-pixel position of the old token list.
* How to expand tabs. * @param tempToken A temporary token to use when creating the token list
* @param textArea * result. This may be <code>null</code> but callers can pass
* The text area from which the token list came. * in a "buffer" token for performance if desired.
* @param x0
* The initial x-pixel position of the old token list.
* @param tempToken
* A temporary token to use when creating the token list
* result. This may be <code>null</code> but callers can pass in
* a "buffer" token for performance if desired.
* @return Information about the "sub" token list. This will be * @return Information about the "sub" token list. This will be
* <code>null</code> if <code>pos</code> was not a valid offset * <code>null</code> if <code>pos</code> was not a valid offset into the
* into the token list. * token list.
* @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float) * @see #getSubTokenList(Token, int, TabExpander, RSyntaxTextArea, float)
*/ */
public static TokenSubList getSubTokenList(Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea, float x0, public static TokenSubList getSubTokenList(Token tokenList, int pos, TabExpander e, final RSyntaxTextArea textArea,
TokenImpl tempToken) { float x0, TokenImpl tempToken) {
if (tempToken == null) { if (tempToken == null) {
tempToken = new TokenImpl(); tempToken = new TokenImpl();
} }
@@ -142,23 +133,22 @@ public final class TokenUtils {
return new TokenSubList(tokenList, x0); return new TokenSubList(tokenList, x0);
// return null; // return null;
} }
/** /**
* A sub-list of tokens. * A sub-list of tokens.
*/ */
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
public static class TokenSubList { public static class TokenSubList {
/** /**
* The "sub" token list. * The "sub" token list.
*/ */
public Token tokenList; public Token tokenList;
/** /**
* The width, in pixels, of the part of the token list "removed from * The width, in pixels, of the part of the token list "removed from the front."
* the front." This way, you know the x-offset of the "new" token list. * This way, you know the x-offset of the "new" token list.
*/ */
public float x; public float x;
public TokenSubList(Token tokenList, float x) { public TokenSubList(Token tokenList, float x) {
this.tokenList = tokenList; this.tokenList = tokenList;
this.x = x; this.x = x;
@@ -34,34 +34,32 @@ import javax.swing.event.MouseInputAdapter;
* @author Robert Futrell * @author Robert Futrell
* @version 1.0 * @version 1.0
*/ */
@SuppressWarnings({ "checkstyle:magicnumber" })
class SizeGrip extends JPanel { class SizeGrip extends JPanel {
/** /**
* The size grip to use if we're on OS X. * The size grip to use if we're on OS X.
*/ */
private transient Image osxSizeGrip; private transient Image osxSizeGrip;
SizeGrip() { SizeGrip() {
MouseHandler adapter = new MouseHandler(); MouseHandler adapter = new MouseHandler();
addMouseListener(adapter); addMouseListener(adapter);
addMouseMotionListener(adapter); addMouseMotionListener(adapter);
setPreferredSize(new Dimension(16, 16)); setPreferredSize(new Dimension(16, 16));
} }
/** /**
* Overridden to ensure that the cursor for this component is appropriate * Overridden to ensure that the cursor for this component is appropriate for
* for the orientation. * the orientation.
* *
* @param o * @param o The new orientation.
* The new orientation.
*/ */
@Override @Override
public void applyComponentOrientation(ComponentOrientation o) { public void applyComponentOrientation(ComponentOrientation o) {
possiblyFixCursor(o.isLeftToRight()); possiblyFixCursor(o.isLeftToRight());
super.applyComponentOrientation(o); super.applyComponentOrientation(o);
} }
/** /**
* Creates and returns the OS X size grip image. * Creates and returns the OS X size grip image.
* *
@@ -93,12 +91,11 @@ class SizeGrip extends JPanel {
} }
return image; return image;
} }
/** /**
* Paints this panel. * Paints this panel.
* *
* @param g * @param g The graphics context.
* The graphics context.
*/ */
@Override @Override
protected void paintComponent(Graphics g) { protected void paintComponent(Graphics g) {
@@ -146,13 +143,12 @@ class SizeGrip extends JPanel {
g.fillRect(2, height - 9, 2, 2); g.fillRect(2, height - 9, 2, 2);
} }
} }
/** /**
* Ensures that the cursor for this component is appropriate for the * Ensures that the cursor for this component is appropriate for the
* orientation. * orientation.
* *
* @param ltr * @param ltr Whether the current component orientation is LTR.
* Whether the current component orientation is LTR.
*/ */
protected void possiblyFixCursor(boolean ltr) { protected void possiblyFixCursor(boolean ltr) {
int cursor = Cursor.NE_RESIZE_CURSOR; int cursor = Cursor.NE_RESIZE_CURSOR;
@@ -163,7 +159,7 @@ class SizeGrip extends JPanel {
setCursor(Cursor.getPredefinedCursor(cursor)); setCursor(Cursor.getPredefinedCursor(cursor));
} }
} }
@Override @Override
public void updateUI() { public void updateUI() {
super.updateUI(); super.updateUI();
@@ -177,23 +173,21 @@ class SizeGrip extends JPanel {
osxSizeGrip = null; osxSizeGrip = null;
} }
} }
/** /**
* Listens for mouse events on this panel and resizes the parent window * Listens for mouse events on this panel and resizes the parent window
* appropriately. * appropriately.
*/ */
private class MouseHandler extends MouseInputAdapter { private class MouseHandler extends MouseInputAdapter {
/* /*
* NOTE: We use SwingUtilities.convertPointToScreen() instead of just * NOTE: We use SwingUtilities.convertPointToScreen() instead of just using the
* using * locations relative to the corner component because the latter proved buggy -
* the locations relative to the corner component because the latter * stretch the window too wide and some kind of arithmetic error started
* proved * happening somewhere - our window would grow way too large.
* buggy - stretch the window too wide and some kind of arithmetic error
* started happening somewhere - our window would grow way too large.
*/ */
private Point origPos; private Point origPos;
@Override @Override
public void mouseDragged(MouseEvent e) { public void mouseDragged(MouseEvent e) {
Point newPos = e.getPoint(); Point newPos = e.getPoint();
@@ -223,13 +217,13 @@ class SizeGrip extends JPanel {
} }
origPos.setLocation(newPos); origPos.setLocation(newPos);
} }
@Override @Override
public void mousePressed(MouseEvent e) { public void mousePressed(MouseEvent e) {
origPos = e.getPoint(); origPos = e.getPoint();
SwingUtilities.convertPointToScreen(origPos, SizeGrip.this); SwingUtilities.convertPointToScreen(origPos, SizeGrip.this);
} }
@Override @Override
public void mouseReleased(MouseEvent e) { public void mouseReleased(MouseEvent e) {
origPos = null; origPos = null;
@@ -24,21 +24,19 @@ import javax.swing.text.DefaultEditorKit;
* <li>INSERT key toggles between insert and overwrite modes. * <li>INSERT key toggles between insert and overwrite modes.
* <li>Ctrl+DELETE key deletes all text between the caret and the end of the * <li>Ctrl+DELETE key deletes all text between the caret and the end of the
* current line. * current line.
* <li>Ctrl+Shift+Up and Ctrl+Shift+Down move the current line up and * <li>Ctrl+Shift+Up and Ctrl+Shift+Down move the current line up and down,
* down, respectively. * respectively.
* <li>Ctrl+J joins lines. * <li>Ctrl+J joins lines.
* <li>Ctrl+Z is undo and Ctrl+Y is redo. * <li>Ctrl+Z is undo and Ctrl+Y is redo.
* <li>Ctrl+Up and Ctrl+Down shift the visible area of the text area up and * <li>Ctrl+Up and Ctrl+Down shift the visible area of the text area up and down
* down one line, respectively. * one line, respectively.
* <li>F2 and Shift+F2 moves to the next and previous bookmarks, * <li>F2 and Shift+F2 moves to the next and previous bookmarks, respectively.
* respectively.
* <li>Ctrl+F2 toggles whether a bookmark is on the current line. * <li>Ctrl+F2 toggles whether a bookmark is on the current line.
* <li>etc. * <li>etc.
* </ul> * </ul>
*/ */
@SuppressWarnings({ "checkstyle:linelength" })
public class RTADefaultInputMap extends InputMap { public class RTADefaultInputMap extends InputMap {
/** /**
* Constructs the default input map for an <code>RTextArea</code>. * Constructs the default input map for an <code>RTextArea</code>.
*/ */
@@ -50,12 +48,14 @@ public class RTADefaultInputMap extends InputMap {
int shift = InputEvent.SHIFT_MASK; int shift = InputEvent.SHIFT_MASK;
boolean isOSX = RTextArea.isOSX(); boolean isOSX = RTextArea.isOSX();
int moveByWordMod = isOSX ? alt : defaultModifier; int moveByWordMod = isOSX ? alt : defaultModifier;
put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), isOSX ? DefaultEditorKit.beginAction : DefaultEditorKit.beginLineAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
isOSX ? DefaultEditorKit.beginAction : DefaultEditorKit.beginLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, shift), put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, shift),
isOSX ? DefaultEditorKit.selectionBeginAction : DefaultEditorKit.selectionBeginLineAction); isOSX ? DefaultEditorKit.selectionBeginAction : DefaultEditorKit.selectionBeginLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, defaultModifier), DefaultEditorKit.beginAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, defaultModifier), DefaultEditorKit.beginAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, defaultModifier | shift), DefaultEditorKit.selectionBeginAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, defaultModifier | shift), DefaultEditorKit.selectionBeginAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), isOSX ? DefaultEditorKit.endAction : DefaultEditorKit.endLineAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0),
isOSX ? DefaultEditorKit.endAction : DefaultEditorKit.endLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_END, shift), put(KeyStroke.getKeyStroke(KeyEvent.VK_END, shift),
isOSX ? DefaultEditorKit.selectionEndAction : DefaultEditorKit.selectionEndLineAction); isOSX ? DefaultEditorKit.selectionEndAction : DefaultEditorKit.selectionEndLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_END, defaultModifier), DefaultEditorKit.endAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_END, defaultModifier), DefaultEditorKit.endAction);
@@ -63,7 +63,8 @@ public class RTADefaultInputMap extends InputMap {
put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), DefaultEditorKit.backwardAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), DefaultEditorKit.backwardAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, shift), DefaultEditorKit.selectionBackwardAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, shift), DefaultEditorKit.selectionBackwardAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, moveByWordMod), DefaultEditorKit.previousWordAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, moveByWordMod), DefaultEditorKit.previousWordAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, moveByWordMod | shift), DefaultEditorKit.selectionPreviousWordAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, moveByWordMod | shift),
DefaultEditorKit.selectionPreviousWordAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), DefaultEditorKit.downAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), DefaultEditorKit.downAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, shift), DefaultEditorKit.selectionDownAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, shift), DefaultEditorKit.selectionDownAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, defaultModifier), RTextAreaEditorKit.rtaScrollDownAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, defaultModifier), RTextAreaEditorKit.rtaScrollDownAction);
@@ -78,10 +79,12 @@ public class RTADefaultInputMap extends InputMap {
put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, alt), RTextAreaEditorKit.rtaLineUpAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, alt), RTextAreaEditorKit.rtaLineUpAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), DefaultEditorKit.pageUpAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), DefaultEditorKit.pageUpAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, shift), RTextAreaEditorKit.rtaSelectionPageUpAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, shift), RTextAreaEditorKit.rtaSelectionPageUpAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, defaultModifier | shift), RTextAreaEditorKit.rtaSelectionPageLeftAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, defaultModifier | shift),
RTextAreaEditorKit.rtaSelectionPageLeftAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), DefaultEditorKit.pageDownAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0), DefaultEditorKit.pageDownAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, shift), RTextAreaEditorKit.rtaSelectionPageDownAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, shift), RTextAreaEditorKit.rtaSelectionPageDownAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, defaultModifier | shift), RTextAreaEditorKit.rtaSelectionPageRightAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, defaultModifier | shift),
RTextAreaEditorKit.rtaSelectionPageRightAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_CUT, 0), DefaultEditorKit.cutAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_CUT, 0), DefaultEditorKit.cutAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_COPY, 0), DefaultEditorKit.copyAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_COPY, 0), DefaultEditorKit.copyAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_PASTE, 0), DefaultEditorKit.pasteAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_PASTE, 0), DefaultEditorKit.pasteAction);
@@ -99,7 +102,8 @@ public class RTADefaultInputMap extends InputMap {
put(KeyStroke.getKeyStroke(KeyEvent.VK_D, defaultModifier), RTextAreaEditorKit.rtaDeleteLineAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_D, defaultModifier), RTextAreaEditorKit.rtaDeleteLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_J, defaultModifier), RTextAreaEditorKit.rtaJoinLinesAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_J, defaultModifier), RTextAreaEditorKit.rtaJoinLinesAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, shift), DefaultEditorKit.deletePrevCharAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, shift), DefaultEditorKit.deletePrevCharAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, defaultModifier), RTextAreaEditorKit.rtaDeletePrevWordAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, defaultModifier),
RTextAreaEditorKit.rtaDeletePrevWordAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), DefaultEditorKit.insertTabAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), DefaultEditorKit.insertTabAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), DefaultEditorKit.insertBreakAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), DefaultEditorKit.insertBreakAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, shift), DefaultEditorKit.insertBreakAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, shift), DefaultEditorKit.insertBreakAction);
@@ -114,8 +118,10 @@ public class RTADefaultInputMap extends InputMap {
if (isOSX) { if (isOSX) {
put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, defaultModifier), DefaultEditorKit.beginLineAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, defaultModifier), DefaultEditorKit.beginLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, defaultModifier), DefaultEditorKit.endLineAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, defaultModifier), DefaultEditorKit.endLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, defaultModifier | shift), DefaultEditorKit.selectionBeginLineAction); put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, defaultModifier | shift),
put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, defaultModifier | shift), DefaultEditorKit.selectionEndLineAction); DefaultEditorKit.selectionBeginLineAction);
put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, defaultModifier | shift),
DefaultEditorKit.selectionEndLineAction);
} }
/* NOTE: Currently, macros aren't part of the default input map for */ /* NOTE: Currently, macros aren't part of the default input map for */
/* RTextArea, as they display their own popup windows, etc. which */ /* RTextArea, as they display their own popup windows, etc. which */
@@ -132,10 +138,10 @@ public class RTADefaultInputMap extends InputMap {
* RTextAreaEditorKit.rtaPlaybackLastMacroAction); * RTextAreaEditorKit.rtaPlaybackLastMacroAction);
*/ */
} }
/** /**
* Returns the default modifier key for a system. For example, on Windows * Returns the default modifier key for a system. For example, on Windows this
* this would be the CTRL key (<code>InputEvent.CTRL_MASK</code>). * would be the CTRL key (<code>InputEvent.CTRL_MASK</code>).
* *
* @return The default modifier key. * @return The default modifier key.
*/ */
@@ -37,33 +37,29 @@ import javax.swing.text.Position;
* @author Robert Futrell * @author Robert Futrell
* @version 0.1 * @version 0.1
*/ */
@SuppressWarnings("deprecation")
public class RTATextTransferHandler extends TransferHandler { public class RTATextTransferHandler extends TransferHandler {
private JTextComponent exportComp; private JTextComponent exportComp;
private boolean shouldRemove; private boolean shouldRemove;
private int p0; private int p0;
private int p1; private int p1;
private boolean withinSameComponent; private boolean withinSameComponent;
/** /**
* Try to find a flavor that can be used to import a Transferable to a * Try to find a flavor that can be used to import a Transferable to a specified
* specified text component. * text component. The set of usable flavors are tried in the following order:
* The set of usable flavors are tried in the following order:
* <ol> * <ol>
* <li>First, an attempt is made to find a flavor matching the content * <li>First, an attempt is made to find a flavor matching the content tyep of
* tyep of the EditorKit for the component. * the EditorKit for the component.
* <li>Second, an attempt to find a text/plain flavor is made. * <li>Second, an attempt to find a text/plain flavor is made.
* <li>Third, an attempt to find a flavor representing a String * <li>Third, an attempt to find a flavor representing a String reference in the
* reference in the same VM is made. * same VM is made.
* <li>Lastly, DataFlavor.stringFlavor is searched for. * <li>Lastly, DataFlavor.stringFlavor is searched for.
* </ol> * </ol>
* *
* @param flavors * @param flavors The flavors to check if c will accept them.
* The flavors to check if c will accept them. * @param c The text component to see whether it will accept any of the
* @param c * specified data flavors as input.
* The text component to see whether it will accept any of the
* specified data flavors as input.
*/ */
protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) { protected DataFlavor getImportFlavor(DataFlavor[] flavors, JTextComponent c) {
DataFlavor refFlavor = null; DataFlavor refFlavor = null;
@@ -86,7 +82,7 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return null; return null;
} }
/** /**
* Import the given stream data into the text component. * Import the given stream data into the text component.
*/ */
@@ -105,38 +101,38 @@ public class RTATextTransferHandler extends TransferHandler {
last = 0; last = 0;
for (int counter = 0; counter < nch; counter++) { for (int counter = 0; counter < nch; counter++) {
switch (buff[counter]) { switch (buff[counter]) {
case '\r': case '\r':
if (lastWasCR) { if (lastWasCR) {
if (counter == 0) { if (counter == 0) {
sbuff.append('\n'); sbuff.append('\n');
} else {
buff[counter - 1] = '\n';
}
} else { } else {
lastWasCR = true; buff[counter - 1] = '\n';
} }
break; } else {
case '\n': lastWasCR = true;
if (lastWasCR) { }
if (counter > (last + 1)) { break;
sbuff.append(buff, last, counter - last - 1); case '\n':
} if (lastWasCR) {
// else nothing to do, can skip \r, next write will if (counter > (last + 1)) {
// write \n sbuff.append(buff, last, counter - last - 1);
lastWasCR = false;
last = counter;
} }
break; // else nothing to do, can skip \r, next write will
default: // write \n
if (lastWasCR) { lastWasCR = false;
if (counter == 0) { last = counter;
sbuff.append('\n'); }
} else { break;
buff[counter - 1] = '\n'; default:
} if (lastWasCR) {
lastWasCR = false; if (counter == 0) {
sbuff.append('\n');
} else {
buff[counter - 1] = '\n';
} }
break; lastWasCR = false;
}
break;
} // End fo switch (buff[counter]). } // End fo switch (buff[counter]).
} // End of for (int counter = 0; counter < nch; counter++). } // End of for (int counter = 0; counter < nch; counter++).
if (last < nch) { if (last < nch) {
@@ -157,16 +153,15 @@ public class RTATextTransferHandler extends TransferHandler {
} }
c.replaceSelection(sbuff != null ? sbuff.toString() : ""); c.replaceSelection(sbuff != null ? sbuff.toString() : "");
} }
/** /**
* This is the type of transfer actions supported by the source. Some * This is the type of transfer actions supported by the source. Some models are
* models are not mutable, so a transfer operation of COPY only should * not mutable, so a transfer operation of COPY only should be advertised in
* be advertised in that case. * that case.
* *
* @param c * @param c The component holding the data to be transfered. This argument is
* The component holding the data to be transfered. This * provided to enable sharing of TransferHandlers by multiple
* argument is provided to enable sharing of TransferHandlers by * components.
* multiple components.
* @return If the text component is editable, COPY_OR_MOVE is returned, * @return If the text component is editable, COPY_OR_MOVE is returned,
* otherwise just COPY is allowed. * otherwise just COPY is allowed.
*/ */
@@ -178,14 +173,13 @@ public class RTATextTransferHandler extends TransferHandler {
return COPY; return COPY;
} }
} }
/** /**
* Create a Transferable to use as the source for a data transfer. * Create a Transferable to use as the source for a data transfer.
* *
* @param comp * @param comp The component holding the data to be transfered. This argument is
* The component holding the data to be transfered. This * provided to enable sharing of TransferHandlers by multiple
* argument is provided to enable sharing of TransferHandlers by * components.
* multiple components.
* @return The representation of the data to be transfered. * @return The representation of the data to be transfered.
* *
*/ */
@@ -197,18 +191,15 @@ public class RTATextTransferHandler extends TransferHandler {
p1 = exportComp.getSelectionEnd(); p1 = exportComp.getSelectionEnd();
return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null; return (p0 != p1) ? (new TextTransferable(exportComp, p0, p1)) : null;
} }
/** /**
* This method is called after data has been exported. This method should * This method is called after data has been exported. This method should remove
* remove the data that was transfered if the action was MOVE. * the data that was transfered if the action was MOVE.
* *
* @param source * @param source The component that was the source of the data.
* The component that was the source of the data. * @param data The data that was transferred or possibly null if the action is
* @param data * <code>NONE</code>.
* The data that was transferred or possibly null * @param action The actual action that was performed.
* if the action is <code>NONE</code>.
* @param action
* The actual action that was performed.
*/ */
@Override @Override
protected void exportDone(JComponent source, Transferable data, int action) { protected void exportDone(JComponent source, Transferable data, int action) {
@@ -227,18 +218,15 @@ public class RTATextTransferHandler extends TransferHandler {
ClipboardHistory.get().add(((TextTransferable) data).getPlainData()); ClipboardHistory.get().add(((TextTransferable) data).getPlainData());
} }
} }
/** /**
* This method causes a transfer to a component from a clipboard or a * This method causes a transfer to a component from a clipboard or a DND drop
* DND drop operation. The Transferable represents the data to be * operation. The Transferable represents the data to be imported into the
* imported into the component. * component.
* *
* @param comp * @param comp The component to receive the transfer. This argument is provided
* The component to receive the transfer. This * to enable sharing of TransferHandlers by multiple components.
* argument is provided to enable sharing of TransferHandlers by * @param t The data to import
* multiple components.
* @param t
* The data to import
* @return <code>true</code> iff the data was inserted into the component. * @return <code>true</code> iff the data was inserted into the component.
*/ */
@Override @Override
@@ -272,17 +260,15 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return imported; return imported;
} }
/** /**
* This method indicates if a component would accept an import of the * This method indicates if a component would accept an import of the given set
* given set of data flavors prior to actually attempting to import it. * of data flavors prior to actually attempting to import it.
* *
* @param comp * @param comp The component to receive the transfer. This argument is
* The component to receive the transfer. This argument is * provided to enable sharing of TransferHandlers by multiple
* provided to enable sharing of TransferHandlers by multiple * components.
* components. * @param flavors The data formats available.
* @param flavors
* The data formats available.
* @return <code>true</code> iff the data can be inserted. * @return <code>true</code> iff the data can be inserted.
*/ */
@Override @Override
@@ -293,19 +279,19 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return (getImportFlavor(flavors, c) != null); return (getImportFlavor(flavors, c) != null);
} }
/** /**
* A possible implementation of the Transferable interface for RTextAreas. * A possible implementation of the Transferable interface for RTextAreas.
*/ */
static class TextTransferable implements Transferable { static class TextTransferable implements Transferable {
private Position p0; private Position p0;
private Position p1; private Position p1;
private JTextComponent c; private JTextComponent c;
protected String plainData; protected String plainData;
private static DataFlavor[] stringFlavors; private static DataFlavor[] stringFlavors;
private static DataFlavor[] plainFlavors; private static DataFlavor[] plainFlavors;
TextTransferable(JTextComponent c, int start, int end) { TextTransferable(JTextComponent c, int start, int end) {
this.c = c; this.c = c;
Document doc = c.getDocument(); Document doc = c.getDocument();
@@ -313,31 +299,27 @@ public class RTATextTransferHandler extends TransferHandler {
p0 = doc.createPosition(start); p0 = doc.createPosition(start);
p1 = doc.createPosition(end); p1 = doc.createPosition(end);
plainData = c.getSelectedText(); plainData = c.getSelectedText();
} catch (BadLocationException ble) {} } catch (BadLocationException ble) {
}
} }
/** /**
* Fetch the data in a text/plain format. * Fetch the data in a text/plain format.
*/ */
protected String getPlainData() { protected String getPlainData() {
return plainData; return plainData;
} }
/** /**
* Returns an object which represents the data to be transferred. The * Returns an object which represents the data to be transferred. The class of
* class * the object returned is defined by the representation class of the flavor.
* of the object returned is defined by the representation class of the
* flavor.
* *
* @param flavor * @param flavor the requested flavor for the data
* the requested flavor for the data
* @see DataFlavor#getRepresentationClass * @see DataFlavor#getRepresentationClass
* @exception IOException * @exception IOException if the data is no longer available in
* if the data is no longer available * the requested flavor.
* in the requested flavor. * @exception UnsupportedFlavorException if the requested data flavor is not
* @exception UnsupportedFlavorException * supported.
* if the requested data flavor is
* not supported.
*/ */
@Override @Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
@@ -359,17 +341,13 @@ public class RTATextTransferHandler extends TransferHandler {
} }
throw new UnsupportedFlavorException(flavor); throw new UnsupportedFlavorException(flavor);
} }
/** /**
* Returns an array of DataFlavor objects indicating the flavors the * Returns an array of DataFlavor objects indicating the flavors the data can be
* data * provided in. The array should be ordered according to preference for
* can be provided in. The array should be ordered according to * providing the data (from most richly descriptive to least descriptive).
* preference
* for providing the data (from most richly descriptive to least
* descriptive).
* *
* @return an array of data flavors in which this data can be * @return an array of data flavors in which this data can be transferred
* transferred
*/ */
@Override @Override
public DataFlavor[] getTransferDataFlavors() { public DataFlavor[] getTransferDataFlavors() {
@@ -389,15 +367,13 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return flavors; return flavors;
} }
/** /**
* Returns whether or not the specified data flavor is supported for * Returns whether or not the specified data flavor is supported for this
* this object. * object.
* *
* @param flavor * @param flavor the requested flavor for the data
* the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported
* @return boolean indicating whether or not the data flavor is
* supported
*/ */
@Override @Override
public boolean isDataFlavorSupported(DataFlavor flavor) { public boolean isDataFlavorSupported(DataFlavor flavor) {
@@ -409,16 +385,13 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return false; return false;
} }
/** /**
* Returns whether or not the specified data flavor is an plain flavor * Returns whether or not the specified data flavor is an plain flavor that is
* that * supported.
* is supported.
* *
* @param flavor * @param flavor the requested flavor for the data
* the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported
* @return boolean indicating whether or not the data flavor is
* supported
*/ */
protected boolean isPlainFlavor(DataFlavor flavor) { protected boolean isPlainFlavor(DataFlavor flavor) {
DataFlavor[] flavors = plainFlavors; DataFlavor[] flavors = plainFlavors;
@@ -429,24 +402,21 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return false; return false;
} }
/** /**
* Should the plain text flavors be offered? If so, the method * Should the plain text flavors be offered? If so, the method getPlainData
* getPlainData should be implemented to provide something reasonable. * should be implemented to provide something reasonable.
*/ */
protected boolean isPlainSupported() { protected boolean isPlainSupported() {
return plainData != null; return plainData != null;
} }
/** /**
* Returns whether or not the specified data flavor is a String flavor * Returns whether or not the specified data flavor is a String flavor that is
* that * supported.
* is supported.
* *
* @param flavor * @param flavor the requested flavor for the data
* the requested flavor for the data * @return boolean indicating whether or not the data flavor is supported
* @return boolean indicating whether or not the data flavor is
* supported
*/ */
protected boolean isStringFlavor(DataFlavor flavor) { protected boolean isStringFlavor(DataFlavor flavor) {
DataFlavor[] flavors = stringFlavors; DataFlavor[] flavors = stringFlavors;
@@ -457,16 +427,17 @@ public class RTATextTransferHandler extends TransferHandler {
} }
return false; return false;
} }
void removeText() { void removeText() {
if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) { if ((p0 != null) && (p1 != null) && (p0.getOffset() != p1.getOffset())) {
try { try {
Document doc = c.getDocument(); Document doc = c.getDocument();
doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset()); doc.remove(p0.getOffset(), p1.getOffset() - p0.getOffset());
} catch (BadLocationException e) {} } catch (BadLocationException e) {
}
} }
} }
// Initialization of supported flavors. // Initialization of supported flavors.
static { static {
try { try {
File diff suppressed because it is too large. Load diff
@@ -26,8 +26,8 @@ import javax.swing.text.View;
import org.fife.ui.rsyntaxtextarea.DocumentRange; import org.fife.ui.rsyntaxtextarea.DocumentRange;
/** /**
* The highlighter implementation used by {@link RTextArea}s. It knows to * The highlighter implementation used by {@link RTextArea}s. It knows to always
* always paint "mark all" highlights below selection highlights. * paint "mark all" highlights below selection highlights.
* <p> * <p>
* *
* Most of this code is copied from javax.swing.text.DefaultHighlighter; * Most of this code is copied from javax.swing.text.DefaultHighlighter;
@@ -37,24 +37,23 @@ import org.fife.ui.rsyntaxtextarea.DocumentRange;
* @version 1.0 * @version 1.0
*/ */
public class RTextAreaHighlighter extends BasicHighlighter { public class RTextAreaHighlighter extends BasicHighlighter {
/** /**
* The text component we are the highlighter for. * The text component we are the highlighter for.
*/ */
protected RTextArea textArea; protected RTextArea textArea;
/** /**
* The "mark all" highlights (to be painted separately from other * The "mark all" highlights (to be painted separately from other highlights).
* highlights).
*/ */
private List<HighlightInfo> markAllHighlights; private List<HighlightInfo> markAllHighlights;
/** /**
* Constructor. * Constructor.
*/ */
public RTextAreaHighlighter() { public RTextAreaHighlighter() {
markAllHighlights = new ArrayList<HighlightInfo>(); markAllHighlights = new ArrayList<HighlightInfo>();
} }
/** /**
* Adds a special "marked occurrence" highlight. * Adds a special "marked occurrence" highlight.
* *
@@ -80,7 +79,7 @@ public class RTextAreaHighlighter extends BasicHighlighter {
mapper.damageRange(textArea, start, end); mapper.damageRange(textArea, start, end);
return i; return i;
} }
/** /**
* Removes all "mark all" highlights from the view. * Removes all "mark all" highlights from the view.
* *
@@ -95,7 +94,7 @@ public class RTextAreaHighlighter extends BasicHighlighter {
} }
markAllHighlights.clear(); markAllHighlights.clear();
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -104,20 +103,19 @@ public class RTextAreaHighlighter extends BasicHighlighter {
this.textArea = null; this.textArea = null;
markAllHighlights.clear(); markAllHighlights.clear();
} }
/** /**
* Returns the number of "mark all" highlights currently shown in the * Returns the number of "mark all" highlights currently shown in the editor.
* editor.
* *
* @return The "mark all" highlight count. * @return The "mark all" highlight count.
*/ */
public int getMarkAllHighlightCount() { public int getMarkAllHighlightCount() {
return markAllHighlights.size(); return markAllHighlights.size();
} }
/** /**
* Returns a list of "mark all" highlights in the text area. If there are * Returns a list of "mark all" highlights in the text area. If there are no
* no such highlights, this will be an empty list. * such highlights, this will be an empty list.
* *
* @return The list of "mark all" highlight ranges. * @return The list of "mark all" highlight ranges.
*/ */
@@ -131,7 +129,7 @@ public class RTextAreaHighlighter extends BasicHighlighter {
} }
return list; return list;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@@ -140,33 +138,27 @@ public class RTextAreaHighlighter extends BasicHighlighter {
super.install(c); super.install(c);
this.textArea = (RTextArea) c; this.textArea = (RTextArea) c;
} }
/** /**
* When leaf Views (such as LabelView) are rendering they should * When leaf Views (such as LabelView) are rendering they should call into this
* call into this method. If a highlight is in the given region it will * method. If a highlight is in the given region it will be drawn immediately.
* be drawn immediately.
* *
* @param g * @param g Graphics used to draw
* Graphics used to draw * @param lineStart starting offset of view
* @param lineStart * @param lineEnd ending offset of view
* starting offset of view * @param viewBounds Bounds of View
* @param lineEnd * @param editor JTextComponent
* ending offset of view * @param view View instance being rendered
* @param viewBounds
* Bounds of View
* @param editor
* JTextComponent
* @param view
* View instance being rendered
*/ */
@Override @Override
public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor, View view) { public void paintLayeredHighlights(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor,
View view) {
paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, markAllHighlights); paintListLayered(g, lineStart, lineEnd, viewBounds, editor, view, markAllHighlights);
super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view); super.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view);
} }
protected void paintListLayered(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor, View view, protected void paintListLayered(Graphics g, int lineStart, int lineEnd, Shape viewBounds, JTextComponent editor,
List<? extends HighlightInfo> highlights) { View view, List<? extends HighlightInfo> highlights) {
for (int i = highlights.size() - 1; i >= 0; i--) { for (int i = highlights.size() - 1; i >= 0; i--) {
HighlightInfo tag = highlights.get(i); HighlightInfo tag = highlights.get(i);
if (tag instanceof LayeredHighlightInfo) { if (tag instanceof LayeredHighlightInfo) {
@@ -175,13 +167,14 @@ public class RTextAreaHighlighter extends BasicHighlighter {
int highlightEnd = lhi.getEndOffset() + 1; // "+1" workaround int highlightEnd = lhi.getEndOffset() + 1; // "+1" workaround
// for Java // for Java
// highlight issues // highlight issues
if ((lineStart < highlightStart && lineEnd > highlightStart) || (lineStart >= highlightStart && lineStart < highlightEnd)) { if ((lineStart < highlightStart && lineEnd > highlightStart)
|| (lineStart >= highlightStart && lineStart < highlightEnd)) {
lhi.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view); lhi.paintLayeredHighlights(g, lineStart, lineEnd, viewBounds, editor, view);
} }
} }
} }
} }
protected void repaintListHighlight(HighlightInfo info) { protected void repaintListHighlight(HighlightInfo info) {
// Note: We're relying on implementation here, not interface. Yuck... // Note: We're relying on implementation here, not interface. Yuck...
if (info instanceof LayeredHighlightInfoImpl) { if (info instanceof LayeredHighlightInfoImpl) {
@@ -195,89 +188,85 @@ public class RTextAreaHighlighter extends BasicHighlighter {
// safeDamageRange(info.p0, info.p1); // safeDamageRange(info.p0, info.p1);
} }
} }
/** /**
* Information about a highlight being painted by this highlighter. * Information about a highlight being painted by this highlighter.
*/ */
public interface HighlightInfo extends Highlighter.Highlight {} public interface HighlightInfo extends Highlighter.Highlight {
}
/** /**
* Information about a layered highlight being painted by this highlighter. * Information about a layered highlight being painted by this highlighter.
*/ */
public interface LayeredHighlightInfo extends HighlightInfo { public interface LayeredHighlightInfo extends HighlightInfo {
/** /**
* Restricts the region based on the receivers offsets and messages * Restricts the region based on the receivers offsets and messages the painter
* the painter to paint the region. * to paint the region.
*/ */
void paintLayeredHighlights(Graphics g, int p0, int p1, Shape viewBounds, JTextComponent editor, View view); void paintLayeredHighlights(Graphics g, int p0, int p1, Shape viewBounds, JTextComponent editor, View view);
} }
/** /**
* A straightforward implementation of <code>HighlightInfo</code>. * A straightforward implementation of <code>HighlightInfo</code>.
*/ */
protected static class HighlightInfoImpl implements HighlightInfo { protected static class HighlightInfoImpl implements HighlightInfo {
private Position p0; private Position p0;
private Position p1; private Position p1;
private Highlighter.HighlightPainter painter; private Highlighter.HighlightPainter painter;
/** To be extended by subclasses. */ /** To be extended by subclasses. */
public Color getColor() { public Color getColor() {
return null; return null;
} }
@Override @Override
public int getStartOffset() { public int getStartOffset() {
return p0.getOffset(); return p0.getOffset();
} }
@Override @Override
public int getEndOffset() { public int getEndOffset() {
return p1.getOffset(); return p1.getOffset();
} }
@Override @Override
public Highlighter.HighlightPainter getPainter() { public Highlighter.HighlightPainter getPainter() {
return painter; return painter;
} }
public void setStartOffset(Position startOffset) { public void setStartOffset(Position startOffset) {
this.p0 = startOffset; this.p0 = startOffset;
} }
public void setEndOffset(Position endOffset) { public void setEndOffset(Position endOffset) {
this.p1 = endOffset; this.p1 = endOffset;
} }
public void setPainter(Highlighter.HighlightPainter painter) { public void setPainter(Highlighter.HighlightPainter painter) {
this.painter = painter; this.painter = painter;
} }
} }
/** /**
* A straightforward implementation of <code>HighlightInfo</code> for * A straightforward implementation of <code>HighlightInfo</code> for painting
* painting layered highlights. * layered highlights.
*/ */
@SuppressWarnings({ "checkstyle:visibilitymodifier" })
protected static class LayeredHighlightInfoImpl extends HighlightInfoImpl implements LayeredHighlightInfo { protected static class LayeredHighlightInfoImpl extends HighlightInfoImpl implements LayeredHighlightInfo {
/* /*
* NOTE: This implementation is a "hack" so typing at the "end" of the * NOTE: This implementation is a "hack" so typing at the "end" of the highlight
* highlight * does not extend it to include the newly-typed chars, which is the standard
* does not extend it to include the newly-typed chars, which is the * behavior of Swing Highlights. It assumes that the "p1" Position set is
* standard * actually 1 char too short, and will render the selection as if that "extra"
* behavior of Swing Highlights. It assumes that the "p1" Position set
* is
* actually 1 char too short, and will render the selection as if that
* "extra"
* char should be highlighted. * char should be highlighted.
*/ */
public int x; public int x;
public int y; public int y;
public int width; public int width;
public int height; public int height;
void union(Shape bounds) { void union(Shape bounds) {
if (bounds == null) { if (bounds == null) {
return; return;
@@ -297,12 +286,13 @@ public class RTextAreaHighlighter extends BasicHighlighter {
height -= y; height -= y;
} }
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
@Override @Override
public void paintLayeredHighlights(Graphics g, int p0, int p1, Shape viewBounds, JTextComponent editor, View view) { public void paintLayeredHighlights(Graphics g, int p0, int p1, Shape viewBounds, JTextComponent editor,
View view) {
int start = getStartOffset(); int start = getStartOffset();
int end = getEndOffset(); int end = getEndOffset();
end++; // Workaround for Java highlight issues end++; // Workaround for Java highlight issues
+236 -304
View File
@@ -22,9 +22,9 @@ import org.fife.ui.rsyntaxtextarea.DocumentRange;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities; import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
/** /**
* A singleton class that can perform advanced find/replace operations * A singleton class that can perform advanced find/replace operations in an
* in an {@link RTextArea}. Simply create a {@link SearchContext} and call * {@link RTextArea}. Simply create a {@link SearchContext} and call one of the
* one of the following methods: * following methods:
* *
* <ul> * <ul>
* <li>{@link #find(JTextArea, SearchContext)} * <li>{@link #find(JTextArea, SearchContext)}
@@ -38,25 +38,22 @@ import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
* @see SearchContext * @see SearchContext
*/ */
public final class SearchEngine { public final class SearchEngine {
/** /**
* Private constructor to prevent instantiation. * Private constructor to prevent instantiation.
*/ */
private SearchEngine() {} private SearchEngine() {
}
/** /**
* Finds the next instance of the string/regular expression specified * Finds the next instance of the string/regular expression specified from the
* from the caret position. If a match is found, it is selected in this * caret position. If a match is found, it is selected in this text area.
* text area.
* *
* @param textArea * @param textArea The text area in which to search.
* The text area in which to search. * @param context What to search for and all search options.
* @param context
* What to search for and all search options.
* @return The result of the operation. * @return The result of the operation.
* @throws PatternSyntaxException * @throws PatternSyntaxException If this is a regular expression search but the
* If this is a regular expression search * search text is an invalid regular expression.
* but the search text is an invalid regular expression.
* @see #replace(RTextArea, SearchContext) * @see #replace(RTextArea, SearchContext)
* @see #replaceAll(RTextArea, SearchContext) * @see #replaceAll(RTextArea, SearchContext)
*/ */
@@ -106,21 +103,17 @@ public final class SearchEngine {
result.setMarkedCount(markAllCount); result.setMarkedCount(markAllCount);
return result; return result;
} }
/** /**
* Finds the next instance of the string/regular expression specified * Finds the next instance of the string/regular expression specified from the
* from the caret position. If a match is found, it is selected in this * caret position. If a match is found, it is selected in this text area.
* text area.
* *
* @param findIn * @param findIn The text to search in.
* The text to search in. * @param context The search context.
* @param context * @return The result of the operation. "Mark all" will always be zero, since
* The search context. * this method does not perform that operation.
* @return The result of the operation. "Mark all" will always be zero, * @throws PatternSyntaxException If this is a regular expression search but the
* since this method does not perform that operation. * search text is an invalid regular expression.
* @throws PatternSyntaxException
* If this is a regular expression search
* but the search text is an invalid regular expression.
*/ */
private static SearchResult findImpl(String findIn, SearchContext context) { private static SearchResult findImpl(String findIn, SearchContext context) {
String text = context.getSearchFor(); String text = context.getSearchFor();
@@ -140,7 +133,8 @@ public final class SearchEngine {
Point regExPos = null; Point regExPos = null;
int start = 0; int start = 0;
do { do {
regExPos = getNextMatchPosRegEx(text, findIn.substring(start), forward, context.getMatchCase(), context.getWholeWord()); regExPos = getNextMatchPosRegEx(text, findIn.substring(start), forward, context.getMatchCase(),
context.getWholeWord());
if (regExPos != null) { if (regExPos != null) {
if (regExPos.x != regExPos.y) { if (regExPos.x != regExPos.y) {
regExPos.translate(start, start); regExPos.translate(start, start);
@@ -157,20 +151,17 @@ public final class SearchEngine {
} }
return new SearchResult(); return new SearchResult();
} }
/** /**
* Returns a <code>CharSequence</code> for a text area that doesn't make a * Returns a <code>CharSequence</code> for a text area that doesn't make a copy
* copy of its contents for iteration. This conserves memory but is likely * of its contents for iteration. This conserves memory but is likely just a tad
* just a tad slower. * slower.
* *
* @param textArea * @param textArea The text area whose document is the basis for the
* The text area whose document is the basis for the * <code>CharSequence</code>.
* <code>CharSequence</code>. * @param start The starting offset of the sequence (or ending offset if
* @param start * <code>forward</code> is <code>false</code>).
* The starting offset of the sequence (or ending offset if * @param forward Whether we're searching forward or backward.
* <code>forward</code> is <code>false</code>).
* @param forward
* Whether we're searching forward or backward.
* @return The character sequence. * @return The character sequence.
*/ */
private static CharSequence getFindInCharSequence(RTextArea textArea, int start, boolean forward) { private static CharSequence getFindInCharSequence(RTextArea textArea, int start, boolean forward) {
@@ -186,10 +177,10 @@ public final class SearchEngine {
} }
return new RDocumentCharSequence(doc, csStart, csEnd); return new RDocumentCharSequence(doc, csStart, csEnd);
} }
/** /**
* Returns the text in which to search, as a string. This is used * Returns the text in which to search, as a string. This is used internally to
* internally to grab the smallest buffer possible in which to search. * grab the smallest buffer possible in which to search.
*/ */
private static String getFindInText(JTextArea textArea, int start, boolean forward) { private static String getFindInText(JTextArea textArea, int start, boolean forward) {
// Be smart about the text we grab to search in. We grab more than // Be smart about the text we grab to search in. We grab more than
@@ -208,42 +199,36 @@ public final class SearchEngine {
} }
return findIn; return findIn;
} }
/** /**
* This method is called internally by * This method is called internally by <code>getNextMatchPosRegExImpl</code> and
* <code>getNextMatchPosRegExImpl</code> and is used to get the locations * is used to get the locations of all regular-expression matches, and possibly
* of all regular-expression matches, and possibly their replacement * their replacement strings.
* strings.
* <p> * <p>
* *
* Returns either: * Returns either:
* <ul> * <ul>
* <li>A list of points representing the starting and ending positions * <li>A list of points representing the starting and ending positions of all
* of all matches returned by the specified matcher, or * matches returned by the specified matcher, or
* <li>A list of <code>RegExReplaceInfo</code>s describing the matches * <li>A list of <code>RegExReplaceInfo</code>s describing the matches found by
* found by the matcher and the replacement strings for each. * the matcher and the replacement strings for each.
* </ul> * </ul>
* *
* If <code>replacement</code> is <code>null</code>, this method call is * If <code>replacement</code> is <code>null</code>, this method call is assumed
* assumed to be part of a "find" operation and points are returned. If * to be part of a "find" operation and points are returned. If if is
* if is non-<code>null</code>, it is assumed to be part of a "replace" * non-<code>null</code>, it is assumed to be part of a "replace" operation and
* operation and the <code>RegExReplaceInfo</code>s are returned. * the <code>RegExReplaceInfo</code>s are returned.
* <p> * <p>
* *
* @param m * @param m The matcher.
* The matcher. * @param replaceStr The string to replace matches with. This is a "template"
* @param replaceStr * string and can contain captured group references in the
* The string to replace matches with. This is a * form "<code>${digit}</code>".
* "template" string and can contain captured group references in
* the form "<code>${digit}</code>".
* @return A list of result objects. * @return A list of result objects.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException If <code>replaceStr</code> references an
* If <code>replaceStr</code> references * invalid group (less than zero or greater
* an invalid group (less than zero or greater than the number * than the number of groups matched).
* of
* groups matched).
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" })
private static List getMatches(Matcher m, String replaceStr) { private static List getMatches(Matcher m, String replaceStr) {
ArrayList matches = new ArrayList(); ArrayList matches = new ArrayList();
while (m.find()) { while (m.find()) {
@@ -256,63 +241,53 @@ public final class SearchEngine {
} }
return matches; return matches;
} }
/** /**
* Searches <code>searchIn</code> for an occurrence of * Searches <code>searchIn</code> for an occurrence of <code>searchFor</code>
* <code>searchFor</code> either forwards or backwards, matching * either forwards or backwards, matching case or not.
* case or not.
* <p> * <p>
* *
* Most clients will have no need to call this method directly. * Most clients will have no need to call this method directly.
* *
* @param searchFor * @param searchFor The string to look for.
* The string to look for. * @param searchIn The string to search in.
* @param searchIn * @param forward Whether to search forward or backward in
* The string to search in. * <code>searchIn</code>.
* @param forward * @param matchCase If <code>true</code>, do a case-sensitive search for
* Whether to search forward or backward in * <code>searchFor</code>.
* <code>searchIn</code>. * @param wholeWord If <code>true</code>, <code>searchFor</code> occurrences
* @param matchCase * embedded in longer words in <code>searchIn</code> don't
* If <code>true</code>, do a case-sensitive search for * count as matches.
* <code>searchFor</code>. * @return The starting position of a match, or <code>-1</code> if no match was
* @param wholeWord * found.
* If <code>true</code>, <code>searchFor</code>
* occurrences embedded in longer words in <code>searchIn</code>
* don't count as matches.
* @return The starting position of a match, or <code>-1</code> if no
* match was found.
*/ */
public static int getNextMatchPos(String searchFor, String searchIn, boolean forward, boolean matchCase, boolean wholeWord) { public static int getNextMatchPos(String searchFor, String searchIn, boolean forward, boolean matchCase,
boolean wholeWord) {
// Make our variables lower case if we're ignoring case. // Make our variables lower case if we're ignoring case.
if (!matchCase) { if (!matchCase) {
return getNextMatchPosImpl(searchFor.toLowerCase(), searchIn.toLowerCase(), forward, matchCase, wholeWord); return getNextMatchPosImpl(searchFor.toLowerCase(), searchIn.toLowerCase(), forward, matchCase, wholeWord);
} }
return getNextMatchPosImpl(searchFor, searchIn, forward, matchCase, wholeWord); return getNextMatchPosImpl(searchFor, searchIn, forward, matchCase, wholeWord);
} }
/** /**
* Actually does the work of matching; assumes searchFor and searchIn * Actually does the work of matching; assumes searchFor and searchIn are
* are already upper/lower-cased appropriately.<br> * already upper/lower-cased appropriately.<br>
* The reason this method is here is to attempt to speed up * The reason this method is here is to attempt to speed up
* <code>FindInFilesDialog</code>; since it repeatedly calls * <code>FindInFilesDialog</code>; since it repeatedly calls this method instead
* this method instead of <code>getNextMatchPos</code>, it gets better * of <code>getNextMatchPos</code>, it gets better performance as it no longer
* performance as it no longer has to allocate a lower-cased string for * has to allocate a lower-cased string for every call.
* every call.
* *
* @param searchFor * @param searchFor The string to search for.
* The string to search for. * @param searchIn The string to search in.
* @param searchIn * @param goForward Whether the search is forward or backward.
* The string to search in. * @param matchCase Whether the search is case-sensitive.
* @param goForward * @param wholeWord Whether only whole words should be matched.
* Whether the search is forward or backward. * @return The location of the next match, or <code>-1</code> if no match was
* @param matchCase * found.
* Whether the search is case-sensitive.
* @param wholeWord
* Whether only whole words should be matched.
* @return The location of the next match, or <code>-1</code> if no
* match was found.
*/ */
private static int getNextMatchPosImpl(String searchFor, String searchIn, boolean goForward, boolean matchCase, boolean wholeWord) { private static int getNextMatchPosImpl(String searchFor, String searchIn, boolean goForward, boolean matchCase,
boolean wholeWord) {
if (wholeWord) { if (wholeWord) {
int len = searchFor.length(); int len = searchFor.length();
int temp = goForward ? 0 : searchIn.length(); int temp = goForward ? 0 : searchIn.length();
@@ -337,80 +312,63 @@ public final class SearchEngine {
return goForward ? searchIn.indexOf(searchFor) : searchIn.lastIndexOf(searchFor); return goForward ? searchIn.indexOf(searchFor) : searchIn.lastIndexOf(searchFor);
} }
} }
/** /**
* Searches <code>searchIn</code> for an occurrence of <code>regEx</code> * Searches <code>searchIn</code> for an occurrence of <code>regEx</code> either
* either forwards or backwards, matching case or not. * forwards or backwards, matching case or not.
* *
* @param regEx * @param regEx The regular expression to look for.
* The regular expression to look for. * @param searchIn The string to search in.
* @param searchIn * @param goForward Whether to search forward. If <code>false</code>, search
* The string to search in. * backward.
* @param goForward * @param matchCase Whether or not to do a case-sensitive search for
* Whether to search forward. If <code>false</code>, * <code>regEx</code>.
* search backward. * @param wholeWord If <code>true</code>, <code>regEx</code> occurrences
* @param matchCase * embedded in longer words in <code>searchIn</code> don't
* Whether or not to do a case-sensitive search for * count as matches.
* <code>regEx</code>. * @return A <code>Point</code> representing the starting and ending position of
* @param wholeWord * the match, or <code>null</code> if no match was found.
* If <code>true</code>, <code>regEx</code> * @throws PatternSyntaxException If <code>regEx</code> is an invalid regular
* occurrences embedded in longer words in <code>searchIn</code> * expression.
* don't count as matches.
* @return A <code>Point</code> representing the starting and ending
* position of the match, or <code>null</code> if no match was
* found.
* @throws PatternSyntaxException
* If <code>regEx</code> is an invalid
* regular expression.
* @see #getNextMatchPos * @see #getNextMatchPos
*/ */
private static Point getNextMatchPosRegEx(String regEx, CharSequence searchIn, boolean goForward, boolean matchCase, private static Point getNextMatchPosRegEx(String regEx, CharSequence searchIn, boolean goForward, boolean matchCase,
boolean wholeWord) { boolean wholeWord) {
return (Point) getNextMatchPosRegExImpl(regEx, searchIn, goForward, matchCase, wholeWord, null); return (Point) getNextMatchPosRegExImpl(regEx, searchIn, goForward, matchCase, wholeWord, null);
} }
/** /**
* Searches <code>searchIn</code> for an occurrence of <code>regEx</code> * Searches <code>searchIn</code> for an occurrence of <code>regEx</code> either
* either forwards or backwards, matching case or not. * forwards or backwards, matching case or not.
* *
* @param regEx * @param regEx The regular expression to look for.
* The regular expression to look for. * @param searchIn The string to search in.
* @param searchIn * @param goForward Whether to search forward. If <code>false</code>, search
* The string to search in. * backward.
* @param goForward * @param matchCase Whether or not to do a case-sensitive search for
* Whether to search forward. If <code>false</code>, * <code>regEx</code>.
* search backward. * @param wholeWord If <code>true</code>, <code>regEx</code> occurrences
* @param matchCase * embedded in longer words in <code>searchIn</code> don't
* Whether or not to do a case-sensitive search for * count as matches.
* <code>regEx</code>. * @param replaceStr The string that will replace the match found (if a match is
* @param wholeWord * found). The object returned will contain the replacement
* If <code>true</code>, <code>regEx</code> * string with matched groups substituted. If this value is
* occurrences embedded in longer words in <code>searchIn</code> * <code>null</code>, it is assumed this call is part of a
* don't count as matches. * "find" instead of a "replace" operation.
* @param replaceStr * @return If <code>replaceStr</code> is <code>null</code>, a <code>Point</code>
* The string that will replace the match found (if * representing the starting and ending points of the match. If it is
* a match is found). The object returned will contain the * non-<code>null</code>, an object with information about the match and
* replacement string with matched groups substituted. If this * the morphed string to replace it with. If no match is found,
* value is <code>null</code>, it is assumed this call is part of * <code>null</code> is returned.
* a * @throws PatternSyntaxException If <code>regEx</code> is an invalid regular
* "find" instead of a "replace" operation. * expression.
* @return If <code>replaceStr</code> is <code>null</code>, a * @throws IndexOutOfBoundsException If <code>replaceStr</code> references an
* <code>Point</code> representing the starting and ending points * invalid group (less than zero or greater
* of the match. If it is non-<code>null</code>, an object with * than the number of groups matched).
* information about the match and the morphed string to replace
* it with. If no match is found, <code>null</code> is returned.
* @throws PatternSyntaxException
* If <code>regEx</code> is an invalid
* regular expression.
* @throws IndexOutOfBoundsException
* If <code>replaceStr</code> references
* an invalid group (less than zero or greater than the number
* of
* groups matched).
* @see #getNextMatchPos * @see #getNextMatchPos
*/ */
private static Object getNextMatchPosRegExImpl(String regEx, CharSequence searchIn, boolean goForward, boolean matchCase, private static Object getNextMatchPosRegExImpl(String regEx, CharSequence searchIn, boolean goForward,
boolean wholeWord, String replaceStr) { boolean matchCase, boolean wholeWord, String replaceStr) {
if (wholeWord) { if (wholeWord) {
regEx = "\\b" + regEx + "\\b"; regEx = "\\b" + regEx + "\\b";
} }
@@ -444,24 +402,20 @@ public final class SearchEngine {
} }
return null; // No match found return null; // No match found
} }
/** /**
* Returns information on how to implement a regular expression "replace" * Returns information on how to implement a regular expression "replace" action
* action in the specified text with the specified replacement string. * in the specified text with the specified replacement string.
* *
* @param searchIn * @param searchIn The string to search in.
* The string to search in. * @param context The search options.
* @param context * @return A <code>RegExReplaceInfo</code> object describing how to implement
* The search options. * the replace.
* @return A <code>RegExReplaceInfo</code> object describing how to * @throws PatternSyntaxException If the search text is an invalid regular
* implement the replace. * expression.
* @throws PatternSyntaxException * @throws IndexOutOfBoundsException If the replacement text references an
* If the search text is an invalid regular * invalid group (less than zero or greater
* expression. * than the number of groups matched).
* @throws IndexOutOfBoundsException
* If the replacement text references an
* invalid group (less than zero or greater than the number of
* groups matched).
* @see #getNextMatchPos * @see #getNextMatchPos
*/ */
private static RegExReplaceInfo getRegExReplaceInfo(CharSequence searchIn, SearchContext context) { private static RegExReplaceInfo getRegExReplaceInfo(CharSequence searchIn, SearchContext context) {
@@ -476,39 +430,34 @@ public final class SearchEngine {
boolean goForward = context.getSearchForward(); boolean goForward = context.getSearchForward();
boolean matchCase = context.getMatchCase(); boolean matchCase = context.getMatchCase();
boolean wholeWord = context.getWholeWord(); boolean wholeWord = context.getWholeWord();
return (RegExReplaceInfo) getNextMatchPosRegExImpl(regex, searchIn, goForward, matchCase, wholeWord, replacement); return (RegExReplaceInfo) getNextMatchPosRegExImpl(regex, searchIn, goForward, matchCase, wholeWord,
replacement);
} }
/** /**
* Called internally by <code>getMatches()</code>. This method assumes * Called internally by <code>getMatches()</code>. This method assumes that the
* that the specified matcher has just found a match, and that you want * specified matcher has just found a match, and that you want to get the string
* to get the string with which to replace that match. * with which to replace that match.
* <p> * <p>
* *
* Escapes simply insert the escaped character, except for <code>\n</code> * Escapes simply insert the escaped character, except for <code>\n</code> and
* and <code>\t</code>, which insert a newline and tab respectively. * <code>\t</code>, which insert a newline and tab respectively. Substrings of
* Substrings of the form <code>$\d+</code> are considered to be matched * the form <code>$\d+</code> are considered to be matched groups. To include a
* groups. To include a literal dollar sign in your template, escape it * literal dollar sign in your template, escape it (i.e. <code>\$</code>).
* (i.e. <code>\$</code>).
* <p> * <p>
* *
* Most clients will have no need to call this method directly. * Most clients will have no need to call this method directly.
* *
* @param m * @param m The matcher.
* The matcher. * @param template The template for the replacement string. For example,
* @param template * "<code>foo</code>" would yield the replacement string
* The template for the replacement string. For example, * "<code>foo</code>", while "<code>$1 is the greatest</code>"
* "<code>foo</code>" would yield the replacement string * would yield different values depending on the value of the
* "<code>foo</code>", while "<code>$1 is the greatest</code>" * first captured group in the match.
* would yield different values depending on the value of the
* first
* captured group in the match.
* @return The string to replace the match with. * @return The string to replace the match with.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException If <code>template</code> references an
* If <code>template</code> references * invalid group (less than zero or greater
* an invalid group (less than zero or greater than the number * than the number of groups matched).
* of
* groups matched).
*/ */
public static String getReplacementText(Matcher m, CharSequence template) { public static String getReplacementText(Matcher m, CharSequence template) {
// NOTE: This code was mostly ripped off from J2SE's Matcher // NOTE: This code was mostly ripped off from J2SE's Matcher
@@ -521,12 +470,12 @@ public final class SearchEngine {
if (nextChar == '\\') { // Escape character. if (nextChar == '\\') { // Escape character.
nextChar = template.charAt(++cursor); nextChar = template.charAt(++cursor);
switch (nextChar) { // Special cases. switch (nextChar) { // Special cases.
case 'n': case 'n':
nextChar = '\n'; nextChar = '\n';
break; break;
case 't': case 't':
nextChar = '\t'; nextChar = '\t';
break; break;
} }
result.append(nextChar); result.append(nextChar);
cursor++; cursor++;
@@ -570,11 +519,11 @@ public final class SearchEngine {
} }
return result.toString(); return result.toString();
} }
/** /**
* Returns whether the characters on either side of * Returns whether the characters on either side of
* <code>substr(searchIn, startPos, startPos+searchStringLength)</code> * <code>substr(searchIn, startPos, startPos+searchStringLength)</code> are
* are <em>not</em> letters or digits. * <em>not</em> letters or digits.
*/ */
private static boolean isWholeWord(CharSequence searchIn, int offset, int len) { private static boolean isWholeWord(CharSequence searchIn, int offset, int len) {
boolean wsBefore, wsAfter; boolean wsBefore, wsAfter;
@@ -590,17 +539,15 @@ public final class SearchEngine {
} }
return wsBefore && wsAfter; return wsBefore && wsAfter;
} }
/** /**
* Makes the caret's dot and mark the same location so that, for the * Makes the caret's dot and mark the same location so that, for the next search
* next search in the specified direction, a match will be found even * in the specified direction, a match will be found even if it was within the
* if it was within the original dot and mark's selection. * original dot and mark's selection.
* *
* @param textArea * @param textArea The text area.
* The text area. * @param forward Whether the search will be forward through the document
* @param forward * (<code>false</code> means backward).
* Whether the search will be forward through the
* document (<code>false</code> means backward).
* @return The new dot and mark position. * @return The new dot and mark position.
*/ */
private static int makeMarkAndDotEqual(JTextArea textArea, boolean forward) { private static int makeMarkAndDotEqual(JTextArea textArea, boolean forward) {
@@ -609,21 +556,18 @@ public final class SearchEngine {
c.setDot(val); c.setDot(val);
return val; return val;
} }
/** /**
* Marks all instances of the specified text in this text area. This * Marks all instances of the specified text in this text area. This method is
* method is typically only called directly in response to search events * typically only called directly in response to search events of type
* of type <code>SearchEvent.Type.MARK_ALL</code>. "Mark all" behavior * <code>SearchEvent.Type.MARK_ALL</code>. "Mark all" behavior is automatically
* is automatically performed when {@link #find(JTextArea, SearchContext)} * performed when {@link #find(JTextArea, SearchContext)} or
* or {@link #replace(RTextArea, SearchContext)} is called. * {@link #replace(RTextArea, SearchContext)} is called.
* *
* @param textArea * @param textArea The text area in which to mark occurrences.
* The text area in which to mark occurrences. * @param context The search context specifying the text to search for. It is
* @param context * assumed that <code>context.getMarkAll()</code> has already
* The search context specifying the text to search for. * been checked and returns <code>true</code>.
* It is assumed that <code>context.getMarkAll()</code> has
* already
* been checked and returns <code>true</code>.
* @return The results of the operation. * @return The results of the operation.
*/ */
public static SearchResult markAll(RTextArea textArea, SearchContext context) { public static SearchResult markAll(RTextArea textArea, SearchContext context) {
@@ -633,21 +577,18 @@ public final class SearchEngine {
// } // }
// return new SearchResult(); // return new SearchResult();
} }
/** /**
* Marks all instances of the specified text in this text area. This * Marks all instances of the specified text in this text area. This method is
* method is typically only called directly in response to search events * typically only called directly in response to search events of type
* of type <code>SearchEvent.Type.MARK_ALL</code>. "Mark all" behavior * <code>SearchEvent.Type.MARK_ALL</code>. "Mark all" behavior is automatically
* is automatically performed when {@link #find(JTextArea, SearchContext)} * performed when {@link #find(JTextArea, SearchContext)} or #replace(RTextArea,
* or #replace(RTextArea, SearchContext) is called. * SearchContext) is called.
* *
* @param textArea * @param textArea The text area in which to mark occurrences.
* The text area in which to mark occurrences. * @param context The search context specifying the text to search for. It is
* @param context * assumed that <code>context.getMarkAll()</code> has already
* The search context specifying the text to search for. * been checked and returns <code>true</code>.
* It is assumed that <code>context.getMarkAll()</code> has
* already
* been checked and returns <code>true</code>.
* @return The results of the operation. * @return The results of the operation.
*/ */
private static SearchResult markAllImpl(RTextArea textArea, SearchContext context) { private static SearchResult markAllImpl(RTextArea textArea, SearchContext context) {
@@ -698,25 +639,22 @@ public final class SearchEngine {
} }
return new SearchResult(null, 0, markAllCount); return new SearchResult(null, 0, markAllCount);
} }
/** /**
* Finds the next instance of the regular expression specified from * Finds the next instance of the regular expression specified from the caret
* the caret position. If a match is found, it is replaced with * position. If a match is found, it is replaced with the specified replacement
* the specified replacement string. * string.
* *
* @param textArea * @param textArea The text area in which to search.
* The text area in which to search. * @param context What to search for and all search options.
* @param context
* What to search for and all search options.
* @return The result of the operation. * @return The result of the operation.
* @throws PatternSyntaxException * @throws PatternSyntaxException If this is a regular expression search but
* If this is a regular expression search * the search text is an invalid regular
* but the search text is an invalid regular expression. * expression.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException If this is a regular expression search but
* If this is a regular expression search * the replacement text references an invalid
* but the replacement text references an invalid group (less * group (less than zero or greater than the
* than * number of groups matched).
* zero or greater than the number of groups matched).
* @see #replace(RTextArea, SearchContext) * @see #replace(RTextArea, SearchContext)
* @see #find(JTextArea, SearchContext) * @see #find(JTextArea, SearchContext)
*/ */
@@ -776,25 +714,22 @@ public final class SearchEngine {
int count = range != null ? 1 : 0; int count = range != null ? 1 : 0;
return new SearchResult(range, count, markAllCount); return new SearchResult(range, count, markAllCount);
} }
/** /**
* Finds the next instance of the text/regular expression specified from * Finds the next instance of the text/regular expression specified from the
* the caret position. If a match is found, it is replaced with the * caret position. If a match is found, it is replaced with the specified
* specified replacement string. * replacement string.
* *
* @param textArea * @param textArea The text area in which to search.
* The text area in which to search. * @param context What to search for and all search options.
* @param context
* What to search for and all search options.
* @return The result of the operation. * @return The result of the operation.
* @throws PatternSyntaxException * @throws PatternSyntaxException If this is a regular expression search but
* If this is a regular expression search * the search text is an invalid regular
* but the search text is an invalid regular expression. * expression.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException If this is a regular expression search but
* If this is a regular expression search * the replacement text references an invalid
* but the replacement text references an invalid group (less * group (less than zero or greater than the
* than * number of groups matched).
* zero or greater than the number of groups matched).
* @see #replaceAll(RTextArea, SearchContext) * @see #replaceAll(RTextArea, SearchContext)
* @see #find(JTextArea, SearchContext) * @see #find(JTextArea, SearchContext)
*/ */
@@ -847,24 +782,21 @@ public final class SearchEngine {
textArea.endAtomicEdit(); textArea.endAtomicEdit();
} }
} }
/** /**
* Replaces all instances of the text/regular expression specified in * Replaces all instances of the text/regular expression specified in the
* the specified document with the specified replacement. * specified document with the specified replacement.
* *
* @param textArea * @param textArea The text area in which to search.
* The text area in which to search. * @param context What to search for and all search options.
* @param context
* What to search for and all search options.
* @return The result of the operation. * @return The result of the operation.
* @throws PatternSyntaxException * @throws PatternSyntaxException If this is a regular expression search but
* If this is a regular expression search * the replacement text is an invalid regular
* but the replacement text is an invalid regular expression. * expression.
* @throws IndexOutOfBoundsException * @throws IndexOutOfBoundsException If this is a regular expression search but
* If this is a regular expression search * the replacement text references an invalid
* but the replacement text references an invalid group (less * group (less than zero or greater than the
* than * number of groups matched).
* zero or greater than the number of groups matched).
* @see #replace(RTextArea, SearchContext) * @see #replace(RTextArea, SearchContext)
* @see #find(JTextArea, SearchContext) * @see #find(JTextArea, SearchContext)
*/ */