/** @param args */
  public static void main(String[] args) {
    WfVersionBO version = new WfVersionBO();

    String processId = "jxstar2";
    Map<String, String> mpUser = FactoryUtil.newMap();
    mpUser.put("user_id", "administrator");
    mpUser.put("user_name", "东宏");
    version.newVersion(processId, mpUser);
  }
  /**
   * 取统计字段
   *
   * @param lsField -- 所有字段信息
   * @return
   */
  private static List<Map<String, String>> getStatField(List<Map<String, String>> lsField) {
    List<Map<String, String>> lsRet = FactoryUtil.newList();
    if (lsField == null) return lsRet;

    Map<String, String> mpField = null;
    String isstat = null;
    for (int i = 0, n = lsField.size(); i < n; i++) {
      mpField = lsField.get(i);

      isstat = mpField.get("is_stat");
      if (isstat.trim().equals("1")) lsRet.add(mpField);
    }

    return lsRet;
  }
  /**
   * 生成公告阅读记录
   *
   * @param msgId -- 消息ID
   * @param userId -- 用户ID
   * @param userName -- 用户名
   * @return
   */
  public String readBoard(String msgId, String userId, String userName) {
    Map<String, String> mpRead = FactoryUtil.newMap();
    mpRead.put("msg_id", msgId);
    mpRead.put("user_id", userId);
    mpRead.put("user_name", userName);
    mpRead.put("read_date", DateUtil.getTodaySec());

    String readId = DmDao.insert("plet_read", mpRead);
    if (readId == null || readId.length() == 0) {
      // "生成公告阅读记录失败!"
      setMessage(JsMessage.getValue("portlet.createboarderror"));
      return _returnFaild;
    }
    return _returnSuccess;
  }
  /**
   * 取表单中的所有图片元素。
   *
   * @param sheet -- 表单对象
   * @return
   */
  private static List<HSSFPicture> getAllPicture(HSSFSheet sheet) {
    List<HSSFPicture> lsPicture = FactoryUtil.newList();

    HSSFPatriarch draw = sheet.getDrawingPatriarch();
    if (draw == null) return lsPicture;

    // 取所有子图形位置
    List<HSSFShape> lsShape = draw.getChildren();
    if (lsShape == null || lsShape.isEmpty()) return lsPicture;

    for (int i = 0, n = lsShape.size(); i < n; i++) {
      HSSFShape shape = lsShape.get(i);
      if (shape instanceof HSSFPicture) {
        lsPicture.add((HSSFPicture) shape);
      }
    }
    return lsPicture;
  }
  /**
   * 复制来源表格中第1个SHEET中的图片到目标表格中的第1个SHEET中。
   *
   * @param destBook -- 目标表格
   * @param srcBook -- 来源表格
   */
  private static void copySheetImage(HSSFWorkbook destBook, HSSFWorkbook srcBook) {
    // 来源表单
    HSSFSheet srcSheet = srcBook.getSheetAt(0);
    // 目标表单
    HSSFSheet destSheet = destBook.getSheetAt(0);

    // 需要偏移的行数
    int endRowNum = destSheet.getPhysicalNumberOfRows();

    // 取来源表单中的图片对象
    List<HSSFPicture> lsSrcPicture = getAllPicture(srcSheet);
    _log.showDebug("----------source picture size:" + lsSrcPicture.size());

    // 取所有子图形数据,如果是主从报表且明细数据占多页时,则会报空指针错误
    List<HSSFPictureData> lsPicData = null;
    try {
      lsPicData = srcBook.getAllPictures();
    } catch (Exception e) {
      // e.printStackTrace();
      _log.showWarn("由于表单明细有多页,造成临时表的图片数据取不到,只能采用原表第1个图替代!");

      // 原表中也没有图片,则不处理图片复制了
      lsPicData = destBook.getAllPictures();
      if (lsPicData == null || lsPicData.isEmpty()) return;

      // 只取原表中第1个图片信息
      List<HSSFPictureData> destData = FactoryUtil.newList();
      for (int i = 0, n = lsSrcPicture.size(); i < n; i++) {
        destData.add(lsPicData.get(0));
      }
      lsPicData = destData;
    }
    if (lsPicData == null || lsPicData.isEmpty()) return;
    _log.showDebug("----------source data size:" + lsPicData.size());

    // data数量可能大于图片数量
    if (lsSrcPicture.size() > lsPicData.size()) {
      _log.showWarn("图片数量与数据数量不符!");
      return;
    }

    // 取图片管理器
    HSSFPatriarch destDraw = destSheet.getDrawingPatriarch();
    if (destDraw == null) {
      destDraw = destSheet.createDrawingPatriarch();
    }

    // 取原目标表单中的图片对象
    List<HSSFPicture> lsDestPicture = getAllPicture(destSheet);
    int index = lsDestPicture.size();

    for (int i = 0, n = lsSrcPicture.size(); i < n; i++) {
      // 取图片对象
      HSSFPicture picture = lsSrcPicture.get(i);
      // 根据图片序号取图片数据
      HSSFPictureData picdata = lsPicData.get(i);
      // 取图片字节信息
      byte[] datas = picdata.getData();

      // 取图片位置信息
      HSSFClientAnchor anchor = (HSSFClientAnchor) picture.getAnchor();

      // 添加行偏移值
      anchor.setRow1(anchor.getRow1() + endRowNum);
      anchor.setRow2(anchor.getRow2() + endRowNum);

      // 插入新图片,返回的新图片序号无效
      destBook.addPicture(datas, picdata.getFormat());
      // 上面代码中新建图片的序号没有考虑原有图片数量,所以取原图片数量+1作为新图片的序号
      index++;
      _log.showDebug("---------copy new image index=" + index);

      destDraw.createPicture(anchor, index);
    }
  }
  /**
   * 填充数据到表格对象。
   *
   * @param sheet -- 表格对象
   * @param lsData -- 输出数据记录
   * @param lsField -- 输出字段明细
   * @param pageSize -- 每页显示行数
   * @param pos -- 开始输出行数
   * @param curPage -- 当前是第几页
   * @param sumPage -- 总页数
   * @return
   */
  public static HSSFSheet fillGrid(
      HSSFSheet sheet,
      List<Map<String, String>> lsData,
      List<Map<String, String>> lsField,
      int pageSize,
      int pos,
      int curPage,
      int sumPage) {
    if (lsField == null || lsData == null) {
      _log.showWarn("data is null or field is null!");
      return sheet;
    }

    if (lsField.isEmpty() || lsData.isEmpty()) {
      _log.showDebug("data is empty or field is null!");
      return sheet;
    }

    HSSFRow row = null; // excel的行对象
    HSSFCell cell = null; // excel的列对象
    String strValue = null; // 每个格的信息内容
    Map<String, String> mpData = null; // 每条记录数据
    Map<String, String> mpField = null; // 每条个字段的信息

    // HSSFCellStyle style = null;			//excel单元格风格对象

    int posi = (pageSize > 0 && pos >= 0) ? pos : 0;
    int cnt = (pageSize <= 0) ? lsData.size() : pageSize + posi;
    int[] posis = new int[2];
    int index = 0, rowIndex = 0;
    int currRow = 0;
    int cntCol = 1; // 合计列的位置
    String strStyle = null, strColName = null, strColCode = null, strColTag = null;

    // 用于每页小计
    List<Map<String, String>> lsStatCol = getStatField(lsField);
    String isOutZero = "0";
    Map<String, String> mpStat = null, mpStatValue = FactoryUtil.newMap();
    String strCol = null;
    BigDecimal bdStat = null;
    boolean isStatCol = false;
    // 用于每页小计

    for (rowIndex = posi, index = 0; rowIndex < cnt; rowIndex++, index++) {
      if (lsData.size() <= rowIndex) break; // 如果rowIndex大于记录数
      mpData = lsData.get(rowIndex);

      for (int i = 0, n = lsField.size(); i < n; i++) {
        mpField = lsField.get(i);
        isOutZero = mpField.get("is_outzero");
        if (isOutZero == null) isOutZero = "1";
        strStyle = mpField.get("format"); // 字段格式

        strColName = mpField.get("display"); // 字段名称
        strColCode = mpField.get("col_code").toLowerCase(); // 字段编码
        strColTag = mpField.get("combo_code"); // 标签

        posis = getPosition(mpField.get("col_pos"));
        if (posis.length != 2) {
          _log.showWarn(strColName + " [" + mpField.get("col_pos") + "] position is error!");
          continue;
        }
        // _log.showDebug("col_code=" + strColCode + " col_pos=" + posis);

        row = sheet.getRow(posis[0] + index);
        currRow = posis[0] + index;
        if (row == null) row = sheet.createRow(posis[0] + index);

        cell = row.getCell(posis[1]);
        if (cell == null) cell = row.createCell(posis[1]);

        if (strColCode.equalsIgnoreCase("{CURUSERNAME}")) {
          // 当前用户
          if (_mpUser != null) strValue = _mpUser.get("user_name");
          else strValue = "";
        } else if (strColCode.equalsIgnoreCase("{CURDATE}")) {
          // 当前日期
          strValue = DateUtil.getToday();
        } else if (strColCode.equalsIgnoreCase("{CURDEPTNAME}")) {
          // 当前部门
          if (_mpUser != null) strValue = _mpUser.get("dept_name");
          else strValue = "";
        } else if (strColCode.equalsIgnoreCase("{NUMBER}")) {
          // 输出序号
          strValue = Integer.toString(rowIndex + 1);
          cntCol = (short) posis[1];
        } else if (strColCode.equalsIgnoreCase("{CURPAGENUM}")) {
          // 当前所在页数
          strValue = Integer.toString(curPage);
        } else if (strColCode.equalsIgnoreCase("{CURSUMPAGE}")) {
          // 当前共页数
          strValue = Integer.toString(sumPage);
        } else {
          // 设置cell的显示值
          strValue = mpData.get(strColCode);
          // 如果已经设置了0值不输出,并且当前值就是0,则输出空字符串
          strValue = (strValue != null) ? strValue : "";
          strValue = (strValue.equalsIgnoreCase("null")) ? "" : strValue;

          // 真实值与显示值
          strValue = getComboTitle(strValue, strColTag);
          // 转换数据格式
          strValue = convertValue(strValue, strStyle);
          if (isOutZero.equals("0")) strValue = getZeroOut(strValue, strStyle);
        }
        cell.setCellValue(strValue.trim());

        if (!lsStatCol.isEmpty()) {
          for (int iStat = 0, statNum = lsStatCol.size(); iStat < statNum; iStat++) {
            mpStat = lsStatCol.get(iStat);
            // _log.showDebug("mpStat = " + mpStat.toString());

            if (mpStat.isEmpty()) continue;
            strCol = (mpStat.get("col_code")).toLowerCase();
            // _log.showDebug("strCol = " + strCol + " | strValue = " + strValue);
            if (strColCode.equalsIgnoreCase(strCol)) {
              isStatCol = true;

              if (mpStatValue.get(strCol) == null) bdStat = new BigDecimal("0");
              else bdStat = new BigDecimal(mpStatValue.get(strCol));

              if (strValue.length() == 0) strValue = "0";
              // _log.showDebug("bdStat.add(new BigDecimal(strValue)) = " + bdStat.add(new
              // BigDecimal(strValue)));

              mpStatValue.put(strCol, bdStat.add(new BigDecimal(strValue)).toString());
            }
          }
        }
      }
    }

    // 填写每页小计
    if (isStatCol == true) {
      currRow++;
      row = sheet.getRow(currRow);
      if (row == null) row = sheet.createRow(currRow);
      cell = row.getCell(cntCol);
      if (cell == null) cell = row.createCell(cntCol);
      cell.setCellValue("小计");
      for (int i = 0, colNum = lsStatCol.size(); i < colNum; i++) {
        mpField = lsStatCol.get(i);
        posis = getPosition(mpField.get("col_pos"));

        // if (row == null) row = sheet.createRow(posi + cnt);

        cell = row.getCell(posis[1]);
        if (cell == null) cell = row.createCell(posis[1]);

        strColCode = (mpField.get("col_code")).toLowerCase();
        strValue = mpStatValue.get(strColCode);
        strStyle = mpField.get("format");
        strValue = convertValue(strValue, strStyle);
        cell.setCellValue(strValue.trim());
      }
    }
    return sheet;
  }
