package bluej.stride.framedjava.ast;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import bluej.parser.lexer.JavaTokenTypes;
import bluej.stride.framedjava.elements.LocatableElement.LocationMap;
import bluej.stride.framedjava.errors.UnknownTypeError;
import bluej.stride.generic.AssistContentThreadSafe;
import bluej.utility.javafx.FXPlatformConsumer;
import javafx.application.Platform;
import bluej.parser.JavaParser;
import bluej.parser.lexer.LocatableToken;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.errors.DirectSlotError;
import bluej.stride.framedjava.errors.SyntaxCodeError;
import bluej.stride.framedjava.errors.UndeclaredVariableInExpressionError;
import bluej.stride.framedjava.errors.UndeclaredVariableLvalueError;
import bluej.stride.framedjava.errors.UnneededSemiColonError;
import bluej.stride.framedjava.frames.AssignFrame;
import bluej.stride.framedjava.slots.ExpressionSlot;
import bluej.stride.generic.InteractionManager;
import bluej.utility.Utility;
import threadchecker.OnThread;
import threadchecker.Tag;
public abstract class ExpressionSlotFragment
extends StructuredSlotFragment{
private ExpressionSlot slot;
private final List<LocatableToken> plains = new ArrayList<>();
private List<LocatableToken> curCompound = null;
private final List<List<LocatableToken>> compounds = new ArrayList<>();
private final List<List<LocatableToken>> types = new ArrayList<>();
private Map<String, CodeElement> vars;
private AssignFrame assignmentLHSParent;
@OnThread(Tag.FXPlatform)
public ExpressionSlotFragment(String content, String javaCode, ExpressionSlot slot)
{
super(content, javaCode);
this.slot = slot;
Parser.parseAsExpression(new JavaParser(new StringReader(wrapForParse(this.getJavaCode())), false)
{
boolean ignoreNext = false;
@Override
protected void gotIdentifier(LocatableToken token)
{
if (!ignoreNext)
plains.add(unwrapForParse(token));
ignoreNext = false;
}
@Override
protected void gotIdentifierEOF(LocatableToken token)
{
gotIdentifier(token);
}
@Override
protected void gotArrayTypeIdentifier(LocatableToken token)
{
}
@Override
protected void gotParentIdentifier(LocatableToken token)
{
}
@Override
protected void gotBinaryOperator(LocatableToken token)
{
if (token.getType() == JavaTokenTypes.METHOD_REFERENCE)
ignoreNext = true;
}
@Override
protected void gotCompoundIdent(LocatableToken token)
{
if (curCompound != null)
throw new IllegalStateException();
curCompound = new ArrayList<>();
curCompound.add(token);
}
@Override
protected void gotCompoundComponent(LocatableToken token)
{
if (curCompound == null || curCompound.isEmpty())
throw new IllegalStateException();
curCompound.add(token);
}
@Override
protected void gotMemberAccess(LocatableToken token)
{
}
@Override
protected void completeCompoundValue(LocatableToken token)
{
compounds.add(finishCompound(token));
}
private List finishCompound(LocatableToken token)
{
if (curCompound == null || curCompound.isEmpty())
throw new IllegalStateException();
curCompound.add(token);
List<LocatableToken> r = Utility.mapList(curCompound, ExpressionSlotFragment.this::unwrapForParse);
curCompound = null;
return r;
}
@Override
protected void completeCompoundClass(LocatableToken token)
{
types.add(finishCompound(token));
}
@Override
protected void gotTypeSpec(List<LocatableToken> tokens)
{
types.add(Utility.mapList(tokens, ExpressionSlotFragment.this::unwrapForParse));
}
});
}
public ExpressionSlotFragment(String content, String javaCode)
{
this(content, javaCode, null);
}
public ExpressionSlotFragment(ExpressionSlotFragment f)
{
this(f.content, f.getJavaCode());
}
@Override
public String getJavaCode(Destination dest, ExpressionSlot<?> completing, Parser.DummyNameGenerator dummyNameGenerator)
{
if (!dest.substitute() || slot == completing || (getJavaCode() != null && Parser.parseableAsExpression(wrapForParse(getJavaCode()))))
return getJavaCode();
else{
return "0!=true";
}
}
@Override
public ExpressionSlot getSlot()
{
return slot;
}
public void registerSlot(ExpressionSlot slot)
{
if (this.slot == null)
this.slot = slot;
}
| Returns false if this expression can be empty and still valid for compilation,
| or true if this expression is required for compilation
| @return
|
protected abstract boolean isRequired();
protected String wrapForParse(String orig)
{
return orig;
}
protected LocatableToken unwrapForParse(LocatableToken token)
{
return token;
}
@Override
@OnThread(Tag.FXPlatform)
public Stream findEarlyErrors()
{
if (content != null && content.endsWith(";"))
return Stream.of(new UnneededSemiColonError(this, () -> getSlot().setText(content.substring(0, content.length() - 1))));
else if (content != null && content.isEmpty() && isRequired())
return Stream.of(new SyntaxCodeError(this, "Expression cannot be empty"));
else if (content == null || !Parser.parseableAsExpression(wrapForParse(getJavaCode())))
return Stream.of(new SyntaxCodeError(this, "Invalid expression"));
else{ return Stream.empty();
}
}
@Override
public Future> findLateErrors(InteractionManager editor, CodeElement parent, LocationMap rootPathMap)
{
CompletableFuture<List<DirectSlotError>> f = new CompletableFuture<>();
Platform.runLater(() -> ASTUtility.withLocalsParamsAndFields(parent, editor, getPosInSourceDoc(), includeDirectDecl(), vars -> {
this.vars = vars;
List<DirectSlotError> undeclaredVarErrors = plains.stream().map(identToken ->
{
if (!vars.containsKey(identToken.getText()))
{
if (assignmentLHSParent != null && identToken.getText().equals(getJavaCode()))
{
return new UndeclaredVariableLvalueError(this, assignmentLHSParent, vars.keySet());
}
return new UndeclaredVariableInExpressionError(this, identToken.getText(), identToken.getColumn() - 1,
identToken.getColumn() - 1 + identToken.getLength(), slot, vars.keySet());
}
return null;
}).filter(x -> x != null).collect(Collectors.toList());
editor.withTypes(availableTypes -> {
Stream<DirectSlotError> unknownTypeErrors = types.stream().filter(t -> t.size() == 1).map(t -> t.get(0)).map(token -> {
String typeName = token.getText();
if (availableTypes.containsKey(typeName))
{
return null;
}
int startPosInSlot = token.getColumn() - 1;
int endPosInSlot = token.getColumn() - 1 + token.getLength();
FXPlatformConsumer<String> replace =
s -> slot.replace(startPosInSlot, endPosInSlot, true, s);
return (DirectSlotError) new UnknownTypeError(this, typeName, replace, editor, availableTypes.values().stream(), editor.getImportSuggestions().values().stream().flatMap(Collection::stream)) {
@Override
public int getStartPosition()
{
return startPosInSlot;
}
@Override
public int getEndPosition()
{
return endPosInSlot;
}
};
}).filter(x -> x != null);
f.complete(Stream.concat(undeclaredVarErrors.stream(), unknownTypeErrors).peek(e -> e.recordPath(rootPathMap.locationFor(this))).collect(Collectors.toList()));
});
}));
return f;
}
| Whether to include our parent's declarations when looking for variables. You usually
| don't want to do this. For example, a declared variable in a var frame is not in scope
| in the initialisation expression. However, this is needed for a special case with constructors,
| where a constructor's params are in scope for the super/this line, which is a direct child.
| (For other method-related items, the method is a grandparent, e.g. parent of assignment which
| is parent of expression, so we still don't need to include the *parent*)
|
protected boolean includeDirectDecl()
{
return false;
}
public void markAssignmentLHS(AssignFrame parent)
{
assignmentLHSParent = parent;
}
public Map getVars()
{
return vars;
}
}
top,
use,
map,
abstract class ExpressionSlotFragment
. ExpressionSlotFragment
. gotIdentifier
. gotIdentifierEOF
. gotArrayTypeIdentifier
. gotParentIdentifier
. gotBinaryOperator
. gotCompoundIdent
. gotCompoundComponent
. gotMemberAccess
. completeCompoundValue
. finishCompound
. completeCompoundClass
. gotTypeSpec
. ExpressionSlotFragment
. ExpressionSlotFragment
. getJavaCode
. getSlot
. registerSlot
. isRequired
. wrapForParse
. unwrapForParse
. findEarlyErrors
. findLateErrors
. getStartPosition
. getEndPosition
. includeDirectDecl
. markAssignmentLHS
. getVars
367 neLoCode
+ 9 LoComm