/**
   * Translates the filter criteria ({@link #addFilterParameter(String, String)}) to a valid HQL
   * where clause (without the 'where' keyword). After calling this method the method {@link
   * #getNamedParameters()} can be called. Note that currently only filtering on string and boolean
   * properties is supported. Also filtering on the identifier of a referenced business object is
   * supported.
   *
   * @return a valid where clause or an empty string if not set.
   */
  public String getWhereClause() {

    if (whereClause != null) {
      return whereClause;
    }

    // add some default filter parameters
    filterParameters.put(
        JsonConstants.QUERY_PARAM_USER, OBContext.getOBContext().getUser().getId());
    if (!filterParameters.containsKey(JsonConstants.QUERY_PARAM_CLIENT)) {
      filterParameters.put(
          JsonConstants.QUERY_PARAM_CLIENT, OBContext.getOBContext().getUser().getId());
    }

    final SimpleDateFormat simpleDateFormat = JsonUtils.createDateFormat();

    Check.isNotNull(entity, "Entity must be set");

    final StringBuilder sb = new StringBuilder();
    boolean addAnd = false;
    final StringBuilder orgPart = new StringBuilder();
    final List<Property> propertyDone = new ArrayList<Property>();
    String whereParameterValue = null;
    for (String key : filterParameters.keySet()) {
      String value = filterParameters.get(key);

      if (key.equals(JsonConstants.WHERE_PARAMETER)) {
        // there are cases where null is set as a string
        // handle this
        if (value.equals("null") || value.length() == 0) {
          continue;
        }
        whereParameterValue = value;
        continue;
      }

      // handle the case that we should filter on the accessible organizations
      if (key.equals(JsonConstants.ORG_PARAMETER)) {
        if (entity.isOrganizationEnabled() && value != null && value.length() > 0) {
          final Set<String> orgs =
              OBContext.getOBContext().getOrganizationStructureProvider().getNaturalTree(value);
          if (orgs.size() > 0) {
            if (getMainAlias() != null) {
              orgPart.append(" " + getMainAlias() + ".organization in (");
            } else {
              orgPart.append(" organization in (");
            }
            boolean addComma = false;
            for (String org : orgs) {
              if (addComma) {
                orgPart.append(",");
              }
              orgPart.append("'" + org + "'");
              addComma = true;
            }
            orgPart.append(") ");
          }
        }
        continue;
      }

      // determine the property
      final List<Property> properties = JsonUtils.getPropertiesOnPath(getEntity(), key);
      if (properties.isEmpty()) {
        continue;
      }
      final Property property = properties.get(properties.size() - 1);
      // invalid propname, ignore this one
      // TODO: possibly warn about it
      if (property == null || propertyDone.contains(property)) {
        continue;
      }
      propertyDone.add(property);

      // we know the property and the string representation of the value...
      // do the conversion

      if (addAnd) {
        if (doOr) {
          sb.append(" or ");
        } else {
          sb.append(" and ");
        }
      }

      String leftWherePart = null;
      if (isDoOr()) {
        leftWherePart = resolveJoins(properties, key);
      } else if (getMainAlias() != null) {
        leftWherePart = getMainAlias() + "." + key.trim();
      } else {
        leftWherePart = key;
      }

      // get rid of the identifier and replace it with the real property name
      // or with the concatenation if there are multiple parts
      // NOTE: the if and else check against the key variable and not the leftwherepart
      // because the key contains the original string (with the _identifier part).
      // Within the if the leftWherePart is used because it contains the join aliases
      if (key.equals(JsonConstants.IDENTIFIER) || key.endsWith("." + JsonConstants.IDENTIFIER)) {
        // the identifierProperties are read from the owning entity of the
        // property, that should work fine, as this last property is always part of the
        // identifier
        final List<Property> identifierProperties = property.getEntity().getIdentifierProperties();
        Check.isTrue(
            identifierProperties.contains(property),
            "Property " + property + " not part of identifier of " + property.getEntity());
        final String prefix;
        final int index = leftWherePart.lastIndexOf(".");
        if (key.equals(JsonConstants.IDENTIFIER)) {
          prefix = getMainAlias() + ".";
        } else if (index == -1) {
          prefix = "";
        } else {
          // the + 1 makes sure that the dot is included
          prefix = leftWherePart.substring(0, index + 1);
        }
        leftWherePart = createIdentifierLeftClause(identifierProperties, prefix);

        // if the value consists of multiple parts then filtering won't work
        // only search on the first part then, is pragmatic but very workable
        if (value != null && value.contains(IdentifierProvider.SEPARATOR)) {
          final int separatorIndex = value.indexOf(IdentifierProvider.SEPARATOR);
          value = value.substring(0, separatorIndex);
        }
      }

      // NOTE: If you change this part, make sure that you sync the changes with the
      // SelectorDataSourceFilter. Check issue https://issues.openbravo.com/view.php?id=14239

      // NOTE the typedParameters.add call must be done after the call to
      // getTypedParameterAlias, this to get the correct alias codes
      if (key.equals(JsonConstants.IDENTIFIER)) {
        if (textMatching == TextMatching.exact) {
          sb.append(leftWherePart + " = " + getTypedParameterAlias());
          typedParameters.add(value);
        } else if (textMatching == TextMatching.startsWith) {
          sb.append(
              "upper("
                  + leftWherePart
                  + ") like "
                  + getTypedParameterAlias()
                  + " escape '"
                  + ESCAPE_CHAR
                  + "' ");
          typedParameters.add(escapeLike(value.toUpperCase()) + "%");
        } else {
          sb.append(
              "upper("
                  + leftWherePart
                  + ") like "
                  + getTypedParameterAlias()
                  + " escape '"
                  + ESCAPE_CHAR
                  + "' ");
          typedParameters.add("%" + escapeLike(value.toUpperCase()).replaceAll(" ", "%") + "%");
        }
      } else if (!property.isPrimitive()) {
        // an in parameter use it...
        if (value.contains(JsonConstants.IN_PARAMETER_SEPARATOR)) {
          final List<String> values = new ArrayList<String>();
          final String[] separatedValues = value.split(JsonConstants.IN_PARAMETER_SEPARATOR);
          for (String separatedValue : separatedValues) {
            values.add(separatedValue);
          }
          sb.append(leftWherePart + ".id in (" + getTypedParameterAlias() + ")");
          typedParameters.add(values);
        } else {
          sb.append(leftWherePart + ".id = " + getTypedParameterAlias());
          typedParameters.add(value);
        }
      } else if (String.class == property.getPrimitiveObjectType()) {
        if (textMatching == TextMatching.exact) {
          sb.append(leftWherePart + " = " + getTypedParameterAlias());
          typedParameters.add(value);
        } else if (textMatching == TextMatching.startsWith) {
          sb.append(
              "upper("
                  + leftWherePart
                  + ") like "
                  + getTypedParameterAlias()
                  + " escape '"
                  + ESCAPE_CHAR
                  + "' ");
          typedParameters.add(escapeLike(value.toUpperCase()) + "%");
        } else {
          sb.append(
              "upper("
                  + leftWherePart
                  + ") like "
                  + getTypedParameterAlias()
                  + " escape '"
                  + ESCAPE_CHAR
                  + "' ");
          typedParameters.add("%" + escapeLike(value.toUpperCase()).replaceAll(" ", "%") + "%");
        }
      } else if (Boolean.class == property.getPrimitiveObjectType()) {
        final String alias = getTypedParameterAlias();
        typedParameters.add(new Boolean(value));
        sb.append(leftWherePart + " = " + alias);
      } else if (property.isNumericType()) {
        try {
          final String alias = getTypedParameterAlias();
          final BigDecimal bdValue = new BigDecimal(value);
          if (Long.class == property.getPrimitiveObjectType()) {
            typedParameters.add(bdValue.longValue());
          } else if (Integer.class == property.getPrimitiveObjectType()) {
            typedParameters.add(bdValue.intValue());
          } else {
            typedParameters.add(bdValue);
          }
          sb.append(leftWherePart + " = " + alias);
        } catch (NumberFormatException e) {
          // ignore on purpose, incorrect value entered by user
          // add a dummy whereclause to make the query format correct
          sb.append(" 1=1 ");
        }
      } else if (Date.class.isAssignableFrom(property.getPrimitiveObjectType())) {
        try {
          final Calendar cal = Calendar.getInstance();
          cal.setTime(simpleDateFormat.parse(value));
          final String alias1 = getTypedParameterAlias();
          typedParameters.add(cal.get(Calendar.DATE));
          final String alias2 = getTypedParameterAlias();
          typedParameters.add(cal.get(Calendar.MONTH) + 1);
          final String alias3 = getTypedParameterAlias();
          typedParameters.add(cal.get(Calendar.YEAR));
          sb.append(
              " (day("
                  + leftWherePart
                  + ") = "
                  + alias1
                  + " and month("
                  + leftWherePart
                  + ") = "
                  + alias2
                  + " and year("
                  + leftWherePart
                  + ") = "
                  + alias3
                  + ") ");
        } catch (Exception e) {
          // ignore these errors, just don't filter then
          // add a dummy whereclause to make the query format correct
          sb.append(" 1=1 ");
        }

        // } else if (property.isDate() || property.isDatetime()) {
        // NOTE: dates arrive in the format of the user....
        // sb.append(leftWherePart + " = ?");
        // typedParameters.add(value);
      } else {
        // TODO: support this....
        throw new UnsupportedOperationException(
            "Type "
                + property.getPrimitiveObjectType()
                + " not yet supported for parameter "
                + key);
      }
      addAnd = true;
    }

    log.debug("Whereclause for entity " + entity.getName());
    log.debug(sb.toString());
    for (Object param : typedParameters) {
      log.debug(param);
    }
    log.debug("Textmatching " + textMatching);

    if (sb.length() == 0) {
      whereClause = orgPart.length() > 0 ? orgPart.toString() : "";
    } else {
      whereClause =
          "(" + sb.toString() + ")" + (orgPart.length() > 0 ? " and " + orgPart.toString() : "");
    }
    if (whereParameterValue != null) {
      if (whereClause.length() > 0) {
        whereClause = " (" + whereClause + ") and (" + whereParameterValue + ") ";
      } else {
        whereClause = " " + whereParameterValue;
      }
    }
    if (whereClause.trim().length() > 0) {
      whereClause = " where " + whereClause;
    }

    // handle special transactional range parameter
    if (whereClause.contains(JsonConstants.QUERY_PARAM_TRANSACTIONAL_RANGE)) {
      final String alias = getTypedParameterAlias();
      String windowId = RequestContext.get().getRequestParameter("windowId");
      if (windowId == null) {
        windowId = "";
      }
      final String range =
          Utility.getTransactionalDate(
              new DalConnectionProvider(false),
              RequestContext.get().getVariablesSecureApp(),
              windowId);
      final int rangeNum = Integer.parseInt(range);
      final Calendar cal = Calendar.getInstance();
      cal.add(Calendar.DAY_OF_MONTH, -1 * rangeNum);
      whereClause = whereClause.replace(JsonConstants.QUERY_PARAM_TRANSACTIONAL_RANGE, alias);
      typedParameters.add(cal.getTime());
    }

    if (whereClause.contains(JsonConstants.QUERY_PARAM_CLIENT)) {
      final String alias = getTypedParameterAlias();
      String clientId = (String) DalUtil.getId(OBContext.getOBContext().getCurrentClient());
      whereClause = whereClause.replace(JsonConstants.QUERY_PARAM_CLIENT, alias);
      typedParameters.add(clientId);
    }
    whereClause = setRequestParameters(whereClause);
    whereClause = substituteContextParameters(whereClause);

    return whereClause;
  }
  private void printPageEdit(
      HttpServletResponse response,
      HttpServletRequest request,
      VariablesSecureApp vars,
      boolean boolNew,
      String strC_BP_Group_ID,
      TableSQLData tableSQL)
      throws IOException, ServletException {
    if (log4j.isDebugEnabled()) log4j.debug("Output: edit");

    HashMap<String, String> usedButtonShortCuts;

    usedButtonShortCuts = new HashMap<String, String>();

    String strOrderByFilter = vars.getSessionValue(tabId + "|orderby");
    String orderClause = " C_BP_Group.Value";
    if (strOrderByFilter == null || strOrderByFilter.equals("")) strOrderByFilter = orderClause;
    /*{
      if (!strOrderByFilter.equals("") && !orderClause.equals("")) strOrderByFilter += ", ";
      strOrderByFilter += orderClause;
    }*/

    String strCommand = null;
    BusinessPartnerCategoryData[] data = null;
    XmlDocument xmlDocument = null;
    FieldProvider dataField = vars.getEditionData(tabId);
    vars.removeEditionData(tabId);
    String strParamName = vars.getSessionValue(tabId + "|paramName");

    boolean hasSearchCondition = false;
    hasSearchCondition =
        (tableSQL.hasInternalFilter() && ("").equals(strParamName))
            || !(("").equals(strParamName) || ("%").equals(strParamName));

    String strParamSessionDate =
        vars.getGlobalVariable(
            "inpParamSessionDate", Utility.getTransactionalDate(this, vars, windowId), "");
    String buscador = "";
    String[] discard = {"", "isNotTest"};

    if (vars.getSessionValue("#ShowTest", "N").equals("Y")) discard[1] = new String("isTest");
    if (dataField == null) {
      if (!boolNew) {
        discard[0] = new String("newDiscard");
        data =
            BusinessPartnerCategoryData.selectEdit(
                this,
                vars.getSessionValue("#AD_SqlDateTimeFormat"),
                vars.getLanguage(),
                strC_BP_Group_ID,
                Utility.getContext(this, vars, "#User_Client", windowId),
                Utility.getContext(this, vars, "#AccessibleOrgTree", windowId, accesslevel));

        if (!strC_BP_Group_ID.equals("") && (data == null || data.length == 0)) {
          response.sendRedirect(strDireccion + request.getServletPath() + "?Command=RELATION");
          return;
        }
        refreshSessionEdit(vars, data);
        strCommand = "EDIT";
      }

      if (boolNew || data == null || data.length == 0) {
        discard[0] = new String("editDiscard");
        strCommand = "NEW";
        data = new BusinessPartnerCategoryData[0];
      } else {
        discard[0] = new String("newDiscard");
      }
    } else {
      if (dataField.getField("cBpGroupId") == null || dataField.getField("cBpGroupId").equals("")) {
        discard[0] = new String("editDiscard");
        strCommand = "NEW";
        boolNew = true;
      } else {
        discard[0] = new String("newDiscard");
        strCommand = "EDIT";
      }
    }

    if (dataField == null) {
      if (boolNew || data == null || data.length == 0) {
        refreshSessionNew(vars);
        data =
            BusinessPartnerCategoryData.set(
                "",
                Utility.getDefault(
                    this, vars, "AD_Client_ID", "@AD_CLIENT_ID@", "192", "", dataField),
                Utility.getDefault(this, vars, "AD_Org_ID", "@AD_Org_ID@", "192", "", dataField),
                "Y",
                Utility.getDefault(this, vars, "CreatedBy", "", "192", "", dataField),
                BusinessPartnerCategoryData.selectDef4966_0(
                    this, Utility.getDefault(this, vars, "CreatedBy", "", "192", "", dataField)),
                Utility.getDefault(this, vars, "UpdatedBy", "", "192", "", dataField),
                BusinessPartnerCategoryData.selectDef4968_1(
                    this, Utility.getDefault(this, vars, "UpdatedBy", "", "192", "", dataField)),
                Utility.getDefault(this, vars, "Value", "", "192", "", dataField),
                Utility.getDefault(this, vars, "Name", "", "192", "", dataField),
                Utility.getDefault(this, vars, "Description", "", "192", "", dataField),
                Utility.getDefault(this, vars, "IsDefault", "", "192", "N", dataField));
      }
    }

    String currentOrg =
        (boolNew
            ? ""
            : (dataField != null ? dataField.getField("adOrgId") : data[0].getField("adOrgId")));
    if (!currentOrg.equals("") && !currentOrg.startsWith("'")) currentOrg = "'" + currentOrg + "'";
    String currentClient =
        (boolNew
            ? ""
            : (dataField != null
                ? dataField.getField("adClientId")
                : data[0].getField("adClientId")));
    if (!currentClient.equals("") && !currentClient.startsWith("'"))
      currentClient = "'" + currentClient + "'";

    boolean hasReadOnlyAccess =
        org.openbravo.erpCommon.utility.WindowAccessData.hasReadOnlyAccess(
            this, vars.getRole(), tabId);
    boolean editableTab =
        (!hasReadOnlyAccess
            && (currentOrg.equals("")
                || Utility.isElementInList(
                    Utility.getContext(this, vars, "#User_Org", windowId, accesslevel), currentOrg))
            && (currentClient.equals("")
                || Utility.isElementInList(
                    Utility.getContext(this, vars, "#User_Client", windowId, accesslevel),
                    currentClient)));
    if (editableTab)
      xmlDocument =
          xmlEngine
              .readXmlTemplate(
                  "org/openbravo/erpWindows/BusinessPartnerCategory/BusinessPartnerCategory_Edition",
                  discard)
              .createXmlDocument();
    else
      xmlDocument =
          xmlEngine
              .readXmlTemplate(
                  "org/openbravo/erpWindows/BusinessPartnerCategory/BusinessPartnerCategory_NonEditable",
                  discard)
              .createXmlDocument();

    xmlDocument.setParameter("tabId", tabId);
    ToolBar toolbar =
        new ToolBar(
            this,
            editableTab,
            vars.getLanguage(),
            "BusinessPartnerCategory",
            (strCommand.equals("NEW")
                || boolNew
                || (dataField == null && (data == null || data.length == 0))),
            "document.frmMain.inpcBpGroupId",
            "",
            "..",
            "".equals("Y"),
            "BusinessPartnerCategory",
            strReplaceWith,
            true,
            false,
            false,
            Utility.hasTabAttachments(this, vars, tabId, strC_BP_Group_ID),
            !hasReadOnlyAccess);
    toolbar.setTabId(tabId);
    toolbar.setDeleteable(true);
    toolbar.prepareEditionTemplate(
        "N".equals("Y"),
        hasSearchCondition,
        vars.getSessionValue("#ShowTest", "N").equals("Y"),
        "STD",
        Utility.getContext(this, vars, "ShowAudit", windowId).equals("Y"));
    xmlDocument.setParameter("toolbar", toolbar.toString());

    // set updated timestamp to manage locking mechanism
    if (!boolNew) {
      xmlDocument.setParameter(
          "updatedTimestamp",
          (dataField != null
              ? dataField.getField("updatedTimeStamp")
              : data[0].getField("updatedTimeStamp")));
    }

    boolean concurrentSave = vars.getSessionValue(tabId + "|concurrentSave").equals("true");
    if (concurrentSave) {
      // after concurrent save error, force autosave
      xmlDocument.setParameter("autosave", "Y");
    } else {
      xmlDocument.setParameter("autosave", "N");
    }
    vars.removeSessionValue(tabId + "|concurrentSave");

    try {
      WindowTabs tabs =
          new WindowTabs(this, vars, tabId, windowId, true, (strCommand.equalsIgnoreCase("NEW")));
      xmlDocument.setParameter("parentTabContainer", tabs.parentTabs());
      xmlDocument.setParameter("mainTabContainer", tabs.mainTabs());
      // if (!strC_BP_Group_ID.equals("")) xmlDocument.setParameter("childTabContainer",
      // tabs.childTabs(false));
      // else xmlDocument.setParameter("childTabContainer", tabs.childTabs(true));
      xmlDocument.setParameter("childTabContainer", tabs.childTabs(false));
      String hideBackButton = vars.getGlobalVariable("hideMenu", "#Hide_BackButton", "");
      NavigationBar nav =
          new NavigationBar(
              this,
              vars.getLanguage(),
              "BusinessPartnerCategory_Relation.html",
              "BusinessPartnerCategory",
              "W",
              strReplaceWith,
              tabs.breadcrumb(),
              hideBackButton.equals("true"),
              !concurrentSave);
      xmlDocument.setParameter("navigationBar", nav.toString());
      LeftTabsBar lBar =
          new LeftTabsBar(
              this, vars.getLanguage(), "BusinessPartnerCategory_Relation.html", strReplaceWith);
      xmlDocument.setParameter("leftTabs", lBar.editionTemplate(strCommand.equals("NEW")));
    } catch (Exception ex) {
      throw new ServletException(ex);
    }

    xmlDocument.setParameter("commandType", strCommand);
    xmlDocument.setParameter("buscador", buscador);
    xmlDocument.setParameter("windowId", windowId);
    xmlDocument.setParameter("changed", "");
    xmlDocument.setParameter("language", "defaultLang=\"" + vars.getLanguage() + "\";");
    xmlDocument.setParameter("theme", vars.getTheme());
    final String strMappingName = Utility.getTabURL(tabId, "E", false);
    xmlDocument.setParameter("mappingName", strMappingName);
    xmlDocument.setParameter("confirmOnChanges", Utility.getJSConfirmOnChanges(vars, windowId));
    // xmlDocument.setParameter("buttonReference", Utility.messageBD(this, "Reference",
    // vars.getLanguage()));

    xmlDocument.setParameter("paramSessionDate", strParamSessionDate);

    xmlDocument.setParameter("directory", "var baseDirectory = \"" + strReplaceWith + "/\";\n");
    OBError myMessage = vars.getMessage(tabId);
    vars.removeMessage(tabId);
    if (myMessage != null) {
      xmlDocument.setParameter("messageType", myMessage.getType());
      xmlDocument.setParameter("messageTitle", myMessage.getTitle());
      xmlDocument.setParameter("messageMessage", myMessage.getMessage());
    }
    xmlDocument.setParameter("displayLogic", getDisplayLogicContext(vars, boolNew));

    if (dataField == null) {
      xmlDocument.setData("structure1", data);

    } else {

      FieldProvider[] dataAux = new FieldProvider[1];
      dataAux[0] = dataField;

      xmlDocument.setData("structure1", dataAux);
    }

    try {
      ComboTableData comboTableData = null;
      String userOrgList = "";
      if (editableTab)
        userOrgList =
            Utility.getContext(this, vars, "#User_Org", windowId, accesslevel); // editable record
      else userOrgList = currentOrg;
      comboTableData =
          new ComboTableData(
              vars,
              this,
              "19",
              "AD_Org_ID",
              "",
              "",
              userOrgList,
              Utility.getContext(this, vars, "#User_Client", windowId),
              0);
      Utility.fillSQLParameters(
          this,
          vars,
          (dataField == null ? data[0] : dataField),
          comboTableData,
          windowId,
          (dataField == null ? data[0].getField("adOrgId") : dataField.getField("adOrgId")));
      xmlDocument.setData(
          "reportAD_Org_ID", "liststructure", comboTableData.select(!strCommand.equals("NEW")));
      comboTableData = null;
      xmlDocument.setParameter("Created_Format", vars.getSessionValue("#AD_SqlDateTimeFormat"));
      xmlDocument.setParameter(
          "Created_Maxlength",
          Integer.toString(vars.getSessionValue("#AD_SqlDateTimeFormat").length()));
      xmlDocument.setParameter("Updated_Format", vars.getSessionValue("#AD_SqlDateTimeFormat"));
      xmlDocument.setParameter(
          "Updated_Maxlength",
          Integer.toString(vars.getSessionValue("#AD_SqlDateTimeFormat").length()));
    } catch (Exception ex) {
      ex.printStackTrace();
      throw new ServletException(ex);
    }

    xmlDocument.setParameter("scriptOnLoad", getShortcutScript(usedButtonShortCuts));

    final String refererURL = vars.getSessionValue(tabId + "|requestURL");
    vars.removeSessionValue(tabId + "|requestURL");
    if (!refererURL.equals("")) {
      final Boolean failedAutosave = (Boolean) vars.getSessionObject(tabId + "|failedAutosave");
      vars.removeSessionValue(tabId + "|failedAutosave");
      if (failedAutosave != null && failedAutosave) {
        final String jsFunction = "continueUserAction('" + refererURL + "');";
        xmlDocument.setParameter("failedAutosave", jsFunction);
      }
    }

    if (strCommand.equalsIgnoreCase("NEW")) {
      vars.removeSessionValue(tabId + "|failedAutosave");
      vars.removeSessionValue(strMappingName + "|hash");
    }

    response.setContentType("text/html; charset=UTF-8");
    PrintWriter out = response.getWriter();
    out.println(xmlDocument.print());
    out.close();
  }