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 extends PossibleLink> 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