/**
 * 主键编码规则为:前缀+3位随机数字+流水号 前缀 -- 采用项目代号; 3位随机号 -- 为保证主键值唯一 流水号 -- 采用唯一的数字;
 *
 * <p>主键序号生成设计思想与CodeCreator编码序号相同。
 *
 * @author TonyTan
 * @version 1.0, 2010-11-4
 */
public class KeyCreator {
  private static KeyCreator _instance = new KeyCreator();

  private static Log _log = Log.getInstance();
  private static BaseDao _dao = BaseDao.getInstance();

  // 每次取主键数量
  private static final int POOL_SIZE = 10000000;
  // 缓存不同表的键对象
  private static Map<String, KeyInfo> _keyList = FactoryUtil.newMap();

  private KeyCreator() {}

  public static KeyCreator getInstance() {
    return _instance;
  }

  /**
   * 创建一数据表的主键值.
   *
   * @param tableName - 数据表名
   * @return
   */
  public synchronized String createKey(String tableName) {
    return getKeyPrefix() + getRandom() + createSerialNo(tableName);
  }

  /**
   * 创建新的树型记录ID
   *
   * @param parentID
   * @param level
   * @param tableName
   * @param pkField
   * @param levelCol
   * @param dsName
   * @return
   */
  public synchronized String createTreeKey(
      String parentID,
      int level,
      String tableName,
      String pkField,
      String levelCol,
      String dsName) {
    if (parentID == null) parentID = "";

    StringBuilder selsql = new StringBuilder();
    StringBuilder wheresql = new StringBuilder();
    selsql.append(" select max(" + pkField + ") as maxval, count(" + pkField + ") as cnt ");
    wheresql.append(" from " + tableName);
    if (parentID.length() == 0) {
      wheresql.append(" where " + pkField + " like '%' ");
      wheresql.append(" and " + levelCol + " = " + level);
    } else {
      wheresql.append(" where " + pkField + " like '" + parentID + "%' ");
      wheresql.append(" and " + levelCol + " = " + level);
    }
    selsql.append(wheresql);

    _log.showDebug("treeid selectsql = " + selsql.toString());

    DaoParam param = _dao.createParam(selsql.toString());
    param.setDsName(dsName);
    Map<String, String> keyMap = _dao.queryMap(param);

    String maxVal = keyMap.get("maxval");
    if (maxVal.length() == 0) {
      if (parentID.length() == 0) {
        return "1001";
      } else {
        // 本级别第一个节点
        return parentID + "0001";
      }
    } else {
      if (maxVal.equals("9999")) {
        _log.showWarn("treeid exceed 9999!");
        return "9999";
      }
    }
    maxVal = "0000" + (Long.parseLong(maxVal) + 1);
    String treeID = parentID + maxVal.substring(maxVal.length() - 4, maxVal.length());

    return treeID;
  }

