Initial version of the project for java studies from 2013.
commit
54c68d7375
|
@ -0,0 +1,11 @@
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
|
||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
out
|
||||||
|
build
|
|
@ -0,0 +1,12 @@
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
mainClassName = 'tpsa.BFInterpreter'
|
|
@ -0,0 +1,70 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.LogManager;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class BFInterpreter {
|
||||||
|
|
||||||
|
static LogManager lm;
|
||||||
|
public static Logger lg;
|
||||||
|
static Level lv = Level.OFF;
|
||||||
|
|
||||||
|
static {
|
||||||
|
lm = LogManager.getLogManager();
|
||||||
|
lm.addLogger(Logger.getLogger("BFInterpreter"));
|
||||||
|
lg = lm.getLogger("BFInterpreter");
|
||||||
|
lg.setLevel(lv);
|
||||||
|
for (Handler h : BFInterpreter.lg.getHandlers())
|
||||||
|
h.setLevel(lv);
|
||||||
|
|
||||||
|
for (Handler h : Logger.getLogger("").getHandlers())
|
||||||
|
h.setLevel(lv);
|
||||||
|
|
||||||
|
for (Handler h : Logger.getLogger("").getHandlers())
|
||||||
|
h.setLevel(lv);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
Thread t = new Thread(
|
||||||
|
new BFMachine(
|
||||||
|
"+[,+[-[>+>+<<-]>[<+>-]+>>++++++++[<-------->-]<-[<[-]>>>+[<+<+>>-]<[>+<-]<[<++>\n"
|
||||||
|
+ ">>+[<+<->>-]<[>+<-]]>[<]<]>>[-]<<<[[-]<[>>+>+<<<-]>>[<<+>>-]>>++++++++[<-------\n"
|
||||||
|
+ "->-]<->>++++[<++++++++>-]<-<[>>>+<<[>+>[-]<<-]>[<+>-]>[<<<<<+>>>>++++[<++++++++\n"
|
||||||
|
+ ">-]>-]<<-<-]>[<<<<[-]>>>>[<<<<->>>>-]]<<++++[<<++++++++>>-]<<-[>>+>+<<<-]>>[<<+\n"
|
||||||
|
+ ">>-]+>>+++++[<----->-]<-[<[-]>>>+[<+<->>-]<[>+<-]<[<++>>>+[<+<+>>-]<[>+<-]]>[<]\n"
|
||||||
|
+ "<]>>[-]<<<[[-]<<[>>+>+<<<-]>>[<<+>>-]+>------------[<[-]>>>+[<+<->>-]<[>+<-]<[<\n"
|
||||||
|
+ "++>>>+[<+<+>>-]<[>+<-]]>[<]<]>>[-]<<<<<------------->>[[-]+++++[<<+++++>>-]<<+>\n"
|
||||||
|
+ ">]<[>++++[<<++++++++>>-]<-]>]<[-]++++++++[<++++++++>-]<+>]<.[-]+>>+<]>[[-]<]<]"));
|
||||||
|
try {
|
||||||
|
t.start();
|
||||||
|
t.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
BFInterpreter.lg.severe(BFInterpreter.ErrorsFormatter(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String ErrorsFormatter(Exception excp) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String newLine = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
sb.append("Exception catched: " + excp.getLocalizedMessage() + newLine);
|
||||||
|
StackTraceElement[] stack = excp.getStackTrace();
|
||||||
|
for (int i = 0; i < stack.length; i++) {
|
||||||
|
sb.append("StackElement(" + i + ") : " + stack[i].getClassName()
|
||||||
|
+ ", " + stack[i].getFileName() + ":"
|
||||||
|
+ stack[i].getLineNumber() + ", "
|
||||||
|
+ stack[i].getMethodName() + newLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,381 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EmptyStackException;
|
||||||
|
import java.util.Stack;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import tpsa.exceptions.BFException;
|
||||||
|
import tpsa.exceptions.BadCallException;
|
||||||
|
import tpsa.exceptions.MalformedCodeException;
|
||||||
|
import tpsa.exceptions.OutOfMemoryPointer;
|
||||||
|
import tpsa.streams.JTextPaneInputStream;
|
||||||
|
import tpsa.streams.JTextPaneOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brainfuck is an esoteric programming language noted for its extreme minimalism.
|
||||||
|
* It is a Turing tarpit, designed to challenge and amuse programmers,
|
||||||
|
* and was not made to be suitable for practical use. It was created in 1993 by Urban Müller.
|
||||||
|
* The name of the language is generally not capitalized except at the start of a sentence,
|
||||||
|
* although it is a proper noun.
|
||||||
|
*
|
||||||
|
* Język ma 8 mnemoników (patrz mnemoniki assemblera)
|
||||||
|
*
|
||||||
|
* > zwiększa wskaźnik o 1 ++p
|
||||||
|
* < zmniejsza wskaźnik o 1 --p
|
||||||
|
* + zwiększa o 1 w bieżącej pozycji ++(*p)
|
||||||
|
* - zmniejsza o 1 w bieżącej pozycji --(*p)
|
||||||
|
* . wyświetla znak w bieżącej pozycji (ASCII) putchar(*p)
|
||||||
|
* , pobiera znak i wstawia go w bieżącej pozycji (ASCII) *p=getchar()
|
||||||
|
* [ skacze bezpośrednio za odpowiadający mu ], jeśli w
|
||||||
|
* bieżącej pozycji znajduje się 0 while(*p){
|
||||||
|
* ] skacze do odpowiadającego mu [ }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa główna maszyny BF. Jest ona odpowiedzialna, za stworzenie VM dla BF i
|
||||||
|
* interpretowanie kodu BF.
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*/
|
||||||
|
public class BFMachine implements Runnable {
|
||||||
|
|
||||||
|
public static int defaultMemorySize = 0x10000;
|
||||||
|
/**
|
||||||
|
* Kod, dla maszyny wirtualnej BF
|
||||||
|
*/
|
||||||
|
private Runnable breakpointHandler;
|
||||||
|
private byte[] code;
|
||||||
|
private int codePointer = 0;
|
||||||
|
private String error;
|
||||||
|
private InputStreamReader iStreamReader = null;
|
||||||
|
private InputStream iS = null;
|
||||||
|
private Object lock = new Object();
|
||||||
|
private AtomicBoolean machineWorking = new AtomicBoolean(false);
|
||||||
|
private byte[] memory;
|
||||||
|
private int memoryPointer = 0;
|
||||||
|
private OutputStreamWriter oStream = null;
|
||||||
|
private AtomicBoolean paused = new AtomicBoolean(false);
|
||||||
|
private Runnable postHandler;
|
||||||
|
private Stack<Integer> stackExecution = new Stack<Integer>();
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
memoryPointer = memory.length / 2;
|
||||||
|
Arrays.fill(memory, (byte) 0);
|
||||||
|
try {
|
||||||
|
iS.reset();
|
||||||
|
} catch (IOException excp) {
|
||||||
|
|
||||||
|
}
|
||||||
|
codePointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Runnable stepHandler;
|
||||||
|
|
||||||
|
private AtomicBoolean stepping = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public BFMachine(JTextPaneInputStream is, JTextPaneOutputStream os,
|
||||||
|
String code, int sizeOfMemory) {
|
||||||
|
this.code = code.getBytes();
|
||||||
|
memory = new byte[sizeOfMemory];
|
||||||
|
memoryPointer = sizeOfMemory / 2;
|
||||||
|
iS = is;
|
||||||
|
oStream = new OutputStreamWriter(os);
|
||||||
|
iStreamReader = new InputStreamReader(is);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domyślnie program startuje z wskaźnikiem pamięci w środku pamięci
|
||||||
|
* operacynej
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* Kod, który wykonywany jest przez maszynę BF
|
||||||
|
*/
|
||||||
|
public BFMachine(String code) {
|
||||||
|
this(code, defaultMemorySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domyślnie program startuje z wskaźnikiem pamięci w środku pamięci
|
||||||
|
* operacynej
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* Kod, który wykonywany jest przez maszynę BF
|
||||||
|
* @param sizeOfMemory
|
||||||
|
* rozmiar pamięci operacyjnej maszyny BF
|
||||||
|
*/
|
||||||
|
public BFMachine(String code, int sizeOfMemory) {
|
||||||
|
this.code = code.getBytes();
|
||||||
|
memory = new byte[sizeOfMemory];
|
||||||
|
memoryPointer = sizeOfMemory / 2;
|
||||||
|
error = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public Runnable getBreakpointHandler() {
|
||||||
|
return breakpointHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getError() {
|
||||||
|
if (error == null)
|
||||||
|
return new String();
|
||||||
|
return new String(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getMemory() {
|
||||||
|
return memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMemoryPointer() {
|
||||||
|
return memoryPointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPosition() {
|
||||||
|
return codePointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Runnable getStepHandler() {
|
||||||
|
return stepHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getStepping() {
|
||||||
|
return stepping.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWorking() {
|
||||||
|
return machineWorking.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zatrzymuje pracę maszynę BF
|
||||||
|
*/
|
||||||
|
public void pause() {
|
||||||
|
paused.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wznawia pracę maszyny BF
|
||||||
|
*/
|
||||||
|
public void resume() {
|
||||||
|
paused.set(false);
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Przewija wskaźnik kodu na pozycję o 1 dalej niż jest koniec
|
||||||
|
* odpowiadającemu mu znakowi końca pętli
|
||||||
|
*
|
||||||
|
* @throws BadCallException
|
||||||
|
* W miejscu wskaźnika BF nie ma znaku początku pętli
|
||||||
|
* @throws MalformedCodeException
|
||||||
|
* Kod podany dla maszyny BF jest niepoprawny
|
||||||
|
*/
|
||||||
|
private void rewindTheLoopToTheEnd() throws BadCallException,
|
||||||
|
MalformedCodeException {
|
||||||
|
if (code[codePointer] != '[')
|
||||||
|
throw new BadCallException("Nie ma '[' w miejscu startu",
|
||||||
|
codePointer);
|
||||||
|
// System.err.println("Wchodzę w pozycji: " + codePointer);
|
||||||
|
int stack = 1;
|
||||||
|
codePointer++;
|
||||||
|
|
||||||
|
while (stack > 0) {
|
||||||
|
while (code[codePointer] != '[' && code[codePointer] != ']') {
|
||||||
|
// System.err.println("Pozycja: " + codePointer + ", char: " +
|
||||||
|
// code[codePointer]);
|
||||||
|
if (++codePointer >= code.length)
|
||||||
|
throw new MalformedCodeException(
|
||||||
|
"Nie wiem jakim cudem, ale pointer wyszedł poza kod",
|
||||||
|
codePointer);
|
||||||
|
}
|
||||||
|
// System.err.println("Pozycja: " + codePointer + ", char: " +
|
||||||
|
// code[codePointer]);
|
||||||
|
switch (code[codePointer]) {
|
||||||
|
case (byte) 0xcc:
|
||||||
|
/*
|
||||||
|
* if (breakpointHandled.get() == false) return; else {
|
||||||
|
* breakpointHandled.set(true); }
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
++stack;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
--stack;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
codePointer++;
|
||||||
|
}
|
||||||
|
// System.err.println("Znak wyjścia: " + codePointer + ", char: " +
|
||||||
|
// code[codePointer]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
memoryPointer = memory.length / 2;
|
||||||
|
codePointer = 0;
|
||||||
|
|
||||||
|
BFInterpreter.lg.info("Machine starting");
|
||||||
|
|
||||||
|
BFInterpreter.lg.finest("Code" + new String(code));
|
||||||
|
|
||||||
|
machineWorking.set(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
while (codePointer < code.length && machineWorking.get()) {
|
||||||
|
|
||||||
|
if (paused.get() || stepping.get()) {
|
||||||
|
|
||||||
|
if (stepHandler != null && stepping.get()) {
|
||||||
|
BFInterpreter.lg.fine("Stepping item");
|
||||||
|
System.err.println("Joł");
|
||||||
|
stepHandler.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BFInterpreter.lg.finest("Executing code at " + codePointer);
|
||||||
|
|
||||||
|
switch (code[codePointer]) {
|
||||||
|
case 'b':
|
||||||
|
if (breakpointHandler != null) {
|
||||||
|
breakpointHandler.run();
|
||||||
|
synchronized (lock) {
|
||||||
|
lock.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
if (++memoryPointer >= memory.length)
|
||||||
|
throw new OutOfMemoryPointer(
|
||||||
|
"Pointer wyszedł poza pamięć", codePointer);
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
if (--memoryPointer < 0)
|
||||||
|
throw new OutOfMemoryPointer(
|
||||||
|
"Pointer wyszedł poza pamięć", codePointer);
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
++memory[memoryPointer];
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
--memory[memoryPointer];
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
BFInterpreter.lg.finest("Priting char");
|
||||||
|
if (oStream == null) {
|
||||||
|
System.out.print(memory[memoryPointer]);
|
||||||
|
} else {
|
||||||
|
oStream.write(memory[memoryPointer]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
BFInterpreter.lg.fine("Reading char from input");
|
||||||
|
int c = (iStreamReader == null) ? System.in.read() : iStreamReader
|
||||||
|
.read();
|
||||||
|
BFInterpreter.lg.fine("Read input code " + c);
|
||||||
|
memory[memoryPointer] = (byte) c;
|
||||||
|
BFInterpreter.lg.fine("Read char");
|
||||||
|
break;
|
||||||
|
case '[':
|
||||||
|
// System.err.println("Wartość w komórce: " +
|
||||||
|
// (int)memory[memoryPointer]);
|
||||||
|
if (memory[memoryPointer] > 0)
|
||||||
|
stackExecution.push(codePointer);
|
||||||
|
else {
|
||||||
|
rewindTheLoopToTheEnd();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
int pos = stackExecution.pop();
|
||||||
|
codePointer = pos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
codePointer++;
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
if (oStream != null)
|
||||||
|
oStream.flush();
|
||||||
|
// oStream.close();
|
||||||
|
} catch (BFException excp) {
|
||||||
|
String message = excp.getMessage();
|
||||||
|
if (message == null)
|
||||||
|
message = new String("Exception");
|
||||||
|
setError(excp.getClass().getName() + ": " + message + ", offset: "
|
||||||
|
+ excp.getOffset());
|
||||||
|
|
||||||
|
} catch (EmptyStackException excp) {
|
||||||
|
setError(excp.getClass().getName()
|
||||||
|
+ "Stary, klamerki się nie zgadzają");
|
||||||
|
} catch (IOException excp) {
|
||||||
|
setError("No nic, IOException..., cokolwiek by to nie znaczyło");
|
||||||
|
} catch (InterruptedException excp) {
|
||||||
|
BFInterpreter.lg.severe(BFInterpreter.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postHandler != null && machineWorking.get() == true)
|
||||||
|
postHandler.run();
|
||||||
|
|
||||||
|
machineWorking.set(false);
|
||||||
|
|
||||||
|
BFInterpreter.lg.finest("Machine has ended");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBreakpointHandler(Runnable breakpointHandler) {
|
||||||
|
this.breakpointHandler = breakpointHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(String code) {
|
||||||
|
this.code = code.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setError(String error) {
|
||||||
|
this.error = new String(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFinishHandler(Runnable runnable) {
|
||||||
|
this.postHandler = runnable;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStepHandler(Runnable stepHandler) {
|
||||||
|
this.stepHandler = stepHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStepping(boolean b) {
|
||||||
|
this.stepping.set(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definitywnie kończy pracę maszyny (nie da się wznowić)
|
||||||
|
*
|
||||||
|
* @see pause
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
// definitywnie kończy pracę, nie da się wznowić
|
||||||
|
machineWorking.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(byte[] code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package tpsa.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ogólny exception dla BF
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BFException extends Exception {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -1955781345162254359L;
|
||||||
|
|
||||||
|
private int offset;
|
||||||
|
|
||||||
|
public BFException(String message, int offset) {
|
||||||
|
super(message);
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package tpsa.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wyjątek rzucany gdy funkcja rewindTheLoopToTheEnd zostanie wywołana nie
|
||||||
|
* wtedy, gdy codePointer będzie wskazywał na '['
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BadCallException extends BFException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -2134149027354550342L;
|
||||||
|
|
||||||
|
public BadCallException(String message, int offset) {
|
||||||
|
super(message, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package tpsa.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* W zasadzie ten wyjątek nigdy nie powinien wystąpić: "Nie wiem jakim cudem,
|
||||||
|
* ale pointer wyszedł poza kod"
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MalformedCodeException extends BFException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5072506605351465843L;
|
||||||
|
|
||||||
|
public MalformedCodeException(String message, int offset) {
|
||||||
|
super(message, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package tpsa.exceptions;
|
||||||
|
|
||||||
|
public class NotImplementedException extends BFException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8190551201861687817L;
|
||||||
|
|
||||||
|
public NotImplementedException(String message, int offset) {
|
||||||
|
super(message, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package tpsa.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wskaźnik pamięci wyszedł poza pamięć
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class OutOfMemoryPointer extends BFException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 6323824785754255254L;
|
||||||
|
|
||||||
|
public OutOfMemoryPointer(String message, int offset) {
|
||||||
|
super(message, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package tpsa.streams;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
|
||||||
|
import tpsa.BFInterpreter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa dająca strumień wejściowy z JTextPane dla BF maszyny
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JTextPaneInputStream extends InputStream implements
|
||||||
|
DocumentListener {
|
||||||
|
|
||||||
|
private boolean eof = false;
|
||||||
|
private Logger lg = Logger.getLogger("BF");
|
||||||
|
private Semaphore sem = new Semaphore(0);
|
||||||
|
private StringBuilder string = new StringBuilder();
|
||||||
|
|
||||||
|
public JTextPaneInputStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
eof = false;
|
||||||
|
string.replace(0, string.length(), "");
|
||||||
|
try {
|
||||||
|
if (sem.availablePermits() > 0)
|
||||||
|
sem.acquire(sem.availablePermits());
|
||||||
|
} catch (InterruptedException excp) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(final DocumentEvent e) {
|
||||||
|
try {
|
||||||
|
lg.info("Strumień (handler) otrzymał nowe powiadomienie o wprowadzonych danych");
|
||||||
|
String s = e.getDocument().getText(e.getOffset(), e.getLength());
|
||||||
|
string.append(s);
|
||||||
|
sem.release(s.length());
|
||||||
|
lg.info("Wartość semafora: " + sem.availablePermits());
|
||||||
|
lg.info("Strumień dodał parę bajtów do bufora");
|
||||||
|
|
||||||
|
final StyledDocument doc = (StyledDocument) e.getDocument();
|
||||||
|
final Style set = doc.getStyle("INPUT");
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doc.setParagraphAttributes(e.getOffset(), e.getLength(),
|
||||||
|
set, false);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BFInterpreter.lg.severe(BFInterpreter.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (eof == true) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
lg.info("Strumień ma dać jeden bajt");
|
||||||
|
sem.acquire();
|
||||||
|
lg.info("Strumień uzyskał bajt na semaforze");
|
||||||
|
String c = string.substring(0, 1);
|
||||||
|
char c2 = c.charAt(0);
|
||||||
|
Character c3 = '\u001a';
|
||||||
|
if (c3.equals(c2)) {
|
||||||
|
lg.info("ERROR SUCCESS");
|
||||||
|
eof = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
string.delete(0, 1);
|
||||||
|
lg.info("Strumień usunął wczytany bajt z bufora");
|
||||||
|
return c2;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package tpsa.streams;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.swing.JTextPane;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
|
||||||
|
import tpsa.BFInterpreter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa strumień wyjściowy do JTextPane dla BF maszyny
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class JTextPaneOutputStream extends OutputStream {
|
||||||
|
|
||||||
|
private Logger lg = Logger.getLogger("BF");
|
||||||
|
private JTextPane pane;
|
||||||
|
|
||||||
|
public JTextPaneOutputStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTextPane getPane() {
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPane(JTextPane pane) {
|
||||||
|
this.pane = pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
lg.finest("Writing byte :" + b);
|
||||||
|
StyledDocument doc = (StyledDocument) pane.getDocument();
|
||||||
|
char c = (char) b;
|
||||||
|
String s = "" + c;
|
||||||
|
Style style = doc.getStyle("OUTPUT");
|
||||||
|
try {
|
||||||
|
doc.insertString(doc.getLength(), s, style);
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BFInterpreter.lg.severe(BFInterpreter.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
Róbta co chceta z tym kodem, tylko nie publikujcie tego z moim imieniem, nazwiskiem, czy nickiem.
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Purpose
|
||||||
|
|
||||||
|
It was a test project for java studies (to complete the course). Except of putting it under the gradle control (it
|
||||||
|
wasn't), whole code is original from those times. I will try to translate original comments, naming convention to
|
||||||
|
non-polish.
|
||||||
|
|
||||||
|
# Technology
|
||||||
|
|
||||||
|
Ancient swing gui with some custom helpers from other authors
|
||||||
|
|
||||||
|
- https://github.com/sporst/splib
|
||||||
|
- https://github.com/Konloch/bytecode-viewer
|
||||||
|
HexEditor (com.jhe.hexed) and other (tv.ports.*) are not mine, just reused.
|
|
@ -0,0 +1,2 @@
|
||||||
|
Pisze kod, dla inputa, taki że kod wygenerowany generuje wpisany input
|
||||||
|
,[>>++++++[-<+++++++>]<+<[->.<]>+++.<++++[->++++<]>.>,]
|
|
@ -0,0 +1,12 @@
|
||||||
|
apply plugin: 'java'
|
||||||
|
apply plugin: 'application'
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':BFInterpreter')
|
||||||
|
}
|
||||||
|
|
||||||
|
mainClassName = 'tpsa.BrainFuckIDE'
|
|
@ -0,0 +1 @@
|
||||||
|
[]]
|
|
@ -0,0 +1,3 @@
|
||||||
|
+++[>+++++<-]>[>+>+++>+>++>+++++>++<[++<]>---]>->-.[>++>+<<--]>--.--.+.>>>++.<<
|
||||||
|
.<------.+.+++++.>>-.<++++.<--.>>>.<<---.<.-->-.>+.[+++++.---<]>>[.--->]<<.<+.+
|
||||||
|
+.++>+++[.<][.]<++.
|
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
+[]
|
|
@ -0,0 +1 @@
|
||||||
|
+[>+]
|
|
@ -0,0 +1,3 @@
|
||||||
|
++++[>+++++<-]>[<+++++>-]+<+[>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+>>>+[[-]++++++>
|
||||||
|
>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++
|
||||||
|
>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-]
|
|
@ -0,0 +1,324 @@
|
||||||
|
package com.jhe.hexed;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.AdjustmentEvent;
|
||||||
|
import java.awt.event.AdjustmentListener;
|
||||||
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.MouseWheelEvent;
|
||||||
|
import java.awt.event.MouseWheelListener;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollBar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by IntelliJ IDEA.
|
||||||
|
* User: laullon
|
||||||
|
* Date: 08-abr-2003
|
||||||
|
* Time: 13:21:09
|
||||||
|
*/
|
||||||
|
public class JHexEditor extends JPanel implements FocusListener,AdjustmentListener,MouseWheelListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 2289328616534802372L;
|
||||||
|
byte[] buff;
|
||||||
|
public int cursor;
|
||||||
|
protected static Font font=new Font("Monospaced",0,12);
|
||||||
|
protected int border=2;
|
||||||
|
public boolean DEBUG=false;
|
||||||
|
private JPanel panel;
|
||||||
|
private JScrollBar sb;
|
||||||
|
private int inicio=0;
|
||||||
|
private int lineas=10;
|
||||||
|
|
||||||
|
public JHexEditor(byte[] buff)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
this.buff=buff;
|
||||||
|
|
||||||
|
this.addMouseWheelListener(this);
|
||||||
|
|
||||||
|
sb=new JScrollBar(JScrollBar.VERTICAL);
|
||||||
|
sb.addAdjustmentListener(this);
|
||||||
|
// sb.setMinimum(0);
|
||||||
|
// sb.setMaximum(buff.length/getLineas());
|
||||||
|
|
||||||
|
JPanel p1,p2,p3;
|
||||||
|
//centro
|
||||||
|
p1=new JPanel(new BorderLayout(1,1));
|
||||||
|
p1.add(new JHexEditorHEX(this),BorderLayout.CENTER);
|
||||||
|
p1.add(new Columnas(),BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// izq.
|
||||||
|
p2=new JPanel(new BorderLayout(1,1));
|
||||||
|
p2.add(new Filas(),BorderLayout.CENTER);
|
||||||
|
p2.add(new Caja(),BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// der
|
||||||
|
p3=new JPanel(new BorderLayout(1,1));
|
||||||
|
p3.add(sb,BorderLayout.EAST);
|
||||||
|
p3.add(new JHexEditorASCII(this),BorderLayout.CENTER);
|
||||||
|
p3.add(new Caja(),BorderLayout.NORTH);
|
||||||
|
|
||||||
|
panel=new JPanel();
|
||||||
|
panel.setLayout(new BorderLayout(1,1));
|
||||||
|
panel.add(p1,BorderLayout.CENTER);
|
||||||
|
panel.add(p2,BorderLayout.WEST);
|
||||||
|
panel.add(p3,BorderLayout.EAST);
|
||||||
|
|
||||||
|
this.setLayout(new BorderLayout(1,1));
|
||||||
|
this.add(panel,BorderLayout.CENTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paint(Graphics g)
|
||||||
|
{
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
Rectangle rec=this.getBounds();
|
||||||
|
lineas=(rec.height/fn.getHeight())-1;
|
||||||
|
int n=(buff.length/16)-1;
|
||||||
|
if(lineas>n) { lineas=n; inicio=0; }
|
||||||
|
|
||||||
|
sb.setValues(getInicio(),+getLineas(),0,buff.length/16);
|
||||||
|
sb.setValueIsAdjusting(true);
|
||||||
|
super.paint(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void actualizaCursor()
|
||||||
|
{
|
||||||
|
int n=(cursor/16);
|
||||||
|
|
||||||
|
System.out.print("- "+inicio+"<"+n+"<"+(lineas+inicio)+"("+lineas+")");
|
||||||
|
|
||||||
|
if(n<inicio) inicio=n;
|
||||||
|
else if(n>=inicio+lineas) inicio=n-(lineas-1);
|
||||||
|
|
||||||
|
System.out.println(" - "+inicio+"<"+n+"<"+(lineas+inicio)+"("+lineas+")");
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getInicio()
|
||||||
|
{
|
||||||
|
return inicio;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getLineas()
|
||||||
|
{
|
||||||
|
return lineas;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void fondo(Graphics g,int x,int y,int s)
|
||||||
|
{
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
g.fillRect(((fn.stringWidth(" ")+1)*x)+border,(fn.getHeight()*y)+border,((fn.stringWidth(" ")+1)*s),fn.getHeight()+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void cuadro(Graphics g,int x,int y,int s)
|
||||||
|
{
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
g.drawRect(((fn.stringWidth(" ")+1)*x)+border,(fn.getHeight()*y)+border,((fn.stringWidth(" ")+1)*s),fn.getHeight()+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void printString(Graphics g,String s,int x,int y)
|
||||||
|
{
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
g.drawString(s,((fn.stringWidth(" ")+1)*x)+border,((fn.getHeight()*(y+1))-fn.getMaxDescent())+border);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void focusGained(FocusEvent e)
|
||||||
|
{
|
||||||
|
this.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void focusLost(FocusEvent e)
|
||||||
|
{
|
||||||
|
this.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void adjustmentValueChanged(AdjustmentEvent e)
|
||||||
|
{
|
||||||
|
inicio=e.getValue();
|
||||||
|
if(inicio<0) inicio=0;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseWheelMoved(MouseWheelEvent e)
|
||||||
|
{
|
||||||
|
inicio+=(e.getUnitsToScroll());
|
||||||
|
if((inicio+lineas)>=buff.length/16) inicio=(buff.length/16)-lineas;
|
||||||
|
if(inicio<0) inicio=0;
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyPressed(KeyEvent e)
|
||||||
|
{
|
||||||
|
switch(e.getKeyCode())
|
||||||
|
{
|
||||||
|
case 33: // rep
|
||||||
|
if(cursor>=(16*lineas)) cursor-=(16*lineas);
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 34: // fin
|
||||||
|
if(cursor<(buff.length-(16*lineas))) cursor+=(16*lineas);
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 35: // fin
|
||||||
|
cursor=buff.length-1;
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 36: // ini
|
||||||
|
cursor=0;
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 37: // <--
|
||||||
|
if(cursor!=0) cursor--;
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 38: // <--
|
||||||
|
if(cursor>15) cursor-=16;
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 39: // -->
|
||||||
|
if(cursor!=(buff.length-1)) cursor++;
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
case 40: // -->
|
||||||
|
if(cursor<(buff.length-16)) cursor+=16;
|
||||||
|
actualizaCursor();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Columnas extends JPanel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -1734199617526339842L;
|
||||||
|
|
||||||
|
public Columnas()
|
||||||
|
{
|
||||||
|
this.setLayout(new BorderLayout(1,1));
|
||||||
|
}
|
||||||
|
public Dimension getPreferredSize()
|
||||||
|
{
|
||||||
|
return getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getMinimumSize()
|
||||||
|
{
|
||||||
|
Dimension d=new Dimension();
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
int h=fn.getHeight();
|
||||||
|
int nl=1;
|
||||||
|
d.setSize(((fn.stringWidth(" ") + 1) * +((16 * 3) - 1))
|
||||||
|
+ (border * 2) + 1, h * nl + (border * 2) + 1);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paint(Graphics g)
|
||||||
|
{
|
||||||
|
Dimension d=getMinimumSize();
|
||||||
|
g.setColor(Color.white);
|
||||||
|
g.fillRect(0,0,d.width,d.height);
|
||||||
|
g.setColor(Color.black);
|
||||||
|
g.setFont(font);
|
||||||
|
|
||||||
|
for(int n=0;n<16;n++)
|
||||||
|
{
|
||||||
|
if(n==(cursor%16)) cuadro(g,n*3,0,2);
|
||||||
|
String s="00"+Integer.toHexString(n);
|
||||||
|
s=s.substring(s.length()-2);
|
||||||
|
printString(g,s,n*3,0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Caja extends JPanel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -6124062720565016834L;
|
||||||
|
|
||||||
|
public Dimension getPreferredSize()
|
||||||
|
{
|
||||||
|
return getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getMinimumSize()
|
||||||
|
{
|
||||||
|
Dimension d=new Dimension();
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
int h=fn.getHeight();
|
||||||
|
d.setSize((fn.stringWidth(" ")+1)+(border*2)+1,h+(border*2)+1);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Filas extends JPanel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8797347523486018051L;
|
||||||
|
|
||||||
|
public Filas()
|
||||||
|
{
|
||||||
|
this.setLayout(new BorderLayout(1,1));
|
||||||
|
}
|
||||||
|
public Dimension getPreferredSize()
|
||||||
|
{
|
||||||
|
return getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getMinimumSize()
|
||||||
|
{
|
||||||
|
Dimension d=new Dimension();
|
||||||
|
FontMetrics fn=getFontMetrics(font);
|
||||||
|
int h=fn.getHeight();
|
||||||
|
int nl=getLineas();
|
||||||
|
d.setSize((fn.stringWidth(" ")+1)*(8)+(border*2)+1,h*nl+(border*2)+1);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paint(Graphics g)
|
||||||
|
{
|
||||||
|
Dimension d=getMinimumSize();
|
||||||
|
g.setColor(Color.white);
|
||||||
|
g.fillRect(0,0,d.width,d.height);
|
||||||
|
g.setColor(Color.black);
|
||||||
|
g.setFont(font);
|
||||||
|
|
||||||
|
int ini=getInicio();
|
||||||
|
int fin=ini+getLineas();
|
||||||
|
int y=0;
|
||||||
|
for(int n=ini;n<fin;n++)
|
||||||
|
{
|
||||||
|
if(n==(cursor/16)) cuadro(g,0,y,8);
|
||||||
|
String s="0000000000000"+Integer.toHexString(n);
|
||||||
|
s=s.substring(s.length()-8);
|
||||||
|
printString(g,s,0,y++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBuff() {
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuff(byte[] buff) {
|
||||||
|
this.buff = buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
package com.jhe.hexed;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by IntelliJ IDEA.
|
||||||
|
* User: laullon
|
||||||
|
* Date: 09-abr-2003
|
||||||
|
* Time: 12:47:18
|
||||||
|
*/
|
||||||
|
public class JHexEditorASCII extends JComponent implements MouseListener,KeyListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5505374841731053461L;
|
||||||
|
private JHexEditor he;
|
||||||
|
|
||||||
|
public JHexEditorASCII(JHexEditor he)
|
||||||
|
{
|
||||||
|
this.he=he;
|
||||||
|
addMouseListener(this);
|
||||||
|
addKeyListener(this);
|
||||||
|
addFocusListener(he);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getPreferredSize()
|
||||||
|
{
|
||||||
|
debug("getPreferredSize()");
|
||||||
|
return getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getMinimumSize()
|
||||||
|
{
|
||||||
|
debug("getMinimumSize()");
|
||||||
|
|
||||||
|
Dimension d=new Dimension();
|
||||||
|
FontMetrics fn = getFontMetrics(JHexEditor.font);
|
||||||
|
int h=fn.getHeight();
|
||||||
|
int nl=he.getLineas();
|
||||||
|
d.setSize((fn.stringWidth(" ")+1)*(16)+(he.border*2)+1,h*nl+(he.border*2)+1);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paint(Graphics g)
|
||||||
|
{
|
||||||
|
debug("paint("+g+")");
|
||||||
|
debug("cursor="+he.cursor+" buff.length="+he.buff.length);
|
||||||
|
Dimension d=getMinimumSize();
|
||||||
|
g.setColor(Color.white);
|
||||||
|
g.fillRect(0,0,d.width,d.height);
|
||||||
|
g.setColor(Color.black);
|
||||||
|
|
||||||
|
g.setFont(JHexEditor.font);
|
||||||
|
|
||||||
|
//datos ascii
|
||||||
|
int ini=he.getInicio()*16;
|
||||||
|
int fin=ini+(he.getLineas()*16);
|
||||||
|
if(fin>he.buff.length) fin=he.buff.length;
|
||||||
|
|
||||||
|
int x=0;
|
||||||
|
int y=0;
|
||||||
|
for(int n=ini;n<fin;n++)
|
||||||
|
{
|
||||||
|
if(n==he.cursor)
|
||||||
|
{
|
||||||
|
g.setColor(Color.blue);
|
||||||
|
if(hasFocus()) he.fondo(g,x,y,1); else he.cuadro(g,x,y,1);
|
||||||
|
if(hasFocus()) g.setColor(Color.white); else g.setColor(Color.black);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
g.setColor(Color.black);
|
||||||
|
}
|
||||||
|
|
||||||
|
String s=""+new Character((char)he.buff[n]);
|
||||||
|
if((he.buff[n]<20)||(he.buff[n]>126)) s=""+(char)16;
|
||||||
|
he.printString(g,s,(x++),y);
|
||||||
|
if(x==16)
|
||||||
|
{
|
||||||
|
x=0;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debug(String s)
|
||||||
|
{
|
||||||
|
if(he.DEBUG) System.out.println("JHexEditorASCII ==> "+s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcular la posicion del raton
|
||||||
|
public int calcularPosicionRaton(int x,int y)
|
||||||
|
{
|
||||||
|
FontMetrics fn = getFontMetrics(JHexEditor.font);
|
||||||
|
x=x/(fn.stringWidth(" ")+1);
|
||||||
|
y=y/fn.getHeight();
|
||||||
|
debug("x="+x+" ,y="+y);
|
||||||
|
return x+((y+he.getInicio())*16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mouselistener
|
||||||
|
public void mouseClicked(MouseEvent e)
|
||||||
|
{
|
||||||
|
debug("mouseClicked("+e+")");
|
||||||
|
he.cursor=calcularPosicionRaton(e.getX(),e.getY());
|
||||||
|
this.requestFocus();
|
||||||
|
he.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mousePressed(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseReleased(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseEntered(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseExited(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//KeyListener
|
||||||
|
public void keyTyped(KeyEvent e)
|
||||||
|
{
|
||||||
|
debug("keyTyped("+e+")");
|
||||||
|
|
||||||
|
he.buff[he.cursor]=(byte)e.getKeyChar();
|
||||||
|
|
||||||
|
if(he.cursor!=(he.buff.length-1)) he.cursor++;
|
||||||
|
he.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyPressed(KeyEvent e)
|
||||||
|
{
|
||||||
|
debug("keyPressed("+e+")");
|
||||||
|
he.keyPressed(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyReleased(KeyEvent e)
|
||||||
|
{
|
||||||
|
debug("keyReleased("+e+")");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFocusTraversable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
package com.jhe.hexed;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.KeyListener;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by IntelliJ IDEA.
|
||||||
|
* User: laullon
|
||||||
|
* Date: 09-abr-2003
|
||||||
|
* Time: 12:47:32
|
||||||
|
*/
|
||||||
|
public class JHexEditorHEX extends JComponent implements MouseListener,KeyListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -4919858660968739876L;
|
||||||
|
private JHexEditor he;
|
||||||
|
private int cursor=0;
|
||||||
|
|
||||||
|
public JHexEditorHEX(JHexEditor he)
|
||||||
|
{
|
||||||
|
this.he=he;
|
||||||
|
addMouseListener(this);
|
||||||
|
addKeyListener(this);
|
||||||
|
addFocusListener(he);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getPreferredSize()
|
||||||
|
{
|
||||||
|
debug("getPreferredSize()");
|
||||||
|
return getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getMaximumSize()
|
||||||
|
{
|
||||||
|
debug("getMaximumSize()");
|
||||||
|
return getMinimumSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dimension getMinimumSize()
|
||||||
|
{
|
||||||
|
debug("getMinimumSize()");
|
||||||
|
|
||||||
|
Dimension d=new Dimension();
|
||||||
|
FontMetrics fn = getFontMetrics(JHexEditor.font);
|
||||||
|
int h=fn.getHeight();
|
||||||
|
int nl=he.getLineas();
|
||||||
|
d.setSize(((fn.stringWidth(" ")+1)*+((16*3)-1))+(he.border*2)+1,h*nl+(he.border*2)+1);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void paint(Graphics g)
|
||||||
|
{
|
||||||
|
debug("paint("+g+")");
|
||||||
|
debug("cursor="+he.cursor+" buff.length="+he.buff.length);
|
||||||
|
Dimension d=getMinimumSize();
|
||||||
|
g.setColor(Color.white);
|
||||||
|
g.fillRect(0,0,d.width,d.height);
|
||||||
|
g.setColor(Color.black);
|
||||||
|
|
||||||
|
g.setFont(JHexEditor.font);
|
||||||
|
|
||||||
|
int ini=he.getInicio()*16;
|
||||||
|
int fin=ini+(he.getLineas()*16);
|
||||||
|
if(fin>he.buff.length) fin=he.buff.length;
|
||||||
|
|
||||||
|
//datos hex
|
||||||
|
int x=0;
|
||||||
|
int y=0;
|
||||||
|
for(int n=ini;n<fin;n++)
|
||||||
|
{
|
||||||
|
if(n==he.cursor)
|
||||||
|
{
|
||||||
|
if(hasFocus())
|
||||||
|
{
|
||||||
|
g.setColor(Color.black);
|
||||||
|
he.fondo(g,(x*3),y,2);
|
||||||
|
g.setColor(Color.blue);
|
||||||
|
he.fondo(g,(x*3)+cursor,y,1);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
g.setColor(Color.blue);
|
||||||
|
he.cuadro(g,(x*3),y,2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hasFocus()) g.setColor(Color.white); else g.setColor(Color.black);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
g.setColor(Color.black);
|
||||||
|
}
|
||||||
|
|
||||||
|
String s=("0"+Integer.toHexString(he.buff[n]));
|
||||||
|
s=s.substring(s.length()-2);
|
||||||
|
he.printString(g,s,((x++)*3),y);
|
||||||
|
if(x==16)
|
||||||
|
{
|
||||||
|
x=0;
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debug(String s)
|
||||||
|
{
|
||||||
|
if(he.DEBUG) System.out.println("JHexEditorHEX ==> "+s);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcular la posicion del raton
|
||||||
|
public int calcularPosicionRaton(int x,int y)
|
||||||
|
{
|
||||||
|
FontMetrics fn = getFontMetrics(JHexEditor.font);
|
||||||
|
x=x/((fn.stringWidth(" ")+1)*3);
|
||||||
|
y=y/fn.getHeight();
|
||||||
|
debug("x="+x+" ,y="+y);
|
||||||
|
return x+((y+he.getInicio())*16);
|
||||||
|
}
|
||||||
|
|
||||||
|
// mouselistener
|
||||||
|
public void mouseClicked(MouseEvent e)
|
||||||
|
{
|
||||||
|
debug("mouseClicked("+e+")");
|
||||||
|
he.cursor=calcularPosicionRaton(e.getX(),e.getY());
|
||||||
|
this.requestFocus();
|
||||||
|
he.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mousePressed(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseReleased(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseEntered(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseExited(MouseEvent e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//KeyListener
|
||||||
|
public void keyTyped(KeyEvent e)
|
||||||
|
{
|
||||||
|
debug("keyTyped("+e+")");
|
||||||
|
|
||||||
|
char c=e.getKeyChar();
|
||||||
|
if(((c>='0')&&(c<='9'))||((c>='A')&&(c<='F'))||((c>='a')&&(c<='f')))
|
||||||
|
{
|
||||||
|
char[] str=new char[2];
|
||||||
|
String n="00"+Integer.toHexString((int)he.buff[he.cursor]);
|
||||||
|
if(n.length()>2) n=n.substring(n.length()-2);
|
||||||
|
str[1-cursor]=n.charAt(1-cursor);
|
||||||
|
str[cursor]=e.getKeyChar();
|
||||||
|
he.buff[he.cursor]=(byte)Integer.parseInt(new String(str),16);
|
||||||
|
|
||||||
|
if(cursor!=1) cursor=1;
|
||||||
|
else if(he.cursor!=(he.buff.length-1)){ he.cursor++; cursor=0;}
|
||||||
|
he.actualizaCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyPressed(KeyEvent e)
|
||||||
|
{
|
||||||
|
debug("keyPressed("+e+")");
|
||||||
|
he.keyPressed(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyReleased(KeyEvent e)
|
||||||
|
{
|
||||||
|
debug("keyReleased("+e+")");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFocusTraversable()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.jhe.hexed;
|
||||||
|
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by IntelliJ IDEA.
|
||||||
|
* User: laullon
|
||||||
|
* Date: 08-abr-2003
|
||||||
|
* Time: 13:16:06
|
||||||
|
*/
|
||||||
|
public class Test extends WindowAdapter
|
||||||
|
{
|
||||||
|
private JFrame win;
|
||||||
|
|
||||||
|
public Test() throws IOException
|
||||||
|
{
|
||||||
|
byte[] ar;
|
||||||
|
ar=new byte[16*16*100];
|
||||||
|
Arrays.fill(ar,(byte)0);
|
||||||
|
|
||||||
|
//ByteArrayOutputStream bos=new ByteArrayOutputStream();
|
||||||
|
//ObjectOutputStream oos=new ObjectOutputStream(bos);
|
||||||
|
//oos.writeObject("dfasnvcxnz.,mvnmc,xznvmcxzmnvcmxzcccbnxz cz hajk vc jbcvj xbnzvc sbj cvxz,bcxjnzbcvjhs avcjz cxmzncvxz ");
|
||||||
|
//ar=bos.toByteArray();
|
||||||
|
|
||||||
|
win=new JFrame();
|
||||||
|
win.getContentPane().add(new JHexEditor(ar));
|
||||||
|
win.addWindowListener(this);
|
||||||
|
win.pack();
|
||||||
|
win.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void windowClosing(WindowEvent e)
|
||||||
|
{
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String arg[]) throws IOException
|
||||||
|
{
|
||||||
|
new Test();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,512 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Event;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.ComponentEvent;
|
||||||
|
import java.awt.event.ComponentListener;
|
||||||
|
import java.awt.event.ContainerEvent;
|
||||||
|
import java.awt.event.ContainerListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
|
import javax.swing.JTextPane;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.event.CaretEvent;
|
||||||
|
import javax.swing.event.CaretListener;
|
||||||
|
import javax.swing.event.UndoableEditListener;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyleConstants;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
|
||||||
|
import tpsa.highlighters.SyntaxHighlighter;
|
||||||
|
import tpsa.streams.JTextPaneInputStream;
|
||||||
|
import tpsa.streams.JTextPaneOutputStream;
|
||||||
|
import tv.porst.jhexview.JHexView;
|
||||||
|
import tv.porst.jhexview.JHexView.DefinitionStatus;
|
||||||
|
import tv.porst.jhexview.SimpleDataProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zakładka tego ide. Każda zakładka posiada swoją maszynę
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BFTab extends JPanel implements CaretListener, ComponentListener,
|
||||||
|
ActionListener, ContainerListener {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 3256469085182527862L;
|
||||||
|
protected static final double startSplitterPosition = 0.5;
|
||||||
|
/**
|
||||||
|
* JTextPane w którym umieszczamy kod BF
|
||||||
|
*/
|
||||||
|
private JTextPane codePanel;
|
||||||
|
private File file;
|
||||||
|
private Style foundStyle;
|
||||||
|
private SyntaxHighlighter high;
|
||||||
|
private String lastCodeWorking;
|
||||||
|
private BFMachine machine;
|
||||||
|
private JScrollPane pane;
|
||||||
|
private JTextPane paneResult;
|
||||||
|
/**
|
||||||
|
* Label, który pokazuje aktualną pozycję w tekście
|
||||||
|
*/
|
||||||
|
private JLabel positionLabel;
|
||||||
|
private TextLineNumber rows;
|
||||||
|
private JScrollPane scrollPaneResult;
|
||||||
|
private JPanel status;
|
||||||
|
private JLabel statusLabel;
|
||||||
|
private JTextPane memoryPanel = new JTextPane();
|
||||||
|
private JSplitPane splitPane1;
|
||||||
|
private Timer t;
|
||||||
|
private JHexView editor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param undoListener
|
||||||
|
* undo/redo listener
|
||||||
|
* @param pauseAction
|
||||||
|
* pause akcja
|
||||||
|
* @param resumeAction
|
||||||
|
* resume akcja
|
||||||
|
* @param stepAction
|
||||||
|
* step akcja
|
||||||
|
* @param stopAction
|
||||||
|
* stop akcja
|
||||||
|
* @param runAction
|
||||||
|
* run akcja
|
||||||
|
* @param undoAction
|
||||||
|
* undo akcja
|
||||||
|
* @param redoAction
|
||||||
|
* redo akcja
|
||||||
|
*/
|
||||||
|
public BFTab(UndoableEditListener undoListener, Action pauseAction,
|
||||||
|
Action resumeAction, Action stepAction, Action stopAction,
|
||||||
|
Action runAction, Action undoAction, Action redoAction) {
|
||||||
|
|
||||||
|
t = new Timer(2000, this);
|
||||||
|
|
||||||
|
lastCodeWorking = new String();
|
||||||
|
codePanel = new JTextPane();
|
||||||
|
pane = new JScrollPane(codePanel);
|
||||||
|
rows = new TextLineNumber(codePanel);
|
||||||
|
|
||||||
|
status = new JPanel();
|
||||||
|
status.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
statusLabel = new JLabel("Ready");
|
||||||
|
positionLabel = new JLabel("0:0");
|
||||||
|
status.add(statusLabel, BorderLayout.WEST);
|
||||||
|
status.add(positionLabel, BorderLayout.EAST);
|
||||||
|
|
||||||
|
StyledDocument doc = (StyledDocument) codePanel.getDocument();
|
||||||
|
|
||||||
|
doc.addDocumentListener(rows);
|
||||||
|
doc.addUndoableEditListener(undoListener);
|
||||||
|
|
||||||
|
codePanel.addCaretListener(this);
|
||||||
|
|
||||||
|
pane.setRowHeaderView(rows);
|
||||||
|
|
||||||
|
pane.setFocusable(true);
|
||||||
|
|
||||||
|
KeyStroke undoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
KeyStroke redoKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Y,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
|
||||||
|
codePanel.getInputMap().put(undoKeyStroke, "undo");
|
||||||
|
codePanel.getInputMap().put(redoKeyStroke, "redo");
|
||||||
|
codePanel.getActionMap().put("undo", undoAction);
|
||||||
|
codePanel.getActionMap().put("redo", redoAction);
|
||||||
|
KeyStroke runWithoutStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F9,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
|
||||||
|
KeyStroke stopStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F4,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
|
||||||
|
codePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
runWithoutStroke, "RunWithoutDebug");
|
||||||
|
codePanel.getActionMap().put("RunWithoutDebug", runAction);
|
||||||
|
|
||||||
|
codePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
stopStroke, "StopAction");
|
||||||
|
codePanel.getActionMap().put("StopAction", stopAction);
|
||||||
|
|
||||||
|
KeyStroke stepStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F6,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
codePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
stepStroke, "stepAction");
|
||||||
|
codePanel.getActionMap().put("stepAction", stepAction);
|
||||||
|
|
||||||
|
// panel2.add(pane);
|
||||||
|
|
||||||
|
paneResult = new JTextPane();
|
||||||
|
KeyStroke eofStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
paneResult.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
eofStroke, "EOF");
|
||||||
|
|
||||||
|
KeyStroke pauseStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F5,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
codePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
pauseStroke, "pauseAction");
|
||||||
|
codePanel.getActionMap().put("pauseAction", pauseAction);
|
||||||
|
|
||||||
|
KeyStroke resumeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F7,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
codePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
resumeStroke, "resumeAction");
|
||||||
|
codePanel.getActionMap().put("resumeAction", resumeAction);
|
||||||
|
|
||||||
|
paneResult.getActionMap().put("EOF", new AbstractAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
Document doc = paneResult.getDocument();
|
||||||
|
String s = "\u001a";
|
||||||
|
try {
|
||||||
|
doc.insertString(doc.getLength(), s, null);
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
StyledDocument doc2 = (StyledDocument) paneResult.getDocument();
|
||||||
|
Style sInput = doc2.addStyle("INPUT", null);
|
||||||
|
StyleConstants.setForeground(sInput, Color.red);
|
||||||
|
Style sOutput = doc2.addStyle("OUTPUT", null);
|
||||||
|
StyleConstants.setForeground(sOutput, Color.GREEN);
|
||||||
|
Style sComment = doc.addStyle("COMMENT", null);
|
||||||
|
StyleConstants.setItalic(sComment, true);
|
||||||
|
StyleConstants.setForeground(sComment, Color.BLUE);
|
||||||
|
Style sCode = doc.addStyle("CODE", null);
|
||||||
|
StyleConstants.setForeground(sCode, Color.BLACK);
|
||||||
|
Style sError = doc.addStyle("ERROR", null);
|
||||||
|
StyleConstants.setUnderline(sError, true);
|
||||||
|
StyleConstants.setStrikeThrough(sError, true);
|
||||||
|
StyleConstants.setForeground(sError, Color.RED);
|
||||||
|
Style sBFCaret = doc.addStyle("BFCaret", null);
|
||||||
|
StyleConstants.setBackground(sBFCaret, Color.RED);
|
||||||
|
Style sBreakpoint = doc.addStyle("BREAKPOINT", null);
|
||||||
|
StyleConstants.setBackground(sBreakpoint, Color.CYAN);
|
||||||
|
|
||||||
|
Style empty = doc.addStyle("empty", null);
|
||||||
|
StyleConstants.setForeground(empty, Color.YELLOW);
|
||||||
|
|
||||||
|
Style foundStyle = doc.addStyle("found", null);
|
||||||
|
StyleConstants.setBackground(foundStyle, Color.BLUE);
|
||||||
|
|
||||||
|
high = new SyntaxHighlighter();
|
||||||
|
doc.addDocumentListener(high);
|
||||||
|
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
scrollPaneResult = new JScrollPane(paneResult);
|
||||||
|
|
||||||
|
scrollPaneResult.setPreferredSize(new Dimension(80, 80));
|
||||||
|
|
||||||
|
add(status, BorderLayout.NORTH);
|
||||||
|
add(pane, BorderLayout.CENTER);
|
||||||
|
add(scrollPaneResult, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WordsHighlighter w = new WordsHighlighter(new String[] { "BF",
|
||||||
|
* "TODO", "FIXME", "XXX", "WARNING", "ERROR", "CRITICAL", "DEBUG",
|
||||||
|
* "FUNCTION", "PROCEDURE" }); codePanel.setHighlighter(w);
|
||||||
|
* codePanel.getDocument().addDocumentListener(w);
|
||||||
|
*/
|
||||||
|
|
||||||
|
final JTextPaneInputStream is = new JTextPaneInputStream();
|
||||||
|
final JTextPaneOutputStream os = new JTextPaneOutputStream();
|
||||||
|
paneResult.getDocument().addDocumentListener(is);
|
||||||
|
os.setPane(paneResult);
|
||||||
|
|
||||||
|
machine = new BFMachine(is, os, "", BFMachine.defaultMemorySize);
|
||||||
|
memoryPanel.setEditable(false);
|
||||||
|
memoryPanel.setPreferredSize(new Dimension(200, 200));
|
||||||
|
|
||||||
|
splitPane1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
|
||||||
|
editor = new JHexView();
|
||||||
|
editor.setData(new SimpleDataProvider(machine.getMemory()));
|
||||||
|
editor.setDefinitionStatus(DefinitionStatus.DEFINED);
|
||||||
|
editor.setEnabled(true);
|
||||||
|
|
||||||
|
// editor.set
|
||||||
|
splitPane1.add(pane);
|
||||||
|
splitPane1.add(editor);
|
||||||
|
splitPane1.setSize(new Dimension(800, 600));
|
||||||
|
|
||||||
|
addComponentListener(this);
|
||||||
|
// t.start();
|
||||||
|
|
||||||
|
add(splitPane1, BorderLayout.CENTER);
|
||||||
|
splitPane1.setDividerLocation(BFTab.startSplitterPosition);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void caretUpdate(final CaretEvent e) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
JTextPane pane = (JTextPane) e.getSource();
|
||||||
|
BrainFuckIDE.lg.fine("Caret update" + e.getDot());
|
||||||
|
|
||||||
|
Document doc = pane.getDocument();
|
||||||
|
try {
|
||||||
|
String s = doc.getText(0, e.getDot());
|
||||||
|
TextEditorPosition pos = getLineNumer(s);
|
||||||
|
positionLabel.setText("" + pos.Line + ":" + pos.Offset);
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTextPane getArea() {
|
||||||
|
return codePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Style getFoundStyle() {
|
||||||
|
return foundStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SyntaxHighlighter getHigh() {
|
||||||
|
return high;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastCodeWorking() {
|
||||||
|
return lastCodeWorking;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected TextEditorPosition getLineNumer(String s) {
|
||||||
|
String lineSeparator = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
int line = 1;
|
||||||
|
int pos = -1, last = 0;
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.finer("contents: " + s);
|
||||||
|
|
||||||
|
while ((pos = s.indexOf(lineSeparator, pos + lineSeparator.length())) >= 0) {
|
||||||
|
BrainFuckIDE.lg.finer("found separator: " + pos);
|
||||||
|
line++;
|
||||||
|
last = pos + lineSeparator.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextEditorPosition(line, s.length() - last);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BFMachine getMachine() {
|
||||||
|
return machine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JScrollPane getPane() {
|
||||||
|
return pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTextPane getPaneResult() {
|
||||||
|
return paneResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JLabel getPositionLabel() {
|
||||||
|
return positionLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextLineNumber getRows() {
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JScrollPane getScrollPaneResult() {
|
||||||
|
return scrollPaneResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPanel getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JLabel getStatusLabel() {
|
||||||
|
return statusLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArea(JTextPane area) {
|
||||||
|
this.codePanel = area;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFile(File file) {
|
||||||
|
this.file = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFoundStyle(Style foundStyle) {
|
||||||
|
this.foundStyle = foundStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHigh(SyntaxHighlighter high) {
|
||||||
|
this.high = high;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastCodeWorking(String lastCodeWorking) {
|
||||||
|
this.lastCodeWorking = lastCodeWorking;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMachine(BFMachine machine) {
|
||||||
|
this.machine = machine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPane(JScrollPane pane) {
|
||||||
|
this.pane = pane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaneResult(JTextPane paneResult) {
|
||||||
|
this.paneResult = paneResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPositionLabel(JLabel positionLabel) {
|
||||||
|
this.positionLabel = positionLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRows(TextLineNumber rows) {
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScrollPaneResult(JScrollPane scrollPaneResult) {
|
||||||
|
this.scrollPaneResult = scrollPaneResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(JPanel status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatusLabel(JLabel statusLabel) {
|
||||||
|
this.statusLabel = statusLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentResized(ComponentEvent e) {
|
||||||
|
// splitPane1.setDividerLocation(BFTab.startSplitterPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentMoved(ComponentEvent e) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentShown(ComponentEvent e) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentHidden(ComponentEvent e) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.fine("Refreshing code syntax");
|
||||||
|
StyledDocument doc = codePanel.getStyledDocument();
|
||||||
|
high.update(doc, 0, doc.getLength(), machine.isWorking());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentAdded(ContainerEvent e) {
|
||||||
|
if (e.getChild() == this) {
|
||||||
|
BrainFuckIDE.lg.info("Component added");
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void componentRemoved(ContainerEvent e) {
|
||||||
|
if (e.getChild() == this) {
|
||||||
|
machine.stop();
|
||||||
|
e.getContainer().removeContainerListener(this);
|
||||||
|
BrainFuckIDE.lg.info("Component removed");
|
||||||
|
t.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JHexView getEditor() {
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEditor(JHexView editor) {
|
||||||
|
this.editor = editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTextPane getCodePanel() {
|
||||||
|
return codePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCodePanel(JTextPane codePanel) {
|
||||||
|
this.codePanel = codePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTextPane getMemoryPanel() {
|
||||||
|
return memoryPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMemoryPanel(JTextPane memoryPanel) {
|
||||||
|
this.memoryPanel = memoryPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSplitPane getSplitPane1() {
|
||||||
|
return splitPane1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSplitPane1(JSplitPane splitPane1) {
|
||||||
|
this.splitPane1 = splitPane1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pobiera timer dla syntaxu
|
||||||
|
*
|
||||||
|
* @return timer
|
||||||
|
*/
|
||||||
|
public Timer getT() {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param t
|
||||||
|
* Timer
|
||||||
|
*/
|
||||||
|
public void setT(Timer t) {
|
||||||
|
this.t = t;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.LogManager;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
public class BrainFuckIDE {
|
||||||
|
|
||||||
|
static public Logger lg;
|
||||||
|
// protected static final long milsToWaitBeforeKill = 60000;
|
||||||
|
static private LogManager lm;
|
||||||
|
static private Level lv = Level.OFF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
UIManager
|
||||||
|
.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
|
||||||
|
} catch (Exception excp) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
BrainFuckIDE.lm = LogManager.getLogManager();
|
||||||
|
BrainFuckIDE.lm.addLogger(Logger.getLogger("BFIDE"));
|
||||||
|
BrainFuckIDE.lg = lm.getLogger("BFIDE");
|
||||||
|
BrainFuckIDE.lg.setLevel(lv);
|
||||||
|
for (Handler h : BrainFuckIDE.lg.getHandlers())
|
||||||
|
h.setLevel(lv);
|
||||||
|
|
||||||
|
for (Handler h : Logger.getLogger("").getHandlers())
|
||||||
|
h.setLevel(lv);
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.info("Brainfuck IDE is starting");
|
||||||
|
|
||||||
|
MainFrame mf = new MainFrame();
|
||||||
|
mf.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param excp
|
||||||
|
* Wyjątek ;-)
|
||||||
|
* @return stringi opisujące ten wyjątek w strawnej postaci
|
||||||
|
*/
|
||||||
|
|
||||||
|
public static String ErrorsFormatter(Exception excp) {
|
||||||
|
|
||||||
|
// System.setOut(null);
|
||||||
|
// System.setErr(null);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
String newLine = System.getProperty("line.separator");
|
||||||
|
|
||||||
|
sb.append("Exception catched: " + excp.getLocalizedMessage() + newLine);
|
||||||
|
StackTraceElement[] stack = excp.getStackTrace();
|
||||||
|
for (int i = 0; i < stack.length; i++) {
|
||||||
|
sb.append("StackElement(" + i + ") : " + stack[i].getClassName()
|
||||||
|
+ ", " + stack[i].getFileName() + ":"
|
||||||
|
+ stack[i].getLineNumber() + ", "
|
||||||
|
+ stack[i].getMethodName() + newLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Oracle or the names of its
|
||||||
|
* contributors may be used to endorse or promote products derived
|
||||||
|
* from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||||
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.event.MouseListener;
|
||||||
|
|
||||||
|
import javax.swing.AbstractButton;
|
||||||
|
import javax.swing.BorderFactory;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.plaf.basic.BasicButtonUI;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to be used as tabComponent;
|
||||||
|
* Contains a JLabel to show the text and
|
||||||
|
* a JButton to close the tab it belongs to
|
||||||
|
*/
|
||||||
|
public class ButtonTabComponent extends JPanel {
|
||||||
|
private class TabButton extends JButton implements ActionListener {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -7852819509277461601L;
|
||||||
|
|
||||||
|
public TabButton() {
|
||||||
|
int size = 17;
|
||||||
|
setPreferredSize(new Dimension(size, size));
|
||||||
|
setToolTipText("close this tab");
|
||||||
|
//Make the button looks the same for all Laf's
|
||||||
|
setUI(new BasicButtonUI());
|
||||||
|
//Make it transparent
|
||||||
|
setContentAreaFilled(false);
|
||||||
|
//No need to be focusable
|
||||||
|
setFocusable(false);
|
||||||
|
setBorder(BorderFactory.createEtchedBorder());
|
||||||
|
setBorderPainted(false);
|
||||||
|
//Making nice rollover effect
|
||||||
|
//we use the same listener for all buttons
|
||||||
|
addMouseListener(buttonMouseListener);
|
||||||
|
setRolloverEnabled(true);
|
||||||
|
//Close the proper tab by clicking the button
|
||||||
|
addActionListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
int i = pane.indexOfTabComponent(ButtonTabComponent.this);
|
||||||
|
if (i != -1) {
|
||||||
|
pane.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//paint the cross
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
//shift the image for pressed buttons
|
||||||
|
if (getModel().isPressed()) {
|
||||||
|
g2.translate(1, 1);
|
||||||
|
}
|
||||||
|
g2.setStroke(new BasicStroke(2));
|
||||||
|
g2.setColor(Color.BLACK);
|
||||||
|
if (getModel().isRollover()) {
|
||||||
|
g2.setColor(Color.MAGENTA);
|
||||||
|
}
|
||||||
|
int delta = 6;
|
||||||
|
g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
|
||||||
|
g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
//we don't want to update UI for this button
|
||||||
|
@Override
|
||||||
|
public void updateUI() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private final static MouseListener buttonMouseListener = new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent e) {
|
||||||
|
Component component = e.getComponent();
|
||||||
|
if (component instanceof AbstractButton) {
|
||||||
|
AbstractButton button = (AbstractButton) component;
|
||||||
|
button.setBorderPainted(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e) {
|
||||||
|
Component component = e.getComponent();
|
||||||
|
if (component instanceof AbstractButton) {
|
||||||
|
AbstractButton button = (AbstractButton) component;
|
||||||
|
button.setBorderPainted(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -2329475802746012869L;
|
||||||
|
|
||||||
|
private final JTabbedPane pane;
|
||||||
|
|
||||||
|
public ButtonTabComponent(final JTabbedPane pane) {
|
||||||
|
//unset default FlowLayout' gaps
|
||||||
|
super(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||||
|
if (pane == null) {
|
||||||
|
throw new NullPointerException("TabbedPane is null");
|
||||||
|
}
|
||||||
|
this.pane = pane;
|
||||||
|
setOpaque(false);
|
||||||
|
|
||||||
|
//make JLabel read titles from JTabbedPane
|
||||||
|
JLabel label = new JLabel() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 6069043534335090073L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getText() {
|
||||||
|
int i = pane.indexOfTabComponent(ButtonTabComponent.this);
|
||||||
|
if (i != -1) {
|
||||||
|
return pane.getTitleAt(i);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
add(label);
|
||||||
|
//add more space between the label and the button
|
||||||
|
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
|
||||||
|
//tab button
|
||||||
|
JButton button = new TabButton();
|
||||||
|
add(button);
|
||||||
|
//add more space to the top of the component
|
||||||
|
setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,970 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Container;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Event;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JCheckBoxMenuItem;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JFileChooser;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.JViewport;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.event.CaretEvent;
|
||||||
|
import javax.swing.event.CaretListener;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import javax.swing.event.UndoableEditEvent;
|
||||||
|
import javax.swing.event.UndoableEditListener;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.DefaultEditorKit;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
import javax.swing.undo.UndoManager;
|
||||||
|
|
||||||
|
import tpsa.dialogs.AboutProgramDialog;
|
||||||
|
import tpsa.dialogs.FindDialog;
|
||||||
|
import tpsa.dialogs.HelpDialog;
|
||||||
|
import tpsa.dialogs.MemorySizeDialog;
|
||||||
|
import tpsa.dialogs.ReplaceDialog;
|
||||||
|
import tpsa.dialogs.filters.BFFileFilter;
|
||||||
|
import tpsa.highlighters.SyntaxHighlighter;
|
||||||
|
|
||||||
|
// DONE liczenie klamerek (bilans)
|
||||||
|
// DONE uruchomienie ostatniej poprawnej wersji BrainFucka
|
||||||
|
// DONE zróżnicowanie podświetlenia (prawie zrobione - działa, ale więcej)
|
||||||
|
// DONE ustalenie skończonej pamięci
|
||||||
|
// DONE wyświetlanie ładnych wyjątków
|
||||||
|
// DONE wyróżnienie komentarzy
|
||||||
|
// DONE debugowanie
|
||||||
|
// DONE wyświetlanie stanu taśmy
|
||||||
|
// DONE scenariusze testowe - przykłady działające, i nie działające, poprawnie obsłużyć
|
||||||
|
// DONE krótki opis BrainFucka
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa głowna edytora, odpowiedzialna za GUI edytora
|
||||||
|
*
|
||||||
|
* @author tpsa
|
||||||
|
*/
|
||||||
|
public class MainFrame extends JFrame implements CaretListener,
|
||||||
|
UndoableEditListener, ChangeListener {
|
||||||
|
static int lastBFPositionShowing = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -4950512615304159756L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* JTextPane w którym umieszczamy kod BF
|
||||||
|
*/
|
||||||
|
private AbstractAction findAction;
|
||||||
|
private FindDialog findDialog;
|
||||||
|
private Action newFileAction;
|
||||||
|
private AbstractAction openAction;
|
||||||
|
private AbstractAction pauseAction;
|
||||||
|
private JMenuItem pauseMachine;
|
||||||
|
/**
|
||||||
|
* Label, który pokazuje aktualną pozycję w tekście
|
||||||
|
*/
|
||||||
|
private JMenuItem redo;
|
||||||
|
private RedoAction redoAction;
|
||||||
|
private AbstractAction replaceAction;
|
||||||
|
private ReplaceDialog replaceDialog;
|
||||||
|
private AbstractAction resumeAction;
|
||||||
|
private JMenuItem resumeMachine;
|
||||||
|
private AbstractAction runAction;
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private AbstractAction runLastWorkingCode = null;
|
||||||
|
private JMenuItem runWithoutDebug;
|
||||||
|
private AbstractAction saveAction;
|
||||||
|
private AbstractAction saveKnownAction;
|
||||||
|
private AbstractAction showLinesAction;
|
||||||
|
private Action showStatusAction;
|
||||||
|
|
||||||
|
private JMenuItem showStatusLabel;
|
||||||
|
|
||||||
|
private Action stepAction;
|
||||||
|
private JMenuItem stepMachine;
|
||||||
|
private AbstractAction stopAction;
|
||||||
|
private JMenuItem stopMachine;
|
||||||
|
private JTabbedPane tabs = new JTabbedPane();
|
||||||
|
private JMenuItem undo;
|
||||||
|
|
||||||
|
private UndoAction undoAction;
|
||||||
|
|
||||||
|
private UndoManager undoManager = new UndoManager();
|
||||||
|
|
||||||
|
private AbstractAction runActionWithDebug;
|
||||||
|
|
||||||
|
private AbstractAction showMemoryPanelAction;
|
||||||
|
|
||||||
|
protected byte[] lastWorkingCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tworzy instancję klasy MainFrame
|
||||||
|
*/
|
||||||
|
public MainFrame() {
|
||||||
|
super("Brainfuck IDE");
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.info("Creating main frame of Brainfuck IDE");
|
||||||
|
|
||||||
|
tabs.addChangeListener(this);
|
||||||
|
|
||||||
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
setPreferredSize(new Dimension(800, 600));
|
||||||
|
|
||||||
|
replaceDialog = new ReplaceDialog(this);
|
||||||
|
findDialog = new FindDialog(this);
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.fine("Setting actions");
|
||||||
|
setActions();
|
||||||
|
|
||||||
|
JMenuBar menuBar = getMainFrameMenuBar();
|
||||||
|
setJMenuBar(menuBar);
|
||||||
|
|
||||||
|
// tab.getArea().addMouseListener(this);
|
||||||
|
// tab.getArea().addMouseMotionListener(this);
|
||||||
|
|
||||||
|
undoAction = new UndoAction(undoManager, undo, null);
|
||||||
|
redoAction = new RedoAction(undoManager, redo, undoAction);
|
||||||
|
undo.setAction(undoAction);
|
||||||
|
undo.setText("Undo");
|
||||||
|
redo.setAction(redoAction);
|
||||||
|
redo.setText("Redo");
|
||||||
|
|
||||||
|
undoAction.setRedoAction(redoAction);
|
||||||
|
|
||||||
|
BFTab tab1 = new BFTab(this, pauseAction, resumeAction, stepAction,
|
||||||
|
stopAction, runAction, undoAction, redoAction);
|
||||||
|
tabs.addContainerListener(tab1);
|
||||||
|
|
||||||
|
tabs.add(tab1);
|
||||||
|
int idx = tabs.indexOfComponent(tab1);
|
||||||
|
tabs.setTitleAt(idx, "Untitled");
|
||||||
|
tabs.setTabComponentAt(idx, new ButtonTabComponent(tabs));
|
||||||
|
add(tabs, BorderLayout.CENTER);
|
||||||
|
// add(status, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.info("Adding document listener");
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void caretUpdate(final CaretEvent e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generuje menu programu
|
||||||
|
*
|
||||||
|
* @return Zwraca instancję JMenuBar (menu), dla programu
|
||||||
|
*/
|
||||||
|
private JMenuBar getMainFrameMenuBar() {
|
||||||
|
JMenuBar menuBar = new JMenuBar();
|
||||||
|
|
||||||
|
JMenu file = new JMenu("File");
|
||||||
|
JMenu edit = new JMenu("Edit");
|
||||||
|
JMenu view = new JMenu("View");
|
||||||
|
JMenu run = new JMenu("Run");
|
||||||
|
JMenu help = new JMenu("Help");
|
||||||
|
|
||||||
|
menuBar.add(file);
|
||||||
|
menuBar.add(edit);
|
||||||
|
menuBar.add(view);
|
||||||
|
menuBar.add(run);
|
||||||
|
menuBar.add(help);
|
||||||
|
|
||||||
|
help.add(new JMenuItem(new AbstractAction("About program") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5839320025951821584L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("About program action performed");
|
||||||
|
Container c = tabs.getTopLevelAncestor();
|
||||||
|
AboutProgramDialog dialog = new AboutProgramDialog((JFrame) c);
|
||||||
|
dialog.setLocationRelativeTo(c);
|
||||||
|
dialog.setLocation(c.getWidth() / 2, c.getHeight() / 2);
|
||||||
|
dialog.showDialog();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
help.add(new JMenuItem(new AbstractAction("Tutorial") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -6114772901713788593L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Help action performed");
|
||||||
|
Container c = tabs.getTopLevelAncestor();
|
||||||
|
HelpDialog dialog = new HelpDialog((JFrame) c);
|
||||||
|
dialog.setLocationRelativeTo(c);
|
||||||
|
dialog.setLocation(c.getWidth() / 2, c.getHeight() / 2);
|
||||||
|
dialog.showDialog();
|
||||||
|
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
JMenuItem newFile = new JMenuItem("New file");
|
||||||
|
newFile.setAction(newFileAction);
|
||||||
|
JMenuItem open = new JMenuItem("Open");
|
||||||
|
open.setAction(openAction);
|
||||||
|
JMenuItem save = new JMenuItem("Save");
|
||||||
|
JMenuItem saveAs = new JMenuItem("Save as");
|
||||||
|
saveAs.setAction(saveAction);
|
||||||
|
JMenuItem quit = new JMenuItem("Quit");
|
||||||
|
|
||||||
|
quit.addActionListener(new ActionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
file.add(newFile);
|
||||||
|
file.add(open);
|
||||||
|
save.setAction(saveKnownAction);
|
||||||
|
file.add(save);
|
||||||
|
file.add(saveAs);
|
||||||
|
file.add(quit);
|
||||||
|
|
||||||
|
undo = new JMenuItem("Undo");
|
||||||
|
redo = new JMenuItem("Redo");
|
||||||
|
|
||||||
|
JMenuItem cut = new JMenuItem("Cut");
|
||||||
|
cut.addActionListener(new DefaultEditorKit.CutAction());
|
||||||
|
// cut.setMnemonic(KeyEvent.VK_X);
|
||||||
|
|
||||||
|
JMenuItem copy = new JMenuItem("Copy");
|
||||||
|
copy.addActionListener(new DefaultEditorKit.CopyAction());
|
||||||
|
// copy.setMnemonic(KeyEvent.VK_C);
|
||||||
|
|
||||||
|
JMenuItem paste = new JMenuItem("Paste");
|
||||||
|
paste.addActionListener(new DefaultEditorKit.PasteAction());
|
||||||
|
// paste.setMnemonic(KeyEvent.VK_P);
|
||||||
|
|
||||||
|
JMenuItem find = new JMenuItem("Find");
|
||||||
|
find.setAction(findAction);
|
||||||
|
|
||||||
|
KeyStroke findStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
find.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(findStroke,
|
||||||
|
"find");
|
||||||
|
find.getActionMap().put("find", findAction);
|
||||||
|
|
||||||
|
JMenuItem replace = new JMenuItem("Replace");
|
||||||
|
replace.setAction(replaceAction);
|
||||||
|
|
||||||
|
KeyStroke replaceStroke = KeyStroke.getKeyStroke(KeyEvent.VK_R,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
find.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(replaceStroke,
|
||||||
|
"replace");
|
||||||
|
find.getActionMap().put("replace", replaceAction);
|
||||||
|
|
||||||
|
KeyStroke newFileStroke = KeyStroke.getKeyStroke(KeyEvent.VK_N,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
newFile.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
newFileStroke, "newFile");
|
||||||
|
newFile.getActionMap().put("newFile", newFileAction);
|
||||||
|
|
||||||
|
KeyStroke saveFileStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S,
|
||||||
|
Event.CTRL_MASK);
|
||||||
|
save.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(saveFileStroke,
|
||||||
|
"saveFile");
|
||||||
|
save.getActionMap().put("saveFile", saveKnownAction);
|
||||||
|
|
||||||
|
edit.add(undo);
|
||||||
|
edit.add(redo);
|
||||||
|
edit.addSeparator();
|
||||||
|
edit.add(cut);
|
||||||
|
edit.add(copy);
|
||||||
|
edit.add(paste);
|
||||||
|
edit.addSeparator();
|
||||||
|
edit.add(find);
|
||||||
|
edit.add(replace);
|
||||||
|
edit.addSeparator();
|
||||||
|
edit.add(new JMenuItem(new AbstractAction("Memory preferences") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 6096201895928552672L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Memory preferences action performed");
|
||||||
|
MainFrame frame = (MainFrame) tabs.getTopLevelAncestor();
|
||||||
|
|
||||||
|
MemorySizeDialog dialog = new MemorySizeDialog(frame);
|
||||||
|
dialog.setSizeMemory(BFMachine.defaultMemorySize);
|
||||||
|
if (!dialog.showDialog())
|
||||||
|
return;
|
||||||
|
|
||||||
|
BFMachine.defaultMemorySize = dialog.getSizeMemory();
|
||||||
|
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
JMenuItem showLineNumbers = new JMenuItem("Show line numbers");
|
||||||
|
showStatusLabel = new JCheckBoxMenuItem("Show status label");
|
||||||
|
|
||||||
|
view.add(showLineNumbers);
|
||||||
|
|
||||||
|
showLineNumbers.setAction(showLinesAction);
|
||||||
|
|
||||||
|
view.add(showMemoryPanelAction);
|
||||||
|
|
||||||
|
view.add(showStatusLabel);
|
||||||
|
|
||||||
|
showStatusLabel.setAction(showStatusAction);
|
||||||
|
|
||||||
|
runWithoutDebug = new JMenuItem("Run without debug");
|
||||||
|
pauseMachine = new JMenuItem("Pause machine");
|
||||||
|
resumeMachine = new JMenuItem("Resume machine");
|
||||||
|
stopMachine = new JMenuItem("Stop machine");
|
||||||
|
stepMachine = new JMenuItem("Step machine");
|
||||||
|
|
||||||
|
runWithoutDebug.setAction(runAction);
|
||||||
|
pauseMachine.setAction(pauseAction);
|
||||||
|
resumeMachine.setAction(resumeAction);
|
||||||
|
stopMachine.setAction(stopAction);
|
||||||
|
stepMachine.setAction(stepAction);
|
||||||
|
|
||||||
|
run.add(runActionWithDebug);
|
||||||
|
run.add(runWithoutDebug);
|
||||||
|
// run.add(runLastWorkingCode);
|
||||||
|
run.add(pauseMachine);
|
||||||
|
run.add(resumeMachine);
|
||||||
|
run.add(stopMachine);
|
||||||
|
run.add(stepAction);
|
||||||
|
|
||||||
|
return menuBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustawia akcje np. find, replace Każda akcja ma reakcję
|
||||||
|
*/
|
||||||
|
private void setActions() {
|
||||||
|
|
||||||
|
showMemoryPanelAction = new AbstractAction("Show memory panel") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 2012577125654615111L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Show memory action performed");
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
if (tab == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
JSplitPane pane = tab.getSplitPane1();
|
||||||
|
ArrayList<Component> components = new ArrayList<Component>(
|
||||||
|
Arrays.asList(pane.getComponents()));
|
||||||
|
BrainFuckIDE.lg.finest("Got components");
|
||||||
|
|
||||||
|
boolean contains = false;
|
||||||
|
for (Component c : components)
|
||||||
|
if (c == tab.getEditor()) {
|
||||||
|
contains = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println(components.size());
|
||||||
|
if (contains) {
|
||||||
|
BrainFuckIDE.lg.info("Removing memory panel");
|
||||||
|
pane.remove(tab.getEditor());
|
||||||
|
} else {
|
||||||
|
BrainFuckIDE.lg.info("Adding memory panel");
|
||||||
|
pane.add(tab.getEditor(), 1);
|
||||||
|
pane.setDividerLocation(BFTab.startSplitterPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
newFileAction = new AbstractAction("New file") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5483399276425267838L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
MainFrame frame = (MainFrame) tabs.getTopLevelAncestor();
|
||||||
|
BFTab tab = new BFTab(frame, pauseAction, resumeAction,
|
||||||
|
stepAction, stopAction, runAction, undoAction,
|
||||||
|
redoAction);
|
||||||
|
tabs.addContainerListener(tab);
|
||||||
|
tabs.add(tab);
|
||||||
|
int idx = tabs.indexOfComponent(tab);
|
||||||
|
tabs.setTitleAt(idx, "Untitled");
|
||||||
|
tabs.setTabComponentAt(idx, new ButtonTabComponent(tabs));
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runAction = new AbstractAction("RunWithoutDebug") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5839320025951821584L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Run action executed");
|
||||||
|
|
||||||
|
final BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
Document doc = tab.getPaneResult().getDocument();
|
||||||
|
final BFMachine machine = tab.getMachine();
|
||||||
|
|
||||||
|
final Thread t = prepareMachine(tab, doc, machine);
|
||||||
|
if (t == null)
|
||||||
|
return;
|
||||||
|
machine.setStepping(false);
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runActionWithDebug = new AbstractAction("Run with debug") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 6096201895928552672L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Run action executed");
|
||||||
|
|
||||||
|
final BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
Document doc = tab.getPaneResult().getDocument();
|
||||||
|
final BFMachine machine = tab.getMachine();
|
||||||
|
|
||||||
|
final Thread t = prepareMachine(tab, doc, machine);
|
||||||
|
if (t == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
machine.setStepping(true);
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
showStatusAction = new AbstractAction("ShowStatusLabel") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 2012577125654615111L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
// BrainFuckIDE.lg.finest("showStatusLabel.isSelected());
|
||||||
|
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
Component[] comp = tab.getComponents();
|
||||||
|
ArrayList<Component> l = new ArrayList<Component>(
|
||||||
|
Arrays.asList(comp));
|
||||||
|
|
||||||
|
boolean contains = l.contains(tab.getStatus());
|
||||||
|
|
||||||
|
if (!contains) {
|
||||||
|
tab.add(tab.getStatus(), BorderLayout.NORTH);
|
||||||
|
} else
|
||||||
|
tab.remove(tab.getStatus());
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
showLinesAction = new AbstractAction("ShowLines") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 2012577125654615111L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
JScrollPane pane = tab.getPane();
|
||||||
|
JViewport port = pane.getRowHeader();
|
||||||
|
if (port == null)
|
||||||
|
pane.setRowHeaderView(tab.getRows());
|
||||||
|
else
|
||||||
|
pane.setRowHeader(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
runLastWorkingCode = new AbstractAction("Run last working code") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -1126695922005433078L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
if (tab == null)
|
||||||
|
return;
|
||||||
|
StyledDocument doc = tab.getArea().getStyledDocument();
|
||||||
|
BFMachine machine = tab.getMachine();
|
||||||
|
if (machine.isWorking()) {
|
||||||
|
JOptionPane.showMessageDialog(tabs.getTopLevelAncestor(),
|
||||||
|
"Machine is working");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Thread t = prepareMachine(tab, doc, machine);
|
||||||
|
if (lastWorkingCode == null) {
|
||||||
|
JOptionPane.showMessageDialog(tabs.getTopLevelAncestor(),
|
||||||
|
"no working code");
|
||||||
|
tab.getStatusLabel().setText("Ready");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
machine.setCode(lastWorkingCode);
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pauseAction = new AbstractAction("Pause machine") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 6096201895928552672L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Pause action performed");
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
BFMachine machine = tab.getMachine();
|
||||||
|
|
||||||
|
if (!machine.isWorking()) {
|
||||||
|
JOptionPane.showMessageDialog(null, "No maching running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
machine.pause();
|
||||||
|
System.err.println("Paused");
|
||||||
|
tab.getStatusLabel().setText("Machine paused");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resumeAction = new AbstractAction("Resume machine") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 2012577125654615111L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.info("Resume action performed");
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
BFMachine machine = tab.getMachine();
|
||||||
|
|
||||||
|
if (!machine.isWorking()) {
|
||||||
|
JOptionPane.showMessageDialog(null, "No maching running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
machine.setStepping(false);
|
||||||
|
machine.resume();
|
||||||
|
tab.getStatusLabel().setText("Running...");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stopAction = new AbstractAction("Stop machine") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -2599448412251509415L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Stop action performed");
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
BFMachine machine = tab.getMachine();
|
||||||
|
if (!machine.isWorking()) {
|
||||||
|
JOptionPane.showMessageDialog(null, "No maching running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
machine.stop();
|
||||||
|
tab.getStatusLabel().setText("Machine stopped - ready...");
|
||||||
|
tab.getArea().setEditable(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
stepAction = new AbstractAction("StepAction") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5880656879834148166L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Step action performed");
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
BFMachine machine = tab.getMachine();
|
||||||
|
if (!machine.isWorking()) {
|
||||||
|
JOptionPane.showMessageDialog(getParent(),
|
||||||
|
"Machine is not working");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
machine.resume();
|
||||||
|
machine.setStepping(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
openAction = new AbstractAction("open") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 4575053887363265531L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
JFileChooser chooser = new JFileChooser(".");
|
||||||
|
chooser.setFileFilter(new BFFileFilter());
|
||||||
|
if (chooser.showDialog(null, "Otwórz") == JFileChooser.APPROVE_OPTION) {
|
||||||
|
|
||||||
|
File f = chooser.getSelectedFile();
|
||||||
|
BufferedReader r = new BufferedReader(
|
||||||
|
new InputStreamReader(new FileInputStream(f)));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
char[] buffer = new char[0x1000];
|
||||||
|
|
||||||
|
while (r.read(buffer) >= 0)
|
||||||
|
sb.append(buffer);
|
||||||
|
|
||||||
|
r.close();
|
||||||
|
|
||||||
|
MainFrame frame = (MainFrame) tabs
|
||||||
|
.getTopLevelAncestor();
|
||||||
|
BFTab tab = new BFTab(frame, pauseAction, resumeAction,
|
||||||
|
stepAction, stopAction, runAction, undoAction,
|
||||||
|
redoAction);
|
||||||
|
tabs.add(tab);
|
||||||
|
int idx = tabs.indexOfComponent(tab);
|
||||||
|
tab.setFile(f);
|
||||||
|
tabs.setTitleAt(idx, f.getName());
|
||||||
|
tabs.setTabComponentAt(idx,
|
||||||
|
new ButtonTabComponent(tabs));
|
||||||
|
|
||||||
|
tabs.setSelectedIndex(idx);
|
||||||
|
|
||||||
|
tab.getArea().setText(sb.toString());
|
||||||
|
tab.getArea().setCaretPosition(0);
|
||||||
|
}
|
||||||
|
} catch (IOException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
saveKnownAction = new AbstractAction("Save") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -985908773082887131L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
if (tab == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
File f = tab.getFile();
|
||||||
|
if (f == null) {
|
||||||
|
saveAction.actionPerformed(e);
|
||||||
|
f = tab.getFile();
|
||||||
|
if (f == null) {
|
||||||
|
JOptionPane
|
||||||
|
.showMessageDialog(tabs.getTopLevelAncestor(),
|
||||||
|
"Coś się schrzaniło\nW zasadzie nie wiem, nawet co");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStreamWriter writer;
|
||||||
|
try {
|
||||||
|
writer = new OutputStreamWriter(new FileOutputStream(f));
|
||||||
|
writer.write(tab.getArea().getText());
|
||||||
|
writer.close();
|
||||||
|
} catch (FileNotFoundException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
} catch (IOException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
saveAction = new AbstractAction("Save as") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 4575053887363265531L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
JFileChooser chooser = new JFileChooser(".");
|
||||||
|
chooser.setFileFilter(new BFFileFilter());
|
||||||
|
if (chooser.showDialog(null, "Zapisz") == JFileChooser.APPROVE_OPTION) {
|
||||||
|
|
||||||
|
File f = chooser.getSelectedFile();
|
||||||
|
tabs.setTitleAt(tabs.getSelectedIndex(), f.getName());
|
||||||
|
tab.setFile(f);
|
||||||
|
BufferedWriter r = new BufferedWriter(
|
||||||
|
new OutputStreamWriter(new FileOutputStream(f)));
|
||||||
|
r.write(tab.getArea().getText());
|
||||||
|
r.close();
|
||||||
|
}
|
||||||
|
} catch (IOException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
findAction = new AbstractAction("find") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8670201600032148368L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Action find performed");
|
||||||
|
|
||||||
|
findDialog.setVisible(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
replaceAction = new AbstractAction("replace") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 4575053887363265531L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Action action performed");
|
||||||
|
replaceDialog.setVisible(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showBFProgress() {
|
||||||
|
try {
|
||||||
|
BrainFuckIDE.lg.info("Show BF progress");
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
BFMachine machine = tab.getMachine();
|
||||||
|
int pos = machine.getPosition();
|
||||||
|
tab.getStatusLabel().setText(
|
||||||
|
"Stepping pos: " + pos + "("
|
||||||
|
+ (char) tab.getMachine().getCode()[pos]
|
||||||
|
+ "), memory pos : "
|
||||||
|
+ tab.getMachine().getMemoryPointer());
|
||||||
|
tab.getEditor().setCurrentOffset(
|
||||||
|
tab.getMachine().getMemoryPointer());
|
||||||
|
StyledDocument doc = (StyledDocument) tab.getArea().getDocument();
|
||||||
|
Style caretStyle = doc.getStyle("BFCaret");
|
||||||
|
Style codeStyle = doc.getStyle("CODE");
|
||||||
|
Style commentStyle = doc.getStyle("COMMENT");
|
||||||
|
|
||||||
|
if (lastBFPositionShowing >= 0) {
|
||||||
|
String s = doc.getText(pos, 1);
|
||||||
|
Character c = s.charAt(0);
|
||||||
|
if (c == '[' || c == ']' || c == '\n' || c == '\r'
|
||||||
|
|| SyntaxHighlighter.BFcharacterList.contains(c))
|
||||||
|
doc.setCharacterAttributes(lastBFPositionShowing, 1,
|
||||||
|
codeStyle, true);
|
||||||
|
else
|
||||||
|
doc.setCharacterAttributes(lastBFPositionShowing, 1,
|
||||||
|
commentStyle, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.setCharacterAttributes(pos, 1, caretStyle, true);
|
||||||
|
lastBFPositionShowing = pos;
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.warning(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoableEditHappened(UndoableEditEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("Undoable edit happend: "
|
||||||
|
+ e.getEdit().getPresentationName());
|
||||||
|
|
||||||
|
// FIXME jak na razie, działa, ale nie wiem dlaczego, jeżeli coś się
|
||||||
|
// sypie z UndoManager tu sprawdzać
|
||||||
|
|
||||||
|
if (e.getEdit().getPresentationName().equals("style change") == false)
|
||||||
|
undoManager.addEdit(e.getEdit());
|
||||||
|
|
||||||
|
undoAction.update();
|
||||||
|
redoAction.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
if (tab == null)
|
||||||
|
return;
|
||||||
|
if (tab.getComponentCount() <= 0)
|
||||||
|
return;
|
||||||
|
ArrayList<Component> components = new ArrayList<Component>(
|
||||||
|
Arrays.asList(tab.getComponents()));
|
||||||
|
showStatusLabel.setSelected(components.contains(tab.getStatus()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Szykuje maszynę, na nowe zawody (reset)
|
||||||
|
*
|
||||||
|
* @param tab
|
||||||
|
* zakładka
|
||||||
|
* @param doc
|
||||||
|
* dokument
|
||||||
|
* @param machine
|
||||||
|
* maszyna
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private Thread prepareMachine(final BFTab tab, Document doc,
|
||||||
|
final BFMachine machine) {
|
||||||
|
if (machine.isWorking()) {
|
||||||
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"Machine is working currently!!!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
tab.getArea().setEditable(false);
|
||||||
|
|
||||||
|
BrainFuckIDE.lg
|
||||||
|
.info("Adding document listener to the input stream of console");
|
||||||
|
try {
|
||||||
|
doc.remove(0, doc.getLength());
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.severe("Exception: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.fine("Creating BF machine");
|
||||||
|
|
||||||
|
machine.setCode(tab.getArea().getText());
|
||||||
|
machine.setFinishHandler(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
String s = machine.getError();
|
||||||
|
if (s != null && !s.equals(""))
|
||||||
|
JOptionPane.showMessageDialog(tabs.getTopLevelAncestor(),
|
||||||
|
"Whoops: BF machine" + " complaints: " + s,
|
||||||
|
"BF error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
else
|
||||||
|
lastWorkingCode = tab.getMachine().getCode();
|
||||||
|
tab.getEditor().repaint();
|
||||||
|
BrainFuckIDE.lg.info("Memory of BF machine is: "
|
||||||
|
+ tab.getMachine().getMemory().length);
|
||||||
|
tab.getArea().setEditable(true);
|
||||||
|
tab.getStatusLabel().setText("Ready");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
machine.setBreakpointHandler(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
showBFProgress();
|
||||||
|
tab.getStatusLabel().setText(
|
||||||
|
tab.getStatusLabel().getText()
|
||||||
|
+ ". Breakpoint reached...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
machine.setStepHandler(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
showBFProgress();
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final Thread t = new Thread(machine);
|
||||||
|
tab.getMachine().reset();
|
||||||
|
tab.getStatusLabel().setText("Running...");
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JTabbedPane getTabs() {
|
||||||
|
return tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTabs(JTabbedPane tabs) {
|
||||||
|
this.tabs = tabs;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.undo.CannotRedoException;
|
||||||
|
import javax.swing.undo.UndoManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa umożliwia działanie ReDo
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*/
|
||||||
|
public class RedoAction extends AbstractAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -2467991386706847458L;
|
||||||
|
private JMenuItem redoButton;
|
||||||
|
private UndoAction undoAction;
|
||||||
|
private UndoManager undoManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param manager
|
||||||
|
* @param button
|
||||||
|
* @param action
|
||||||
|
*/
|
||||||
|
public RedoAction(UndoManager manager, JMenuItem button, UndoAction action) {
|
||||||
|
super("Redo");
|
||||||
|
setEnabled(false);
|
||||||
|
undoManager = manager;
|
||||||
|
redoButton = button;
|
||||||
|
undoAction = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
undoManager.redo();
|
||||||
|
} catch (CannotRedoException ex) {
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
undoAction.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param action
|
||||||
|
* Akcja analogiczna do ReDo Undo
|
||||||
|
*/
|
||||||
|
public void setUndoAction(UndoAction action) {
|
||||||
|
undoAction = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uaktualnia historię edycji
|
||||||
|
*/
|
||||||
|
protected void update() {
|
||||||
|
BrainFuckIDE.lg.fine("Updating state of undo/redo action");
|
||||||
|
if (undoManager.canRedo()) {
|
||||||
|
setEnabled(true);
|
||||||
|
redoButton.setEnabled(true);
|
||||||
|
putValue(Action.NAME, undoManager.getRedoPresentationName());
|
||||||
|
} else {
|
||||||
|
setEnabled(false);
|
||||||
|
redoButton.setEnabled(false);
|
||||||
|
putValue(Action.NAME, "Redo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa pozycji w dokumencie
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TextEditorPosition {
|
||||||
|
/**
|
||||||
|
* Linia w dokumencie
|
||||||
|
*/
|
||||||
|
public int Line;
|
||||||
|
/**
|
||||||
|
* Offset w linii dokumentu
|
||||||
|
*/
|
||||||
|
public int Offset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public TextEditorPosition()
|
||||||
|
{
|
||||||
|
Line = 0;
|
||||||
|
Offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextEditorPosition(int line, int offset) {
|
||||||
|
this.Line = line;
|
||||||
|
this.Offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,484 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.FontMetrics;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Insets;
|
||||||
|
import java.awt.Point;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.border.CompoundBorder;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.border.MatteBorder;
|
||||||
|
import javax.swing.event.CaretEvent;
|
||||||
|
import javax.swing.event.CaretListener;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.text.AttributeSet;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Element;
|
||||||
|
import javax.swing.text.JTextComponent;
|
||||||
|
import javax.swing.text.StyleConstants;
|
||||||
|
import javax.swing.text.Utilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will display line numbers for a related text component. The text
|
||||||
|
* component must use the same line height for each line. TextLineNumber
|
||||||
|
* supports wrapped lines and will highlight the line number of the current
|
||||||
|
* line in the text component.
|
||||||
|
*
|
||||||
|
* This class was designed to be used as a component added to the row header
|
||||||
|
* of a JScrollPane.
|
||||||
|
*/
|
||||||
|
public class TextLineNumber extends JPanel
|
||||||
|
implements CaretListener, DocumentListener, PropertyChangeListener
|
||||||
|
{
|
||||||
|
public final static float CENTER = 0.5f;
|
||||||
|
private final static int HEIGHT = Integer.MAX_VALUE - 1000000;
|
||||||
|
public final static float LEFT = 0.0f;
|
||||||
|
private final static Border OUTER = new MatteBorder(0, 0, 0, 2, Color.GRAY);
|
||||||
|
|
||||||
|
public final static float RIGHT = 1.0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 3696719794891436122L;
|
||||||
|
|
||||||
|
// Text component this TextTextLineNumber component is in sync with
|
||||||
|
|
||||||
|
private int borderGap;
|
||||||
|
|
||||||
|
// Properties that can be changed
|
||||||
|
|
||||||
|
private JTextComponent component;
|
||||||
|
private Color currentLineForeground;
|
||||||
|
private float digitAlignment;
|
||||||
|
private HashMap<String, FontMetrics> fonts;
|
||||||
|
private int lastDigits;
|
||||||
|
|
||||||
|
// Keep history information to reduce the number of times the component
|
||||||
|
// needs to be repainted
|
||||||
|
|
||||||
|
private int lastHeight;
|
||||||
|
private int lastLine;
|
||||||
|
private int minimumDisplayDigits;
|
||||||
|
|
||||||
|
private boolean updateFont;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a line number component for a text component. This minimum
|
||||||
|
* display width will be based on 3 digits.
|
||||||
|
*
|
||||||
|
* @param component the related text component
|
||||||
|
*/
|
||||||
|
public TextLineNumber(JTextComponent component)
|
||||||
|
{
|
||||||
|
this(component, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a line number component for a text component.
|
||||||
|
*
|
||||||
|
* @param component the related text component
|
||||||
|
* @param minimumDisplayDigits the number of digits used to calculate
|
||||||
|
* the minimum width of the component
|
||||||
|
*/
|
||||||
|
public TextLineNumber(JTextComponent component, int minimumDisplayDigits)
|
||||||
|
{
|
||||||
|
this.component = component;
|
||||||
|
|
||||||
|
setFont( component.getFont() );
|
||||||
|
|
||||||
|
setBorderGap( 5 );
|
||||||
|
setCurrentLineForeground( Color.RED );
|
||||||
|
setDigitAlignment( RIGHT );
|
||||||
|
setMinimumDisplayDigits( minimumDisplayDigits );
|
||||||
|
|
||||||
|
component.getDocument().addDocumentListener(this);
|
||||||
|
component.addCaretListener( this );
|
||||||
|
component.addPropertyChangeListener("font", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Implement CaretListener interface
|
||||||
|
//
|
||||||
|
@Override
|
||||||
|
public void caretUpdate(CaretEvent e)
|
||||||
|
{
|
||||||
|
// Get the line the caret is positioned on
|
||||||
|
|
||||||
|
int caretPosition = component.getCaretPosition();
|
||||||
|
Element root = component.getDocument().getDefaultRootElement();
|
||||||
|
int currentLine = root.getElementIndex( caretPosition );
|
||||||
|
|
||||||
|
// Need to repaint so the correct line number can be highlighted
|
||||||
|
|
||||||
|
if (lastLine != currentLine)
|
||||||
|
{
|
||||||
|
repaint();
|
||||||
|
lastLine = currentLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Implement DocumentListener interface
|
||||||
|
//
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e)
|
||||||
|
{
|
||||||
|
documentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A document change may affect the number of displayed lines of text.
|
||||||
|
* Therefore the lines numbers will also change.
|
||||||
|
*/
|
||||||
|
private void documentChanged()
|
||||||
|
{
|
||||||
|
// Preferred size of the component has not been updated at the time
|
||||||
|
// the DocumentEvent is fired
|
||||||
|
|
||||||
|
SwingUtilities.invokeLater(new Runnable()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
int preferredHeight = component.getPreferredSize().height;
|
||||||
|
|
||||||
|
// Document change has caused a change in the number of lines.
|
||||||
|
// Repaint to reflect the new line numbers
|
||||||
|
|
||||||
|
if (lastHeight != preferredHeight)
|
||||||
|
{
|
||||||
|
setPreferredWidth();
|
||||||
|
repaint();
|
||||||
|
lastHeight = preferredHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the border gap
|
||||||
|
*
|
||||||
|
* @return the border gap in pixels
|
||||||
|
*/
|
||||||
|
public int getBorderGap()
|
||||||
|
{
|
||||||
|
return borderGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the current line rendering Color
|
||||||
|
*
|
||||||
|
* @return the Color used to render the current line number
|
||||||
|
*/
|
||||||
|
public Color getCurrentLineForeground()
|
||||||
|
{
|
||||||
|
return currentLineForeground == null ? getForeground() : currentLineForeground;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the digit alignment
|
||||||
|
*
|
||||||
|
* @return the alignment of the painted digits
|
||||||
|
*/
|
||||||
|
public float getDigitAlignment()
|
||||||
|
{
|
||||||
|
return digitAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the minimum display digits
|
||||||
|
*
|
||||||
|
* @return the minimum display digits
|
||||||
|
*/
|
||||||
|
public int getMinimumDisplayDigits()
|
||||||
|
{
|
||||||
|
return minimumDisplayDigits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the X offset to properly align the line number when drawn
|
||||||
|
*/
|
||||||
|
private int getOffsetX(int availableWidth, int stringWidth)
|
||||||
|
{
|
||||||
|
return (int)((availableWidth - stringWidth) * digitAlignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the Y offset for the current row
|
||||||
|
*/
|
||||||
|
private int getOffsetY(int rowStartOffset, FontMetrics fontMetrics)
|
||||||
|
throws BadLocationException
|
||||||
|
{
|
||||||
|
// Get the bounding rectangle of the row
|
||||||
|
|
||||||
|
Rectangle r = component.modelToView( rowStartOffset );
|
||||||
|
int lineHeight = fontMetrics.getHeight();
|
||||||
|
int y = r.y + r.height;
|
||||||
|
int descent = 0;
|
||||||
|
|
||||||
|
// The text needs to be positioned above the bottom of the bounding
|
||||||
|
// rectangle based on the descent of the font(s) contained on the row.
|
||||||
|
|
||||||
|
if (r.height == lineHeight) // default font is being used
|
||||||
|
{
|
||||||
|
descent = fontMetrics.getDescent();
|
||||||
|
}
|
||||||
|
else // We need to check all the attributes for font changes
|
||||||
|
{
|
||||||
|
if (fonts == null)
|
||||||
|
fonts = new HashMap<String, FontMetrics>();
|
||||||
|
|
||||||
|
Element root = component.getDocument().getDefaultRootElement();
|
||||||
|
int index = root.getElementIndex( rowStartOffset );
|
||||||
|
Element line = root.getElement( index );
|
||||||
|
|
||||||
|
for (int i = 0; i < line.getElementCount(); i++)
|
||||||
|
{
|
||||||
|
Element child = line.getElement(i);
|
||||||
|
AttributeSet as = child.getAttributes();
|
||||||
|
String fontFamily = (String)as.getAttribute(StyleConstants.FontFamily);
|
||||||
|
Integer fontSize = (Integer)as.getAttribute(StyleConstants.FontSize);
|
||||||
|
String key = fontFamily + fontSize;
|
||||||
|
|
||||||
|
FontMetrics fm = fonts.get( key );
|
||||||
|
|
||||||
|
if (fm == null)
|
||||||
|
{
|
||||||
|
Font font = new Font(fontFamily, Font.PLAIN, fontSize);
|
||||||
|
fm = component.getFontMetrics( font );
|
||||||
|
fonts.put(key, fm);
|
||||||
|
}
|
||||||
|
|
||||||
|
descent = Math.max(descent, fm.getDescent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return y - descent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the line number to be drawn. The empty string will be returned
|
||||||
|
* when a line of text has wrapped.
|
||||||
|
*/
|
||||||
|
protected String getTextLineNumber(int rowStartOffset)
|
||||||
|
{
|
||||||
|
Element root = component.getDocument().getDefaultRootElement();
|
||||||
|
int index = root.getElementIndex( rowStartOffset );
|
||||||
|
Element line = root.getElement( index );
|
||||||
|
|
||||||
|
if (line.getStartOffset() == rowStartOffset)
|
||||||
|
return String.valueOf(index + 1);
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the update font property
|
||||||
|
*
|
||||||
|
* @return the update font property
|
||||||
|
*/
|
||||||
|
public boolean getUpdateFont()
|
||||||
|
{
|
||||||
|
return updateFont;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e)
|
||||||
|
{
|
||||||
|
documentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to know if the caret is currently positioned on the line we
|
||||||
|
* are about to paint so the line number can be highlighted.
|
||||||
|
*/
|
||||||
|
private boolean isCurrentLine(int rowStartOffset)
|
||||||
|
{
|
||||||
|
int caretPosition = component.getCaretPosition();
|
||||||
|
Element root = component.getDocument().getDefaultRootElement();
|
||||||
|
|
||||||
|
if (root.getElementIndex( rowStartOffset ) == root.getElementIndex(caretPosition))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw the line numbers
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void paintComponent(Graphics g)
|
||||||
|
{
|
||||||
|
super.paintComponent(g);
|
||||||
|
|
||||||
|
// Determine the width of the space available to draw the line number
|
||||||
|
|
||||||
|
FontMetrics fontMetrics = component.getFontMetrics( component.getFont() );
|
||||||
|
Insets insets = getInsets();
|
||||||
|
int availableWidth = getSize().width - insets.left - insets.right;
|
||||||
|
|
||||||
|
// Determine the rows to draw within the clipped bounds.
|
||||||
|
|
||||||
|
Rectangle clip = g.getClipBounds();
|
||||||
|
int rowStartOffset = component.viewToModel( new Point(0, clip.y) );
|
||||||
|
int endOffset = component.viewToModel( new Point(0, clip.y + clip.height) );
|
||||||
|
|
||||||
|
while (rowStartOffset <= endOffset)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isCurrentLine(rowStartOffset))
|
||||||
|
g.setColor( getCurrentLineForeground() );
|
||||||
|
else
|
||||||
|
g.setColor( getForeground() );
|
||||||
|
|
||||||
|
// Get the line number as a string and then determine the
|
||||||
|
// "X" and "Y" offsets for drawing the string.
|
||||||
|
|
||||||
|
String lineNumber = getTextLineNumber(rowStartOffset);
|
||||||
|
int stringWidth = fontMetrics.stringWidth( lineNumber );
|
||||||
|
int x = getOffsetX(availableWidth, stringWidth) + insets.left;
|
||||||
|
int y = getOffsetY(rowStartOffset, fontMetrics);
|
||||||
|
g.drawString(lineNumber, x, y);
|
||||||
|
|
||||||
|
// Move to the next row
|
||||||
|
|
||||||
|
rowStartOffset = Utilities.getRowEnd(component, rowStartOffset) + 1;
|
||||||
|
}
|
||||||
|
catch(Exception e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Implement PropertyChangeListener interface
|
||||||
|
//
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent evt)
|
||||||
|
{
|
||||||
|
if (evt.getNewValue() instanceof Font)
|
||||||
|
{
|
||||||
|
if (updateFont)
|
||||||
|
{
|
||||||
|
Font newFont = (Font) evt.getNewValue();
|
||||||
|
setFont(newFont);
|
||||||
|
lastDigits = 0;
|
||||||
|
setPreferredWidth();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e)
|
||||||
|
{
|
||||||
|
documentChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The border gap is used in calculating the left and right insets of the
|
||||||
|
* border. Default value is 5.
|
||||||
|
*
|
||||||
|
* @param borderGap the gap in pixels
|
||||||
|
*/
|
||||||
|
public void setBorderGap(int borderGap)
|
||||||
|
{
|
||||||
|
this.borderGap = borderGap;
|
||||||
|
Border inner = new EmptyBorder(0, borderGap, 0, borderGap);
|
||||||
|
setBorder( new CompoundBorder(OUTER, inner) );
|
||||||
|
lastDigits = 0;
|
||||||
|
setPreferredWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Color used to render the current line digits. Default is Coolor.RED.
|
||||||
|
*
|
||||||
|
* @param currentLineForeground the Color used to render the current line
|
||||||
|
*/
|
||||||
|
public void setCurrentLineForeground(Color currentLineForeground)
|
||||||
|
{
|
||||||
|
this.currentLineForeground = currentLineForeground;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the horizontal alignment of the digits within the component.
|
||||||
|
* Common values would be:
|
||||||
|
* <ul>
|
||||||
|
* <li>TextLineNumber.LEFT
|
||||||
|
* <li>TextLineNumber.CENTER
|
||||||
|
* <li>TextLineNumber.RIGHT (default)
|
||||||
|
* </ul>
|
||||||
|
* @param currentLineForeground the Color used to render the current line
|
||||||
|
*/
|
||||||
|
public void setDigitAlignment(float digitAlignment)
|
||||||
|
{
|
||||||
|
this.digitAlignment =
|
||||||
|
digitAlignment > 1.0f ? 1.0f : digitAlignment < 0.0f ? -1.0f : digitAlignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify the mimimum number of digits used to calculate the preferred
|
||||||
|
* width of the component. Default is 3.
|
||||||
|
*
|
||||||
|
* @param minimumDisplayDigits the number digits used in the preferred
|
||||||
|
* width calculation
|
||||||
|
*/
|
||||||
|
public void setMinimumDisplayDigits(int minimumDisplayDigits)
|
||||||
|
{
|
||||||
|
this.minimumDisplayDigits = minimumDisplayDigits;
|
||||||
|
setPreferredWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the width needed to display the maximum line number
|
||||||
|
*/
|
||||||
|
private void setPreferredWidth()
|
||||||
|
{
|
||||||
|
Element root = component.getDocument().getDefaultRootElement();
|
||||||
|
int lines = root.getElementCount();
|
||||||
|
int digits = Math.max(String.valueOf(lines).length(), minimumDisplayDigits);
|
||||||
|
|
||||||
|
// Update sizes when number of digits in the line number changes
|
||||||
|
|
||||||
|
if (lastDigits != digits)
|
||||||
|
{
|
||||||
|
lastDigits = digits;
|
||||||
|
FontMetrics fontMetrics = getFontMetrics( getFont() );
|
||||||
|
int width = fontMetrics.charWidth( '0' ) * digits;
|
||||||
|
Insets insets = getInsets();
|
||||||
|
int preferredWidth = insets.left + insets.right + width;
|
||||||
|
|
||||||
|
Dimension d = getPreferredSize();
|
||||||
|
d.setSize(preferredWidth, HEIGHT);
|
||||||
|
setPreferredSize( d );
|
||||||
|
setSize( d );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the update font property. Indicates whether this Font should be
|
||||||
|
* updated automatically when the Font of the related text component
|
||||||
|
* is changed.
|
||||||
|
*
|
||||||
|
* @param updateFont when true update the Font and repaint the line
|
||||||
|
* numbers, otherwise just repaint the line numbers.
|
||||||
|
*/
|
||||||
|
public void setUpdateFont(boolean updateFont)
|
||||||
|
{
|
||||||
|
this.updateFont = updateFont;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
package tpsa;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.undo.CannotUndoException;
|
||||||
|
import javax.swing.undo.UndoManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Klasa implementująca UnDo
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class UndoAction extends AbstractAction {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5058740336403951058L;
|
||||||
|
|
||||||
|
private RedoAction redoAction;
|
||||||
|
private JMenuItem undoButton;
|
||||||
|
|
||||||
|
private UndoManager undoManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param manager
|
||||||
|
* @param button
|
||||||
|
* @param action
|
||||||
|
*/
|
||||||
|
public UndoAction(UndoManager manager, JMenuItem button, RedoAction action) {
|
||||||
|
super("Undo");
|
||||||
|
BrainFuckIDE.lg.info("Creating instance of class");
|
||||||
|
setEnabled(false);
|
||||||
|
undoManager = manager;
|
||||||
|
undoButton = button;
|
||||||
|
redoAction = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
// System.err.println("YEP");
|
||||||
|
undoManager.undo();
|
||||||
|
// System.err.println("WORKS");
|
||||||
|
} catch (CannotUndoException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
redoAction.update();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param action
|
||||||
|
*/
|
||||||
|
public void setRedoAction(RedoAction action) {
|
||||||
|
redoAction = action;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void update() {
|
||||||
|
BrainFuckIDE.lg.fine("Updating undo/redo state");
|
||||||
|
if (undoManager.canUndo()) {
|
||||||
|
setEnabled(true);
|
||||||
|
undoButton.setEnabled(true);
|
||||||
|
putValue(Action.NAME, undoManager.getUndoPresentationName());
|
||||||
|
} else {
|
||||||
|
setEnabled(false);
|
||||||
|
undoButton.setEnabled(false);
|
||||||
|
putValue(Action.NAME, "Undo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package tpsa.dialogs;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* O programie dialog
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class AboutProgramDialog extends JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -1176985069310756058L;
|
||||||
|
|
||||||
|
public AboutProgramDialog(JFrame frame)
|
||||||
|
{
|
||||||
|
super(frame, "About program", ModalityType.APPLICATION_MODAL);
|
||||||
|
setPreferredSize(new Dimension(500, 400));
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean showDialog() {
|
||||||
|
setVisible(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
package tpsa.dialogs;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Box;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.JTextPane;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.WindowConstants;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
|
||||||
|
import tpsa.BFTab;
|
||||||
|
import tpsa.BrainFuckIDE;
|
||||||
|
import tpsa.MainFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog wyszukiwania wyrażenia
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class FindDialog extends JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -486529548871883227L;
|
||||||
|
private JTextField match;
|
||||||
|
private boolean result;
|
||||||
|
/**
|
||||||
|
* @param frame
|
||||||
|
*/
|
||||||
|
public FindDialog(final MainFrame frame) {
|
||||||
|
super(frame);
|
||||||
|
setTitle("Find string");
|
||||||
|
|
||||||
|
setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
|
||||||
|
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||||
|
setPreferredSize(new Dimension(250, 100));
|
||||||
|
|
||||||
|
Box b = Box.createVerticalBox();
|
||||||
|
|
||||||
|
JRootPane pane = getRootPane();
|
||||||
|
KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||||
|
KeyStroke enterStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
|
||||||
|
|
||||||
|
pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeStroke,
|
||||||
|
"escape");
|
||||||
|
pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(enterStroke,
|
||||||
|
"EnterFind");
|
||||||
|
pane.getActionMap().put("escape", new AbstractAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -375905298222684550L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
result = false;
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pane.getActionMap().put("EnterFind", new AbstractAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -8373549218006477790L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.fine("Searching...");
|
||||||
|
JTabbedPane tabs = frame.getTabs();
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
if (tab == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String s = tab.getArea().getText();
|
||||||
|
String search = getMatch();
|
||||||
|
int oldPos = tab.getArea().getCaretPosition();
|
||||||
|
int pos = -1;
|
||||||
|
pos = s.indexOf(search, oldPos + 1);
|
||||||
|
if (pos < 0) {
|
||||||
|
BrainFuckIDE.lg.info("Rewinding to the beginning");
|
||||||
|
pos = s.indexOf(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.fine("Searching for: " + search);
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.finest("found: " + pos);
|
||||||
|
if (pos >= 0) {
|
||||||
|
Rectangle rect = tab.getArea().modelToView(pos);
|
||||||
|
BrainFuckIDE.lg.finest("rect: " + rect.toString());
|
||||||
|
JTextPane area = tab.getArea();
|
||||||
|
area.scrollRectToVisible(rect);
|
||||||
|
area.setCaretPosition(pos);
|
||||||
|
} else
|
||||||
|
JOptionPane.showMessageDialog(frame,
|
||||||
|
"Not found from cursor phrase: " + search);
|
||||||
|
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.warning(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Box b1 = Box.createHorizontalBox();
|
||||||
|
|
||||||
|
b1.add(new JLabel("Find: "));
|
||||||
|
match = new JTextField(10);
|
||||||
|
b1.add(match);
|
||||||
|
|
||||||
|
JPanel p2 = new JPanel();
|
||||||
|
p2.setLayout(new FlowLayout(FlowLayout.CENTER));
|
||||||
|
|
||||||
|
JButton find = new JButton("Find");
|
||||||
|
// JButton findNext = new JButton("Find next");
|
||||||
|
p2.add(find);
|
||||||
|
// p2.add(findNext);
|
||||||
|
|
||||||
|
b.add(b1);
|
||||||
|
Component gl = Box.createHorizontalGlue();
|
||||||
|
gl.setPreferredSize(new Dimension(10, 10));
|
||||||
|
b.add(gl);
|
||||||
|
b.add(p2);
|
||||||
|
|
||||||
|
find.addActionListener(new ActionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
result = true;
|
||||||
|
setVisible(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add(find);
|
||||||
|
// add(findNext);
|
||||||
|
|
||||||
|
Component glue = Box.createVerticalGlue();
|
||||||
|
glue.setPreferredSize(new Dimension(Integer.MAX_VALUE,
|
||||||
|
Integer.MAX_VALUE));
|
||||||
|
b.add(glue);
|
||||||
|
|
||||||
|
add(b);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wyrażenie do wyszukania
|
||||||
|
*
|
||||||
|
* @return Wyrażenie, które użytkownik chce wyszukać
|
||||||
|
*/
|
||||||
|
public String getMatch() {
|
||||||
|
return new String(match.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokaż dialog
|
||||||
|
*
|
||||||
|
* @return Potwierdzenie/odrzucenie zmian
|
||||||
|
*/
|
||||||
|
public boolean showDialog() {
|
||||||
|
result = false;
|
||||||
|
setVisible(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package tpsa.dialogs;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog pomocy
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class HelpDialog extends JDialog{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 5854396908923903514L;
|
||||||
|
|
||||||
|
public HelpDialog(JFrame frame) {
|
||||||
|
super(frame, "Help", ModalityType.APPLICATION_MODAL);
|
||||||
|
setPreferredSize(new Dimension(500, 400));
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean showDialog() {
|
||||||
|
setVisible(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,147 @@
|
||||||
|
package tpsa.dialogs;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.ActionMap;
|
||||||
|
import javax.swing.Box;
|
||||||
|
import javax.swing.InputMap;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
|
||||||
|
import tpsa.BrainFuckIDE;
|
||||||
|
import tpsa.MainFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog ustalania pamięci VM BrainFucka
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MemorySizeDialog extends JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -6179258766072502357L;
|
||||||
|
private Action okAction;
|
||||||
|
private Action cancelAction;
|
||||||
|
private JTextField sizeEdit;
|
||||||
|
private boolean result = false;
|
||||||
|
|
||||||
|
public MemorySizeDialog(final MainFrame frame) {
|
||||||
|
super(frame);
|
||||||
|
setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
|
||||||
|
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||||
|
|
||||||
|
setPreferredSize(new Dimension(200, 150));
|
||||||
|
|
||||||
|
Box b = Box.createVerticalBox();
|
||||||
|
|
||||||
|
JPanel p1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
|
||||||
|
p1.add(new JLabel("Podaj rozmiar VM Brainfucka: "));
|
||||||
|
sizeEdit = new JTextField(8);
|
||||||
|
p1.add(sizeEdit);
|
||||||
|
|
||||||
|
JButton ok = new JButton("OK");
|
||||||
|
JButton cancel = new JButton("Cancel");
|
||||||
|
|
||||||
|
JPanel p2 = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
|
p2.add(ok);
|
||||||
|
p2.add(cancel);
|
||||||
|
|
||||||
|
b.add(p1);
|
||||||
|
b.add(p2);
|
||||||
|
|
||||||
|
okAction = new AbstractAction("OK") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -5794707237614988673L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
BrainFuckIDE.lg.info("ok action performed");
|
||||||
|
if (getSizeMemory() <= 0) {
|
||||||
|
JOptionPane
|
||||||
|
.showMessageDialog(frame,
|
||||||
|
"Wartość rozmiaru pamieci musi być typu Integer i >= 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
setVisible(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cancelAction = new AbstractAction("Cancel") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 8441237664658329642L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ok.setAction(okAction);
|
||||||
|
cancel.setAction(cancelAction);
|
||||||
|
|
||||||
|
JRootPane pane = getRootPane();
|
||||||
|
KeyStroke acceptStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
|
||||||
|
KeyStroke cancelStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||||
|
|
||||||
|
InputMap iMap = pane
|
||||||
|
.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
||||||
|
ActionMap aMap = pane.getActionMap();
|
||||||
|
|
||||||
|
iMap.put(acceptStroke, "accept");
|
||||||
|
aMap.put("accept", okAction);
|
||||||
|
|
||||||
|
iMap.put(cancelStroke, "cancel");
|
||||||
|
aMap.put("cancel", cancelAction);
|
||||||
|
|
||||||
|
add(b);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSizeMemory() {
|
||||||
|
return Integer.parseInt(sizeEdit.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSizeMemory(int s) {
|
||||||
|
sizeEdit.setText("" + s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pokazuje ten wspaniały dialog
|
||||||
|
*
|
||||||
|
* @return w zasadzie nic, czy użytkownik zaakceptował wprowadzone dane
|
||||||
|
*/
|
||||||
|
public boolean showDialog() {
|
||||||
|
result = false;
|
||||||
|
setVisible(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
package tpsa.dialogs;
|
||||||
|
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.Action;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JComponent;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JRootPane;
|
||||||
|
import javax.swing.JTabbedPane;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.JTextPane;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
|
||||||
|
import tpsa.BFTab;
|
||||||
|
import tpsa.BrainFuckIDE;
|
||||||
|
import tpsa.MainFrame;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dialog do zastępowania treści jednej drugą
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class ReplaceDialog extends JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 103076309978197148L;
|
||||||
|
/**
|
||||||
|
* Wyrażenie, które ma znaleść
|
||||||
|
*/
|
||||||
|
private JTextField match;
|
||||||
|
/**
|
||||||
|
* Wyrażenie, którym ma zastąpić match
|
||||||
|
*/
|
||||||
|
private JTextField replace;
|
||||||
|
/**
|
||||||
|
* Czy użytkownik, zaakceptował dane
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
public ReplaceDialog(final MainFrame frame) {
|
||||||
|
super(frame);
|
||||||
|
setTitle("Replace");
|
||||||
|
setPreferredSize(new Dimension(250, 100));
|
||||||
|
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||||
|
|
||||||
|
setTitle("Find");
|
||||||
|
|
||||||
|
setLayout(new GridLayout(3, 2));
|
||||||
|
|
||||||
|
JRootPane pane = getRootPane();
|
||||||
|
KeyStroke escapeStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||||
|
|
||||||
|
pane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeStroke,
|
||||||
|
"escape");
|
||||||
|
pane.getActionMap().put("escape", new AbstractAction() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -3277168784395957597L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
success = false;
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add(new JLabel("Find: "));
|
||||||
|
match = new JTextField(10);
|
||||||
|
add(match);
|
||||||
|
|
||||||
|
replace = new JTextField(10);
|
||||||
|
add(new JLabel("Replace: "));
|
||||||
|
add(replace);
|
||||||
|
|
||||||
|
|
||||||
|
JButton replace = new JButton("Replace");
|
||||||
|
Action replaceAct = new AbstractAction("Replace one") {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
try {
|
||||||
|
JTabbedPane tabs = frame.getTabs();
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
JTextPane area = tab.getArea();
|
||||||
|
int cursorPos = area.getCaretPosition();
|
||||||
|
StringBuilder s = new StringBuilder(area.getText());
|
||||||
|
int posToChange = s.indexOf(getMatch(), cursorPos);
|
||||||
|
if (posToChange < 0) {
|
||||||
|
JOptionPane.showMessageDialog(frame,
|
||||||
|
"Nope, nie znalazłem ciągu do zastąpienia");
|
||||||
|
return;
|
||||||
|
// ретурн;
|
||||||
|
}
|
||||||
|
s.replace(posToChange, posToChange + getMatch().length(),
|
||||||
|
getReplace());
|
||||||
|
tab.getArea().setText(s.toString());
|
||||||
|
Rectangle rect = tab.getArea().modelToView(posToChange);
|
||||||
|
BrainFuckIDE.lg.finest("rect: " + rect.toString());
|
||||||
|
area.scrollRectToVisible(rect);
|
||||||
|
area.setCaretPosition(posToChange);
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.warning(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
JOptionPane.showMessageDialog(frame,
|
||||||
|
"Coś się stało, i właściwie nie chcę wiedzieć, co",
|
||||||
|
"Powitanie", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
replace.setAction(replaceAct);
|
||||||
|
KeyStroke replaceStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
|
||||||
|
getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
|
||||||
|
replaceStroke, "replace");
|
||||||
|
getRootPane().getActionMap().put("replace", replaceAct);
|
||||||
|
|
||||||
|
JButton replaceAll = new JButton("Replace all");
|
||||||
|
|
||||||
|
replaceAll.addActionListener(new ActionListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
JTabbedPane tabs = frame.getTabs();
|
||||||
|
BFTab tab = (BFTab) tabs.getSelectedComponent();
|
||||||
|
String s = tab.getArea().getText();
|
||||||
|
int count = ReplaceDialog.countOccurences(s, getMatch());
|
||||||
|
s = s.replaceAll(getMatch(), getReplace());
|
||||||
|
tab.getArea().setText(s);
|
||||||
|
|
||||||
|
JOptionPane.showMessageDialog(frame, "Sucess. Changed all "
|
||||||
|
+ count + " occurences of phrase: " + getMatch(),
|
||||||
|
"Abnormal error", JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add(replace);
|
||||||
|
add(replaceAll);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Wyrażenie, które użytkownik chce zastąpić
|
||||||
|
*/
|
||||||
|
public String getMatch() {
|
||||||
|
return new String(match.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Wyrażenie, którym użytkownik chce zastąpić match
|
||||||
|
*/
|
||||||
|
public String getReplace() {
|
||||||
|
return new String(replace.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Czy potwierdzono zmiany
|
||||||
|
*/
|
||||||
|
public boolean getSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(boolean b) {
|
||||||
|
if (b == true)
|
||||||
|
success = false;
|
||||||
|
super.setVisible(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int countOccurences(String s, String match) {
|
||||||
|
int count = 0;
|
||||||
|
int pos = -1;
|
||||||
|
while ((pos = s.indexOf(match, pos + 1)) >= 0)
|
||||||
|
++count;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package tpsa.dialogs.filters;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import javax.swing.filechooser.FileFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filtr rozszerzeń kodów źródłowych BrainFucka
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class BFFileFilter extends FileFilter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(File f) {
|
||||||
|
if (f.isDirectory())
|
||||||
|
return true;
|
||||||
|
if (f.getName().endsWith(".bf"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "BrainFuck source code";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package tpsa.highlighters;
|
||||||
|
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reprezentuje klasę coś ala TODO dla swing utilities co ma odmalować
|
||||||
|
* odpowiednim kolorem
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*/
|
||||||
|
public class StyleHighlight {
|
||||||
|
private StyledDocument doc;
|
||||||
|
|
||||||
|
private int length;
|
||||||
|
|
||||||
|
private int offset;
|
||||||
|
|
||||||
|
private Style style;
|
||||||
|
|
||||||
|
public StyleHighlight() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public StyleHighlight(StyledDocument doc, int offset, int length,
|
||||||
|
Style style) {
|
||||||
|
this.doc = doc;
|
||||||
|
this.offset = offset;
|
||||||
|
this.length = length;
|
||||||
|
this.style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StyledDocument getDoc() {
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
|
public int getLength() {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
public int getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
public Style getStyle() {
|
||||||
|
return style;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDoc(StyledDocument doc) {
|
||||||
|
this.doc = doc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLength(int length) {
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOffset(int offset) {
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStyle(Style style) {
|
||||||
|
this.style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
package tpsa.highlighters;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.Style;
|
||||||
|
import javax.swing.text.StyledDocument;
|
||||||
|
|
||||||
|
import tpsa.BrainFuckIDE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wyróżnia kod od komentarzy
|
||||||
|
*
|
||||||
|
* @author Duga Eye
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class SyntaxHighlighter implements DocumentListener {
|
||||||
|
|
||||||
|
public static ArrayList<Character> BFcharacterList = new ArrayList<Character>(
|
||||||
|
Arrays.asList(new Character[] { '>', '<', '+', '-', '.', ',', '[',
|
||||||
|
']', 'b' }));
|
||||||
|
|
||||||
|
final ArrayList<StyleHighlight> list = new ArrayList<StyleHighlight>();
|
||||||
|
|
||||||
|
public SyntaxHighlighter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToTheList(StyledDocument doc, final int offset,
|
||||||
|
Style comment, int from, final ArrayList<StyleHighlight> list,
|
||||||
|
int position) {
|
||||||
|
int length;
|
||||||
|
|
||||||
|
length = position - from;
|
||||||
|
if (length <= 0)
|
||||||
|
return;
|
||||||
|
synchronized (list) {
|
||||||
|
list.add(new StyleHighlight(doc, offset + from, length, comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
// update(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForBugs(StyledDocument doc) {
|
||||||
|
try {
|
||||||
|
String s = doc.getText(0, doc.getLength());
|
||||||
|
Style errorStyle = doc.getStyle("ERROR");
|
||||||
|
|
||||||
|
int indentation = 0;
|
||||||
|
int idx = 0;
|
||||||
|
while (idx < s.length()) {
|
||||||
|
char c = 0;
|
||||||
|
do {
|
||||||
|
c = s.charAt(idx++);
|
||||||
|
} while (c != '[' && c != ']' && idx < s.length());
|
||||||
|
switch (c) {
|
||||||
|
case '[':
|
||||||
|
indentation++;
|
||||||
|
break;
|
||||||
|
case ']':
|
||||||
|
if (--indentation < 0) {
|
||||||
|
indentation = 0;
|
||||||
|
list.add(new StyleHighlight(doc, idx - 1, 1, errorStyle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHighlights();
|
||||||
|
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
update(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
update(e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setStyleComment(StyledDocument doc, final int offset,
|
||||||
|
String s, Style comment) {
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.finest("Text: '" + s + "'");
|
||||||
|
Style sCode = doc.getStyle("CODE");
|
||||||
|
int state = 0;
|
||||||
|
int from = 0;
|
||||||
|
synchronized (sCode) {
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < s.length(); i++) {
|
||||||
|
char c = s.charAt(i);
|
||||||
|
switch (state) {
|
||||||
|
case 0: // seria kodu
|
||||||
|
if (!BFcharacterList.contains(c)) {
|
||||||
|
addToTheList(doc, offset, sCode, from, list, i);
|
||||||
|
from = i;
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 1: // seria komentarzy
|
||||||
|
if (BFcharacterList.contains(c)) {
|
||||||
|
addToTheList(doc, offset, comment, from, list, i);
|
||||||
|
from = i;
|
||||||
|
state = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == 1) {
|
||||||
|
addToTheList(doc, offset, comment, from, list, s.length());
|
||||||
|
} else {
|
||||||
|
addToTheList(doc, offset, sCode, from, list, s.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(StyledDocument doc, int offset, int length,
|
||||||
|
boolean machine) {
|
||||||
|
Style comment = doc.getStyle("COMMENT");
|
||||||
|
String s;
|
||||||
|
try {
|
||||||
|
BrainFuckIDE.lg.finer("Change to analyse: " + offset + ", "
|
||||||
|
+ length + ", length: " + doc.getLength());
|
||||||
|
|
||||||
|
s = doc.getText(offset, Math.min(length, doc.getLength() - offset));
|
||||||
|
|
||||||
|
setStyleComment(doc, offset, s, comment);
|
||||||
|
|
||||||
|
checkForBugs(doc);
|
||||||
|
checkBreakpoints(doc);
|
||||||
|
|
||||||
|
updateHighlights();
|
||||||
|
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkBreakpoints(StyledDocument doc) {
|
||||||
|
/*
|
||||||
|
* try { BrainFuckIDE.lg.finer("Check breapoints"); String s =
|
||||||
|
* doc.getText(0, doc.getLength()); int pos = -1; Style sBreakpoint =
|
||||||
|
* doc.getStyle("BREAKPOINT"); while ((pos = s.indexOf('b', pos + 1)) >=
|
||||||
|
* 0) { BrainFuckIDE.lg.finer("Breakpoint pos: " + pos); int i; for (i =
|
||||||
|
* 0; i < s.length() - pos && s.charAt(i + pos) != 'b'; i++) ;
|
||||||
|
*
|
||||||
|
* synchronized (list) { list.add(new StyleHighlight(doc, pos, i - 1,
|
||||||
|
* sBreakpoint)); } pos += i; }
|
||||||
|
*
|
||||||
|
* } catch (BadLocationException excp) { }
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(DocumentEvent e) {
|
||||||
|
update((StyledDocument) e.getDocument(), e.getOffset(), e.getLength(),
|
||||||
|
false);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHighlights() {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (list) {
|
||||||
|
for (StyleHighlight h : list)
|
||||||
|
h.getDoc().setCharacterAttributes(h.getOffset(),
|
||||||
|
h.getLength(), h.getStyle(), true);
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
package tpsa.highlighters;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import javax.swing.event.DocumentEvent;
|
||||||
|
import javax.swing.event.DocumentListener;
|
||||||
|
import javax.swing.text.BadLocationException;
|
||||||
|
import javax.swing.text.DefaultHighlighter;
|
||||||
|
import javax.swing.text.Document;
|
||||||
|
|
||||||
|
import tpsa.BrainFuckIDE;
|
||||||
|
|
||||||
|
public class WordsHighlighter extends DefaultHighlighter implements
|
||||||
|
DocumentListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private Long last = 0L;
|
||||||
|
/**
|
||||||
|
* Words to highlight
|
||||||
|
* Words to highlight TODO not implemented
|
||||||
|
*/
|
||||||
|
private String[] words;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param word
|
||||||
|
*/
|
||||||
|
public WordsHighlighter(String[] words) {
|
||||||
|
// FIXME shallow copy
|
||||||
|
this.words = words.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changedUpdate(DocumentEvent e) {
|
||||||
|
updateFrequent(e);
|
||||||
|
updateNotToFrequent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertUpdate(DocumentEvent e) {
|
||||||
|
updateFrequent(e);
|
||||||
|
updateNotToFrequent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeUpdate(DocumentEvent e) {
|
||||||
|
updateFrequent(e);
|
||||||
|
updateNotToFrequent(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param s
|
||||||
|
* @param start
|
||||||
|
* @param length
|
||||||
|
* TODO
|
||||||
|
* @throws BadLocationException
|
||||||
|
*/
|
||||||
|
private void searchForOccurance(String s, int start, int length)
|
||||||
|
throws BadLocationException {
|
||||||
|
DefaultHighlightPainter todoHighLighter = new DefaultHighlightPainter(
|
||||||
|
Color.LIGHT_GRAY);
|
||||||
|
|
||||||
|
int pos = -1;
|
||||||
|
|
||||||
|
String copy = new String(s).toUpperCase(Locale.forLanguageTag("pl_PL"));
|
||||||
|
for (int i = 0; i < words.length; i++) {
|
||||||
|
pos = -1;
|
||||||
|
while ((pos = copy.indexOf(words[i], pos + 1)) >= 0) {
|
||||||
|
int endOfHighlighting = Math.min(pos + words[i].length()
|
||||||
|
+ start, length - 1);
|
||||||
|
addHighlight(pos + start, endOfHighlighting,
|
||||||
|
todoHighLighter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
private void update(DocumentEvent e) {
|
||||||
|
try {
|
||||||
|
Document doc = e.getDocument();
|
||||||
|
String s = doc.getText(0, doc.getLength());
|
||||||
|
removeAllHighlights();
|
||||||
|
|
||||||
|
searchForOccurance(s, 0, doc.getLength());
|
||||||
|
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
private void updateFrequent(DocumentEvent e) {
|
||||||
|
int offset = e.getOffset();
|
||||||
|
int length = e.getLength();
|
||||||
|
Document doc = e.getDocument();
|
||||||
|
|
||||||
|
try {
|
||||||
|
int start = Math.max(0, offset - 10);
|
||||||
|
int end = Math.min(doc.getLength() - start, length + 20);
|
||||||
|
String s = doc.getText(start, end);
|
||||||
|
Highlight[] highlights = getHighlights();
|
||||||
|
for (int i = 0; i < highlights.length; i++)
|
||||||
|
if (highlights[i].getStartOffset() >= offset
|
||||||
|
&& highlights[i].getEndOffset() <= offset + length)
|
||||||
|
removeHighlight(highlights[i]);
|
||||||
|
|
||||||
|
searchForOccurance(s, start, doc.getLength());
|
||||||
|
|
||||||
|
} catch (BadLocationException excp) {
|
||||||
|
BrainFuckIDE.lg.severe(BrainFuckIDE.ErrorsFormatter(excp));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param e
|
||||||
|
*/
|
||||||
|
private void updateNotToFrequent(DocumentEvent e) {
|
||||||
|
long now = System.nanoTime();
|
||||||
|
long diff = now - last;
|
||||||
|
if (diff > 2e9) {
|
||||||
|
last = now;
|
||||||
|
update(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
public class ColoredRange implements Comparable<ColoredRange> {
|
||||||
|
|
||||||
|
private final Color fcolor;
|
||||||
|
|
||||||
|
private final long start;
|
||||||
|
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
private final Color bgcolor;
|
||||||
|
|
||||||
|
public ColoredRange(final long start, final int size, final Color fcolor,
|
||||||
|
final Color bgcolor) {
|
||||||
|
|
||||||
|
this.start = start;
|
||||||
|
this.size = size;
|
||||||
|
this.fcolor = fcolor;
|
||||||
|
this.bgcolor = bgcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(final ColoredRange arg0) {
|
||||||
|
return (int) (start - arg0.start);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsOffset(final long offset) {
|
||||||
|
return offset >= start && offset < start + size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getBackgroundColor() {
|
||||||
|
return bgcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor() {
|
||||||
|
return fcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getStart() {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public final class ColoredRangeManager {
|
||||||
|
|
||||||
|
private final ArrayList<ColoredRange> ranges = new ArrayList<ColoredRange>();
|
||||||
|
|
||||||
|
public void addRange(final ColoredRange range) {
|
||||||
|
ranges.add(range);
|
||||||
|
Collections.sort(ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
ranges.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColoredRange findRange(final long offset) {
|
||||||
|
|
||||||
|
for (final ColoredRange range : ranges) {
|
||||||
|
if (range.getStart() >= offset) {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColoredRange findRangeWith(final long offset) {
|
||||||
|
|
||||||
|
for (final ColoredRange range : ranges) {
|
||||||
|
if (range.containsOffset(offset)) {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeRange(final long offset, final int size) {
|
||||||
|
// Try to find the range that contains the offset
|
||||||
|
ColoredRange range = findRangeWith(offset);
|
||||||
|
|
||||||
|
// If there is no such range, at least find the range right
|
||||||
|
// before the offset.
|
||||||
|
if (range == null) {
|
||||||
|
range = findRange(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is no such range then there is nothing to remove.
|
||||||
|
if (range == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The <= is a hack; used to be just ==; Check this again
|
||||||
|
if (offset <= range.getStart()) {
|
||||||
|
// The offset starts exactly at the range. That means
|
||||||
|
// we start decolorizing at the beginning of the range.
|
||||||
|
|
||||||
|
if (range.getSize() == size) {
|
||||||
|
// If the sizes are equal the entire range is decolorized and
|
||||||
|
// we're done.
|
||||||
|
|
||||||
|
ranges.remove(range);
|
||||||
|
} else if (range.getSize() < size) {
|
||||||
|
// If the range is smaller the entire range is decolorized and
|
||||||
|
// the
|
||||||
|
// remaining bytes are decolorized in the next step.
|
||||||
|
|
||||||
|
ranges.remove(range);
|
||||||
|
removeRange(range.getStart() + range.getSize(),
|
||||||
|
size - range.getSize());
|
||||||
|
} else
|
||||||
|
// if (range.getSize() > size)
|
||||||
|
{
|
||||||
|
// If the range is larger than the decolorizing area, ditch the
|
||||||
|
// range and add a new range that contains the area that stays
|
||||||
|
// colorized.
|
||||||
|
|
||||||
|
ranges.remove(range);
|
||||||
|
addRange(new ColoredRange(offset + size,
|
||||||
|
range.getSize() - size, range.getColor(),
|
||||||
|
range.getBackgroundColor()));
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
// if (offset > range.getStart())
|
||||||
|
{
|
||||||
|
// We start to decolorize somewhere in the middle of the range.
|
||||||
|
|
||||||
|
if (offset + size == range.getStart() + range.getSize()) {
|
||||||
|
// If the last offset of the range and the decolorizing area is equal,
|
||||||
|
// then the latter part of the range is ditched. Get rid of the range
|
||||||
|
// and create a new range that represents the first part of the area.
|
||||||
|
|
||||||
|
ranges.remove(range);
|
||||||
|
|
||||||
|
final long newStart = range.getStart();
|
||||||
|
final int newSize = range.getSize() - size;
|
||||||
|
// int newSize = (int)(offset - range.getStart());
|
||||||
|
addRange(new ColoredRange(newStart, newSize, range.getColor(), range.getBackgroundColor()));
|
||||||
|
} else if (offset + size < range.getStart() + range.getSize()) {
|
||||||
|
// Some part is cut out of the middle of the range. Get rid of the old range
|
||||||
|
// and create two new ranges for the first part and last part of the old range.
|
||||||
|
|
||||||
|
ranges.remove(range);
|
||||||
|
|
||||||
|
final long newStartFirst = range.getStart();
|
||||||
|
final int newSizeFirst = (int) (offset - range.getStart());
|
||||||
|
|
||||||
|
addRange(new ColoredRange(newStartFirst, newSizeFirst,
|
||||||
|
range.getColor(), range.getBackgroundColor()));
|
||||||
|
|
||||||
|
final long newStartLast = offset + size;
|
||||||
|
final int newSizeLast = (int) (range.getStart()
|
||||||
|
+ range.getSize() - offset - size);
|
||||||
|
|
||||||
|
addRange(new ColoredRange(newStartLast, newSizeLast,
|
||||||
|
range.getColor(), range.getBackgroundColor()));
|
||||||
|
} else
|
||||||
|
// if (offset + size > range.getStart() + range.getSize())
|
||||||
|
{
|
||||||
|
// More than the current range must be decolorized. That means
|
||||||
|
// the entire range
|
||||||
|
// except for the start is ditched.
|
||||||
|
|
||||||
|
ranges.remove(range);
|
||||||
|
|
||||||
|
final long newStart = range.getStart();
|
||||||
|
final int newSize = (int) (offset - range.getStart());
|
||||||
|
|
||||||
|
addRange(new ColoredRange(newStart, newSize, range.getColor(), range.getBackgroundColor()));
|
||||||
|
removeRange(range.getStart() + range.getSize(), size - (int) (range.getStart() + range.getSize() - offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
|
||||||
|
public interface IColormap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the byte at the given offset should be
|
||||||
|
* colored or not.
|
||||||
|
*
|
||||||
|
* @param data The data array that can be used to determine the return value.
|
||||||
|
* @param currentOffset The offset of the byte in question.
|
||||||
|
*
|
||||||
|
* @return True if the byte should be colored. False, otherwise.
|
||||||
|
*/
|
||||||
|
boolean colorize(final byte[] data, final long currentOffset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the background color that should be used to color
|
||||||
|
* the byte at the given offset.
|
||||||
|
*
|
||||||
|
* @param data The data array that can be used to determine the return value.
|
||||||
|
* @param currentOffset The offset of the byte in question.
|
||||||
|
*
|
||||||
|
* @return The background color to be used by that byte. Null, if
|
||||||
|
* the default background color should be used,
|
||||||
|
*/
|
||||||
|
Color getBackgroundColor(final byte[] data, final long currentOffset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the foreground color that should be used to color
|
||||||
|
* the byte at the given offset.
|
||||||
|
*
|
||||||
|
* @param data The data array that can be used to determine the return value.
|
||||||
|
* @param currentOffset The offset of the byte in question.
|
||||||
|
*
|
||||||
|
* @return The foreground color to be used by that byte. Null, if
|
||||||
|
* the default foreground color should be used,
|
||||||
|
*/
|
||||||
|
Color getForegroundColor(final byte[] data, final long currentOffset);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
public interface IDataChangedListener {
|
||||||
|
|
||||||
|
void dataChanged();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
public interface IDataProvider {
|
||||||
|
|
||||||
|
void addListener(final IDataChangedListener hexView);
|
||||||
|
|
||||||
|
byte[] getData();
|
||||||
|
|
||||||
|
byte[] getData(long offset, int length);
|
||||||
|
|
||||||
|
int getDataLength();
|
||||||
|
|
||||||
|
boolean hasData(long start, int length);
|
||||||
|
|
||||||
|
boolean isEditable();
|
||||||
|
|
||||||
|
boolean keepTrying();
|
||||||
|
|
||||||
|
void removeListener(IDataChangedListener listener);
|
||||||
|
|
||||||
|
void setData(long offset, byte[] data);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
public interface IHexViewListener {
|
||||||
|
|
||||||
|
void selectionChanged(long start, long length);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
import javax.swing.JPopupMenu;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface must be implemented by all classes that want to provide
|
||||||
|
* context menus for the JHexView control.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface IMenuCreator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called to generate a popup menu after the user
|
||||||
|
* right-clicked somewhere in the hex control.
|
||||||
|
*
|
||||||
|
* @param offset The offset of the right-click.
|
||||||
|
*
|
||||||
|
* @return The popup menu suitable for that offset or null if no popup menu
|
||||||
|
* should be shown.
|
||||||
|
*/
|
||||||
|
JPopupMenu createMenu(long offset);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,64 @@
|
||||||
|
package tv.porst.jhexview;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data provider that provides data to the hex view component from a
|
||||||
|
* static array. Use this data provider if you already have all the data
|
||||||
|
* in memory and do not have to reload memory from an external source.
|
||||||
|
*/
|
||||||
|
public final class SimpleDataProvider implements IDataProvider {
|
||||||
|
|
||||||
|
private final byte[] m_data;
|
||||||
|
|
||||||
|
public SimpleDataProvider(final byte[] data) {
|
||||||
|
this.m_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addListener(final IDataChangedListener listener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getData() {
|
||||||
|
return getData(0L, getDataLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getData(final long offset, final int length) {
|
||||||
|
return Arrays.copyOfRange(this.m_data, (int) offset, (int) (offset + length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDataLength() {
|
||||||
|
return this.m_data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getOffset() {
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasData(final long offset, final int length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEditable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keepTrying() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeListener(final IDataChangedListener listener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setData(final long offset, final byte[] data) {
|
||||||
|
System.arraycopy(data, 0, this.m_data, (int) offset, data.length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package tv.porst.splib.arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains helper functions for working with arrays.
|
||||||
|
*/
|
||||||
|
public final class ArrayHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a sub array of a given array.
|
||||||
|
*
|
||||||
|
* @param source The source array from which the sub array is extracted.
|
||||||
|
* @param offset The start offset of the sub array.
|
||||||
|
* @param length The length of the sub array in bytes.
|
||||||
|
*
|
||||||
|
* @return The sub array.
|
||||||
|
*/
|
||||||
|
public static byte[] getSubArray(final byte[] source, final int offset, final int length) {
|
||||||
|
|
||||||
|
if (source == null) {
|
||||||
|
throw new IllegalArgumentException("Source argument must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset < 0) {
|
||||||
|
throw new IllegalArgumentException("Start offset must not be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset >= source.length) {
|
||||||
|
throw new IllegalArgumentException("Start offset is too big.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
throw new IllegalArgumentException("Length must not be negative.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + length > source.length) {
|
||||||
|
throw new IllegalArgumentException("Range is too big.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] subArray = new byte[length];
|
||||||
|
|
||||||
|
System.arraycopy(source, offset, subArray, 0, length);
|
||||||
|
|
||||||
|
return subArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes data from a byte array.
|
||||||
|
*
|
||||||
|
* @param data The input byte array.
|
||||||
|
* @param startOffset The start offset of the data to remove.
|
||||||
|
* @param length Length in bytes of the data to remove.
|
||||||
|
*
|
||||||
|
* @return The new array without the removed data.
|
||||||
|
*/
|
||||||
|
public static byte[] removeData(final byte[] data, final int startOffset, final int length) {
|
||||||
|
final byte[] newData = new byte[data.length - length];
|
||||||
|
|
||||||
|
System.arraycopy(data, 0, newData, 0, startOffset);
|
||||||
|
System.arraycopy(data, startOffset + length, newData, startOffset, data.length - startOffset - length);
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces data in a byte array.
|
||||||
|
*
|
||||||
|
* @param data The input byte array.
|
||||||
|
* @param startOffset The start offset of the data to change.
|
||||||
|
* @param length The length in bytes of the data to change.
|
||||||
|
* @param replacementValue The new value for each byte in the replacement range.
|
||||||
|
*
|
||||||
|
* @return The new array with the replaced data.
|
||||||
|
*/
|
||||||
|
public static byte[] replaceData(final byte[] data, final int startOffset, final int length, final byte replacementValue) {
|
||||||
|
final byte[] newData = data.clone();
|
||||||
|
|
||||||
|
for (int i=startOffset;i<startOffset+length;i++) {
|
||||||
|
newData[i] = replacementValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for working with arrays.
|
||||||
|
*/
|
|
@ -0,0 +1,35 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed ASCII string.
|
||||||
|
*/
|
||||||
|
public final class AsciiString extends ParsedType<String> implements IFileElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag that says whether the parsed string was zero-terminated or not.
|
||||||
|
*/
|
||||||
|
private final boolean zeroTerminated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new AsciiString object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the string in the input stream.
|
||||||
|
* @param value String value.
|
||||||
|
* @param zeroTerminated Flag that says whether the parsed string was zero-terminated or not.
|
||||||
|
*/
|
||||||
|
public AsciiString(final int bytePosition, final String value, final boolean zeroTerminated) {
|
||||||
|
|
||||||
|
super(bytePosition, (value.length() + (zeroTerminated ? 1 : 0)) * 8, value);
|
||||||
|
|
||||||
|
this.zeroTerminated = zeroTerminated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the flag that says whether the parsed string was zero-terminated or not.
|
||||||
|
*
|
||||||
|
* @return The flag that says whether the parsed string was zero-terminated or not.
|
||||||
|
*/
|
||||||
|
public boolean isZeroTerminated() {
|
||||||
|
return zeroTerminated;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,531 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that can be used to parse simple data structures from
|
||||||
|
* byte streams.
|
||||||
|
*/
|
||||||
|
public class BinaryParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data to parse.
|
||||||
|
*/
|
||||||
|
private final byte[] data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current byte position.
|
||||||
|
*/
|
||||||
|
private int bytePosition = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current bit position.
|
||||||
|
*/
|
||||||
|
private int bitPosition = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new binary parser object that parses data from
|
||||||
|
* the given byte array.
|
||||||
|
*
|
||||||
|
* @param data The source array.
|
||||||
|
*/
|
||||||
|
public BinaryParser(final byte[] data) {
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
throw new IllegalArgumentException("Data argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aligns the parser to the next byte.
|
||||||
|
*/
|
||||||
|
public void align() {
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
bytePosition++;
|
||||||
|
bitPosition = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current bit position.
|
||||||
|
*
|
||||||
|
* @return The current bit position.
|
||||||
|
*/
|
||||||
|
public int getBitPosition() {
|
||||||
|
return bitPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current byte position.
|
||||||
|
*
|
||||||
|
* @return The current byte position.
|
||||||
|
*/
|
||||||
|
public int getBytePosition() {
|
||||||
|
return bytePosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the byte stream.
|
||||||
|
*
|
||||||
|
* @return The length of the byte stream.
|
||||||
|
*/
|
||||||
|
public int getLength() {
|
||||||
|
return data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether parsing the whole byte stream is complete.
|
||||||
|
*
|
||||||
|
* @return True, if parsing is complete. False, if there is data left to parse.
|
||||||
|
*/
|
||||||
|
public boolean isDone() {
|
||||||
|
return bytePosition == data.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeks at the next few bits without moving the current parsing
|
||||||
|
* position forward.
|
||||||
|
*
|
||||||
|
* @param numberOfBits The number of bits to peek at. This value must be between 0 and 32.
|
||||||
|
*
|
||||||
|
* @return The peeked bits.
|
||||||
|
*/
|
||||||
|
public UBits peekBits(final int numberOfBits) {
|
||||||
|
|
||||||
|
if (numberOfBits < 0 || numberOfBits > 32) {
|
||||||
|
throw new IllegalArgumentException("Number of bits argument must be between 0 and 32");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int bytePosition = this.bytePosition;
|
||||||
|
final int bitPosition = this.bitPosition;
|
||||||
|
|
||||||
|
final UBits value = readBits(numberOfBits);
|
||||||
|
|
||||||
|
this.bitPosition = bitPosition;
|
||||||
|
this.bytePosition = bytePosition;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeks at the next UINT16 value without moving the current parsing
|
||||||
|
* position forward.
|
||||||
|
*
|
||||||
|
* @return The peeked UINT16 value.
|
||||||
|
*/
|
||||||
|
public UINT16 peekUInt16() {
|
||||||
|
|
||||||
|
final int bytePosition = this.bytePosition;
|
||||||
|
final int bitPosition = this.bitPosition;
|
||||||
|
|
||||||
|
final UINT16 value = readUInt16();
|
||||||
|
|
||||||
|
this.bitPosition = bitPosition;
|
||||||
|
this.bytePosition = bytePosition;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeks at the next UINT32 value without moving the current parsing
|
||||||
|
* position forward.
|
||||||
|
*
|
||||||
|
* @return The peeked UINT32 value.
|
||||||
|
*/
|
||||||
|
public UINT32 peekUInt32() {
|
||||||
|
|
||||||
|
final int bytePosition = this.bytePosition;
|
||||||
|
final int bitPosition = this.bitPosition;
|
||||||
|
|
||||||
|
final UINT32 value = readUInt32();
|
||||||
|
|
||||||
|
this.bitPosition = bitPosition;
|
||||||
|
this.bytePosition = bytePosition;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peeks at the next UINT8 value without moving the current parsing
|
||||||
|
* position forward.
|
||||||
|
*
|
||||||
|
* @return The peeked UINT8 value.
|
||||||
|
*/
|
||||||
|
public UINT8 peekUInt8() {
|
||||||
|
|
||||||
|
final int bytePosition = this.bytePosition;
|
||||||
|
final int bitPosition = this.bitPosition;
|
||||||
|
|
||||||
|
final UINT8 value = readUInt8();
|
||||||
|
|
||||||
|
this.bitPosition = bitPosition;
|
||||||
|
this.bytePosition = bytePosition;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next few bits from the byte stream.
|
||||||
|
*
|
||||||
|
* @param numberOfBits The number of bits to read. This value must be between 0 and 32.
|
||||||
|
*
|
||||||
|
* @return The read bits.
|
||||||
|
*/
|
||||||
|
public UBits readBits(final int numberOfBits) {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + numberOfBits > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int oldBytePosition = bytePosition;
|
||||||
|
final int oldBitPosition = bitPosition;
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
for (int i=0;i<numberOfBits;i++) {
|
||||||
|
value = (value << 1) | ((data[bytePosition] >> (7 - bitPosition)) & 1);
|
||||||
|
|
||||||
|
bitPosition++;
|
||||||
|
|
||||||
|
if (bitPosition == 8) {
|
||||||
|
bitPosition = 0;
|
||||||
|
bytePosition++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UBits(8 * oldBytePosition + oldBitPosition, numberOfBits, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next byte from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The read byte.
|
||||||
|
*/
|
||||||
|
public byte readByte() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 8 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
return data[bytePosition++];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next bit from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The read bit.
|
||||||
|
*/
|
||||||
|
public Flag readFlag() {
|
||||||
|
|
||||||
|
final int bytePosition = this.bytePosition;
|
||||||
|
final int bitPosition = this.bitPosition;
|
||||||
|
|
||||||
|
final UBits value = readBits(1);
|
||||||
|
|
||||||
|
return new Flag(8 * bytePosition + bitPosition, value.value() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next float from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next float.
|
||||||
|
*/
|
||||||
|
public Float32 readFloat() {
|
||||||
|
|
||||||
|
final float value = Float.intBitsToFloat(readInt32().value());
|
||||||
|
|
||||||
|
return new Float32(8 * bytePosition + bitPosition - 32, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next short float from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next short float.
|
||||||
|
*/
|
||||||
|
public Float16 readFloat16() {
|
||||||
|
readInt16();
|
||||||
|
|
||||||
|
return new Float16(8 * bytePosition + bitPosition - 16, (float) 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next float from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next float.
|
||||||
|
*/
|
||||||
|
public Float64 readFloat64() {
|
||||||
|
|
||||||
|
final double value = Double.longBitsToDouble(readInt64().value());
|
||||||
|
|
||||||
|
return new Float64(8 * bytePosition + bitPosition - 64, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next short integer from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next short integer.
|
||||||
|
*/
|
||||||
|
public INT16 readInt16() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 16 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
final int secondByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
return new INT16(8 * bytePosition + bitPosition - 16, secondByte << 8 | firstByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next three-byte integer from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next three-byte integer.
|
||||||
|
*/
|
||||||
|
public INT24 readInt24() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 24 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
final int secondByte = readByte() & 0xFF;
|
||||||
|
final int thirdByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
int value = thirdByte << 16 | secondByte << 8 | firstByte;
|
||||||
|
|
||||||
|
if (value > 0x7FFFFF) {
|
||||||
|
value = value - 0x1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new INT24(8 * bytePosition + bitPosition - 24, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next integer from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next integer.
|
||||||
|
*/
|
||||||
|
public INT32 readInt32() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 32 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
final int secondByte = readByte() & 0xFF;
|
||||||
|
final int thirdByte = readByte() & 0xFF;
|
||||||
|
final int fourthByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
return new INT32(8 * bytePosition + bitPosition - 32, fourthByte << 24 | thirdByte << 16 | secondByte << 8 | firstByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next long from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next integer.
|
||||||
|
*/
|
||||||
|
public INT64 readInt64() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 64 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
final int secondByte = readByte() & 0xFF;
|
||||||
|
final int thirdByte = readByte() & 0xFF;
|
||||||
|
final int fourthByte = readByte() & 0xFF;
|
||||||
|
final int fifthByte = readByte() & 0xFF;
|
||||||
|
final int sixthByte = readByte() & 0xFF;
|
||||||
|
final int seventhByte = readByte() & 0xFF;
|
||||||
|
final int eigthByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
return new INT64(8 * bytePosition + bitPosition - 64, eigthByte << 56 | seventhByte << 48 | sixthByte << 40 | fifthByte << 32 | fourthByte << 24 | thirdByte << 16 | secondByte << 8 | firstByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next few signed bits from the byte stream.
|
||||||
|
*
|
||||||
|
* @param numberOfBits The number of signed bits to read. This value must be between 0 and 32.
|
||||||
|
*
|
||||||
|
* @return The read signed bits.
|
||||||
|
*/
|
||||||
|
public Bits readSBits(final int numberOfBits) {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + numberOfBits > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int oldBytePosition = bytePosition;
|
||||||
|
final int oldBitPosition = bitPosition;
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
for (int i=0;i<numberOfBits;i++) {
|
||||||
|
value = (value << 1) | ((data[bytePosition] >> (7 - bitPosition)) & 1);
|
||||||
|
|
||||||
|
bitPosition++;
|
||||||
|
|
||||||
|
if (bitPosition == 8) {
|
||||||
|
bitPosition = 0;
|
||||||
|
bytePosition++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Bits(8 * oldBytePosition + oldBitPosition, numberOfBits, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next 0-terminated ASCII string from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The read string.
|
||||||
|
*/
|
||||||
|
public AsciiString readString() {
|
||||||
|
|
||||||
|
final int bytePosition = this.bytePosition;
|
||||||
|
final int bitPosition = this.bitPosition;
|
||||||
|
|
||||||
|
final StringBuffer value = new StringBuffer();
|
||||||
|
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
while ((b = readByte()) != 0)
|
||||||
|
{
|
||||||
|
value.append((char) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AsciiString(8 * bytePosition + bitPosition, value.toString(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next number of bytes from the byte stream into an ASCII string.
|
||||||
|
*
|
||||||
|
* @param numberOfBytes The number of bytes to add to the string.
|
||||||
|
*
|
||||||
|
* @return The read string.
|
||||||
|
*/
|
||||||
|
public AsciiString readString(final int numberOfBytes) {
|
||||||
|
|
||||||
|
if (numberOfBytes < 0) {
|
||||||
|
throw new IllegalArgumentException("The number of bytes in the string can not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuffer value = new StringBuffer();
|
||||||
|
|
||||||
|
for (int i=0;i<numberOfBytes;i++)
|
||||||
|
{
|
||||||
|
value.append((char) readByte());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AsciiString(8 * bytePosition - 8 * numberOfBytes, value.toString(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next unsigned short integer from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next unsigned short integer.
|
||||||
|
*/
|
||||||
|
public UINT16 readUInt16() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 16 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
final int secondByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
return new UINT16(8 * bytePosition + bitPosition - 16, secondByte << 8 | firstByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next unsigned integer from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next unsigned integer.
|
||||||
|
*/
|
||||||
|
public UINT32 readUInt32() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 32 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
final int secondByte = readByte() & 0xFF;
|
||||||
|
final int thirdByte = readByte() & 0xFF;
|
||||||
|
final int fourthByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
return new UINT32(8 * bytePosition + bitPosition - 32, fourthByte << 24 | thirdByte << 16 | secondByte << 8 | firstByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next unsigned byte from the byte stream.
|
||||||
|
*
|
||||||
|
* @return The next unsigned byte.
|
||||||
|
*/
|
||||||
|
public UINT8 readUInt8() {
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition + 32 > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Not enough data left");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition != 0) {
|
||||||
|
throw new IllegalStateException("Parser is not byte aligned");
|
||||||
|
}
|
||||||
|
|
||||||
|
final int firstByte = readByte() & 0xFF;
|
||||||
|
|
||||||
|
return new UINT8(8 * bytePosition + bitPosition - 8, firstByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current read position in the byte stream.
|
||||||
|
*
|
||||||
|
* @param bytePosition The new byte position.
|
||||||
|
* @param bitPosition The new bit position.
|
||||||
|
*/
|
||||||
|
public void setPosition(final int bytePosition, final int bitPosition) {
|
||||||
|
|
||||||
|
if (bytePosition < 0) {
|
||||||
|
throw new IllegalArgumentException("Byte position argument must not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bitPosition < 0) {
|
||||||
|
throw new IllegalArgumentException("Bit position argument must not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytePosition * 8 + bitPosition > data.length * 8) {
|
||||||
|
throw new IllegalArgumentException("Can not move read position beyond the end of the input buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bytePosition = bytePosition;
|
||||||
|
this.bitPosition = bitPosition;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception class used to signal exceptions during binary stream parsing.
|
||||||
|
*/
|
||||||
|
public final class BinaryParserException extends RuntimeException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new exception object.
|
||||||
|
*
|
||||||
|
* @param message The exception message.
|
||||||
|
*/
|
||||||
|
public BinaryParserException(final String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains helper functions for working with the binary parser.
|
||||||
|
*/
|
||||||
|
public final class BinaryParserHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a certain number of bits can be read from the input stream before
|
||||||
|
* the end of the stream is reached.
|
||||||
|
*
|
||||||
|
* @param parser The parser whose stream is checked.
|
||||||
|
* @param numberOfBits The number of bits to check for.
|
||||||
|
*
|
||||||
|
* @return True, if there are the requested number of bits left in the input stream. False, otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean hasBitsLeft(final BinaryParser parser, final long numberOfBits) {
|
||||||
|
|
||||||
|
if (parser == null) {
|
||||||
|
throw new IllegalArgumentException("Parser argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numberOfBits < 0) {
|
||||||
|
throw new IllegalArgumentException("Number of requested bits can not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser.getBytePosition() * 8 + parser.getBitPosition() + numberOfBits <= parser.getLength() * 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a certain number of bytes can be read from the input stream before
|
||||||
|
* the end of the stream is reached.
|
||||||
|
*
|
||||||
|
* @param parser The parser whose stream is checked.
|
||||||
|
* @param numberOfBytes The number of bytes to check for.
|
||||||
|
*
|
||||||
|
* @return True, if there are the requested number of bytes left in the input stream. False, otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean hasBytesLeft(final BinaryParser parser, final long numberOfBytes) {
|
||||||
|
|
||||||
|
if (parser == null) {
|
||||||
|
throw new IllegalArgumentException("Parser argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numberOfBytes < 0) {
|
||||||
|
throw new IllegalArgumentException("Number of requested bytes can not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasBitsLeft(parser, numberOfBytes * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a byte array from the input stream of a parser.
|
||||||
|
*
|
||||||
|
* @param parser The parser from which the byte array is read.
|
||||||
|
* @param length The length of the byte array to read.
|
||||||
|
*
|
||||||
|
* @return The byte array.
|
||||||
|
*/
|
||||||
|
public static byte[] readByteArray(final BinaryParser parser, final int length) {
|
||||||
|
|
||||||
|
if (parser == null) {
|
||||||
|
throw new IllegalArgumentException("Parser argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
throw new IllegalArgumentException("The length of the byte array must not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasBytesLeft(parser, length)) {
|
||||||
|
throw new IllegalArgumentException("Not enough bytes left in the input stream");
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] array = new byte[length];
|
||||||
|
|
||||||
|
for (int i=0;i<array.length;i++) {
|
||||||
|
array[i] = parser.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed bit field.
|
||||||
|
*/
|
||||||
|
public final class Bits extends ParsedType<Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new bit field object.
|
||||||
|
*
|
||||||
|
* @param bitPosition Bit position of the bit field in the input stream.
|
||||||
|
* @param bitLength Length of the field in bits.
|
||||||
|
* @param value Bit field value.
|
||||||
|
*/
|
||||||
|
public Bits(final int bitPosition, final int bitLength, final Integer value) {
|
||||||
|
super(bitPosition, bitLength, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed flag.
|
||||||
|
*/
|
||||||
|
public final class Flag extends ParsedType<Boolean> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new flag object.
|
||||||
|
*
|
||||||
|
* @param bitPosition Bit position of the flag in the input stream.
|
||||||
|
* @param value Floag value.
|
||||||
|
*/
|
||||||
|
public Flag(final int bitPosition, final boolean value) {
|
||||||
|
super(bitPosition, 1, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed short float.
|
||||||
|
*/
|
||||||
|
public class Float16 extends ParsedType<Float> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new short float object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the short float in the input stream.
|
||||||
|
* @param value Float value.
|
||||||
|
*/
|
||||||
|
public Float16(final int bytePosition, final Float value) {
|
||||||
|
super(bytePosition, 16, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed float.
|
||||||
|
*/
|
||||||
|
public final class Float32 extends ParsedType<Float> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new float object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the float in the input stream.
|
||||||
|
* @param value Float value.
|
||||||
|
*/
|
||||||
|
public Float32(final int bytePosition, final Float value) {
|
||||||
|
super(bytePosition, 32, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed float.
|
||||||
|
*/
|
||||||
|
public final class Float64 extends ParsedType<Double> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new float object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the float in the input stream.
|
||||||
|
* @param value Float value.
|
||||||
|
*/
|
||||||
|
public Float64(final int bytePosition, final Double value) {
|
||||||
|
super(bytePosition, 64, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by all parsed types.
|
||||||
|
*/
|
||||||
|
public interface IFileElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the length of the element in bits.
|
||||||
|
*
|
||||||
|
* @return The length of the element in bits.
|
||||||
|
*/
|
||||||
|
int getBitLength();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the element in bits.
|
||||||
|
*
|
||||||
|
* @return The position of the element in bits.
|
||||||
|
*/
|
||||||
|
int getBitPosition();
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed short int.
|
||||||
|
*/
|
||||||
|
public final class INT16 extends ParsedType<Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new short int object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the short int in the input stream.
|
||||||
|
* @param value Integer value.
|
||||||
|
*/
|
||||||
|
public INT16(final int bytePosition, final int value) {
|
||||||
|
super(bytePosition, 16, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed 3-byte int.
|
||||||
|
*/
|
||||||
|
public final class INT24 extends ParsedType<Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new 3-byte int object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the 3-byte int in the input stream.
|
||||||
|
* @param value Integer value.
|
||||||
|
*/
|
||||||
|
public INT24(final int bytePosition, final int value) {
|
||||||
|
super(bytePosition, 24, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed int.
|
||||||
|
*/
|
||||||
|
public final class INT32 extends ParsedType<Integer> implements IParsedINTElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the type in bytes.
|
||||||
|
*/
|
||||||
|
public static final int BYTE_LENGTH = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new short int object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the int in the input stream.
|
||||||
|
* @param value Integer value.
|
||||||
|
*/
|
||||||
|
public INT32(final int bytePosition, final int value) {
|
||||||
|
super(bytePosition, 32, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed long.
|
||||||
|
*/
|
||||||
|
public final class INT64 extends ParsedType<Long> implements IParsedINTElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new long object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the long in the input stream.
|
||||||
|
* @param value Long value.
|
||||||
|
*/
|
||||||
|
public INT64(final int bytePosition, final long value) {
|
||||||
|
super(bytePosition, 64, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by parsed integer values.
|
||||||
|
*/
|
||||||
|
public interface IParsedINTElement extends IFileElement {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all parsed types.
|
||||||
|
*
|
||||||
|
* @param <ValueType> Type of the parsed value.
|
||||||
|
*/
|
||||||
|
public abstract class ParsedType<ValueType> implements IFileElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bit position of the parsed value in the input stream.
|
||||||
|
*/
|
||||||
|
private final int bitPosition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parsed value.
|
||||||
|
*/
|
||||||
|
private final ValueType value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the field in bits.
|
||||||
|
*/
|
||||||
|
private final int bitLength;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new parsed type object.
|
||||||
|
*
|
||||||
|
* @param bitPosition Bit position of the parsed value in the input stream.
|
||||||
|
* @param bitLength Length of the field in bits.
|
||||||
|
* @param value Parsed value.
|
||||||
|
*/
|
||||||
|
public ParsedType(final int bitPosition, final int bitLength, final ValueType value) {
|
||||||
|
|
||||||
|
if (bitPosition < 0) {
|
||||||
|
throw new IllegalArgumentException("Byte position must not be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException("Value argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bitPosition = bitPosition;
|
||||||
|
this.bitLength = bitLength;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBitLength() {
|
||||||
|
return bitLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the byte position of the parsed value in the input stream.
|
||||||
|
*
|
||||||
|
* @return The byte position of the parsed value in the input stream.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int getBitPosition() {
|
||||||
|
return bitPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parsed value.
|
||||||
|
*
|
||||||
|
* @return The parsed value.
|
||||||
|
*/
|
||||||
|
public ValueType value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed unsigned bit field.
|
||||||
|
*/
|
||||||
|
public final class UBits extends ParsedType<Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unsigned bit field object.
|
||||||
|
*
|
||||||
|
* @param bitPosition Bit position of the unsigned bit field in the input stream.
|
||||||
|
* @param bitLength Length of the field in bits.
|
||||||
|
* @param value Unsigned bit field value.
|
||||||
|
*/
|
||||||
|
public UBits(final int bitPosition, final int bitLength, final Integer value) {
|
||||||
|
super(bitPosition, bitLength, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed unsigned short int.
|
||||||
|
*/
|
||||||
|
public final class UINT16 extends ParsedType<Integer> implements IParsedINTElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the type in bytes.
|
||||||
|
*/
|
||||||
|
public static final int BYTE_LENGTH = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unsigned short int object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the unsigned short int in the input stream.
|
||||||
|
* @param value Integer value.
|
||||||
|
*/
|
||||||
|
public UINT16(final int bytePosition, final int value) {
|
||||||
|
super(bytePosition, 16, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed unsigned int.
|
||||||
|
*/
|
||||||
|
public class UINT32 extends ParsedType<Long> implements IParsedINTElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the type in bytes.
|
||||||
|
*/
|
||||||
|
public static final int BYTE_LENGTH = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unsigned int object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the unsigned int in the input stream.
|
||||||
|
* @param value Integer value.
|
||||||
|
*/
|
||||||
|
public UINT32(final int bytePosition, final long value) {
|
||||||
|
super(bytePosition, 32, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a parsed unsigned byte.
|
||||||
|
*/
|
||||||
|
public final class UINT8 extends ParsedType<Integer> implements IParsedINTElement {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the type in bytes.
|
||||||
|
*/
|
||||||
|
public static final int BYTE_LENGTH = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new unsigned byte object.
|
||||||
|
*
|
||||||
|
* @param bytePosition Byte position of the unsigned byte in the input stream.
|
||||||
|
* @param value Integer value.
|
||||||
|
*/
|
||||||
|
public UINT8(final int bytePosition, final int value) {
|
||||||
|
super(bytePosition, 8, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package tv.porst.splib.binaryparser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for parsing binary data. The main class
|
||||||
|
* of this package is {@link BinaryParser}. Using this class you can parse
|
||||||
|
* binary data into the types defined in the other classes.
|
||||||
|
*/
|
|
@ -0,0 +1,51 @@
|
||||||
|
package tv.porst.splib.convert;
|
||||||
|
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts helper functions for working with different data types.
|
||||||
|
*/
|
||||||
|
public final class ConvertHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether a given character is a valid decimal character.
|
||||||
|
*
|
||||||
|
* @param c The character to test.
|
||||||
|
*
|
||||||
|
* @return True, if the given character is a valid decimal character.
|
||||||
|
*/
|
||||||
|
public static boolean isDecCharacter(final char c)
|
||||||
|
{
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether a character is a valid character of a hexadecimal string.
|
||||||
|
*
|
||||||
|
* @param c The character to test.
|
||||||
|
*
|
||||||
|
* @return True, if the character is a hex character. False, otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isHexCharacter(final char c)
|
||||||
|
{
|
||||||
|
return isDecCharacter(c) || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether a character is a printable ASCII character.
|
||||||
|
*
|
||||||
|
* @param c The character to test.
|
||||||
|
*
|
||||||
|
* @return True, if the character is a printable ASCII character. False,
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public static boolean isPrintableCharacter(final char c)
|
||||||
|
{
|
||||||
|
final Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
|
||||||
|
|
||||||
|
return !Character.isISOControl(c) &&
|
||||||
|
c != KeyEvent.CHAR_UNDEFINED &&
|
||||||
|
block != null &&
|
||||||
|
block != Character.UnicodeBlock.SPECIALS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.convert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for converting between types.
|
||||||
|
*/
|
|
@ -0,0 +1,33 @@
|
||||||
|
package tv.porst.splib.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for recursive directory traversal.
|
||||||
|
*/
|
||||||
|
public final class DirectoryTraverser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Traverses a directory.
|
||||||
|
*
|
||||||
|
* @param directory The directory to traverse.
|
||||||
|
* @param callback The callback object to invoked for each encountered file.
|
||||||
|
*/
|
||||||
|
public static void traverse(final File directory, final IDirectoryTraversalVisitor callback) {
|
||||||
|
|
||||||
|
if (directory.isDirectory()) {
|
||||||
|
for (final File file : directory.listFiles()) {
|
||||||
|
if (!file.isDirectory()) {
|
||||||
|
callback.visit(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final File file : directory.listFiles()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
traverse(file, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
package tv.porst.splib.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains helper functions for working with files.
|
||||||
|
*/
|
||||||
|
public final class FileHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the filename from a full path.
|
||||||
|
*
|
||||||
|
* @param fullPath The full input path.
|
||||||
|
*
|
||||||
|
* @return The raw filename.
|
||||||
|
*/
|
||||||
|
public static String extractFilename(final String fullPath) {
|
||||||
|
|
||||||
|
final int index = fullPath.lastIndexOf(File.separator);
|
||||||
|
|
||||||
|
return index == -1 ? fullPath : fullPath.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a whole file into a byte array.
|
||||||
|
*
|
||||||
|
* @param file The input file.
|
||||||
|
*
|
||||||
|
* @return The bytes read from the file.
|
||||||
|
*
|
||||||
|
* @throws IOException Thrown if the file could not be read.
|
||||||
|
*/
|
||||||
|
public static byte[] readFile(final File file) throws IOException {
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
throw new IllegalArgumentException("File argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileInputStream inputStream = new FileInputStream(file);
|
||||||
|
|
||||||
|
final byte[] data = new byte[(int) file.length()];
|
||||||
|
inputStream.read(data);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a whole file into a byte array.
|
||||||
|
*
|
||||||
|
* @param file The input file.
|
||||||
|
*
|
||||||
|
* @return The bytes read from the file.
|
||||||
|
*
|
||||||
|
* @throws IOException Thrown if the file could not be read.
|
||||||
|
*/
|
||||||
|
public static byte[] readFile(final String file) throws IOException {
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
throw new IllegalArgumentException("File argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
return readFile(new File(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes binary data to a file.
|
||||||
|
*
|
||||||
|
* @param file The file to write to.
|
||||||
|
* @param data The data to write.
|
||||||
|
*
|
||||||
|
* @throws IOException Thrown if writing the data to the byte failed.
|
||||||
|
*/
|
||||||
|
public static void writeFile(final File file, final byte[] data) throws IOException {
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
throw new IllegalArgumentException("File argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final FileOutputStream inputStream = new FileOutputStream(file);
|
||||||
|
|
||||||
|
inputStream.write(data);
|
||||||
|
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes binary data to a file.
|
||||||
|
*
|
||||||
|
* @param fileName The name of the file to write to.
|
||||||
|
* @param data The data to write.
|
||||||
|
*
|
||||||
|
* @throws IOException Thrown if writing the data to the byte failed.
|
||||||
|
*/
|
||||||
|
public static void writeFile(final String fileName, final byte[] data) throws IOException {
|
||||||
|
|
||||||
|
if (fileName == null) {
|
||||||
|
throw new IllegalArgumentException("File name argument must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
final File file = new File(fileName);
|
||||||
|
|
||||||
|
final FileOutputStream inputStream = new FileOutputStream(file);
|
||||||
|
|
||||||
|
inputStream.write(data);
|
||||||
|
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package tv.porst.splib.file;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by all callback objects for directory traversal.
|
||||||
|
*/
|
||||||
|
public interface IDirectoryTraversalVisitor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a new file is found during traversal.
|
||||||
|
*
|
||||||
|
* @param file The next file.
|
||||||
|
*/
|
||||||
|
void visit(File file);
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for working with files.
|
||||||
|
*/
|
|
@ -0,0 +1,41 @@
|
||||||
|
package tv.porst.splib.general;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that provides a generic interface for working with listeners.
|
||||||
|
*
|
||||||
|
* @param <ListenerType> Type of the listeners managed by the class.
|
||||||
|
*/
|
||||||
|
public final class ListenerProvider<ListenerType> implements Iterable<ListenerType> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currently added listeners.
|
||||||
|
*/
|
||||||
|
private final List<ListenerType> listeners = new ArrayList<ListenerType>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new listener to the provider.
|
||||||
|
*
|
||||||
|
* @param listener The new listener object to add.
|
||||||
|
*/
|
||||||
|
public void add(final ListenerType listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<ListenerType> iterator() {
|
||||||
|
return listeners.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a listener from the provider.
|
||||||
|
*
|
||||||
|
* @param listener The listener to remove.
|
||||||
|
*/
|
||||||
|
public void remove(final ListenerType listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.general;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes that do not fit into any of the other packages.
|
||||||
|
*/
|
|
@ -0,0 +1,27 @@
|
||||||
|
package tv.porst.splib.gui;
|
||||||
|
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.GraphicsEnvironment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class that contains GUI functions.
|
||||||
|
*/
|
||||||
|
public final class GuiHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the system monospace font.
|
||||||
|
*
|
||||||
|
* @return The name of the system monospace font.
|
||||||
|
*/
|
||||||
|
public static String getMonospaceFont() {
|
||||||
|
|
||||||
|
final GraphicsEnvironment localGraphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||||
|
final Font[] fonts = localGraphicsEnvironment.getAllFonts();
|
||||||
|
for (final Font font : fonts) {
|
||||||
|
if (font.getName().equals("Courier New")) {
|
||||||
|
return "Courier New";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "Monospaced";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.gui;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for working with GUI components.
|
||||||
|
*/
|
|
@ -0,0 +1,15 @@
|
||||||
|
package tv.porst.splib.gui.caret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by classes that want to be notified about
|
||||||
|
* changed in the caret.
|
||||||
|
*/
|
||||||
|
public interface ICaretListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Invoked after the caret status changed.
|
||||||
|
*
|
||||||
|
* @param caret The caret whose status changed.
|
||||||
|
*/
|
||||||
|
public void caretStatusChanged(JCaret caret);
|
||||||
|
}
|
|
@ -0,0 +1,245 @@
|
||||||
|
package tv.porst.splib.gui.caret;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.Timer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caret class that can be used to draw carets in custom GUI components.
|
||||||
|
*/
|
||||||
|
public class JCaret
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The default blink time of the caret in milliseconds.
|
||||||
|
*/
|
||||||
|
private static final int DEFAULT_BLINK_TIME = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default color of the caret.
|
||||||
|
*/
|
||||||
|
private static final Color DEFAULT_CARET_COLOR = Color.RED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of listeners that are notified when the caret status changes.
|
||||||
|
*/
|
||||||
|
private final List<ICaretListener> m_listeners = new ArrayList<ICaretListener>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timer that is used to make the caret blink.
|
||||||
|
*/
|
||||||
|
private final Timer m_caretTimer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag that determines whether the caret is visible or not.
|
||||||
|
*/
|
||||||
|
private boolean m_isCaretVisible = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color of the caret.
|
||||||
|
*/
|
||||||
|
private final Color m_caretColor = Color.RED;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listeners that are notified about changes in the caret.
|
||||||
|
*/
|
||||||
|
private final InternalListener m_listener = new InternalListener();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new caret with a default blink period of 500ms and
|
||||||
|
* the default caret color red.
|
||||||
|
*/
|
||||||
|
public JCaret()
|
||||||
|
{
|
||||||
|
this(DEFAULT_BLINK_TIME, DEFAULT_CARET_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new caret with the default blink period and a custom caret color.
|
||||||
|
*
|
||||||
|
* @param caretColor The color of the caret.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException Thrown if the color is null.
|
||||||
|
*/
|
||||||
|
public JCaret(final Color caretColor)
|
||||||
|
{
|
||||||
|
this(DEFAULT_BLINK_TIME, caretColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new caret with a custom blink period and the default caret color red.
|
||||||
|
*
|
||||||
|
* @param blinkPeriod The blink period in milliseconds.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Thrown if the blink period is negative.
|
||||||
|
*/
|
||||||
|
public JCaret(final int blinkPeriod)
|
||||||
|
{
|
||||||
|
this(blinkPeriod, DEFAULT_CARET_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new caret with a custom blink period and a custom
|
||||||
|
* caret color.
|
||||||
|
*
|
||||||
|
* @param blinkPeriod The blink period in milliseconds.
|
||||||
|
* @param caretColor The color of the caret.
|
||||||
|
|
||||||
|
* @throws IllegalArgumentException Thrown if the blink period is negative.
|
||||||
|
* @throws NullPointerException Thrown if the color is null.
|
||||||
|
*/
|
||||||
|
public JCaret(final int blinkPeriod, final Color caretColor)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (blinkPeriod < 0)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Error: Blink period can't be negative");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (caretColor == null)
|
||||||
|
{
|
||||||
|
throw new NullPointerException("Error: Caret color can't be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the timer that makes the caret blink
|
||||||
|
m_caretTimer = new Timer(blinkPeriod, m_listener);
|
||||||
|
m_caretTimer.setRepeats(true);
|
||||||
|
m_caretTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies all listeners of a status change of the caret.
|
||||||
|
*/
|
||||||
|
private void notifyListeners()
|
||||||
|
{
|
||||||
|
for (final ICaretListener listener : m_listeners)
|
||||||
|
{
|
||||||
|
listener.caretStatusChanged(JCaret.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new status change listener to the list of listeners.
|
||||||
|
*
|
||||||
|
* @param listener The new listener.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException Thrown if the passed listener is null.
|
||||||
|
*/
|
||||||
|
public void addCaretListener(final ICaretListener listener)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (listener == null)
|
||||||
|
{
|
||||||
|
throw new NullPointerException("Error: Listener can't be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// No duplicate listeners
|
||||||
|
if (!m_listeners.contains(listener))
|
||||||
|
{
|
||||||
|
m_listeners.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws the caret on a given graphics surface.
|
||||||
|
*
|
||||||
|
* @param g The graphics surface where the caret is drawn.
|
||||||
|
* @param x The x coordinate of the caret.
|
||||||
|
* @param y The y coordinate of the caret.
|
||||||
|
* @param height The height of the caret.
|
||||||
|
*
|
||||||
|
* @throws NullPointerException Thrown if the graphics context is null.
|
||||||
|
*/
|
||||||
|
public void draw(final Graphics g, final int x, final int y, final int height)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (g == null)
|
||||||
|
{
|
||||||
|
throw new NullPointerException("Error: Graphics context can't be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isVisible())
|
||||||
|
{
|
||||||
|
|
||||||
|
// Save the old color.
|
||||||
|
final Color oldColor = g.getColor();
|
||||||
|
|
||||||
|
// Draw the caret
|
||||||
|
g.setColor(m_caretColor);
|
||||||
|
g.drawLine(x, y, x, y + height - 1);
|
||||||
|
|
||||||
|
// Restore the old color.
|
||||||
|
g.setColor(oldColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the caret is currently visible nor not.
|
||||||
|
*
|
||||||
|
* @return True, if the caret is visible. False, otherwise.
|
||||||
|
*/
|
||||||
|
public boolean isVisible()
|
||||||
|
{
|
||||||
|
return m_isCaretVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a registered listeners.
|
||||||
|
*
|
||||||
|
* @param listener The listener to remove.
|
||||||
|
*/
|
||||||
|
public void removeListener(final ICaretListener listener)
|
||||||
|
{
|
||||||
|
m_listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the visibility status of the caret.
|
||||||
|
*
|
||||||
|
* @param isCaretVisible The new visibility status of the caret.
|
||||||
|
*/
|
||||||
|
public void setVisible(final boolean isCaretVisible)
|
||||||
|
{
|
||||||
|
this.m_isCaretVisible = isCaretVisible;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the caret from blinking.
|
||||||
|
*/
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
m_caretTimer.stop();
|
||||||
|
m_caretTimer.removeActionListener(m_listener);
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class of internal listeners that are used to hide
|
||||||
|
* the callback functions from the public interface.
|
||||||
|
*/
|
||||||
|
private class InternalListener implements ActionListener
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is called every time the caret timer
|
||||||
|
* ticks.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(final ActionEvent arg0)
|
||||||
|
{
|
||||||
|
|
||||||
|
// Switch the caret status at every timer click.
|
||||||
|
m_isCaretVisible = !m_isCaretVisible;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.gui.caret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes that simulate a caret in custom components.
|
||||||
|
*/
|
|
@ -0,0 +1,76 @@
|
||||||
|
package tv.porst.splib.gui.tree;
|
||||||
|
|
||||||
|
import javax.swing.Icon;
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a node in an icon tree.
|
||||||
|
*/
|
||||||
|
public class IconNode extends DefaultMutableTreeNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The icon shown for the node.
|
||||||
|
*/
|
||||||
|
protected Icon icon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the icon.
|
||||||
|
*/
|
||||||
|
protected String iconName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new icon node.
|
||||||
|
*/
|
||||||
|
public IconNode() {
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new icon node.
|
||||||
|
*
|
||||||
|
* @param userObject The user object associated with the node.
|
||||||
|
*/
|
||||||
|
public IconNode(final Object userObject) {
|
||||||
|
this(userObject, true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new icon node.
|
||||||
|
*
|
||||||
|
* @param userObject The user object associated with the node.
|
||||||
|
* @param allowsChildren Flag that says whether the node can have children or not.
|
||||||
|
* @param icon The icon shown in the node.
|
||||||
|
*/
|
||||||
|
public IconNode(final Object userObject, final boolean allowsChildren, final Icon icon) {
|
||||||
|
super(userObject, allowsChildren);
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon of the node.
|
||||||
|
*
|
||||||
|
* @return The icon of the node.
|
||||||
|
*/
|
||||||
|
public Icon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the icon name of the node.
|
||||||
|
*
|
||||||
|
* @return The icon name of the node.
|
||||||
|
*/
|
||||||
|
public String getIconName() {
|
||||||
|
if (iconName != null) {
|
||||||
|
return iconName;
|
||||||
|
} else {
|
||||||
|
final String str = userObject.toString();
|
||||||
|
int index = str.lastIndexOf(".");
|
||||||
|
if (index != -1) {
|
||||||
|
return str.substring(++index);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package tv.porst.splib.gui.tree;
|
||||||
|
|
||||||
|
import java.awt.Component;
|
||||||
|
|
||||||
|
import javax.swing.JTree;
|
||||||
|
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||||
|
import javax.swing.tree.TreeModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renderer used to display icons in tree nodes.
|
||||||
|
*/
|
||||||
|
class CustomTreeCellRenderer extends DefaultTreeCellRenderer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean sel, final boolean expanded, final boolean leaf, final int row, final boolean hasFocus) {
|
||||||
|
if((value instanceof IconNode) && (value != null)) {
|
||||||
|
setIcon(((IconNode)value).getIcon());
|
||||||
|
}
|
||||||
|
|
||||||
|
//we can not call super.getTreeCellRendererComponent method, since it overrides our setIcon call and cause rendering of labels to '...' when node expansion is done
|
||||||
|
|
||||||
|
//so, we copy (and modify logic little bit) from super class method:
|
||||||
|
|
||||||
|
final String stringValue = tree.convertValueToText(value, sel,
|
||||||
|
expanded, leaf, row, hasFocus);
|
||||||
|
|
||||||
|
this.hasFocus = hasFocus;
|
||||||
|
setText(stringValue);
|
||||||
|
if(sel) {
|
||||||
|
setForeground(getTextSelectionColor());
|
||||||
|
} else {
|
||||||
|
setForeground(getTextNonSelectionColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tree.isEnabled()) {
|
||||||
|
setEnabled(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setEnabled(true);
|
||||||
|
}
|
||||||
|
setComponentOrientation(tree.getComponentOrientation());
|
||||||
|
selected = sel;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a tree where the nodes are custom icons.
|
||||||
|
*/
|
||||||
|
public class IconTree extends JTree {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new icon tree object.
|
||||||
|
*
|
||||||
|
* @param model The model of the icon tree.
|
||||||
|
*/
|
||||||
|
public IconTree(final TreeModel model) {
|
||||||
|
super(model);
|
||||||
|
|
||||||
|
setCellRenderer(new CustomTreeCellRenderer());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.gui.tree;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for working with trees.
|
||||||
|
*/
|
|
@ -0,0 +1,41 @@
|
||||||
|
package tv.porst.splib.maps;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains helper functions for working with maps.
|
||||||
|
*/
|
||||||
|
public final class MapHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts a map by its values.
|
||||||
|
*
|
||||||
|
* @param <S> Type of map keys.
|
||||||
|
* @param <T> Type of map values.
|
||||||
|
* @param map The map to sort.
|
||||||
|
*
|
||||||
|
* @return The sorted map.
|
||||||
|
*/
|
||||||
|
public static <S, T extends Comparable<T>> Map<S, T> sortByValue(final Map<S, T> map) {
|
||||||
|
|
||||||
|
final List<Map.Entry<S, T>> list = new LinkedList<Map.Entry<S, T>>(map.entrySet());
|
||||||
|
Collections.sort(list, new Comparator<Map.Entry<S, T>>() {
|
||||||
|
@Override
|
||||||
|
public int compare(final Map.Entry<S, T> o1, final Map.Entry<S, T> o2) {
|
||||||
|
return o1.getValue().compareTo(o2.getValue());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final Map<S, T> result = new LinkedHashMap<S, T>();
|
||||||
|
for (final Map.Entry<S, T> element : list) {
|
||||||
|
result.put(element.getKey(), element.getValue());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.maps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for working with maps.
|
||||||
|
*/
|
|
@ -0,0 +1,60 @@
|
||||||
|
package tv.porst.splib.strings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains helper functions for working with strings.
|
||||||
|
*/
|
||||||
|
public final class StringHelpers {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joins a bunch of strings into a single string.
|
||||||
|
*
|
||||||
|
* @param parts The string parts to join.
|
||||||
|
* @param delimiter The delimiter to add between strings.
|
||||||
|
*
|
||||||
|
* @return The joined string.
|
||||||
|
*/
|
||||||
|
public static String join(final String[] parts, final String delimiter) {
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (final String part : parts) {
|
||||||
|
if (part == parts[0] && (part == null || part.equals(""))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.length() != 0) {
|
||||||
|
builder.append(delimiter);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new string by repeating a given string a number of times.
|
||||||
|
*
|
||||||
|
* @param string The string to repeat.
|
||||||
|
* @param repeats Number of repeats.
|
||||||
|
*
|
||||||
|
* @return The new string.
|
||||||
|
*/
|
||||||
|
public static String repeat(final String string, final int repeats) {
|
||||||
|
|
||||||
|
if (repeats < 0) {
|
||||||
|
throw new IllegalArgumentException("Number of repeats can not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repeats == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < repeats; i++) {
|
||||||
|
builder.append(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package tv.porst.splib.strings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This package contains classes for working with strings.
|
||||||
|
*/
|
|
@ -0,0 +1,5 @@
|
||||||
|
subprojects {
|
||||||
|
tasks.withType(JavaCompile) {
|
||||||
|
options.encoding = 'UTF-8'
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,185 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
|
@ -0,0 +1,2 @@
|
||||||
|
rootProject.name = 'brainfuck-ide'
|
||||||
|
include ':bfIDE', ':BFInterpreter'
|
Loading…
Reference in New Issue