protected void buildCustomizeEntityColumnInfo(
     StringBuilder logSb,
     String columnName,
     Column column,
     Table relatedTable,
     Column relatedColumn,
     String forcedJavaNatice) {
   final StringBuilder sb = new StringBuilder();
   sb.append(" ").append(column.isPrimaryKey() ? "*" : " ");
   sb.append(columnName);
   sb.append(" ");
   sb.append(column.getDbTypeExpression());
   final String columnSize = column.getColumnSize();
   if (Srl.is_NotNull_and_NotTrimmedEmpty(columnSize)) {
     sb.append("(").append(columnSize).append(")");
   }
   if (relatedColumn != null) {
     sb.append(" related to ").append(relatedTable.getTableDbName());
     sb.append(".").append(relatedColumn.getName());
   }
   if (Srl.is_NotNull_and_NotTrimmedEmpty(forcedJavaNatice)) {
     sb.append(" forced to ").append(forcedJavaNatice);
   }
   logSb.append(sb).append(ln());
 }
 protected void setupColumnComment(
     Map<String, DfColumnMeta> metaMap, String columnName, Column column) {
   final DfColumnMeta columnMeta = metaMap.get(columnName);
   final String sql2EntityRelatedTableName = columnMeta.getSql2EntityRelatedTableName();
   final Table relatedTable = getRelatedTable(sql2EntityRelatedTableName);
   String relatedComment = null;
   if (relatedTable != null) {
     final String relatedColumnName = columnMeta.getSql2EntityRelatedColumnName();
     final Column relatedColumn = relatedTable.getColumn(relatedColumnName);
     if (relatedColumn != null) {
       relatedComment = relatedColumn.getPlainComment();
     }
   }
   // the meta has its select column comment
   final String selectColumnComment = columnMeta.getColumnComment();
   final String commentMark = "// ";
   final String delimiter = getAliasDelimiterInDbComment();
   final StringBuilder sb = new StringBuilder();
   if (Srl.is_NotNull_and_NotTrimmedEmpty(relatedComment)) {
     sb.append(relatedComment);
     if (Srl.is_NotNull_and_NotTrimmedEmpty(selectColumnComment)) { // both exist
       if (Srl.is_NotNull_and_NotTrimmedEmpty(delimiter)) { // use alias option
         if (relatedComment.contains(delimiter)) { // resolved in related comment
           sb.append(ln()).append(commentMark).append(selectColumnComment);
         } else { // unresolved yet
           if (isDbCommentOnAliasBasis()) { // related comment is alias
             sb.append(delimiter);
           } else { // related comment is description
             sb.append(ln());
           }
           sb.append(commentMark).append(selectColumnComment);
         }
       } else { // no alias option
         sb.append(ln()).append(commentMark).append(selectColumnComment);
       }
     }
   } else { // not found related comment
     if (Srl.is_NotNull_and_NotTrimmedEmpty(selectColumnComment)) {
       if (Srl.is_NotNull_and_NotTrimmedEmpty(delimiter)) { // use alias option
         if (isDbCommentOnAliasBasis()) {
           // select column comment is treated as description
           sb.append(delimiter);
         }
       }
       sb.append(commentMark).append(selectColumnComment);
     }
   }
   column.setPlainComment(sb.toString());
 }
 // -----------------------------------------------------
 //                                           For Comment
 //                                           -----------
 protected void doProcessAutoDetectForNode(
     String sql,
     Map<String, String> propertyNameTypeMap,
     Map<String, String> propertyNameOptionMap,
     ForNode forNode) {
   final String expression = forNode.getExpression();
   if (!isPmCommentStartsWithPmb(expression)) {
     return;
   }
   final String propertyName = substringPmCommentPmbRear(expression);
   if (propertyNameTypeMap.containsKey(propertyName)) {
     // because of priority low (bind variable is given priority over for-comment)
     return;
   }
   if (isRevervedProperty(propertyName)) {
     return;
   }
   final DetectedPropertyInfo detected = analyzeForNodeElementType(forNode, propertyName);
   if (detected != null) {
     final String propertyType = switchPlainTypeName(detected.getPropertyType());
     propertyNameTypeMap.put(propertyName, propertyType);
     final String propertyOption = detected.getPropertyOption();
     if (Srl.is_NotNull_and_NotTrimmedEmpty(propertyOption)) {
       propertyNameOptionMap.put(propertyName, propertyOption);
     }
   }
 }
 // -----------------------------------------------------
 //                                               Logging
 //                                               -------
 protected void buildCustomizeEntityTitle(
     StringBuilder logSb, String entityName, DfCustomizeEntityInfo entityInfo) {
   logSb.append(entityName);
   final String handlingDisp = entityInfo.buildHandlingDisp();
   if (Srl.is_NotNull_and_NotTrimmedEmpty(handlingDisp)) {
     logSb.append(" ").append(handlingDisp);
   }
   logSb.append(ln());
 }
 // -----------------------------------------------------
 //                                         Bind Variable
 //                                         -------------
 protected void processAutoDetectBindNode(
     String sql,
     Map<String, String> propertyNameTypeMap,
     Map<String, String> propertyNameOptionMap,
     Set<String> autoDetectedPropertyNameSet,
     BindVariableNode variableNode) {
   final String expression = variableNode.getExpression();
   final String testValue = variableNode.getTestValue();
   if (testValue == null) {
     return;
   }
   if (!isPmCommentStartsWithPmb(expression)) {
     return;
   }
   if (isPmCommentNestedProperty(expression) || isPmCommentMethodCall(expression)) {
     return;
   }
   final String propertyName = substringPmCommentPmbRear(expression);
   if (isRevervedProperty(propertyName)) {
     return;
   }
   final String typeName = derivePropertyTypeFromTestValue(testValue);
   propertyNameTypeMap.put(propertyName, typeName); // override if same one exists
   autoDetectedPropertyNameSet.add(propertyName);
   final String option = variableNode.getOptionDef();
   // add option if it exists
   // so it is enough to set an option to only one bind variable comment
   // if several bind variable comments for the same property exist
   final String derivedOption = derivePropertyOptionFromTestValue(testValue);
   if (Srl.is_NotNull_and_NotTrimmedEmpty(option)) {
     final String resolvedOption;
     if (Srl.is_NotNull_and_NotTrimmedEmpty(derivedOption)) {
       resolvedOption = option + "|" + derivedOption; // merged
     } else {
       resolvedOption = option;
     }
     propertyNameOptionMap.put(propertyName, resolvedOption);
   } else {
     if (Srl.is_NotNull_and_NotTrimmedEmpty(derivedOption)) {
       propertyNameOptionMap.put(propertyName, derivedOption);
     }
   }
 }
 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;
 }
 protected void showParameterBean() {
   _log.info("* * * * * * * * *");
   _log.info("* ParameterBean *");
   _log.info("* * * * * * * * *");
   final StringBuilder logSb = new StringBuilder();
   final Map<String, DfPmbMetaData> pmbMetaDataMap = _sql2entityMeta.getPmbMetaDataMap();
   for (Entry<String, DfPmbMetaData> pmbEntry : pmbMetaDataMap.entrySet()) {
     final DfPmbMetaData pmbMetaData = pmbEntry.getValue();
     logSb.append(pmbMetaData.getClassName());
     if (pmbMetaData.hasSuperClassDefinition()) {
       logSb.append(" extends ").append(pmbMetaData.getSuperClassName());
     }
     if (pmbMetaData.isRelatedToProcedure()) {
       logSb.append(" (procedure");
       if (pmbMetaData.isProcedureRefCustomizeEntity()) {
         logSb.append(" with customize-entity");
       }
       logSb.append(")").append(ln());
       final Map<String, DfProcedureColumnMeta> propertyNameColumnInfoMap =
           pmbMetaData.getPropertyNameColumnInfoMap();
       for (Entry<String, DfProcedureColumnMeta> columnEntry :
           propertyNameColumnInfoMap.entrySet()) {
         final DfProcedureColumnMeta columnInfo = columnEntry.getValue();
         logSb.append("  ").append(columnInfo.getColumnNameDisp());
         logSb.append(ln());
       }
     } else {
       if (pmbMetaData.isTypedParameterBean()) {
         logSb.append(" ").append(pmbMetaData.buildTypedDisp());
       }
       logSb.append(ln());
       final Map<String, String> propertyNameTypeMap = pmbMetaData.getPropertyNameTypeMap();
       final Map<String, String> propertyOptionMap = pmbMetaData.getPropertyNameOptionMap();
       for (Entry<String, String> propEntry : propertyNameTypeMap.entrySet()) {
         final String propertyName = propEntry.getKey();
         final String propertyType = propEntry.getValue();
         logSb.append("  ").append(propertyType).append(" ").append(propertyName);
         final String optionDef = propertyOptionMap.get(propertyName);
         if (Srl.is_NotNull_and_NotTrimmedEmpty(optionDef)) {
           logSb.append(":").append(optionDef);
         }
         logSb.append(ln());
       }
     }
     logSb.append(ln());
   }
   if (logSb.length() > 0) {
     _log.info(ln() + logSb.toString().trim());
   }
 }
 // ===================================================================================
 //                                                                  Parameter Property
 //                                                                  ==================
 protected void processParameterProperty(
     String sql, String parameterBeanName, DfPmbMetaData pmbMetaData) {
   final Map<String, String> propertyNameTypeMap = new LinkedHashMap<String, String>();
   final Map<String, String> propertyNameOptionMap = new LinkedHashMap<String, String>();
   final Set<String> autoDetectedPropertyNameSet = new LinkedHashSet<String>();
   pmbMetaData.setPropertyNameTypeMap(propertyNameTypeMap);
   pmbMetaData.setPropertyNameOptionMap(propertyNameOptionMap);
   pmbMetaData.setAutoDetectedPropertyNameSet(autoDetectedPropertyNameSet);
   final List<DfSql2EntityMark> parameterBeanElement = getParameterBeanPropertyTypeList(sql);
   final String autoDetectMark = "AutoDetect";
   for (DfSql2EntityMark mark : parameterBeanElement) {
     final String element = mark.getContent().trim();
     if (element.equalsIgnoreCase(autoDetectMark)) {
       processAutoDetect(
           sql, propertyNameTypeMap, propertyNameOptionMap, autoDetectedPropertyNameSet);
       break;
     }
   }
   for (DfSql2EntityMark mark : parameterBeanElement) {
     final String element = mark.getContent().trim();
     final String nameDelimiter = " ";
     final String optionDelimiter = ":";
     if (autoDetectMark.equals(element)) {
       continue; // because of already resolved
     }
     final int delimiterIndex = element.indexOf(optionDelimiter);
     final String slaslaOption;
     {
       final String optionPrefix = DfPmbPropertyOptionComment.OPTION_PREFIX;
       final String optionSuffix = DfPmbPropertyOptionComment.OPTION_SUFFIX;
       final String comment = mark.getComment();
       if (Srl.is_NotNull_and_NotTrimmedEmpty(comment)) {
         final String filtered = Srl.replace(comment, "|", "/").trim();
         slaslaOption = optionPrefix + filtered + optionSuffix;
       } else {
         slaslaOption = null;
       }
     }
     final String propertyDef;
     final String optionDef;
     if (delimiterIndex > 0) {
       propertyDef = element.substring(0, delimiterIndex).trim();
       final int optionIndex = delimiterIndex + optionDelimiter.length();
       final String basicOption = element.substring(optionIndex).trim();
       optionDef = basicOption + (slaslaOption != null ? "|" + slaslaOption : "");
     } else {
       propertyDef = element;
       optionDef = (slaslaOption != null ? slaslaOption : null);
     }
     final int nameIndex = propertyDef.lastIndexOf(nameDelimiter);
     if (nameIndex <= 0) {
       String msg = "The parameter bean element should be [typeName propertyName].";
       msg = msg + " But: element=" + element + " srcFile=" + _outsideSqlFile;
       throw new IllegalStateException(msg);
     }
     // ParameterBean has the "import" clause of language-embedded utility
     final String typeName = prepareDefinedPropertyType(propertyDef, nameIndex);
     final String propertyName = propertyDef.substring(nameIndex + nameDelimiter.length()).trim();
     if (propertyNameTypeMap.containsKey(propertyName)) {
       // means the auto-detected property is found,
       // and it should be overridden
       propertyNameTypeMap.remove(propertyName);
       propertyNameOptionMap.remove(propertyName);
     }
     propertyNameTypeMap.put(propertyName, typeName);
     if (optionDef != null) {
       propertyNameOptionMap.put(propertyName, optionDef);
     }
   }
   final Map<String, Map<String, String>> bqpMap =
       _bqpSetupper.extractBasicBqpMap(createOutsideSqlPackAsOne());
   if (!bqpMap.isEmpty()) {
     final Map<String, String> bqpElementMap = bqpMap.values().iterator().next();
     pmbMetaData.setBqpElementMap(bqpElementMap);
   }
 }