package bluej.stride.framedjava.frames;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import javafx.beans.binding.DoubleExpression;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;

import bluej.Config;
import bluej.stride.framedjava.ast.AccessPermissionFragment;
import bluej.stride.framedjava.ast.ExpressionSlotFragment;
import bluej.stride.framedjava.ast.JavadocUnit;
import bluej.stride.framedjava.ast.NameDefSlotFragment;
import bluej.stride.framedjava.ast.ParamFragment;
import bluej.stride.framedjava.ast.SuperThis;
import bluej.stride.framedjava.ast.SuperThisParamsExpressionFragment;
import bluej.stride.framedjava.ast.SuperThisFragment;
import bluej.stride.framedjava.ast.TypeSlotFragment;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.elements.ConstructorElement;
import bluej.stride.framedjava.elements.NormalMethodElement;
import bluej.stride.framedjava.slots.ExpressionSlot;
import bluej.stride.framedjava.slots.SuperThisParamsExpressionSlot;
import bluej.stride.generic.ExtensionDescription;
import bluej.stride.generic.ExtensionDescription.ExtensionSource;
import bluej.stride.generic.Frame;
import bluej.stride.generic.FrameCanvas;
import bluej.stride.generic.FrameContentRow;
import bluej.stride.generic.FrameCursor;
import bluej.stride.generic.FrameFactory;
import bluej.stride.generic.InteractionManager;
import bluej.stride.operations.CustomFrameOperation;
import bluej.stride.operations.FrameOperation;
import bluej.stride.slots.ChoiceSlot;
import bluej.stride.slots.EditableSlot;
import bluej.stride.slots.EditableSlot.MenuItemOrder;
import bluej.stride.slots.FormalParameters;
import bluej.stride.slots.HeaderItem;
import bluej.stride.slots.SlotLabel;
import bluej.utility.Utility;
import bluej.utility.javafx.FXRunnable;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;
import threadchecker.OnThread;
import threadchecker.Tag;

