@Override
  protected boolean beforeSave(boolean newRecord) {
    AccessSqlParser parser = new AccessSqlParser("SELECT * FROM " + getFromClause());
    TableInfo[] tableInfos = parser.getTableInfo(0);
    if (tableInfos == null || tableInfos.length == 0) {
      log.saveError("ParseFromClauseError", "Failed to parse from clause");
      return false;
    }

    // only one default per table
    if (newRecord || is_ValueChanged("IsDefault")) {
      if (isDefault()) {
        if (newRecord) {
          Query query =
              new Query(
                  getCtx(),
                  MTable.get(getCtx(), Table_ID),
                  "AD_Table_ID=? AND IsDefault='Y' AND AD_Client_ID=?",
                  get_TrxName());
          List<MInfoWindow> list = query.setParameters(getAD_Table_ID(), getAD_Client_ID()).list();
          for (MInfoWindow iw : list) {
            iw.setIsDefault(false);
            iw.saveEx();
          }
        } else {
          Query query =
              new Query(
                  getCtx(),
                  MTable.get(getCtx(), Table_ID),
                  "AD_InfoWindow_ID<>? AND AD_Table_ID=? AND IsDefault='Y' AND AD_Client_ID=?",
                  get_TrxName());
          List<MInfoWindow> list =
              query
                  .setParameters(getAD_InfoWindow_ID(), getAD_Table_ID(), getAD_Client_ID())
                  .list();
          for (MInfoWindow iw : list) {
            iw.setIsDefault(false);
            iw.saveEx();
          }
        }
      }
    }

    // evaluate need valid
    boolean isNeedValid =
        is_new()
            || is_ValueChanged(I_AD_InfoWindow.COLUMNNAME_AD_Table_ID)
            || is_ValueChanged(I_AD_InfoWindow.COLUMNNAME_WhereClause)
            || is_ValueChanged(I_AD_InfoWindow.COLUMNNAME_FromClause)
            || is_ValueChanged(I_AD_InfoWindow.COLUMNNAME_OrderByClause)
            || is_ValueChanged(I_AD_InfoWindow.COLUMNNAME_OtherClause)
            || is_ValueChanged(I_AD_InfoWindow.COLUMNNAME_IsDistinct);

    // valid config
    if (isNeedValid) {
      validate();
    }

    return true;
  }
  public void addRecordDependentAccessSql(
      final StringBuilder retSQL,
      final AccessSqlParser asp,
      final String tableName,
      final boolean rw) {
    final String mainSql = asp.getMainSql();

    int AD_Table_ID = 0;
    String whereColumnName = null;
    final List<Integer> includes = new ArrayList<Integer>();
    final List<Integer> excludes = new ArrayList<Integer>();
    for (final TableRecordPermission recordDependentAccess : getDependentRecordPermissionsList()) {
      final String columnName =
          recordDependentAccess.getKeyColumnName(asp.getTableInfo(asp.getMainSqlIndex()));
      if (columnName == null) {
        continue; // no key column
      }

      if (mainSql.toUpperCase().startsWith("SELECT COUNT(*) FROM ")) {
        // globalqss - Carlos Ruiz - [ 1965744 ] Dependent entities access problem
        // this is the count select, it doesn't have the column but needs to be filtered

        if (!TablesAccessInfo.instance.isPhysicalColumn(tableName, columnName)) {
          continue;
        }
      } else {
        final int posColumn = mainSql.indexOf(columnName);
        if (posColumn == -1) {
          continue;
        }
        // we found the column name - make sure it's a column name
        char charCheck = mainSql.charAt(posColumn - 1); // before
        if (!(charCheck == ',' || charCheck == '.' || charCheck == ' ' || charCheck == '(')) {
          continue;
        }
        charCheck = mainSql.charAt(posColumn + columnName.length()); // after
        if (!(charCheck == ',' || charCheck == ' ' || charCheck == ')')) {
          continue;
        }
      }

      if (AD_Table_ID != 0 && AD_Table_ID != recordDependentAccess.getAD_Table_ID()) {
        retSQL.append(getDependentAccess(whereColumnName, includes, excludes));
      }

      AD_Table_ID = recordDependentAccess.getAD_Table_ID();
      // *** we found the column in the main query
      if (recordDependentAccess.isExclude()) {
        excludes.add(recordDependentAccess.getRecord_ID());
        logger.fine("Exclude " + columnName + " - " + recordDependentAccess);
      } else if (!rw || !recordDependentAccess.isReadOnly()) {
        includes.add(recordDependentAccess.getRecord_ID());
        logger.fine("Include " + columnName + " - " + recordDependentAccess);
      }
      whereColumnName = getDependentRecordWhereColumn(mainSql, columnName);
    } // for all dependent records
    retSQL.append(getDependentAccess(whereColumnName, includes, excludes));

    // //
    // retSQL.append(orderBy);
    // logger.finest(retSQL.toString());
    // return retSQL.toString();
  } // addAccessSQL