1
0
mirror of https://github.com/aNNiMON/HotaruFX.git synced 2024-09-19 14:14:21 +03:00

Update to run on Java 11 with OpenJFX

Removed lombok
This commit is contained in:
Victor 2019-10-29 17:33:02 +02:00
parent eb6bf422cc
commit 93e7a87c03
67 changed files with 656 additions and 426 deletions

View File

@ -1,26 +1,33 @@
plugins {
id 'com.github.johnrengelman.shadow' version '2.0.1'
id 'java'
id 'application'
id "com.github.johnrengelman.shadow" version "5.1.0"
id 'org.openjfx.javafxplugin' version '0.0.8'
}
group 'HotaruFX'
version '0.9.1'
version '1.0.0'
mainClassName = 'com.annimon.hotarufx.Main'
sourceCompatibility = 1.8
sourceCompatibility = 11
repositories {
mavenCentral()
}
javafx {
version = "11"
modules = ['javafx.controls', 'javafx.fxml', "javafx.swing"]
}
ext.junit5Version = '5.5.2'
dependencies {
implementation 'org.fxmisc.richtext:richtextfx:0.6.10'
compileOnly 'org.projectlombok:lombok:1.16.18'
testCompileOnly 'org.projectlombok:lombok:1.16.18'
testRuntime 'org.junit.platform:junit-platform-launcher:1.0.0'
testRuntime 'org.junit.jupiter:junit-jupiter-engine:5.0.0'
testRuntime 'org.junit.vintage:junit-vintage-engine:4.12.0'
testRuntime 'org.junit.jupiter:junit-jupiter-params:5.0.0'
implementation 'org.fxmisc.richtext:richtextfx:0.10.2'
testRuntime 'org.junit.platform:junit-platform-launcher:1.5.2'
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit5Version"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit5Version"
testRuntime "org.junit.jupiter:junit-jupiter-engine:$junit5Version"
testRuntime "org.junit.vintage:junit-vintage-engine:$junit5Version"
testImplementation 'org.hamcrest:hamcrest-library:1.3'
}

View File

