Initial version of the project for java studies from 2013.
commit
54c68d7375
bfIDE
src/main/java
tpsa
tv/porst
splib
convert
strings
gradle/wrapper
|
@ -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