diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/Bundle.java b/app/src/main/java/com/annimon/hotarufx/bundles/Bundle.java index be5f5da..b20aaa5 100644 --- a/app/src/main/java/com/annimon/hotarufx/bundles/Bundle.java +++ b/app/src/main/java/com/annimon/hotarufx/bundles/Bundle.java @@ -1,8 +1,23 @@ package com.annimon.hotarufx.bundles; import com.annimon.hotarufx.lib.Context; +import java.util.Map; +import java.util.stream.Collectors; public interface Bundle { - void load(Context context); + Map functionsInfo(); + + default Map functions() { + return functionsInfo().entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue().getType())); + } + + default void load(Context context) { + functionsInfo().forEach((name, info) -> { + context.functions().put(name, info.extract(context)); + }); + } } diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/BundleLoader.java b/app/src/main/java/com/annimon/hotarufx/bundles/BundleLoader.java index ed0d9f4..9bac0b6 100644 --- a/app/src/main/java/com/annimon/hotarufx/bundles/BundleLoader.java +++ b/app/src/main/java/com/annimon/hotarufx/bundles/BundleLoader.java @@ -1,25 +1,47 @@ package com.annimon.hotarufx.bundles; import com.annimon.hotarufx.lib.Context; +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 { + public static List> allBundles() { + return Arrays.asList( + CompositionBundle.class, + NodesBundle.class + ); + } + public static void loadSingle(Context context, Class clazz) { load(context, Collections.singletonList(clazz)); } + public static Map functions() { + val functions = new HashMap(); + apply(allBundles(), functions, ((bundle, map) -> map.putAll(bundle.functions()))); + return functions; + } + public static void load(Context context, List> bundles) { - if (bundles == null || bundles.isEmpty()) { + apply(bundles, context, Bundle::load); + } + + private static void apply(List> bundles, + T obj, BiConsumer action) { + if (action == null || bundles == null || bundles.isEmpty()) { return; } for (Class clazz : bundles) { try { val bundle = clazz.newInstance(); - bundle.load(context); + action.accept(bundle, obj); } catch (IllegalAccessException | InstantiationException ignore) {} } } diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java b/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java index cb90300..a363cdf 100644 --- a/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java +++ b/app/src/main/java/com/annimon/hotarufx/bundles/CompositionBundle.java @@ -1,19 +1,35 @@ package com.annimon.hotarufx.bundles; import com.annimon.hotarufx.lib.Context; +import com.annimon.hotarufx.lib.Function; import com.annimon.hotarufx.lib.NodeValue; import com.annimon.hotarufx.lib.NumberValue; import com.annimon.hotarufx.lib.Types; import com.annimon.hotarufx.visual.Composition; import com.annimon.hotarufx.visual.visitors.RenderVisitor; import java.util.Arrays; +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; public class CompositionBundle implements Bundle { + private static final Map FUNCTIONS; + static { + FUNCTIONS = new HashMap<>(); + FUNCTIONS.put("composition", of(COMMON, CompositionBundle::composition)); + FUNCTIONS.put("render", of(COMMON, CompositionBundle::render)); + } + @Override - public void load(Context context) { - context.functions().put("composition", args -> { + public Map functionsInfo() { + return FUNCTIONS; + } + + private static Function composition(Context context) { + return args -> { final int width, height; final double frameRate; switch (args.length) { @@ -47,9 +63,11 @@ public class CompositionBundle implements Bundle { context.variables().put("HalfWidth", NumberValue.of(scene.getVirtualWidth() / 2)); context.variables().put("HalfHeight", NumberValue.of(scene.getVirtualHeight() / 2)); return NumberValue.ZERO; - }); + }; + } - context.functions().put("render", args -> { + private static Function render(Context context) { + return args -> { val renderVisitor = new RenderVisitor(context.composition().getTimeline()); val scene = context.composition().getScene(); Arrays.stream(args) @@ -57,6 +75,6 @@ public class CompositionBundle implements Bundle { .map(v -> ((NodeValue) v).getNode()) .forEach(node -> node.accept(renderVisitor, scene)); return NumberValue.ZERO; - }); + }; } } diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/FunctionInfo.java b/app/src/main/java/com/annimon/hotarufx/bundles/FunctionInfo.java new file mode 100644 index 0000000..11f8449 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/bundles/FunctionInfo.java @@ -0,0 +1,27 @@ +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) { + return of(type, (Context c) -> function); + } + + public static FunctionInfo of(FunctionType type, java.util.function.Function extractor) { + return new FunctionInfo(type, extractor); + } + + @Getter + private final FunctionType type; + private final java.util.function.Function functionExtractor; + + public Function extract(Context context) { + return functionExtractor.apply(context); + } +} diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/FunctionType.java b/app/src/main/java/com/annimon/hotarufx/bundles/FunctionType.java new file mode 100644 index 0000000..1fb72d7 --- /dev/null +++ b/app/src/main/java/com/annimon/hotarufx/bundles/FunctionType.java @@ -0,0 +1,5 @@ +package com.annimon.hotarufx.bundles; + +public enum FunctionType { + COMMON, NODE +} \ No newline at end of file diff --git a/app/src/main/java/com/annimon/hotarufx/bundles/NodesBundle.java b/app/src/main/java/com/annimon/hotarufx/bundles/NodesBundle.java index 062df90..58280af 100644 --- a/app/src/main/java/com/annimon/hotarufx/bundles/NodesBundle.java +++ b/app/src/main/java/com/annimon/hotarufx/bundles/NodesBundle.java @@ -1,6 +1,5 @@ package com.annimon.hotarufx.bundles; -import com.annimon.hotarufx.lib.Context; import com.annimon.hotarufx.lib.Function; import com.annimon.hotarufx.lib.NodeValue; import com.annimon.hotarufx.lib.Types; @@ -8,28 +7,38 @@ import com.annimon.hotarufx.lib.Validator; import com.annimon.hotarufx.lib.Value; import com.annimon.hotarufx.visual.objects.*; import java.util.Arrays; +import java.util.HashMap; 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; public class NodesBundle implements Bundle { - @Override - public void load(Context context) { - context.functions().put("arc", node(ArcNode::new)); - context.functions().put("circle", node(CircleNode::new)); - context.functions().put("ellipse", node(EllipseNode::new)); - context.functions().put("group", group()); - context.functions().put("line", node(LineNode::new)); - context.functions().put("polygon", poly(PolygonNode::new)); - context.functions().put("polyline", poly(PolylineNode::new)); - context.functions().put("rectangle", node(RectangleNode::new)); - context.functions().put("svgPath", node(SVGPathNode::new)); - context.functions().put("text", node(TextNode::new)); + private static final Map FUNCTIONS; + static { + FUNCTIONS = new HashMap<>(); + FUNCTIONS.put("arc", of(NODE, node(ArcNode::new))); + FUNCTIONS.put("circle", of(NODE, node(CircleNode::new))); + FUNCTIONS.put("ellipse", of(NODE, node(EllipseNode::new))); + FUNCTIONS.put("group", of(NODE, group())); + FUNCTIONS.put("line", of(NODE, node(LineNode::new))); + FUNCTIONS.put("polygon", of(NODE, poly(PolygonNode::new))); + FUNCTIONS.put("polyline", of(NODE, poly(PolylineNode::new))); + FUNCTIONS.put("rectangle", of(NODE, node(RectangleNode::new))); + FUNCTIONS.put("svgPath", of(NODE, node(SVGPathNode::new))); + FUNCTIONS.put("text", of(NODE, node(TextNode::new))); } - private Function node(Supplier supplier) { + @Override + public Map functionsInfo() { + return FUNCTIONS; + } + + private static Function node(Supplier supplier) { return args -> { val map = Validator.with(args).requireMapAt(0); val node = new NodeValue(supplier.get()); @@ -38,7 +47,7 @@ public class NodesBundle implements Bundle { }; } - private Function poly(java.util.function.Function, ObjectNode> ctor) { + private static Function poly(java.util.function.Function, ObjectNode> ctor) { return args -> { val validator = Validator.with(args); val map = validator.requireMapAt(1); @@ -51,7 +60,7 @@ public class NodesBundle implements Bundle { }; } - private Function group() { + private static Function group() { return args -> { val nodes = Arrays.stream(args) .filter(v -> v.type() == Types.NODE)