first impl

master
Niclas Thobaben 2020-02-28 17:40:53 +01:00
parent cec70f1e1f
commit 0e2b9a4e7e
12 changed files with 700 additions and 0 deletions

View File

@ -0,0 +1,97 @@
package de.synolo.lib.fw;
import de.synolo.lib.fw.cmd.Command;
import de.synolo.lib.fw.cmd.UndoManager;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestMain extends Application {
static class TestCommand implements Command {
@Override
public void execute() {
System.out.println("Execute command!");
}
@Override
public boolean isUndoable() {
return true;
}
@Override
public boolean isCollapsable(Command other) {
// TODO Auto-generated method stub
return false;
}
@Override
public void collapse(Command other) {
// TODO Auto-generated method stub
}
@Override
public void undo() {
System.out.println("Undo command");
}
@Override
public void redo() {
System.out.println("Redo command");
}
@Override
public String getId() {
return "command.test";
}
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
UndoManager undoManager = new UndoManager();
BorderPane bp = new BorderPane();
Scene scene = new Scene(bp);
Button btn = new Button("Execute command");
bp.setCenter(btn);
Button btnUndo = new Button("Undo");
btnUndo.disableProperty().bind(undoManager.undoAvailableProperty().not());
btnUndo.setOnAction(e -> undoManager.undo());
Button btnRedo = new Button("Redo");
btnRedo.disableProperty().bind(undoManager.redoAvailableProperty().not());
btnRedo.setOnAction(e -> undoManager.redo());
btn.setOnAction(e -> undoManager.execute(new TestCommand()));
VBox vbox = new VBox();
vbox.getChildren().addAll(btnUndo, btnRedo);
bp.setBottom(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
}

View File

@ -0,0 +1,48 @@
package de.synolo.lib.fw.app;
import de.synolo.lib.fw.utils.Logging;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public abstract class AbstractApplication<C extends ApplicationContext> extends Application {
private C context;
private Stage primaryStage;
@Override
public void init() throws Exception {
super.init();
Logging.init();
}
@Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.context = initContext();
buildApplication(primaryStage);
}
protected abstract void buildApplication(Stage primaryStage) throws Exception;
public C getContext() { return this.context; }
protected abstract C initContext();
public Stage getPrimaryStage() { return this.primaryStage; }
public Stage showDialog(Modality modality, StageStyle style, Scene scene) {
Stage dialog = new Stage();
dialog.initOwner(this.primaryStage);
dialog.initModality(modality);
dialog.initStyle(style);
dialog.setScene(scene);
dialog.show();
return dialog;
}
}

View File

@ -0,0 +1,24 @@
package de.synolo.lib.fw.app;
import java.util.prefs.Preferences;
import de.synolo.lib.fw.cmd.UndoManager;
import de.synolo.lib.fw.utils.TypedProperties;
public abstract class ApplicationContext {
protected TypedProperties properties;
protected Preferences preferences;
protected UndoManager undoManager;
public ApplicationContext() {}
public TypedProperties getProperties() { return this.properties; }
public Preferences getPreferences() { return this.preferences; }
public UndoManager getUndoManager() { return this.undoManager; }
public abstract String getApplicationName();
public abstract String getApplicationVersion();
}

View File

@ -0,0 +1,17 @@
package de.synolo.lib.fw.cmd;
public interface Command {
public void execute();
public boolean isUndoable();
public boolean isCollapsable(Command other);
public void collapse(Command other);
public void undo();
public void redo();
public String getId();
}

View File

@ -0,0 +1,90 @@
package de.synolo.lib.fw.cmd;
import java.util.Stack;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
public class UndoManager {
private Stack<Command> undoStack;
private Stack<Command> redoStack;
private ReadOnlyBooleanWrapper undoAvailalableProperty = new ReadOnlyBooleanWrapper(false);
private ReadOnlyBooleanWrapper redoAvailalableProperty = new ReadOnlyBooleanWrapper(false);
private int max;
public UndoManager(int maxCommands) {
this.max = maxCommands;
this.undoStack = new Stack<>();
this.redoStack = new Stack<>();
}
public UndoManager() {
this(100);
}
public int getMaxCommands() { return this.max; }
public void setMaxCommands(int max) { this.max = max; }
public boolean isUndoAvailable() {
return !this.undoStack.isEmpty();
}
public ReadOnlyBooleanProperty undoAvailableProperty() { return this.undoAvailalableProperty.getReadOnlyProperty(); }
public boolean isRedoAvailable() {
return !this.redoStack.isEmpty();
}
public ReadOnlyBooleanProperty redoAvailableProperty() { return this.redoAvailalableProperty.getReadOnlyProperty(); }
public void execute(Command cmd) {
Command last = null;
if(!this.undoStack.isEmpty() && (last = this.undoStack.peek()) != null) {
if(last.isCollapsable(cmd)) {
last.collapse(cmd);
return;
}else {
this.redoStack.clear();
this.undoStack.push(cmd);
}
}else if(cmd.isUndoable()) {
this.redoStack.clear();
this.undoStack.push(cmd);
}
if(this.undoStack.size() > this.max) {
this.undoStack.remove(0);
}
cmd.execute();
update();
}
public void undo() {
System.out.println("undo...");
if(isUndoAvailable()) {
Command cmd = this.undoStack.pop();
this.redoStack.push(cmd);
cmd.undo();
}
update();
}
public void redo() {
if(isRedoAvailable()) {
Command cmd = this.redoStack.pop();
this.undoStack.push(cmd);
cmd.redo();
}
update();
}
private void update() {
this.undoAvailalableProperty.set(isUndoAvailable());
this.redoAvailalableProperty.set(isRedoAvailable());
System.out.println(this.undoStack);
}
}

View File

@ -0,0 +1,26 @@
package de.synolo.lib.fw.utils;
public enum LogLevel {
TRACE(16),
DEBUG(8),
INFO(4),
WARNING(2),
ERROR(0);
private int val;
private LogLevel(final int val) { this.val = val; }
public int val() { return this.val; }
public static LogLevel forVal(int val) {
LogLevel found = ERROR;
LogLevel[] levels = LogLevel.class.getEnumConstants();
for(LogLevel level : levels) {
if(val >= level.val() && found.val() < level.val()) {
found = level;
}
}
return found;
}
}

View File

@ -0,0 +1,187 @@
package de.synolo.lib.fw.utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class Logging {
public static final String DEFAULT_FORMAT = "[%date][%level] %msg\n";
public static final DateFormat DEFAULT_DATEFORMAT = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss.SSS");
public static Logging out;
public static void init() {
out = new Logging(null);
}
public static void init(String lvl) {
LogLevel level = lvl == null ? null : LogLevel.valueOf(lvl.toUpperCase());
out = new Logging(level);
}
protected String format;
protected LogLevel level;
protected PrintStream filestream, defaultstream;
protected File file = null;
protected DateFormat dateformat;
protected ArrayList<String> messages = new ArrayList<String>();
protected Thread loggingThread;
protected Logging(LogLevel level) {
this.format = DEFAULT_FORMAT;
this.dateformat = DEFAULT_DATEFORMAT;
this.level = level == null ? LogLevel.INFO : level;
this.defaultstream = System.out;
System.setErr(new PrintStream(new LoggingOutputStream(LogLevel.ERROR)));
System.setOut(new PrintStream(new LoggingOutputStream(LogLevel.INFO)));
log("Initialized Logging with LogLevel: %s", this.level.toString());
}
private synchronized void writeToFile(String write) {
if (filestream != null) {
filestream.println(write);
filestream.flush();
}
defaultstream.print(write);
}
public void log(LogLevel level, String format, Object...args) {
if(level.val() > this.level.val()) {
return;
}
String dateStr = this.dateformat.format(new Date());
String levelStr = String.format("%-8s", level.toString());
String msgStr = String.format(format, args);
String formatted = String.format("[%s][%s] %s \n", dateStr, levelStr, msgStr);
writeToFile(formatted);
}
public void log(String format, Object...args) {
log(LogLevel.INFO, format, args);
}
public void log(String msg) {
log(msg, "");
}
public void err(String format, Object...args) {
log(LogLevel.ERROR, format, args);
}
public void err(String msg) {
err(msg, "");
}
public void err(Object obj) {
err(obj.toString());
}
public void warn(String format, Object...args) {
log(LogLevel.WARNING, format, args);
}
public void warn(String msg) {
warn(msg, "");
}
public void debug(String format, Object...args) {
log(LogLevel.DEBUG, format, args);
}
public void debug(String msg) {
debug(msg, "");
}
public void detail(String format, Object...args) {
log(LogLevel.TRACE, format, args);
}
public void detail(String msg) {
detail(msg, "");
}
public String getFormat() {
return format;
}
public void setFormat(String format) {
this.format = format;
}
public LogLevel getLevel() {
return level;
}
public void setLevel(LogLevel level) {
this.level = level;
}
public void setLevel(int level) {
this.level = LogLevel.forVal(level);
}
public File getFile() {
return file;
}
public void setFile(File file){
try {
if(!file.exists()) {
file.createNewFile();
}
this.filestream = new PrintStream(new FileOutputStream(file, true));
}catch(IOException e) {
e.printStackTrace();
}
this.file = file;
}
public DateFormat getDateformat() {
return dateformat;
}
public void setDateformat(DateFormat dateformat) {
this.dateformat = dateformat;
}
protected class LoggingOutputStream extends OutputStream {
byte[] buffer;
int bufferPosition;
LogLevel logLevel;
public LoggingOutputStream() {
buffer = new byte[4096];
bufferPosition = 0;
this.logLevel = LogLevel.INFO;
}
public LoggingOutputStream(LogLevel logLevel) {
buffer = new byte[4096];
bufferPosition = 0;
this.logLevel = logLevel;
}
@Override
public void flush(){
if (bufferPosition > 0){
String str = new String(buffer,0,bufferPosition);
log(this.logLevel, str, "");
bufferPosition = 0;
};
}
@Override
public void write(int b) throws IOException {
if (b == 0x0A){
flush();
} else {
buffer[bufferPosition++] = (byte)b;
if (bufferPosition == buffer.length){
flush();
}
};
}
}
}

View File

@ -0,0 +1,107 @@
package de.synolo.lib.fw.utils;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.MultipleSelectionModel;
public class MultipleSelectionModelWrapper<T> extends MultipleSelectionModel<T>{
private ObservableList<T> items;
private ObservableList<Integer> indices = FXCollections.observableArrayList();
private ObservableList<T> selectedItems = FXCollections.observableArrayList();
public MultipleSelectionModelWrapper(ObservableList<T> list) {
this.items = list;
}
@Override
public ObservableList<Integer> getSelectedIndices() { return this.indices; }
@Override
public ObservableList<T> getSelectedItems() { return this.selectedItems; }
@Override
public void selectIndices(int index, int... indices) {
select(index);
for(int idx : indices)
select(idx);
}
@Override
public void selectAll() {
clearSelection();
for(int i = 0; i < this.items.size(); i++) {
this.selectedItems.add(this.items.get(i));
this.indices.add(i);
}
}
@Override
public void selectFirst() {
clearSelection();
select(0);
}
@Override
public void selectLast() {
clearSelection();
select(this.items.size()-1);
}
@Override
public void clearAndSelect(int index) {
clearSelection();
select(index);
}
@Override
public void select(int index) {
if(index < this.items.size() && index >= 0) {
T item = this.items.get(index);
if(!this.selectedItems.contains(item)) {
this.selectedItems.add(item);
this.indices.add(index);
}
}
}
@Override
public void select(T obj) {
if(this.items.contains(obj) && !this.selectedItems.contains(obj)) {
int index = this.items.indexOf(obj);
this.selectedItems.add(obj);
this.indices.add(index);
}
}
@Override
public void clearSelection(int index) {
if(this.indices.contains(index)) {
T item = this.items.get(index);
this.selectedItems.remove(item);
this.indices.remove(index);
}
}
@Override
public void clearSelection() {
this.indices.clear();
this.selectedItems.clear();
}
@Override
public boolean isSelected(int index) { return this.indices.contains(index); }
@Override
public boolean isEmpty() { return this.selectedItems.isEmpty(); }
@Override
public void selectPrevious() {
int index = this.indices.get(this.indices.size()-1);
select(index-1);
}
@Override
public void selectNext() {
int index = this.indices.get(this.indices.size()-1);
select(index+1);
}
}

View File

@ -0,0 +1,42 @@
package de.synolo.lib.fw.utils;
import java.util.Properties;
public class TypedProperties extends Properties {
private static final long serialVersionUID = 1L;
interface Converter<T> {
T convert(String input) throws Exception;
}
public <T> T getObject(String key, T defaultValue, Converter<T> converter) {
T val = defaultValue;
try {
val = converter.convert(getProperty(key));
}catch(Exception e) {
val = defaultValue;
}
return val;
}
public byte getByte(String key, byte defaultValue) { return getObject(key, defaultValue, Byte::parseByte); }
public byte getByte(String key) { return getByte(key, (byte)0); }
public short getShort(String key, short defaultValue) { return getObject(key, defaultValue, Short::parseShort); }
public short getShort(String key) { return getShort(key, (short)0); }
public int getInt(String key, int defaultValue) { return getObject(key, defaultValue, Integer::parseInt); }
public int getInt(String key) { return getInt(key, 0); }
public long getLong(String key, long defaultValue) { return getObject(key, defaultValue, Long::parseLong); }
public long getLong(String key) { return getLong(key, (byte)0); }
public float getFloat(String key, float defaultValue) { return getObject(key, defaultValue, Float::parseFloat); }
public float getFloat(String key) { return getFloat(key, (byte)0); }
public double getDouble(String key, double defaultValue) { return getObject(key, defaultValue, Double::parseDouble); }
public double getDouble(String key) { return getDouble(key, (byte)0); }
}

View File

@ -0,0 +1,9 @@
<archetype>
<id>lib-fw</id>
<sources>
<source>src/main/java/App.java</source>
</sources>
<testSources>
<source>src/test/java/AppTest.java</source>
</testSources>
</archetype>

View File

@ -0,0 +1,15 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>$de.synolo</groupId>
<artifactId>$lib-fw</artifactId>
<version>$0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,38 @@
package $de.synolo.lib.fw;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}