/**
  * Returns the (non-static) fields that have the invariant annotation and are not yet initialized
  * in a given store.
  */
 public List<VariableTree> getUninitializedInvariantFields(
     Store store,
     TreePath path,
     boolean isStatic,
     List<? extends AnnotationMirror> receiverAnnotations) {
   ClassTree currentClass = TreeUtils.enclosingClass(path);
   List<VariableTree> fields = InitializationChecker.getAllFields(currentClass);
   List<VariableTree> violatingFields = new ArrayList<>();
   AnnotationMirror invariant = getFieldInvariantAnnotation();
   for (VariableTree field : fields) {
     if (isUnused(field, receiverAnnotations)) {
       continue; // don't consider unused fields
     }
     VariableElement fieldElem = TreeUtils.elementFromDeclaration(field);
     if (ElementUtils.isStatic(fieldElem) == isStatic) {
       // Does this field need to satisfy the invariant?
       if (getAnnotatedType(field).hasEffectiveAnnotation(invariant)) {
         // Has the field been initialized?
         if (!store.isFieldInitialized(fieldElem)) {
           violatingFields.add(field);
         }
       }
     }
   }
   return violatingFields;
 }
    @Override
    public Void visitMemberSelect(MemberSelectTree tree, AnnotatedTypeMirror type) {
      if (TreeUtils.isFieldAccess(tree) && isUnderlyingTypeAValue(type)) {
        VariableElement elem = (VariableElement) InternalUtils.symbol(tree);
        Object value = elem.getConstantValue();
        if (value != null) {
          // compile time constant
          type.replaceAnnotation(
              resultAnnotationHandler(
                  type.getUnderlyingType(), Collections.singletonList(value), tree));
          return null;
        }
        if (ElementUtils.isStatic(elem) && ElementUtils.isFinal(elem)) {
          Element e = InternalUtils.symbol(tree.getExpression());
          if (e != null) {
            String classname = ElementUtils.getQualifiedClassName(e).toString();
            String fieldName = tree.getIdentifier().toString();
            value = evalutator.evaluateStaticFieldAccess(classname, fieldName, tree);
            if (value != null)
              type.replaceAnnotation(
                  resultAnnotationHandler(
                      type.getUnderlyingType(), Collections.singletonList(value), tree));
            return null;
          }
        }

        if (tree.getIdentifier().toString().equals("length")) {
          AnnotatedTypeMirror receiverType = getAnnotatedType(tree.getExpression());
          if (receiverType.getKind() == TypeKind.ARRAY) {
            AnnotationMirror arrayAnno = receiverType.getAnnotation(ArrayLen.class);
            if (arrayAnno != null) {
              // array.length, where array : @ArrayLen(x)
              List<Integer> lengths = ValueAnnotatedTypeFactory.getArrayLength(arrayAnno);
              type.replaceAnnotation(createNumberAnnotationMirror(new ArrayList<Number>(lengths)));
              return null;
            }
          }
        }
      }
      return null;
    }
    @Override
    public Void visitMethodInvocation(MethodInvocationTree tree, AnnotatedTypeMirror type) {
      if (isUnderlyingTypeAValue(type)
          && methodIsStaticallyExecutable(TreeUtils.elementFromUse(tree))) {
        // Get argument values
        List<? extends ExpressionTree> arguments = tree.getArguments();
        ArrayList<List<?>> argValues;
        if (arguments.size() > 0) {
          argValues = new ArrayList<List<?>>();
          for (ExpressionTree argument : arguments) {
            AnnotatedTypeMirror argType = getAnnotatedType(argument);
            List<?> values = getValues(argType, argType.getUnderlyingType());
            if (values.isEmpty()) {
              // values aren't known, so don't try to evaluate the
              // method
              return null;
            }
            argValues.add(values);
          }
        } else {
          argValues = null;
        }

        // Get receiver values
        AnnotatedTypeMirror receiver = getReceiverType(tree);
        List<?> receiverValues;

        if (receiver != null && !ElementUtils.isStatic(TreeUtils.elementFromUse(tree))) {
          receiverValues = getValues(receiver, receiver.getUnderlyingType());
          if (receiverValues.isEmpty()) {
            // values aren't known, so don't try to evaluate the
            // method
            return null;
          }
        } else {
          receiverValues = null;
        }

        // Evaluate method
        List<?> returnValues = evalutator.evaluteMethodCall(argValues, receiverValues, tree);
        AnnotationMirror returnType =
            resultAnnotationHandler(type.getUnderlyingType(), returnValues, tree);
        type.replaceAnnotation(returnType);
      }

      return null;
    }
 /**
  * Returns the (non-static) fields that have the invariant annotation and are initialized in a
  * given store.
  */
 public List<VariableTree> getInitializedInvariantFields(Store store, TreePath path) {
   // TODO: Instead of passing the TreePath around, can we use
   // getCurrentClassTree?
   ClassTree currentClass = TreeUtils.enclosingClass(path);
   List<VariableTree> fields = InitializationChecker.getAllFields(currentClass);
   List<VariableTree> initializedFields = new ArrayList<>();
   AnnotationMirror invariant = getFieldInvariantAnnotation();
   for (VariableTree field : fields) {
     VariableElement fieldElem = TreeUtils.elementFromDeclaration(field);
     if (!ElementUtils.isStatic(fieldElem)) {
       // Does this field need to satisfy the invariant?
       if (getAnnotatedType(field).hasEffectiveAnnotation(invariant)) {
         // Has the field been initialized?
         if (store.isFieldInitialized(fieldElem)) {
           initializedFields.add(field);
         }
       }
     }
   }
   return initializedFields;
 }
  // TODO: These are special cases for isRegex(String, int) and asRegex(String, int).
  // They should be replaced by adding an @EnsuresQualifierIf annotation that supports
  // specifying attributes.
  @Override
  public TransferResult<CFValue, CFStore> visitMethodInvocation(
      MethodInvocationNode n, TransferInput<CFValue, CFStore> in) {
    RegexClassicAnnotatedTypeFactory factory =
        (RegexClassicAnnotatedTypeFactory) analysis.getTypeFactory();
    TransferResult<CFValue, CFStore> result = super.visitMethodInvocation(n, in);

    // refine result for some helper methods
    MethodAccessNode target = n.getTarget();
    ExecutableElement method = target.getMethod();
    Node receiver = target.getReceiver();
    if (!(receiver instanceof ClassNameNode)) {
      return result;
    }
    ClassNameNode cn = (ClassNameNode) receiver;
    String receiverName = cn.getElement().toString();

    if (isRegexUtil(receiverName)) {
      if (ElementUtils.matchesElement(method, IS_REGEX_METHOD_NAME, String.class, int.class)) {
        // RegexUtil.isRegex(s, groups) method
        // (No special case is needed for isRegex(String) because of
        // the annotation on that method's definition.)

        CFStore thenStore = result.getRegularStore();
        CFStore elseStore = thenStore.copy();
        ConditionalTransferResult<CFValue, CFStore> newResult =
            new ConditionalTransferResult<>(result.getResultValue(), thenStore, elseStore);
        Receiver firstParam =
            FlowExpressions.internalReprOf(
                factory.getContext().getAnnotationProvider(), n.getArgument(0));

        // add annotation with correct group count (if possible,
        // regex annotation without count otherwise)
        Node count = n.getArgument(1);
        if (count instanceof IntegerLiteralNode) {
          IntegerLiteralNode iln = (IntegerLiteralNode) count;
          Integer groupCount = iln.getValue();
          AnnotationMirror regexAnnotation = factory.createRegexAnnotation(groupCount);
          thenStore.insertValue(firstParam, regexAnnotation);
        } else {
          AnnotationMirror regexAnnotation =
              AnnotationUtils.fromClass(factory.getElementUtils(), Regex.class);
          thenStore.insertValue(firstParam, regexAnnotation);
        }
        return newResult;

      } else if (ElementUtils.matchesElement(
          method, AS_REGEX_METHOD_NAME, String.class, int.class)) {
        // RegexUtil.asRegex(s, groups) method
        // (No special case is needed for asRegex(String) because of
        // the annotation on that method's definition.)

        // add annotation with correct group count (if possible,
        // regex annotation without count otherwise)
        AnnotationMirror regexAnnotation;
        Node count = n.getArgument(1);
        if (count instanceof IntegerLiteralNode) {
          IntegerLiteralNode iln = (IntegerLiteralNode) count;
          Integer groupCount = iln.getValue();
          regexAnnotation = factory.createRegexAnnotation(groupCount);
        } else {
          regexAnnotation = AnnotationUtils.fromClass(factory.getElementUtils(), Regex.class);
        }
        CFValue newResultValue =
            analysis.createSingleAnnotationValue(
                regexAnnotation, result.getResultValue().getType().getUnderlyingType());
        return new RegularTransferResult<>(newResultValue, result.getRegularStore());
      }
    }

    return result;
  };