package nl.utwente.viskell.ui.components; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.stream.Collectors; import com.google.common.collect.ImmutableMap; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.layout.Pane; import jfxtras.scene.layout.VBox; import nl.utwente.viskell.haskell.env.FunctionInfo; import nl.utwente.viskell.haskell.expr.Binder; import nl.utwente.viskell.haskell.expr.ConstructorBinder; import nl.utwente.viskell.haskell.expr.Expression; import nl.utwente.viskell.haskell.type.FunType; import nl.utwente.viskell.haskell.type.Type; import nl.utwente.viskell.haskell.type.TypeScope; import nl.utwente.viskell.ui.ToplevelPane; import com.google.common.collect.ImmutableList; /** A block that matches a data constructors and yields all the elements of that constructor. */ public class MatchBlock extends Block { /** Information about the data constructor as if it was a function. */ protected FunctionInfo info; /** The InputAnchor of this Matchblock. */ protected InputAnchor input; /** A list of nodes with an OutputAnchor and Label for every constructor element. */ protected List<Pane> outputs; /** The space containing the input anchor(s). */ @FXML protected Pane inputSpace; /** The space containing the output anchor. */ @FXML protected Pane outputSpace; /** The Label with the constructor name. */ @FXML protected Label name; protected ConstructorBinder primaryBinder; protected Label inputLabel; public MatchBlock(ToplevelPane pane, FunctionInfo funInfo) { super(pane); this.loadFXML("MatchBlock"); outputs = new ArrayList<>(); info = funInfo; Type type = info.getFreshSignature(); ArrayList<Binder> elemBinders = new ArrayList<>(); int outputCount = 0; while (type instanceof FunType) { FunType ftype = (FunType) type; Binder binder = new Binder("res"+outputCount, ftype.getArgument()); elemBinders.add(binder); OutputAnchor anchor = new OutputAnchor(this, binder); VBox box = new VBox(0); box.setTranslateY(9); box.getStyleClass().add("argumentSpace"); Label typeLabel = new Label(ftype.getArgument().prettyPrint()); typeLabel.getStyleClass().add("resultType"); box.getChildren().addAll(typeLabel, anchor); outputs.add(box); type = ftype.getResult(); outputCount++; } primaryBinder = new ConstructorBinder(info.getName(), elemBinders); input = new InputAnchor(this, type); inputLabel = new Label(info.getFreshSignature().prettyPrint()); inputLabel.getStyleClass().add("inputType"); String fname = funInfo.getDisplayName(); name.setText(fname.charAt(0) == '(' && fname.length() > 2 ? fname.substring(1, fname.length()-1) : fname); inputSpace.getChildren().addAll(input, inputLabel); inputSpace.setTranslateY(-9); outputSpace.getChildren().addAll(outputs); } /** @return a Map of class-specific properties of this Block. */ @Override protected Map<String, Object> toBundleFragment() { return ImmutableMap.of("funInfo", this.info.toBundleFragment()); } /** return a new instance of this Block type deserializing class-specific properties used in constructor **/ public static MatchBlock fromBundleFragment(ToplevelPane pane, Map<String,Object> bundleFragment) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException { Map<String, Object> funInfoBundle = (Map<String, Object>)bundleFragment.get("funInfo"); FunctionInfo funInfo = FunctionInfo.fromBundleFragment(funInfoBundle); return new MatchBlock(pane, funInfo); } @Override public List<InputAnchor> getAllInputs() { return ImmutableList.of(input); } @Override public List<OutputAnchor> getAllOutputs() { return outputs.stream().map(box -> (OutputAnchor)(box.getChildren().get(1))).collect(Collectors.toList()); } @Override public Optional<Block> getNewCopy() { return Optional.of(new MatchBlock(this.getToplevel(), this.info)); } @Override protected void refreshAnchorTypes() { Type type = info.getFreshSignature(); TypeScope scope = new TypeScope(); for (Pane box : outputs) { if (type instanceof FunType) { FunType ftype = (FunType)type; OutputAnchor anchor = (OutputAnchor)box.getChildren().get(1); anchor.setFreshRequiredType(ftype.getArgument(), scope); type = ftype.getResult(); } else { throw new RuntimeException("too many arguments in this matchblock " + name.getText()); } } input.setFreshRequiredType(type, scope); } @Override public Expression getLocalExpr(Set<OutputAnchor> outsideAnchors) { return input.getLocalExpr(outsideAnchors); } @Override public void invalidateVisualState() { this.input.invalidateVisualState(); boolean validConnection = input.hasValidConnection(); inputSpace.setTranslateY(validConnection ? 0 : -9); inputLabel.setText(validConnection ? "zyxwv" : input.getStringType()); inputLabel.setVisible(!validConnection); for (Pane box : outputs) { OutputAnchor anchor = (OutputAnchor)box.getChildren().get(1); Label label = (Label)box.getChildren().get(0); label.setText(anchor.getStringType()); anchor.invalidateVisualState(); } } public Binder getPrimaryBinder() { return primaryBinder; } }