public class ConstructorFrame extends MethodFrameWithBody<ConstructorElement> {

   
private final SlotLabel headerLabel;
   
private ConstructorElement element;

   
private FrameContentRow callRow; 
private ChoiceSlot<SuperThis> superThis; 
private SuperThisParamsExpressionSlot superThisParams; 

   
private ConstructorFrame(InteractionManager editor)
{   
super(editor);
   setDocumentationPromptText(Config.getString("frame.class.constructor.doc.prompt"));
       
   headerLabel = new SlotLabel("<constructor>");
   JavaFXUtil.addStyleClass(headerLabel, "constructor-name-caption");

       
   paramsPane = new FormalParameters(editor, this, this, getHeaderRow(), "constructor-param-");

   headerLabel.textProperty().bind(editor.nameProperty());

   getHeaderRow().bindContentsConcat(FXCollections.<ObservableList<? extends HeaderItem>>observableArrayList(
   FXCollections.observableArrayList(access),
   FXCollections.observableArrayList(headerLabel),
   paramsPane.getSlots(),
   throwsPane.getHeaderItems()
       
   ));     
   }

   
public ConstructorFrame(InteractionManager editor, AccessPermissionFragment access, String documentation,
           
SuperThisFragment delegate, ExpressionSlotFragment delegateParams, boolean enabled) {
   this(editor);
   this.access.setValue(access.getValue());
   access.registerSlot(this.access);
   setDocumentation(documentation);
   if (delegate != null || delegateParams != null) {
      addSuperThis(delegate, delegateParams);         
      }
   frameEnabledProperty.set(enabled);     
   }

   
public static FrameFactory getFactory()
{           
return new FrameFactory<ConstructorFrame>() {            
      @Override
           
      public ConstructorFrame createBlock(InteractionManager editor)
{                         
return new ConstructorFrame(editor);             
         }

           
      @Override
           
      public Class getBlockClass()
{                         
return ConstructorFrame.class;             
         }         
      };
   }

   
@Override
   
public void regenerateCode()
{   
List<ParamFragment> params = generateParams();

       
   element = new ConstructorElement(this, new AccessPermissionFragment(this, access), params, throwsPane.getTypes(),
               
   Utility.orNull(superThis, s -> new SuperThisFragment(this, s, superThisParams)),
   superThisParams == null ? null : superThisParams.getSlotElement(), getContents(),
               
   new JavadocUnit(getDocumentation()), frameEnabledProperty.get());     
   }

   
@Override
   
public ConstructorElement getCode()
{           
return element;     
   }

   
private void addSuperThis(SuperThisFragment st, ExpressionSlotFragment params)
{   
if (superThis != null) {
      superThis.setValue(st.getValue());        
      }

   else {
           
   callRow = new FrameContentRow(this, "constructor-call-");
           
   callRow.setMargin(new Insets(0, 6, 0, 0));
           
   superThis = new ChoiceSlot<>(getEditor(), this, callRow, SuperThis.all(), SuperThis::isValid, "constructor-", Collections.emptyMap());
   superThis.setValue(st.getValue());

           
   superThisParams = new SuperThisParamsExpressionSlot(getEditor(), this, this, callRow, superThis, "constructor-param-") {                 
      @Override
      @OnThread(Tag.FXPlatform)
               
      public boolean backspaceAtStart()
{         
if (isAlmostBlank()) {
            FrameCursor fc = getCanvas().getFirstCursor();
            getEditor().beginRecordingState(fc);
            removeSuperThis();
            fc.requestFocus();
            getEditor().endRecordingState(fc);
                       
            return true;                     
            }
         return super.backspaceAtStart();                  
         }              
      };
   callRow.setHeaderItems(Arrays.asList(
                   
   superThis,
   ExpressionSlot.makeBracketSlot("(", true, null),
                   
   superThisParams,
   ExpressionSlot.makeBracketSlot(")", false, null)
           
   ));

   contents.setAll(documentationPane, getHeaderRow(), callRow, getCanvas());
   if (params != null) {
      superThisParams.setText(params);             
      }
   superThisParams.requestFocus();
   getEditor().modifiedFrame(this, false);
   JavaFXUtil.setPseudoclass("bj-super-this", true, getNode());         
   }
st.registerSlot(superThis);     
}

   
private void removeSuperThis()
{   
if (callRow != null) {            
      
| TODO shrink and remove: | |Timeline t = new Timeline(new KeyFrame(Duration.millis(0), new KeyValue(n.maxHeightProperty(), n.getHeight())), | |new KeyFrame(Duration.millis(200), new KeyValue(n.maxHeightProperty(), 0.0))); | |t.setOnFinished(e -> headBox.getChildren().remove(n)); | |t.play(); callRow = null; superThis = null; superThisParams = null; contents.setAll(documentationPane, getHeaderRow(), getCanvas()); getCanvas().getFirstCursor().requestFocus(); getEditor().modifiedFrame(this, false); JavaFXUtil.setPseudoclass("bj-super-this", false, getNode()); } } @Override public List getAvailableExtensions(FrameCanvas canvas, FrameCursor cursorInCanvas) { if (callRow == null) { List<ExtensionDescription> extensions = new ArrayList(super.getAvailableExtensions(canvas, cursorInCanvas)); extensions.addAll(Arrays.asList(new ExtensionDescription(StrideDictionary.SUPER_EXTENSION_CHAR, Config.getString("frame.class.add.super"), () -> addSuperThis(new SuperThisFragment(SuperThis.SUPER), null), true, ExtensionSource.INSIDE_FIRST, ExtensionSource.MODIFIER), new ExtensionDescription(StrideDictionary.THIS_EXTENSION_CHAR, Config.getString("frame.class.add.this"), () -> addSuperThis(new SuperThisFragment(SuperThis.THIS), null), true, ExtensionSource.INSIDE_FIRST, ExtensionSource.MODIFIER))); return extensions; } else { return Arrays.asList(new ExtensionDescription('\b', Config.getString("frame.class.remove.super"), () -> removeSuperThis(), true, ExtensionSource.INSIDE_FIRST)); } } @Override @OnThread(Tag.FXPlatform) public List getContextOperations() { List<FrameOperation> r = new ArrayList<>(super.getContextOperations()); r.add(new CustomFrameOperation(getEditor(), "constructor->method", Arrays.asList(Config.getString("frame.operation.change"), Config.getString("frame.operation.change.to.method")), MenuItemOrder.TRANSFORM, this, () -> { Frame parent = getParentCanvas().getParent().getFrame(); if (parent instanceof ClassFrame) { FrameCanvas p = ((ClassFrame) parent).getMethodsCanvas(); FrameCursor c = p.getLastCursor(); NormalMethodElement el = new NormalMethodElement(null, new AccessPermissionFragment(this, access), false, false, new TypeSlotFragment("", ""), new NameDefSlotFragment(""), generateParams(), throwsPane.getTypes(), getContents(), new JavadocUnit(getDocumentation()), frameEnabledProperty.get()); c.insertBlockAfter(el.createFrame(getEditor())); getParentCanvas().removeBlock(this); } })); return r; } @Override public EditableSlot getErrorShowRedirect() { return null; } @Override protected FrameContentRow makeHeader(String stylePrefix) { return new MethodHeaderRow(this, stylePrefix) { @Override protected EditableSlot getSlotAfterParams() { return throwsPane.getTypeSlots().findFirst().orElse(null); } @Override protected EditableSlot getSlotBeforeParams() { return access; } }; } @Override public boolean tryRestoreTo(CodeElement codeElement) { if (codeElement instanceof ConstructorElement) { ConstructorElement ce = (ConstructorElement)codeElement; if (this.element.hasDelegate() && !ce.hasDelegate()) { removeSuperThis(); } if (!this.element.hasDelegate() && ce.hasDelegate()) { addSuperThis(new SuperThisFragment(ce.getDelegate()), new SuperThisParamsExpressionFragment(ce.getDelegateParams(), ce.getDelegateParamsJava())); } if (this.element.hasDelegate() && ce.hasDelegate()) { restoreDelegate(ce); } restoreDetails(ce); return true; } return false; } private void restoreDelegate(ConstructorElement ce) { if (!superThis.getValue(null).equals(ce.getDelegate())) { superThis.setValue(ce.getDelegate()); } if (!superThisParams.getText().equals(ce.getDelegateParams())) { superThisParams.setText(ce.getDelegateParams()); } } @Override public boolean focusWhenJustAdded() { getCanvas().getFirstCursor().requestFocus(); return true; } @Override protected DoubleExpression tweakOpeningCurlyY() { if (callRow == null) { return super.tweakOpeningCurlyY(); } else { return callRow.flowPaneHeight().negate(); } } @Override @OnThread(Tag.FXPlatform) public void setView(View oldView, View newView, SharedTransition animate) { super.setView(oldView, newView, animate); if (callRow != null) { if (newView == View.JAVA_PREVIEW) { double maxAmount = getCanvas().getCurlyBracketHeight(); JavaFXUtil.addChangeListener(animate.getProgress(), t -> { callRow.getNode().setTranslateY(t.doubleValue() * maxAmount); }); callRow.applyCss(); FXRunnable setPad = () -> getCanvas().setTopOutsideBorderBackgroundPadding(Optional.of(-2 + callRow.getSceneBounds().getHeight())); setPad.run(); animate.addOnStopped(setPad); } else if (oldView == View.JAVA_PREVIEW) { getCanvas().setTopOutsideBorderBackgroundPadding(Optional.empty()); double orig = callRow.getNode().getTranslateY(); JavaFXUtil.addChangeListener(animate.getOppositeProgress(), t -> { callRow.getNode().setTranslateY(t.doubleValue() * orig); }); } if (newView.isBirdseye() || oldView.isBirdseye()) { animate.getProgress().addListener((prop, oldVal, newVal) -> { if (oldVal.doubleValue() < 0.5 && newVal.doubleValue() >= 0.5) { callRow.setVisible(!newView.isBirdseye()); } }); } } } }

.   ConstructorFrame
.   HeaderItem>>observableArrayList
.   ConstructorFrame
.   getFactory
.   createBlock
.   getBlockClass
.   regenerateCode
.   getCode
.   addSuperThis
.   backspaceAtStart
.   removeSuperThis
.   getAvailableExtensions
.   getContextOperations
.   getErrorShowRedirect
.   makeHeader
.   getSlotAfterParams
.   getSlotBeforeParams
.   tryRestoreTo
.   restoreDelegate
.   focusWhenJustAdded
.   tweakOpeningCurlyY
.   setView




432 neLoCode + 5 LoComm