package nl.utwente.viskell.ui.components;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import nl.utwente.viskell.ghcj.GhciSession;
import nl.utwente.viskell.ghcj.HaskellException;
import nl.utwente.viskell.haskell.expr.*;
import nl.utwente.viskell.haskell.type.*;
import nl.utwente.viskell.ui.ToplevelPane;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
* DisplayBlock is an extension of {@link Block} that only provides a display of
* the input it receives through it's {@link InputAnchor}. The input will be
* rendered visually on the Block. DisplayBlock can be empty and contain no
* value at all, the value can be altered at any time by providing a different
* input source using a {@link Connection}.
*/
public class DisplayBlock extends Block implements ConnectionAnchor.Target {
/** The Anchor that is used as input. */
protected InputAnchor inputAnchor;
/** The space containing the input anchor. */
@FXML protected Pane inputSpace;
/** The label on which to display the value of this block */
@FXML protected Label value;
/** Show class constrained type variable for the input anchor */
private final Type showConstraint;
/**
* Creates a new instance of DisplayBlock.
* @param pane on which this DisplayBlock resides.
*/
public DisplayBlock(ToplevelPane pane) {
this(pane, "DisplayBlock");
}
protected DisplayBlock(ToplevelPane pane, String fxml) {
super(pane);
this.showConstraint = pane.getEnvInstance().buildType("Show a => a");
loadFXML(fxml);
inputAnchor = new InputAnchor(this);
inputSpace.getChildren().add(0, inputAnchor);
}
@SuppressWarnings("UnusedParameters")
public static DisplayBlock fromBundleFragment(ToplevelPane pane, Map<String, Object> bundleFragment) {
return new DisplayBlock(pane);
}
@Override
public void invalidateVisualState() {
this.inputAnchor.invalidateVisualState();
if (this.inValidContext && inputAnchor.hasValidConnection()) {
try {
TypeChecker.unify("is showable", inputAnchor.getType().getFresh(), showConstraint.getFresh());
GhciSession ghci = getToplevel().getGhciSession();
Expression expr = inputAnchor.getFullExpr();
Type type = inputAnchor.getType().getConcrete();
if (type instanceof TypeApp) {
List<Type> tapps = ((TypeApp)type).asFlattenedAppChain();
if (tapps.get(0) instanceof ListTypeCon) {
// add an extra take on lists, so we don't try to fully eval infinite ones
FunVar take = new FunVar(this.getToplevel().getEnvInstance().lookupFun("take"));
expr = new Apply (new Apply(take, new Value(Type.con("Int"), "32")), expr);
}
}
ListenableFuture<String> result = ghci.pull(expr);
Futures.addCallback(result, new FutureCallback<String>() {
public void onSuccess(String s) {
// Can't call setOutput directly - this may not be JavaFX app thread.
// Instead, schedule setting the output.
Platform.runLater(() -> value.setText(s));
}
public void onFailure(Throwable throwable) {
if (throwable instanceof HaskellException && "Open expression".equals(throwable.getMessage())) {
Platform.runLater(() -> value.setText("unfinished?"));
} else {
Platform.runLater(() -> value.setText("?!?!?!"));
}
}
});
} catch (HaskellTypeError e) {
value.setText("_ :: " + inputAnchor.getStringType());
}
} else {
value.setText("?");
}
}
//TODO NOTE: only used for a meaningless test
public String getOutput() {
return value.getText();
}
@Override
public ConnectionAnchor getAssociatedAnchor() {
return inputAnchor;
}
@Override
public List<InputAnchor> getAllInputs() {
return ImmutableList.of(inputAnchor);
}
@Override
public List<OutputAnchor> getAllOutputs() {
return ImmutableList.of();
}
@Override
public Optional<Block> getNewCopy() {
return Optional.of(new DisplayBlock(this.getToplevel()));
}
@Override
public Expression getLocalExpr(Set<OutputAnchor> outsideAnchors) {
return inputAnchor.getLocalExpr(outsideAnchors);
}
@Override
public void refreshAnchorTypes() {
this.inputAnchor.setFreshRequiredType(TypeScope.unique("q"), new TypeScope());
}
@Override
public String toString() {
return "DisplayBlock[" + value.getText() + "]";
}
@Override
public boolean checkValidInCurrentContainer() {
if (this.container instanceof LambdaContainer || this.container instanceof Lane) {
return false;
} else {
return super.checkValidInCurrentContainer();
}
}
}