package nl.utwente.viskell.ui.components; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import javafx.geometry.Pos; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.Polygon; import nl.utwente.viskell.haskell.expr.Binder; import nl.utwente.viskell.haskell.expr.Expression; import nl.utwente.viskell.haskell.expr.Value; import nl.utwente.viskell.haskell.type.Type; import nl.utwente.viskell.ui.ToplevelPane; /** A compact Monoid append block. */ public class AppendBlock extends Block { /** A list of InputAnchor for each input. */ private final List<InputAnchor> inputs; /** The output anchor with the monoid append result. */ private final OutputAnchor output; /** Monoid class constrained type variable for the anchors. */ private final Type monoidConstraint; public AppendBlock(ToplevelPane pane, int arity) { super(pane); this.monoidConstraint = pane.getEnvInstance().buildType("Monoid m => m"); this.output = new OutputAnchor(this, new Binder("append_"+arity)); this.inputs = new ArrayList<>(); for (int i = 0; i < arity; i++) { this.inputs.add(new InputAnchor(this)); } HBox inputSpace = new HBox(40); inputSpace.getChildren().addAll(this.inputs); inputSpace.setPickOnBounds(false); inputSpace.setAlignment(Pos.CENTER); HBox outputSpace = new HBox(this.output); outputSpace.setPickOnBounds(false); outputSpace.setAlignment(Pos.CENTER); int xbase = 10; // = anchor_width / 2 int xstep = (arity - 1) * 5; // 5 = horizontal_spacing / 8 int ystep = 8; // = height / 3 Polygon appender = new Polygon( -xbase-4*xstep, 0*ystep, xbase+4*xstep, 0*ystep, 3+xbase+4*xstep, 1*ystep, xbase+0*xstep, 3*ystep, -xbase-0*xstep, 3*ystep, -3-xbase-4*xstep, 1*ystep); appender.setFill(Color.SADDLEBROWN); appender.setPickOnBounds(true); VBox body = new VBox(inputSpace, appender, outputSpace); body.setPickOnBounds(false); this.getChildren().add(body); this.setPickOnBounds(false); } /** @return class-specific properties of this Block. */ @Override protected Map<String, Object> toBundleFragment() { return ImmutableMap.of("arity", this.inputs.size()); } public static AppendBlock fromBundleFragment(ToplevelPane pane, Map<String,Object> bundleFragment) { int arity = ((Double)bundleFragment.get("arity")).intValue(); return new AppendBlock(pane, arity); } @Override public List<InputAnchor> getAllInputs() { return inputs; } @Override public List<OutputAnchor> getAllOutputs() { return ImmutableList.of(this.output); } @Override protected void refreshAnchorTypes() { Type monoidal = this.monoidConstraint.getFresh(); for (InputAnchor input : this.inputs) { input.setExactRequiredType(monoidal); } this.output.setExactRequiredType(monoidal); } @Override public Expression getLocalExpr(Set<OutputAnchor> outsideAnchors) { // sometimes textual code generation is just easier StringBuilder expr = new StringBuilder(); expr.append("("); for (int i = 0; i < this.inputs.size(); i++) { if (i != 0) { expr.append(" `mappend` "); } InputAnchor input = this.inputs.get(i); expr.append(input.getLocalExpr(outsideAnchors).toHaskell()); } expr.append(')'); return new Value(this.output.getType(Optional.empty()).getFresh(), expr.toString()); } @Override public void invalidateVisualState() { for (InputAnchor input : this.inputs) { input.invalidateVisualState(); } } @Override public Optional<Block> getNewCopy() { return Optional.of(new AppendBlock(this.getToplevel(), this.inputs.size())); } }