protected void doProcessAutoDetect(
      String sql,
      Map<String, String> propertyNameTypeMap,
      Map<String, String> propertyNameOptionMap,
      Set<String> autoDetectedPropertyNameSet,
      Node node) {
    // only bind variable comment is supported
    // because simple specification is very important here
    if (node instanceof BindVariableNode) {
      final BindVariableNode bindNode = (BindVariableNode) node;
      processAutoDetectBindNode(
          sql, propertyNameTypeMap, propertyNameOptionMap, autoDetectedPropertyNameSet, bindNode);
    } else if (node instanceof IfNode) {
      final IfNode ifNode = (IfNode) node;
      doProcessAutoDetectIfNode(sql, propertyNameTypeMap, propertyNameOptionMap, ifNode);

      // process alternate boolean methods
      // which is supported with auto-detect
      doProcessAlternateBooleanMethodIfNode(sql, ifNode);
    } else if (node instanceof ForNode) {
      final ForNode forNode = (ForNode) node;
      doProcessAutoDetectForNode(sql, propertyNameTypeMap, propertyNameOptionMap, forNode);
    }
    for (int i = 0; i < node.getChildSize(); i++) {
      final Node childNode = node.getChild(i);

      // recursive call
      doProcessAutoDetect(
          sql, propertyNameTypeMap, propertyNameOptionMap, autoDetectedPropertyNameSet, childNode);
    }
  }
 protected DetectedPropertyInfo analyzeForNodeElementType(Node node, String propertyName) {
   if (isPmCommentNestedProperty(propertyName) || isPmCommentMethodCall(propertyName)) {
     return null;
   }
   final DfLanguageGrammar grammar = getLanguageGrammar();
   DetectedPropertyInfo detected = null;
   for (int i = 0; i < node.getChildSize(); i++) {
     final Node childNode = node.getChild(i);
     if (childNode instanceof BindVariableNode) {
       final BindVariableNode bindNode = (BindVariableNode) childNode;
       final String expression = bindNode.getExpression();
       if (!isPmCommentEqualsCurrent(expression)) {
         continue;
       }
       if (isPmCommentNestedProperty(expression) || isPmCommentMethodCall(expression)) {
         continue;
       }
       // /*#current*/ here
       final String testValue = bindNode.getTestValue();
       if (testValue == null) {
         continue;
       }
       final String propertyType = derivePropertyTypeFromTestValue(testValue);
       final String propertyOption = derivePropertyOptionFromTestValue(testValue);
       if (Srl.is_NotNull_and_NotTrimmedEmpty(propertyType)) {
         detected = new DetectedPropertyInfo();
         final String generic = grammar.buildGenericOneClassHint(propertyType);
         detected.setPropertyType("List" + generic);
         detected.setPropertyOption(propertyOption);
       }
     } else if (childNode instanceof ForNode) {
       final ForNode nestedNode = (ForNode) childNode;
       final String expression = nestedNode.getExpression();
       if (!isPmCommentStartsWithCurrent(expression)) {
         continue;
       }
       // /*FOR #current.xxx*/ here
       final String nestedForPropName = substringPmCommentCurrentRear(expression);
       detected = analyzeForNodeElementType(nestedNode, nestedForPropName); // recursive call
       if (detected != null) {
         final String generic = grammar.buildGenericOneClassHint(detected.getPropertyType());
         detected.setPropertyType("List" + generic);
       }
     } else if (childNode instanceof ScopeNode) { // IF, Begin, First, ...
       detected = analyzeForNodeElementType(childNode, propertyName); // recursive call
     }
     if (detected != null) {
       break;
     }
   }
   if (detected == null) {
     return null;
   }
   return detected;
 }