  /**
   * 取配置文件中的项目代号为主键前缀
   *
   * @return
   */
  private String getKeyPrefix() {
    String prefix = SystemConfig.getConfigByKey("ServerConfigs", "projectcode");
    if (prefix.length() == 0) prefix = "dh";
    return prefix;
  }

  /**
   * 取主键的序号
   *
   * @return
   */
  private String createSerialNo(String tableName) {
    KeyInfo keyInfo;

    tableName = tableName.toLowerCase();
    if (_keyList.containsKey(tableName)) {
      keyInfo = _keyList.get(tableName);
    } else {
      // 如果缓存对象数量超过1000,则清除所有缓存对象
      if (_keyList.size() > 1000) {
        _keyList.clear();
      }

      keyInfo = new KeyInfo(POOL_SIZE, tableName);
      _keyList.put(tableName, keyInfo);
    }

    // 取新的序号
    int serial = keyInfo.getNextKey();
    return Integer.toString(serial);
  }

  /**
   * 为确保主键万无一失,每个主键前加一个3位随机数
   *
   * @return
   */
  private String getRandom() {
    int random = (int) (Math.random() * 1000);
    StringBuilder fix = new StringBuilder(Integer.toString(random));

    int len = 3 - fix.length();
    for (int i = 0; i < len; fix.insert(0, '0'), i++) ;

    return fix.toString();
  }