@ -9,7 +9,6 @@ import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
import lombok.val;
public class Main extends Application {
@ -20,8 +19,8 @@ public class Main extends Application {
ClickableHyperLink.setHostServices(getHostServices());
primaryStage.setTitle("HotaruFX");
try {
val loader = new FXMLLoader(getClass().getResource("/fxml/Editor.fxml"));
val scene = new Scene(loader.load());
final var loader = new FXMLLoader(getClass().getResource("/fxml/Editor.fxml"));
final var scene = new Scene(loader.load());
scene.getStylesheets().addAll(
getClass().getResource("/styles/theme-dark.css").toExternalForm(),
getClass().getResource("/styles/codearea.css").toExternalForm(),
@ -33,7 +32,7 @@ public class Main extends Application {
primaryStage.setOnCloseRequest(controller::onCloseRequest);
primaryStage.setScene(scene);
} catch (IOException ex) {
val text = new TextArea(Exceptions.stackTraceToString(ex));
final var text = new TextArea(Exceptions.stackTraceToString(ex));
text.setEditable(false);
primaryStage.setScene(new Scene(text));
}

View File

@ -1,13 +1,13 @@
package com.annimon.hotarufx.bundles;
import com.annimon.hotarufx.lib.Context;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import lombok.val;
public final class BundleLoader {
@ -26,7 +26,7 @@ public final class BundleLoader {
}
public static Map<String, FunctionType> functions() {
val functions = new HashMap<String, FunctionType>();
final var functions = new HashMap<String, FunctionType>();
apply(runtimeBundles(), functions, ((bundle, map) -> map.putAll(bundle.functions())));
return functions;
}
@ -43,9 +43,11 @@ public final class BundleLoader {
for (Class<? extends Bundle> clazz : bundles) {
try {
val bundle = clazz.newInstance();
final var ctor = clazz.getDeclaredConstructor();
final var bundle = ctor.newInstance();
action.accept(bundle, obj);
} catch (IllegalAccessException | InstantiationException ignore) {}
} catch (IllegalAccessException | InstantiationException
| NoSuchMethodException | InvocationTargetException ignore) {}
}
}
}

View File

@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javafx.scene.paint.Paint;
import lombok.val;
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
@ -57,11 +56,11 @@ public class CompositionBundle implements Bundle {
width = args[0].asInt();
height = args[1].asInt();
frameRate = args[2].asDouble();
val background = PropertyType.PAINT.<Paint>getFromHFX().apply(args[3]);
final var background = PropertyType.PAINT.<Paint>getFromHFX().apply(args[3]);
composition = new Composition(width, height, frameRate, background);
break;
}
val scene = composition.getScene();
final var scene = composition.getScene();
context.composition(composition);
context.variables().put("Width", NumberValue.of(scene.getVirtualWidth()));
context.variables().put("Height", NumberValue.of(scene.getVirtualHeight()));
@ -73,8 +72,8 @@ public class CompositionBundle implements Bundle {
private static Function render(Context context) {
return args -> {
val renderVisitor = new RenderVisitor(context.composition().getTimeline());
val scene = context.composition().getScene();
final var renderVisitor = new RenderVisitor(context.composition().getTimeline());
final var scene = context.composition().getScene();
Arrays.stream(args)
.filter(v -> v.type() == Types.NODE)
.map(v -> ((NodeValue) v).getNode())

View File

@ -9,7 +9,6 @@ import com.annimon.hotarufx.lib.Validator;
import java.util.HashMap;
import java.util.Map;
import javafx.scene.text.Font;
import lombok.val;
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
@ -28,7 +27,7 @@ public class FontBundle implements Bundle {
private static Function newFont(Context context) {
return args -> {
val validator = Validator.with(args);
final var validator = Validator.with(args);
validator.check(1);
if (args[0].type() == Types.MAP) {
return new FontValue(FontValue.toFont((MapValue) args[0]));

View File

@ -2,11 +2,7 @@ package com.annimon.hotarufx.bundles;
import com.annimon.hotarufx.lib.Context;
import com.annimon.hotarufx.lib.Function;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class FunctionInfo {
public static FunctionInfo of(FunctionType type, Function function) {
@ -17,10 +13,18 @@ public class FunctionInfo {
return new FunctionInfo(type, extractor);
}
@Getter
private final FunctionType type;
private final java.util.function.Function<Context, Function> functionExtractor;
private FunctionInfo(FunctionType type, java.util.function.Function<Context, Function> functionExtractor) {
this.type = type;
this.functionExtractor = functionExtractor;
}
public FunctionType getType() {
return type;
}
public Function extract(Context context) {
return functionExtractor.apply(context);
}

View File

@ -13,7 +13,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javafx.scene.shape.Shape;
import lombok.val;
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
@ -32,15 +31,15 @@ public class NodeUtilsBundle implements Bundle {
private static Function strokePattern(Context context) {
return args -> {
val validator = Validator.with(args);
final var validator = Validator.with(args);
validator.checkOrOr(1, 2);
if (args[0].type() != Types.NODE || !(args[0].raw() instanceof ShapeNode)) {
throw new TypeException("Shape required at first argument");
}
val shape = (Shape) ((ShapeNode) args[0].raw()).getFxNode();
final var shape = (Shape) ((ShapeNode) args[0].raw()).getFxNode();
if (args.length == 2) {
val array = validator.requireArrayAt(1);
val dashList = array.stream()
final var array = validator.requireArrayAt(1);
final var dashList = array.stream()
.map(Value::asDouble)
.collect(Collectors.toList());
shape.getStrokeDashArray().setAll(dashList);

View File

@ -12,7 +12,6 @@ import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import lombok.val;
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
import static com.annimon.hotarufx.bundles.FunctionType.NODE;
@ -41,8 +40,8 @@ public class NodesBundle implements Bundle {
private static Function node(Supplier<? extends ObjectNode> supplier) {
return args -> {
val map = Validator.with(args).requireMapAt(0);
val node = new NodeValue(supplier.get());
final var map = Validator.with(args).requireMapAt(0);
final var node = new NodeValue(supplier.get());
node.fill(map);
return node;
};
@ -50,12 +49,12 @@ public class NodesBundle implements Bundle {
private static Function poly(java.util.function.Function<List<Double>, ObjectNode> ctor) {
return args -> {
val validator = Validator.with(args);
val map = validator.requireMapAt(1);
val points = validator.requireArrayAt(0).stream()
final var validator = Validator.with(args);
final var map = validator.requireMapAt(1);
final var points = validator.requireArrayAt(0).stream()
.map(Value::asDouble)
.collect(Collectors.toList());
val node = new NodeValue(ctor.apply(points));
final var node = new NodeValue(ctor.apply(points));
node.fill(map);
return node;
};
@ -63,10 +62,10 @@ public class NodesBundle implements Bundle {
private static Function image() {
return args -> {
val validator = Validator.with(args);
val map = validator.requireMapAt(1);
val url = args[0].asString();
val node = new NodeValue(new ImageNode(url));
final var validator = Validator.with(args);
final var map = validator.requireMapAt(1);
final var url = args[0].asString();
final var node = new NodeValue(new ImageNode(url));
node.fill(map);
return node;
};
@ -74,7 +73,7 @@ public class NodesBundle implements Bundle {
private static Function group() {
return args -> {
val nodes = Arrays.stream(args)
final var nodes = Arrays.stream(args)
.filter(v -> v.type() == Types.NODE)
.map(v -> ((NodeValue) v).getNode())
.collect(Collectors.toList());

View File

@ -2,7 +2,6 @@ package com.annimon.hotarufx.exceptions;
import java.io.PrintWriter;
import java.io.StringWriter;
import lombok.val;
public final class Exceptions {
@ -11,7 +10,7 @@ public final class Exceptions {
}
public static String stackTraceToString(Throwable throwable) {
val sw = new StringWriter();
final var sw = new StringWriter();
throwable.printStackTrace(new PrintWriter(sw));
return sw.toString();
}

View File

@ -13,7 +13,6 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import lombok.val;
public class FileManager implements DocumentManager {
@ -55,7 +54,7 @@ public class FileManager implements DocumentManager {
return false;
}
val content = readFile(currentFile);
final var content = readFile(currentFile);
if (content.isEmpty()) {
return false;
}
@ -80,7 +79,7 @@ public class FileManager implements DocumentManager {
} else {
fileChooser.setInitialFileName("animation.hfx");
}
val newFile = fileChooser.showSaveDialog(stage);
final var newFile = fileChooser.showSaveDialog(stage);
if (newFile == null) {
return false;
}

View File

@ -3,18 +3,18 @@ package com.annimon.hotarufx.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import lombok.val;
import java.nio.charset.StandardCharsets;
public class IOStream {
public static String readContent(InputStream is) throws IOException {
val baos = new ByteArrayOutputStream();
val bufferSize = 4096;
val buffer = new byte[bufferSize];
final var baos = new ByteArrayOutputStream();
final var bufferSize = 4096;
final var buffer = new byte[bufferSize];
int read;
while ((read = is.read(buffer, 0, bufferSize)) != -1) {
baos.write(buffer, 0, read);
}
return baos.toString("UTF-8");
return baos.toString(StandardCharsets.UTF_8);
}
}

View File

@ -3,12 +3,11 @@ package com.annimon.hotarufx.lexer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.val;
public class HotaruLexer extends Lexer {
public static List<Token> tokenize(String input) {
val lexer = new HotaruLexer(input);
final var lexer = new HotaruLexer(input);
lexer.tokenize();
return lexer.getTokens();
}
@ -48,7 +47,7 @@ public class HotaruLexer extends Lexer {
}
public Token nextToken() {
val current = peek(0);
final var current = peek(0);
if (Character.isDigit(current)) return tokenizeNumber();
else if (Character.isJavaIdentifierStart(current)) return tokenizeWord();
else if (current == '#') return tokenizeComment();
@ -99,7 +98,7 @@ public class HotaruLexer extends Lexer {
current = next();
}
val word = getBuffer().toString();
final var word = getBuffer().toString();
return addToken(KEYWORDS.getOrDefault(word, HotaruTokenId.WORD));
}
@ -110,7 +109,7 @@ public class HotaruLexer extends Lexer {
char current = peek(0);
while (true) {
if (current == '\\') {
val buffer = getBuffer();
final var buffer = getBuffer();
current = next();
if (current == openChar) {
current = next();
@ -166,7 +165,7 @@ public class HotaruLexer extends Lexer {
char current = peek(0);
clearBuffer();
while (true) {
val text = getBuffer().toString();
final var text = getBuffer().toString();
if (!text.isEmpty() && !OPERATORS.containsKey(text + current)) {
return addToken(OPERATORS.get(text), "", text.length());
}

View File

@ -1,9 +1,5 @@
package com.annimon.hotarufx.lexer;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
@AllArgsConstructor(access = AccessLevel.PACKAGE)
public enum HotaruTokenId {
NUMBER(Category.NUMBER),
@ -41,6 +37,10 @@ public enum HotaruTokenId {
private final Category category;
HotaruTokenId(Category category) {
this.category = category;
}
public String getPrimaryCategory() {
return category.name().toLowerCase();
}

View File

@ -3,8 +3,8 @@ package com.annimon.hotarufx.lexer;
import com.annimon.hotarufx.exceptions.LexerException;
import java.util.ArrayList;
import java.util.List;
import lombok.val;
@SuppressWarnings("WeakerAccess")
public abstract class Lexer {
private final String input;
@ -88,7 +88,7 @@ public abstract class Lexer {
}
protected Token addToken(HotaruTokenId tokenId, String text, int length) {
val token = createToken(tokenId, text, length);
final var token = createToken(tokenId, text, length);
tokens.add(token);
return token;
}

View File

@ -1,14 +1,46 @@
package com.annimon.hotarufx.lexer;
import lombok.Data;
import java.util.Objects;
@Data
public class SourcePosition {
private final int position;
private final int row;
private final int column;
public SourcePosition(int position, int row, int column) {
this.position = position;
this.row = row;
this.column = column;
}
public int getPosition() {
return position;
}
public int getRow() {
return row;
}
public int getColumn() {
return column;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SourcePosition that = (SourcePosition) o;
return position == that.position &&
row == that.row &&
column == that.column;
}
@Override
public int hashCode() {
return Objects.hash(position, row, column);
}
@Override
public String toString() {
return "[" + row + ", " + column + "]";

View File

@ -1,8 +1,7 @@
package com.annimon.hotarufx.lexer;
import lombok.Data;
import java.util.Objects;
@Data
public class Token {
private final HotaruTokenId type;
@ -10,6 +9,45 @@ public class Token {
private final int length;
private final SourcePosition position;
public Token(HotaruTokenId type, String text, int length, SourcePosition position) {
this.type = type;
this.text = text;
this.length = length;
this.position = position;
}
public HotaruTokenId getType() {
return type;
}
public String getText() {
return text;
}
public int getLength() {
return length;
}
public SourcePosition getPosition() {
return position;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Token token = (Token) o;
return length == token.length &&
type == token.type &&
Objects.equals(text, token.text) &&
Objects.equals(position, token.position);
}
@Override
public int hashCode() {
return Objects.hash(type, text, length, position);
}
@Override
public String toString() {
return type.name() + " " + position + " " + text;

View File

@ -6,7 +6,6 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.function.Function;
import java.util.stream.Stream;
import lombok.Getter;
public class ArrayValue implements Value, Iterable<Value> {
@ -17,7 +16,6 @@ public class ArrayValue implements Value, Iterable<Value> {
.toArray(Value[]::new));
}
@Getter
private final Value[] elements;
public ArrayValue(int size) {
@ -33,6 +31,10 @@ public class ArrayValue implements Value, Iterable<Value> {
this(array.elements);
}
public Value[] getElements() {
return elements;
}
public Value[] getCopyElements() {
final Value[] result = new Value[elements.length];
System.arraycopy(elements, 0, result, 0, elements.length);

View File

@ -4,17 +4,16 @@ import com.annimon.hotarufx.exceptions.TypeException;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import lombok.val;
public class FontValue extends MapValue {
public static Font toFont(MapValue mapValue) {
val map = mapValue.getMap();
val family = map.getOrDefault("family", new StringValue(Font.getDefault().getFamily())).asString();
val weight = map.getOrDefault("weight", NumberValue.of(FontWeight.NORMAL.getWeight())).asInt();
val isItalic = map.getOrDefault("italic", NumberValue.ZERO).asBoolean();
val posture = isItalic ? FontPosture.ITALIC : FontPosture.REGULAR;
val size = map.getOrDefault("size", NumberValue.MINUS_ONE).asDouble();
final var map = mapValue.getMap();
final var family = map.getOrDefault("family", new StringValue(Font.getDefault().getFamily())).asString();
final var weight = map.getOrDefault("weight", NumberValue.of(FontWeight.NORMAL.getWeight())).asInt();
final var isItalic = map.getOrDefault("italic", NumberValue.ZERO).asBoolean();
final var posture = isItalic ? FontPosture.ITALIC : FontPosture.REGULAR;
final var size = map.getOrDefault("size", NumberValue.MINUS_ONE).asDouble();
return Font.font(family, FontWeight.findByWeight(weight), posture, size);
}
@ -27,10 +26,10 @@ public class FontValue extends MapValue {
}
private void init() {
val map = super.getMap();
final var map = super.getMap();
map.put("family", new StringValue(font.getFamily()));
map.put("isItalic", NumberValue.fromBoolean(font.getStyle().toLowerCase().contains("italic")));
val weight = FontWeight.findByName(font.getStyle());
final var weight = FontWeight.findByName(font.getStyle());
map.put("weight", NumberValue.of(weight != null
? (weight.getWeight())
: FontWeight.NORMAL.getWeight()));

View File

@ -2,19 +2,21 @@ package com.annimon.hotarufx.lib;
import com.annimon.hotarufx.exceptions.TypeException;
import java.util.Objects;
import lombok.Getter;
public class FunctionValue implements Value {
public static final FunctionValue EMPTY = new FunctionValue(args -> NumberValue.ZERO);
@Getter
private final Function value;
public FunctionValue(Function value) {
this.value = value;
}
public Function getValue() {
return value;
}
@Override
public int type() {
return Types.FUNCTION;

View File

@ -2,17 +2,19 @@ package com.annimon.hotarufx.lib;
import com.annimon.hotarufx.exceptions.TypeException;
import javafx.animation.Interpolator;
import lombok.Getter;
public class InterpolatorValue implements Value {
@Getter
private final Interpolator interpolator;
public InterpolatorValue(Interpolator interpolator) {
this.interpolator = interpolator;
}
public Interpolator getInterpolator() {
return interpolator;
}
@Override
public int type() {
return Types.INTERPOLATOR;

View File

@ -9,7 +9,6 @@ import com.annimon.hotarufx.visual.objects.ObjectNode;
import javafx.scene.Node;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import lombok.val;
public class NodeValue implements Value {
@ -45,8 +44,8 @@ public class NodeValue implements Value {
throw new HotaruRuntimeException("Unable to get property " + key + " from node value");
}
final Property property = bindings.get(key);
val timeline = property.getProperty().get();
val type = property.getType();
final var timeline = property.getProperty().get();
final var type = property.getType();
switch (type) {
case BOOLEAN:
return type.<Boolean>getToHFX().apply(
@ -76,8 +75,8 @@ public class NodeValue implements Value {
public void set(String key, Value value) {
if (!bindings.containsKey(key)) return;
final Property property = bindings.get(key);
val timeline = property.getProperty().get();
val type = property.getType();
final var timeline = property.getProperty().get();
final var type = property.getType();
switch (type) {
case BOOLEAN:
((PropertyTimeline<Boolean>) timeline).getProperty().setValue(
@ -108,7 +107,7 @@ public class NodeValue implements Value {
}
public Value getProperty(String key) {
val property = bindings.get(key);
final var property = bindings.get(key);
if (property == null) {
throw new HotaruRuntimeException("Unable to get property " + key + " from node value");
}

View File

@ -11,12 +11,9 @@ import javafx.animation.Interpolator;
import javafx.scene.Node;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import lombok.Getter;
import lombok.val;
public class PropertyValue implements Value {
@Getter
private final Property property;
private final Map<String, Value> fields;
@ -27,13 +24,17 @@ public class PropertyValue implements Value {
fields.put("clear", new FunctionValue(clear()));
}
public Property getProperty() {
return property;
}
@Override
public int type() {
return Types.PROPERTY;
}
public Value getField(String name) {
val field = fields.get(name);
final var field = fields.get(name);
if (field == null) {
throw new HotaruRuntimeException("PropertyValue does not have " + name + " field");
}
@ -53,7 +54,7 @@ public class PropertyValue implements Value {
}
interpolator = ((InterpolatorValue) args[2]).getInterpolator();
}
val type = property.getType();
final var type = property.getType();
switch (type) {
case BOOLEAN:
((PropertyTimeline<Boolean>)property.getProperty().get()).add(

View File

@ -2,14 +2,19 @@ package com.annimon.hotarufx.lib;
import com.annimon.hotarufx.exceptions.ArgumentsMismatchException;
import com.annimon.hotarufx.exceptions.TypeException;
import lombok.RequiredArgsConstructor;
import lombok.val;
@RequiredArgsConstructor(staticName = "with")
public class Validator {
public static Validator with(Value[] args) {
return new Validator(args);
}
private final Value[] args;
private Validator(Value[] args) {
this.args = args;
}
public Validator check(int expected) {
if (args.length != expected) throw new ArgumentsMismatchException(String.format(
"%d %s expected, got %d", expected, pluralize(expected), args.length));
@ -38,7 +43,7 @@ public class Validator {
public ArrayValue requireArrayAt(int index) {
checkAtLeast(index + 1);
val value = args[index];
final var value = args[index];
if (value.type() != Types.ARRAY) {
throw new TypeException(String.format("Array required at %d argument", index));
}
@ -47,7 +52,7 @@ public class Validator {
public MapValue requireMapAt(int index) {
checkAtLeast(index + 1);
val value = args[index];
final var value = args[index];
if (value.type() != Types.MAP) {
throw new TypeException(String.format("Map required at %d argument", index));
}

View File

@ -9,13 +9,12 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.val;
public class HotaruParser extends Parser {
public static Node parse(List<Token> tokens) {
val parser = new HotaruParser(tokens);
val program = parser.parse();
final var parser = new HotaruParser(tokens);
final var program = parser.parse();
if (parser.getParseErrors().hasErrors()) {
throw new ParseException(parser.getParseErrors().toString());
}
@ -27,7 +26,7 @@ public class HotaruParser extends Parser {
}
private Node block() {
val block = new BlockNode();
final var block = new BlockNode();
block.start(getSourcePosition());
consume(HotaruTokenId.LBRACE);
while (!match(HotaruTokenId.RBRACE)) {
@ -55,7 +54,7 @@ public class HotaruParser extends Parser {
private Node functionChain(Node qualifiedNameExpr) {
// f1()()() || f1().f2().f3() || f1().key
val expr = function(qualifiedNameExpr);
final var expr = function(qualifiedNameExpr);
if (lookMatch(0, HotaruTokenId.LPAREN)) {
return functionChain(expr);
}
@ -64,7 +63,7 @@ public class HotaruParser extends Parser {
private Node objectAccess(Node expr) {
if (lookMatch(0, HotaruTokenId.DOT)) {
val indices = variableSuffix();
final var indices = variableSuffix();
if (indices == null || indices.isEmpty())
return expr;
@ -81,7 +80,7 @@ public class HotaruParser extends Parser {
private FunctionNode function(Node qualifiedNameExpr) {
// function(arg1, arg2, ...)
consume(HotaruTokenId.LPAREN);
val function = new FunctionNode(qualifiedNameExpr);
final var function = new FunctionNode(qualifiedNameExpr);
while (!match(HotaruTokenId.RPAREN)) {
function.addArgument(expression());
match(HotaruTokenId.COMMA);
@ -92,7 +91,7 @@ public class HotaruParser extends Parser {
private Node array() {
// [value1, value2, ...]
consume(HotaruTokenId.LBRACKET);
val elements = new ArrayList<Node>();
final var elements = new ArrayList<Node>();
while (!match(HotaruTokenId.RBRACKET)) {
elements.add(expression());
match(HotaruTokenId.COMMA);
@ -105,9 +104,9 @@ public class HotaruParser extends Parser {
consume(HotaruTokenId.LBRACE);
final Map<String, Node> elements = new HashMap<>();
while (!match(HotaruTokenId.RBRACE)) {
val key = consume(HotaruTokenId.WORD).getText();
final var key = consume(HotaruTokenId.WORD).getText();
consume(HotaruTokenId.COLON);
val value = expression();
final var value = expression();
elements.put(key, value);
match(HotaruTokenId.COMMA);
}
@ -120,7 +119,7 @@ public class HotaruParser extends Parser {
}
private Node assignment() {
val assignment = assignmentStrict();
final var assignment = assignmentStrict();
if (assignment != null) {
return assignment;
}
@ -129,8 +128,8 @@ public class HotaruParser extends Parser {
private Node assignmentStrict() {
final int position = pos;
val startSourcePosition = getSourcePosition();
val targetExpr = qualifiedName();
final var startSourcePosition = getSourcePosition();
final var targetExpr = qualifiedName();
if ((targetExpr == null) || !(targetExpr instanceof Accessible)) {
pos = position;
return null;
@ -157,7 +156,7 @@ public class HotaruParser extends Parser {
private Node primary() {
if (match(HotaruTokenId.LPAREN)) {
val result = expression();
final var result = expression();
match(HotaruTokenId.RPAREN);
return result;
}
@ -189,8 +188,8 @@ public class HotaruParser extends Parser {
}
// node@prop || map.node@prop
if (match(HotaruTokenId.AT)) {
val propName = consume(HotaruTokenId.WORD).getText();
val expr = new PropertyNode(qualifiedNameExpr, propName);
final var propName = consume(HotaruTokenId.WORD).getText();
final var expr = new PropertyNode(qualifiedNameExpr, propName);
return objectAccess(expr);
}
return qualifiedNameExpr;
@ -221,8 +220,8 @@ public class HotaruParser extends Parser {
final List<Node> indices = new ArrayList<>();
while (lookMatch(0, HotaruTokenId.DOT)) {
if (match(HotaruTokenId.DOT)) {
val fieldName = consume(HotaruTokenId.WORD).getText();
val key = new ValueNode(fieldName);
final var fieldName = consume(HotaruTokenId.WORD).getText();
final var key = new ValueNode(fieldName);
indices.add(key);
}
}
@ -230,7 +229,7 @@ public class HotaruParser extends Parser {
}
private Node value() {
val current = get(0);
final var current = get(0);
if (match(HotaruTokenId.TRUE)) {
return new ValueNode(NumberValue.ONE);
}

View File

@ -7,7 +7,6 @@ import com.annimon.hotarufx.lexer.Token;
import com.annimon.hotarufx.parser.ast.BlockNode;
import com.annimon.hotarufx.parser.ast.Node;
import java.util.List;
import lombok.val;
public abstract class Parser {
@ -38,7 +37,7 @@ public abstract class Parser {
public Node parse() {
parseErrors.clear();
val result = new BlockNode();
final var result = new BlockNode();
result.start(getSourcePosition());
while (!match(HotaruTokenId.EOF)) {
try {
@ -77,7 +76,7 @@ public abstract class Parser {
}
protected Token consume(HotaruTokenId type) {
val current = get(0);
final var current = get(0);
if (type != current.getType()) {
throw new ParseException("Token " + current + " doesn't match " + type, current.getPosition());
}
@ -86,7 +85,7 @@ public abstract class Parser {
}
protected boolean match(HotaruTokenId type) {
val current = get(0);
final var current = get(0);
if (type != current.getType()) return false;
pos++;
return true;
@ -97,7 +96,7 @@ public abstract class Parser {
}
protected Token get(int relativePosition) {
val position = pos + relativePosition;
final var position = pos + relativePosition;
if (position >= size) return EOF;
return tokens.get(position);
}

View File

@ -1,18 +1,30 @@
package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.lexer.SourcePosition;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Accessors(fluent = true)
public abstract class ASTNode implements Node {
@Getter @Setter
private SourcePosition start;
@Getter @Setter
private SourcePosition end;
public SourcePosition start() {
return start;
}
public SourcePosition end() {
return end;
}
public ASTNode start(SourcePosition start) {
this.start = start;
return this;
}
public ASTNode end(SourcePosition end) {
this.end = end;
return this;
}
public String getSourceRange() {
return start + " .. " + end;
}

View File

@ -3,20 +3,25 @@ package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.lib.Value;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class AccessNode extends ASTNode implements Accessible {
@Getter
public final Node root;
public final List<Node> indices;
public AccessNode(Node root, List<Node> indices) {
this.root = root;
this.indices = indices;
}
public AccessNode(String variable, List<Node> indices) {
this(new VariableNode(variable), indices);
}
public Node getRoot() {
return root;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -2,13 +2,15 @@ package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import java.util.List;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class ArrayNode extends ASTNode {
public final List<Node> elements;
public ArrayNode(List<Node> elements) {
this.elements = elements;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -1,14 +1,17 @@
package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class AssignNode extends ASTNode {
public final Accessible target;
public final Node value;
public AssignNode(Accessible target, Node value) {
this.target = target;
this.value = value;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -2,13 +2,15 @@ package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import java.util.Map;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class MapNode extends ASTNode {
public final Map<String, Node> elements;
public MapNode(Map<String, Node> elements) {
this.elements = elements;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -1,14 +1,17 @@
package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class PropertyNode extends ASTNode {
public final Node node;
public final String property;
public PropertyNode(Node node, String property) {
this.node = node;
this.property = property;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -1,9 +1,7 @@
package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class UnaryNode extends ASTNode {
public enum Operator { NEGATE };
@ -11,6 +9,11 @@ public class UnaryNode extends ASTNode {
public final Operator operator;
public final Node node;
public UnaryNode(Operator operator, Node node) {
this.operator = operator;
this.node = node;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -1,9 +1,7 @@
package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class UnitNode extends ASTNode {
public enum Unit {MILLISECONDS, SECONDS};
@ -11,6 +9,11 @@ public class UnitNode extends ASTNode {
public final Node value;
public final Unit operator;
public UnitNode(Node value, Unit operator) {
this.value = value;
this.operator = operator;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -4,13 +4,15 @@ import com.annimon.hotarufx.lib.NumberValue;
import com.annimon.hotarufx.lib.StringValue;
import com.annimon.hotarufx.lib.Value;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class ValueNode extends ASTNode {
public final Value value;
public ValueNode(Value value) {
this.value = value;
}
public ValueNode(Number value) {
this(NumberValue.of(value));
}

View File

@ -2,13 +2,15 @@ package com.annimon.hotarufx.parser.ast;
import com.annimon.hotarufx.lib.Value;
import com.annimon.hotarufx.parser.visitors.ResultVisitor;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class VariableNode extends ASTNode implements Accessible {
public final String name;
public VariableNode(String name) {
this.name = name;
}
@Override
public <R, T> R accept(ResultVisitor<R, T> visitor, T input) {
return visitor.visit(this, input);

View File

@ -10,7 +10,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import lombok.val;
public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@ -21,7 +20,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override
public Value visit(ArrayNode node, Context context) {
val elements = node.elements.stream()
final var elements = node.elements.stream()
.map(el -> el.accept(this, context))
.toArray(Value[]::new);
return new ArrayValue(elements);
@ -29,7 +28,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override
public Value visit(AssignNode node, Context context) {
val value = node.value.accept(this, context);
final var value = node.value.accept(this, context);
return node.target.set(this, value, context);
}
@ -44,20 +43,20 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override
public Value visit(FunctionNode node, Context context) {
val value = node.functionNode.accept(this, context);
final var value = node.functionNode.accept(this, context);
final Function function;
switch (value.type()) {
case Types.FUNCTION:
function = ((FunctionValue) value).getValue();
break;
default:
val functionName = value.asString();
final var functionName = value.asString();
function = context.functions().get(functionName);
if (function == null)
throw new FunctionNotFoundException(functionName, node.start(), node.end());
break;
}
val args = node.arguments.stream()
final var args = node.arguments.stream()
.map(n -> n.accept(this, context))
.toArray(Value[]::new);
return function.execute(args);
@ -74,11 +73,11 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override
public Value visit(PropertyNode node, Context context) {
val value = node.node.accept(this, context);
final var value = node.node.accept(this, context);
if (value.type() != Types.NODE) {
throw new TypeException("Node value expected");
}
val nodeValue = (NodeValue) value;
final var nodeValue = (NodeValue) value;
return nodeValue.getProperty(node.property);
}
@ -86,7 +85,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
public Value visit(UnaryNode node, Context context) {
switch (node.operator) {
case NEGATE:
val value = node.node.accept(this, context);
final var value = node.node.accept(this, context);
if (value.type() == Types.STRING) {
final StringBuilder sb = new StringBuilder(value.asString());
return new StringValue(sb.reverse().toString());
@ -112,8 +111,8 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override
public Value visit(UnitNode node, Context context) {
val value = node.value.accept(this, context);
val frameRate = context.composition().getTimeline().getFrameRate();
final var value = node.value.accept(this, context);
final var frameRate = context.composition().getTimeline().getFrameRate();
final double frame;
switch (node.operator) {
case SECONDS:
@ -155,12 +154,12 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
.orElseThrow(exceptionSupplier);
switch (container.type()) {
case Types.MAP: {
val key = node.indices.get(lastIndex).accept(this, context).asString();
final var key = node.indices.get(lastIndex).accept(this, context).asString();
((MapValue) container).getMap().put(key, value);
} break;
case Types.NODE: {
val key = node.indices.get(lastIndex).accept(this, context).asString();
final var key = node.indices.get(lastIndex).accept(this, context).asString();
((NodeValue) container).set(key, value);
} break;
@ -174,18 +173,18 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
for (Node index : nodes) {
switch (container.type()) {
case Types.MAP: {
val key = index.accept(this, context).asString();
final var key = index.accept(this, context).asString();
container = ((MapValue) container).getMap().get(key);
} break;
case Types.NODE: {
val key = index.accept(this, context).asString();
final var key = index.accept(this, context).asString();
container = ((NodeValue) container).get(key);
} break;
case Types.PROPERTY: {
val key = index.accept(this, context).asString();
val propertyValue = (PropertyValue) container;
final var key = index.accept(this, context).asString();
final var propertyValue = (PropertyValue) container;
container = propertyValue.getField(key);
} break;
@ -198,7 +197,7 @@ public class InterpreterVisitor implements ResultVisitor<Value, Context> {
@Override
public Value get(VariableNode node, Context context) {
val result = context.variables().get(node.name);
final var result = context.variables().get(node.name);
if (result == null)
throw new VariableNotFoundException(node.name, node.start(), node.end());
return result;

View File

@ -8,7 +8,6 @@ import java.util.Map;
import java.util.stream.Collectors;
import javafx.scene.text.Font;
import javafx.util.Pair;
import lombok.Getter;
public enum FontAwesome {
@ -28,7 +27,6 @@ public enum FontAwesome {
SEARCH_MINUS("\uf010", "search-minus"),
UNDO("\uf0e2", "undo");
@Getter
private final String symbol;
private final List<String> names;
@ -41,6 +39,10 @@ public enum FontAwesome {
}
}
public String getSymbol() {
return symbol;
}
public static String getIcon(String name) {
return MAPPING.get(name);
}

View File

@ -14,7 +14,6 @@ import java.util.function.Function;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
import lombok.val;
public class RenderPreparer {
@ -71,8 +70,8 @@ public class RenderPreparer {
}
private void evaluate() {
val parser = new HotaruParser(HotaruLexer.tokenize(source.input));
val program = parser.parse();
final var parser = new HotaruParser(HotaruLexer.tokenize(source.input));
final var program = parser.parse();
if (parser.getParseErrors().hasErrors()) {
throw new RendererException(parser.getParseErrors().toString());
}
@ -91,10 +90,10 @@ public class RenderPreparer {
public WithStage prepareStage(Stage primaryStage) {
checkCompositionExists();
val stage = new Stage();
final var stage = new Stage();
stage.initOwner(primaryStage);
stage.initModality(Modality.WINDOW_MODAL);
val composition = source.context.composition();
final var composition = source.context.composition();
stage.setScene(sceneProvider().apply(composition));
return new WithStage(composition, stage);
}

View File

@ -15,13 +15,10 @@ import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import javafx.beans.property.BooleanProperty;
import javafx.concurrent.Task;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.StyleSpans;
import org.fxmisc.richtext.StyleSpansBuilder;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;
@RequiredArgsConstructor
public class SyntaxHighlighter {
private final CodeArea editor;
@ -29,6 +26,11 @@ public class SyntaxHighlighter {
private Set<String> nodeFunctions;
private Map<HotaruTokenId, String> operatorClasses;
public SyntaxHighlighter(CodeArea editor, ExecutorService executor) {
this.editor = editor;
this.executor = executor;
}
public void init(BooleanProperty enabledProperty) {
operatorClasses = new HashMap<>();
operatorClasses.put(HotaruTokenId.AT, "keyframes-extractor");
@ -51,13 +53,13 @@ public class SyntaxHighlighter {
}
private Task<StyleSpans<Collection<String>>> computeHighlightingAsync() {
val text = editor.getText();
val task = new Task<StyleSpans<Collection<String>>>() {
final var text = editor.getText();
final var task = new Task<StyleSpans<Collection<String>>>() {
@Override
protected StyleSpans<Collection<String>> call() throws Exception {
val spans = new StyleSpansBuilder<Collection<String>>();
for (val t : new HotaruLexer(text).tokenize()) {
val category = t.getType().getPrimaryCategory();
final var spans = new StyleSpansBuilder<Collection<String>>();
for (final var t : new HotaruLexer(text).tokenize()) {
final var category = t.getType().getPrimaryCategory();
switch (category) {
case "string":
case "keyword":
@ -75,7 +77,7 @@ public class SyntaxHighlighter {
break;
case "operator":
val className = operatorClasses.get(t.getType());
final var className = operatorClasses.get(t.getType());
if (className != null) {
spans.add(Collections.singleton(className), t.getLength());
} else {

View File

@ -20,7 +20,6 @@ import java.util.LinkedHashMap;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
@ -43,13 +42,14 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Duration;
import lombok.val;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
@SuppressWarnings("unused")
public class EditorController implements Initializable, DocumentListener {
private static final int DEFAULT_FONT_SIZE = 14;
@FXML
private Menu examplesMenu;
@ -73,6 +73,7 @@ public class EditorController implements Initializable, DocumentListener {
private Stage primaryStage;
private SyntaxHighlighter syntaxHighlighter;
private DocumentManager documentManager;
private int fontSize = DEFAULT_FONT_SIZE;
@FXML
private void handleMenuNew(ActionEvent event) {
@ -83,7 +84,7 @@ public class EditorController implements Initializable, DocumentListener {
@FXML
private void handleMenuOpen(ActionEvent event) {
val isOpened = documentManager.open(primaryStage, editor::replaceText);
final var isOpened = documentManager.open(primaryStage, editor::replaceText);
if (isOpened) {
updateTitle();
}
@ -120,13 +121,13 @@ public class EditorController implements Initializable, DocumentListener {
}
private boolean confirmExit() {
val alert = new Alert(Alert.AlertType.CONFIRMATION);
final var alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Exit");
alert.setHeaderText("Are you sure you want to exit?");
alert.initOwner(primaryStage);
alert.initModality(Modality.APPLICATION_MODAL);
alert.getDialogPane().setContent(new Group());
val icon = new FontAwesomeIcon(FontAwesome.QUESTION_CIRCLE);
final var icon = new FontAwesomeIcon(FontAwesome.QUESTION_CIRCLE);
alert.getDialogPane().setGraphic(icon);
return alert.showAndWait()
.filter(b -> b == ButtonType.OK)
@ -144,22 +145,22 @@ public class EditorController implements Initializable, DocumentListener {
}
private void changeFontSize(int delta) {
if (editor.getFont() == null) return;
val newSize = (int) editor.getFont().getSize() + delta;
final int newSize = fontSize + delta;
if (8 > newSize || newSize > 40) return;
fontSize = newSize;
editor.setStyle("-fx-font-size: " + newSize + "px");
}
@FXML
private void handleMenuAbout(ActionEvent event) {
val stage = new Stage();
final var stage = new Stage();
stage.setTitle("About");
stage.setResizable(false);
stage.initOwner(primaryStage);
stage.initModality(Modality.WINDOW_MODAL);
try {
val loader = new FXMLLoader(getClass().getResource("/fxml/About.fxml"));
val scene = new Scene(loader.load());
final var loader = new FXMLLoader(getClass().getResource("/fxml/About.fxml"));
final var scene = new Scene(loader.load());
scene.getStylesheets().addAll(
getClass().getResource("/styles/theme-dark.css").toExternalForm(),
getClass().getResource("/styles/about.css").toExternalForm()
@ -174,7 +175,7 @@ public class EditorController implements Initializable, DocumentListener {
@FXML
private void handleMenuPlay(ActionEvent event) {
log.setText("");
val input = editor.getText();
final var input = editor.getText();
if (input.isEmpty()) {
return;
}
@ -185,14 +186,14 @@ public class EditorController implements Initializable, DocumentListener {
.evaluateWithRuntimeBundle()
.prepareStage(primaryStage)
.peek((stage, composition) -> {
val timeline = composition.getTimeline();
final var timeline = composition.getTimeline();
timeline.getFxTimeline().currentTimeProperty().addListener((o, oldValue, d) -> {
val min = (int) d.toMinutes();
val durationSec = d.subtract(Duration.minutes(min));
val sec = (int) durationSec.toSeconds();
val durationMs = durationSec.subtract(Duration.seconds(sec));
val frame = (int) (durationMs.toMillis() * timeline.getFrameRate() / 1000d);
val allFrame = (int) (d.toMillis() * timeline.getFrameRate() / 1000d);
final var min = (int) d.toMinutes();
final var durationSec = d.subtract(Duration.minutes(min));
final var sec = (int) durationSec.toSeconds();
final var durationMs = durationSec.subtract(Duration.seconds(sec));
final var frame = (int) (durationMs.toMillis() * timeline.getFrameRate() / 1000d);
final var allFrame = (int) (d.toMillis() * timeline.getFrameRate() / 1000d);
stage.setTitle(String.format("%02d:%02d.%02d %d", min, sec, frame, allFrame));
});
@ -209,7 +210,7 @@ public class EditorController implements Initializable, DocumentListener {
@FXML
private void handleMenuRender(ActionEvent event) {
log.setText("");
val input = editor.getText();
final var input = editor.getText();
if (input.isEmpty()) {
return;
}
@ -220,18 +221,18 @@ public class EditorController implements Initializable, DocumentListener {
.evaluateForRender()
.prepareStage(primaryStage)
.peek((stage, composition) -> {
val chooser = new DirectoryChooser();
final var chooser = new DirectoryChooser();
chooser.setTitle("Choose directory for rendering frames");
val directory = chooser.showDialog(primaryStage);
final var directory = chooser.showDialog(primaryStage);
if (directory == null || !directory.exists() || !directory.isDirectory()) {
return;
}
val fxTimeline = composition.getTimeline().getFxTimeline();
final var fxTimeline = composition.getTimeline().getFxTimeline();
stage.setOnShown(e -> {
fxTimeline.playFromStart();
fxTimeline.pause();
val task = new RenderTask(directory, composition, stage.getScene());
final var task = new RenderTask(directory, composition, stage.getScene());
task.messageProperty().addListener(ev -> {
stage.setTitle(task.getMessage());
});
@ -257,6 +258,7 @@ public class EditorController implements Initializable, DocumentListener {
documentManager = new FileManager();
populateExamples();
initSyntaxHighlighter();
changeFontSize(0);
initUndoRedo();
initCopyCutPaste();
openExample();
@ -266,7 +268,7 @@ public class EditorController implements Initializable, DocumentListener {
}
private void populateExamples() {
val map = new LinkedHashMap<String, String>();
final var map = new LinkedHashMap<String, String>();
map.put("HotaruFX Logo", "hotarufx-logo.hfx");
map.put("Font Awesome Icons", "font-awesome.hfx");
map.put("HSV Color", "hsv.hfx");
@ -280,19 +282,19 @@ public class EditorController implements Initializable, DocumentListener {
map.put("Blend Modes", "blend-modes.hfx");
map.put("Stroke Ants", "stroke-ants.hfx");
examplesMenu.getItems().clear();
for (val entry : map.entrySet()) {
val item = new MenuItem(entry.getKey());
for (final var entry : map.entrySet()) {
final var item = new MenuItem(entry.getKey());
item.setOnAction(e -> openExample("/examples/" + entry.getValue()));
examplesMenu.getItems().add(item);
}
}
private void initSyntaxHighlighter() {
val highlightProperty = syntaxHighlightingItem.selectedProperty();
final var highlightProperty = syntaxHighlightingItem.selectedProperty();
highlightProperty.addListener((observable, oldValue, highlightEnabled) -> {
if (highlightEnabled) {
// create event to reinit highlighter
val pos = editor.getCaretPosition();
final var pos = editor.getCaretPosition();
editor.insertText(pos, " ");
editor.replaceText(pos, pos + 1, "");
} else {
@ -304,16 +306,14 @@ public class EditorController implements Initializable, DocumentListener {
}
private void initUndoRedo() {
undoButton.disableProperty().bind(
Bindings.not(editor.undoAvailableProperty()));
redoButton.disableProperty().bind(
Bindings.not(editor.redoAvailableProperty()));
undoButton.disableProperty().bind(editor.undoAvailableProperty().map(x -> !x));
redoButton.disableProperty().bind(editor.redoAvailableProperty().map(x -> !x));
undoButton.setOnAction(editorAction(editor::undo));
redoButton.setOnAction(editorAction(editor::redo));
}
private void initCopyCutPaste() {
val selectionEmpty = new BooleanBinding() {
final var selectionEmpty = new BooleanBinding() {
{ bind(editor.selectionProperty()); }
@Override
protected boolean computeValue() {
@ -367,7 +367,7 @@ public class EditorController implements Initializable, DocumentListener {
}
private void openExample(String path) {
val content = (path != null) ? readProgram(path) : fallbackProgram();
final var content = (path != null) ? readProgram(path) : fallbackProgram();
editor.replaceText(content);
}

View File

@ -11,7 +11,6 @@ import javafx.scene.Scene;
import javafx.scene.image.WritableImage;
import javafx.util.Duration;
import javax.imageio.ImageIO;
import lombok.val;
public class RenderTask extends Task<Boolean> {
@ -31,25 +30,25 @@ public class RenderTask extends Task<Boolean> {
@Override
protected Boolean call() throws Exception {
val fxTimeline = timeLine.getFxTimeline();
final var fxTimeline = timeLine.getFxTimeline();
val totalFrames = toFrame(fxTimeline.getTotalDuration());
final var totalFrames = toFrame(fxTimeline.getTotalDuration());
int frame = 0;
while (frame < totalFrames) {
updateProgress(frame, totalFrames);
updateMessage(String.format("%d / %d", frame + 1, totalFrames));
fxTimeline.jumpTo(toDuration(frame));
val image = newImage();
final var image = newImage();
val latch = new CountDownLatch(1);
final var latch = new CountDownLatch(1);
Platform.runLater(() -> {
scene.snapshot(image);
latch.countDown();
});
latch.await();
val file = new File(directory, String.format("frame_%05d.png", frame + 1));
final var file = new File(directory, String.format("frame_%05d.png", frame + 1));
ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
frame++;
}

View File

@ -5,25 +5,18 @@ import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import lombok.Getter;
import lombok.val;
public class Composition {
@Getter
private final int
virtualWidth, virtualHeight,
sceneWidth, sceneHeight;
@Getter
private final double factor;
@Getter
private final TimeLine timeline;
@Getter
private final VirtualScene scene;
@Getter
private final Paint background;
public Composition() {
@ -53,17 +46,49 @@ public class Composition {
scene = newScene();
}
public int getVirtualWidth() {
return virtualWidth;
}
public int getVirtualHeight() {
return virtualHeight;
}
public int getSceneWidth() {
return sceneWidth;
}
public int getSceneHeight() {
return sceneHeight;
}
public double getFactor() {
return factor;
}
public TimeLine getTimeline() {
return timeline;
}
public VirtualScene getScene() {
return scene;
}
public Paint getBackground() {
return background;
}
private VirtualScene newScene() {
val group = new NodesGroup(sceneWidth, sceneHeight);
final var group = new NodesGroup(sceneWidth, sceneHeight);
group.setScaleX(1d / factor);
group.setScaleY(1d / factor);
group.setTranslateX(sceneWidth / 2);
group.setTranslateY(sceneHeight / 2);
group.setTranslateX(sceneWidth / 2f);
group.setTranslateY(sceneHeight / 2f);
return new VirtualScene(group, virtualWidth, virtualHeight);
}
public Scene producePreviewScene() {
val fxScene = new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
final var fxScene = new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
fxScene.setOnKeyPressed(e -> {
switch (e.getCode()) {
case SPACE:
@ -92,8 +117,6 @@ public class Composition {
}
public Scene produceRendererScene() {
val fxScene = new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
return fxScene;
return new Scene(scene.getGroup(), sceneWidth, sceneHeight, background);
}
}

View File

@ -1,18 +1,38 @@
package com.annimon.hotarufx.visual;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Objects;
@RequiredArgsConstructor(staticName="of")
@EqualsAndHashCode
public class KeyFrame implements Comparable<KeyFrame> {
@Getter
public static KeyFrame of(int frame) {
return new KeyFrame(frame);
}
private KeyFrame(int frame) {
this.frame = frame;
}
private final int frame;
public int getFrame() {
return frame;
}
@Override
public int compareTo(KeyFrame o) {
return Integer.compare(frame, o.frame);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
KeyFrame keyFrame = (KeyFrame) o;
return frame == keyFrame.frame;
}
@Override
public int hashCode() {
return Objects.hash(frame);
}
}

View File

@ -1,17 +1,26 @@
package com.annimon.hotarufx.visual;
import javafx.animation.Interpolator;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public class KeyFrameValue<T> {
private final T value;
private final Interpolator interpolator;
public KeyFrameValue(T value, Interpolator interpolator) {
this.value = value;
this.interpolator = interpolator;
}
public KeyFrameValue(T value) {
this(value, Interpolator.LINEAR);
}
public T getValue() {
return value;
}
public Interpolator getInterpolator() {
return interpolator;
}
}

View File

@ -1,16 +1,22 @@
package com.annimon.hotarufx.visual;
import java.util.function.Supplier;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public final class Property {
@Getter
private final PropertyType type;
@Getter
private final Supplier<PropertyTimeline<?>> property;
public Property(PropertyType type, Supplier<PropertyTimeline<?>> property) {
this.type = type;
this.property = property;
}
public PropertyType getType() {
return type;
}
public Supplier<PropertyTimeline<?>> getProperty() {
return property;
}
}

View File

@ -4,9 +4,7 @@ import java.util.Map;
import java.util.TreeMap;
import javafx.animation.Interpolator;
import javafx.beans.value.WritableValue;
import lombok.Getter;
@Getter
public class PropertyTimeline<T> {
private final WritableValue<T> property;
@ -17,6 +15,14 @@ public class PropertyTimeline<T> {
keyFrames = new TreeMap<>();
}
public WritableValue<T> getProperty() {
return property;
}
public Map<KeyFrame, KeyFrameValue<T>> getKeyFrames() {
return keyFrames;
}
public PropertyTimeline<T> add(KeyFrame keyFrame, T value) {
keyFrames.put(keyFrame, new KeyFrameValue<>(value));
return this;

View File

@ -13,11 +13,7 @@ import java.util.function.Function;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
@SuppressWarnings("ConstantConditions")
@RequiredArgsConstructor(access = AccessLevel.PACKAGE)
public enum PropertyType {
BOOLEAN(Value::asBoolean, o -> NumberValue.fromBoolean(Boolean.TRUE.equals(o))),
@ -28,6 +24,11 @@ public enum PropertyType {
PAINT(v -> Color.valueOf(v.asString()), o -> new StringValue(o.toString())),
FONT(toFont(), object -> new FontValue((Font) object));
PropertyType(Function<Value, Object> fromHFX, Function<Object, Value> toHFX) {
this.fromHFX = fromHFX;
this.toHFX = toHFX;
}
private final Function<Value, Object> fromHFX;
private final Function<Object, Value> toHFX;

View File

@ -3,15 +3,11 @@ package com.annimon.hotarufx.visual;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.util.Duration;
import lombok.Getter;
import lombok.val;
public class TimeLine {
@Getter
private final double frameRate;
@Getter
private final Timeline fxTimeline;
public TimeLine(double frameRate) {
@ -19,6 +15,14 @@ public class TimeLine {
fxTimeline = new Timeline(frameRate);
}
public double getFrameRate() {
return frameRate;
}
public Timeline getFxTimeline() {
return fxTimeline;
}
public void addKeyFrame(KeyFrame keyFrame, KeyValue fxKeyValue) {
fxTimeline.getKeyFrames().add(new javafx.animation.KeyFrame(
duration(keyFrame), fxKeyValue));
@ -44,14 +48,14 @@ public class TimeLine {
public void seekFrame(final int value) {
fxTimeline.pause();
val offset = Duration.millis(1000d * Math.abs(value) / frameRate);
val now = fxTimeline.getCurrentTime();
val newDuration = value > 0 ? now.add(offset) : now.subtract(offset);
final var offset = Duration.millis(1000d * Math.abs(value) / frameRate);
final var now = fxTimeline.getCurrentTime();
final var newDuration = value > 0 ? now.add(offset) : now.subtract(offset);
fxTimeline.jumpTo(newDuration);
}
public void seek(final int sec) {
val now = fxTimeline.getCurrentTime();
final var now = fxTimeline.getCurrentTime();
fxTimeline.jumpTo(now.add(Duration.seconds(sec)));
}
}

View File

@ -2,18 +2,31 @@ package com.annimon.hotarufx.visual;
import com.annimon.hotarufx.ui.control.NodesGroup;
import javafx.scene.Node;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class VirtualScene {
@Getter
private final NodesGroup group;
@Getter
private final int virtualWidth, virtualHeight;
public VirtualScene(NodesGroup group, int virtualWidth, int virtualHeight) {
this.group = group;
this.virtualWidth = virtualWidth;
this.virtualHeight = virtualHeight;
}
public NodesGroup getGroup() {
return group;
}
public int getVirtualWidth() {
return virtualWidth;
}
public int getVirtualHeight() {
return virtualHeight;
}
public void add(Node node) {
group.getChildren().add(node);
}

View File

@ -9,7 +9,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javafx.scene.Group;
import lombok.val;
import static com.annimon.hotarufx.visual.PropertyType.BOOLEAN;
public class GroupNode extends ObjectNode {
@ -27,7 +26,7 @@ public class GroupNode extends ObjectNode {
super(group);
this.group = group;
this.nodes = new ArrayList<>(nodes);
val fxNodes = nodes.stream()
final var fxNodes = nodes.stream()
.map(ObjectNode::getFxNode)
.collect(Collectors.toList());
group.getChildren().addAll(fxNodes);

View File

@ -14,9 +14,6 @@ import javafx.beans.value.WritableValue;
import javafx.scene.Node;
import javafx.scene.effect.BlendMode;
import javafx.util.StringConverter;
import lombok.Getter;
import lombok.Setter;
import lombok.val;
import static com.annimon.hotarufx.visual.PropertyType.*;
public abstract class ObjectNode {
@ -31,7 +28,6 @@ public abstract class ObjectNode {
private PropertyTimelineHolder<Number> scaleX, scaleY, scaleZ;
private PropertyTimelineHolder<Number> layoutX, layoutY;
private PropertyTimelineHolder<String> blendMode;
@Getter @Setter
private boolean isRenderable;
public ObjectNode(Node node) {
@ -57,6 +53,14 @@ public abstract class ObjectNode {
return node;
}
public boolean isRenderable() {
return isRenderable;
}
public void setRenderable(boolean renderable) {
isRenderable = renderable;
}
public PropertyTimeline<Boolean> visibleProperty() {
return visible.setIfEmptyThenGet(node::visibleProperty);
}
@ -155,7 +159,7 @@ public abstract class ObjectNode {
protected <T extends Enum<T>> Supplier<WritableValue<String>> enumToString(Class<T> enumClass, ObjectProperty<T> property) {
return () -> {
val stringProperty = new SimpleStringProperty();
final var stringProperty = new SimpleStringProperty();
stringProperty.bindBidirectional(property, new StringConverter<T>() {
@Override
public String toString(T object) {
@ -174,7 +178,7 @@ public abstract class ObjectNode {
return Enum.valueOf(enumClass, string);
} catch (IllegalArgumentException e) {
try {
val number = (int) Double.parseDouble(string);
final var number = (int) Double.parseDouble(string);
return enumClass.cast(EnumSet.allOf(enumClass).toArray()[number]);
} catch (Exception ex) {
throw new HotaruRuntimeException("No constant " + string

View File

@ -3,13 +3,15 @@ package com.annimon.hotarufx.visual.visitors;
import com.annimon.hotarufx.visual.TimeLine;
import com.annimon.hotarufx.visual.VirtualScene;
import com.annimon.hotarufx.visual.objects.*;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class RenderVisitor implements NodeVisitor<Void, VirtualScene> {
private final TimeLine timeline;
public RenderVisitor(TimeLine timeline) {
this.timeline = timeline;
}
@Override
public Void visit(ArcNode node, VirtualScene scene) {
return render(node, scene);

View File

@ -16,7 +16,7 @@
<LineText />
<LineText />
<LineText styleClass="bold" content="0.9.1" />
<LineText styleClass="bold" content="1.0.0" />
<LineText />
<LineText content="Programming language for creating animations" />

View File

@ -272,7 +272,7 @@
})
</code>
</LibraryItem>
<LibraryItem text="SVG Hearth">
<LibraryItem text="SVG Heart">
<graphic>
<SVGPath content="M23.6,0c-3.4,0-6.3,2.7-7.6,5.6C14.7,2.7,11.8,0,8.4,0C3.8,0,0,3.8,0,8.4c0,9.4,9.5,11.9,16,21.2 6.1-9.3,16-12.1,16-21.2C32,3.8,28.2,0,23.6,0z"
fill="#c91720"

View File

@ -6,7 +6,6 @@ import com.annimon.hotarufx.lib.NumberValue;
import com.annimon.hotarufx.lib.Validator;
import java.util.HashMap;
import java.util.Map;
import lombok.val;
import org.junit.jupiter.api.Assertions;
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
@ -30,7 +29,7 @@ public class AssertionsBundle implements Bundle {
private static Function assertHasVariable(Context context) {
return args -> {
Validator.with(args).check(1);
val name = args[0].asString();
final var name = args[0].asString();
Assertions.assertTrue(context.variables().containsKey(name));
return NumberValue.ZERO;
};
@ -39,7 +38,7 @@ public class AssertionsBundle implements Bundle {
private static Function assertHasFunction(Context context) {
return args -> {
Validator.with(args).check(1);
val name = args[0].asString();
final var name = args[0].asString();
Assertions.assertTrue(context.functions().containsKey(name));
return NumberValue.ZERO;
};
@ -56,8 +55,8 @@ public class AssertionsBundle implements Bundle {
private static Function assertEquals(Context context) {
return args -> {
Validator.with(args).check(2);
val expectedValue = args[0];
val actualValue = args[1];
final var expectedValue = args[0];
final var actualValue = args[1];
Assertions.assertEquals(expectedValue, actualValue);
return NumberValue.ZERO;
};

View File

@ -2,7 +2,6 @@ package com.annimon.hotarufx.bundles;
import com.annimon.hotarufx.lib.Context;
import com.annimon.hotarufx.lib.NumberValue;
import lombok.val;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
@ -11,7 +10,7 @@ class CompositionBundleTest {
@Test
void testBundle() {
val context = new Context();
final var context = new Context();
BundleLoader.loadSingle(context, CompositionBundle.class);
assertThat(context.functions(), hasKey("composition"));

View File

@ -10,7 +10,6 @@ import com.annimon.hotarufx.visual.objects.CircleNode;
import java.util.Arrays;
import java.util.HashMap;
import javafx.scene.paint.Color;
import lombok.val;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
@ -19,7 +18,7 @@ class NodesBundleTest {
@Test
void testBundle() {
val context = new Context();
final var context = new Context();
BundleLoader.load(context, Arrays.asList(
CompositionBundle.class,
NodesBundle.class
@ -27,20 +26,20 @@ class NodesBundleTest {
assertThat(context.functions(), hasKey("circle"));
val map = new HashMap<String, Value>();
final var map = new HashMap<String, Value>();
map.put("translateZ", NumberValue.of(-40));
map.put("cx", NumberValue.of(-10));
map.put("radius", NumberValue.of(50));
map.put("fill", new StringValue("#00AA00"));
val value = context.functions().get("circle").execute(new MapValue(map));
final var value = context.functions().get("circle").execute(new MapValue(map));
assertThat(value, instanceOf(NodeValue.class));
val nodeValue = (NodeValue) value;
final var nodeValue = (NodeValue) value;
assertThat(nodeValue.getNode(), notNullValue());
assertThat(nodeValue.getNode(), instanceOf(CircleNode.class));
val circle = (CircleNode) nodeValue.getNode();
final var circle = (CircleNode) nodeValue.getNode();
assertThat(circle.circle.getCenterX(), closeTo(-10, 0.001));
assertThat(circle.circle.getRadius(), closeTo(50, 0.001));
assertThat(circle.circle.getFill(), is(Color.web("#00AA00")));

View File

@ -5,7 +5,6 @@ import com.annimon.hotarufx.lib.Function;
import com.annimon.hotarufx.lib.NumberValue;
import java.util.HashMap;
import java.util.Map;
import lombok.val;
import static com.annimon.hotarufx.bundles.FunctionInfo.of;
import static com.annimon.hotarufx.bundles.FunctionType.COMMON;
@ -46,13 +45,13 @@ public class PrintBundle implements Bundle {
private static Function dump(Context context) {
return args -> {
val maxVariableLength = context.variables()
final var maxVariableLength = context.variables()
.keySet().stream()
.mapToInt(String::length)
.max()
.orElse(20);
System.out.println("\n\tVARIABLES");
val format = "%"+maxVariableLength+"s %s%n";
final var format = "%"+maxVariableLength+"s %s%n";
context.variables().forEach((k, v) -> {
System.out.printf(format, k, v);
});

View File

@ -14,7 +14,6 @@ import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.stream.Stream;
import lombok.val;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@ -45,8 +44,8 @@ class ProgramTest {
@MethodSource("programPathProvider")
@ParameterizedTest
void testProgram(Path path) {
val context = new Context();
val bundles = new ArrayList<Class<? extends Bundle>>();
final var context = new Context();
final var bundles = new ArrayList<Class<? extends Bundle>>();
bundles.addAll(BundleLoader.runtimeBundles());
bundles.add(AssertionsBundle.class);
bundles.add(PrintBundle.class);

View File

@ -12,7 +12,6 @@ import com.annimon.hotarufx.parser.ast.UnitNode;
import com.annimon.hotarufx.parser.ast.ValueNode;
import com.annimon.hotarufx.parser.ast.VariableNode;
import java.util.Arrays;
import lombok.val;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
@ -69,23 +68,23 @@ class HotaruParserTest {
Node node = p(input);
assertThat(node, instanceOf(BlockNode.class));
val block = (BlockNode) node;
final var block = (BlockNode) node;
assertThat(block.statements.size(), is(2));
val expectedValues = Arrays.asList(
final var expectedValues = Arrays.asList(
NumberValue.fromBoolean(true),
NumberValue.fromBoolean(false)
);
val it = expectedValues.iterator();
final var it = expectedValues.iterator();
for (Node statement : block.statements) {
assertThat(statement, instanceOf(AssignNode.class));
val assignNode = (AssignNode) statement;
final var assignNode = (AssignNode) statement;
assertThat(assignNode.target, instanceOf(VariableNode.class));
assertThat(assignNode.value, instanceOf(ValueNode.class));
val value = ((ValueNode) assignNode.value).value;
final var value = ((ValueNode) assignNode.value).value;
assertThat(value, is(it.next()));
}
}
@ -96,26 +95,26 @@ class HotaruParserTest {
Node node = p(input);
assertThat(node, instanceOf(BlockNode.class));
val block = (BlockNode) node;
final var block = (BlockNode) node;
assertThat(block.statements.size(), is(2));
val expectedValues = Arrays.asList(
final var expectedValues = Arrays.asList(
NumberValue.of(500),
NumberValue.of(0.5)
);
val it = expectedValues.iterator();
final var it = expectedValues.iterator();
for (Node statement : block.statements) {
assertThat(statement, instanceOf(AssignNode.class));
val assignNode = (AssignNode) statement;
final var assignNode = (AssignNode) statement;
assertThat(assignNode.target, instanceOf(VariableNode.class));
assertThat(assignNode.value, instanceOf(UnitNode.class));
val unitNode = (UnitNode) assignNode.value;
final var unitNode = (UnitNode) assignNode.value;
assertThat(unitNode.value, instanceOf(ValueNode.class));
val value = ((ValueNode) unitNode.value).value;
final var value = ((ValueNode) unitNode.value).value;
assertThat(value, is(it.next()));
}
}

View File

@ -14,7 +14,6 @@ import com.annimon.hotarufx.lib.Value;
import com.annimon.hotarufx.parser.HotaruParser;
import java.util.Arrays;
import java.util.Map;
import lombok.val;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
@ -120,7 +119,7 @@ class InterpreterVisitorTest {
@Test
void testUnits() {
val context = new Context();
final var context = new Context();
BundleLoader.loadSingle(context, CompositionBundle.class);
context.functions().put("rate", context.functions().get("composition"));
Value value;

View File

@ -1,6 +1,5 @@
package com.annimon.hotarufx.visual;
import lombok.val;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
@ -9,7 +8,7 @@ class TimeLineTest {
@Test
void add() {
val timeline = new PropertyTimeline<String>(null);
final var timeline = new PropertyTimeline<String>(null);
timeline.add(KeyFrame.of(20), null);
timeline.add(KeyFrame.of(10), null);
timeline.add(KeyFrame.of(0), null);

View File

@ -10,7 +10,6 @@ import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import lombok.val;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
@ -52,36 +51,36 @@ class NodePropertiesTypeTest {
@SuppressWarnings("unchecked")
void testNode(ObjectNode node, String name, Property property, String nodeName) {
try {
val value = property.getProperty().get().getProperty();
final var value = property.getProperty().get().getProperty();
switch (property.getType()) {
case BOOLEAN:
val booleanValue = (WritableValue<Boolean>) value;
final var booleanValue = (WritableValue<Boolean>) value;
booleanValue.setValue(true);
assertTrue(booleanValue.getValue());
break;
case NUMBER:
val numberValue = (WritableValue<Number>) value;
final var numberValue = (WritableValue<Number>) value;
numberValue.setValue(2);
assertThat(numberValue.getValue().intValue(), is(2));
break;
case STRING:
val stringValue = (WritableValue<String>) value;
final var stringValue = (WritableValue<String>) value;
stringValue.setValue("0");
assertThat(stringValue.getValue(), is("0"));
break;
case NODE:
case CLIP_NODE:
val nodeValue = (WritableValue<Node>) value;
final var nodeValue = (WritableValue<Node>) value;
nodeValue.setValue(new Text("test"));
assertThat(((Text) nodeValue.getValue()).getText(), is("test"));
break;
case PAINT:
val paintValue = (WritableValue<Paint>) value;
final var paintValue = (WritableValue<Paint>) value;
paintValue.setValue(Color.BLUE);
assertThat(paintValue.getValue(), is(Color.BLUE));
break;
case FONT:
val fontValue = (WritableValue<Font>) value;
final var fontValue = (WritableValue<Font>) value;
fontValue.setValue(Font.getDefault());
assertThat(fontValue.getValue().getFamily(), is(Font.getDefault().getFamily()));
break;

Binary file not shown.

View File

@ -1,6 +1,5 @@
#Mon Aug 21 12:17:04 EEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip

28
gradlew vendored
View File

@ -1,5 +1,21 @@
#!/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
@ -28,16 +44,16 @@ 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=""
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
warn () {
echo "$*"
}
die ( ) {
die () {
echo
echo "$*"
echo
@ -109,8 +125,8 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
# 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"`
@ -155,7 +171,7 @@ if $cygwin ; then
fi
# Escape application args
save ( ) {
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}

184
gradlew.bat vendored
View File

@ -1,84 +1,100 @@
@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 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=
@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 init
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 init
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
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
: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
@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 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 init
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 init
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
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
: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 %CMD_LINE_ARGS%
: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