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