  /**
   * 主键唯一序号生成类
   *
   * @author TonyTan
   * @version 1.0, 2010-11-2
   */
  private class KeyInfo {
    private int keyMax;
    private int keyMin;
    private int nextKey;
    private int poolSize;
    private String keyName;

    public KeyInfo(int poolSize, String keyName) {
      this.poolSize = poolSize;
      this.keyName = keyName;

      retrieveFromDB();
    }

    public int getNextKey() {
      if (nextKey > keyMax) {
        retrieveFromDB();
      }

      // 每次取新号时,更新当前最大值;采用累加1的方式可以解决多线程多事务累加不丢失的问题。
      String usql = "update sys_tableid set max_value = max_value + 1 where table_name = ?";
      DaoParam uparam = _dao.createParam(usql);
      uparam.addStringValue(keyName);
      if (!_dao.update(uparam)) {
        _log.showWarn("get next code no update error!! tablename={0}!!", keyName);
      }

      return nextKey++;
    }

    private void retrieveFromDB() {
      // 从数据库中取上次分配的最大值
      int dbmax = 0;
      String ssql = "select max_value from sys_tableid where table_name = ?";

      DaoParam sparam = _dao.createParam(ssql);
      sparam.addStringValue(keyName);
      Map<String, String> mpMax = _dao.queryMap(sparam);

      if (!mpMax.isEmpty()) {
        dbmax = Integer.parseInt(mpMax.get("max_value"));
      } else {
        // 新建一条记录
        String usql = "insert into sys_tableid(max_value, table_name) values(0, ?)";
        DaoParam uparam = _dao.createParam(usql);
        uparam.addStringValue(keyName);
        if (!_dao.update(uparam)) {
          _log.showWarn("get next keyid no update error!! tablename={0}!!", keyName);
        }
      }

      keyMax = dbmax + poolSize;
      keyMin = dbmax + 1;
      nextKey = keyMin;
    }
  }
}