package bluej.stride.framedjava.slots;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import javafx.application.Platform;

import bluej.stride.framedjava.ast.ExpressionSlotFragment;
import bluej.stride.framedjava.ast.JavaFragment;
import bluej.stride.framedjava.ast.Parser;
import bluej.stride.framedjava.ast.links.PossibleLink;
import bluej.stride.framedjava.ast.links.PossibleMethodUseLink;
import bluej.stride.framedjava.ast.links.PossibleTypeLink;
import bluej.stride.framedjava.ast.links.PossibleVarLink;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.generic.InteractionManager;
import bluej.utility.javafx.FXConsumer;
import bluej.utility.javafx.JavaFXUtil;
import threadchecker.OnThread;
import threadchecker.Tag;


| Created by neil on 22/05/2016. | public class InfixExpression extends InfixStructured<ExpressionSlot<?>, InfixExpression>{
| Keeps track of whether we have queued a task to update the prompts: | private boolean queuedUpdatePrompts; private InfixExpression(InteractionManager editor, ExpressionSlot<?> slot, String initialContent, BracketedStructured wrapper, StructuredSlot.ModificationToken token, Character... closingChars) { super(editor, slot, initialContent, wrapper, token, closingChars); } InfixExpression(InteractionManager editor, ExpressionSlot<?> slot, StructuredSlot.ModificationToken token) { super(editor, slot, token); } | Is this string an operator | p.public static boolean isExpressionOperator(String s) { switch (s) { case "+": case "-": case "*": case "/": case "==": case "!=": case ">": case ">=": case "<=": case "<": case "%": case "&": case "&&": case "|": case "||": case "^": case "~": case "!": case ".": case "..": case "<:": case ",": case "<<": case ">>": case ">>>": case "->": case "::": return true; default: return false; } } @Override p.public boolean isOperator(String s) { return isExpressionOperator(s); }
| Does the given character form a one-character operator, or begin a multi-character operator | p.public static boolean beginsExpressionOperator(char c) { switch (c) { case '+': case '-': case '*': case '/': case '=': case '!': case '>': case '<': case '%': case '&': case '|': case '^': case '~': case '.': case ',': case ':': return true; default: return false; } } @Override p.public boolean beginsOperator(char c) { return beginsExpressionOperator(c); }
| Can this operator be unary | @Override p.public boolean canBeUnary(String s) { if (s == null) return false; switch (s) { case "+": case "-": case "~": case "!": case "new ": return true; default: return false; } } @Override protected boolean isOpeningBracket(char c) { return c == '(' || c == '[' || c == '{'; } @Override protected boolean isClosingBracket(char c) { return c == ')' || c == ']' || c == '}'; } @Override protected boolean isDisallowed(char c) { return c == ';'; } @Override p.public InfixExpression newInfix(InteractionManager editor, ExpressionSlot<?> slot, String initialContent, BracketedStructured<?, ExpressionSlot<?>> wrapper, StructuredSlot.ModificationToken token, Character... closingChars) { return new InfixExpression(editor, slot, initialContent, wrapper, token, closingChars); } @OnThread(Tag.FXPlatform) void treatAsConstructorParams_updatePrompts() { List<StructuredSlotField> params = getSimpleParameters(); if (params.stream().allMatch(p -> p == null)) return; slot.withParamNamesForConstructor( poss -> setPromptsFromParamNames(poss)); } @OnThread(Tag.FXPlatform) p.public void treatAsParams_updatePrompts(String methodName, CaretPos absPosOfMethodName) { List<StructuredSlotField> params = getSimpleParameters(); if (params.stream().allMatch(p -> p == null)) return; if (slot == null) return; slot.withParamNamesForPos(absPosOfMethodName, methodName, poss -> setPromptsFromParamNames(poss)); }
| A callback called when we have fetched information on parameter names, and want | to use it to update the prompts for method parameters. | | @param possibilities This is the list of possible parameters. If there is a single | method of that name, possibilities will be a singleton list, with | the content being parameter names, e.g. | Arrays.asList(Arrays.asList("x", "y")) for setLocation(int x, int y) |* If possibilities is empty, there are no methods found. * If possibilities is not size 1, there are multiple overloads for that name. @OnThread(Tag.FXPlatform) private void setPromptsFromParamNames(List<List<String>> possibilities) { List<StructuredSlotField> curParams = getSimpleParameters(); int curArity; if (curParams.size() == 1 && curParams.get(0) != null && curParams.get(0).isEmpty()) curArity = 0; else{ curArity = curParams.size(); } boolean arityFlexible = curParams.stream().allMatch(f -> f != null && f.isEmpty()); List<List<String>> matchedPoss = possibilities.stream() .filter(ps -> arityFlexible || ps.size() == curArity) .sorted(Comparator.comparing(List::size)) .collect(Collectors.toList()); if (matchedPoss.size() != 1) { if (arityFlexible && !isEmpty()) { modification(this::blank); } curParams.stream().filter(f -> f != null).forEach(f -> f.setPromptText("")); } else { List<String> match = matchedPoss.get(0); if (arityFlexible && match.size() != curArity) { boolean wasFocused = isFocused(); modificationPlatform(token -> { blank(token); for (int i = 0; i < match.size() - 1; i++) { insertChar(getEndPos(), ',', false, token); } }); curParams = getSimpleParameters(); if (wasFocused) getFirstField().requestFocus(); } for (int i = 0; i < match.size(); i++) { String prompt = match.get(i); if (prompt == null || Parser.isDummyName(prompt)) prompt = ""; if (i < curParams.size() && curParams.get(i) != null) curParams.get(i).setPromptText(prompt); } } } @OnThread(Tag.FXPlatform) private void updatePromptsInMethodCalls() { queuedUpdatePrompts = false; for (int i = 0; i < fields.size(); i++) { if (i < fields.size() - 1 && fields.get(i) instanceof StructuredSlotField && !fields.get(i).isFieldAndEmpty() && fields.get(i + 1) instanceof BracketedStructured && ((BracketedStructured)fields.get(i+1)).getOpening() == '(' ) { BracketedStructured bracketedParams = (BracketedStructured) fields.get(i + 1); CaretPos absPos = absolutePos(new CaretPos(i, new CaretPos(0, null))); ((InfixExpression)bracketedParams.getContent()).treatAsParams_updatePrompts(fields.get(i).getCopyText(null, null), absPos); } } }
| Queues a call to update prompts in method calls after modification has finished. Will not | queue more than one such call at any time. | void queueUpdatePromptsInMethodCalls() { if (!queuedUpdatePrompts && slot != null && Platform.isFxApplicationThread()) { queuedUpdatePrompts = true; slot.afterCurrentModification(this::updatePromptsInMethodCalls); } } @Override @OnThread(Tag.FXPlatform) public void calculateTooltipFor(StructuredSlotField expressionSlotField, FXConsumer<String> handler) { int slotIndex = fields.indexOf(expressionSlotField); if (!expressionSlotField.getText().equals("") && slotIndex + 1 < fields.size() && fields.get(slotIndex + 1) instanceof BracketedStructured) { CaretPos relPos = new CaretPos(slotIndex, new CaretPos(0, null)); slot.withMethodHint(absolutePos(relPos), fields.get(slotIndex).getCopyText(null, null), methodHints -> { if (methodHints.size() == 1) { handler.accept(methodHints.get(0)); } else { handler.accept(""); } }); } else if (parent != null || slot.isConstructorParams()) { int paramIndex = 0; int totalParams = 1; for (int i = 0; i < operators.size(); i++) { if (operators.get(i) != null && operators.get(i).getCopyText().equals(",")) { totalParams += 1; if (i < slotIndex) paramIndex += 1; } } if (parent != null) parent.getParent().withTooltipForParam(parent, paramIndex, handler); else { final int finalParamIndex = paramIndex; slot.withParamHintsForConstructor(totalParams, conHints -> { if (conHints.size() == 1 && finalParamIndex < conHints.get(0).size()) { handler.accept(conHints.get(0).get(finalParamIndex)); } }); } } else { handler.accept(""); } } @OnThread(Tag.FXPlatform) public void withTooltipForParam(BracketedStructured bracketedExpression, int paramPos, FXConsumer<String> handler) { int expIndex = fields.indexOf(bracketedExpression); if (fields.get(expIndex - 1).getCopyText(null, null).equals("")) { handler.accept(""); } else { CaretPos relPos = new CaretPos(expIndex - 1, new CaretPos(0, null)); slot.withParamHintsForPos(absolutePos(relPos), fields.get(expIndex - 1).getCopyText(null, null), paramHints -> { if (paramHints.size() == 1) { if (paramPos < paramHints.get(0).size()) { handler.accept(paramHints.get(0).get(paramPos)); return; } } handler.accept(""); }); } } @Override protected StructuredSlotField makeNewField(String content, boolean stringLiteral) { StructuredSlotField field = super.makeNewField(content, stringLiteral); JavaFXUtil.addChangeListener(field.textProperty(), t -> queueUpdatePromptsInMethodCalls()); return field; } public List findLinks(Optional<Character> surroundingBracket, Map<String, CodeElement> vars, Function<Integer, JavaFragment.PosInSourceDoc> posCalculator, int offset) { final List<PossibleLink> r = new ArrayList<>(); int cur = 0; int beginningSlot = cur; int endSlot = cur; int endLength = -1; String curOperand = ""; while (cur < fields.size()) { if (fields.get(cur) instanceof StructuredSlotField) { final StructuredSlotField expressionSlotField = (StructuredSlotField) fields.get(cur); if (expressionSlotField.getText().equals("class") && curOperand.endsWith(".")) { r.add(new PossibleTypeLink(curOperand.substring(0, curOperand.length() - 1), offset+caretPosToStringPos(new CaretPos(beginningSlot, new CaretPos(0, null)), false), offset+caretPosToStringPos(new CaretPos(endSlot, new CaretPos(endLength, null)), false), getSlot())); } if (curOperand.equals("")) beginningSlot = cur; curOperand += expressionSlotField.getText(); endSlot = cur; endLength = expressionSlotField.getText().length(); if (cur < operators.size() && operators.get(cur) == null) { } else if (cur < operators.size() && operators.get(cur).get().equals(".")) { curOperand += "."; } else { if (cur == operators.size() && beginningSlot == 0 && surroundingBracket.isPresent() && surroundingBracket.get() == '(') { r.add(new PossibleTypeLink(curOperand, offset+caretPosToStringPos(new CaretPos(beginningSlot, new CaretPos(0, null)), false), offset+caretPosToStringPos(new CaretPos(endSlot, new CaretPos(endLength, null)), false), getSlot())); } if (cur == beginningSlot) { if (vars != null) { CodeElement el = vars.get(curOperand); if (el != null) r.add(new PossibleVarLink(curOperand, el, offset+caretPosToStringPos(new CaretPos(beginningSlot, new CaretPos(0, null)), false), offset+caretPosToStringPos(new CaretPos(endSlot, new CaretPos(endLength, null)), false), getSlot())); } } curOperand = ""; } } else if (fields.get(cur) instanceof BracketedStructured) { StructuredSlotField prev = (StructuredSlotField) fields.get(cur - 1); int innerOffset = 1 + offset + caretPosToStringPos(new CaretPos(cur - 1, new CaretPos(prev.getText().length(), null)), false); BracketedStructured<InfixExpression, ExpressionSlot<?>> be = (BracketedStructured<InfixExpression, ExpressionSlot<?>>)fields.get(cur); r.addAll(be.getContent().findLinks(Optional.of(be.getOpening()), vars, posCalculator, innerOffset)); if (!curOperand.equals("") && be.getOpening() == '(') { final int endSlotFinal = endSlot; r.add(new PossibleMethodUseLink(curOperand.substring(curOperand.indexOf(".") + 1), be.getContent().getSimpleParameters().size(), () -> posCalculator.apply(offset+caretPosToStringPos(new CaretPos(endSlotFinal, new CaretPos(0, null)), true)), offset+caretPosToStringPos(new CaretPos(endSlot, new CaretPos(0, null)), false), offset+caretPosToStringPos(new CaretPos(endSlot, new CaretPos(endLength, null)), false), getSlot())); } } cur += 1; } return r; } @Override protected boolean supportsFloatLiterals() { return true; } }

.   InfixExpression
.   isExpressionOperator
.   isOperator
.   beginsExpressionOperator
.   beginsOperator
.   canBeUnary
.   isOpeningBracket
.   isClosingBracket
.   isDisallowed
.   newInfix
.   treatAsConstructorParams_updatePrompts
.   treatAsParams_updatePrompts
.   setPromptsFromParamNames
.   updatePromptsInMethodCalls
.   queueUpdatePromptsInMethodCalls
.   calculateTooltipFor
.   withTooltipForParam
.   makeNewField
.   findLinks
.   supportsFloatLiterals




566 neLoCode + 13 LoComm