package bluej.stride.framedjava.frames;

import bluej.Config;
import bluej.editor.stride.BirdseyeManager;
import bluej.parser.AssistContent.CompletionKind;
import bluej.parser.AssistContent.ParamInfo;
import bluej.parser.entity.EntityResolver;
import bluej.stride.framedjava.ast.AccessPermission;
import bluej.stride.framedjava.ast.JavadocUnit;
import bluej.stride.framedjava.ast.NameDefSlotFragment;
import bluej.stride.framedjava.ast.TypeSlotFragment;
import bluej.stride.framedjava.elements.ClassElement;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.elements.ImportElement;
import bluej.stride.framedjava.elements.NormalMethodElement;
import bluej.stride.framedjava.slots.TypeSlot;
import bluej.stride.generic.*;
import bluej.stride.generic.ExtensionDescription.ExtensionSource;
import bluej.stride.operations.CopyFrameAsImageOperation;
import bluej.stride.operations.CopyFrameAsJavaOperation;
import bluej.stride.operations.CopyFrameAsStrideOperation;
import bluej.stride.operations.CustomFrameOperation;
import bluej.stride.operations.FrameOperation;
import bluej.stride.slots.EditableSlot;
import bluej.stride.slots.EditableSlot.MenuItemOrder;
import bluej.stride.slots.Focus;
import bluej.stride.slots.HeaderItem;
import bluej.stride.slots.Implements;
import bluej.stride.slots.SlotLabel;
import bluej.stride.slots.TriangleLabel;
import bluej.utility.Utility;
import bluej.utility.javafx.FXConsumer;
import bluej.utility.javafx.FXPlatformConsumer;
import bluej.utility.javafx.FXRunnable;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.util.Duration;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ClassFrame extends TopLevelDocumentMultiCanvasFrame<ClassElement>{    
   private final SlotLabel abstractLabel = new SlotLabel("abstract");
   
   private final SimpleBooleanProperty focusHasBeenInNameOrExtends;
   
   private BooleanProperty abstractModifier = new SimpleBooleanProperty(false);

   
   private final TypeSlot extendsSlot;
   
   private final SimpleBooleanProperty showingExtends;
   
   private final TriangleLabel inheritedLabel;
   
   private final BooleanBinding showInheritedToggle;
   
   private final ObservableList<InheritedCanvas> extendsInheritedCanvases = FXCollections.observableArrayList(); 

   
   private final Implements implementsSlot;

   
   private final ReadOnlyBooleanProperty focusInName;
   
   private final ReadOnlyBooleanProperty focusInExtends;
   
   private final BooleanBinding focusInNameOrExtends;
   
   private Map<String, List<AssistContentThreadSafe>> curMembersByClass = Collections.emptyMap();

   
   private final FrameCanvas constructorsCanvas;
   
   private final SlotLabel constructorsLabel;
   
   private final FrameContentRow constructorsLabelRow;

   
   public ClassFrame(InteractionManager editor, EntityResolver projectResolver, String packageName, List<ImportElement> imports,
                     
   JavadocUnit documentation, boolean abstractModifierParam, NameDefSlotFragment className, TypeSlotFragment extendsName,
                     
   List<TypeSlotFragment> implementsList, boolean enabled)
   {
      super(editor, projectResolver, "class", "class-", packageName, imports, documentation, className, enabled);

      this.abstractModifier.set(abstractModifierParam);
      JavaFXUtil.addChangeListener(this.abstractModifier, abs -> editor.modifiedFrame(this, false));

       
      showingExtends = new SimpleBooleanProperty(extendsName != null);
       
      SlotLabel extendsLabel = new SlotLabel("extends");
      JavaFXUtil.addStyleClass(extendsLabel, "class-extends-caption");
       
      extendsSlot = new TypeSlot(editor, this, this, getHeaderRow(), TypeSlot.Role.EXTENDS, "class-extends-");
      extendsSlot.addClosingChar(' ');
      extendsSlot.setSimplePromptText("parent class");
      if (extendsName != null) {
         extendsSlot.setText(extendsName);         
         }
        
       
      implementsSlot = new Implements(this, () -> {
           
      TypeSlot s = new TypeSlot(editor, this, this, getHeaderRow(), TypeSlot.Role.INTERFACE, "class-");
      s.setSimplePromptText("interface type");
           
      return s;         
      }, () -> getCanvases().findFirst().ifPresent(c -> c.getFirstCursor().requestFocus()), editor);
   implementsList.forEach(t -> implementsSlot.addTypeSlotAtEnd(t.getContent(), false));

       
   
   
   
   focusInName = JavaFXUtil.delay(paramName.effectivelyFocusedProperty(), Duration.ZERO, Duration.millis(100));
   focusInExtends = JavaFXUtil.delay(extendsSlot.effectivelyFocusedProperty(), Duration.ZERO, Duration.millis(100));
   focusInNameOrExtends = BooleanBinding.booleanExpression(focusInName).or(focusInExtends);
       
   focusHasBeenInNameOrExtends = new SimpleBooleanProperty(false);
   FXRunnable updateFocus = () ->
       {
      if (focusInNameOrExtends.get())
           {
         implementsSlot.ensureAtLeastOneSlot();
         focusHasBeenInNameOrExtends.set(true);             
         }
           

      else
           {
         if (!implementsSlot.focusedProperty().get())
         implementsSlot.clearIfSingleEmpty();
         focusHasBeenInNameOrExtends.set(focusHasBeenInNameOrExtends.get() && implementsSlot.focusedProperty().get());             
         }         
      };
   JavaFXUtil.addChangeListener(focusInNameOrExtends, f -> updateFocus.run());
   JavaFXUtil.addChangeListener(implementsSlot.focusedProperty(), f -> updateFocus.run());
   JavaFXUtil.addChangeListener(focusHasBeenInNameOrExtends, h -> showingExtends.set(!extendsSlot.isEmpty() || focusHasBeenInNameOrExtends.get()));
   extendsSlot.onTextPropertyChange(s -> showingExtends.set(!extendsSlot.isEmpty() || focusHasBeenInNameOrExtends.get()));

       
   inheritedLabel = new TriangleLabel(editor, t -> extendsInheritedCanvases.forEach(c -> c.grow(t)),
           
   t -> extendsInheritedCanvases.forEach(c -> c.shrink(t)), new SimpleBooleanProperty(false));
       
   
   inheritedLabel.setDisable(true);
   extendsInheritedCanvases.addListener((ListChangeListener<? super InheritedCanvas>) c ->
   inheritedLabel.setDisable(extendsInheritedCanvases.isEmpty())
       
   );
   JavaFXUtil.addChangeListenerPlatform(inheritedLabel.expandedProperty(),
   b -> editor.updateErrorOverviewBar());

       
   
   
   showInheritedToggle = showingExtends.and(Bindings.isNotEmpty(extendsInheritedCanvases));
   getHeaderRow().bindContentsConcat(FXCollections.<ObservableList<? extends HeaderItem>>observableArrayList(
   JavaFXUtil.listBool(abstractModifier, abstractLabel),
   FXCollections.observableArrayList(headerCaptionLabel),
   FXCollections.observableArrayList(paramName),
   JavaFXUtil.listBool(showingExtends, List.of(extendsLabel, extendsSlot)),
   JavaFXUtil.listBool(showInheritedToggle, inheritedLabel),
   implementsSlot.getHeaderItems()
           
   ));

   constructorsLabel = makeLabel(Config.getString("frame.editor.label.constructors"));
       
   this.constructorsCanvas = new FrameCanvas(editor, this, "class-");
       
   constructorsLabelRow = new FrameContentRow(this, constructorsLabel);
   addCanvas(constructorsLabelRow, constructorsCanvas, 1);     
   }

   
@Override
   
public Stream getPossiblyHiddenSlotsDirect()
   {
   return Stream.of(extendsSlot);     
   }

   
protected Frame findASpecialMethod()
   {        
   
   return getMethods().stream().filter(f -> f.getName().equals("act") && f.getParamsPane().isEmpty()).findFirst().orElse(null);     
   }

   
@Override
   
public synchronized void regenerateCode()
   {
   List<CodeElement> fields = getMembers(fieldsCanvas);
   List<CodeElement> constructors = getMembers(constructorsCanvas);
   List<CodeElement> methods = getMembers(methodsCanvas);
   List<ImportElement> imports = Utility.mapList(getMembers(importCanvas), e -> (ImportElement)e);
       
   element = new ClassElement(this, projectResolver, abstractModifier.get(), paramName.getSlotElement(),
   showingExtends.get() && !extendsSlot.getText().equals("") ? extendsSlot.getSlotElement() : null,
                   
   implementsSlot.getTypes(), fields, constructors, methods, new JavadocUnit(getDocumentation()),
   packageNameLabel == null ? null : packageNameLabel.getText(), imports, frameEnabledProperty.get());     
   }

   
@Override
@OnThread(value = Tag.Any, ignoreParent = true)
   
public synchronized ClassElement getCode()
   {        
   return element;     
   }

   
@Override
   
public List getContextOperations()
   {        
   ArrayList<FrameOperation> ops = new ArrayList<>();
       
   ops.add(new CopyFrameAsStrideOperation(editor));
       
   ops.add(new CopyFrameAsImageOperation(editor));
       
   ops.add(new CopyFrameAsJavaOperation(editor));
       
   ops.add(new CustomFrameOperation(getEditor(), "addRemoveAbstract", Arrays.asList(Config.getString("frame.class.toggle.abstract")),
   MenuItemOrder.TOGGLE_ABSTRACT, this, () ->  abstractModifier.set(!abstractModifier.get())));

   if (extendsSlot.isEmpty())
       {            
      ops.add(new CustomFrameOperation(getEditor(), "addExtends", Arrays.asList(Config.getString("frame.class.add.extends")),
      MenuItemOrder.TOGGLE_EXTENDS, this, () -> showAndFocusExtends()));         
      }
       

   else
       {            
      CustomFrameOperation op = new CustomFrameOperation(getEditor(), "removeExtends",
      Arrays.asList(Config.getString("frame.class.remove.extends.from").replace("$", extendsSlot.getText())),
      MenuItemOrder.TOGGLE_EXTENDS, this, () -> extendsSlot.setText(""));
      op.setWideCustomItem(true);
      ops.add(op);         
      }

       
   ops.add(new CustomFrameOperation(getEditor(), "addImplements", Arrays.asList(Config.getString("frame.class.add.implements")),
   MenuItemOrder.TOGGLE_IMPLEMENTS, this, () -> implementsSlot.addTypeSlotAtEnd("", true)));

   final List<TypeSlotFragment> types = implementsSlot.getTypes();
       
   for (int i = 0; i < types.size(); i++)
       {            
      final int index = i;
      TypeSlotFragment type = types.get(i);
           
      CustomFrameOperation removeOp = new CustomFrameOperation(getEditor(), "removeImplements",
      Arrays.asList(Config.getString("frame.class.remove.implements").replace("$", type.getContent())),
      MenuItemOrder.TOGGLE_IMPLEMENTS, this, () -> implementsSlot.removeIndex(index));
      removeOp.setWideCustomItem(true);
      ops.add(removeOp);         
      }

       
   return ops;     
   }

   

| Show the extends slot, and focus it. | private void showAndFocusExtends() { paramName.requestFocus(); extendsSlot.requestFocus(); } @Override public List getAvailableExtensions(FrameCanvas canvas, FrameCursor cursorInCanvas) { ExtensionDescription abstractExtension = null; ExtensionDescription implementsExtension = null; ExtensionDescription extendsExtension = null; if (fieldsCanvas.equals(canvas) || canvas == null) { abstractExtension = new ExtensionDescription(StrideDictionary.ABSTRACT_EXTENSION_CHAR, Config.getString("frame.class.toggle.abstract"), () -> abstractModifier.set(!abstractModifier.get()), true, ExtensionSource.INSIDE_FIRST, ExtensionSource.MODIFIER); implementsExtension = new ExtensionDescription(StrideDictionary.IMPLEMENTS_EXTENSION_CHAR, "Add implements declaration", () -> implementsSlot.addTypeSlotAtEnd("", true), true, ExtensionSource.INSIDE_FIRST, ExtensionSource.MODIFIER); if (!showingExtends.get()) { extendsExtension = new ExtensionDescription(StrideDictionary.EXTENDS_EXTENSION_CHAR, "Add extends declaration", () -> showAndFocusExtends(), true, ExtensionSource.INSIDE_FIRST, ExtensionSource.MODIFIER); } } return Utility.nonNulls(Arrays.asList(abstractExtension, extendsExtension, implementsExtension)); }
| | |private void removeExtends() | |{} showingExtends.set(false); | |paramClassName.requestFocus(Focus.RIGHT); | |extendsSlot.setText(""); editor.modifiedFrame(this, false); } */ @Override @OnThread(Tag.FXPlatform) public void saved() { if (extendsInheritedCanvases.isEmpty()) { updateInheritedItems(); } | |} | |private Comparator<String> getSuperClassComparator() | |{ | |return (a, b) -> { | |if (a == null || b == null) | |throw new IllegalArgumentException("Null strings for super-class names"); if (a.equals(b)) return 0; // Object is always last: if ("java.lang.Object".equals(a)) return 1; else if ("java.lang.Object".equals(b)) return -1; // Direct super-class is always first: if (extendsSlot.getText().equals(a)) return -1; else if (extendsSlot.getText().equals(b)) | |return 1; | |else{ return a.compareTo(b); | |} | |}; | |} | |@OnThread(Tag.FXPlatform) | |private void updateInheritedItems() | |{ | |// Add available frames: | |withInheritedItems(new HashSet<>(Arrays.asList(CompletionKind.FIELD, CompletionKind.METHOD)), membersByClass -> | |{ | |if (inheritedEquals(membersByClass, curMembersByClass)) | |{ | |// Same as before, so nothing to do: | |return; | |} | |extendsInheritedCanvases.forEach(c -> removeCanvas(c.canvas)); | |extendsInheritedCanvases.clear(); | |// We should probably sort by inheritance hierarchy, but for now we'll just go near-alphabetical | |List<String> classNames = membersByClass.keySet().stream().sorted(getSuperClassComparator()).collect(Collectors.toList()); | |// Add in reverse order: | |Collections.reverse(classNames); | |classNames.forEach(cls -> { | |InheritedCanvas section = new InheritedCanvas(this, editor, cls, classNames.size() == 1); | |// If triangle already folded in, make sure everything is collapsed: | |if (inheritedLabel.expandedProperty().get() == false) | |{ | |section.canvas.shrinkUsing(new ReadOnlyDoubleWrapper(0.0)); | |if (section.optionalCollapse != null) | |section.optionalCollapse.setVisible(false); | |if (section.precedingDividerLabel != null) | |section.precedingDividerLabel.shrinkInstantly(); | |} | |extendsInheritedCanvases.add(section); | |addCanvas(section.precedingDivider, section.canvas, 0); | |List<AssistContentThreadSafe> items = membersByClass.get(cls); | |List<AssistContentThreadSafe> methods = items.stream().filter(a -> a.getKind() == CompletionKind.METHOD).collect(Collectors.toList()); | |List<AssistContentThreadSafe> fields = items.stream().filter(a -> a.getKind() == CompletionKind.FIELD).collect(Collectors.toList()); | |//InheritedFrame inheritedFrame = new InheritedFrame(editor, cls, fields, methods); | |// Reversed as insertBlockBefore will cause them to be inserted in a reversed order. | |Collections.reverse(fields); | |fields.forEach(field -> | |section.canvas.insertBlockBefore(new InheritedFieldFrame(editor, | |AccessPermission.fromAccess(field.getAccessPermission()), field.getType(), field.getName()), | |section.canvas.getFirstCursor()) | |); | |// Reversed as insertBlockBefore will cause them to be inserted in a reversed order. | |Collections.reverse(methods); | |methods.forEach(method -> | |section.canvas.insertBlockBefore(new InheritedMethodFrame(editor, this, cls, | |AccessPermission.fromAccess(method.getAccessPermission()), method.getType(), method.getName(), method.getParams()), | |section.canvas.getFirstCursor()) | |); | |}); | |extendsInheritedCanvases.forEach(s -> s.canvas.getCursors().forEach(c -> c.getNode().setFocusTraversable(false))); | |curMembersByClass = membersByClass; | |}); | |} | |/** | Checks if two sets of inherited items are the same, in terms of the information they would | produce in the inherited canvas. | @param a One of the sets, mapping super-class name to items | @param b The other set | @return True if they are equal in that they would produce the same display in the inherited canvas. | private static boolean inheritedEquals(Map<String, List<AssistContentThreadSafe>> a, Map<String, List<AssistContentThreadSafe>> b) { if (a.size() != b.size()) return false; Set<String> ak = a.keySet(); Set<String> bk = b.keySet(); if (!ak.equals(bk)) return false; for (String k : ak) { List<AssistContentThreadSafe> av = a.get(k); List<AssistContentThreadSafe> bv = b.get(k); if (av.size() != bv.size()) return false; for (int i = 0; i < av.size(); i++) { AssistContentThreadSafe ax = av.get(i); AssistContentThreadSafe bx = bv.get(i); if (ax.getKind() != bx.getKind() || ax.getAccessPermission() != bx.getAccessPermission() || !ax.getName().equals(bx.getName()) || !ax.getType().equals(bx.getType())) return false; if (ax.getKind() == CompletionKind.METHOD) { List<ParamInfo> ap = ax.getParams(); List<ParamInfo> bp = bx.getParams(); if (ap.size() != bp.size()) return false; for (int j = 0; j < ap.size(); j++) { String aFormalName = ap.get(j).getFormalName(); String bFormalName = bp.get(j).getFormalName(); if ((aFormalName == null ? bFormalName != null : !aFormalName.equals(bFormalName)) || !ap.get(j).getQualifiedType().equals(bp.get(j).getQualifiedType())) return false; } } } } return true; } @OnThread(Tag.FXPlatform) public void withInheritedItems(Set<CompletionKind> kinds, FXPlatformConsumer<Map<String, List<AssistContentThreadSafe>>> handler) { editor.withAccessibleMembers(getCode().getPosInsideClass(), kinds, true, allMembers -> { HashMap<String, List<AssistContentThreadSafe>> methodsByClass = new HashMap<>(); for (AssistContentThreadSafe a : allMembers) { if (a.getDeclaringClass().equals(paramName.getText())) { continue; } if (methodsByClass.containsKey(a.getDeclaringClass())) { methodsByClass.get(a.getDeclaringClass()).add(a); } else { List<AssistContentThreadSafe> l = new ArrayList<>(); l.add(a); methodsByClass.put(a.getDeclaringClass(), l); } } Comparator<AssistContentThreadSafe> comparator = Comparator.comparing(AssistContentThreadSafe::getName) .thenComparing(ac -> ac.getParams() == null ? Collections.emptyList() : Utility.mapList(ac.getParams(), ParamInfo::getUnqualifiedType) , Utility.listComparator()); methodsByClass.forEach((k, v) -> v.sort(comparator)); handler.accept(methodsByClass); }); } @Override public BirdseyeManager prepareBirdsEyeView(SharedTransition animate) { final List<FrameCanvas> canvases = Arrays.asList(constructorsCanvas, methodsCanvas); int startingCanvas = -1; Frame startingFrame = null; while (startingFrame == null) { startingCanvas += 1; startingFrame = canvases.get(startingCanvas).getBlockContents().stream() .filter(f -> f instanceof CommentFrame).findFirst().orElse(null); } Node focusOwner = canvases.get(0).getNode().getScene().getFocusOwner(); canvasLoop: for (int i = 0; i < canvases.size(); i++) { for (Frame f : canvases.get(i).getBlockContents()) { if (!(f instanceof CommentFrame) && (nodeInside(focusOwner, (Parent)f.getNode()) || f.getCursorBefore().getNode() == focusOwner || (f.getCursorAfter().getFrameAfter() == null && f.getCursorAfter().getNode() == focusOwner))) { startingCanvas = i; startingFrame = f; break canvasLoop; } } } final int finalStartingCanvas = startingCanvas; final Frame finalStartingFrame = startingFrame; return new BirdseyeManager() { private int canvasIndex = finalStartingCanvas; private Frame frame = finalStartingFrame; @Override public Node getNodeForRectangle() { return frame.getNode(); } @Override public Node getNodeForVisibility() { return getHeaderNodeOf(frame); } private Frame getFrameAt(double sceneX, double sceneY) { for (FrameCanvas canvas : canvases) { for (Frame f : canvas.getBlockContents()) { Node n = f.getNode(); Point2D scene = n.localToScene(n.getBoundsInLocal().getMinX(), n.getBoundsInLocal().getMinY()); if (scene.getX() <= sceneX && sceneX < scene.getX() + n.getBoundsInLocal().getWidth() && scene.getY() <= sceneY && sceneY < scene.getY() + n.getBoundsInLocal().getHeight()) { return f; } } } return null; } @Override public boolean canClick(double sceneX, double sceneY) { return getFrameAt(sceneX, sceneY) != null; } @Override public FrameCursor getClickedTarget(double sceneX, double sceneY) { Frame f = getFrameAt(sceneX, sceneY); if (f != null) { return f.getFirstInternalCursor(); } else { return null; } } @Override public FrameCursor getCursorForCurrent() { return frame.getFirstInternalCursor(); } @Override public void up() { Frame candidate = canvases.get(canvasIndex).getFrameBefore( canvases.get(canvasIndex).getCursorBefore(frame)); int prospective = canvasIndex; while (candidate == null || candidate instanceof CommentFrame) { if (candidate == null) { prospective -= 1; if (prospective < 0) { candidate = null; break; } candidate = canvases.get(prospective).getFrameBefore( canvases.get(prospective).getLastCursor()); } else { candidate = canvases.get(prospective).getFrameBefore( canvases.get(prospective).getCursorBefore(candidate)); } } if (candidate != null && !(candidate instanceof CommentFrame)) { frame = candidate; canvasIndex = prospective; } } @Override public void down() { Frame candidate = canvases.get(canvasIndex).getFrameAfter( canvases.get(canvasIndex).getCursorAfter(frame)); int prospective = canvasIndex; while (candidate == null || candidate instanceof CommentFrame) { if (candidate == null) { prospective += 1; if (prospective >= canvases.size()) { candidate = null; break; } candidate = canvases.get(prospective).getFrameAfter( canvases.get(prospective).getFirstCursor()); } else { candidate = canvases.get(prospective).getFrameAfter( canvases.get(prospective).getCursorAfter(candidate)); } } if (candidate != null && !(candidate instanceof CommentFrame)) { frame = candidate; canvasIndex = prospective; } } }; } @Override public void addExtendsClassOrInterface(String className) { extendsSlot.setText(className); } @Override public void removeExtendsClass() { extendsSlot.setText(""); } @Override public void addImplements(String className) { implementsSlot.addTypeSlotAtEnd(className, false); } @Override public void removeExtendsOrImplementsInterface(String interfaceName) { List<TypeSlotFragment> implementsTypes = implementsSlot.getTypes(); for (int i = 0; i < implementsTypes.size(); i++) { if (implementsTypes.get(i).getContent().equals(interfaceName)) { implementsSlot.removeIndex(i); return; } } } private boolean nodeInside(Node target, Parent parent) { for (Node node : parent.getChildrenUnmodifiable()) { if (node == target) { return true; } p.public else if(node instanceof Parent) { if (nodeInside(target, (Parent) node)) { return true; } } } return false; } @Override public boolean canDoBirdseye() { return Stream.concat( constructorsCanvas.getBlockContents().stream(), methodsCanvas.getBlockContents().stream()) .anyMatch(f -> !(f instanceof CommentFrame)); } @Override public void addDefaultConstructor() { constructorsCanvas.getFirstCursor().insertBlockAfter(ConstructorFrame.getFactory().createBlock(editor)); } @Override public List getConstructors() { return constructorsCanvas.getBlocksSubtype(ConstructorFrame.class); } @Override public List getMethods() { return methodsCanvas.getBlocksSubtype(NormalMethodFrame.class); } public FrameCanvas getConstructorsCanvas() { return constructorsCanvas; } public void findMethod(String methodName, List<ParamInfo> params, FXConsumer<NormalMethodFrame> callback) { ClassElement el = getCode(); JavaFXUtil.runNowOrLater(() -> { Optional<NormalMethodFrame> method = el.streamMethods() .filter(e -> { if (!(e instanceof NormalMethodElement)) return false; NormalMethodElement m = (NormalMethodElement) e; return m.equalDeclaration(methodName, params, el); }).map(e -> ((NormalMethodElement) e).getFrame()) .findFirst(); callback.accept(method.orElse(null)); }); } @Override @OnThread(Tag.FXPlatform) public void setView(View oldView, View newView, SharedTransition animateProgress) { super.setView(oldView, newView, animateProgress); if (!extendsInheritedCanvases.isEmpty()) { if (newView != View.NORMAL) inheritedLabel.expandedProperty().set(false); } inheritedLabel.setVisible(newView == View.NORMAL); } @Override protected List getLabelRows() { return Arrays.asList(importRow, fieldsLabelRow, constructorsLabelRow, methodsLabelRow); } @Override protected List getCanvasLabels() { return Arrays.asList(importsLabel, fieldsLabel, constructorsLabel, methodsLabel); } @OnThread(Tag.FXPlatform) public void compiled() { JavaFXUtil.runAfterCurrent(() -> updateInheritedItems()); } @Override public Stream getPersistentCanvases() { List<FrameCanvas> extendsFrameCanvases = extendsInheritedCanvases.stream() .map(inheritedCanvas -> inheritedCanvas.canvas).collect(Collectors.toList()); return getCanvases().filter(canvas -> !extendsFrameCanvases.contains(canvas)); } @Override public FrameTypeCheck check(FrameCanvas canvas) { if (canvas == fieldsCanvas) return StrideDictionary.checkClassField(); else if (canvas == methodsCanvas) return StrideDictionary.checkClassMethod(); else if (canvas == constructorsCanvas) return StrideDictionary.checkConstructor(); else{ throw new IllegalStateException("Asking about canvas unknown to ClassFrame"); } } @Override public CanvasKind getChildKind(FrameCanvas c) { if (c == fieldsCanvas) return CanvasKind.FIELDS; else if (c == constructorsCanvas) return CanvasKind.CONSTRUCTORS; else if (c == methodsCanvas) return CanvasKind.METHODS; else{ return CanvasKind.STATEMENTS; } } @Override public void restore(ClassElement target) { paramName.setText(target.getName()); abstractModifier.set(target.isAbstract()); restoreExtends(target); implementsSlot.setTypes(target.getImplements()); importCanvas.restore(target.getImports(), editor); methodsCanvas.restore(target.getMethods(), editor); fieldsCanvas.restore(target.getFields(), editor); constructorsCanvas.restore(target.getConstructors(), editor); } private void restoreExtends(ClassElement target) { String targetExtends = target.getExtends(); if (targetExtends != null) { if (!extendsSlot.getText().equals(targetExtends)) { extendsSlot.setText(targetExtends); } } else if (showingExtends.get()) { extendsSlot.setText(""); } } @Override protected FrameContentRow makeHeader(String stylePrefix) { return new FrameContentRow(this, stylePrefix) { @Override public boolean focusRightEndFromNext() { focusHasBeenInNameOrExtends.set(true); implementsSlot.ensureAtLeastOneSlot(); Utility.findLast(implementsSlot.getTypeSlots()).get().requestFocus(Focus.RIGHT); return true; } }; } @Override @OnThread(Tag.FXPlatform) public boolean backspaceAtStart(FrameContentItem srcRow, HeaderItem src) { if (src == extendsSlot) { extendsSlot.setText(""); paramName.requestFocus(Focus.RIGHT); return false; } return super.backspaceAtStart(srcRow, src); } }

.   ClassFrame
.   HeaderItem>>observableArrayList
.   getPossiblyHiddenSlotsDirect
.   findASpecialMethod
.   regenerateCode
.   getCode
.   getContextOperations
.   showAndFocusExtends
.   getAvailableExtensions
.   inheritedEquals
.   withInheritedItems
.   prepareBirdsEyeView
.   getNodeForRectangle
.   getNodeForVisibility
.   getFrameAt
.   canClick
.   getClickedTarget
.   getCursorForCurrent
.   up
.   down
.   addExtendsClassOrInterface
.   removeExtendsClass
.   addImplements
.   removeExtendsOrImplementsInterface
.   nodeInside
.   if
.   canDoBirdseye
.   addDefaultConstructor
.   getConstructors
.   getMethods
.   getConstructorsCanvas
.   findMethod
.   setView
.   getLabelRows
.   getCanvasLabels
.   compiled
.   getPersistentCanvases
.   check
.   getChildKind
.   restore
.   restoreExtends
.   makeHeader
.   focusRightEndFromNext
.   backspaceAtStart




957 neLoCode + 76 LoComm