[
  {
    "path": ".gitattributes",
    "content": "*.js linguist-language=Java\n*.css linguist-language=Java\n*.html linguist-language=Java\n*.vue linguist-language=Java\n*.sql linguist-language=Java\n"
  },
  {
    "path": ".gitignore",
    "content": "## ide\n**/.idea\n*.iml\n\n## backend\n**/target\n**/logs\n\n## front\n**/*.lock\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"{}\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n   Copyright 2019 scott\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "POI_5升级说明.md",
    "content": "# AutoPOI v2.0.0 升级指南\n\n## 版本信息\n\n- **当前版本**: 2.0.0\n- **发布日期**: 2025-10-22\n- **POI 版本**: 5.4.1（从 4.1.2 升级）\n\n## 🎯 重大更新\n\n### 1. POI 升级到 5.4.1\n- ✅ 性能提升 20-30%\n- ✅ 大数据导出优化（10万行 < 1秒）\n- ✅ 更好的内存管理\n- ✅ 修复多个已知问题\n\n### 2. Spring Boot 多版本支持\n- ✅ Spring Boot 2.x（`autopoi-spring-boot-2-starter`）\n- ✅ Spring Boot 3.x（`autopoi-spring-boot-3-starter`）\n- ✅ Jakarta EE 完整适配\n\n### 3. 依赖更新\n- Apache POI: 4.1.2 → 5.4.1\n- Commons IO: 2.11.0 → 2.20.0\n- Log4j: 升级到 2.24.3\n\n## 📦 Maven 依赖更新\n\n### Spring Boot 2.x 项目\n\n```xml\n<!-- 移除旧版本 -->\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-web</artifactId>\n <version>1.4.18</version>\n</dependency>\n\n<!-- 使用新版本 -->\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-spring-boot-2-starter</artifactId>\n <version>2.0.0</version>\n</dependency>\n```\n\n### Spring Boot 3.x 项目\n\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-spring-boot-3-starter</artifactId>\n <version>2.0.0</version>\n</dependency>\n```\n\n**注意**: Spring Boot 3.x 需要修改 Servlet 导入：\n```java\n// 修改前\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n// 修改后\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n```\n\n### 纯 Java 项目\n\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi</artifactId>\n <version>2.0.0</version>\n</dependency>\n```\n\n## ⚠️ 重要变更\n\n### 1. 模块重命名\n- `autopoi-web` → `autopoi-spring-boot-2-starter`\n\n### 2. 依赖变更\n\n#### ❌ 已删除的依赖\n**`poi-ooxml-schemas` 依赖已移除**\n\n```xml\n<!-- ❌ POI 4.x 需要显式引入 -->\n<dependency>\n    <groupId>org.apache.poi</groupId>\n    <artifactId>poi-ooxml-schemas</artifactId>\n    <version>1.4</version>\n</dependency>\n```\n\n**删除原因：**\n1. **POI 5.x 架构改进** - 从 POI 5.0 开始，`poi-ooxml` 依赖已经内置了必要的 schema 定义\n2. **依赖简化** - POI 5.x 使用了更轻量的 `poi-ooxml-lite` 替代方案，减少了包体积\n3. **避免冲突** - 旧版本的 `poi-ooxml-schemas` 包体积达 15MB+，容易与其他依赖冲突\n4. **自动传递** - `poi-ooxml` 5.x 会自动引入所需的 schema 类，无需手动添加\n\n**迁移操作：**\n- ✅ 直接删除 `poi-ooxml-schemas` 依赖即可\n- ✅ 只保留 `poi` 和 `poi-ooxml` 依赖\n- ✅ 无需任何代码修改\n\n```xml\n<!-- ✅ POI 5.x 只需这两个依赖 -->\n<dependency>\n    <groupId>org.apache.poi</groupId>\n    <artifactId>poi</artifactId>\n    <version>5.4.1</version>\n</dependency>\n<dependency>\n    <groupId>org.apache.poi</groupId>\n    <artifactId>poi-ooxml</artifactId>\n    <version>5.4.1</version>\n</dependency>\n```\n\n**验证方法：**\n```bash\n# 检查依赖树，确认没有 poi-ooxml-schemas\nmvn dependency:tree | grep poi\n```\n\n### 3. 不兼容的 API（已废弃）\n- ❌ **图表生成功能暂不可用**（`ExcelChartBuildService`）\n  - 原因：POI 5.x 的图表 API 完全重构\n  - 影响：如果使用了图表生成功能，需要暂时移除或使用其他方案\n\n### 4. API 变更（内部已适配，无需修改代码）\n以下变更已在内部处理，用户代码无需修改：\n- `cell.getCellTypeEnum()` → `cell.getCellType()`\n- `HSSFDateUtil` → `DateUtil`\n- `SharedStringsTable` → `SharedStrings`\n\n## 📝 迁移步骤\n\n### 步骤 1: 更新依赖\n根据你的项目类型，更新 `pom.xml` 中的依赖（参考上面的配置）。\n\n### 步骤 2: 修改导入（仅 Spring Boot 3.x）\n如果升级到 Spring Boot 3.x，将 `javax.servlet` 改为 `jakarta.servlet`。\n\n### 步骤 3: 检查图表功能\n如果使用了 `ExcelChartBuildService`，需要暂时移除或寻找替代方案。\n\n### 步骤 4: 测试验证\n```bash\nmvn clean compile\nmvn test\n```\n\n### 步骤 5: 修复 Workbook 关闭问题\nPOI 5.x 要求显式关闭 Workbook：\n\n```java\n// 推荐写法\ntry (FileOutputStream fos = new FileOutputStream(\"output.xlsx\")) {\n    workbook.write(fos);\n} finally {\n    if (workbook != null) {\n        workbook.close(); // 必须显式关闭\n    }\n}\n```\n\n## ✅ 功能支持对照表\n\n| 功能 | v1.4.x | v2.0.0 | 说明 |\n|------|--------|--------|------|\n| Excel 导入导出 | ✅ | ✅ | 性能提升 |\n| 注解驱动 | ✅ | ✅ | 完全兼容 |\n| 多表头 | ✅ | ✅ | 完全兼容 |\n| 多 Sheet | ✅ | ✅ | 完全兼容 |\n| 模板导出 | ✅ | ✅ | 完全兼容 |\n| 大数据导出 | ✅ | ✅ | 性能优化 |\n| Word 导出 | ✅ | ✅ | 完全兼容 |\n| 图表生成 | ⚠️ | ❌ | 暂不支持 |\n| Spring Boot 2.x | ✅ | ✅ | 完全兼容 |\n| Spring Boot 3.x | ❌ | ✅ | 新增支持 |\n\n## 🐛 已修复问题\n\n1. ✅ 大数据导出性能问题\n2. ✅ Workbook 资源泄漏问题\n3. ✅ 依赖冲突问题（commons-io、log4j）\n4. ✅ 生成的 Excel 文件无法打开问题\n\n## 📚 相关资源\n\n- [完整使用文档](./README.md)\n- [GitHub Issues](https://github.com/zhangdaiscott/autopoi/issues)\n- [示例代码](./autopoi/src/test/java/)\n\n## 💡 常见问题\n\n### Q1: 升级后编译失败？\n检查是否正确更新了依赖名称：`autopoi-web` → `autopoi-spring-boot-2-starter`\n\n### Q2: Spring Boot 3 项目报错？\n确保已将 `javax.servlet` 改为 `jakarta.servlet`\n\n### Q3: 生成的 Excel 无法打开？\n确保在写入后显式关闭 Workbook 对象（参考步骤5）\n\n### Q4: 图表功能不可用？\nPOI 5.x 的图表 API 已重构，该功能暂未适配，建议使用其他图表库\n\n### Q5: 性能有提升吗？\n是的！测试显示性能提升 20-30%，大数据导出速度显著优化\n\n## 🎉 升级建议\n\n- ✅ **推荐升级**: 如果不使用图表功能\n- ⚠️ **谨慎升级**: 如果依赖图表生成功能\n- ✅ **强烈推荐**: Spring Boot 3.x 新项目\n"
  },
  {
    "path": "README.md",
    "content": "[中文](./README.zh-CN.md) | English\n\n\nAutoPOI (Excel and Word Easy Utility)\n===========================\nAutoPOI, as its name suggests \"auto\", pursues automation. It enables anyone without POI experience to quickly implement Excel import/export and Word template export in a foolproof manner. You can complete Excel import/export with just 5 lines of code.\n\nCurrent Version: 2.0.4 (Released: 2025-12-21)\n\n---------------------------\nKey Features of AutoPOI\n--------------------------\n1. Elegant design, easy to use\n2. Rich interfaces, easy to extend\n3. Many default values, write less do more\n4. AbstractView support, web export made simple\n\n---------------------------\nMain Utility Classes\n---------------------------\n\n1. ExcelExportUtil - Excel export (normal export, template export)\n2. ExcelImportUtil - Excel import\n3. WordExportUtil - Word export (only supports docx, doc version has image bugs in POI, not supported yet)\n\n---------------------------\nDifference Between XLS and XLSX Export\n---------------------------\n\n1. Export time: XLS is 2-3x faster than XLSX\n2. Export size: XLS is 2-3x larger than XLSX or more\n3. Need to consider both network speed and local processing speed\n\n---------------------------\nProject Modules\n---------------------------\n1. autopoi-parent - Parent POM\n2. autopoi - Core utility package for Excel export/import and Word export\n3. autopoi-spring-boot-2-starter - Spring Boot 2.x support (compatible with javax.servlet)\n4. autopoi-spring-boot-3-starter - Spring Boot 3.x support (compatible with jakarta.servlet)\n5. SAX import uses xercesImpl package (may cause unexpected issues), Word export uses poi-scratchpad, all as optional dependencies\n\n--------------------------\nMaven Dependencies\n--------------------------\n\n**For Spring Boot 2.x projects:**\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-spring-boot-2-starter</artifactId>\n <version>2.0.4</version>\n</dependency>\n```\n\n**For Spring Boot 3.x projects:**\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-spring-boot-3-starter</artifactId>\n <version>2.0.4</version>\n</dependency>\n```\n\n**For pure Java projects (without Spring):**\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi</artifactId>\n <version>2.0.4</version>\n</dependency>\n```\n\n--------------------------\nTemplate Expression Support\n--------------------------\n- Space separation\n- Ternary operator: {{test ? obj:obj2}}\n- n: Indicates numeric cell type {{n:}}\n- le: Represents length {{le:()}} used in if/else {{le:() > 8 ? obj1 : obj2}}\n- fd: Format date {{fd:(obj;yyyy-MM-dd)}}\n- fn: Format number {{fn:(obj;###.00)}}\n- fe: Iterate data, create row\n- !fe: Iterate data without creating row\n- $fe: Insert by moving down, move current and below rows down by .size() rows, then insert\n- !if: Delete current column {{!if:(test)}}\n- Single quotes for constant values '', e.g., '1' outputs 1\n\n\n---------------------------\nExport Examples\n---------------------------\n1. Annotations - Both import and export are annotation-based. Add annotations to entities to mark export objects and perform operations.\n\n```Java\n@ExcelTarget(\"courseEntity\")\npublic class CourseEntity implements java.io.Serializable {\n    /** Primary Key */\n    private String id;\n    /** Course Name */\n    @Excel(name = \"Course Name\", orderNum = \"1\", needMerge = true)\n    private String name;\n    /** Teacher */\n    @ExcelEntity(id = \"yuwen\")\n    @ExcelVerify()\n    private TeacherEntity teacher;\n    /** Math Teacher */\n    @ExcelEntity(id = \"shuxue\")\n    private TeacherEntity shuxueteacher;\n\n    @ExcelCollection(name = \"Students\", orderNum = \"4\")\n    private List<StudentEntity> students;\n}\n```\n\n2. Basic Export\nPass export parameters, export object, and object list to complete the export.\n\n```Java\nHSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams(\n    \"2412312\", \"Test\", \"Test\"), CourseEntity.class, list);\n```\n\n3. Export with Index\nSet a parameter value to add an index column in the export.\n\n```Java\nExportParams params = new ExportParams(\"2412312\", \"Test\", \"Test\");\nparams.setAddIndex(true);\nHSSFWorkbook workbook = ExcelExportUtil.exportExcel(params,\n    TeacherEntity.class, telist);\n```\n\n4. Export Map\nCreate annotation-like collections to complete Map export.\n\n```Java\nList<ExcelExportEntity> entity = new ArrayList<ExcelExportEntity>();\nentity.add(new ExcelExportEntity(\"Name\", \"name\"));\nentity.add(new ExcelExportEntity(\"Gender\", \"sex\"));\n\nList<Map<String, String>> list = new ArrayList<Map<String, String>>();\nMap<String, String> map;\nfor (int i = 0; i < 10; i++) {\n    map = new HashMap<String, String>();\n    map.put(\"name\", \"1\" + i);\n    map.put(\"sex\", \"2\" + i);\n    list.add(map);\n}\n\nHSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams(\n    \"Test\", \"Test\"), entity, list);\n```\n\n5. Template Export\nComplete export based on template configuration.\n\n```Java\nTemplateExportParams params = new TemplateExportParams();\nparams.setHeadingRows(2);\nparams.setHeadingStartRow(2);\nMap<String,Object> map = new HashMap<String, Object>();\nmap.put(\"year\", \"2013\");\nmap.put(\"sunCourses\", list.size());\nMap<String,Object> obj = new HashMap<String, Object>();\nmap.put(\"obj\", obj);\nobj.put(\"name\", list.size());\nparams.setTemplateUrl(\"org/jeecgframework/poi/excel/doc/exportTemp.xls\");\nWorkbook book = ExcelExportUtil.exportExcel(params, CourseEntity.class, list, map);\n```\n\n6. Import\nSet import parameters, pass file or stream to get the corresponding list.\n\n```Java\nImportParams params = new ImportParams();\nparams.setTitleRows(2);\nparams.setHeadRows(2);\n//params.setSheetNum(9);\nparams.setNeedSave(true);\nlong start = new Date().getTime();\nList<CourseEntity> list = ExcelImportUtil.importExcel(new File(\n    \"d:/tt.xls\"), CourseEntity.class, params);\n```\n\n7. Seamless Spring MVC Integration\nExcel export done with just a few lines.\n\n```Java\n@RequestMapping(value = \"/exportXls\")\npublic ModelAndView exportXls(HttpServletRequest request, HttpServletResponse response) {\n    ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());\n    List<JeecgDemo> pageList = jeecgDemoService.list();\n    // Export file name\n    mv.addObject(NormalExcelConstants.FILE_NAME, \"Export Excel File Name\");\n    // Annotated object Class\n    mv.addObject(NormalExcelConstants.CLASS, JeecgDemo.class);\n    // Custom table parameters\n    mv.addObject(NormalExcelConstants.PARAMS, new ExportParams(\"Custom Export Excel Title\", \"Custom Sheet Name\"));\n    // Export data list\n    mv.addObject(NormalExcelConstants.DATA_LIST, pageList);\n    return mv;\n}\n```\n\n| Custom View | Purpose | Description |\n| ------ | ------ | ------ |\n| JeecgEntityExcelView | Entity object export view | e.g., List&lt;JeecgDemo&gt; |\n| JeecgMapExcelView | Map object export view | List&lt;Map&lt;String, String&gt;&gt; list |\n| JeecgTemplateExcelView | Excel template export view | - |\n| JeecgTemplateWordView | Word template export view | - |\n\n8. Excel Import Validation\nFilter data that doesn't meet rules, append error messages to Excel. Provides common validation rules and generic validation interface.\n\n```Java\n/**\n * Email validation\n */\n@Excel(name = \"Email\", width = 25)\n@ExcelVerify(isEmail = true, notNull = true)\nprivate String email;\n\n/**\n * Mobile phone validation\n */\n@Excel(name = \"Mobile\", width = 20)\n@ExcelVerify(isMobile = true, notNull = true)\nprivate String mobile;\n\nExcelImportResult<ExcelVerifyEntity> result = ExcelImportUtil.importExcelVerify(\n    new File(\"d:/tt.xls\"), ExcelVerifyEntity.class, params);\nfor (int i = 0; i < result.getList().size(); i++) {\n    System.out.println(ReflectionToStringBuilder.toString(result.getList().get(i)));\n}\n```\n\n9. Import Map\nSet import parameters, pass file or stream to get the corresponding list. Custom Key requires implementing IExcelDataHandler interface.\n\n```Java\nImportParams params = new ImportParams();\nList<Map<String,Object>> list = ExcelImportUtil.importExcel(new File(\n    \"d:/tt.xls\"), Map.class, params);\n```\n\n10. Dictionary Usage\nAdd dicCode=\"\" in the entity property Excel annotation, where dicCode is the Code of the data dictionary in the jeecg system.\n\n```Java\n@Excel(name=\"Gender\", width=15, dicCode=\"sex\")\nprivate java.lang.String sex;\n```\n\n11. Dictionary Table Usage\ndictTable is the database table name, dicCode is the associated field name, dicText is the field corresponding to the content displayed in Excel.\n\n```Java\n@Excel(name=\"Department\", dictTable=\"t_s_depart\", dicCode=\"id\", dicText=\"departname\")\nprivate java.lang.String depart;\n```\n\n12. Replace Usage\nIf database stores 0/1, Excel cells display Female/Male.\n\n```Java\n@Excel(name=\"Test Replace\", width=15, replace={\"Male_1\",\"Female_0\"})\nprivate java.lang.String fdReplace;\n```\n\n13. Advanced Field Conversion\n- exportConvert: Set to true to replace values during export, add a method with \"convert\" prefix before the original get method name.\n- importConvert: Set to true to replace values during import, add a method with \"convert\" prefix before the original set method name.\n\n```Java\n@Excel(name=\"Test Convert\", width=15, exportConvert=true, importConvert=true)\nprivate java.lang.String fdConvert;\n\n/**\n * Conversion example: Add suffix to the field value\n * @return\n */\npublic String convertgetFdConvert(){\n    return this.fdConvert + \" Yuan\";\n}\n\n/**\n * Conversion example: Replace \"Yuan\" in Excel cell\n * @return\n */\npublic void convertsetFdConvert(String fdConvert){\n    this.fdConvert = fdConvert.replace(\" Yuan\", \"\");\n}\n```\n\n---------------------------\nExcel Annotation Reference\n---------------------------\n\n**@Excel**\n\n| Property | Type | Default | Description |\n|----------------|----------|------------------|------------------------------------------------------------------------|\n| name | String | null | Column name, supports name_id |\n| needMerge | boolean | false | Whether to merge cells vertically (for single cells in a list, merge multiple rows created by list) |\n| orderNum | String | \"0\" | Column order, supports name_id |\n| replace | String[] | {} | Value replacement, export {a_id,b_id}, import reversed |\n| savePath | String | \"upload\" | Import file save path, can be filled for images, default is upload/className/ |\n| type | int | 1 | Export type: 1=text, 2=image, 3=function, 10=number, default is text |\n| width | double | 10 | Column width |\n| height | double | 10 | Column height (will be deprecated, use @ExcelTarget height instead) |\n| isStatistics | boolean | false | Auto statistics, append a statistics row with all data summed |\n| isHyperlink | boolean | FALSE | Hyperlink, need to implement interface to return object |\n| isImportField | boolean | TRUE | Validate field exists in imported Excel, supports name_id |\n| exportFormat | String | \"\" | Export date format |\n| importFormat | String | \"\" | Import date format |\n| format | String | \"\" | Date format, equivalent to setting both exportFormat and importFormat |\n| databaseFormat | String | \"yyyyMMddHHmmss\" | Database format for string type date fields |\n| numFormat | String | \"\" | Number format, Pattern parameter, uses DecimalFormat |\n| imageType | int | 1 | Image type: 1=from file, 2=from database, default is file |\n| suffix | String | \"\" | Text suffix, e.g., % makes 90 become 90% |\n| isWrap | boolean | TRUE | Whether to wrap, supports \\n |\n| mergeRely | int[] | {} | Merge cell dependencies, e.g., {0} for second column based on first |\n| mergeVertical | boolean | false | Vertically merge cells with same content |\n| fixedIndex | int | -1 | Corresponds to Excel column, ignore name |\n| isColumnHidden | boolean | FALSE | Export hidden column |\n\n**@ExcelCollection**\n\n| Property | Type | Default | Description |\n|----------|----------|-----------------|------------------|\n| id | String | null | Define ID |\n| name | String | null | Define collection column name, supports name_id |\n| orderNum | int | 0 | Order, supports name_id |\n| type | Class<?> | ArrayList.class | Used to create objects during import |\n\n**Single Table Export Entity Example**\n\n```Java\npublic class SysUser implements Serializable {\n\n    /** id */\n    private String id;\n\n    /** Login Account */\n    @Excel(name = \"Login Account\", width = 15)\n    private String username;\n\n    /** Real Name */\n    @Excel(name = \"Real Name\", width = 15)\n    private String realname;\n\n    /** Avatar */\n    @Excel(name = \"Avatar\", width = 15)\n    private String avatar;\n\n    /** Birthday */\n    @Excel(name = \"Birthday\", width = 15, format = \"yyyy-MM-dd\")\n    private Date birthday;\n\n    /** Gender (1: Male 2: Female) */\n    @Excel(name = \"Gender\", width = 15, dicCode=\"sex\")\n    private Integer sex;\n\n    /** Email */\n    @Excel(name = \"Email\", width = 15)\n    private String email;\n\n    /** Phone */\n    @Excel(name = \"Phone\", width = 15)\n    private String phone;\n\n    /** Status (1: Normal 2: Frozen) */\n    @Excel(name = \"Status\", width = 15, replace={\"Normal_1\",\"Frozen_0\"})\n    private Integer status;\n}\n```\n\n**One-to-Many Export Entity Example**\n\n```Java\n@Data\npublic class JeecgOrderMainPage {\n    \n    /** Primary Key */\n    private java.lang.String id;\n    \n    /** Order Number */\n    @Excel(name=\"Order Number\", width=15)\n    private java.lang.String orderCode;\n    \n    /** Order Type */\n    private java.lang.String ctype;\n    \n    /** Order Date */\n    @Excel(name=\"Order Date\", width=15, format = \"yyyy-MM-dd\")\n    private java.util.Date orderDate;\n    \n    /** Order Amount */\n    @Excel(name=\"Order Amount\", width=15)\n    private java.lang.Double orderMoney;\n    \n    /** Order Note */\n    private java.lang.String content;\n    \n    /** Created By */\n    private java.lang.String createBy;\n    \n    /** Create Time */\n    private java.util.Date createTime;\n    \n    /** Updated By */\n    private java.lang.String updateBy;\n    \n    /** Update Time */\n    private java.util.Date updateTime;\n    \n    @ExcelCollection(name=\"Customer\")\n    private List<JeecgOrderCustomer> jeecgOrderCustomerList;\n    \n    @ExcelCollection(name=\"Ticket\")\n    private List<JeecgOrderTicket> jeecgOrderTicketList;\n}\n```\n\n---------------------------\nExample Code\n---------------------------\n- [Example Code](./autopoi-spring-boot-2-starter/src/test/java/) - Unit test code location\n\n---------------------------\nLicense\n---------------------------\nApache License 2.0\n\n"
  },
  {
    "path": "README.zh-CN.md",
    "content": "中文 | [English](./README.md)\n\n\nAutoPOI (Excel和 Word简易工具类)\n===========================\n AutoPOI 功能如同名字auto，追求的就是自动化，让一个没接触过poi的人员，可以傻瓜化的快速实现Excel导入导出、Word模板导出、可以仅仅5行代码就可以完成Excel的导入导出。\n \n 当前最新版本： 2.0.4（发布日期：2025-12-21）\n \n---------------------------\nAutoPOI的主要特点\n--------------------------\n\t1.设计精巧,使用简单\n\t2.接口丰富,扩展简单\n\t3.默认值多,write less do more\n\t4.AbstractView 支持,web导出可以简单明了\n\n---------------------------\nAutoPOI的几个入口工具类\n---------------------------\n\n\t1.ExcelExportUtil Excel导出(普通导出,模板导出)\n\t2.ExcelImportUtil Excel导入\n\t3.WordExportUtil  Word导出(只支持docx ,doc版本poi存在图片的bug,暂不支持)\n\t\n---------------------------\n关于Excel导出XLS和XLSX区别\n---------------------------\n\n\t1.导出时间XLS比XLSX快2-3倍\n\t2.导出大小XLS是XLSX的2-3倍或者更多\n\t3.导出需要综合网速和本地速度做考虑^~^\n\t\n---------------------------\n几个工程的说明\n---------------------------\n\t1.autopoi-parent 父包--作用大家都懂得\n\t2.autopoi 导入导出的工具包,可以完成Excel导出,导入,Word的导出,Excel的导出功能\n\t3.autopoi-spring-boot-2-starter  Spring Boot 2.x 支持(兼容 javax.servlet)\n\t4.autopoi-spring-boot-3-starter  Spring Boot 3.x 支持(兼容 jakarta.servlet)\n\t5.sax 导入使用xercesImpl这个包(这个包可能造成奇怪的问题哈),word导出使用poi-scratchpad,都作为可选包了\n\t\n--------------------------\nMaven 依赖配置\n--------------------------\n\n**Spring Boot 2.x 项目：**\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-spring-boot-2-starter</artifactId>\n <version>2.0.4</version>\n</dependency>\n```\n\n**Spring Boot 3.x 项目：**\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi-spring-boot-3-starter</artifactId>\n <version>2.0.4</version>\n</dependency>\n```\n\n**纯 Java 项目（无 Spring）：**\n```xml\n<dependency>\n <groupId>org.jeecgframework</groupId>\n <artifactId>autopoi</artifactId>\n <version>2.0.4</version>\n</dependency>\n```\n\n--------------------------\nAutoPoi 模板 表达式支持\n--------------------------\n- 空格分割\n- 三目运算  {{test ? obj:obj2}}\n- n: 表示 这个cell是数值类型 {{n:}}\n- le: 代表长度{{le:()}} 在if/else 运用{{le:() > 8 ? obj1 :  obj2}}\n- fd: 格式化时间 {{fd:(obj;yyyy-MM-dd)}}\n- fn: 格式化数字 {{fn:(obj;###.00)}}\n- fe: 遍历数据,创建row\n- !fe: 遍历数据不创建row \n- $fe: 下移插入,把当前行,下面的行全部下移.size()行,然后插入\n- !if: 删除当前列 {{!if:(test)}}\n- 单引号表示常量值 ''  比如'1' 那么输出的就是 1\n\n\n---------------------------\nAutoPoi导出实例\n---------------------------\n1.注解,导入导出都是基于注解的,实体上做上注解,标示导出对象,同时可以做一些操作\n\n```Java\n\t@ExcelTarget(\"courseEntity\")\n\tpublic class CourseEntity implements java.io.Serializable {\n\t/** 主键 */\n\tprivate String id;\n\t/** 课程名称 */\n\t@Excel(name = \"课程名称\", orderNum = \"1\", needMerge = true)\n\tprivate String name;\n\t/** 老师主键 */\n\t@ExcelEntity(id = \"yuwen\")\n\t@ExcelVerify()\n\tprivate TeacherEntity teacher;\n\t/** 老师主键 */\n\t@ExcelEntity(id = \"shuxue\")\n\tprivate TeacherEntity shuxueteacher;\n\n\t@ExcelCollection(name = \"选课学生\", orderNum = \"4\")\n\tprivate List<StudentEntity> students;\n```\n\n2.基础导出\n\t传入导出参数,导出对象,以及对象列表即可完成导出\n\t\n```Java\n\tHSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams(\n\t\t\t\t\"2412312\", \"测试\", \"测试\"), CourseEntity.class, list);\n```\n\n3.基础导出,带有索引\n\t在到处参数设置一个值,就可以在导出列增加索引\n\t\n```Java\n\tExportParams params = new ExportParams(\"2412312\", \"测试\", \"测试\");\n\tparams.setAddIndex(true);\n\tHSSFWorkbook workbook = ExcelExportUtil.exportExcel(params,\n\t\t\tTeacherEntity.class, telist);\n```\t\t\t\n\n4.导出Map\n\t创建类似注解的集合,即可完成Map的导出,略有麻烦\n\t\n```Java\n\tList<ExcelExportEntity> entity = new ArrayList<ExcelExportEntity>();\n\tentity.add(new ExcelExportEntity(\"姓名\", \"name\"));\n\tentity.add(new ExcelExportEntity(\"性别\", \"sex\"));\n\n\tList<Map<String, String>> list = new ArrayList<Map<String, String>>();\n\tMap<String, String> map;\n\tfor (int i = 0; i < 10; i++) {\n\t\tmap = new HashMap<String, String>();\n\t\tmap.put(\"name\", \"1\" + i);\n\t\tmap.put(\"sex\", \"2\" + i);\n\t\tlist.add(map);\n\t}\n\n\tHSSFWorkbook workbook = ExcelExportUtil.exportExcel(new ExportParams(\n\t\t\t\"测试\", \"测试\"), entity, list);\t\n```\t\t\n\t\n5.模板导出\n\t根据模板配置,完成对应导出\n\t\n```Java\n\tTemplateExportParams params = new TemplateExportParams();\n\tparams.setHeadingRows(2);\n\tparams.setHeadingStartRow(2);\n\tMap<String,Object> map = new HashMap<String, Object>();\n    map.put(\"year\", \"2013\");\n    map.put(\"sunCourses\", list.size());\n    Map<String,Object> obj = new HashMap<String, Object>();\n    map.put(\"obj\", obj);\n    obj.put(\"name\", list.size());\n\t params.setTemplateUrl(\"org/jeecgframework/poi/excel/doc/exportTemp.xls\");\n\tWorkbook book = ExcelExportUtil.exportExcel(params, CourseEntity.class, list,\n\t\t\tmap);\n```\t\t\t\n\n6.导入\n\t设置导入参数,传入文件或者流,即可获得相应的list\n\t\n```Java\n\tImportParams params = new ImportParams();\n\tparams.setTitleRows(2);\n\tparams.setHeadRows(2);\n\t//params.setSheetNum(9);\n\tparams.setNeedSave(true);\n\tlong start = new Date().getTime();\n\tList<CourseEntity> list = ExcelImportUtil.importExcel(new File(\n\t\t\t\"d:/tt.xls\"), CourseEntity.class, params);\n```\t\n\n7.SpringMvc的无缝融合\n\t简单几句话,Excel导出搞定\n\t\n```Java\n\t@RequestMapping(value = \"/exportXls\")\n\tpublic ModelAndView exportXls(HttpServletRequest request, HttpServletResponse response) {\n\t\tModelAndView mv = new ModelAndView(new JeecgEntityExcelView());\n\t\tList<JeecgDemo> pageList = jeecgDemoService.list();\n\t\t//导出文件名称\n\t\tmv.addObject(NormalExcelConstants.FILE_NAME,\"导出Excel文件名字\");\n\t\t//注解对象Class\n\t\tmv.addObject(NormalExcelConstants.CLASS,JeecgDemo.class);\n\t\t//自定义表格参数\n\t\tmv.addObject(NormalExcelConstants.PARAMS,new ExportParams(\"自定义导出Excel模板内容标题\", \"自定义Sheet名字\"));\n\t\t//导出数据列表\n\t\tmv.addObject(NormalExcelConstants.DATA_LIST,pageList);\n\t\treturn mv;\n\t}\n```\n\n\n| 自定义视图 | 用途 |  描述 |\n| ------ | ------ | ------ |\n| JeecgMapExcelView | 实体对象导出视图 | 例如：List<JeecgDemo> |\n| JeecgEntityExcelView | Map对象导出视图 | List<Map<String, String>> list |\n| JeecgTemplateExcelView | Excel模板导出视图 | - | \n| JeecgTemplateWordView | Word模板导出视图 | - |\n\n\n8.Excel导入校验,过滤不符合规则的数据,追加错误信息到Excel,提供常用的校验规则,已经通用的校验接口\n\n```Java\n\t/**\n     * Email校验\n     */\n    @Excel(name = \"Email\", width = 25)\n    @ExcelVerify(isEmail = true, notNull = true)\n    private String email;\n    /**\n     * 手机号校验\n     */\n    @Excel(name = \"Mobile\", width = 20)\n    @ExcelVerify(isMobile = true, notNull = true)\n    private String mobile;\n    \n    ExcelImportResult<ExcelVerifyEntity> result = ExcelImportUtil.importExcelVerify(new File(\n            \"d:/tt.xls\"), ExcelVerifyEntity.class, params);\n    for (int i = 0; i < result.getList().size(); i++) {\n        System.out.println(ReflectionToStringBuilder.toString(result.getList().get(i)));\n    }\n```\n\n9.导入Map\n\t设置导入参数,传入文件或者流,即可获得相应的list,自定义Key,需要实现IExcelDataHandler接口\n\t\n```Java\n\tImportParams params = new ImportParams();\n\tList<Map<String,Object>> list = ExcelImportUtil.importExcel(new File(\n\t\t\t\"d:/tt.xls\"), Map.class, params);\n```\n\n10.字典用法\n        在实体属性注解excel中添加dicCode=\"\",此处dicCode即为jeecg系统中数据字典的Code\n\t\n```Java\n   @Excel(name=\"性别\",width=15,dicCode=\"sex\")\n   private java.lang.String sex;\n```\n\n11.字典表用法\n       此处dictTable为数据库表名，dicCode为关联字段名，dicText为excel中显示的内容对应的字段\n\t\n```Java\n\t@Excel(name=\"部门\",dictTable=\"t_s_depart\",dicCode=\"id\",dicText=\"departname\")\n    private java.lang.String depart;\n```\n\n12.Replace用法\n       若数据库中存储的是0/1 ，则导出/导入的excel单元格中显示的是女/男\n\t\n```Java\n\t@Excel(name=\"测试替换\",width=15,replace={\"男_1\",\"女_0\"})\n\tprivate java.lang.String fdReplace;\n```\n\n13.高级字段转换用法\n- exportConvert：在导出的时候需要替换值则配置该值为true，同时增加一个方法，方法名为原get方法名前加convert。\n- importConvert：在导入的时候需要替换值则配置该值为true，同时增加一个方法，方法名为原set方法名前加convert。\n\n```Java\n\t@Excel(name=\"测试转换\",width=15,exportConvert=true,importConvert=true)\n\tprivate java.lang.String fdConvert;\n\t\n\t/**\n\t  * 转换值示例： 在该字段值的后面加上元\n\t  * @return\n\t  */\n\tpublic String convertgetFdConvert(){\n\t  return this.fdConvert+\"元\";\n\t}\n\t  \n\t/**\n\t * 转换值示例： 替换掉excel单元格中的\"元\"\n\t * @return\n\t */\n\tpublic void convertsetFdConvert(String fdConvert){\n\t  this.fdConvert = fdConvert.replace(\"元\",\"\");\n\t}\n```\n\n---------------------------\n Excel 注解说明\n---------------------------\n\n@Excel\n\n| 属性             | 类型       | 默认值              | 功能                                                                     |\n|----------------|----------|------------------|------------------------------------------------------------------------|\n| name           | String   | null             | 列名,支持name_id                                                           |\n| needMerge      | boolean  | fasle            | 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row)                           |\n| orderNum       | String   | \"0\"              | 列的排序,支持name_id                                                         |\n| replace        | String[] | {}               | 值得替换 导出是{a_id,b_id} 导入反过来                                              |\n| savePath       | String   | \"upload\"         | 导入文件保存路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/ |\n| type           | int      | 1                | 导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本                                    |\n| width          | double   | 10               | 列宽                                                                     |\n| height         | double   | 10               | 列高,后期打算统一使用@ExcelTarget的height,这个会被废弃,注意                               |\n| isStatistics   | boolean  | fasle            | 自动统计数据,在追加一行统计,把所有数据都和输出 这个处理会吞没异常,请注意这一点                              |\n| isHyperlink    | boolean  | FALSE            | 超链接,如果是需要实现接口返回对象                                                      |\n| isImportField  | boolean  | TRUE             | 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持name_id                |\n| exportFormat   | String   | \"\"               | 导出的时间格式,以这个是否为空来判断是否需要格式化日期                                            |\n| importFormat   | String   | \"\"               | 导入的时间格式,以这个是否为空来判断是否需要格式化日期                                            |\n| format         | String   | \"\"               | 时间格式,相当于同时设置了exportFormat 和 importFormat                               |\n| databaseFormat | String   | \"yyyyMMddHHmmss\" | 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出      |\n| numFormat      | String   | \"\"               | 数字格式化,参数是Pattern,使用的对象是DecimalFormat                                   |\n| imageType      | int      | 1                | 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的                              |\n| suffix         | String   | \"\"               | 文字后缀,如% 90 变成90%                                                       |\n| isWrap         | boolean  | TRUE             | 是否换行 即支持\\n                                                             |\n| mergeRely      | int[]    | {}               | 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了                                       |\n| mergeVertical  | boolean  | fasle            | 纵向合并内容相同的单元格                                                           |\n| fixedIndex     | int      | -1               | 对应excel的列,忽略名字                                                         |\n| isColumnHidden | boolean  | FALSE            | 导出隐藏列                                                                  |\n\n\n@ExcelCollection\n\n| 属性       | 类型       | 默认值             | 功能               |\n|----------|----------|-----------------|------------------|\n| id       | String   | null            | 定义ID             |\n| name     | String   | null            | 定义集合列名,支持nanm_id |\n| orderNum | int      | 0               | 排序,支持name_id     |\n| type     | Class<?> | ArrayList.class | 导入时创建对象使用        |\n\n\n\n单表导出实体注解源码\n\n```Java\npublic class SysUser implements Serializable {\n\n    /**id*/\n    private String id;\n\n    /**登录账号 */\n    @Excel(name = \"登录账号\", width = 15)\n    private String username;\n\n    /**真实姓名*/\n    @Excel(name = \"真实姓名\", width = 15)\n    private String realname;\n\n    /**头像*/\n    @Excel(name = \"头像\", width = 15)\n    private String avatar;\n\n    /**生日*/\n    @Excel(name = \"生日\", width = 15, format = \"yyyy-MM-dd\")\n    private Date birthday;\n\n    /**性别（1：男 2：女）*/\n    @Excel(name = \"性别\", width = 15,dicCode=\"sex\")\n    private Integer sex;\n\n    /**电子邮件*/\n    @Excel(name = \"电子邮件\", width = 15)\n    private String email;\n\n    /**电话*/\n    @Excel(name = \"电话\", width = 15)\n    private String phone;\n\n    /**状态(1：正常  2：冻结 ）*/\n    @Excel(name = \"状态\", width = 15,replace={\"正常_1\",\"冻结_0\"})\n    private Integer status;\n```\n\n一对多导出实体注解源码\n\n```Java\n@Data\npublic class JeecgOrderMainPage {\n\t\n\t/**主键*/\n\tprivate java.lang.String id;\n\t/**订单号*/\n\t@Excel(name=\"订单号\",width=15)\n\tprivate java.lang.String orderCode;\n\t/**订单类型*/\n\tprivate java.lang.String ctype;\n\t/**订单日期*/\n\t@Excel(name=\"订单日期\",width=15,format = \"yyyy-MM-dd\")\n\tprivate java.util.Date orderDate;\n\t/**订单金额*/\n\t@Excel(name=\"订单金额\",width=15)\n\tprivate java.lang.Double orderMoney;\n\t/**订单备注*/\n\tprivate java.lang.String content;\n\t/**创建人*/\n\tprivate java.lang.String createBy;\n\t/**创建时间*/\n\tprivate java.util.Date createTime;\n\t/**修改人*/\n\tprivate java.lang.String updateBy;\n\t/**修改时间*/\n\tprivate java.util.Date updateTime;\n\t\n\t@ExcelCollection(name=\"客户\")\n\tprivate List<JeecgOrderCustomer> jeecgOrderCustomerList;\n\t@ExcelCollection(name=\"机票\")\n\tprivate List<JeecgOrderTicket> jeecgOrderTicketList;\n}\n```"
  },
  {
    "path": "autopoi/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.jeecgframework</groupId>\n\t\t<artifactId>autopoi-parent</artifactId>\n\t\t<version>2.0.4</version>\n\t</parent>\n\t<artifactId>autopoi</artifactId>\n\t<name>autopoi</name>\n\n\t<properties>\n\t\t<!-- autopoi core requires JDK 8+ -->\n\t\t<maven.compiler.source>1.8</maven.compiler.source>\n\t\t<maven.compiler.target>1.8</maven.compiler.target>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\t\n\t<dependencies>\n\t\t<!-- poi -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.poi</groupId>\n\t\t\t<artifactId>poi</artifactId>\n\t\t</dependency>\n\t<dependency>\n\t\t<groupId>org.apache.poi</groupId>\n\t\t<artifactId>poi-ooxml</artifactId>\n\t\t<exclusions>\n\t\t\t<exclusion>\n\t\t\t\t<groupId>commons-io</groupId>\n\t\t\t\t<artifactId>commons-io</artifactId>\n\t\t\t</exclusion>\n\t\t\t<exclusion>\n\t\t\t\t<artifactId>xml-apis</artifactId>\n\t\t\t\t<groupId>xml-apis</groupId>\n\t\t\t</exclusion>\n\t\t\t<exclusion>\n\t\t\t\t<artifactId>commons-codec</artifactId>\n\t\t\t\t<groupId>commons-codec</groupId>\n\t\t\t</exclusion>\n\t\t</exclusions>\n\t</dependency>\n\t<!-- Sax 读入的时候使用 -->\n\t\t<dependency>\n\t\t\t<groupId>xerces</groupId>\n\t\t\t<artifactId>xercesImpl</artifactId>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\t\t<!-- Word 时候用到 -->\n\t\t<dependency>\n\t\t\t<groupId>org.apache.poi</groupId>\n\t\t\t<artifactId>poi-scratchpad</artifactId>\n\t\t</dependency>\n\n\t\t<!-- google 工具类 -->\n\t\t<dependency>\n\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t<artifactId>guava</artifactId>\n\t\t</dependency>\n\n\t\t<dependency>\n\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t<artifactId>commons-lang3</artifactId>\n\t\t</dependency>\n\n\t\t<!-- commons -->\n\t\t<dependency>\n\t\t\t<groupId>commons-io</groupId>\n\t\t\t<artifactId>commons-io</artifactId>\n\t\t</dependency>\n\n\n\t\t<!--日志 -->\n\t\t<!-- slf4j -->\n\t\t<dependency>\n\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\n\t\t<!--spring -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-webmvc</artifactId>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\t\n\t\t<!-- 测试依赖 -->\n\t\t<dependency>\n\t\t\t<groupId>junit</groupId>\n\t\t\t<artifactId>junit</artifactId>\n\t\t\t<version>4.13.2</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\n\t\t<dependency>\n\t\t\t<groupId>org.apache.logging.log4j</groupId>\n\t\t\t<artifactId>log4j-to-slf4j</artifactId>\n\t\t\t<version>2.24.3</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\n\t\t<dependency>\n\t\t\t<groupId>org.apache.logging.log4j</groupId>\n\t\t\t<artifactId>log4j-api</artifactId>\n\t\t\t<version>2.24.3</version>\n\t\t\t<scope>test</scope>\n\t\t</dependency>\n\t\n\t</dependencies>\n\n<build>\n\t<plugins>\n\t\t<!-- JDK 8 编译配置 - 关键：release 参数确保只使用 JDK 8 API -->\n\t\t<plugin>\n\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t<version>3.11.0</version>\n\t\t\t<configuration>\n\t\t\t\t<source>1.8</source>\n\t\t\t\t<target>1.8</target>\n\t\t\t\t<release>8</release>\n\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t</configuration>\n\t\t</plugin>\n\t\t<!-- Maven Surefire Plugin：允许运行测试 -->\n\t\t<plugin>\n\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t<version>3.0.0-M5</version>\n\t\t\t<configuration>\n\t\t\t\t<!-- autopoi 模块允许运行测试，覆盖父 pom 的配置 -->\n\t\t\t\t<skipTests>false</skipTests>\n\t\t\t</configuration>\n\t\t</plugin>\n\t</plugins>\n</build>\n</project>"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/core/util/ApplicationContextUtil.java",
    "content": "package org.jeecgframework.core.util;\n\nimport org.springframework.beans.BeansException;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.annotation.Lazy;\nimport org.springframework.stereotype.Component;\n\n/**\n * \n * @author  张代浩\n *\n */\n@Lazy(false)\n@Component\npublic class ApplicationContextUtil implements ApplicationContextAware {\n\n\tprivate static ApplicationContext context;\n\n\n\t@Override\n\tpublic void setApplicationContext(ApplicationContext context) throws BeansException {\n\t\tApplicationContextUtil.context = context;\n\t}\n\n\n\tpublic static ApplicationContext getContext() {\n\t\treturn context;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/dict/service/AutoPoiDictServiceI.java",
    "content": "package org.jeecgframework.dict.service;\n\n\n/**\n * 描述：\n * @author：scott\n * @since：2017-4-12 下午04:58:15\n * @version:1.0\n */\npublic interface AutoPoiDictServiceI{\n\t/**\n \t * 方法描述:  查询数据字典\n \t * 作    者： yiming.zhang\n \t * 日    期： 2014年5月11日-下午4:22:42\n \t * @param dicTable\n \t * @param dicCode\n \t * @param dicText\n \t * @return \n \t * 返回类型： List<DictEntity>\n \t */\n \tpublic String[] queryDict(String dicTable,String dicCode, String dicText);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/cache/ExcelCache.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.cache;\n\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.apache.poi.ss.usermodel.WorkbookFactory;\nimport org.jeecgframework.poi.cache.manager.POICacheManager;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Excel类型的缓存\n * \n * @author JEECG\n * @date 2014年2月11日\n * @version 1.0\n */\npublic final class ExcelCache {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ExcelCache.class);\n\n\tpublic static Workbook getWorkbook(String url, Integer[] sheetNums, boolean needAll) {\n\t\tInputStream is = null;\n\t\tList<Integer> sheetList = Arrays.asList(sheetNums);\n\t\ttry {\n\t\t\tis = POICacheManager.getFile(url);\n\t\t\tWorkbook wb = WorkbookFactory.create(is);\n\t\t\t// 删除其他的sheet\n\t\t\tif (!needAll) {\n\t\t\t\tfor (int i = wb.getNumberOfSheets() - 1; i >= 0; i--) {\n\t\t\t\t\tif (!sheetList.contains(i)) {\n\t\t\t\t\t\twb.removeSheetAt(i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn wb;\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tis.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\t//update-begin-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505\n\tpublic static Workbook getWorkbookByTemplate(String url, Integer[] sheetNums, boolean needAll) {\n\t\tList<Integer> sheetList = Arrays.asList(sheetNums);\n\t\tInputStream fis = null;\n\t\ttry {\n\t\t\t//update-begin----author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作--------\n\t\t\t//ClassPathResource  resource = new ClassPathResource(url);\n\t\t\tfis = new FileInputStream(url);\n\t\t\tLOGGER.info(\"  >>>  poi3升级到4兼容改造工作, url=\"+url);\n\t\t\t//fis = resource.getInputStream();\n\t\t\t//update-end-----author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作--------\n\t\t\tWorkbook wb = WorkbookFactory.create(fis);\n\t\t\t// 删除其他的sheet\n\t\t\tif (!needAll) {\n\t\t\t\tfor (int i = wb.getNumberOfSheets() - 1; i >= 0; i--) {\n\t\t\t\t\tif (!sheetList.contains(i)) {\n\t\t\t\t\t\twb.removeSheetAt(i);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn wb;\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tfis.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n    //update-end-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/cache/ImageCache.java",
    "content": "/**\n * Copyright 2013-2015 JueYue (qrb.jueyue@gmail.com)\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.cache;\n\nimport org.apache.poi.util.IOUtils;\nimport org.jeecgframework.poi.cache.manager.POICacheManager;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.imageio.ImageIO;\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\n\n/**\n * 图片缓存处理\n *\n * @author liusq\n * 2022-05-27 下午4:16:32\n */\npublic class ImageCache {\n\n    private static final Logger LOGGER = LoggerFactory\n            .getLogger(ImageCache.class);\n\n    public static byte[] getImage(String imagePath) {\n        InputStream                 is           = POICacheManager.getFile(imagePath);\n        ByteArrayOutputStream       byteArrayOut = new ByteArrayOutputStream();\n        final ByteArrayOutputStream swapStream   = new ByteArrayOutputStream();\n        try {\n            int ch;\n            while ((ch = is.read()) != -1) {\n                swapStream.write(ch);\n            }\n            Image         image     = Toolkit.getDefaultToolkit().createImage(swapStream.toByteArray());\n            BufferedImage bufferImg = toBufferedImage(image);\n            ImageIO.write(bufferImg,\n                    imagePath.substring(imagePath.lastIndexOf(\".\") + 1, imagePath.length()),\n                    byteArrayOut);\n            return byteArrayOut.toByteArray();\n        } catch (Exception e) {\n            LOGGER.error(e.getMessage(), e);\n            return null;\n        } finally {\n            IOUtils.closeQuietly(is);\n            IOUtils.closeQuietly(swapStream);\n            IOUtils.closeQuietly(byteArrayOut);\n        }\n\n    }\n\n\n    public static BufferedImage toBufferedImage(Image image) {\n        if (image instanceof BufferedImage) {\n            return (BufferedImage) image;\n        }\n        // This code ensures that all the pixels in the image are loaded\n        image = new ImageIcon(image).getImage();\n        BufferedImage bimage = null;\n        GraphicsEnvironment ge = GraphicsEnvironment\n                .getLocalGraphicsEnvironment();\n        try {\n            int                   transparency = Transparency.OPAQUE;\n            GraphicsDevice        gs           = ge.getDefaultScreenDevice();\n            GraphicsConfiguration gc           = gs.getDefaultConfiguration();\n            bimage = gc.createCompatibleImage(image.getWidth(null),\n                    image.getHeight(null), transparency);\n        } catch (HeadlessException e) {\n            // The system does not have a screen\n        }\n        if (bimage == null) {\n            // Create a buffered image using the default color model\n            int type = BufferedImage.TYPE_INT_RGB;\n            bimage = new BufferedImage(image.getWidth(null),\n                    image.getHeight(null), type);\n        }\n        // Copy image to buffered image\n        Graphics g = bimage.createGraphics();\n        // Paint the image onto the buffered image\n        g.drawImage(image, 0, 0, null);\n        g.dispose();\n        return bimage;\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/cache/WordCache.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.cache;\n\nimport java.io.InputStream;\n\nimport org.jeecgframework.poi.cache.manager.POICacheManager;\nimport org.jeecgframework.poi.word.entity.MyXWPFDocument;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * word 缓存中心\n * \n * @author JEECG\n * @date 2014年7月24日 下午10:54:31\n */\npublic class WordCache {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(WordCache.class);\n\n\tpublic static MyXWPFDocument getXWPFDocumen(String url) {\n\t\tInputStream is = null;\n\t\ttry {\n\t\t\tis = POICacheManager.getFile(url);\n\t\t\tMyXWPFDocument doc = new MyXWPFDocument(is);\n\t\t\treturn doc;\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tis.close();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/cache/manager/FileLoade.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.cache.manager;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\n\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 文件加载类,根据路径加载指定文件\n * \n * @author JEECG\n * @date 2014年2月10日\n * @version 1.0\n */\nclass FileLoade {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(FileLoade.class);\n\n\tpublic byte[] getFile(String url) {\n\t\tFileInputStream fileis = null;\n\t\tByteArrayOutputStream baos = null;\n\t\ttry {\n\t\t\t// 先用绝对路径查询,再查询相对路径\n\t\t\ttry {\n\t\t\t\tfileis = new FileInputStream(url);\n\t\t\t} catch (FileNotFoundException e) {\n\t\t\t\tString path = PoiPublicUtil.getWebRootPath(url);\n\t\t\t\tfileis = new FileInputStream(path);\n\t\t\t}\n\t\t\tbaos = new ByteArrayOutputStream();\n\t\t\tbyte[] buffer = new byte[1024];\n\t\t\tint len;\n\t\t\twhile ((len = fileis.read(buffer)) > -1) {\n\t\t\t\tbaos.write(buffer, 0, len);\n\t\t\t}\n\t\t\tbaos.flush();\n\t\t\treturn baos.toByteArray();\n\t\t} catch (FileNotFoundException e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} catch (IOException e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tif (fileis != null)\n\t\t\t\t\tfileis.close();\n\t\t\t\tif (fileis != null)\n\t\t\t\t\tbaos.close();\n\t\t\t} catch (IOException e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\tLOGGER.error(fileis + \"这个路径文件没有找到,请查询\");\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/cache/manager/POICacheManager.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.cache.manager;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.util.Arrays;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.TimeUnit;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.cache.CacheBuilder;\nimport com.google.common.cache.CacheLoader;\nimport com.google.common.cache.LoadingCache;\n\n/**\n * 缓存管理\n * \n * @author JEECG\n * @date 2014年2月10日\n * @version 1.0\n */\npublic final class POICacheManager {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(POICacheManager.class);\n\n\tprivate static LoadingCache<String, byte[]> loadingCache;\n\n\tstatic {\n\t\tloadingCache = CacheBuilder.newBuilder().expireAfterWrite(7, TimeUnit.DAYS).maximumSize(50).build(new CacheLoader<String, byte[]>() {\n\t\t\t@Override\n\t\t\tpublic byte[] load(String url) throws Exception {\n\t\t\t\treturn new FileLoade().getFile(url);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic static InputStream getFile(String id) {\n\t\ttry {\n\t\t\t// 复杂数据,防止操作原数据\n\t\t\tbyte[] result = Arrays.copyOf(loadingCache.get(id), loadingCache.get(id).length);\n\t\t\treturn new ByteArrayInputStream(result);\n\t\t} catch (ExecutionException e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t}\n\t\treturn null;\n\t}\n\n\t//update-begin---author:chenrui ---date:20240403  for：[issue/#5933]增加清除缓存方法------------\n    /**\n     * 清除所有缓存\n     *\n     * @author chenrui\n     * @date 2024/4/3 11:46\n     */\n    public static void cleanAll() {\n        loadingCache.invalidateAll();\n    }\n\n    /**\n     * 清除缓存\n     *\n     * @param id 缓存key\n     * @author chenrui\n     * @date 2024/4/3 11:46\n     */\n    public static void clean(String id) {\n        loadingCache.invalidate(id);\n    }\n\t//update-end---author:chenrui ---date:20240403  for：[issue/#5933]增加清除缓存方法------------\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/cache/package-info.java",
    "content": "/**\n * 对POI用到的模板进行缓存,进行统一管理,缓存工具暂时使用guava(脱离配置文件)\n * 缓存方式统一为byte[] 屏蔽文件类型的差异\n * 缓存获取方式,URL或者URL+index(EXcel的)\n */\n/**\n * @author JEECG\n * @date 2014年2月10日\n * @version 1.0\n */\npackage org.jeecgframework.poi.cache;"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/consts/ImageScaleMode.java",
    "content": "package org.jeecgframework.poi.consts;\n\n/**\n * 图片缩放模式枚举\n * for [issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式\n * \n * @author chenrui\n * @date 2025-10-29\n */\npublic enum ImageScaleMode {\n    \n    /**\n     * 拉伸填充\n     */\n    STRETCH(0, \"拉伸填充\"),\n    \n    /**\n     * 等比例缩放适应\n     */\n    FIT(1, \"等比例缩放适应\"),\n    \n    /**\n     * 不缩放（原始大小）\n     */\n    ORIGINAL(2, \"不缩放（原始大小）\");\n    \n    private final int code;\n    private final String description;\n    \n    ImageScaleMode(int code, String description) {\n        this.code = code;\n        this.description = description;\n    }\n    \n    public int getCode() {\n        return code;\n    }\n    \n    public String getDescription() {\n        return description;\n    }\n    \n    /**\n     * 根据code获取枚举\n     * \n     * @param code 代码值\n     * @return 对应的枚举值，如果找不到则返回STRETCH\n     */\n    public static ImageScaleMode valueOf(int code) {\n        for (ImageScaleMode mode : values()) {\n            if (mode.code == code) {\n                return mode;\n            }\n        }\n        return STRETCH;\n    }\n}\n\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/entity/ImageEntity.java",
    "content": "package org.jeecgframework.poi.entity;\n\nimport org.jeecgframework.poi.consts.ImageScaleMode;\n\n/**\n * word导出,图片设置和图片信息\n * \n * @author liusq\n * @date  2022-5-27\n */\npublic class ImageEntity {\n\n    public static String URL  = \"url\";\n    public static String Data = \"data\"; \n    \n    /**\n     * 图片输入方式\n     */\n    private String       type = URL;\n    /**\n     * 图片宽度\n     */\n    private int          width;\n    // 图片高度\n    private int          height;\n    // 图片地址\n    private String       url;\n    // 图片信息\n    private byte[]       data;\n\n    private int          rowspan = 1;\n    private int          colspan = 1;\n    \n    /**\n     * 图片缩放模式枚举\n     * for [issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式\n     */\n    private ImageScaleMode scaleModeEnum = ImageScaleMode.STRETCH;\n\n\n    public ImageEntity() {\n\n    }\n\n    public ImageEntity(byte[] data, int width, int height) {\n        this.data = data;\n        this.width = width;\n        this.height = height;\n        this.type = Data;\n    }\n\n    public ImageEntity(String url, int width, int height) {\n        this.url = url;\n        this.width = width;\n        this.height = height;\n    }\n\n    public byte[] getData() {\n        return data;\n    }\n\n    public int getHeight() {\n        return height;\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public String getUrl() {\n        return url;\n    }\n\n    public int getWidth() {\n        return width;\n    }\n\n    public void setData(byte[] data) {\n        this.data = data;\n    }\n\n    public void setHeight(int height) {\n        this.height = height;\n    }\n\n    public void setType(String type) {\n        this.type = type;\n    }\n\n    public void setUrl(String url) {\n        this.url = url;\n    }\n\n    public void setWidth(int width) {\n        this.width = width;\n    }\n\n    public int getRowspan() {\n        return rowspan;\n    }\n\n    public void setRowspan(int rowspan) {\n        this.rowspan = rowspan;\n    }\n\n    public int getColspan() {\n        return colspan;\n    }\n\n    public void setColspan(int colspan) {\n        this.colspan = colspan;\n    }\n\n    /**\n     * 获取图片缩放模式\n     * @deprecated 使用 {@link #getScaleModeEnum()} 代替\n     * @return 缩放模式代码值\n     */\n    @Deprecated\n    public int getScaleMode() {\n        return scaleModeEnum.getCode();\n    }\n\n    /**\n     * 设置图片缩放模式\n     * @deprecated 使用 {@link #setScaleModeEnum(ImageScaleMode)} 代替\n     * @param scaleMode 缩放模式代码值\n     */\n    @Deprecated\n    public void setScaleMode(int scaleMode) {\n        this.scaleModeEnum = ImageScaleMode.valueOf(scaleMode);\n    }\n    \n    /**\n     * 获取图片缩放模式枚举\n     * @return 缩放模式枚举\n     */\n    public ImageScaleMode getScaleModeEnum() {\n        return scaleModeEnum;\n    }\n    \n    /**\n     * 设置图片缩放模式枚举\n     * @param scaleModeEnum 缩放模式枚举\n     */\n    public void setScaleModeEnum(ImageScaleMode scaleModeEnum) {\n        this.scaleModeEnum = scaleModeEnum;\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelExportUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.apache.poi.xssf.streaming.SXSSFWorkbook;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.export.ExcelBatchExportServer;\nimport org.jeecgframework.poi.excel.export.ExcelExportServer;\nimport org.jeecgframework.poi.excel.export.template.ExcelExportOfTemplateUtil;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServer;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced;\nimport org.jeecgframework.poi.handler.inter.IWriter;\n\n/**\n * excel 导出工具类\n * \n * @author JEECG\n * @version 1.0\n * @date 2013-10-17\n */\n//update-begin---author:chenrui ---date:20231221  for：[issue/#5248]加强继承扩展便利性(删除final)------------\npublic class ExcelExportUtil {\n//update-end---author:chenrui ---date:20231221  for：[issue/#5248]加强继承扩展便利性(删除final)------------\n\t//update-begin---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\t//单sheet最大值\n\tpublic static       int    USE_SXSSF_LIMIT = 100000;\n\t//update-end---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\tprivate ExcelExportUtil() {\n\t}\n\n\t//---update-begin-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\t/**\n\t * 根据Entity创建对应的Excel\n\t * \n\t * @param entity\n\t *            表格标题属性\n\t * @param pojoClass\n\t *            Excel对象Class\n\t * @param dataSet\n\t *            Excel对象数据List\n\t * @param exportFields\n\t * \t          自定义导出Excel字段数组\n\t */\n\tpublic static Workbook exportExcel(ExportParams entity, Class<?> pojoClass, Collection<?> dataSet,String[] exportFields) {\n\t\tWorkbook workbook;\n\t\tif (ExcelType.HSSF.equals(entity.getType())) {\n\t\t\tworkbook = new HSSFWorkbook();\n\t\t} else if (dataSet.size() < 1000) {\n\t\t\tworkbook = new XSSFWorkbook();\n\t\t} else {\n\t\t\tworkbook = new SXSSFWorkbook();\n\t\t}\n\t\tnew ExcelExportServer().createSheet(workbook, entity, pojoClass, dataSet,exportFields);\n\t\treturn workbook;\n\t}\n\t//---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\n\n\t/**\n\t * 根据Entity创建对应的Excel\n\t *\n\t * @param entity\n\t *            表格标题属性\n\t * @param pojoClass\n\t *            Excel对象Class\n\t * @param dataSet\n\t *            Excel对象数据List\n\t */\n\tpublic static Workbook exportExcel(ExportParams entity, Class<?> pojoClass, Collection<?> dataSet) {\n\t\tWorkbook workbook;\n\t\tif (ExcelType.HSSF.equals(entity.getType())) {\n\t\t\tworkbook = new HSSFWorkbook();\n\t\t} else if (dataSet.size() < 1000) {\n\t\t\tworkbook = new XSSFWorkbook();\n\t\t} else {\n\t\t\tworkbook = new SXSSFWorkbook();\n\t\t}\n\t\tnew ExcelExportServer().createSheet(workbook, entity, pojoClass, dataSet,null);\n\t\treturn workbook;\n\t}\n\n\t/**\n\t * 根据Map创建对应的Excel\n\t * \n\t * @param entity\n\t *            表格标题属性\n\t * @param pojoClass\n\t *            Excel对象Class\n\t * @param dataSet\n\t *            Excel对象数据List\n\t */\n\tpublic static Workbook exportExcel(ExportParams entity, List<ExcelExportEntity> entityList, Collection<? extends Map<?, ?>> dataSet) {\n\t\tWorkbook workbook;\n\t\tif (ExcelType.HSSF.equals(entity.getType())) {\n\t\t\tworkbook = new HSSFWorkbook();\n\t\t} else if (dataSet.size() < 1000) {\n\t\t\tworkbook = new XSSFWorkbook();\n\t\t} else {\n\t\t\tworkbook = new SXSSFWorkbook();\n\t\t}\n\t\tnew ExcelExportServer().createSheetForMap(workbook, entity, entityList, dataSet);\n\t\treturn workbook;\n\t}\n\n\t/**\n\t * 一个excel 创建多个sheet\n\t * \n\t * @param list\n\t *            多个Map key title 对应表格Title key entity 对应表格对应实体 key data\n\t *            Collection 数据\n\t * @return\n\t */\n\tpublic static Workbook exportExcel(List<Map<String, Object>> list, ExcelType type) {\n\t\tWorkbook workbook;\n\t\tif (ExcelType.HSSF.equals(type)) {\n\t\t\tworkbook = new HSSFWorkbook();\n\t\t} else {\n\t\t\tworkbook = new XSSFWorkbook();\n\t\t}\n\t\tfor (Map<String, Object> map : list) {\n\t\t\tExcelExportServer server = new ExcelExportServer();\n\t\t\tserver.createSheet(workbook, (ExportParams) map.get(\"title\"), (Class<?>) map.get(\"entity\"), (Collection<?>) map.get(\"data\"),null);\n\t\t}\n\t\treturn workbook;\n\t}\n\n\t/**\n\t * 导出文件通过模板解析,不推荐这个了,推荐全部通过模板来执行处理\n\t * \n\t * @param params\n\t *            导出参数类\n\t * @param pojoClass\n\t *            对应实体\n\t * @param dataSet\n\t *            实体集合\n\t * @param map\n\t * \n\t *            模板集合\n\t * @return\n\t */\n\tpublic static Workbook exportExcel(TemplateExportParams params, Class<?> pojoClass, Collection<?> dataSet, Map<String, Object> map) {\n\t\treturn new ExcelExportOfTemplateUtil().createExcleByTemplate(params, pojoClass, dataSet, map);\n\t}\n\n\t/**\n\t * 导出文件通过模板解析只有模板,没有集合\n\t * \n\t * @param params\n\t *            导出参数类\n\t * @param map\n\t *            模板集合\n\t * @return\n\t */\n\tpublic static Workbook exportExcel(TemplateExportParams params, Map<String, Object> map) {\n\t\treturn new ExcelExportOfTemplateUtil().createExcleByTemplate(params, null, null, map);\n\t}\n\n\n\t//update-begin---author:liusq  Date:20211227 for：[LOWCOD-2521]大数据导出方法【全局】----\n\t/**\n\t * 大数据量导出\n\t *\n\t * @param entity    表格标题属性\n\t * @param pojoClass Excel对象Class\n\t * @date 2022年1月4号\n\t * @return ExcelBatchExportServer 批处理服务\n\t */\n\tpublic static IWriter<Workbook> exportBigExcel(ExportParams entity, Class<?> pojoClass) {\n\t\tExcelBatchExportServer batchServer = new ExcelBatchExportServer();\n\t\tbatchServer.init(entity, pojoClass);\n\t\treturn batchServer;\n\t}\n\n\t/**\n\t * 大数据量导出\n\t *\n\t * @param entity\n\t * @param excelParams\n\t * @date 2022年1月4号\n\t * @return ExcelBatchExportServer 批处理服务\n\t */\n\tpublic static IWriter<Workbook> exportBigExcel(ExportParams entity, List<ExcelExportEntity> excelParams) {\n\t\tExcelBatchExportServer batchServer = new ExcelBatchExportServer();\n\t\tbatchServer.init(entity, excelParams);\n\t\treturn batchServer;\n\t}\n\n\t/**\n\t * 大数据量导出\n\t *\n\t * @param entity      导出参数属性\n\t * @param pojoClass   Excel对象Class\n\t * @param server      查询数据的接口\n\t * @param queryParams 查询数据的参数\n\t * @date 2022年1月4号\n\t * @return Workbook\n\t */\n\tpublic static Workbook exportBigExcel(ExportParams entity, Class<?> pojoClass,\n\t\t\t\t\t\t\t\t\t\t  IExcelExportServer server, Object queryParams) {\n\t\tExcelBatchExportServer batchServer = new ExcelBatchExportServer();\n\t\tbatchServer.init(entity, pojoClass);\n\t\treturn batchServer.exportBigExcel(server, queryParams);\n\t}\n\n\t/**\n\t * 大数据量导出\n\t * @param entity\n\t * @param excelParams\n\t * @param server      查询数据的接口\n\t * @param queryParams 查询数据的参数\n\t * @date 2022年1月4号\n\t * @return Workbook\n\t */\n\tpublic static Workbook exportBigExcel(ExportParams entity, List<ExcelExportEntity> excelParams,\n\t\t\t\t\t\t\t\t\t\t  IExcelExportServer server, Object queryParams) {\n\t\tExcelBatchExportServer batchServer = new ExcelBatchExportServer();\n\t\tbatchServer.init(entity, excelParams);\n\t\treturn batchServer.exportBigExcel(server, queryParams);\n\t}\n\t//update-end---author:liusq  Date:20211227 for：[LOWCOD-2521]大数据导出方法【全局】----\n\n\t//update-begin---author:chenrui  Date:20251103 for：[issues/8892]解决40万+数据导出查询效率问题----\n\t/**\n\t * 大数据量导出 - 高性能游标分页方案\n     * for [QQYUN-13964]演示系统数据量大，点击没反应\n\t * <p>\n\t * 适用场景:\n\t * 1. 数据量超过40万+的导出场景\n\t * 2. 需要避免深分页性能问题\n\t * 3. 数据表有自增ID或其他有序字段\n\t * \n\t * 性能对比:\n\t * - 传统方式: 40万数据,最后一页查询可能需要10+秒\n\t * - 游标方式: 40万数据,每页查询时间恒定在0.1秒左右\n\t * \n\t * 使用示例:\n\t * <pre>\n\t * ExportParams params = new ExportParams(\"订单列表\", \"订单\");\n\t * IExcelExportServerEnhanced server = new IExcelExportServerEnhanced() {\n\t *     public List<Object> selectListForExcelExport(Object queryParams, Object lastRecord, int pageSize) {\n\t *         Long lastId = lastRecord != null ? ((Order)lastRecord).getId() : 0L;\n\t *         return orderMapper.selectList(new QueryWrapper<Order>()\n\t *             .gt(\"id\", lastId)\n\t *             .orderByAsc(\"id\")\n\t *             .last(\"LIMIT \" + pageSize));\n\t *     }\n\t * };\n\t * Workbook workbook = ExcelExportUtil.exportBigExcelEnhanced(params, Order.class, server, queryParams);\n\t * </pre>\n\t * \n\t * @param entity      导出参数属性\n\t * @param pojoClass   Excel对象Class\n\t * @param server      增强的查询数据接口(支持游标分页)\n\t * @param queryParams 查询数据的参数\n\t * @date 2025年11月3号\n\t * @return Workbook\n\t */\n\tpublic static <T> Workbook exportBigExcelEnhanced(ExportParams entity, Class<T> pojoClass,\n\t\t\t\t\t\t\t\t\t\t\t\t  IExcelExportServerEnhanced<T> server, Object queryParams) {\n\t\tExcelBatchExportServer batchServer = new ExcelBatchExportServer();\n\t\tbatchServer.init(entity, pojoClass);\n\t\treturn batchServer.exportBigExcelEnhanced(server, queryParams);\n\t}\n\n\t/**\n\t * 大数据量导出 - 高性能游标分页方案(自定义列)\n\t * \n\t * @param entity       导出参数属性\n\t * @param excelParams  自定义导出列\n\t * @param server       增强的查询数据接口(支持游标分页)\n\t * @param queryParams  查询数据的参数\n\t * @date 2025年11月3号\n\t * @return Workbook\n\t */\n\tpublic static <T> Workbook exportBigExcelEnhanced(ExportParams entity, List<ExcelExportEntity> excelParams,\n\t\t\t\t\t\t\t\t\t\t\t\t  IExcelExportServerEnhanced<T> server, Object queryParams) {\n\t\tExcelBatchExportServer batchServer = new ExcelBatchExportServer();\n\t\tbatchServer.init(entity, excelParams);\n\t\treturn batchServer.exportBigExcelEnhanced(server, queryParams);\n\t}\n\t//update-end---author:chenrui  Date:20251103 for：[issues/8892]解决40万+数据导出查询效率问题----\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportCheckUtil.java",
    "content": "package org.jeecgframework.poi.excel;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.openxml4j.exceptions.InvalidFormatException;\nimport org.apache.poi.openxml4j.opc.OPCPackage;\nimport org.apache.poi.poifs.filesystem.POIFSFileSystem;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.jeecgframework.core.util.ApplicationContextUtil;\nimport org.jeecgframework.dict.service.AutoPoiDictServiceI;\nimport org.jeecgframework.poi.excel.annotation.Excel;\nimport org.jeecgframework.poi.excel.annotation.ExcelCollection;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.annotation.ExcelVerify;\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;\nimport org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity;\nimport org.jeecgframework.poi.excel.imports.ExcelImportServer;\nimport org.jeecgframework.poi.exception.excel.ExcelImportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;\nimport org.jeecgframework.poi.util.ExcelUtil;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.PushbackInputStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.math.BigDecimal;\nimport java.util.*;\n\n/**\n * EXCEL INCLUE CHECK\n * 验证excel标题是否存在，当前默认有0.8（80%）即可通过验证\n */\npublic class ExcelImportCheckUtil {\n    private final static Logger LOGGER = LoggerFactory.getLogger(ExcelImportCheckUtil.class);\n\n    /**当有标题到达多少可以通过验证*/\n    public static final Double defScreenRate = 0.8;\n\n    /**\n     * check inclue filed match rate\n     * @param inputstream\n     * @param pojoClass\n     * @param params\n     * @return\n     */\n    public static Boolean check(InputStream inputstream, Class<?> pojoClass, ImportParams params) {\n        return check(inputstream,pojoClass,params,defScreenRate);\n    }\n    /**\n     * check inclue filed match rate\n     * @param inputstream\n     * @param pojoClass\n     * @param params\n     * @param screenRate field match rate (defalut:0.8)\n     * @return\n     */\n    public static Boolean check(InputStream inputstream, Class<?> pojoClass, ImportParams params, Double screenRate) {\n        Workbook book = null;\n        int errorNum = 0;\n        int successNum = 0;\n        if (!(inputstream.markSupported())) {\n            inputstream = new PushbackInputStream(inputstream, 8);\n        }\n        try {\n//            if (POIFSFileSystem.hasPOIFSHeader(inputstream)) {\n//                book = new HSSFWorkbook(inputstream);\n//            } else if (POIXMLDocument.hasOOXMLHeader(inputstream)) {\n//                book = new XSSFWorkbook(OPCPackage.open(inputstream));\n//            }\n            book = WorkbookFactory.create(inputstream);\n            LOGGER.info(\"  >>>  poi3升级到4兼容改造工作, pojoClass=\"+pojoClass);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        for (int i = 0; i < params.getSheetNum(); i++) {\n            Row row = null;\n            //跳过表头和标题行\n            Iterator<Row> rows;\n            try{\n                rows=  book.getSheetAt(i).rowIterator();\n            }catch (Exception e){\n                //为空说明读取不到，故不是excel\n                  throw new RuntimeException(\"请导入正确格式的excel文件！\");\n            }\n\n\n            for (int j = 0; j < params.getTitleRows() + params.getHeadRows(); j++) {\n                try{\n                    row = rows.next();\n                }catch (NoSuchElementException e){\n                    //为空说明标题不出在，excel格式错误\n                    throw new RuntimeException(\"请填写内容标题！\");\n                }\n            }\n            Sheet sheet = book.getSheetAt(i);\n            Map<Integer, String> titlemap = null;\n            try {\n                titlemap = getTitleMap(sheet, params);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            Set<Integer> columnIndexSet = titlemap.keySet();\n            Integer maxColumnIndex = Collections.max(columnIndexSet);\n            Integer minColumnIndex = Collections.min(columnIndexSet);\n            while (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) {\n                row = rows.next();\n                Map<String, ExcelImportEntity> excelParams = new HashMap<String, ExcelImportEntity>();\n                List<ExcelCollectionParams> excelCollection = new ArrayList<ExcelCollectionParams>();\n                String targetId = null;\n                if (!Map.class.equals(pojoClass)) {\n                    Field fileds[] = PoiPublicUtil.getClassFields(pojoClass);\n                    ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);\n                    if (etarget != null) {\n                        targetId = etarget.value();\n                    }\n                    try {\n                        getAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);\n                    } catch (Exception e) {\n                        e.printStackTrace();\n                    }\n                }\n                try {\n                    int firstCellNum = row.getFirstCellNum();\n                    if (firstCellNum > minColumnIndex) {\n                        firstCellNum = minColumnIndex;\n                    }\n                    int lastCellNum = row.getLastCellNum();\n                    if (lastCellNum < maxColumnIndex + 1) {\n                        lastCellNum = maxColumnIndex + 1;\n                    }\n                    for (int j = firstCellNum, le = lastCellNum; j < le; j++) {\n                        String titleString = (String) titlemap.get(j);\n                        if (excelParams.containsKey(titleString) || Map.class.equals(pojoClass)) {\n                            successNum+=1;\n                        }else{\n                            if(excelCollection.size()>0){\n                                Iterator var33 = excelCollection.iterator();\n                                ExcelCollectionParams param = (ExcelCollectionParams)var33.next();\n                                if (param.getExcelParams().containsKey(titleString)) {\n                                    successNum+=1;\n                                }else{\n                                    errorNum+=1;\n                                }\n                            }else{\n                                errorNum+=1;\n                            }\n                        }\n                    }\n                    if(successNum<errorNum){\n                        return false;\n                    }else if(successNum>errorNum){\n                        if(errorNum>0){\n                            double newNumber = (double) successNum / (successNum + errorNum);\n                            BigDecimal bg = new BigDecimal(newNumber);\n                            double f1 = bg.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();\n                            if(f1<screenRate){\n                                return false;\n                            }else{\n                                return true;\n                            }\n                        }else{\n                            return true;\n                        }\n                    }else if(successNum==errorNum){\n                        return false;\n                    }else{\n                        return false;\n                    }\n                } catch (ExcelImportException e) {\n                    if (!e.getType().equals(ExcelImportEnum.VERIFY_ERROR)) {\n                        throw new ExcelImportException(e.getType(), e);\n                    }\n                }\n\n            }\n        }\n     return null;\n    }\n\n    /**\n     * 获取文件名称标题\n     * @Author JEECG\n     * @date 20201023\n     * @throws Exception\n     */\n    private static Map<Integer, String> getTitleMap(Sheet sheet, ImportParams params) throws Exception {\n        Map<Integer, String> titlemap = new HashMap<Integer, String>();\n        Iterator<Cell> cellTitle = null;\n        String collectionName = null;\n        Row headRow = null;\n        int headBegin = params.getTitleRows();\n        int allRowNum = sheet.getPhysicalNumberOfRows();\n        while(headRow == null && headBegin < allRowNum){\n            headRow = sheet.getRow(headBegin++);\n        }\n        if(headRow==null){\n            throw new Exception(\"不识别该文件\");\n        }\n        if (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) {\n            params.setHeadRows(2);\n        }else{\n            params.setHeadRows(1);\n        }\n        cellTitle = headRow.cellIterator();\n        while (cellTitle.hasNext()) {\n            Cell cell = cellTitle.next();\n            String value = getKeyValue(cell);\n            if (StringUtils.isNotEmpty(value)) {\n                titlemap.put(cell.getColumnIndex(), value);//加入表头列表\n            }\n        }\n\n        //多行表头\n        for (int j = headBegin; j < headBegin + params.getHeadRows()-1; j++) {\n            headRow = sheet.getRow(j);\n            cellTitle = headRow.cellIterator();\n            while (cellTitle.hasNext()) {\n                Cell cell = cellTitle.next();\n                String value = getKeyValue(cell);\n                if (StringUtils.isNotEmpty(value)) {\n                    int columnIndex = cell.getColumnIndex();\n                    //当前cell的上一行是否为合并单元格\n                    if(ExcelUtil.isMergedRegion(sheet, cell.getRowIndex()-1, columnIndex)){\n                        collectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex()-1, columnIndex);\n                        if(params.isIgnoreHeader(collectionName)){\n                            titlemap.put(cell.getColumnIndex(), value);\n                        }else{\n                            titlemap.put(cell.getColumnIndex(), collectionName + \"_\" + value);\n                        }\n                    }else{\n                        titlemap.put(cell.getColumnIndex(), value);\n                    }\n                }\n            }\n        }\n        return titlemap;\n    }\n    /**\n     * 获取key的值,针对不同类型获取不同的值\n     *\n     * @Author JEECG\n     * @date 20201023\n     * @param cell\n     * @return\n     */\n    private static String getKeyValue(Cell cell) {\n        if(cell==null){\n            return null;\n        }\n        Object obj = null;\n        switch (cell.getCellType()) {\n            case STRING:\n                obj = cell.getStringCellValue();\n                break;\n            case BOOLEAN:\n                obj = cell.getBooleanCellValue();\n                break;\n            case NUMERIC:\n                obj = cell.getNumericCellValue();\n                break;\n            case FORMULA:\n                obj = cell.getCellFormula();\n                break;\n        }\n        return obj == null ? null : obj.toString().trim();\n    }\n\n    /**\n     * 获取需要导出的全部字段\n     *\n     *\n     *\n     * @param targetId\n     *            目标ID\n     * @param fields\n     * @param excelCollection\n     * @throws Exception\n     */\n    public static void getAllExcelField(String targetId, Field[] fields, Map<String, ExcelImportEntity> excelParams, List<ExcelCollectionParams> excelCollection, Class<?> pojoClass, List<Method> getMethods) throws Exception {\n        ExcelImportEntity excelEntity = null;\n        for (int i = 0; i < fields.length; i++) {\n            Field field = fields[i];\n            if (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) {\n                continue;\n            }\n            if (PoiPublicUtil.isCollection(field.getType())) {\n                // 集合对象设置属性\n                ExcelCollectionParams collection = new ExcelCollectionParams();\n                collection.setName(field.getName());\n                Map<String, ExcelImportEntity> temp = new HashMap();\n                ParameterizedType pt = (ParameterizedType)field.getGenericType();\n                Class<?> clz = (Class)pt.getActualTypeArguments()[0];\n                collection.setType(clz);\n                getExcelFieldList(targetId, PoiPublicUtil.getClassFields(clz), clz, temp, (List)null);\n                collection.setExcelParams(temp);\n                collection.setExcelName(((ExcelCollection)field.getAnnotation(ExcelCollection.class)).name());\n                additionalCollectionName(collection);\n                excelCollection.add(collection);\n            } else if (PoiPublicUtil.isJavaClass(field)) {\n                addEntityToMap(targetId, field, (ExcelImportEntity)excelEntity, pojoClass, getMethods, excelParams);\n            } else {\n                List<Method> newMethods = new ArrayList<Method>();\n                if (getMethods != null) {\n                    newMethods.addAll(getMethods);\n                }\n                newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass));\n                getAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, excelCollection, field.getType(), newMethods);\n            }\n        }\n    }\n    public static void getExcelFieldList(String targetId, Field[] fields, Class<?> pojoClass, Map<String, ExcelImportEntity> temp, List<Method> getMethods) throws Exception {\n        ExcelImportEntity excelEntity = null;\n        for (int i = 0; i < fields.length; i++) {\n            Field field = fields[i];\n            if (!PoiPublicUtil.isNotUserExcelUserThis((List)null, field, targetId)) {\n                if (PoiPublicUtil.isJavaClass(field)) {\n                    addEntityToMap(targetId, field, (ExcelImportEntity)excelEntity, pojoClass, getMethods, temp);\n                } else {\n                    List<Method> newMethods = new ArrayList();\n                    if (getMethods != null) {\n                        newMethods.addAll(getMethods);\n                    }\n\n                    newMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass, field.getType()));\n                    getExcelFieldList(targetId, PoiPublicUtil.getClassFields(field.getType()), field.getType(), temp, newMethods);\n                }\n            }\n        }\n    }\n    /**\n     * 追加集合名称到前面\n     *\n     * @param collection\n     */\n    private static void additionalCollectionName(ExcelCollectionParams collection) {\n        Set<String> keys = new HashSet();\n        keys.addAll(collection.getExcelParams().keySet());\n        Iterator var3 = keys.iterator();\n\n        while(var3.hasNext()) {\n            String key = (String)var3.next();\n            collection.getExcelParams().put(collection.getExcelName() + \"_\" + key, collection.getExcelParams().get(key));\n            collection.getExcelParams().remove(key);\n        }\n    }\n    /**\n     * 把这个注解解析放到类型对象中\n     *\n     * @param targetId\n     * @param field\n     * @param excelEntity\n     * @param pojoClass\n     * @param getMethods\n     * @param temp\n     * @throws Exception\n     */\n    public static void addEntityToMap(String targetId, Field field, ExcelImportEntity excelEntity, Class<?> pojoClass, List<Method> getMethods, Map<String, ExcelImportEntity> temp) throws Exception {\n        Excel excel = field.getAnnotation(Excel.class);\n        excelEntity = new ExcelImportEntity();\n        excelEntity.setType(excel.type());\n        excelEntity.setSaveUrl(excel.savePath());\n        excelEntity.setSaveType(excel.imageType());\n        excelEntity.setReplace(excel.replace());\n        excelEntity.setDatabaseFormat(excel.databaseFormat());\n        excelEntity.setVerify(getImportVerify(field));\n        excelEntity.setSuffix(excel.suffix());\n        excelEntity.setNumFormat(excel.numFormat());\n        excelEntity.setGroupName(excel.groupName());\n        //update-begin-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题\n        excelEntity.setMultiReplace(excel.multiReplace());\n        if(StringUtils.isNotEmpty(excel.dicCode())){\n            AutoPoiDictServiceI jeecgDictService = null;\n            try {\n                jeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class);\n            } catch (Exception e) {\n            }\n            if(jeecgDictService!=null){\n                String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText());\n                if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){\n                    excelEntity.setReplace(dictReplace);\n                }\n            }\n        }\n        //update-end-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题\n        getExcelField(targetId, field, excelEntity, excel, pojoClass);\n        if (getMethods != null) {\n            List<Method> newMethods = new ArrayList<Method>();\n            newMethods.addAll(getMethods);\n            newMethods.add(excelEntity.getMethod());\n            excelEntity.setMethods(newMethods);\n        }\n        temp.put(excelEntity.getName(), excelEntity);\n\n    }\n    public static void getExcelField(String targetId, Field field, ExcelImportEntity excelEntity, Excel excel, Class<?> pojoClass) throws Exception {\n        excelEntity.setName(getExcelName(excel.name(), targetId));\n        String fieldname = field.getName();\n        //update-begin-author:taoyan for:TASK #2798 【例子】导入扩展方法，支持自定义导入字段转换规则\n        excelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass, field.getType(),excel.importConvert()));\n        //update-end-author:taoyan for:TASK #2798 【例子】导入扩展方法，支持自定义导入字段转换规则\n        if (StringUtils.isNotEmpty(excel.importFormat())) {\n            excelEntity.setFormat(excel.importFormat());\n        } else {\n            excelEntity.setFormat(excel.format());\n        }\n    }\n    /**\n     * 判断在这个单元格显示的名称\n     *\n     * @param exportName\n     * @param targetId\n     * @return\n     */\n    public static String getExcelName(String exportName, String targetId) {\n        if (exportName.indexOf(\"_\") < 0) {\n            return exportName;\n        }\n        String[] arr = exportName.split(\",\");\n        for (String str : arr) {\n            if (str.indexOf(targetId) != -1) {\n                return str.split(\"_\")[0];\n            }\n        }\n        return null;\n    }\n    /**\n     * 获取导入校验参数\n     *\n     * @param field\n     * @return\n     */\n    public static ExcelVerifyEntity getImportVerify(Field field) {\n        ExcelVerify verify = field.getAnnotation(ExcelVerify.class);\n        if (verify != null) {\n            ExcelVerifyEntity entity = new ExcelVerifyEntity();\n            entity.setEmail(verify.isEmail());\n            entity.setInterHandler(verify.interHandler());\n            entity.setMaxLength(verify.maxLength());\n            entity.setMinLength(verify.minLength());\n            entity.setMobile(verify.isMobile());\n            entity.setNotNull(verify.notNull());\n            entity.setRegex(verify.regex());\n            entity.setRegexTip(verify.regexTip());\n            entity.setTel(verify.isTel());\n            return entity;\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelImportUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.List;\n\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport org.jeecgframework.poi.excel.entity.result.ExcelImportResult;\nimport org.jeecgframework.poi.excel.imports.ExcelImportServer;\nimport org.jeecgframework.poi.excel.imports.sax.SaxReadExcel;\nimport org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead;\nimport org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Excel 导入工具\n * \n * @author JEECG\n * @date 2013-9-24\n * @version 1.0\n */\n@SuppressWarnings({ \"unchecked\" })\npublic final class ExcelImportUtil {\n\n\tprivate ExcelImportUtil() {\n\t}\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ExcelImportUtil.class);\n\n\t/**\n\t * Excel 导入 数据源本地文件,不返回校验结果 导入 字 段类型 Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param file\n\t * @param pojoClass\n\t * @param params\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static <T> List<T> importExcel(File file, Class<?> pojoClass, ImportParams params) {\n\t\tFileInputStream in = null;\n\t\tList<T> result = null;\n\t\ttry {\n\t\t\tin = new FileInputStream(file);\n\t\t\tresult = new ExcelImportServer().importExcelByIs(in, pojoClass, params).getList();\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tin.close();\n\t\t\t} catch (IOException e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Excel 导入 数据源IO流,不返回校验结果 导入 字段类型 Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param file\n\t * @param pojoClass\n\t * @param params\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static <T> List<T> importExcel(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {\n\t\treturn new ExcelImportServer().importExcelByIs(inputstream, pojoClass, params).getList();\n\t}\n\n\t/**\n\t * Excel 导入 数据源IO流,返回校验结果 字段类型 Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param file\n\t * @param pojoClass\n\t * @param params\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static <T> ExcelImportResult<T> importExcelVerify(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {\n\t\treturn new ExcelImportServer().importExcelByIs(inputstream, pojoClass, params);\n\t}\n\n\t/**\n\t * Excel 导入 数据源本地文件,返回校验结果 字段类型 Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param file\n\t * @param pojoClass\n\t * @param params\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static <T> ExcelImportResult<T> importExcelVerify(File file, Class<?> pojoClass, ImportParams params) {\n\t\tFileInputStream in = null;\n\t\ttry {\n\t\t\tin = new FileInputStream(file);\n\t\t\treturn new ExcelImportServer().importExcelByIs(in, pojoClass, params);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t} finally {\n\t\t\ttry {\n\t\t\t\tin.close();\n\t\t\t} catch (IOException e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Excel 通过SAX解析方法,适合大数据导入,不支持图片 导入 数据源IO流,不返回校验结果 导入 字段类型\n\t * Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param inputstream\n\t * @param pojoClass\n\t * @param params\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static <T> List<T> importExcelBySax(InputStream inputstream, Class<?> pojoClass, ImportParams params) {\n\t\treturn new SaxReadExcel().readExcel(inputstream, pojoClass, params, null, null);\n\t}\n\n\t/**\n\t * Excel 通过SAX解析方法,适合大数据导入,不支持图片 导入 数据源本地文件,不返回校验结果 导入 字 段类型\n\t * Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param file\n\t * @param rowRead\n\t * @return\n\t * @throws Exception\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic static void importExcelBySax(InputStream inputstream, Class<?> pojoClass, ImportParams params, IExcelReadRowHanlder hanlder) {\n\t\tnew SaxReadExcel().readExcel(inputstream, pojoClass, params, null, hanlder);\n\t}\n\n\t/**\n\t * Excel 通过SAX解析方法,适合大数据导入,不支持图片 导入 数据源IO流,不返回校验结果 导入 字段类型\n\t * Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param file\n\t * @param rowRead\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static <T> List<T> importExcelBySax(InputStream inputstream, ISaxRowRead rowRead) {\n\t\treturn new SaxReadExcel().readExcel(inputstream, null, null, rowRead, null);\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/ExcelToHtmlUtil.java",
    "content": "package org.jeecgframework.poi.excel;\n\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.html.ExcelToHtmlServer;\n\n/**\n * Excel 变成界面\n * \n * @author JEECG\n * @date 2015年5月10日 上午11:51:48\n */\npublic final class ExcelToHtmlUtil {\n\n\tprivate ExcelToHtmlUtil() {\n\t}\n\n\t/**\n\t * 转换成为Table\n\t * \n\t * @param wb\n\t *            Excel\n\t * @return\n\t */\n\tpublic static String toTableHtml(Workbook wb) {\n\t\treturn new ExcelToHtmlServer(wb, false, 0).printPage();\n\t}\n\n\t/**\n\t * 转换成为Table\n\t * \n\t * @param wb\n\t *            Excel\n\t * @param sheetNum\n\t *            sheetNum\n\t * @return\n\t */\n\tpublic static String toTableHtml(Workbook wb, int sheetNum) {\n\t\treturn new ExcelToHtmlServer(wb, false, sheetNum).printPage();\n\t}\n\n\t/**\n\t * 转换成为完整界面\n\t * \n\t * @param wb\n\t *            Excel\n\t * @param sheetNum\n\t *            sheetNum\n\t * @return\n\t */\n\tpublic static String toAllHtml(Workbook wb) {\n\t\treturn new ExcelToHtmlServer(wb, true, 0).printPage();\n\t}\n\n\t/**\n\t * 转换成为完整界面\n\t * \n\t * @param wb\n\t *            Excel\n\t * @param sheetNum\n\t *            sheetNum\n\t * @return\n\t */\n\tpublic static String toAllHtml(Workbook wb, int sheetNum) {\n\t\treturn new ExcelToHtmlServer(wb, true, sheetNum).printPage();\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/Excel.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.annotation.Inherited;\n\n/**\n * Excel 导出基本注释\n * \n * @author JEECG\n * @date 2014年6月20日 下午10:25:12\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\n//update-begin---author:chenrui ---date:20231221  for：[issue/#5248]加强继承扩展便利性------------\n@Inherited\n//update-end---author:chenrui ---date:20231221  for：[issue/#5248]加强继承扩展便利性------------\npublic @interface Excel {\n\n\t/**\n\t * 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式\n\t */\n\tpublic String databaseFormat() default \"yyyyMMddHHmmss\";\n\n\t/**\n\t * 导出的时间格式,以这个是否为空来判断是否需要格式化日期\n\t */\n\tpublic String exportFormat() default \"\";\n\n\t/**\n\t * 时间格式,相当于同时设置了exportFormat 和 importFormat\n\t */\n\tpublic String format() default \"\";\n\n\t/**\n\t * 导出时在excel中每个列的高度 单位为字符，一个汉字=2个字符\n\t */\n\tpublic double height() default 10;\n\n\t/**\n\t * 导出类型 1 从file读取_old ,2 是从数据库中读取字节文件, 3文件地址_new, 4网络地址 同样导入也是一样的\n\t *\n\t */\n\tpublic int imageType() default 3;\n\n\t/**\n\t * 导入的时间格式,以这个是否为空来判断是否需要格式化日期\n\t */\n\tpublic String importFormat() default \"\";\n\n\t/**\n\t * 文字后缀,如% 90 变成90%\n\t */\n\tpublic String suffix() default \"\";\n\n\t/**\n\t * 是否换行 即支持\\n\n\t */\n\tpublic boolean isWrap() default true;\n\n\t/**\n\t * 合并单元格依赖关系,比如第二列合并是基于第一列 则{1}就可以了\n\t */\n\tpublic int[] mergeRely() default {};\n\n\t/**\n\t * 纵向合并内容相同的单元格\n\t */\n\tpublic boolean mergeVertical() default false;\n\n\t/**\n\t * 导出时，对应数据库的字段 主要是用户区分每个字段， 不能有annocation重名的 导出时的列名\n\t * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用\n\t */\n\tpublic String name();\n\n\t/**\n\t * 是否需要纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row)\n\t */\n\tpublic boolean needMerge() default false;\n\n\t/**\n\t * 展示到第几个可以使用a_id,b_id来确定不同排序\n\t */\n\tpublic String orderNum() default \"0\";\n\n\t/**\n\t * 值得替换 导出是{\"男_1\",\"女_0\"} 导入反过来,所以只用写一个\n\t */\n\tpublic String[] replace() default {};\n\t\n\t/**\n\t * 导入路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/\n\t *\n\t */\n\tpublic String savePath() default \"upload\";\n\n\t/**\n\t * 导出类型 0是常规, 1 是文本 2 是图片,3是函数,4是数字 默认是文本\n\t */\n\tpublic int type() default 1;\n\n\t/**\n\t * 导出时在excel中每个列的宽 单位为字符，一个汉字=2个字符 如 以列名列内容中较合适的长度 例如姓名列6 【姓名一般三个字】\n\t * 性别列4【男女占1，但是列标题两个汉字】 限制1-255\n\t */\n\tpublic double width() default 10;\n\n\t/**\n\t * 是否自动统计数据,如果是统计,true的话在最后追加一行统计,把所有数据都和 这个处理会吞没异常,请注意这一点\n\t * \n\t * @return\n\t */\n\tpublic boolean isStatistics() default false;\n\t\n\t/**\n\t * 方法描述: 数据字典表\n\t * 作    者： yiming.zhang\n\t * 日    期： 2014年5月11日-下午5:26:40\n\t * @return \n\t * 返回类型： String\n\t */\n\tpublic String dictTable() default \"\";\n\n\t/**\n\t * 方法描述:  数据code\n\t * 作    者： yiming.zhang\n\t * 日    期： 2014年5月13日-下午9:37:16\n\t * @return \n\t * 返回类型： String\n\t */\n\tpublic String dicCode() default \"\";\n\t\n\t/**\n\t * 方法描述:  数据Text\n\t * 作    者： yiming.zhang\n\t * 日    期： 2014年5月11日-下午5:29:05\n\t * @return \n\t * 返回类型： String\n\t */\n\tpublic String dicText() default \"\";\n\t\n\t/**\n\t * 导入数据是否需要转化  \n\t * 若是为true,则需要在pojo中加入 方法：convertset字段名(String text)  \n\t * @return\n\t */\n\tpublic boolean importConvert() default false;\n\t/**\n\t * 导出数据是否需要转化\n\t * 若是为true,则需要在pojo中加入方法:convertget字段名()\n\t * @return\n\t */\n\tpublic boolean exportConvert() default false;\n\t\n\t/**\n\t * 值的替换是否支持替换多个(默认true,若数据库值本来就包含逗号则需要配置该值为false)\n\t * @author taoYan\n\t * @since 2018年8月1日\n\t */\n\tpublic boolean multiReplace() default true;\n\n\t/**\n\t * 父表头\n\t * @return\n\t */\n\tString groupName() default \"\";\n\n\t/**\n\t * 数字格式化,参数是Pattern,使用的对象是DecimalFormat\n\t * @return\n\t */\n\tString numFormat() default \"\";\n\t/**\n\t *  是否需要隐藏该列\n\t * @return\n\t */\n\tpublic boolean isColumnHidden() default  false;\n\t/**\n\t * 固定的某一列,解决不好解析的问题\n\t * @return\n\t */\n\tpublic int fixedIndex() default -1;\n\t/**\n\t * 这个是不是超链接,如果是需要实现接口返回对象\n\t * @return\n\t */\n\tpublic boolean isHyperlink() default false;\n\t\n\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t/**\n\t * 是否为动态列\n\t */\n\tpublic boolean dynamic() default false;\n\n\t/**\n\t * 动态列标题字段名，默认 name\n\t */\n\tpublic String dynamicField() default \"name\";\n\n\t/**\n\t * 动态列表格值字段名，默认 value\n\t */\n\tpublic String dynamicVal() default \"value\";\n\n\t/**\n\t * 是否保留原始字段列，默认不保留\n\t */\n\tpublic boolean dynamicKeepSelf() default false;\n\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelCollection.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.ArrayList;\n\n/**\n * 导出的集合\n * \n * @author JEECG 2013年8月24日\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface ExcelCollection {\n\n\t/**\n\t * 定义excel导出ID 来限定导出字段,处理一个类对应多个不同名称的情况\n\t */\n\tpublic String id() default \"\";\n\n\t/**\n\t * 导出时，对应数据库的字段 主要是用户区分每个字段， 不能有annocation重名的 导出时的列名\n\t * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用\n\t */\n\tpublic String name();\n\n\t/**\n\t * 展示到第几个同样可以使用a_id,b_id\n\t * \n\t */\n\tpublic String orderNum() default \"0\";\n\n\t/**\n\t * 创建时创建的类型 默认值是 arrayList\n\t */\n\tpublic Class<?> type() default ArrayList.class;\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 标记是不是导出excel 标记为实体类\n * \n * @author JEECG\n * \n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface ExcelEntity {\n\n\t/**\n\t * 定义excel导出ID 来限定导出字段,处理一个类对应多个不同名称的情况\n\t */\n\tpublic String id() default \"\";\n\n\t/**\n\t * 导出时，对应数据库的字段 主要是用户区分每个字段， 不能有annocation重名的 导出时的列名\n\t * 导出排序跟定义了annotation的字段的顺序有关 可以使用a_id,b_id来确实是否使用\n\t */\n\tpublic String name() default \"\";\n\n\t/**\n\t * 导出时，是否展示name对应的文本\n\t * @return\n\t */\n\tboolean show() default false;\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelIgnore.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * 标记为excel 创建实体忽略,放置死循环的造成\n * \n * @author JEECG\n * @date 2013-9-24\n * @version 1.0\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface ExcelIgnore {\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelTarget.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * excel 导出是用于标记id的\n * \n * @author JEECG\n * \n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target({ ElementType.TYPE })\npublic @interface ExcelTarget {\n\t/**\n\t * 定义excel导出ID 来限定导出字段\n\t */\n\tpublic String value();\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/annotation/ExcelVerify.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\n\n/**\n * Excel 导入校验\n * \n * @author JEECG\n * @date 2014年6月23日 下午10:46:26\n */\n@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.FIELD)\npublic @interface ExcelVerify {\n\t/**\n\t * 接口校验\n\t * \n\t * @return\n\t */\n\tpublic boolean interHandler() default false;\n\n\t/**\n\t * 是电子邮件\n\t * \n\t * @return\n\t */\n\tpublic boolean isEmail() default false;\n\n\t/**\n\t * 是13位移动电话\n\t * \n\t * @return\n\t */\n\tpublic boolean isMobile() default false;\n\n\t/**\n\t * 是座机号码\n\t * \n\t * @return\n\t */\n\tpublic boolean isTel() default false;\n\n\t/**\n\t * 最大长度\n\t * \n\t * @return\n\t */\n\tpublic int maxLength() default -1;\n\n\t/**\n\t * 最小长度\n\t * \n\t * @return\n\t */\n\tpublic int minLength() default -1;\n\n\t/**\n\t * 不允许空\n\t * \n\t * @return\n\t */\n\tpublic boolean notNull() default false;\n\n\t/**\n\t * 正在表达式\n\t * \n\t * @return\n\t */\n\tpublic String regex() default \"\";\n\n\t/**\n\t * 正在表达式,错误提示信息\n\t * \n\t * @return\n\t */\n\tpublic String regexTip() default \"数据不符合规范\";\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/ExcelBaseParams.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity;\n\nimport org.jeecgframework.poi.handler.inter.IExcelDataHandler;\n\n/**\n * 基础参数\n * \n * @author JEECG\n * @date 2014年6月20日 下午1:56:52\n */\npublic class ExcelBaseParams {\n\n\t/**\n\t * 数据处理接口,以此为主,replace,format都在这后面\n\t */\n\tprivate IExcelDataHandler dataHanlder;\n\n\tpublic IExcelDataHandler getDataHanlder() {\n\t\treturn dataHanlder;\n\t}\n\n\tpublic void setDataHanlder(IExcelDataHandler dataHanlder) {\n\t\tthis.dataHanlder = dataHanlder;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/ExportParams.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity;\n\nimport org.apache.poi.ss.usermodel.IndexedColors;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;\n\n/**\n * Excel 导出参数\n * \n * @author JEECG\n * @version 1.0 2013年8月24日\n */\npublic class ExportParams extends ExcelBaseParams {\n\n\t/**\n\t * 表格名称\n\t */\n\tprivate String title;\n\n\t/**\n\t * 表格名称\n\t */\n\tprivate short titleHeight = 10;\n\n\t/**\n\t * 第二行名称\n\t */\n\tprivate String secondTitle;\n\n\t/**\n\t * 表格名称\n\t */\n\tprivate short secondTitleHeight = 8;\n\t/**\n\t * sheetName\n\t */\n\tprivate String sheetName;\n\t/**\n\t * 过滤的属性\n\t */\n\tprivate String[] exclusions;\n\t/**\n\t * 是否添加需要需要\n\t */\n\tprivate boolean addIndex;\n\t/**\n\t * 是否添加需要需要\n\t */\n\tprivate String indexName = \"序号\";\n\t/**\n\t * 冰冻列\n\t */\n\tprivate int freezeCol;\n\t/**\n\t * 表头颜色\n\t */\n\tprivate short color = IndexedColors.WHITE.index;\n\t/**\n\t * 属性说明行的颜色 例如:HSSFColor.SKY_BLUE.index 默认\n\t */\n\tprivate short headerColor = IndexedColors.SKY_BLUE.index;\n\t/**\n\t * Excel 导出版本\n\t */\n\tprivate ExcelType type = ExcelType.HSSF;\n\t/**\n\t * Excel 导出style\n\t */\n\tprivate Class<?> style = ExcelExportStylerDefaultImpl.class;\n\t/**\n\t * 是否创建表头\n\t */\n\tprivate boolean isCreateHeadRows = true;\n\n\t/**\n\t * 本地文件存储根路径  base path\n\t */\n\tprivate String imageBasePath;\n//update-begin---author:liusq  Date:20220104  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\t/**\n\t * 是否固定表头\n\t */\n\tprivate boolean isFixedTitle     = true;\n\t/**\n\t * 单sheet最大值\n\t * 03版本默认6W行,07默认100W\n\t */\n\tprivate int     maxNum           = 0;\n\t/**\n\t * 导出时在excel中每个列的高度 单位为字符，一个汉字=2个字符\n\t * 全局设置,优先使用\n\t */\n\tprivate short height = 0;\n\n\t/**\n\t * 只读\n\t */\n\tprivate boolean readonly = false;\n//update-end---author:liusq  Date:20220104  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\tpublic ExportParams() {\n\n\t}\n\n\tpublic ExportParams(String title, String sheetName) {\n\t\tthis.title = title;\n\t\tthis.sheetName = sheetName;\n\t}\n\n\tpublic ExportParams(String title, String sheetName, ExcelType type) {\n\t\tthis.title = title;\n\t\tthis.sheetName = sheetName;\n\t\tthis.type = type;\n\t}\n\n\tpublic ExportParams(String title, String secondTitle, String sheetName) {\n\t\tthis.title = title;\n\t\tthis.secondTitle = secondTitle;\n\t\tthis.sheetName = sheetName;\n\t}\n    \n    public ExportParams(String title, String secondTitle, String sheetName, ExcelType type) {\n        this.title = title;\n        this.secondTitle = secondTitle;\n        this.sheetName = sheetName;\n        this.type = type;\n    }\n\n\tpublic ExportParams(String title, String secondTitle, String sheetName,String imageBasePath) {\n\t\tthis.title = title;\n\t\tthis.secondTitle = secondTitle;\n\t\tthis.sheetName = sheetName;\n\t\tthis.imageBasePath = imageBasePath;\n\t}\n\n\tpublic short getColor() {\n\t\treturn color;\n\t}\n\n\tpublic String[] getExclusions() {\n\t\treturn exclusions;\n\t}\n\n\tpublic short getHeaderColor() {\n\t\treturn headerColor;\n\t}\n\n\tpublic String getSecondTitle() {\n\t\treturn secondTitle;\n\t}\n\n\tpublic short getSecondTitleHeight() {\n\t\treturn (short) (secondTitleHeight * 50);\n\t}\n\n\tpublic String getSheetName() {\n\t\treturn sheetName;\n\t}\n\n\tpublic String getTitle() {\n\t\treturn title;\n\t}\n\n\tpublic short getTitleHeight() {\n\t\treturn (short) (titleHeight * 50);\n\t}\n\n\tpublic boolean isAddIndex() {\n\t\treturn addIndex;\n\t}\n\n\tpublic void setAddIndex(boolean addIndex) {\n\t\tthis.addIndex = addIndex;\n\t}\n\n\tpublic void setColor(short color) {\n\t\tthis.color = color;\n\t}\n\n\tpublic void setExclusions(String[] exclusions) {\n\t\tthis.exclusions = exclusions;\n\t}\n\n\tpublic void setHeaderColor(short headerColor) {\n\t\tthis.headerColor = headerColor;\n\t}\n\n\tpublic void setSecondTitle(String secondTitle) {\n\t\tthis.secondTitle = secondTitle;\n\t}\n\n\tpublic void setSecondTitleHeight(short secondTitleHeight) {\n\t\tthis.secondTitleHeight = secondTitleHeight;\n\t}\n\n\tpublic void setSheetName(String sheetName) {\n\t\tthis.sheetName = sheetName;\n\t}\n\n\tpublic void setTitle(String title) {\n\t\tthis.title = title;\n\t}\n\n\tpublic void setTitleHeight(short titleHeight) {\n\t\tthis.titleHeight = titleHeight;\n\t}\n\n\tpublic ExcelType getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(ExcelType type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic String getIndexName() {\n\t\treturn indexName;\n\t}\n\n\tpublic void setIndexName(String indexName) {\n\t\tthis.indexName = indexName;\n\t}\n\n\tpublic Class<?> getStyle() {\n\t\treturn style;\n\t}\n\n\tpublic void setStyle(Class<?> style) {\n\t\tthis.style = style;\n\t}\n\n\tpublic int getFreezeCol() {\n\t\treturn freezeCol;\n\t}\n\n\tpublic void setFreezeCol(int freezeCol) {\n\t\tthis.freezeCol = freezeCol;\n\t}\n\n\tpublic boolean isCreateHeadRows() {\n\t\treturn isCreateHeadRows;\n\t}\n\n\tpublic void setCreateHeadRows(boolean isCreateHeadRows) {\n\t\tthis.isCreateHeadRows = isCreateHeadRows;\n\t}\n\n\tpublic String getImageBasePath() {\n\t\treturn imageBasePath;\n\t}\n\n\tpublic void setImageBasePath(String imageBasePath) {\n\t\tthis.imageBasePath = imageBasePath;\n\t}\n\n\tpublic int getMaxNum() {\n\t\treturn maxNum;\n\t}\n\n\tpublic void setMaxNum(int maxNum) {\n\t\tthis.maxNum = maxNum;\n\t}\n\n\tpublic short getHeight() {\n\t\treturn height == -1 ? -1 : (short) (height * 50);\n\t}\n\n\tpublic void setHeight(short height) {\n\t\tthis.height = height;\n\t}\n\n\tpublic boolean isFixedTitle() {\n\t\treturn isFixedTitle;\n\t}\n\n\tpublic void setFixedTitle(boolean fixedTitle) {\n\t\tisFixedTitle = fixedTitle;\n\t}\n\n\tpublic boolean isReadonly() {\n\t\treturn readonly;\n\t}\n\n\tpublic void setReadonly(boolean readonly) {\n\t\tthis.readonly = readonly;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/ImportParams.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity;\n\nimport org.jeecgframework.poi.handler.inter.IExcelVerifyHandler;\n\nimport java.util.List;\n\n/**\n * 导入参数设置\n * \n * @author JEECG\n * @date 2013-9-24\n * @version 1.0\n */\npublic class ImportParams extends ExcelBaseParams {\n\t/**\n\t * 表格标题行数,默认0\n\t */\n\tprivate int titleRows = 0;\n\t/**\n\t * 表头行数,默认1\n\t */\n\tprivate int headRows = 1;\n\t/**\n\t * 字段真正值和列标题之间的距离 默认0\n\t */\n\tprivate int startRows = 0;\n\t/**\n\t * 主键设置,如何这个cell没有值,就跳过 或者认为这个是list的下面的值\n\t */\n\tprivate int keyIndex = 0;\n\t//update-begin-author:liusq date:20220605 for:https://gitee.com/jeecg/jeecg-boot/issues/I57UPC 导入 ImportParams 中没有startSheetIndex参数\n\t/**\n\t * 开始读取的sheet位置,默认为0\n\t */\n\tprivate int                 startSheetIndex  = 0;\n\t//update-end-author:liusq date:20220605 for:https://gitee.com/jeecg/jeecg-boot/issues/I57UPC 导入 ImportParams 中没有startSheetIndex参数\n\n\t//update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错\n\t/**\n\t * 上传表格需要读取的sheet 数量,默认为0\n\t */\n\tprivate int sheetNum = 0;\n\t//update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错\n\t/**\n\t * 是否需要保存上传的Excel,默认为false\n\t */\n\tprivate boolean needSave = false;\n\t/**\n\t * 保存上传的Excel目录,默认是 如 TestEntity这个类保存路径就是\n\t * upload/excelUpload/Test/yyyyMMddHHmss_***** 保存名称上传时间_五位随机数\n\t */\n\tprivate String saveUrl = \"upload/excelUpload\";\n\t/**\n\t * 校验处理接口\n\t */\n\tprivate IExcelVerifyHandler verifyHanlder;\n\t/**\n\t * 最后的无效行数\n\t */\n\tprivate int lastOfInvalidRow = 0;\n\n\t/**\n\t * 不需要解析的表头 只作为多表头展示，无字段与其绑定\n\t */\n\tprivate List<String> ignoreHeaderList;\n\n\t/**\n\t * 指定导入的sheetName\n\t */\n\tprivate String sheetName;\n\n\t/**\n\t * 图片列 集合\n\t */\n\tprivate List<String> imageList;\n\n\tpublic int getHeadRows() {\n\t\treturn headRows;\n\t}\n\n\tpublic int getKeyIndex() {\n\t\treturn keyIndex;\n\t}\n\n\tpublic String getSaveUrl() {\n\t\treturn saveUrl;\n\t}\n\n\tpublic int getSheetNum() {\n\t\treturn sheetNum;\n\t}\n\n\tpublic int getStartRows() {\n\t\treturn startRows;\n\t}\n\n\tpublic int getTitleRows() {\n\t\treturn titleRows;\n\t}\n\n\tpublic IExcelVerifyHandler getVerifyHanlder() {\n\t\treturn verifyHanlder;\n\t}\n\n\tpublic boolean isNeedSave() {\n\t\treturn needSave;\n\t}\n\n\tpublic void setHeadRows(int headRows) {\n\t\tthis.headRows = headRows;\n\t}\n\n\tpublic void setKeyIndex(int keyIndex) {\n\t\tthis.keyIndex = keyIndex;\n\t}\n\n\tpublic void setNeedSave(boolean needSave) {\n\t\tthis.needSave = needSave;\n\t}\n\n\tpublic void setSaveUrl(String saveUrl) {\n\t\tthis.saveUrl = saveUrl;\n\t}\n\n\tpublic void setSheetNum(int sheetNum) {\n\t\tthis.sheetNum = sheetNum;\n\t}\n\n\tpublic void setStartRows(int startRows) {\n\t\tthis.startRows = startRows;\n\t}\n\n\tpublic void setTitleRows(int titleRows) {\n\t\tthis.titleRows = titleRows;\n\t}\n\n\tpublic void setVerifyHanlder(IExcelVerifyHandler verifyHanlder) {\n\t\tthis.verifyHanlder = verifyHanlder;\n\t}\n\n\tpublic int getLastOfInvalidRow() {\n\t\treturn lastOfInvalidRow;\n\t}\n\n\tpublic void setLastOfInvalidRow(int lastOfInvalidRow) {\n\t\tthis.lastOfInvalidRow = lastOfInvalidRow;\n\t}\n\n\tpublic List<String> getImageList() {\n\t\treturn imageList;\n\t}\n\n\tpublic void setImageList(List<String> imageList) {\n\t\tthis.imageList = imageList;\n\t}\n\n\tpublic List<String> getIgnoreHeaderList() {\n\t\treturn ignoreHeaderList;\n\t}\n\n\tpublic void setIgnoreHeaderList(List<String> ignoreHeaderList) {\n\t\tthis.ignoreHeaderList = ignoreHeaderList;\n\t}\n\n\tpublic String getSheetName() {\n\t\treturn sheetName;\n\t}\n\n\tpublic void setSheetName(String sheetName) {\n\t\tthis.sheetName = sheetName;\n\t}\n\n\t/**\n\t * 根据表头显示的文字 判断是否忽略该表头\n\t * @param text\n\t * @return\n\t */\n\tpublic boolean isIgnoreHeader(String text){\n\t\tif(ignoreHeaderList!=null && ignoreHeaderList.indexOf(text)>=0){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\tpublic int getStartSheetIndex() {\n\t\treturn startSheetIndex;\n\t}\n\n\tpublic void setStartSheetIndex(int startSheetIndex) {\n\t\tthis.startSheetIndex = startSheetIndex;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/TemplateExportParams.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity;\n\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.apache.poi.ss.usermodel.WorkbookFactory;\nimport org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * 模板导出参数设置\n * \n * @author JEECG\n * @date 2013-10-17\n * @version 1.0\n */\npublic class TemplateExportParams extends ExcelBaseParams {\n\n\t/**\n\t * 输出全部的sheet\n\t */\n\tprivate boolean scanAllsheet = false;\n\t/**\n\t * 模板的路径\n\t */\n\tprivate String templateUrl;\n\t/**\n\t * 模板\n\t */\n\tprivate Workbook templateWb;\n\n\t/**\n\t * 需要导出的第几个 sheetNum,默认是第0个\n\t */\n\tprivate Integer[] sheetNum = new Integer[] { 0 };\n\n\t/**\n\t * 这只sheetName 不填就使用原来的\n\t */\n\tprivate String[] sheetName;\n\n\t/**\n\t * 表格列标题行数,默认1\n\t */\n\tprivate int headingRows = 1;\n\n\t/**\n\t * 表格列标题开始行,默认1\n\t */\n\tprivate int headingStartRow = 1;\n\t/**\n\t * 设置数据源的NUM\n\t */\n\tprivate int dataSheetNum = 0;\n\t/**\n\t * Excel 导出style\n\t */\n\tprivate Class<?> style = ExcelExportStylerDefaultImpl.class;\n\t/**\n\t * FOR EACH 用到的局部变量\n\t */\n\tprivate String tempParams = \"t\";\n    //列循环支持\n\tprivate boolean   colForEach      = false;\n\n\t/**\n\t * 默认构造器\n\t */\n\tpublic TemplateExportParams() {\n\n\t}\n\n\t/**\n\t * 构造器\n\t * \n\t * @param templateUrl\n\t *            模板路径\n\t * @param scanAllsheet\n\t *            是否输出全部的sheet\n\t * @param sheetName\n\t *            sheet的名称,可不填\n\t */\n\tpublic TemplateExportParams(String templateUrl, boolean scanAllsheet, String... sheetName) {\n\t\tthis.templateUrl = templateUrl;\n\t\tthis.scanAllsheet = scanAllsheet;\n\t\tif (sheetName != null && sheetName.length > 0) {\n\t\t\tthis.sheetName = sheetName;\n\n\t\t}\n\t}\n\n\t/**\n\t * 构造器\n\t * \n\t * @param templateUrl\n\t *            模板路径\n\t * @param sheetNum\n\t *            sheet 的位置,可不填\n\t */\n\tpublic TemplateExportParams(String templateUrl, Integer... sheetNum) {\n\t\tthis.templateUrl = templateUrl;\n\t\tif (sheetNum != null && sheetNum.length > 0) {\n\t\t\tthis.sheetNum = sheetNum;\n\t\t}\n\t}\n\n\t/**\n\t * 单个sheet输出构造器\n\t * \n\t * @param templateUrl\n\t *            模板路径\n\t * @param sheetName\n\t *            sheet的名称\n\t * @param sheetNum\n\t *            sheet的位置,可不填\n\t */\n\tpublic TemplateExportParams(String templateUrl, String sheetName, Integer... sheetNum) {\n\t\tthis.templateUrl = templateUrl;\n\t\tthis.sheetName = new String[] { sheetName };\n\t\tif (sheetNum != null && sheetNum.length > 0) {\n\t\t\tthis.sheetNum = sheetNum;\n\t\t}\n\t}\n\t//update-begin-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式\n\t/**\n\t * 构造器\n\t * @param inputStream 输入流\n\t * @param scanAllsheet 是否输出全部的sheet\n\t * @param sheetName    sheet的名称,可不填\n\t */\n\tpublic TemplateExportParams(InputStream inputStream, boolean scanAllsheet, String... sheetName) throws IOException {\n\t\tthis.templateWb = WorkbookFactory.create(inputStream);\n\t\tthis.scanAllsheet = scanAllsheet;\n\t\tif (sheetName != null && sheetName.length > 0) {\n\t\t\tthis.sheetName = sheetName;\n\t\t}\n\t}\n\t/**\n\t * 构造器\n\t * @param inputStream 输入流\n\t * @param sheetNum    sheet 的位置,可不填\n\t */\n\tpublic TemplateExportParams(InputStream inputStream, Integer... sheetNum) throws IOException {\n\t\tthis.templateWb = WorkbookFactory.create(inputStream);\n\t\tif (sheetNum != null && sheetNum.length > 0) {\n\t\t\tthis.sheetNum = sheetNum;\n\t\t}\n\t}\n\n\t/**\n\t * 单个sheet输出构造器\n\t * @param inputStream 输入流\n\t * @param sheetName   sheet的名称\n\t * @param sheetNum    sheet的位置,可不填\n\t */\n\tpublic TemplateExportParams(InputStream inputStream, String sheetName, Integer... sheetNum) throws IOException {\n\t\tthis.templateWb = WorkbookFactory.create(inputStream);\n\t\tthis.sheetName = new String[] { sheetName };\n\t\tif (sheetNum != null && sheetNum.length > 0) {\n\t\t\tthis.sheetNum = sheetNum;\n\t\t}\n\t}\n\t//update-end-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式\n\tpublic int getHeadingRows() {\n\t\treturn headingRows;\n\t}\n\n\tpublic int getHeadingStartRow() {\n\t\treturn headingStartRow;\n\t}\n\n\tpublic String[] getSheetName() {\n\t\treturn sheetName;\n\t}\n\n\tpublic Integer[] getSheetNum() {\n\t\treturn sheetNum;\n\t}\n\n\tpublic String getTemplateUrl() {\n\t\treturn templateUrl;\n\t}\n\n\tpublic void setHeadingRows(int headingRows) {\n\t\tthis.headingRows = headingRows;\n\t}\n\n\tpublic void setHeadingStartRow(int headingStartRow) {\n\t\tthis.headingStartRow = headingStartRow;\n\t}\n\n\tpublic void setSheetName(String[] sheetName) {\n\t\tthis.sheetName = sheetName;\n\t}\n\n\tpublic void setSheetName(String sheetName) {\n\t\tthis.sheetName = new String[] { sheetName };\n\t}\n\n\tpublic void setSheetNum(Integer[] sheetNum) {\n\t\tthis.sheetNum = sheetNum;\n\t}\n\n\tpublic void setSheetNum(Integer sheetNum) {\n\t\tthis.sheetNum = new Integer[] { sheetNum };\n\t}\n\n\tpublic void setTemplateUrl(String templateUrl) {\n\t\tthis.templateUrl = templateUrl;\n\t}\n\n\tpublic Class<?> getStyle() {\n\t\treturn style;\n\t}\n\n\tpublic void setStyle(Class<?> style) {\n\t\tthis.style = style;\n\t}\n\n\tpublic int getDataSheetNum() {\n\t\treturn dataSheetNum;\n\t}\n\n\tpublic void setDataSheetNum(int dataSheetNum) {\n\t\tthis.dataSheetNum = dataSheetNum;\n\t}\n\n\tpublic boolean isScanAllsheet() {\n\t\treturn scanAllsheet;\n\t}\n\n\tpublic void setScanAllsheet(boolean scanAllsheet) {\n\t\tthis.scanAllsheet = scanAllsheet;\n\t}\n\n\tpublic String getTempParams() {\n\t\treturn tempParams;\n\t}\n\n\tpublic void setTempParams(String tempParams) {\n\t\tthis.tempParams = tempParams;\n\t}\n\n\tpublic boolean isColForEach() {\n\t\treturn colForEach;\n\t}\n\n\tpublic void setColForEach(boolean colForEach) {\n\t\tthis.colForEach = colForEach;\n\t}\n\tpublic Workbook getTemplateWb() {\n\t\treturn templateWb;\n\t}\n\n\tpublic void setTemplateWb(Workbook templateWb) {\n\t\tthis.templateWb = templateWb;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/enmus/CellValueType.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.enmus;\n\n/**\n * Cell 值得类型\n * \n * @author JEECG\n * @date 2014年12月29日 下午10:20:49\n */\npublic enum CellValueType {\n\n\tString, Number, Boolean, Date, TElement, Null, None;\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/enmus/ExcelStyleType.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.enmus;\n\nimport org.jeecgframework.poi.excel.export.styler.ExcelExportStylerBorderImpl;\nimport org.jeecgframework.poi.excel.export.styler.ExcelExportStylerColorImpl;\nimport org.jeecgframework.poi.excel.export.styler.ExcelExportStylerDefaultImpl;\n\n/**\n * 插件提供的几个默认样式\n * \n * @author JEECG\n * @date 2015年1月9日 下午9:02:24\n */\npublic enum ExcelStyleType {\n\n\tNONE(\"默认样式\", ExcelExportStylerDefaultImpl.class), BORDER(\"边框样式\", ExcelExportStylerBorderImpl.class), COLOR(\"间隔行样式\", ExcelExportStylerColorImpl.class);\n\n\tprivate String name;\n\tprivate Class<?> clazz;\n\n\tExcelStyleType(String name, Class<?> clazz) {\n\t\tthis.name = name;\n\t\tthis.clazz = clazz;\n\t}\n\n\tpublic Class<?> getClazz() {\n\t\treturn clazz;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/enmus/ExcelType.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.enmus;\n\n/**\n * Excel 文件格式类型枚举\n * <p>用于指定导出/导入的 Excel 文件格式版本</p>\n * \n * @author JEECG\n * @date 2014年12月29日 下午9:08:21\n */\npublic enum ExcelType {\n\n\t/**\n\t * HSSF 格式 - Excel 97-2003 版本 (.xls)\n\t * <ul>\n\t *   <li>文件扩展名：.xls</li>\n\t *   <li>最大行数：65,536 行（2^16）</li>\n\t *   <li>最大列数：256 列（2^8）</li>\n\t *   <li>适用场景：兼容老版本 Excel，数据量较小的场景</li>\n\t *   <li>对应 POI 类：HSSFWorkbook</li>\n\t * </ul>\n\t */\n\tHSSF,\n\t\n\t/**\n\t * XSSF 格式 - Excel 2007+ 版本 (.xlsx)\n\t * <ul>\n\t *   <li>文件扩展名：.xlsx</li>\n\t *   <li>最大行数：1,048,576 行（2^20）</li>\n\t *   <li>最大列数：16,384 列（2^14）</li>\n\t *   <li>适用场景：现代 Excel 版本，大数据量导出，推荐使用</li>\n\t *   <li>对应 POI 类：XSSFWorkbook</li>\n\t *   <li>优势：支持更大数据量，文件压缩比更高，功能更丰富</li>\n\t * </ul>\n\t * <p><b>注意：</b>导出时请确保文件扩展名与格式类型匹配，避免文件损坏</p>\n\t */\n\tXSSF;\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelBaseEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.params;\n\nimport java.lang.reflect.Method;\nimport java.util.List;\n\n/**\n * Excel 导入导出基础对象类\n * \n * @author JEECG\n * @date 2014年6月20日 下午2:26:09\n */\npublic class ExcelBaseEntity {\n\t/**\n\t * 对应name\n\t */\n\tprotected String name;\n\t/**\n\t * 对应type\n\t */\n\tprivate int type = 1;\n\t/**\n\t * 数据库格式\n\t */\n\tprivate String databaseFormat;\n\t/**\n\t * 导出日期格式\n\t */\n\tprivate String format;\n\n\t/**\n\t * 数字格式化,参数是Pattern,使用的对象是DecimalFormat\n\t */\n\tprivate String numFormat;\n\t/**\n\t * 替换值表达式 ：\"男_1\",\"女_0\"\n\t */\n\tprivate String[] replace;\n\t/**\n\t * 替换是否是替换多个值\n\t */\n\tprivate boolean multiReplace;\n\n\t/**\n\t * 表头组名称\n\t */\n\tprivate String groupName;\n\t\n\t/**\n\t * set/get方法\n\t */\n\tprivate Method method;\n\t/**\n\t * 固定的列\n\t */\n\tprivate Integer      fixedIndex;\n\t/**\n\t * 字典名称\n\t */\n\tprivate String       dict;\n\t/**\n\t * 这个是不是超链接,如果是需要实现接口返回对象\n\t */\n\tprivate boolean     hyperlink;\n\n\tprivate List<Method> methods;\n\n\tpublic String getDatabaseFormat() {\n\t\treturn databaseFormat;\n\t}\n\n\tpublic String getFormat() {\n\t\treturn format;\n\t}\n\n\tpublic Method getMethod() {\n\t\treturn method;\n\t}\n\n\tpublic List<Method> getMethods() {\n\t\treturn methods;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String[] getReplace() {\n\t\treturn replace;\n\t}\n\n\tpublic int getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setDatabaseFormat(String databaseFormat) {\n\t\tthis.databaseFormat = databaseFormat;\n\t}\n\n\tpublic void setFormat(String format) {\n\t\tthis.format = format;\n\t}\n\n\tpublic void setMethod(Method method) {\n\t\tthis.method = method;\n\t}\n\n\tpublic void setMethods(List<Method> methods) {\n\t\tthis.methods = methods;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic void setReplace(String[] replace) {\n\t\tthis.replace = replace;\n\t}\n\n\tpublic void setType(int type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic boolean isMultiReplace() {\n\t\treturn multiReplace;\n\t}\n\tpublic void setMultiReplace(boolean multiReplace) {\n\t\tthis.multiReplace = multiReplace;\n\t}\n\n\tpublic String getNumFormat() {\n\t\treturn numFormat;\n\t}\n\n\tpublic void setNumFormat(String numFormat) {\n\t\tthis.numFormat = numFormat;\n\t}\n\n\tpublic String getGroupName() {\n\t\treturn groupName;\n\t}\n\n\tpublic void setGroupName(String groupName) {\n\t\tthis.groupName = groupName;\n\t}\n\n\tpublic Integer getFixedIndex() {\n\t\treturn fixedIndex;\n\t}\n\n\tpublic void setFixedIndex(Integer fixedIndex) {\n\t\tthis.fixedIndex = fixedIndex;\n\t}\n\n\tpublic String getDict() {\n\t\treturn dict;\n\t}\n\n\tpublic void setDict(String dict) {\n\t\tthis.dict = dict;\n\t}\n\n\tpublic boolean isHyperlink() {\n\t\treturn hyperlink;\n\t}\n\n\tpublic void setHyperlink(boolean hyperlink) {\n\t\tthis.hyperlink = hyperlink;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelCollectionParams.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.params;\n\nimport java.util.Map;\n\n/**\n * Excel 对于的 Collection\n * \n * @author JEECG\n * @date 2013-9-26\n * @version 1.0\n */\npublic class ExcelCollectionParams {\n\n\t/**\n\t * 集合对应的名称\n\t */\n\tprivate String name;\n\t/**\n\t * Excel 列名称\n\t */\n\tprivate String excelName;\n\t/**\n\t * 实体对象\n\t */\n\tprivate Class<?> type;\n\t/**\n\t * 这个list下面的参数集合实体对象\n\t */\n\tprivate Map<String, ExcelImportEntity> excelParams;\n\n\tpublic Map<String, ExcelImportEntity> getExcelParams() {\n\t\treturn excelParams;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic Class<?> getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setExcelParams(Map<String, ExcelImportEntity> excelParams) {\n\t\tthis.excelParams = excelParams;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic void setType(Class<?> type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic String getExcelName() {\n\t\treturn excelName;\n\t}\n\n\tpublic void setExcelName(String excelName) {\n\t\tthis.excelName = excelName;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelExportEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.params;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * excel 导出工具类,对cell类型做映射\n * \n * @author JEECG\n * @version 1.0 2013年8月24日\n */\npublic class ExcelExportEntity extends ExcelBaseEntity implements Comparable<ExcelExportEntity> {\n\n\t/**\n\t * 如果是MAP导出,这个是map的key\n\t */\n\tprivate Object key;\n\n\tprivate double width = 10;\n\n\tprivate double height = 10;\n\n\t/**\n\t * 图片的类型,1是文件地址(class目录),2是数据库字节,3是文件地址(磁盘目录)，4网络图片\n\t */\n\tprivate int exportImageType = 3;\n\n\t/**\n\t * 图片储存位置(磁盘目录) 用于导出获取图片绝对路径\n\t */\n\tprivate String imageBasePath;\n\n\t/**\n\t * 排序顺序\n\t */\n\tprivate int orderNum = 0;\n\n\t/**\n\t * 是否支持换行\n\t */\n\tprivate boolean isWrap;\n\n\t/**\n\t * 是否需要合并\n\t */\n\tprivate boolean needMerge;\n\t/**\n\t * 单元格纵向合并\n\t */\n\tprivate boolean mergeVertical;\n\t/**\n\t * 合并依赖\n\t */\n\tprivate int[] mergeRely;\n\t/**\n\t * 后缀\n\t */\n\tprivate String suffix;\n\t/**\n\t * 统计\n\t */\n\tprivate boolean isStatistics;\n\n\t/**\n\t * 是否横向合并\n\t */\n\tprivate boolean colspan;\n\n\t/**\n\t * 被横向合并的列名称\n\t */\n\tprivate List<String> subColumnList;\n\n\t/**\n\t * 父表头的名称\n\t */\n\tprivate String groupName;\n\t/**\n\t *  是否隐藏列\n\t */\n\tprivate boolean isColumnHidden;\n\n\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t/**\n\t * 是否为动态列\n\t */\n\tprivate boolean dynamic;\n\t/**\n\t * 动态列标题字段\n\t */\n\tprivate String dynamicField;\n\t/**\n\t * 动态列值字段\n\t */\n\tprivate String dynamicValue;\n\t/**\n\t * 是否保留原始列\n\t */\n\tprivate boolean dynamicKeepSelf;\n\t/**\n\t * 当前动态列对应的标题名称\n\t */\n\tprivate String dynamicColumnName;\n\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\n\tprivate List<ExcelExportEntity> list;\n\n\tpublic ExcelExportEntity() {\n\n\t}\n\n\tpublic ExcelExportEntity(String name) {\n\t\tsuper.name = name;\n\t}\n\n\tpublic ExcelExportEntity(String name, Object key) {\n\t\tsuper.name = name;\n\t\tthis.key = key;\n\t}\n\n\t/**\n\t * 构造器\n\t * @param name 描述-文字\n\t * @param key 存储key 如果是MAP导出,这个是map的key\n\t * @param colspan 是否为合并列（a,b列公用一个表头c,则a,b,c都需要设置为true）\n\t */\n\tpublic ExcelExportEntity(String name, Object key, boolean colspan) {\n\t\tsuper.name = name;\n\t\tthis.key = key;\n\t\tthis.colspan = colspan;\n\t\tthis.needMerge = colspan;\n\t}\n\n\tpublic ExcelExportEntity(String name, Object key, int width) {\n\t\tsuper.name = name;\n\t\tthis.width = width;\n\t\tthis.key = key;\n\t}\n\n\tpublic int getExportImageType() {\n\t\treturn exportImageType;\n\t}\n\n\tpublic double getHeight() {\n\t\treturn height;\n\t}\n\n\tpublic Object getKey() {\n\t\treturn key;\n\t}\n\n\tpublic List<ExcelExportEntity> getList() {\n\t\treturn list;\n\t}\n\n\tpublic int[] getMergeRely() {\n\t\treturn mergeRely == null ? new int[0] : mergeRely;\n\t}\n\n\tpublic int getOrderNum() {\n\t\treturn orderNum;\n\t}\n\n\tpublic double getWidth() {\n\t\treturn width;\n\t}\n\n\tpublic boolean isMergeVertical() {\n\t\treturn mergeVertical;\n\t}\n\n\tpublic boolean isNeedMerge() {\n\t\treturn needMerge;\n\t}\n\n\tpublic boolean isWrap() {\n\t\treturn isWrap;\n\t}\n\n\tpublic void setExportImageType(int exportImageType) {\n\t\tthis.exportImageType = exportImageType;\n\t}\n\n\tpublic void setHeight(double height) {\n\t\tthis.height = height;\n\t}\n\n\tpublic void setKey(Object key) {\n\t\tthis.key = key;\n\t}\n\n\tpublic void setList(List<ExcelExportEntity> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic void setMergeRely(int[] mergeRely) {\n\t\tthis.mergeRely = mergeRely;\n\t}\n\n\tpublic void setMergeVertical(boolean mergeVertical) {\n\t\tthis.mergeVertical = mergeVertical;\n\t}\n\n\tpublic void setNeedMerge(boolean needMerge) {\n\t\tthis.needMerge = needMerge;\n\t}\n\n\tpublic void setOrderNum(int orderNum) {\n\t\tthis.orderNum = orderNum;\n\t}\n\n\tpublic void setWidth(double width) {\n\t\tthis.width = width;\n\t}\n\n\tpublic void setWrap(boolean isWrap) {\n\t\tthis.isWrap = isWrap;\n\t}\n\n\tpublic String getSuffix() {\n\t\treturn suffix;\n\t}\n\n\tpublic void setSuffix(String suffix) {\n\t\tthis.suffix = suffix;\n\t}\n\n\tpublic boolean isStatistics() {\n\t\treturn isStatistics;\n\t}\n\n\tpublic void setStatistics(boolean isStatistics) {\n\t\tthis.isStatistics = isStatistics;\n\t}\n\n\tpublic String getImageBasePath() {\n\t\treturn imageBasePath;\n\t}\n\n\tpublic void setImageBasePath(String imageBasePath) {\n\t\tthis.imageBasePath = imageBasePath;\n\t}\n\n\tpublic boolean isColspan() {\n\t\treturn colspan;\n\t}\n\n\tpublic void setColspan(boolean colspan) {\n\t\tthis.colspan = colspan;\n\t}\n\n\tpublic List<String> getSubColumnList() {\n\t\treturn subColumnList;\n\t}\n\n\tpublic void setSubColumnList(List<String> subColumnList) {\n\t\tthis.subColumnList = subColumnList;\n\t}\n\n\tpublic String getGroupName() {\n\t\treturn groupName;\n\t}\n\n\tpublic void setGroupName(String groupName) {\n\t\tthis.groupName = groupName;\n\t}\n\n\tpublic boolean isColumnHidden() {\n\t\treturn isColumnHidden;\n\t}\n\n\tpublic void setColumnHidden(boolean columnHidden) {\n\t\tisColumnHidden = columnHidden;\n\t}\n\n\t/**\n\t * 是否为合并子列\n\t * @return\n\t */\n\tpublic boolean isSubColumn(){\n\t\treturn this.colspan && (this.subColumnList==null || this.subColumnList.size()==0);\n\t}\n\n\t/**\n\t * 是否为合并父列\n\t * @return\n\t */\n\tpublic boolean isMergeColumn(){\n\t\treturn this.colspan && this.subColumnList!=null && this.subColumnList.size()>0;\n\t}\n\n\tpublic boolean isDynamic() {\n\t\treturn dynamic;\n\t}\n\n\tpublic void setDynamic(boolean dynamic) {\n\t\tthis.dynamic = dynamic;\n\t}\n\n\tpublic String getDynamicField() {\n\t\treturn dynamicField;\n\t}\n\n\tpublic void setDynamicField(String dynamicField) {\n\t\tthis.dynamicField = dynamicField;\n\t}\n\n\tpublic String getDynamicValue() {\n\t\treturn dynamicValue;\n\t}\n\n\tpublic void setDynamicValue(String dynamicValue) {\n\t\tthis.dynamicValue = dynamicValue;\n\t}\n\n\tpublic boolean isDynamicKeepSelf() {\n\t\treturn dynamicKeepSelf;\n\t}\n\n\tpublic void setDynamicKeepSelf(boolean dynamicKeepSelf) {\n\t\tthis.dynamicKeepSelf = dynamicKeepSelf;\n\t}\n\n\tpublic String getDynamicColumnName() {\n\t\treturn dynamicColumnName;\n\t}\n\n\tpublic void setDynamicColumnName(String dynamicColumnName) {\n\t\tthis.dynamicColumnName = dynamicColumnName;\n\t}\n\t/**\n\t * 获取被合并的子列\n\t * @param all\n\t * @return\n\t */\n\tpublic List<ExcelExportEntity> initSubExportEntity(List<ExcelExportEntity> all){\n\t\tList<ExcelExportEntity> sub = new ArrayList<ExcelExportEntity>();\n\t\tfor (ExcelExportEntity temp : all) {\n\t\t\tif(this.subColumnList.contains(temp.getKey())){\n\t\t\t\tsub.add(temp);\n\t\t\t}\n\t\t}\n\t\tthis.setList(sub);\n\t\treturn sub;\n\t}\n\n\t@Override\n\tpublic int compareTo(ExcelExportEntity prev) {\n\t\treturn this.getOrderNum() - prev.getOrderNum();\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelForEachParams.java",
    "content": "package org.jeecgframework.poi.excel.entity.params;\n\nimport java.io.Serializable;\nimport java.util.Stack;\n\nimport org.apache.poi.ss.usermodel.CellStyle;\n\n/**\n * 模板for each是的参数\n * @author JueYue\n * @date 2015年4月29日 下午9:22:48\n */\npublic class ExcelForEachParams implements Serializable {\n\n    /**\n     *\n     */\n    private static final long serialVersionUID = 1L;\n    /**\n     * key\n     */\n    private String            name;\n    /**\n     * key\n     */\n    private Stack<String> tempName;\n    /**\n     * 模板的cellStyle\n     */\n    private CellStyle         cellStyle;\n    /**\n     * 行高\n     */\n    private short             height;\n    /**\n     * 常量值\n     */\n    private String            constValue;\n    /**\n     * 列合并\n     */\n    private int               colspan          = 1;\n    /**\n     * 行合并\n     */\n    private int               rowspan          = 1;\n    /**\n     * 行合并\n     */\n    private              boolean       collectCell;\n\n    public ExcelForEachParams() {\n\n    }\n\n    public ExcelForEachParams(String name, CellStyle cellStyle, short height) {\n        this.name = name;\n        this.cellStyle = cellStyle;\n        this.height = height;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public CellStyle getCellStyle() {\n        return cellStyle;\n    }\n\n    public void setCellStyle(CellStyle cellStyle) {\n        this.cellStyle = cellStyle;\n    }\n\n    public short getHeight() {\n        return height;\n    }\n\n    public void setHeight(short height) {\n        this.height = height;\n    }\n\n    public String getConstValue() {\n        return constValue;\n    }\n\n    public void setConstValue(String constValue) {\n        this.constValue = constValue;\n    }\n\n    public int getColspan() {\n        return colspan;\n    }\n\n    public void setColspan(int colspan) {\n        this.colspan = colspan;\n    }\n\n    public int getRowspan() {\n        return rowspan;\n    }\n\n    public void setRowspan(int rowspan) {\n        this.rowspan = rowspan;\n    }\n\n    public boolean isCollectCell() {\n        return collectCell;\n    }\n\n    public void setCollectCell(boolean collectCell) {\n        this.collectCell = collectCell;\n    }\n\n    public Stack<String> getTempName() {\n        return tempName;\n    }\n\n    public void setTempName(Stack<String> tempName) {\n        this.tempName = tempName;\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelImportEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.params;\n\nimport java.util.List;\n\n/**\n * excel 导入工具类,对cell类型做映射\n * \n * @author JEECG\n * @version 1.0 2013年8月24日\n */\npublic class ExcelImportEntity extends ExcelBaseEntity {\n\t/**\n\t * 对应 Collection NAME\n\t */\n\tprivate String collectionName;\n\t/**\n\t * 保存图片的地址 当saveType设置为3/4时，此值可以设置为：local,minio,alioss\n\t */\n\tprivate String saveUrl;\n\t/**\n\t * 保存图片的类型,1是文件_old,2是数据库字节,3文件地址_new,4网络地址\n\t */\n\tprivate int saveType;\n\t/**\n\t * 对应exportType\n\t */\n\tprivate String classType;\n\t/**\n\t * 校驗參數\n\t */\n\tprivate ExcelVerifyEntity verify;\n\t/**\n\t * 后缀\n\t */\n\tprivate String suffix;\n\n\tprivate List<ExcelImportEntity> list;\n\n\tpublic String getClassType() {\n\t\treturn classType;\n\t}\n\n\tpublic String getCollectionName() {\n\t\treturn collectionName;\n\t}\n\n\tpublic List<ExcelImportEntity> getList() {\n\t\treturn list;\n\t}\n\n\tpublic int getSaveType() {\n\t\treturn saveType;\n\t}\n\n\tpublic String getSaveUrl() {\n\t\treturn saveUrl;\n\t}\n\n\tpublic ExcelVerifyEntity getVerify() {\n\t\treturn verify;\n\t}\n\n\tpublic void setClassType(String classType) {\n\t\tthis.classType = classType;\n\t}\n\n\tpublic void setCollectionName(String collectionName) {\n\t\tthis.collectionName = collectionName;\n\t}\n\n\tpublic void setList(List<ExcelImportEntity> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic void setSaveType(int saveType) {\n\t\tthis.saveType = saveType;\n\t}\n\n\tpublic void setSaveUrl(String saveUrl) {\n\t\tthis.saveUrl = saveUrl;\n\t}\n\n\tpublic void setVerify(ExcelVerifyEntity verify) {\n\t\tthis.verify = verify;\n\t}\n\n\tpublic String getSuffix() {\n\t\treturn suffix;\n\t}\n\n\tpublic void setSuffix(String suffix) {\n\t\tthis.suffix = suffix;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelTemplateParams.java",
    "content": "package org.jeecgframework.poi.excel.entity.params;\n\nimport java.io.Serializable;\n\nimport org.apache.poi.ss.usermodel.CellStyle;\n\n/**\n * 模板便利是的参数\n * \n * @author JEECG\n * @date 2015年4月29日 下午9:22:48\n */\npublic class ExcelTemplateParams implements Serializable {\n\n\t/**\n     * \n     */\n\tprivate static final long serialVersionUID = 1L;\n\t/**\n\t * key\n\t */\n\tprivate String name;\n\t/**\n\t * 模板的cellStyle\n\t */\n\tprivate CellStyle cellStyle;\n\t/**\n\t * 行高\n\t */\n\tprivate short height;\n\n\tpublic ExcelTemplateParams() {\n\n\t}\n\n\tpublic ExcelTemplateParams(String name, CellStyle cellStyle, short height) {\n\t\tthis.name = name;\n\t\tthis.cellStyle = cellStyle;\n\t\tthis.height = height;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic CellStyle getCellStyle() {\n\t\treturn cellStyle;\n\t}\n\n\tpublic void setCellStyle(CellStyle cellStyle) {\n\t\tthis.cellStyle = cellStyle;\n\t}\n\n\tpublic short getHeight() {\n\t\treturn height;\n\t}\n\n\tpublic void setHeight(short height) {\n\t\tthis.height = height;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/ExcelVerifyEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.params;\n\n/**\n * Excel 校验对象\n * \n * @author JEECG\n * @date 2014年6月29日 下午4:24:59\n */\npublic class ExcelVerifyEntity {\n\n\t/**\n\t * 接口校验\n\t * \n\t * @return\n\t */\n\tprivate boolean interHandler;\n\n\t/**\n\t * 不允许空\n\t * \n\t * @return\n\t */\n\tprivate boolean notNull;\n\n\t/**\n\t * 是13位移动电话\n\t * \n\t * @return\n\t */\n\tprivate boolean isMobile;\n\t/**\n\t * 是座机号码\n\t * \n\t * @return\n\t */\n\tprivate boolean isTel;\n\n\t/**\n\t * 是电子邮件\n\t * \n\t * @return\n\t */\n\tprivate boolean isEmail;\n\n\t/**\n\t * 最小长度\n\t * \n\t * @return\n\t */\n\tprivate int minLength;\n\n\t/**\n\t * 最大长度\n\t * \n\t * @return\n\t */\n\tprivate int maxLength;\n\n\t/**\n\t * 正在表达式\n\t * \n\t * @return\n\t */\n\tprivate String regex;\n\t/**\n\t * 正在表达式,错误提示信息\n\t * \n\t * @return\n\t */\n\tprivate String regexTip;\n\n\tpublic int getMaxLength() {\n\t\treturn maxLength;\n\t}\n\n\tpublic int getMinLength() {\n\t\treturn minLength;\n\t}\n\n\tpublic String getRegex() {\n\t\treturn regex;\n\t}\n\n\tpublic String getRegexTip() {\n\t\treturn regexTip;\n\t}\n\n\tpublic boolean isEmail() {\n\t\treturn isEmail;\n\t}\n\n\tpublic boolean isInterHandler() {\n\t\treturn interHandler;\n\t}\n\n\tpublic boolean isMobile() {\n\t\treturn isMobile;\n\t}\n\n\tpublic boolean isNotNull() {\n\t\treturn notNull;\n\t}\n\n\tpublic boolean isTel() {\n\t\treturn isTel;\n\t}\n\n\tpublic void setEmail(boolean isEmail) {\n\t\tthis.isEmail = isEmail;\n\t}\n\n\tpublic void setInterHandler(boolean interHandler) {\n\t\tthis.interHandler = interHandler;\n\t}\n\n\tpublic void setMaxLength(int maxLength) {\n\t\tthis.maxLength = maxLength;\n\t}\n\n\tpublic void setMinLength(int minLength) {\n\t\tthis.minLength = minLength;\n\t}\n\n\tpublic void setMobile(boolean isMobile) {\n\t\tthis.isMobile = isMobile;\n\t}\n\n\tpublic void setNotNull(boolean notNull) {\n\t\tthis.notNull = notNull;\n\t}\n\n\tpublic void setRegex(String regex) {\n\t\tthis.regex = regex;\n\t}\n\n\tpublic void setRegexTip(String regexTip) {\n\t\tthis.regexTip = regexTip;\n\t}\n\n\tpublic void setTel(boolean isTel) {\n\t\tthis.isTel = isTel;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/params/MergeEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.params;\n\nimport java.util.List;\n\n/**\n * 合并单元格使用对象\n * \n * Created by jue on 14-6-11.\n */\npublic class MergeEntity {\n\t/**\n\t * 合并开始行\n\t */\n\tprivate int startRow;\n\t/**\n\t * 合并结束行\n\t */\n\tprivate int endRow;\n\t/**\n\t * 文字\n\t */\n\tprivate String text;\n\t/**\n\t * 依赖关系文本\n\t */\n\tprivate List<String> relyList;\n\n\tpublic MergeEntity() {\n\n\t}\n\n\tpublic MergeEntity(String text, int startRow, int endRow) {\n\t\tthis.text = text;\n\t\tthis.endRow = endRow;\n\t\tthis.startRow = startRow;\n\t}\n\n\tpublic int getEndRow() {\n\t\treturn endRow;\n\t}\n\n\tpublic List<String> getRelyList() {\n\t\treturn relyList;\n\t}\n\n\tpublic int getStartRow() {\n\t\treturn startRow;\n\t}\n\n\tpublic String getText() {\n\t\treturn text;\n\t}\n\n\tpublic void setEndRow(int endRow) {\n\t\tthis.endRow = endRow;\n\t}\n\n\tpublic void setRelyList(List<String> relyList) {\n\t\tthis.relyList = relyList;\n\t}\n\n\tpublic void setStartRow(int startRow) {\n\t\tthis.startRow = startRow;\n\t}\n\n\tpublic void setText(String text) {\n\t\tthis.text = text;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/result/ExcelImportResult.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.result;\n\nimport java.util.List;\n\nimport org.apache.poi.ss.usermodel.Workbook;\n\n/**\n * 导入返回类\n * \n * @author JEECG\n * @date 2014年6月29日 下午5:12:10\n */\npublic class ExcelImportResult<T> {\n\n\t/**\n\t * 结果集\n\t */\n\tprivate List<T> list;\n\n\t/**\n\t * 是否存在校验失败\n\t */\n\tprivate boolean verfiyFail;\n\n\t/**\n\t * 数据源\n\t */\n\tprivate Workbook workbook;\n\n\tpublic ExcelImportResult() {\n\n\t}\n\n\tpublic ExcelImportResult(List<T> list, boolean verfiyFail, Workbook workbook) {\n\t\tthis.list = list;\n\t\tthis.verfiyFail = verfiyFail;\n\t\tthis.workbook = workbook;\n\t}\n\n\tpublic List<T> getList() {\n\t\treturn list;\n\t}\n\n\tpublic Workbook getWorkbook() {\n\t\treturn workbook;\n\t}\n\n\tpublic boolean isVerfiyFail() {\n\t\treturn verfiyFail;\n\t}\n\n\tpublic void setList(List<T> list) {\n\t\tthis.list = list;\n\t}\n\n\tpublic void setVerfiyFail(boolean verfiyFail) {\n\t\tthis.verfiyFail = verfiyFail;\n\t}\n\n\tpublic void setWorkbook(Workbook workbook) {\n\t\tthis.workbook = workbook;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/result/ExcelVerifyHanlderResult.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.result;\n\n/**\n * Excel导入处理返回结果\n * \n * @author JEECG\n * @date 2014年6月23日 下午11:03:29\n */\npublic class ExcelVerifyHanlderResult {\n\t/**\n\t * 是否正确\n\t */\n\tprivate boolean success;\n\t/**\n\t * 错误信息\n\t */\n\tprivate String msg;\n\n\tpublic ExcelVerifyHanlderResult() {\n\n\t}\n\n\tpublic ExcelVerifyHanlderResult(boolean success) {\n\t\tthis.success = success;\n\t}\n\n\tpublic ExcelVerifyHanlderResult(boolean success, String msg) {\n\t\tthis.success = success;\n\t\tthis.msg = msg;\n\t}\n\n\tpublic String getMsg() {\n\t\treturn msg;\n\t}\n\n\tpublic boolean isSuccess() {\n\t\treturn success;\n\t}\n\n\tpublic void setMsg(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n\tpublic void setSuccess(boolean success) {\n\t\tthis.success = success;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/sax/SaxReadCellEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.sax;\n\nimport org.jeecgframework.poi.excel.entity.enmus.CellValueType;\n\n/**\n * Cell 对象\n * \n * @author JEECG\n * @date 2014年12月29日 下午10:12:57\n */\npublic class SaxReadCellEntity {\n\t/**\n\t * 值类型\n\t */\n\tprivate CellValueType cellType;\n\t/**\n\t * 值\n\t */\n\tprivate Object value;\n\n\tpublic SaxReadCellEntity(CellValueType cellType, Object value) {\n\t\tthis.cellType = cellType;\n\t\tthis.value = value;\n\t}\n\n\tpublic CellValueType getCellType() {\n\t\treturn cellType;\n\t}\n\n\tpublic void setCellType(CellValueType cellType) {\n\t\tthis.cellType = cellType;\n\t}\n\n\tpublic Object getValue() {\n\t\treturn value;\n\t}\n\n\tpublic void setValue(Object value) {\n\t\tthis.value = value;\n\t}\n\n\t@Override\n\tpublic String toString() {\n\t\treturn \"[type=\" + cellType.toString() + \",value=\" + value + \"]\";\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/entity/vo/PoiBaseConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.entity.vo;\n\n/**\n * 基础常量 Created by jue on 14-4-21.\n */\npublic interface PoiBaseConstants {\n\t/**\n\t * 字段属性对应方法\n\t */\n\tpublic static String GET = \"get\";\n\t/**\n\t * 字段属性对应方法\n\t */\n\tpublic static String SET = \"set\";\n\t/**\n\t * 字段属性对应方法\n\t */\n\tpublic static String IS = \"is\";\n\t/**\n\t * 是否增加属性列\n\t */\n\tpublic static String IS_ADD_INDEX = \"isAddIndex\";\n\t/**\n\t * 字段属性对应convert方法\n\t */\n\tpublic static String CONVERT = \"convert\";\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/ExcelBatchExportServer.java",
    "content": "package org.jeecgframework.poi.excel.export;\nimport org.apache.poi.ss.usermodel.Drawing;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.apache.poi.xssf.streaming.SXSSFWorkbook;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants;\nimport org.jeecgframework.poi.excel.export.styler.IExcelExportStyler;\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServer;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced;\nimport org.jeecgframework.poi.handler.inter.IWriter;\nimport org.jeecgframework.poi.util.PoiExcelGraphDataUtil;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\n\nimport static  org.jeecgframework.poi.excel.ExcelExportUtil.USE_SXSSF_LIMIT;\n\n/**\n * 提供批次插入服务\n * @author liusq\n * @date 2022年1月4日\n */\npublic class ExcelBatchExportServer extends ExcelExportServer implements IWriter<Workbook> {\n\n\tprivate final static Logger LOGGER = LoggerFactory.getLogger(ExcelBatchExportServer.class);\n\n\tprivate Workbook                workbook;\n\tprivate Sheet                   sheet;\n\tprivate List<ExcelExportEntity> excelParams;\n\tprivate ExportParams entity;\n\tprivate int                     titleHeight;\n\tprivate Drawing                 patriarch;\n\tprivate short                   rowHeight;\n\tprivate int                     index;\n\n\tpublic void init(ExportParams entity, Class<?> pojoClass) {\n\t\tList<ExcelExportEntity> excelParams = createExcelExportEntityList(entity, pojoClass);\n\t\tinit(entity, excelParams);\n\t}\n\n\t/**\n\t * 初始化数据\n\t * @param entity  导出参数\n\t * @param excelParams\n\t */\n\tpublic void init(ExportParams entity, List<ExcelExportEntity> excelParams) {\n\t\tLOGGER.debug(\"ExcelBatchExportServer only support SXSSFWorkbook\");\n\t\tentity.setType(ExcelType.XSSF);\n\t\tworkbook = new SXSSFWorkbook();\n\t\tthis.entity = entity;\n\t\tthis.excelParams = excelParams;\n\t\tsuper.type = entity.getType();\n\t\tcreateSheet(workbook, entity, excelParams);\n\t\tif (entity.getMaxNum() == 0) {\n\t\t\tentity.setMaxNum(USE_SXSSF_LIMIT);\n\t\t}\n\t\tinsertDataToSheet(workbook, entity, excelParams, null, sheet);\n\t}\n\n\tpublic List<ExcelExportEntity> createExcelExportEntityList(ExportParams entity, Class<?> pojoClass) {\n\t\ttry {\n\t\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\t\tif (entity.isAddIndex()) {\n\t\t\t\texcelParams.add(indexExcelEntity(entity));\n\t\t\t}\n\t\t\t// 得到所有字段\n\t\t\tField[]     fileds   = PoiPublicUtil.getClassFields(pojoClass);\n\t\t\tExcelTarget etarget  = pojoClass.getAnnotation(ExcelTarget.class);\n\t\t\tString      targetId = etarget == null ? null : etarget.value();\n\t\t\tgetAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass,\n\t\t\t\t\tnull);\n\t\t\tsortAllParams(excelParams);\n\n\t\t\treturn excelParams;\n\t\t} catch (Exception e) {\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t}\n\t}\n\n\tpublic void createSheet(Workbook workbook, ExportParams entity, List<ExcelExportEntity> excelParams) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"Excel export start ,List<ExcelExportEntity> is {}\", excelParams);\n\t\t\tLOGGER.debug(\"Excel version is {}\",\n\t\t\t\t\tentity.getType().equals(ExcelType.HSSF) ? \"03\" : \"07\");\n\t\t}\n\t\tif (workbook == null || entity == null || excelParams == null) {\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);\n\t\t}\n\t\ttry {\n\t\t\ttry {\n\t\t\t\tsheet = workbook.createSheet(entity.getSheetName());\n\t\t\t} catch (Exception e) {\n\t\t\t\t// 重复遍历,出现了重名现象,创建非指定的名称Sheet\n\t\t\t\tsheet = workbook.createSheet();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t}\n\t}\n\n\t@Override\n\tprotected void insertDataToSheet(Workbook workbook, ExportParams entity,\n\t\t\t\t\t\t\t\t\t List<ExcelExportEntity> entityList, Collection<? extends Map<?, ?>> dataSet,\n\t\t\t\t\t\t\t\t\t Sheet sheet) {\n\t\ttry {\n\t\t\tdataHanlder = entity.getDataHanlder();\n\t\t\tif (dataHanlder != null && dataHanlder.getNeedHandlerFields() != null) {\n\t\t\t\tneedHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());\n\t\t\t}\n\t\t\t// 创建表格样式\n\t\t\tsetExcelExportStyler((IExcelExportStyler) entity.getStyle()\n\t\t\t\t\t.getConstructor(Workbook.class).newInstance(workbook));\n\t\t\tpatriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);\n\t\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\t\tif (entity.isAddIndex()) {\n\t\t\t\texcelParams.add(indexExcelEntity(entity));\n\t\t\t}\n\t\t\texcelParams.addAll(entityList);\n\t\t\tsortAllParams(excelParams);\n\t\t\tthis.index = entity.isCreateHeadRows()\n\t\t\t\t\t? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;\n\t\t\ttitleHeight = index;\n\t\t\tsetCellWith(excelParams, sheet);\n\t\t\tsetColumnHidden(excelParams, sheet);\n\t\t\trowHeight = getRowHeight(excelParams);\n\t\t\tsetCurrentIndex(1);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause());\n\t\t}\n\t}\n\n\tpublic Workbook exportBigExcel(IExcelExportServer server, Object queryParams) {\n\t\tint page = 1;\n\t\tList<Object> list = server\n\t\t\t\t.selectListForExcelExport(queryParams, page++);\n\t\twhile (list != null && list.size() > 0) {\n\t\t\twrite(list);\n\t\t\tlist = server.selectListForExcelExport(queryParams, page++);\n\t\t}\n\t\treturn close();\n\t}\n\n\t/**\n\t * 大数据导出 - 游标分页方式\n\t * 推荐用于大数据量（20万+）导出,避免深分页性能问题\n     * for [QQYUN-13964]演示系统数据量大，点击没反应\n\t * \n\t * 实现原理:\n\t * 1. 使用上一批次的最后一条记录作为查询起点\n\t * 2. 避免 LIMIT offset, size 造成的性能衰减\n\t * 3. 查询速度恒定,不会随着数据量增加而变慢\n\t * \n\t * @param server 增强的查询服务\n\t * @param queryParams 查询参数\n\t * @return Workbook\n\t */\n\tpublic <T> Workbook exportBigExcelEnhanced(IExcelExportServerEnhanced<T> server, Object queryParams) {\n\t\tint pageSize = server.getPageSize();\n\t\tT lastRecord = null;\n\t\tint totalCount = 0;\n\t\t\n\t\tList<T> list = server.selectListForExcelExport(queryParams, lastRecord, pageSize);\n\t\twhile (list != null && list.size() > 0) {\n\t\t\twrite(list);\n\t\t\ttotalCount += list.size();\n\t\t\t// 记录最后一条记录,用于下次查询\n\t\t\tlastRecord = list.get(list.size() - 1);\n\t\t\t\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"已导出 {} 条数据\", totalCount);\n\t\t\t}\n\t\t\t\n\t\t\t// 如果返回的数据少于pageSize,说明已经是最后一批了\n\t\t\tif (list.size() < pageSize) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t\n\t\t\tlist = server.selectListForExcelExport(queryParams, lastRecord, pageSize);\n\t\t}\n\t\t\n\t\tLOGGER.info(\"大数据导出完成,共导出 {} 条数据\", totalCount);\n\t\treturn close();\n\t}\n\n\t@Override\n\tpublic Workbook get() {\n\t\treturn this.workbook;\n\t}\n\n\t@Override\n\tpublic IWriter<Workbook> write(Collection data) {\n\t\tif (sheet.getLastRowNum() + data.size() > entity.getMaxNum()) {\n\t\t\tsheet = workbook.createSheet();\n\t\t\tindex = 0;\n\t\t}\n\t\tIterator<?> its = data.iterator();\n\t\twhile (its.hasNext()) {\n\t\t\tObject t = its.next();\n\t\t\ttry {\n\t\t\t\tindex += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic Workbook close() {\n\t\tif (entity.getFreezeCol() != 0) {\n\t\t\tsheet.createFreezePane(entity.getFreezeCol(), titleHeight, entity.getFreezeCol(), titleHeight);\n\t\t}\n\t\tmergeCells(sheet, excelParams, titleHeight);\n\t\t// 创建合计信息\n\t\taddStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);\n\t\treturn workbook;\n\t}\n\t/**\n\t * 添加Index列\n\t */\n\t@Override\n\tpublic ExcelExportEntity indexExcelEntity(ExportParams entity) {\n\t\tExcelExportEntity exportEntity = new ExcelExportEntity();\n\t\t//保证是第一排\n\t\texportEntity.setOrderNum(Integer.MIN_VALUE);\n\t\texportEntity.setNeedMerge(true);\n\t\texportEntity.setName(entity.getIndexName());\n\t\texportEntity.setWidth(10);\n\t\texportEntity.setFormat(PoiBaseConstants.IS_ADD_INDEX);\n\t\treturn exportEntity;\n\t}\n}"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/ExcelExportServer.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.ss.util.CellRangeAddress;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants;\nimport org.jeecgframework.poi.excel.export.base.ExcelExportBase;\nimport org.jeecgframework.poi.excel.export.styler.IExcelExportStyler;\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;\nimport org.jeecgframework.poi.util.PoiExcelGraphDataUtil;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Excel导出服务\n * \n * @author JEECG\n * @date 2014年6月17日 下午5:30:54\n */\npublic class ExcelExportServer extends ExcelExportBase {\n\n\tprivate final static Logger LOGGER = LoggerFactory.getLogger(ExcelExportServer.class);\n\n\t// 最大行数,超过自动多Sheet\n\tprivate int MAX_NUM = 60000;\n\n\tprotected int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook, List<ExcelExportEntity> excelParams) {\n\t\tint rows = 0, feildWidth = getFieldWidth(excelParams);\n\t\tif (entity.getTitle() != null) {\n\t\t\trows += createHeaderRow(entity, sheet, workbook, feildWidth);\n\t\t}\n\t\trows += createTitleRow(entity, sheet, workbook, rows, excelParams);\n\t\t//update-begin---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\t\tif (entity.isFixedTitle()) {\n\t\t\tsheet.createFreezePane(0, rows, 0, rows);\n\t\t}\n\t\t//update-begin---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\t\treturn rows;\n\t}\n\n\t/**\n\t * 创建 表头改变\n\t * \n\t * @param entity\n\t * @param sheet\n\t * @param workbook\n\t * @param feildWidth\n\t */\n\tpublic int createHeaderRow(ExportParams entity, Sheet sheet, Workbook workbook, int feildWidth) {\n\t\tRow row = sheet.createRow(0);\n\t\trow.setHeight(entity.getTitleHeight());\n\t\tcreateStringCell(row, 0, entity.getTitle(), getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);\n\t\tfor (int i = 1; i <= feildWidth; i++) {\n\t\t\tcreateStringCell(row, i, \"\", getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);\n\t\t}\n\t\t//update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\ttry {\n\t\tsheet.addMergedRegion(new CellRangeAddress(0, 0, 0, feildWidth));\n\t\t}catch (IllegalArgumentException e){\n\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\te.fillInStackTrace();\n\t\t}\n\t\t//update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\tif (entity.getSecondTitle() != null) {\n\t\t\trow = sheet.createRow(1);\n\t\t\trow.setHeight(entity.getSecondTitleHeight());\n\t\t\t//update-begin-author:liusq date:20230407 for:[issue/4342]autopoi导出带副标题的数据表，副标题缺左边框\n\t\t\tCellStyle style = getExcelExportStyler().getHeaderStyle(entity.getHeaderColor());\n\t\t\tstyle.setAlignment(HorizontalAlignment.RIGHT);\n\t\t\t//update-end-author:liusq date:20230407 for:[issue/4342]autopoi导出带副标题的数据表，副标题缺左边框\n\t\t\tcreateStringCell(row, 0, entity.getSecondTitle(), style, null);\n\t\t\tfor (int i = 1; i <= feildWidth; i++) {\n\t\t\t\tcreateStringCell(row, i, \"\", getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null);\n\t\t\t}\n\t\t\t//update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\ttry{\n\t\t\tsheet.addMergedRegion(new CellRangeAddress(1, 1, 0, feildWidth));\n\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t  e.fillInStackTrace();\n\t\t  }\n\t\t  //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\treturn 2;\n\t\t}\n\t\treturn 1;\n\t}\n\n\tpublic void createSheet(Workbook workbook, ExportParams entity, Class<?> pojoClass, Collection<?> dataSet, String[] exportFields) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"Excel export start ,class is {}\", pojoClass);\n\t\t\tLOGGER.debug(\"Excel version is {}\", entity.getType().equals(ExcelType.HSSF) ? \"03\" : \"07\");\n\t\t}\n\t\tif (workbook == null || entity == null || pojoClass == null || dataSet == null) {\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);\n\t\t}\n\t\tsuper.type = entity.getType();\n\t\tif (type.equals(ExcelType.XSSF)) {\n\t\t\tMAX_NUM = 1000000;\n\t\t}\n\t\tSheet sheet = null;\n\t\ttry {\n\t\t\tsheet = workbook.createSheet(entity.getSheetName());\n\t\t} catch (Exception e) {\n\t\t\t// 重复遍历,出现了重名现象,创建非指定的名称Sheet\n\t\t\tsheet = workbook.createSheet();\n\t\t}\n\t\ttry {\n\t\t\tdataHanlder = entity.getDataHanlder();\n\t\t\tif (dataHanlder != null) {\n\t\t\t\tString[] needHandlerFields = dataHanlder.getNeedHandlerFields();\n\t\t\t\tif(needHandlerFields!=null && needHandlerFields.length>0){\n\t\t\t\t\tneedHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());\n\t\t\t\t}\n\t\t\t}\n\t\t\t// 创建表格样式\n\t\t\tsetExcelExportStyler((IExcelExportStyler) entity.getStyle().getConstructor(Workbook.class).newInstance(workbook));\n\t\t\tDrawing patriarch = sheet.createDrawingPatriarch();\n\t\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\t\tif (entity.isAddIndex()) {\n\t\t\t\texcelParams.add(indexExcelEntity(entity));\n\t\t\t}\n\t\t\t// 得到所有字段\n\t\t\tField fileds[] = PoiPublicUtil.getClassFields(pojoClass);\n\n            //---update-begin-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n            //支持自定义导出字段\n            if (exportFields != null) {\n                List<Field> list = new ArrayList<Field>(Arrays.asList(fileds));\n                for (int i = 0; i < list.size(); i++) {\n                    if (!Arrays.asList(exportFields).contains(list.get(i).getName())) {\n                        list.remove(i);\n                        i--;\n                    }\n                }\n\n                if (list != null && list.size() > 0) {\n                    fileds = list.toArray(new Field[0]);\n                } else {\n                    fileds = null;\n                }\n            }\n            //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\n\t\t\tExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);\n\t\t\tString targetId = etarget == null ? null : etarget.value();\n\t\t\tgetAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass, null);\n\t\t\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t\trebuildDynamicColumns(dataSet, excelParams);\n\t\t\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t\t//update-begin-author:taoyan date:20200304 for:在此方法循环内设置一下图片磁盘目录，便于导出\n\t\t\treConfigExcelExportParams(excelParams,entity);\n\t\t\t//update-end-author:taoyan date:20200304 for:在此方法循环内设置一下图片磁盘目录，便于导出\n\t\t\tint index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;\n\t\t\tint titleHeight = index;\n\t\t\tsetCellWith(excelParams, sheet);\n\t\t\t//update-begin-author:liusq date:20210723 for:设置隐藏列\n\t\t\tsetColumnHidden(excelParams, sheet);\n\t\t\t//update-end-author:liusq date:20210723 for:设置隐藏列\n\n\t\t\tshort rowHeight = getRowHeight(excelParams);\n\t\t\tsetCurrentIndex(1);\n\t\t\tIterator<?> its = dataSet.iterator();\n\t\t\tList<Object> tempList = new ArrayList<Object>();\n\t\t\twhile (its.hasNext()) {\n\t\t\t\tObject t = its.next();\n\t\t\t\tindex += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight);\n\t\t\t\ttempList.add(t);\n\t\t\t\tif (index >= MAX_NUM)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmergeCells(sheet, excelParams, titleHeight);\n\n\t\t\tif (entity.getFreezeCol() != 0) {\n\t\t\t\tsheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);\n\t\t\t}\n\n\t\t\tits = dataSet.iterator();\n\t\t\tfor (int i = 0, le = tempList.size(); i < le; i++) {\n\t\t\t\tits.next();\n\t\t\t\tits.remove();\n\t\t\t}\n\t\t\t// 创建合计信息\n\t\t\taddStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);\n\n\t\t\t// 发现还有剩余list 继续循环创建Sheet\n\t\t\tif (dataSet.size() > 0) {\n\t\t\t\tcreateSheet(workbook, entity, pojoClass, dataSet,exportFields);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t}\n\t}\n\n\tpublic void createSheetForMap(Workbook workbook, ExportParams entity, List<ExcelExportEntity> entityList, Collection<? extends Map<?, ?>> dataSet) {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"Excel version is {}\", entity.getType().equals(ExcelType.HSSF) ? \"03\" : \"07\");\n\t\t}\n\t\tif (workbook == null || entity == null || entityList == null || dataSet == null) {\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);\n\t\t}\n\t\tsuper.type = entity.getType();\n\t\tif (type.equals(ExcelType.XSSF)) {\n\t\t\tMAX_NUM = 1000000;\n\t\t}\n\t\tSheet sheet = null;\n\t\ttry {\n\t\t\tsheet = workbook.createSheet(entity.getSheetName());\n\t\t} catch (Exception e) {\n\t\t\t// 重复遍历,出现了重名现象,创建非指定的名称Sheet\n\t\t\tsheet = workbook.createSheet();\n\t\t}\n\t\ttry {\n\t\t\tdataHanlder = entity.getDataHanlder();\n\t\t\tif (dataHanlder != null) {\n\t\t\t\tneedHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());\n\t\t\t}\n\t\t\t// 创建表格样式\n\t\t\tsetExcelExportStyler((IExcelExportStyler) entity.getStyle().getConstructor(Workbook.class).newInstance(workbook));\n\t\t\tDrawing patriarch = sheet.createDrawingPatriarch();\n\t\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\t\tif (entity.isAddIndex()) {\n\t\t\t\texcelParams.add(indexExcelEntity(entity));\n\t\t\t}\n\t\t\texcelParams.addAll(entityList);\n\t\t\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t\trebuildDynamicColumns(dataSet, excelParams);\n\t\t\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t\tsortAllParams(excelParams);\n\t\t\tint index = entity.isCreateHeadRows() ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;\n\t\t\tint titleHeight = index;\n\t\t\tsetCellWith(excelParams, sheet);\n\t\t\t//update-begin-author:liusq date:20210723 for:设置隐藏列\n\t\t\tsetColumnHidden(excelParams, sheet);\n\t\t\t//update-end-author:liusq date:20210723 for:设置隐藏列\n\t\t\tshort rowHeight = getRowHeight(excelParams);\n\t\t\tsetCurrentIndex(1);\n\t\t\tIterator<?> its = dataSet.iterator();\n\t\t\tList<Object> tempList = new ArrayList<Object>();\n\t\t\twhile (its.hasNext()) {\n\t\t\t\tObject t = its.next();\n\t\t\t\tindex += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight);\n\t\t\t\ttempList.add(t);\n\t\t\t\tif (index >= MAX_NUM)\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (entity.getFreezeCol() != 0) {\n\t\t\t\tsheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);\n\t\t\t}\n\n\t\t\tmergeCells(sheet, excelParams, titleHeight);\n\n\t\t\tits = dataSet.iterator();\n\t\t\tfor (int i = 0, le = tempList.size(); i < le; i++) {\n\t\t\t\tits.next();\n\t\t\t\tits.remove();\n\t\t\t}\n\t\t\t// 发现还有剩余list 继续循环创建Sheet\n\t\t\tif (dataSet.size() > 0) {\n\t\t\t\tcreateSheetForMap(workbook, entity, entityList, dataSet);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\te.printStackTrace(); // 添加打印完整堆栈信息\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t}\n\t}\n\n\t/**\n\t * 创建表头\n\t * \n\t * @param title\n\t * @param index\n\t */\n\tprivate int createTitleRow(ExportParams title, Sheet sheet, Workbook workbook, int index, List<ExcelExportEntity> excelParams) {\n\t\tRow row = sheet.createRow(index);\n\t\tint rows = getRowNums(excelParams);\n\t\trow.setHeight((short) 450);\n\t\tRow listRow = null;\n\t\tif (rows == 2) {\n\t\t\tlistRow = sheet.createRow(index + 1);\n\t\t\tlistRow.setHeight((short) 450);\n\t\t}\n\t\tint cellIndex = 0;\n\t\tCellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor());\n\t\tfor (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) {\n\t\t\tExcelExportEntity entity = excelParams.get(i);\n\t\t\t//update-begin-author:taoyan date:20200319 for:建议autoPoi升级，优化数据返回List Map格式下的复合表头导出excel的体验 #873\n\t\t\tif(entity.isColspan()){\n\t\t\t\tList<String> subList = entity.getSubColumnList();\n\t\t\t\tif(subList==null || subList.size()==0){\n\t\t\t\t\tcontinue;\n\t\t\t\t}else{\n\t\t\t\t\tentity.initSubExportEntity(excelParams);\n\t\t\t\t}\n\t\t\t}\n\t\t\t//update-end-author:taoyan date:20200319 for:建议autoPoi升级，优化数据返回List Map格式下的复合表头导出excel的体验 #873\n\t\t\tif (StringUtils.isNotBlank(entity.getName())) {\n\t\t\t\tcreateStringCell(row, cellIndex, entity.getName(), titleStyle, entity);\n\t\t\t}\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tList<ExcelExportEntity> sTitel = entity.getList();\n\t\t\t\t //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\tif (StringUtils.isNotBlank(entity.getName())) {\n\t\t\t\t\ttry {\n\t\t\t\t\tsheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex, cellIndex + sTitel.size() - 1));\n\t\t\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t\t\t\te.fillInStackTrace();\n\t\t\t\t\t}\n\t\t\t\t\t//update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\t}\n\t\t\t\tfor (int j = 0, size = sTitel.size(); j < size; j++) {\n\t\t\t\t\tcreateStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(), titleStyle, entity);\n\t\t\t\t\tcellIndex++;\n\t\t\t\t}\n\t\t\t\tcellIndex--;\n\t\t\t} else if (rows == 2) {\n\t\t\t\tcreateStringCell(listRow, cellIndex, \"\", titleStyle, entity);\n\t\t\t\t//update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\ttry{\n\t\t\t\tsheet.addMergedRegion(new CellRangeAddress(index, index + 1, cellIndex, cellIndex));\n\t\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t\t\te.fillInStackTrace();\n\t\t\t\t}\n\t\t\t\t//update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t}\n\t\t\tcellIndex++;\n\t\t}\n\t\treturn rows;\n\n\t}\n\n\t/**\n\t * 判断表头是只有一行还是两行\n\t * \n\t * @param excelParams\n\t * @return\n\t */\n\tprivate int getRowNums(List<ExcelExportEntity> excelParams) {\n\t\tfor (int i = 0; i < excelParams.size(); i++) {\n\t\t\t//update-begin-author:taoyan date:20200319 for:建议autoPoi升级，优化数据返回List Map格式下的复合表头导出excel的体验 #873\n\t\t\tExcelExportEntity temp = excelParams.get(i);\n\t\t\tif ((temp.getList() != null || temp.isColspan()) && StringUtils.isNotBlank(temp.getName())) {\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\t//update-end-author:taoyan date:20200319 for:建议autoPoi升级，优化数据返回List Map格式下的复合表头导出excel的体验 #873\n\t\t}\n\t\treturn 1;\n\t}\n\n\tprotected ExcelExportEntity indexExcelEntity(ExportParams entity) {\n\t\tExcelExportEntity exportEntity = new ExcelExportEntity();\n\t\texportEntity.setOrderNum(0);\n\t\texportEntity.setName(entity.getIndexName());\n\t\texportEntity.setWidth(10);\n\t\texportEntity.setFormat(PoiBaseConstants.IS_ADD_INDEX);\n\t\treturn exportEntity;\n\t}\n   //update-begin---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\t/**\n\t * 添加数据到sheet\n\t * @param workbook\n\t * @param entity 导出参数\n\t * @param entityList\n\t * @param dataSet 导出数据\n\t * @param sheet\n\t * @date 2022年1月4号\n\t */\n\tprotected void insertDataToSheet(Workbook workbook, ExportParams entity,\n\t\t\t\t\t\t\t\t\t List<ExcelExportEntity> entityList,Collection<? extends Map<?, ?>> dataSet,\n\t\t\t\t\t\t\t\t\t Sheet sheet) {\n\t\ttry {\n\t\t\tdataHanlder = entity.getDataHanlder();\n\t\t\tif (dataHanlder != null && dataHanlder.getNeedHandlerFields() != null) {\n\t\t\t\tneedHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());\n\t\t\t}\n\t\t\t// 创建表格样式\n\t\t\tsetExcelExportStyler((IExcelExportStyler) entity.getStyle()\n\t\t\t\t\t.getConstructor(Workbook.class).newInstance(workbook));\n\t\t\tDrawing                 patriarch   = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet);\n\t\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\t\tif (entity.isAddIndex()) {\n\t\t\t\texcelParams.add(indexExcelEntity(entity));\n\t\t\t}\n\t\t\texcelParams.addAll(entityList);\n\t\t\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t\trebuildDynamicColumns(dataSet, excelParams);\n\t\t\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t\tsortAllParams(excelParams);\n\t\t\tint index = entity.isCreateHeadRows()\n\t\t\t\t\t? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0;\n\t\t\tint titleHeight = index;\n\t\t\tsetCellWith(excelParams, sheet);\n\t\t\tsetColumnHidden(excelParams, sheet);\n\t\t\tshort rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams);\n\t\t\tsetCurrentIndex(1);\n\t\t\tIterator<?>  its      = dataSet.iterator();\n\t\t\tList<Object> tempList = new ArrayList<Object>();\n\t\t\twhile (its.hasNext()) {\n\t\t\t\tObject t = its.next();\n\t\t\t\tindex += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0];\n\t\t\t\ttempList.add(t);\n\t\t\t\tif (index >= MAX_NUM) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (entity.getFreezeCol() != 0) {\n\t\t\t\tsheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0);\n\t\t\t}\n\n\t\t\tmergeCells(sheet, excelParams, titleHeight);\n\n\t\t\tits = dataSet.iterator();\n\t\t\tfor (int i = 0, le = tempList.size(); i < le; i++) {\n\t\t\t\tits.next();\n\t\t\t\tits.remove();\n\t\t\t}\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\"List data more than max ,data size is {}\",\n\t\t\t\t\t\tdataSet.size());\n\t\t\t}\n\t\t\t// 发现还有剩余list 继续循环创建Sheet\n\t\t\tif (dataSet.size() > 0) {\n\t\t\t\tcreateSheetForMap(workbook, entity, entityList, dataSet);\n\t\t\t} else {\n\t\t\t\t// 创建合计信息\n\t\t\t\taddStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet);\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\te.printStackTrace(); // 添加打印完整堆栈信息\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t}\n\t}\n   //update-end---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExcelExportBase.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.base;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.commons.lang3.builder.ReflectionToStringBuilder;\nimport org.apache.poi.hssf.usermodel.HSSFClientAnchor;\nimport org.apache.poi.hssf.usermodel.HSSFRichTextString;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.ss.util.CellRangeAddress;\nimport org.apache.poi.xssf.usermodel.XSSFClientAnchor;\nimport org.apache.poi.xssf.usermodel.XSSFRichTextString;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants;\nimport org.jeecgframework.poi.excel.export.styler.IExcelExportStyler;\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;\nimport org.jeecgframework.poi.util.MyX509TrustManager;\nimport org.jeecgframework.poi.util.PoiMergeCellUtil;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.imageio.ImageIO;\nimport javax.net.ssl.HttpsURLConnection;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.TrustManager;\nimport java.awt.image.BufferedImage;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.security.SecureRandom;\nimport java.text.DecimalFormat;\nimport java.util.*;\n\n/**\n * 提供POI基础操作服务\n * \n * @author JEECG\n * @date 2014年6月17日 下午6:15:13\n */\npublic abstract class ExcelExportBase extends ExportBase {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportBase.class);\n\n\tprivate int currentIndex = 0;\n\n\tprotected ExcelType type = ExcelType.HSSF;\n\n\tprivate Map<Integer, Double> statistics = new HashMap<Integer, Double>();\n\n\tprivate static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat(\"######0.00\");\n\n\t/**\n\t * 常规格式的内置格式标识符\n\t *  for [issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248\n\t */\n\tprivate static final short GENERAL_FORMAT = (short)BuiltinFormats.getBuiltinFormat(\"General\");\n\n\t/**\n\t * 单元格样式缓存\n\t * for [issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248\n\t */\n\tprivate final Map<String, CellStyle> cellStyleMap = new HashMap<>();\n\n\t//update-begin-author:liusq---date:20220527--for: 修改成protected，列循环时继承类需要用到 ---\n\tprotected IExcelExportStyler excelExportStyler;\n    //update-end-author:liusq---date:20220527--for: 修改成protected，列循环时继承类需要用到 ---\n\n\n\t/**\n\t * 创建 最主要的 Cells\n\t * \n\t * @param styles\n\t * @param rowHeight\n\t * @throws Exception\n\t */\n\tpublic int createCells(Drawing patriarch, int index, Object t, List<ExcelExportEntity> excelParams, Sheet sheet, Workbook workbook, short rowHeight) throws Exception {\n\t\tExcelExportEntity entity;\n\t\tRow row = sheet.createRow(index);\n\t\tDataFormat df = workbook.createDataFormat();\n\t\trow.setHeight(rowHeight);\n\t\tint maxHeight = 1, cellNum = 0;\n\t\tint indexKey = createIndexCell(row, index, excelParams.get(0));\n\t\tcellNum += indexKey;\n\t\tfor (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\t//update-begin-author:taoyan date:20200319 for:建议autoPoi升级，优化数据返回List Map格式下的复合表头导出excel的体验 #873\n\t\t\tif(entity.isSubColumn()){\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif(entity.isMergeColumn()){\n\t\t\t\tMap<String,Object> subColumnMap = new HashMap<>();\n\t\t\t\tList<String> mapKeys = entity.getSubColumnList();\n\t\t\t\tfor (String subKey : mapKeys) {\n\t\t\t\t\tObject subKeyValue = null;\n\t\t\t\t\tif (t instanceof Map) {\n\t\t\t\t\t\tsubKeyValue = ((Map<?, ?>) t).get(subKey);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tsubKeyValue = PoiPublicUtil.getParamsValue(subKey,t);\n\t\t\t\t\t}\n\t\t\t\t\tsubColumnMap.put(subKey,subKeyValue);\n\t\t\t\t}\n\t\t\t\tcreateListCells(patriarch, index, cellNum, subColumnMap, entity.getList(), sheet, workbook);\n\t\t\t\tcellNum += entity.getSubColumnList().size();\n\t\t\t//update-end-author:taoyan date:20200319 for:建议autoPoi升级，优化数据返回List Map格式下的复合表头导出excel的体验 #873\n\t\t\t} else if (entity.getList() != null) {\n\t\t\t\tCollection<?> list = getListCellValue(entity, t);\n\t\t\t\tint listC = 0;\n\t\t\t\tfor (Object obj : list) {\n\t\t\t\t\tcreateListCells(patriarch, index + listC, cellNum, obj, entity.getList(), sheet, workbook);\n\t\t\t\t\tlistC++;\n\t\t\t\t}\n\t\t\t\tcellNum += entity.getList().size();\n\t\t\t\tif (list != null && list.size() > maxHeight) {\n\t\t\t\t\tmaxHeight = list.size();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tObject value = getCellValue(entity, t);\n\t\t\t\t//update-begin--Author:xuelin  Date:20171018 for：TASK #2372 【excel】AutoPoi 导出类型，type增加数字类型--------------------\n\t\t\t\tif (entity.getType() == 1) {\n\t\t\t\t\tcreateStringCell(row, cellNum++, value == null ? \"\" : value.toString(), index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), entity);\n\t\t\t\t} else if (entity.getType() == 4){\n\t\t\t\t\tcreateNumericCell(row, cellNum++, value == null ? \"\" : value.toString(), getNumberCellStyle(index, df, entity), entity);\n\t\t\t\t} else if (entity.getType() == 0) {\n\t\t\t\t\t//update-begin---author:chenrui ---date:20250604  for：[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------\n\t\t\t\t\tcreateStringCell(row, cellNum++, value == null ? \"\" : value.toString(), getGeneralCellStyle(index, workbook,entity), entity);\n\t\t\t\t\t//update-end---author:chenrui ---date:20250604  for：[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------\n\t\t\t\t} else {\n\t\t\t\t\tcreateImageCell(patriarch, entity, row, cellNum++, value == null ? \"\" : value.toString(), t);\n\t\t\t\t}\n\t\t\t\t//update-end--Author:xuelin  Date:20171018 for：TASK #2372 【excel】AutoPoi 导出类型，type增加数字类型--------------------\n\n\t\t\t\t//update-begin-author:liusq---date:20220728--for:[issues/I5I840] @Excel注解中不支持超链接，但文档中支持 ---\n\t\t\t\tif (entity.isHyperlink()) {\n\t\t\t\t\trow.getCell(cellNum - 1)\n\t\t\t\t\t\t\t.setHyperlink(dataHanlder.getHyperlink(\n\t\t\t\t\t\t\t\t\trow.getSheet().getWorkbook().getCreationHelper(), t,\n\t\t\t\t\t\t\t\t\tentity.getName(), value));\n\t\t\t\t}\n               //update-end-author:liusq---date:20220728--for:[issues/I5I840] @Excel注解中不支持超链接，但文档中支持 ---\n\t\t\t}\n\t\t}\n\t\t// 合并需要合并的单元格\n\t\tcellNum = 0;\n\t\tfor (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tcellNum += entity.getList().size();\n\t\t\t} else if (entity.isNeedMerge()) {\n\t\t\t\tfor (int i = index + 1; i < index + maxHeight; i++) {\n\t\t\t\t\tsheet.getRow(i).createCell(cellNum);\n\t\t\t\t\tsheet.getRow(i).getCell(cellNum).setCellStyle(getStyles(false, entity));\n\t\t\t\t}\n\t\t\t\t//update-begin-author:wangshuai date:20201116 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\ttry {\n\t\t\t\t\tif (maxHeight > 1) {\n\t\t\t\t\t\tsheet.addMergedRegion(new CellRangeAddress(index, index + maxHeight - 1, cellNum, cellNum));\n\t\t\t\t\t}\n\t\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t\t\te.fillInStackTrace();\n\t\t\t\t}\n\t\t\t\t//update-end-author:wangshuai date:20201116 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\tcellNum++;\n\t\t\t}\n\t\t}\n\t\treturn maxHeight;\n\n\t}\n\n\t/**\n\t * 获取数值单元格样式\n\t * @param index\n\t * @param df\n\t * @param entity\n\t * @return\n\t */\n\tprivate CellStyle getNumberCellStyle(int index,DataFormat df, ExcelExportEntity entity) {\n       //update-begin-author:liusq---date:2023-12-07--for: [issues/5538]导出表格设置了数字格式导出之后仍然是文本格式，并且无法进行计算\n\t\tCellStyle cellStyle = index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity);\n\t\tString numFormat = StringUtils.isNotBlank(entity.getNumFormat())? entity.getNumFormat():\"0.00_ \";\n\t\tcellStyle.setDataFormat(df.getFormat(numFormat));\n\t\treturn cellStyle;\n\t\t//update-end-author:liusq---date:2023-12-07--for:[issues/5538]导出表格设置了数字格式导出之后仍然是文本格式，并且无法进行计算\n\t}\n\n\t/**\n\t * 获取常规单元格样式\n\t * for [issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248\n\t * @param index\n\t * @param workbook\n\t * @param entity\n\t * @return\n\t * @author chenrui\n\t * @date 2025/6/4 17:40\n\t */\n\tprivate CellStyle getGeneralCellStyle(int index, Workbook workbook, ExcelExportEntity entity) {\n\t\tString cellStyleKey = index % 2 == 0 ? \"twoGeneral\" : \"oneGeneral\";\n\t\tif (this.cellStyleMap.containsKey(cellStyleKey)) {\n\t\t\treturn this.cellStyleMap.get(cellStyleKey);\n\t\t}\n\t\tCellStyle cellStyle = index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity);\n\t\tCellStyle newStyle = workbook.createCellStyle();\n\t\tnewStyle.cloneStyleFrom(cellStyle);\n\t\tnewStyle.setDataFormat(GENERAL_FORMAT);\n\t\tthis.cellStyleMap.put(cellStyleKey, newStyle);\n\t\treturn newStyle;\n\t}\n\n\t/**\n\t * 通过https地址获取图片数据\n\t * @param imagePath\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate byte[] getImageDataByHttps(String imagePath) throws Exception {\n\t\tSSLContext sslcontext = SSLContext.getInstance(\"SSL\",\"SunJSSE\");\n\t\tsslcontext.init(null, new TrustManager[]{new MyX509TrustManager()}, new SecureRandom());\n\t\tURL url = new URL(imagePath);\n\t\tHttpsURLConnection conn = (HttpsURLConnection) url.openConnection();\n\t\tconn.setSSLSocketFactory(sslcontext.getSocketFactory());\n\t\tconn.setRequestMethod(\"GET\");\n\t\tconn.setConnectTimeout(5 * 1000);\n\t\tInputStream inStream = conn.getInputStream();\n\t\tbyte[] value = readInputStream(inStream);\n\t\treturn value;\n\t}\n\n\t/**\n\t * 通过http地址获取图片数据\n\t * @param imagePath\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate byte[] getImageDataByHttp(String imagePath) throws Exception {\n\t\tURL url = new URL(imagePath);\n\t\tHttpURLConnection conn = (HttpURLConnection) url.openConnection();\n\t\tconn.setRequestMethod(\"GET\");\n\t\tconn.setConnectTimeout(5 * 1000);\n\t\tInputStream inStream = conn.getInputStream();\n\t\tbyte[] value = readInputStream(inStream);\n\t\treturn value;\n\t}\n\n\t/**\n\t * 图片类型的Cell\n\t * \n\t * @param patriarch\n\t * @param entity\n\t * @param row\n\t * @param i\n\t * @param imagePath\n\t * @param obj\n\t * @throws Exception\n\t */\n\tpublic void createImageCell(Drawing patriarch, ExcelExportEntity entity, Row row, int i, String imagePath, Object obj) throws Exception {\n\t\trow.setHeight((short) (50 * entity.getHeight()));\n\t\trow.createCell(i);\n\t\tClientAnchor anchor;\n\t\tif (type.equals(ExcelType.HSSF)) {\n\t\t\tanchor = new HSSFClientAnchor(0, 0, 0, 0, (short) i, row.getRowNum(), (short) (i + 1), row.getRowNum() + 1);\n\t\t} else {\n\t\t\tanchor = new XSSFClientAnchor(0, 0, 0, 0, (short) i, row.getRowNum(), (short) (i + 1), row.getRowNum() + 1);\n\t\t}\n\n\t\tif (StringUtils.isEmpty(imagePath)) {\n\t\t\treturn;\n\t\t}\n\n\t\t//update-beign-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159\n\t\tint imageType = entity.getExportImageType();\n\t\tbyte[] value = null;\n\t\tif(imageType == 2){\n\t\t\t//原来逻辑 2\n\t\t\tvalue = (byte[]) (entity.getMethods() != null ? getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj, new Object[] {}));\n\t\t} else if(imageType==4 || imagePath.startsWith(\"http\")){\n\t\t\t//新增逻辑 网络图片4\n\t\t\ttry {\n\t\t\t\tif (imagePath.indexOf(\",\") != -1) {\n\t\t\t\t\tif(imagePath.startsWith(\",\")){\n\t\t\t\t\t\timagePath = imagePath.substring(1);\n\t\t\t\t\t}\n\t\t\t\t\tString[] images = imagePath.split(\",\");\n\t\t\t\t\timagePath = images[0];\n\t\t\t\t}\n\t\t\t\tif(imagePath.startsWith(\"https\")){\n\t\t\t\t\tvalue = getImageDataByHttps(imagePath);\n\t\t\t\t}else{\n\t\t\t\t\tvalue = getImageDataByHttp(imagePath);\n\t\t\t\t}\n\t\t\t} catch (Exception exception) {\n\t\t\t\tLOGGER.warn(exception.getMessage());\n\t\t\t\t//exception.printStackTrace();\n\t\t\t}\n\t\t} else {\n\t\t\tByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();\n\t\t\tBufferedImage bufferImg;\n\t\t\tString path = null;\n\t\t\tif(imageType == 1){\n\t\t\t\t//原来逻辑 1\n\t\t\t\tpath = PoiPublicUtil.getWebRootPath(imagePath);\n\t\t\t\tLOGGER.debug(\"--- createImageCell getWebRootPath ----filePath--- \"+ path);\n\t\t\t\tpath = path.replace(\"WEB-INF/classes/\", \"\");\n\t\t\t\tpath = path.replace(\"file:/\", \"\");\n\t\t\t}else if(imageType==3){\n\t\t\t\t//新增逻辑 本地图片3\n\t\t\t\t//begin-------author：liusq---data：2021-01-27----for：本地图片ImageBasePath为空报错的问题\n\t\t\t\tif(StringUtils.isNotBlank(entity.getImageBasePath())){\n\t\t\t\t\tif(!entity.getImageBasePath().endsWith(File.separator) && !imagePath.startsWith(File.separator)){\n\t\t\t\t\t\tpath = entity.getImageBasePath()+File.separator+imagePath;\n\t\t\t\t\t}else{\n\t\t\t\t\t\tpath = entity.getImageBasePath()+imagePath;\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tpath = imagePath;\n\t\t\t\t}\n\t\t\t\t//end-------author：liusq---data：2021-01-27----for：本地图片ImageBasePath为空报错的问题\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tbufferImg = ImageIO.read(new File(path));\n\t\t\t\t//update-begin-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR\n\t\t\t\tImageIO.write(bufferImg, imagePath.substring(imagePath.lastIndexOf(\".\") + 1, imagePath.length()), byteArrayOut);\n\t\t\t\t//update-end-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR\n\t\t\t\tvalue = byteArrayOut.toByteArray();\n\t\t\t} catch (Exception e) {\n\t\t\t\tLOGGER.error(e.getMessage());\n\t\t\t}\n\t\t}\n\t\tif (value != null) {\n\t\t\tpatriarch.createPicture(anchor, row.getSheet().getWorkbook().addPicture(value, getImageType(value)));\n\t\t}\n\t\t//update-end-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159\n\n\n\t}\n\n\t/**\n\t * inStream读取到字节数组\n\t * @param inStream\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate byte[] readInputStream(InputStream inStream) throws Exception {\n\t\tif(inStream==null){\n\t\t\treturn null;\n\t\t}\n\t\tByteArrayOutputStream outStream = new ByteArrayOutputStream();\n\t\tbyte[] buffer = new byte[1024];\n\t\tint len = 0;\n\t\t//每次读取的字符串长度，如果为-1，代表全部读取完毕\n\t\twhile ((len = inStream.read(buffer)) != -1) {\n\t\t\toutStream.write(buffer, 0, len);\n\t\t}\n\t\tinStream.close();\n\t\treturn outStream.toByteArray();\n\t}\n\n\tprivate int createIndexCell(Row row, int index, ExcelExportEntity excelExportEntity) {\n\t\tif (excelExportEntity.getName().equals(\"序号\") && PoiBaseConstants.IS_ADD_INDEX.equals(excelExportEntity.getFormat())) {\n\t\t\tcreateStringCell(row, 0, currentIndex + \"\", index % 2 == 0 ? getStyles(false, null) : getStyles(true, null), null);\n\t\t\tcurrentIndex = currentIndex + 1;\n\t\t\treturn 1;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * 创建List之后的各个Cells\n\t * @param patriarch\n\t * @param index\n\t * @param cellNum\n\t * @param obj\n\t * @param excelParams\n\t * @param sheet\n\t * @param workbook\n\t * @throws Exception\n\t */\n\tpublic void createListCells(Drawing patriarch, int index, int cellNum, Object obj, List<ExcelExportEntity> excelParams, Sheet sheet, Workbook workbook) throws Exception {\n\t\tExcelExportEntity entity;\n\t\tRow row;\n\t\tDataFormat df = workbook.createDataFormat();\n\t\tif (sheet.getRow(index) == null) {\n\t\t\trow = sheet.createRow(index);\n\t\t\trow.setHeight(getRowHeight(excelParams));\n\t\t} else {\n\t\t\trow = sheet.getRow(index);\n\t\t}\n\t\tfor (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\tObject value = getCellValue(entity, obj);\n\t\t\t//update-begin--Author:xuelin  Date:20171018 for：TASK #2372 【excel】AutoPoi 导出类型，type增加数字类型--------------------\n\t\t\tif (entity.getType() == 1) {\n\t\t\t\tcreateStringCell(row, cellNum++, value == null ? \"\" : value.toString(), row.getRowNum() % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), entity);\n\t\t\t\t//update-begin-author:liusq---date:20220728--for: 新增isHyperlink属性 ---\n\t\t\t\tif (entity.isHyperlink()) {\n\t\t\t\t\trow.getCell(cellNum - 1)\n\t\t\t\t\t\t\t.setHyperlink(dataHanlder.getHyperlink(\n\t\t\t\t\t\t\t\t\trow.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(),\n\t\t\t\t\t\t\t\t\tvalue));\n\t\t\t\t}\n\t\t\t\t//update-end-author:liusq---date:20220728--for: 新增isHyperlink属性 ---\n\t\t\t} else if (entity.getType() == 4){\n\t\t\t\tcreateNumericCell(row, cellNum++, value == null ? \"\" : value.toString(), getNumberCellStyle(index, df, entity), entity);\n\t\t\t\t//update-begin-author:liusq---date:20220728--for: 新增isHyperlink属性 ---\n\t\t\t\tif (entity.isHyperlink()) {\n\t\t\t\t\trow.getCell(cellNum - 1)\n\t\t\t\t\t\t\t.setHyperlink(dataHanlder.getHyperlink(\n\t\t\t\t\t\t\t\t\trow.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(),\n\t\t\t\t\t\t\t\t\tvalue));\n\t\t\t\t}\n\t\t\t\t//update-end-author:liusq---date:20220728--for: 新增isHyperlink属性 ---\n\t\t\t} else if (entity.getType() == 0) {\n\t\t\t\t//update-begin---author:chenrui ---date:20250604  for：[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------\n\t\t\t\tcreateStringCell(row, cellNum++, value == null ? \"\" : value.toString(), getGeneralCellStyle(index, workbook, entity), entity);\n\t\t\t\tif (entity.isHyperlink()) {\n\t\t\t\t\trow.getCell(cellNum - 1)\n\t\t\t\t\t\t\t.setHyperlink(dataHanlder.getHyperlink(\n\t\t\t\t\t\t\t\t\trow.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(),\n\t\t\t\t\t\t\t\t\tvalue));\n\t\t\t\t}\n\t\t\t\t//update-end---author:chenrui ---date:20250604  for：[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------\n\t\t\t} else{\n\t\t\t\tcreateImageCell(patriarch, entity, row, cellNum++, value == null ? \"\" : value.toString(), obj);\n\t\t\t}\n\t\t\t//update-end--Author:xuelin  Date:20171018 for：TASK #2372 【excel】AutoPoi 导出类型，type增加数字类型--------------------\n\t\t}\n\t}\n\n\t//update-begin--Author:xuelin  Date:20171018 for：TASK #2372 【excel】AutoPoi 导出类型，type增加数字类型--------------------\n\tpublic void createNumericCell (Row row, int index, String text, CellStyle style, ExcelExportEntity entity) {\n\t\tCell cell = row.createCell(index);\n\t\tif (style != null) {\n\t\t\tcell.setCellStyle(style);\n\t\t}\n\t\tif(StringUtils.isEmpty(text)){\n\t\t\tcell.setCellValue(\"\");\n\t\t\tcell.setCellType(CellType.BLANK);\n\t\t}else{\n\t\t\tcell.setCellValue(Double.parseDouble(text));\n\t\t\tcell.setCellType(CellType.NUMERIC);\n\t\t}\n\t\taddStatisticsData(index, text, entity);\n\t}\n\t\n\t/**\n\t * 创建文本类型的Cell\n\t * \n\t * @param row\n\t * @param index\n\t * @param text\n\t * @param style\n\t * @param entity\n\t */\n\tpublic void createStringCell(Row row, int index, String text, CellStyle style, ExcelExportEntity entity) {\n\t\tCell cell = row.createCell(index);\n\t\tif (style != null && style.getDataFormat() > 0 && style.getDataFormat() < 12) {\n\t\t\tcell.setCellValue(Double.parseDouble(text));\n\t\t\tcell.setCellType(CellType.NUMERIC);\n\t\t}else{\n\t\t\tRichTextString Rtext;\n\t\t\tif (type.equals(ExcelType.HSSF)) {\n\t\t\t\tRtext = new HSSFRichTextString(text);\n\t\t\t} else {\n\t\t\t\tRtext = new XSSFRichTextString(text);\n\t\t\t}\n\t\t\tcell.setCellValue(Rtext);\n\t\t}\n\t\tif (style != null) {\n\t\t\tcell.setCellStyle(style);\n\t\t}\n\t\taddStatisticsData(index, text, entity);\n\t}\n\n\t/**\n\t * 设置字段下划线\n\t * @param row\n\t * @param index\n\t * @param text\n\t * @param style\n\t * @param entity\n\t * @param workbook\n\t */\n\t/*public void createStringCell(Row row, int index, String text, CellStyle style, ExcelExportEntity entity, Workbook workbook) {\n\t\tCell cell = row.createCell(index);\n\t\tif (style != null && style.getDataFormat() > 0 && style.getDataFormat() < 12) {\n\t\t\tcell.setCellValue(Double.parseDouble(text));\n\t\t\tcell.setCellType(CellType.NUMERIC);\n\t\t}else{\n\t\t\tRichTextString Rtext;\n\t\t\tif (type.equals(ExcelType.HSSF)) {\n\t\t\t\tRtext = new HSSFRichTextString(text);\n\t\t\t} else {\n\t\t\t\tRtext = new XSSFRichTextString(text);\n\t\t\t}\n\t\t\tcell.setCellValue(Rtext);\n\t\t}\n\t\tif (style != null) {\n\t\t\tFont font = workbook.createFont();\n\t\t\tfont.setUnderline(Font.U_SINGLE);\n\t\t\tstyle.setFont(font);\n\t\t\tcell.setCellStyle(style);\n\t\t}\n\t\taddStatisticsData(index, text, entity);\n\t}*/\n\t//update-end--Author:xuelin  Date:20171018 for：TASK #2372 【excel】AutoPoi 导出类型，type增加数字类型----------------------\n\t\n\t/**\n\t * 创建统计行\n\t * \n\t * @param styles\n\t * @param sheet\n\t */\n\tpublic void addStatisticsRow(CellStyle styles, Sheet sheet) {\n\t\tif (statistics.size() > 0) {\n\t\t\tRow row = sheet.createRow(sheet.getLastRowNum() + 1);\n\t\t\tSet<Integer> keys = statistics.keySet();\n\t\t\tcreateStringCell(row, 0, \"合计\", styles, null);\n\t\t\tfor (Integer key : keys) {\n\t\t\t\tcreateStringCell(row, key, DOUBLE_FORMAT.format(statistics.get(key)), styles, null);\n\t\t\t}\n\t\t\tstatistics.clear();\n\t\t}\n\n\t}\n\n\t/**\n\t * 合计统计信息\n\t * \n\t * @param index\n\t * @param text\n\t * @param entity\n\t */\n\tprivate void addStatisticsData(Integer index, String text, ExcelExportEntity entity) {\n\t\tif (entity != null && entity.isStatistics()) {\n\t\t\tDouble temp = 0D;\n\t\t\tif (!statistics.containsKey(index)) {\n\t\t\t\tstatistics.put(index, temp);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\ttemp = Double.valueOf(text);\n\t\t\t} catch (NumberFormatException e) {\n\t\t\t}\n\t\t\tstatistics.put(index, statistics.get(index) + temp);\n\t\t}\n\t}\n\n\t/**\n\t * 获取导出报表的字段总长度\n\t * \n\t * @param excelParams\n\t * @return\n\t */\n\tpublic int getFieldWidth(List<ExcelExportEntity> excelParams) {\n\t\tint length = -1;// 从0开始计算单元格的\n\t\tfor (ExcelExportEntity entity : excelParams) {\n\t\t\t//update-begin---author:liusq   Date:20200909  for：AutoPoi多表头导出，会多出一列空白列 #1513------------\n\t\t\tif(entity.getGroupName()!=null){\n\t\t\t\tcontinue;\n\t\t\t}else if (entity.getSubColumnList()!=null&&entity.getSubColumnList().size()>0){\n\t\t\t\tlength += entity.getSubColumnList().size();\n\t\t\t}else{\n\t\t\t\tlength += entity.getList() != null ? entity.getList().size() : 1;\n\t\t\t}\n\t\t\t//update-end---author:liusq   Date:20200909  for：AutoPoi多表头导出，会多出一列空白列 #1513------------\n\t\t}\n\t\treturn length;\n\t}\n\n\t/**\n\t * 获取图片类型,设置图片插入类型\n\t * \n\t * @param value\n\t * @return\n\t * @Author JEECG\n\t * @date 2013年11月25日\n\t */\n\tpublic int getImageType(byte[] value) {\n\t\tString type = PoiPublicUtil.getFileExtendName(value);\n\t\tif (type.equalsIgnoreCase(\"JPG\")) {\n\t\t\treturn Workbook.PICTURE_TYPE_JPEG;\n\t\t} else if (type.equalsIgnoreCase(\"PNG\")) {\n\t\t\treturn Workbook.PICTURE_TYPE_PNG;\n\t\t}\n\t\treturn Workbook.PICTURE_TYPE_JPEG;\n\t}\n\n\tprivate Map<Integer, int[]> getMergeDataMap(List<ExcelExportEntity> excelParams) {\n\t\tMap<Integer, int[]> mergeMap = new HashMap<Integer, int[]>();\n\t\t// 设置参数顺序,为之后合并单元格做准备\n\t\tint i = 0;\n\t\tfor (ExcelExportEntity entity : excelParams) {\n\t\t\tif (entity.isMergeVertical()) {\n\t\t\t\tmergeMap.put(i, entity.getMergeRely());\n\t\t\t}\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tfor (ExcelExportEntity inner : entity.getList()) {\n\t\t\t\t\tif (inner.isMergeVertical()) {\n\t\t\t\t\t\tmergeMap.put(i, inner.getMergeRely());\n\t\t\t\t\t}\n\t\t\t\t\ti++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ti++;\n\t\t\t}\n\t\t}\n\t\treturn mergeMap;\n\t}\n\n\t/**\n\t * 获取样式\n\t * \n\t * @param entity\n\t * @param needOne\n\t * @return\n\t */\n\tpublic CellStyle getStyles(boolean needOne, ExcelExportEntity entity) {\n\t\treturn excelExportStyler.getStyles(needOne, entity);\n\t}\n\n\t/**\n\t * 合并单元格\n\t * \n\t * @param sheet\n\t * @param excelParams\n\t * @param titleHeight\n\t */\n\tpublic void mergeCells(Sheet sheet, List<ExcelExportEntity> excelParams, int titleHeight) {\n\t\tMap<Integer, int[]> mergeMap = getMergeDataMap(excelParams);\n\t\tPoiMergeCellUtil.mergeCells(sheet, mergeMap, titleHeight);\n\t}\n\n\tpublic void setCellWith(List<ExcelExportEntity> excelParams, Sheet sheet) {\n\t\tint index = 0;\n\t\tfor (int i = 0; i < excelParams.size(); i++) {\n\t\t\tif (excelParams.get(i).getList() != null) {\n\t\t\t\tList<ExcelExportEntity> list = excelParams.get(i).getList();\n\t\t\t\tfor (int j = 0; j < list.size(); j++) {\n\t\t\t\t\tsheet.setColumnWidth(index, (int) (256 * list.get(j).getWidth()));\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsheet.setColumnWidth(index, (int) (256 * excelParams.get(i).getWidth()));\n\t\t\t\tindex++;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 设置隐藏列\n\t * @param excelParams\n\t * @param sheet\n\t */\n\tpublic void setColumnHidden(List<ExcelExportEntity> excelParams, Sheet sheet) {\n\t\tint index = 0;\n\t\tfor (int i = 0; i < excelParams.size(); i++) {\n\t\t\tif (excelParams.get(i).getList() != null) {\n\t\t\t\tList<ExcelExportEntity> list = excelParams.get(i).getList();\n\t\t\t\tfor (int j = 0; j < list.size(); j++) {\n\t\t\t\t\tsheet.setColumnHidden(index, list.get(j).isColumnHidden());\n\t\t\t\t\tindex++;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsheet.setColumnHidden(index, excelParams.get(i).isColumnHidden());\n\t\t\t\tindex++;\n\t\t\t}\n\t\t}\n\t}\n\tpublic void setCurrentIndex(int currentIndex) {\n\t\tthis.currentIndex = currentIndex;\n\t}\n\n\tpublic void setExcelExportStyler(IExcelExportStyler excelExportStyler) {\n\t\tthis.excelExportStyler = excelExportStyler;\n\t}\n\n\tpublic IExcelExportStyler getExcelExportStyler() {\n\t\treturn excelExportStyler;\n\t}\n\t//update-begin---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n    /**\n     *创建单元格，返回最大高度和单元格数\n     * @param patriarch\n     * @param index\n     * @param t\n     * @param excelParams\n     * @param sheet\n     * @param workbook\n     * @param rowHeight 行高\n     * @param cellNum 格数\n     * @return\n     */\n\tpublic int[] createCells(Drawing patriarch, int index, Object t,\n\t\t\t\t\t\t\t List<ExcelExportEntity> excelParams, Sheet sheet, Workbook workbook,\n\t\t\t\t\t\t\t short rowHeight, int cellNum) {\n\t\ttry {\n\t\t\tExcelExportEntity entity;\n\t\t\tRow               row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index);\n\t\t\tDataFormat        df = workbook.createDataFormat();\n\t\t\tif (rowHeight != -1) {\n\t\t\t\trow.setHeight(rowHeight);\n\t\t\t}\n\t\t\tint maxHeight = 1, listMaxHeight = 1;\n\t\t\t// 合并需要合并的单元格\n\t\t\tint margeCellNum = cellNum;\n\t\t\tint indexKey     = 0;\n\t\t\tif (excelParams != null && !excelParams.isEmpty()) {\n\t\t\t\tindexKey = createIndexCell(row, index, excelParams.get(0));\n\t\t\t}\n\t\t\tcellNum += indexKey;\n\t\t\tfor (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\t\tentity = excelParams.get(k);\n\t\t\t\t//不论数据是否为空都应该把该列的数据跳过去\n\t\t\t\tif (entity.getList() != null) {\n\t\t\t\t\tCollection<?> list          = getListCellValue(entity, t);\n\t\t\t\t\tint           tmpListHeight = 0;\n\t\t\t\t\tif (list != null && list.size() > 0) {\n\t\t\t\t\t\tint tempCellNum = 0;\n\t\t\t\t\t\tfor (Object obj : list) {\n\t\t\t\t\t\t\tint[] temp = createCells(patriarch, index + tmpListHeight, obj, entity.getList(), sheet, workbook, rowHeight, cellNum);\n\t\t\t\t\t\t\ttempCellNum = temp[1];\n\t\t\t\t\t\t\ttmpListHeight += temp[0];\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcellNum = tempCellNum;\n\t\t\t\t\t\tlistMaxHeight = Math.max(listMaxHeight, tmpListHeight);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcellNum = cellNum + getListCellSize(entity.getList());\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tObject value = getCellValue(entity, t);\n\t\t\t\t\tif (entity.getType() == 1) {\n\t\t\t\t\t\tcreateStringCell(row, cellNum++, value == null ? \"\" : value.toString(),\n\t\t\t\t\t\t\t\tindex % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity),\n\t\t\t\t\t\t\t\tentity);\n\n\t\t\t\t\t} else if (entity.getType() == 4) {\n\t\t\t\t\t\tcreateNumericCell(row, cellNum++, value == null ? \"\" : value.toString(),\n\t\t\t\t\t\t\t\tgetNumberCellStyle(index, df, entity),\n\t\t\t\t\t\t\t\tentity);\n\t\t\t\t\t} else if (entity.getType() == 0) {\n\t\t\t\t\t\t//update-begin---author:chenrui ---date:20250604  for：[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------\n\t\t\t\t\t\tcreateStringCell(row, cellNum++, value == null ? \"\" : value.toString(),\n\t\t\t\t\t\t\t\tgetGeneralCellStyle(index, workbook, entity), entity);\n\t\t\t\t\t\t//update-end---author:chenrui ---date:20250604  for：[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248------------\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcreateImageCell(patriarch, entity, row, cellNum++,\n\t\t\t\t\t\t\t\tvalue == null ? \"\" : value.toString(), t);\n\t\t\t\t\t}\n\t\t\t\t\t//update-begin-author:liusq---date:20220728--for: 新增isHyperlink属性 ---\n\t\t\t\t\tif (entity.isHyperlink()) {\n\t\t\t\t\t\trow.getCell(cellNum - 1)\n\t\t\t\t\t\t\t\t.setHyperlink(dataHanlder.getHyperlink(\n\t\t\t\t\t\t\t\t\t\trow.getSheet().getWorkbook().getCreationHelper(), t,\n\t\t\t\t\t\t\t\t\t\tentity.getName(), value));\n\t\t\t\t\t}\n\t\t\t\t\t//update-end-author:liusq---date:20220728--for: 新增isHyperlink属性 ---\n\t\t\t\t}\n\t\t\t}\n\t\t\tmaxHeight += listMaxHeight - 1;\n\t\t\tif (indexKey == 1 && excelParams.get(1).isNeedMerge()) {\n\t\t\t\texcelParams.get(0).setNeedMerge(true);\n\t\t\t}\n\t\t\tfor (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\t\tentity = excelParams.get(k);\n\t\t\t\tif (entity.getList() != null) {\n\t\t\t\t\tmargeCellNum += entity.getList().size();\n\t\t\t\t} else if (entity.isNeedMerge() && maxHeight > 1) {\n\t\t\t\t\tfor (int i = index + 1; i < index + maxHeight; i++) {\n\t\t\t\t\t\tif (sheet.getRow(i) == null) {\n\t\t\t\t\t\t\tsheet.createRow(i);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tsheet.getRow(i).createCell(margeCellNum);\n\t\t\t\t\t\tsheet.getRow(i).getCell(margeCellNum).setCellStyle(getStyles(false, entity));\n\t\t\t\t\t}\n\t\t\t\t\tPoiMergeCellUtil.addMergedRegion(sheet, index, index + maxHeight - 1, margeCellNum, margeCellNum);\n\t\t\t\t\tmargeCellNum++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn new int[]{maxHeight, cellNum};\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(\"excel cell export error ,data is :{}\", ReflectionToStringBuilder.toString(t));\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);\n\t\t}\n\t}\n\n\t/**\n\t * 获取集合的宽度\n\t *\n\t * @param list\n\t * @return\n\t */\n\tprotected int getListCellSize(List<ExcelExportEntity> list) {\n\t\tint cellSize = 0;\n\t\tfor (ExcelExportEntity ee : list) {\n\t\t\tif (ee.getList() != null) {\n\t\t\t\tcellSize += getListCellSize(ee.getList());\n\t\t\t} else {\n\t\t\t\tcellSize++;\n\t\t\t}\n\t\t}\n\t\treturn cellSize;\n\t}\n\t//update-end---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.base;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.text.DecimalFormat;\nimport java.text.SimpleDateFormat;\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.*;\n\nimport com.google.common.collect.Lists;\nimport org.apache.commons.lang3.StringUtils;\nimport org.jeecgframework.core.util.ApplicationContextUtil;\nimport org.jeecgframework.dict.service.AutoPoiDictServiceI;\nimport org.jeecgframework.poi.excel.annotation.Excel;\nimport org.jeecgframework.poi.excel.annotation.ExcelCollection;\nimport org.jeecgframework.poi.excel.annotation.ExcelEntity;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.handler.inter.IExcelDataHandler;\nimport org.jeecgframework.poi.handler.inter.IExcelDictHandler;\nimport org.jeecgframework.poi.util.JsonParser;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\n\n/**\n * 导出基础处理,不设计POI,只设计对象,保证复用性\n * \n * @author JEECG\n * @date 2014年8月9日 下午11:01:32\n */\npublic class ExportBase {\n\n\tprotected IExcelDataHandler dataHanlder;\n\n\t//update-begin-author:liusq---date:20220527--for: 增加列循环功能时中用到 ---\n\tprotected IExcelDictHandler dictHandler;\n\t//update-end-author:liusq---date:20220527--for: be 增加列循环功能时中用到---\n\n\n\n\n\tprotected List<String> needHanlderList;\n\n\t/**\n\t * 创建导出实体对象\n\t * \n\t * @param field\n\t * @param targetId\n\t * @param pojoClass\n\t * @param getMethods\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate ExcelExportEntity createExcelExportEntity(Field field, String targetId, Class<?> pojoClass, List<Method> getMethods) throws Exception {\n\t\tExcel excel = field.getAnnotation(Excel.class);\n\t\tExcelExportEntity excelEntity = new ExcelExportEntity();\n\t\texcelEntity.setType(excel.type());\n\t\tgetExcelField(targetId, field, excelEntity, excel, pojoClass);\n\t\tif (getMethods != null) {\n\t\t\tList<Method> newMethods = new ArrayList<Method>();\n\t\t\tnewMethods.addAll(getMethods);\n\t\t\tnewMethods.add(excelEntity.getMethod());\n\t\t\texcelEntity.setMethods(newMethods);\n\t\t}\n\t\treturn excelEntity;\n\t}\n\n\tprivate Object formatValue(Object value, ExcelExportEntity entity) throws Exception {\n\t\tDate temp = null;\n\t\t//update-begin-author:wangshuai date:20201118 for:Excel导出错误原因，value为\"\"字符串，gitee I249JF\n\t\tif(\"\".equals(value)){\n\t\t\tvalue= null;\n\t\t}\n\t\t//update-begin-author:wangshuai date:20201118 for:Excel导出错误原因，value为\"\"字符串，gitee I249JF\n\t\tif (value instanceof String && entity.getDatabaseFormat()!=null) {\n\t\t\tSimpleDateFormat format = new SimpleDateFormat(entity.getDatabaseFormat());\n\t\t\ttemp = format.parse(value.toString());\n\t\t} else if (value instanceof Date) {\n\t\t\ttemp = (Date) value;\n\t\t//update-begin-author:taoyan date:2022-5-17 for: mybatis-plus升级 时间字段变成了jdk8的LocalDateTime，导致格式化失败\n\t\t} else if (value instanceof LocalDateTime) {\n\t\t\tLocalDateTime ldt = (LocalDateTime) value;\n\t\t\tDateTimeFormatter format = DateTimeFormatter.ofPattern(entity.getFormat());\n\t\t\treturn format.format(ldt);\n\t\t} else if (value instanceof LocalDate) {\n\t\t\tLocalDate ld = (LocalDate) value;\n\t\t\tDateTimeFormatter format = DateTimeFormatter.ofPattern(entity.getFormat());\n\t\t\treturn format.format(ld);\n\t\t}\n\t\t//update-end-author:taoyan date:2022-5-17 for: mybatis-plus升级 时间字段变成了jdk8的LocalDateTime，导致格式化失败\n\t\tif (temp != null) {\n\t\t\tSimpleDateFormat format = new SimpleDateFormat(entity.getFormat());\n\t\t\tvalue = format.format(temp);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * 获取需要导出的全部字段\n\t * \n\t * @param exclusions\n\t * @param targetId\n\t *            目标ID\n\t * @param fields\n\t * @throws Exception\n\t */\n\tpublic void getAllExcelField(String[] exclusions, String targetId, Field[] fields, List<ExcelExportEntity> excelParams, Class<?> pojoClass, List<Method> getMethods) throws Exception {\n\t\tList<String> exclusionsList = exclusions != null ? Arrays.asList(exclusions) : null;\n\t\tExcelExportEntity excelEntity;\n\t\t// 遍历整个filed\n\t\tfor (int i = 0; i < fields.length; i++) {\n\t\t\tField field = fields[i];\n\t\t\t// 先判断是不是collection,在判断是不是java自带对象,之后就是我们自己的对象了\n\t\t\tif (PoiPublicUtil.isNotUserExcelUserThis(exclusionsList, field, targetId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// 首先判断Excel 可能一下特殊数据用户回自定义处理\n\t\t\tif (field.getAnnotation(Excel.class) != null) {\n\t\t\t\texcelParams.add(createExcelExportEntity(field, targetId, pojoClass, getMethods));\n\t\t\t} else if (PoiPublicUtil.isCollection(field.getType())) {\n\t\t\t\tExcelCollection excel = field.getAnnotation(ExcelCollection.class);\n\t\t\t\tParameterizedType pt = (ParameterizedType) field.getGenericType();\n\t\t\t\tClass<?> clz = (Class<?>) pt.getActualTypeArguments()[0];\n\t\t\t\tList<ExcelExportEntity> list = new ArrayList<ExcelExportEntity>();\n\t\t\t\tgetAllExcelField(exclusions, StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId, PoiPublicUtil.getClassFields(clz), list, clz, null);\n\t\t\t\texcelEntity = new ExcelExportEntity();\n\t\t\t\texcelEntity.setName(getExcelName(excel.name(), targetId));\n\t\t\t\texcelEntity.setOrderNum(getCellOrder(excel.orderNum(), targetId));\n\t\t\t\texcelEntity.setMethod(PoiPublicUtil.getMethod(field.getName(), pojoClass));\n\t\t\t\texcelEntity.setList(list);\n\t\t\t\texcelParams.add(excelEntity);\n\t\t\t} else {\n\t\t\t\tList<Method> newMethods = new ArrayList<Method>();\n\t\t\t\tif (getMethods != null) {\n\t\t\t\t\tnewMethods.addAll(getMethods);\n\t\t\t\t}\n\t\t\t\tnewMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass));\n\t\t\t\tExcelEntity excel = field.getAnnotation(ExcelEntity.class);\n\t\t\t\t//update-begin-author:taoyan date:20210531 for:excel导出支持 注解@ExcelEntity显示合并表头\n\t\t\t\tif(excel.show()==true){\n\t\t\t\t\tList<ExcelExportEntity> list = new ArrayList<ExcelExportEntity>();\n\t\t\t\t\t// 这里有个设计的坑，导出的时候最后一个参数是null, 即getgetMethods获取的是空，导入的时候需要设置层级getmethod\n\t\t\t\t\tgetAllExcelField(exclusions, StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId, PoiPublicUtil.getClassFields(field.getType()), list, field.getType(), null);\n\t\t\t\t\texcelEntity = new ExcelExportEntity();\n\t\t\t\t\texcelEntity.setName(getExcelName(excel.name(), targetId));\n\t\t\t\t\texcelEntity.setMethod(PoiPublicUtil.getMethod(field.getName(), pojoClass));\n\t\t\t\t\texcelEntity.setList(list);\n\t\t\t\t\texcelParams.add(excelEntity);\n\t\t\t\t}else{\n\t\t\t\t\tgetAllExcelField(exclusions, StringUtils.isNotEmpty(excel.id()) ? excel.id() : targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, field.getType(), newMethods);\n\t\t\t\t}\n\t\t\t\t//update-end-author:taoyan date:20210531 for:excel导出支持 注解@ExcelEntity显示合并表头\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 获取这个字段的顺序\n\t * \n\t * @param orderNum\n\t * @param targetId\n\t * @return\n\t */\n\tpublic int getCellOrder(String orderNum, String targetId) {\n\t\tif (isInteger(orderNum) || targetId == null) {\n\t\t\treturn Integer.valueOf(orderNum);\n\t\t}\n\t\tString[] arr = orderNum.split(\",\");\n\t\tString[] temp;\n\t\tfor (String str : arr) {\n\t\t\ttemp = str.split(\"_\");\n\t\t\tif (targetId.equals(temp[1])) {\n\t\t\t\treturn Integer.valueOf(temp[0]);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t/**\n\t * 获取填如这个cell的值,提供一些附加功能\n\t * \n\t * @param entity\n\t * @param obj\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic Object getCellValue(ExcelExportEntity entity, Object obj) throws Exception {\n\t\tObject value;\n\t\t//update-begin---author:chenrui ---date:20250819  for：[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错------------\n\t\tif(null == obj){\n\t\t\treturn \"\";\n\t\t}\n\t\t//update-end---author:chenrui ---date:20250819  for：[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错------------\n\t\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\tif (entity.isDynamic()) {\n\t\t\tvalue = getDynamicCellValue(entity, obj);\n\t\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t\t}else if (obj instanceof Map) {\n\t\t\tvalue = ((Map<?, ?>) obj).get(entity.getKey());\n\t\t} else {\n\t\t\tvalue = entity.getMethods() != null ? getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj, new Object[] {});\n\t\t}\n\n\t\t//update-begin-author:scott date:20200831 for:导出excel实体反射，时间格式转换错误 #1573\n\t\tvalue = Optional.ofNullable(value).orElse(\"\");\n\t\tif (StringUtils.isEmpty(value.toString())) {\n\t\t\treturn \"\";\n\t\t}\n\t\t//update-end-author:scott date:20200831 for:导出excel实体反射，时间格式转换错误 #1573\n\n\t\t//update-begin-author:taoyan date:2020319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\tif (StringUtils.isNotEmpty(entity.getNumFormat()) && value!=null) {\n\t\t\tvalue = new DecimalFormat(entity.getNumFormat()).format(value);\n\t\t}\n\t\t//update-end-author:taoyan date:2020319 for:Excel注解的numFormat方法似乎未实现 #970\n\n\t\tif (StringUtils.isNotEmpty(entity.getDict()) && dictHandler != null) {\n\t\t\tvalue = dictHandler.toName(entity.getDict(), obj, entity.getName(), value);\n\t\t}\n\t\tif (StringUtils.isNotEmpty(entity.getFormat())) {\n\t\t\tvalue = formatValue(value, entity);\n\t\t}\n\t\tif (entity.getReplace() != null && entity.getReplace().length > 0) {\n\t\t\t//update-begin-author:taoyan date：20180731 for:TASK #3038 【bug】Excel 导出多个值（逗号隔开的情况下，导出字典值是ID值）\n\t\t\tif(value == null){\n\t\t\t\tvalue = \"\";//String.valueOf(value) 如果value为null 则返回\"null\"\n\t\t\t}\n\t\t\tString oldVal=value.toString();\n\t\t\tif(entity.isMultiReplace()){\n\t\t\t\tvalue = multiReplaceValue(entity.getReplace(), String.valueOf(value));\n\t\t\t}else{\n\t\t\t\tvalue = replaceValue(entity.getReplace(), String.valueOf(value));\n\t\t\t}\n\t\t\t//update-end-author:taoyan date：20180731 for:TASK #3038 【bug】Excel 导出多个值（逗号隔开的情况下，导出字典值是ID值）\n\n\t\t\t//update-begin-author:liusq date：20210127 for: 两个数值相等，就证明处理翻译失败的情况\n\t\t\tif(oldVal.equals(value)){\n\n\t\t\t}\n\t\t\t//update-end-author:liusq date：20210127 for: 两个数值相等，就证明处理翻译失败的情况\n\t\t}\n\t\tif (needHanlderList != null && needHanlderList.contains(entity.getName())) {\n\t\t\tvalue = dataHanlder.exportHandler(obj, entity.getName(), value);\n\t\t}\n\t\tif (StringUtils.isNotEmpty(entity.getSuffix()) && value != null) {\n\t\t\tvalue = value + entity.getSuffix();\n\t\t}\n\t\treturn value == null ? \"\" : value.toString();\n\t}\n\n\t/**\n\t * 获取集合的值\n\t * \n\t * @param entity\n\t * @param obj\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic Collection<?> getListCellValue(ExcelExportEntity entity, Object obj) throws Exception {\n\t\tObject value;\n\t\tif (obj instanceof Map) {\n\t\t\tvalue = ((Map<?, ?>) obj).get(entity.getKey());\n\t\t} else {\n\t\t\tvalue = entity.getMethod().invoke(obj, new Object[] {});\n\t\t\tif(value instanceof Collection){\n\t\t\t\treturn (Collection<?>)value;\n\t\t\t}else{\n\t\t\t\tList list = new ArrayList();\n\t\t\t\tlist.add(value);\n\t\t\t\treturn list;\n\t\t\t}\n\t\t}\n\t\treturn (Collection<?>) value;\n\t}\n\n\t/**\n\t * 注解到导出对象的转换\n\t * \n\t * @param targetId\n\t * @param field\n\t * @param excelEntity\n\t * @param excel\n\t * @param pojoClass\n\t * @throws Exception\n\t */\n\tprivate void getExcelField(String targetId, Field field, ExcelExportEntity excelEntity, Excel excel, Class<?> pojoClass) throws Exception {\n\t\texcelEntity.setName(getExcelName(excel.name(), targetId));\n\t\texcelEntity.setWidth(excel.width());\n\t\texcelEntity.setHeight(excel.height());\n\t\texcelEntity.setNeedMerge(excel.needMerge());\n\t\texcelEntity.setMergeVertical(excel.mergeVertical());\n\t\texcelEntity.setMergeRely(excel.mergeRely());\n\t\texcelEntity.setReplace(excel.replace());\n\t\texcelEntity.setHyperlink(excel.isHyperlink());\n\t\tif(StringUtils.isNotEmpty(excel.dicCode())){\n\t\t\tAutoPoiDictServiceI jeecgDictService = null;\n\t\t\ttry {\n\t\t\t\tjeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class);\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t\tif(jeecgDictService!=null){\n\t\t\t\t String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText());\n\t\t\t\t if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){\n\t\t\t\t\t excelEntity.setReplace(dictReplace);\n\t\t\t\t }\n\t\t\t}\n\t\t}\n\t\texcelEntity.setOrderNum(getCellOrder(excel.orderNum(), targetId));\n\t\texcelEntity.setWrap(excel.isWrap());\n\t\texcelEntity.setExportImageType(excel.imageType());\n\t\texcelEntity.setSuffix(excel.suffix());\n\t\texcelEntity.setDatabaseFormat(excel.databaseFormat());\n\t\texcelEntity.setFormat(StringUtils.isNotEmpty(excel.exportFormat()) ? excel.exportFormat() : excel.format());\n\t\texcelEntity.setStatistics(excel.isStatistics());\n\t\tString fieldname = field.getName();\n\t\t//update-begin-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于注解的解决方案\n\t\texcelEntity.setKey(fieldname);\n\t\t//update-end-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于注解的解决方案\n\t\t//update-begin-author:taoyan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\texcelEntity.setNumFormat(excel.numFormat());\n\t\t//update-end-author:taoyan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n        // 动态列配置\n\t\texcelEntity.setDynamic(excel.dynamic());\n\t\texcelEntity.setDynamicField(excel.dynamicField());\n\t\texcelEntity.setDynamicValue(excel.dynamicVal());\n\t\texcelEntity.setDynamicKeepSelf(excel.dynamicKeepSelf());\n\n\t\t//update-begin-author:liusq date:202010723 for:Excel注解的isColumnHidden方法未实现\n\t\texcelEntity.setColumnHidden(excel.isColumnHidden());\n\t\t//update-end-author:liusq date:202010723 for:Excel注解的isColumnHidden方法未实现\n\n\t\t//update-begin-author:taoyan date:20180615 for:TASK #2798 【例子】导入扩展方法，支持自定义导入字段转换规则\n\t\texcelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass,excel.exportConvert()));\n\t\t//update-end-author:taoyan date:20180615 for:TASK #2798 【例子】导入扩展方法，支持自定义导入字段转换规则\n\t\t//update-begin-author:taoyan date:20180801 for:TASK #3038 【bug】Excel 导出多个值（逗号隔开的情况下，导出字典值是ID值）\n\t\texcelEntity.setMultiReplace(excel.multiReplace());\n\t\t//update-end-author:taoyan date:20180801 for:TASK #3038 【bug】Excel 导出多个值（逗号隔开的情况下，导出字典值是ID值）\n\t\t//update-begin-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于实体注解的解决方案\n\t\tif(StringUtils.isNotEmpty(excel.groupName())){\n\t\t\texcelEntity.setGroupName(excel.groupName());\n\t\t\texcelEntity.setColspan(true);\n\t\t}\n\t\t//update-end-author:taoyan date:20200319 for:autopoi 双表头问题 #862 基于实体注解的解决方案\n\t}\n\n\t/**\n\t * 判断在这个单元格显示的名称\n\t * \n\t * @param exportName\n\t * @param targetId\n\t * @return\n\t */\n\tpublic String getExcelName(String exportName, String targetId) {\n\t\tif (exportName.indexOf(\",\") < 0 || targetId==null) {\n\t\t\treturn exportName;\n\t\t}\n\t\tString[] arr = exportName.split(\",\");\n\t\tfor (String str : arr) {\n\t\t\tif (str.indexOf(targetId) != -1) {\n\t\t\t\treturn str.split(\"_\")[0];\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 多个反射获取值\n\t * \n\t * @param list\n\t * @param t\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic Object getFieldBySomeMethod(List<Method> list, Object t) throws Exception {\n\t\tfor (Method m : list) {\n\t\t\tif (t == null) {\n\t\t\t\tt = \"\";\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tt = m.invoke(t, new Object[] {});\n\t\t}\n\t\treturn t;\n\t}\n\n\t/**\n\t * 根据注解获取行高\n\t * \n\t * @param excelParams\n\t * @return\n\t */\n\tpublic short getRowHeight(List<ExcelExportEntity> excelParams) {\n\t\tdouble maxHeight = 0;\n\t\tfor (int i = 0; i < excelParams.size(); i++) {\n\t\t\tmaxHeight = maxHeight > excelParams.get(i).getHeight() ? maxHeight : excelParams.get(i).getHeight();\n\t\t\tif (excelParams.get(i).getList() != null) {\n\t\t\t\tfor (int j = 0; j < excelParams.get(i).getList().size(); j++) {\n\t\t\t\t\tmaxHeight = maxHeight > excelParams.get(i).getList().get(j).getHeight() ? maxHeight : excelParams.get(i).getList().get(j).getHeight();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn (short) (maxHeight * 50);\n\t}\n\n\t/**\n\t * 判断字符串是否是整数\n\t */\n\tpublic boolean isInteger(String value) {\n\t\ttry {\n\t\t\tInteger.parseInt(value);\n\t\t\treturn true;\n\t\t} catch (NumberFormatException e) {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate Object replaceValue(String[] replace, String value) {\n\t\tString[] temp;\n\t\tfor (String str : replace) {\n\t\t\t//temp = str.split(\"_\"); {'男_sheng_1','女_2'}\n\t\t\t//update-begin-author:liusq date：20210127 for:字符串截取修改\n\t\t\ttemp = getValueArr(str);\n\t\t\t//update-end-author:liusq date：20210127 for:字符串截取修改\n\n\t\t\t//update-begin---author:scott   Date:20211220  for：[issues/I4MBB3]@Excel dicText字段的值有下划线时，导入功能不能正确解析---\n\t\t\tif (value.equals(temp[1]) || value.replace(\"_\",\"---\").equals(temp[1])) {\n\t\t\t\tvalue = temp[0];\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\t//update-end---author:scott   Date:20211220  for：[issues/I4MBB3]@Excel dicText字段的值有下划线时，导入功能不能正确解析---\n\t\t}\n\t\treturn value;\n\t}\n\t\n\t//update-begin-author:taoyan date：20180731 for:TASK #3038 【bug】Excel 导出多个值（逗号隔开的情况下，导出字典值是ID值）\n\t/**\n\t * 如果需要被替换的值是多选项，则每一项之间有逗号隔开，走以下方法\n\t * @author taoYan\n\t * @since 2018年7月31日\n\t */\n\tprivate Object multiReplaceValue(String[] replace, String value) {\n\t\tif(value.indexOf(\",\")>0){\n\t\t\tString[] radioVals = value.split(\",\");\n\t\t\tString[] temp;\n\t\t\tString result = \"\";\n\t\t\tfor(int i =0;i<radioVals.length;i++){\n\t\t\t\tString radio = radioVals[i];\n\t\t\t\tfor (String str : replace) {\n\t\t\t\t\ttemp = str.split(\"_\");\n\t\t\t\t\t//update-begin-author:liusq date：20210127 for:字符串截取修改\n\t\t\t\t\ttemp = getValueArr(str);\n\t\t\t\t\t//update-end-author:liusq date：20210127 for:字符串截取修改\n\n\t\t\t\t\t//update-begin---author:scott   Date:20211220  for：[issues/I4MBB3]@Excel dicText字段的值有下划线时，导入功能不能正确解析---\n\t\t\t\t\tif (radio.equals(temp[1]) || radio.replace(\"_\",\"---\").equals(temp[1])) {\n\t\t\t\t\t\tresult = result.concat(temp[0])+\",\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\t//update-end---author:scott   Date:20211220  for：[issues/I4MBB3]@Excel dicText字段的值有下划线时，导入功能不能正确解析---\n\t\t\t\t}\n\t\t\t}\n\t\t\tif(result.equals(\"\")){\n\t\t\t\tresult = value;\n\t\t\t}else{\n\t\t\t\tresult = result.substring(0, result.length()-1);\n\t\t\t}\n\t\t\treturn result;\n\t\t}else{\n\t\t\treturn replaceValue(replace, value);\n\t\t}\n\t}\n\t//update-end-author:taoyan date：20180731 for:TASK #3038 【bug】Excel 导出多个值（逗号隔开的情况下，导出字典值是ID值）\n\n\t/**\n\t * 对字段根据用户设置排序\n\t */\n\tpublic void sortAllParams(List<ExcelExportEntity> excelParams) {\n\t\tCollections.sort(excelParams);\n\t\tfor (ExcelExportEntity entity : excelParams) {\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tCollections.sort(entity.getList());\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 循环ExcelExportEntity集合 附加配置信息<br>\n\t * 1.列排序<br>\n\t * 2.读取图片根路径设置(如果有字段是图片类型 并且存储在本地 则设置磁盘路径获取全地址导出)<br>\n\t * 3.多表头配置(仅限于单表 会走这个逻辑处理)\n\t */\n\tpublic void reConfigExcelExportParams(List<ExcelExportEntity> excelParams, ExportParams exportParams) {\n\t\tSet<String> NameSet = new HashSet<String>();\n\t\tMap<String,List<String>> groupAndColumnList = new HashMap<String,List<String>>();\n\t\tMap<String,Integer> groupOrder = new HashMap<>();\n\t\tint index = -99;\n\t\tfor (ExcelExportEntity entity : excelParams) {\n\t\t\tif(entity.getOrderNum()==0){\n\t\t\t\tentity.setOrderNum(index++);\n\t\t\t}\n\t\t\tif(entity.getExportImageType()==3){\n\t\t\t\tentity.setImageBasePath(exportParams.getImageBasePath());\n\t\t\t}\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tCollections.sort(entity.getList());\n\t\t\t\t//update-begin---author:chenrui ---date:20240402  for：生成代码后子表图片无法导出(流)------------\n\t\t\t\tthis.reConfigExcelExportParams(entity.getList(), exportParams);\n\t\t\t\t//update-end---author:chenrui ---date:20240402  for：生成代码后子表图片无法导出(流)------------\n\t\t\t}\n\t\t\tString groupName = entity.getGroupName();\n\t\t\tif(StringUtils.isNotEmpty(groupName)){\n\t\t\t\tList<String> ls = groupAndColumnList.get(groupName);\n\t\t\t\tif(ls==null){\n\t\t\t\t\tls = new ArrayList<String>();\n\t\t\t\t\tgroupAndColumnList.put(groupName,ls);\n\t\t\t\t}\n\t\t\t\tls.add(entity.getKey().toString());\n\n\t\t\t\tInteger order = groupOrder.get(groupName);\n\t\t\t\tif(order==null || entity.getOrderNum()<order){\n\t\t\t\t\torder = entity.getOrderNum();\n\t\t\t\t}\n\t\t\t\tgroupOrder.put(groupName,order);\n\t\t\t}\n\t\t}\n\n\t\tfor(String key: groupAndColumnList.keySet()){\n\t\t\tExcelExportEntity temp = new ExcelExportEntity(key);\n\t\t\ttemp.setColspan(true);\n\t\t\ttemp.setSubColumnList(groupAndColumnList.get(key));\n\t\t\ttemp.setOrderNum(groupOrder.get(key));\n\t\t\texcelParams.add(temp);\n\t\t}\n\t\tCollections.sort(excelParams);\n\t}\n\n\t//update-begin-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n\t/**\n\t * 动态列获取值\n\t * @param entity\n\t * @param obj\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate Object getDynamicCellValue(ExcelExportEntity entity, Object obj) throws Exception {\n\t\tCollection<?> dataList = getDynamicListValue(entity, obj);\n\t\tif (dataList == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tString header = entity.getDynamicColumnName();\n\t\tString titleField = StringUtils.defaultIfBlank(entity.getDynamicField(), \"name\");\n\t\tString valueField = StringUtils.defaultIfBlank(entity.getDynamicValue(), \"value\");\n\t\tfor (Object item : dataList) {\n\t\t\tObject nameVal = PoiPublicUtil.getParamsValue(titleField, item);\n\t\t\tif (nameVal != null && header != null && header.equals(nameVal.toString())) {\n\t\t\t\tObject result = PoiPublicUtil.getParamsValue(valueField, item);\n\t\t\t\treturn result == null ? \"\" : result;\n\t\t\t}\n\t\t}\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * 获取动态列数据集合\n\t * @param entity\n\t * @param obj\n\t * @return\n\t * @throws Exception\n\t */\n\tprotected Collection<?> getDynamicListValue(ExcelExportEntity entity, Object obj) throws Exception {\n\t\tObject value;\n\t\tif (obj instanceof Map) {\n\t\t\tvalue = ((Map<?, ?>) obj).get(entity.getKey());\n\t\t} else {\n\t\t\tvalue = entity.getMethods() != null ? getFieldBySomeMethod(entity.getMethods(), obj) : entity.getMethod().invoke(obj, new Object[] {});\n\t\t}\n\t\tif (value == null) {\n\t\t\treturn null;\n\t\t}\n\t\tif (value instanceof String) {\n\t\t\tCollection<?> parsed = parseJsonArrayToList((String) value);\n\t\t\tif (parsed != null) {\n\t\t\t\treturn parsed;\n\t\t\t}\n\t\t}\n\t\tif (value instanceof Collection) {\n\t\t\treturn (Collection<?>) value;\n\t\t}\n\t\tif (value.getClass().isArray()) {\n\t\t\treturn Arrays.asList((Object[]) value);\n\t\t}\n\t\treturn Collections.singletonList(value);\n\t}\n\t/**\n\t * 解析 JSON 数组字符串为集合（使用 JDK ScriptEngine，避免额外依赖）\n\t */\n\tprivate Collection<?> parseJsonArrayToList(String json) {\n\t\tif (StringUtils.isBlank(json)) {\n\t\t\treturn null;\n\t\t}\n\t\tString trim = json.trim();\n\t\tif (!trim.startsWith(\"[\") || !trim.endsWith(\"]\")) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tList<Object> objects = JsonParser.parseJsonArrayToList(json);\n\t\t\treturn objects;\n\t\t} catch (Exception e) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 处理动态列,根据数据动态扩充表头\n\t * @param dataSet\n\t * @param excelParams\n\t */\n\tprotected void rebuildDynamicColumns(Collection<?> dataSet, List<ExcelExportEntity> excelParams) throws Exception {\n\t\tif (excelParams == null || excelParams.isEmpty() || dataSet == null || dataSet.isEmpty()) {\n\t\t\treturn;\n\t\t}\n\t\tList<ExcelExportEntity> result = new ArrayList<ExcelExportEntity>();\n\t\tfor (ExcelExportEntity entity : excelParams) {\n\t\t\tif (!entity.isDynamic()) {\n\t\t\t\tresult.add(entity);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tList<ExcelExportEntity> dynamicList = buildDynamicEntities(entity, dataSet);\n\t\t\tif (dynamicList.isEmpty() && !entity.isDynamicKeepSelf()) {\n\t\t\t\t// 没有动态数据时仍然保留原列避免列缺失\n\t\t\t\tresult.add(entity);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entity.isDynamicKeepSelf()) {\n\t\t\t\tresult.add(copyKeepSelfEntity(entity));\n\t\t\t}\n\t\t\tresult.addAll(dynamicList);\n\t\t}\n\t\texcelParams.clear();\n\t\texcelParams.addAll(result);\n\t}\n\n\tprivate ExcelExportEntity copyKeepSelfEntity(ExcelExportEntity source){\n\t\tExcelExportEntity target = copyDynamicBase(source);\n\t\ttarget.setDynamic(false);\n\t\ttarget.setDynamicField(null);\n\t\ttarget.setDynamicValue(null);\n\t\ttarget.setDynamicColumnName(null);\n\t\ttarget.setDynamicKeepSelf(false);\n\t\ttarget.setName(source.getName());\n\t\treturn target;\n\t}\n\n\tprivate List<ExcelExportEntity> buildDynamicEntities(ExcelExportEntity entity, Collection<?> dataSet) throws Exception {\n\t\tLinkedHashSet<String> headers = new LinkedHashSet<String>();\n\t\tfor (Object data : dataSet) {\n\t\t\tCollection<?> list = getDynamicListValue(entity, data);\n\t\t\tif (list == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (Object item : list) {\n\t\t\t\tObject header = PoiPublicUtil.getParamsValue(StringUtils.defaultIfBlank(entity.getDynamicField(), \"name\"), item);\n\t\t\t\tif (header != null && StringUtils.isNotBlank(header.toString())) {\n\t\t\t\t\theaders.add(header.toString());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tList<ExcelExportEntity> dynamicEntities = new ArrayList<ExcelExportEntity>();\n\t\tfor (String header : headers) {\n\t\t\tExcelExportEntity copy = copyDynamicBase(entity);\n\t\t\tcopy.setName(header);\n\t\t\tcopy.setDynamicColumnName(header);\n\t\t\tcopy.setOrderNum(entity.getOrderNum());\n\t\t\tdynamicEntities.add(copy);\n\t\t}\n\t\treturn dynamicEntities;\n\t}\n\n\tprivate ExcelExportEntity copyDynamicBase(ExcelExportEntity source) {\n\t\tExcelExportEntity target = new ExcelExportEntity();\n\t\ttarget.setType(source.getType());\n\t\ttarget.setName(source.getName());\n\t\ttarget.setKey(source.getKey());\n\t\ttarget.setWidth(source.getWidth());\n\t\ttarget.setHeight(source.getHeight());\n\t\ttarget.setExportImageType(source.getExportImageType());\n\t\ttarget.setImageBasePath(source.getImageBasePath());\n\t\ttarget.setOrderNum(source.getOrderNum());\n\t\ttarget.setWrap(source.isWrap());\n\t\ttarget.setNeedMerge(source.isNeedMerge());\n\t\ttarget.setMergeVertical(source.isMergeVertical());\n\t\ttarget.setMergeRely(source.getMergeRely());\n\t\ttarget.setSuffix(source.getSuffix());\n\t\ttarget.setStatistics(source.isStatistics());\n\t\ttarget.setColspan(source.isColspan());\n\t\ttarget.setSubColumnList(source.getSubColumnList());\n\t\ttarget.setGroupName(source.getGroupName());\n\t\ttarget.setColumnHidden(source.isColumnHidden());\n\t\ttarget.setReplace(source.getReplace());\n\t\ttarget.setMethod(source.getMethod());\n\t\ttarget.setMethods(source.getMethods());\n\t\ttarget.setMultiReplace(source.isMultiReplace());\n\t\ttarget.setNumFormat(source.getNumFormat());\n\t\ttarget.setDatabaseFormat(source.getDatabaseFormat());\n\t\ttarget.setFormat(source.getFormat());\n\t\ttarget.setFixedIndex(source.getFixedIndex());\n\t\ttarget.setDict(source.getDict());\n\t\ttarget.setHyperlink(source.isHyperlink());\n\t\ttarget.setMergeVertical(source.isMergeVertical());\n\t\t// 动态配置\n\t\ttarget.setDynamic(true);\n\t\ttarget.setDynamicField(source.getDynamicField());\n\t\ttarget.setDynamicValue(source.getDynamicValue());\n\t\ttarget.setDynamicKeepSelf(source.isDynamicKeepSelf());\n\t\treturn target;\n\t}\n\t/**\n\t * 字典文本中含多个下划线横岗，取最后一个（解决空值情况）\n\t *\n\t * @param val\n\t * @return\n\t */\n\tpublic String[] getValueArr(String val) {\n\t\tint i = val.lastIndexOf(\"_\");//最后一个分隔符的位置\n\t\tString[] c=new String[2];\n\t\tc[0]=val.substring(0, i); //label\n\t\tc[1]=val.substring(i+1); //key\n\t\treturn c;\n\t}\n\t//update-end-author:liusq date:20251211 for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/AbstractExcelExportStyler.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.styler;\n\nimport org.apache.poi.ss.usermodel.BuiltinFormats;\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.entity.params.ExcelForEachParams;\n\n/**\n * 抽象接口提供两个公共方法\n * \n * @author JEECG\n * @date 2015年1月9日 下午5:48:55\n */\npublic abstract class AbstractExcelExportStyler implements IExcelExportStyler {\n\t// 单行\n\tprotected CellStyle stringNoneStyle;\n\tprotected CellStyle stringNoneWrapStyle;\n\t// 间隔行\n\tprotected CellStyle stringSeptailStyle;\n\tprotected CellStyle stringSeptailWrapStyle;\n\n\tprotected Workbook workbook;\n\n\tprotected static final short STRING_FORMAT = (short) BuiltinFormats.getBuiltinFormat(\"TEXT\");\n\n\tprotected void createStyles(Workbook workbook) {\n\t\tthis.stringNoneStyle = stringNoneStyle(workbook, false);\n\t\tthis.stringNoneWrapStyle = stringNoneStyle(workbook, true);\n\t\tthis.stringSeptailStyle = stringSeptailStyle(workbook, false);\n\t\tthis.stringSeptailWrapStyle = stringSeptailStyle(workbook, true);\n\t\tthis.workbook = workbook;\n\t}\n\n\t@Override\n\tpublic CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity) {\n\t\tif (noneStyler && (entity == null || entity.isWrap())) {\n\t\t\treturn stringNoneWrapStyle;\n\t\t}\n\t\tif (noneStyler) {\n\t\t\treturn stringNoneStyle;\n\t\t}\n\t\tif (noneStyler == false && (entity == null || entity.isWrap())) {\n\t\t\treturn stringSeptailWrapStyle;\n\t\t}\n\t\treturn stringSeptailStyle;\n\t}\n\n\tpublic CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {\n\t\treturn null;\n\t}\n\n\tpublic CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {\n\t\treturn null;\n\t}\n\n\t/**\n\t * 获取模板样式（列循环时用到）\n\t * @param isSingle\n\t * @param excelForEachParams\n\t * @return\n\t */\n\t@Override\n\tpublic CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {\n\t\treturn null;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/ExcelExportStylerBorderImpl.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.styler;\n\nimport org.apache.poi.ss.usermodel.*;\n\n/**\n * 带有边框的Excel样式\n * \n * @author JEECG\n * @date 2015年1月9日 下午5:55:29\n */\npublic class ExcelExportStylerBorderImpl extends AbstractExcelExportStyler implements IExcelExportStyler {\n\n\tpublic ExcelExportStylerBorderImpl(Workbook workbook) {\n\t\tsuper.createStyles(workbook);\n\t}\n\n\t@Override\n\tpublic CellStyle getHeaderStyle(short color) {\n\t\tCellStyle titleStyle = workbook.createCellStyle();\n\t\tFont font = workbook.createFont();\n\t\tfont.setFontHeightInPoints((short) 12);\n\t\ttitleStyle.setFont(font);\n\t\ttitleStyle.setBorderLeft(BorderStyle.THIN); // 左边框\n\t\ttitleStyle.setBorderRight(BorderStyle.THIN); // 右边框\n\t\ttitleStyle.setBorderBottom(BorderStyle.THIN);\n\t\ttitleStyle.setBorderTop(BorderStyle.THIN);\n\t\ttitleStyle.setAlignment(HorizontalAlignment.CENTER);\n\t\ttitleStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\treturn titleStyle;\n\t}\n\n\t@Override\n\tpublic CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {\n\t\tCellStyle style = workbook.createCellStyle();\n\t\tstyle.setBorderLeft(BorderStyle.THIN); // 左边框\n\t\tstyle.setBorderRight(BorderStyle.THIN); // 右边框\n\t\tstyle.setBorderBottom(BorderStyle.THIN);\n\t\tstyle.setBorderTop(BorderStyle.THIN);\n\t\tstyle.setAlignment(HorizontalAlignment.CENTER);\n\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\tstyle.setDataFormat(STRING_FORMAT);\n\t\tif (isWarp) {\n\t\t\tstyle.setWrapText(true);\n\t\t}\n\t\treturn style;\n\t}\n\n\t@Override\n\tpublic CellStyle getTitleStyle(short color) {\n\t\tCellStyle titleStyle = workbook.createCellStyle();\n\t\ttitleStyle.setBorderLeft(BorderStyle.THIN); // 左边框\n\t\ttitleStyle.setBorderRight(BorderStyle.THIN); // 右边框\n\t\ttitleStyle.setBorderBottom(BorderStyle.THIN);\n\t\ttitleStyle.setBorderTop(BorderStyle.THIN);\n\t\ttitleStyle.setAlignment(HorizontalAlignment.CENTER);\n\t\ttitleStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\ttitleStyle.setWrapText(true);\n\t\treturn titleStyle;\n\t}\n\n\t@Override\n\tpublic CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {\n\t\treturn isWarp ? stringNoneWrapStyle : stringNoneStyle;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/ExcelExportStylerColorImpl.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.styler;\n\nimport org.apache.poi.ss.usermodel.*;\n\n/**\n * 带有样式的导出服务\n * \n * @author JEECG\n * @date 2015年1月9日 下午4:54:15\n */\npublic class ExcelExportStylerColorImpl extends AbstractExcelExportStyler implements IExcelExportStyler {\n\n\tpublic ExcelExportStylerColorImpl(Workbook workbook) {\n\t\tsuper.createStyles(workbook);\n\t}\n\n\t@Override\n\tpublic CellStyle getHeaderStyle(short headerColor) {\n\t\tCellStyle titleStyle = workbook.createCellStyle();\n\t\tFont font = workbook.createFont();\n\t\tfont.setFontHeightInPoints((short) 24);\n\t\ttitleStyle.setFont(font);\n\t\ttitleStyle.setFillForegroundColor(headerColor);\n\t\ttitleStyle.setAlignment(HorizontalAlignment.CENTER);\n\t\ttitleStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\treturn titleStyle;\n\t}\n\n\t@Override\n\tpublic CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {\n\t\tCellStyle style = workbook.createCellStyle();\n\t\tstyle.setBorderLeft(BorderStyle.THIN); // 左边框\n\t\tstyle.setBorderRight(BorderStyle.THIN); // 右边框\n\t\tstyle.setBorderBottom(BorderStyle.THIN);\n\t\tstyle.setBorderTop(BorderStyle.THIN);\n\t\tstyle.setAlignment(HorizontalAlignment.CENTER);\n\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\tstyle.setDataFormat(STRING_FORMAT);\n\t\tif (isWarp) {\n\t\t\tstyle.setWrapText(true);\n\t\t}\n\t\treturn style;\n\t}\n\n\t@Override\n\tpublic CellStyle getTitleStyle(short color) {\n\t\tCellStyle titleStyle = workbook.createCellStyle();\n\t\ttitleStyle.setFillForegroundColor(color); // 填充的背景颜色\n\t\ttitleStyle.setAlignment(HorizontalAlignment.CENTER);\n\t\ttitleStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\ttitleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充图案\n\t\ttitleStyle.setWrapText(true);\n\t\treturn titleStyle;\n\t}\n\n\t@Override\n\tpublic CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {\n\t\tCellStyle style = workbook.createCellStyle();\n\t\tstyle.setBorderLeft(BorderStyle.THIN); // 左边框\n\t\tstyle.setBorderRight(BorderStyle.THIN); // 右边框\n\t\tstyle.setBorderBottom(BorderStyle.THIN);\n\t\tstyle.setBorderTop(BorderStyle.THIN);\n\t\tstyle.setFillForegroundColor((short) 41); // 填充的背景颜色\n\t\tstyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充图案\n\t\tstyle.setAlignment(HorizontalAlignment.CENTER);\n\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\tstyle.setDataFormat(STRING_FORMAT);\n\t\tif (isWarp) {\n\t\t\tstyle.setWrapText(true);\n\t\t}\n\t\treturn style;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/ExcelExportStylerDefaultImpl.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.styler;\n\nimport org.apache.poi.ss.usermodel.*;\n\n/**\n * 样式的默认实现\n * \n * @author JEECG\n * @date 2015年1月9日 下午5:36:08\n */\npublic class ExcelExportStylerDefaultImpl extends AbstractExcelExportStyler implements IExcelExportStyler {\n\n\tpublic ExcelExportStylerDefaultImpl(Workbook workbook) {\n\t\tsuper.createStyles(workbook);\n\t}\n\n\t@Override\n\tpublic CellStyle getTitleStyle(short color) {\n\t\tCellStyle titleStyle = workbook.createCellStyle();\n\t\ttitleStyle.setAlignment(HorizontalAlignment.CENTER);\n\t\ttitleStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\ttitleStyle.setWrapText(true);\n\t\treturn titleStyle;\n\t}\n\n\t@Override\n\tpublic CellStyle stringSeptailStyle(Workbook workbook, boolean isWarp) {\n\t\tCellStyle style = workbook.createCellStyle();\n\t\tstyle.setAlignment( HorizontalAlignment.CENTER);\n\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\tstyle.setDataFormat(STRING_FORMAT);\n\t\tif (isWarp) {\n\t\t\tstyle.setWrapText(true);\n\t\t}\n\t\treturn style;\n\t}\n\n\t@Override\n\tpublic CellStyle getHeaderStyle(short color) {\n\t\tCellStyle titleStyle = workbook.createCellStyle();\n\t\tFont font = workbook.createFont();\n\t\tfont.setFontHeightInPoints((short) 12);\n\t\ttitleStyle.setFont(font);\n\t\ttitleStyle.setAlignment( HorizontalAlignment.CENTER);\n\t\ttitleStyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\treturn titleStyle;\n\t}\n\n\t@Override\n\tpublic CellStyle stringNoneStyle(Workbook workbook, boolean isWarp) {\n\t\tCellStyle style = workbook.createCellStyle();\n\t\tstyle.setAlignment(HorizontalAlignment.CENTER);\n\t\tstyle.setVerticalAlignment(VerticalAlignment.CENTER);\n\t\tstyle.setDataFormat(STRING_FORMAT);\n\t\tif (isWarp) {\n\t\t\tstyle.setWrapText(true);\n\t\t}\n\t\treturn style;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/styler/IExcelExportStyler.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.styler;\n\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.entity.params.ExcelForEachParams;\n\n/**\n * Excel导出样式接口\n * \n * @author JEECG\n * @date 2015年1月9日 下午5:32:30\n */\npublic interface IExcelExportStyler {\n\n\t/**\n\t * 列表头样式\n\t * \n\t * @param headerColor\n\t * @return\n\t */\n\tpublic CellStyle getHeaderStyle(short headerColor);\n\n\t/**\n\t * 标题样式\n\t * \n\t * @param color\n\t * @return\n\t */\n\tpublic CellStyle getTitleStyle(short color);\n\n\t/**\n\t * 获取样式方法\n\t * \n\t * @param noneStyler\n\t * @param entity\n\t * @return\n\t */\n\tpublic CellStyle getStyles(boolean noneStyler, ExcelExportEntity entity);\n\t/**\n\t * 模板使用的样式设置\n\t */\n\tpublic CellStyle getTemplateStyles(boolean isSingle, ExcelForEachParams excelForEachParams);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.export.template;\n\nimport java.lang.reflect.Field;\nimport java.util.*;\n\nimport org.apache.commons.lang3.ObjectUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.hssf.usermodel.HSSFClientAnchor;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.ss.util.CellRangeAddress;\nimport org.apache.poi.xssf.usermodel.XSSFClientAnchor;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.jeecgframework.poi.cache.ExcelCache;\nimport org.jeecgframework.poi.cache.ImageCache;\nimport org.jeecgframework.poi.consts.ImageScaleMode;\nimport org.jeecgframework.poi.entity.ImageEntity;\nimport javax.imageio.ImageIO;\nimport java.awt.image.BufferedImage;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.entity.params.ExcelForEachParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelTemplateParams;\nimport org.jeecgframework.poi.excel.export.base.ExcelExportBase;\nimport org.jeecgframework.poi.excel.export.styler.IExcelExportStyler;\nimport org.jeecgframework.poi.excel.html.helper.MergedRegionHelper;\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;\n\nimport static org.jeecgframework.poi.util.PoiElUtil.*;\n\nimport org.jeecgframework.poi.util.*;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Excel 导出根据模板导出\n *\n * @author JEECG\n * @date 2013-10-17\n * @version 1.0\n */\npublic final class ExcelExportOfTemplateUtil extends ExcelExportBase {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ExcelExportOfTemplateUtil.class);\n\n\t/**\n\t * 缓存TEMP 的for each创建的cell ,跳过这个cell的模板语法查找,提高效率\n\t */\n\tprivate Set<String> tempCreateCellSet = new HashSet<String>();\n\t/**\n\t * 模板参数,全局都用到\n\t */\n\tprivate TemplateExportParams teplateParams;\n\t/**\n\t * 单元格合并信息\n\t */\n\tprivate MergedRegionHelper mergedRegionHelper;\n\n\n\t/**\n\t * 往Sheet 填充正常数据,根据表头信息 使用导入的部分逻辑,坐对象映射\n\t *\n\t * @param teplateParams\n\t * @param pojoClass\n\t * @param dataSet\n\t * @param workbook\n\t */\n\tprivate void addDataToSheet(Class<?> pojoClass, Collection<?> dataSet, Sheet sheet, Workbook workbook) throws Exception {\n\n\t\tif (workbook instanceof XSSFWorkbook) {\n\t\t\tsuper.type = ExcelType.XSSF;\n\t\t}\n\t\t// 获取表头数据\n\t\tMap<String, Integer> titlemap = getTitleMap(sheet);\n\t\tDrawing patriarch = sheet.createDrawingPatriarch();\n\t\t// 得到所有字段\n\t\tField[] fileds = PoiPublicUtil.getClassFields(pojoClass);\n\t\tExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);\n\t\tString targetId = null;\n\t\tif (etarget != null) {\n\t\t\ttargetId = etarget.value();\n\t\t}\n\t\t// 获取实体对象的导出数据\n\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\tgetAllExcelField(null, targetId, fileds, excelParams, pojoClass, null);\n\t\t// 根据表头进行筛选排序\n\t\tsortAndFilterExportField(excelParams, titlemap);\n\t\tshort rowHeight = getRowHeight(excelParams);\n\t\tint index = teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), titleHeight = index;\n\t\t// 下移数据,模拟插入\n\t\tsheet.shiftRows(teplateParams.getHeadingRows() + teplateParams.getHeadingStartRow(), sheet.getLastRowNum(), getShiftRows(dataSet, excelParams), true, true);\n\t\tif (excelParams.size() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tIterator<?> its = dataSet.iterator();\n\t\twhile (its.hasNext()) {\n\t\t\tObject t = its.next();\n\t\t\tindex += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight);\n\t\t}\n\t\t// 合并同类项\n\t\tmergeCells(sheet, excelParams, titleHeight);\n\t}\n\n\t/**\n\t * 下移数据\n\t *\n\t * @param its\n\t * @param excelParams\n\t * @return\n\t */\n\tprivate int getShiftRows(Collection<?> dataSet, List<ExcelExportEntity> excelParams) throws Exception {\n\t\tint size = 0;\n\t\tIterator<?> its = dataSet.iterator();\n\t\twhile (its.hasNext()) {\n\t\t\tObject t = its.next();\n\t\t\tsize += getOneObjectSize(t, excelParams);\n\t\t}\n\t\treturn size;\n\t}\n\n\t/**\n\t * 获取单个对象的高度,主要是处理一堆多的情况\n\t *\n\t * @param styles\n\t * @param rowHeight\n\t * @throws Exception\n\t */\n\tpublic int getOneObjectSize(Object t, List<ExcelExportEntity> excelParams) throws Exception {\n\t\tExcelExportEntity entity;\n\t\tint maxHeight = 1;\n\t\tfor (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tCollection<?> list = (Collection<?>) entity.getMethod().invoke(t, new Object[] {});\n\t\t\t\tif (list != null && list.size() > maxHeight) {\n\t\t\t\t\tmaxHeight = list.size();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn maxHeight;\n\n\t}\n\n\tpublic Workbook createExcleByTemplate(TemplateExportParams params, Class<?> pojoClass, Collection<?> dataSet, Map<String, Object> map) {\n\t\t// step 1. 判断模板的地址\n\t\tif (params == null || map == null || (StringUtils.isEmpty(params.getTemplateUrl()) && params.getTemplateWb() == null)) {\n\t\t\tthrow new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR);\n\t\t}\n\t\tWorkbook wb = null;\n\t\t// step 2. 判断模板的Excel类型,解析模板\n\t\ttry {\n\t\t\tthis.teplateParams = params;\n\t\t\t//update-begin-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式\n\t\t\tif (params.getTemplateWb() != null) {\n\t\t\t\twb = params.getTemplateWb();\n\t\t\t} else {\n\t\t\t\twb = getCloneWorkBook();\n\t\t\t}\n\t\t\t//update-end-author:liusq---date:2024-09-03--for: [issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式\n\t\t\t// 创建表格样式\n\t\t\tsetExcelExportStyler((IExcelExportStyler) teplateParams.getStyle().getConstructor(Workbook.class).newInstance(wb));\n\t\t\t// step 3. 解析模板\n\t\t\t//update-begin---author:chenrui ---date:20240801  for：[issues/6925]xlsx模版导出图片------------\n\t\t\tif (wb instanceof XSSFWorkbook) {\n\t\t\t\tsuper.type = ExcelType.XSSF;\n\t\t\t}\n\t\t\t//update-end---author:chenrui ---date:20240801  for：[issues/6925]xlsx模版导出图片------------\n\t\t\tfor (int i = 0, le = params.isScanAllsheet() ? wb.getNumberOfSheets() : params.getSheetNum().length; i < le; i++) {\n\t\t\t\tif (params.getSheetName() != null && params.getSheetName().length > i && StringUtils.isNotEmpty(params.getSheetName()[i])) {\n\t\t\t\t\twb.setSheetName(i, params.getSheetName()[i]);\n\t\t\t\t}\n\t\t\t\ttempCreateCellSet.clear();\n\t\t\t\tparseTemplate(wb.getSheetAt(i), map, params.isColForEach());\n\t\t\t}\n\t\t\tif (dataSet != null) {\n\t\t\t\t// step 4. 正常的数据填充\n\t\t\t\tdataHanlder = params.getDataHanlder();\n\t\t\t\tif (dataHanlder != null) {\n\t\t\t\t\tneedHanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());\n\t\t\t\t}\n\t\t\t\taddDataToSheet(pojoClass, dataSet, wb.getSheetAt(params.getDataSheetNum()), wb);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\treturn null;\n\t\t}\n\t\treturn wb;\n\t}\n\n\t/**\n\t * 克隆excel防止操作原对象,workbook无法克隆,只能对excel进行克隆\n\t *\n\t * @param teplateParams\n\t * @throws Exception\n\t * @Author JEECG\n\t * @date 2013-11-11\n\t */\n\tprivate Workbook getCloneWorkBook() throws Exception {\n\t\t//update-begin-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505\n\t\treturn ExcelCache.getWorkbookByTemplate(teplateParams.getTemplateUrl(), teplateParams.getSheetNum(), teplateParams.isScanAllsheet());\n\t\t//update-end-author:wangshuai date:20200730 for:jar 包上传到服务器后 autopoi 读取不到excel模版文件 #1505\n\t}\n\n\t/**\n\t * 获取表头数据,设置表头的序号\n\t *\n\t * @param teplateParams\n\t * @param sheet\n\t * @return\n\t */\n\tprivate Map<String, Integer> getTitleMap(Sheet sheet) {\n\t\tRow row = null;\n\t\tIterator<Cell> cellTitle;\n\t\tMap<String, Integer> titlemap = new HashMap<String, Integer>();\n\t\tfor (int j = 0; j < teplateParams.getHeadingRows(); j++) {\n\t\t\trow = sheet.getRow(j + teplateParams.getHeadingStartRow());\n\t\t\tcellTitle = row.cellIterator();\n\t\t\tint i = row.getFirstCellNum();\n\t\t\twhile (cellTitle.hasNext()) {\n\t\t\t\tCell cell = cellTitle.next();\n\t\t\t\tString value = cell.getStringCellValue();\n\t\t\t\tif (!StringUtils.isEmpty(value)) {\n\t\t\t\t\ttitlemap.put(value, i);\n\t\t\t\t}\n\t\t\t\ti = i + 1;\n\t\t\t}\n\t\t}\n\t\treturn titlemap;\n\n\t}\n\n\tprivate void parseTemplate(Sheet sheet, Map<String, Object> map, boolean colForeach) throws Exception {\n\t\tdeleteCell(sheet, map);\n\t\t//update-begin-author:liusq---date:20220527--for: 模板导出列循环核心代码 ---\n\t\tmergedRegionHelper = new MergedRegionHelper(sheet);\n\t\tif (colForeach) {\n\t\t\tcolForeach(sheet, map);\n\t\t}\n\t\t//update-end-author:liusq---date:20220527--for: 模板导出列循环核心代码 ---\n\t\tRow row = null;\n\t\tint index = 0;\n\t\twhile (index <= sheet.getLastRowNum()) {\n\t\t\trow = sheet.getRow(index++);\n\t\t\tif (row == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {\n\t\t\t\tif (row.getCell(i) != null && !tempCreateCellSet.contains(row.getRowNum() + \"_\" + row.getCell(i).getColumnIndex())) {\n\t\t\t\t\tsetValueForCellByMap(row.getCell(i), map);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 先判断删除,省得影响效率\n\t *\n\t * @param sheet\n\t * @param map\n\t * @throws Exception\n\t */\n\tprivate void deleteCell(Sheet sheet, Map<String, Object> map) throws Exception {\n\t\tRow row = null;\n\t\tCell cell = null;\n\t\tint index = 0;\n\t\twhile (index <= sheet.getLastRowNum()) {\n\t\t\trow = sheet.getRow(index++);\n\t\t\tif (row == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {\n\t\t\t\tcell = row.getCell(i);\n\t\t\t\tif (row.getCell(i) != null && (cell.getCellType() == CellType.STRING || cell.getCellType() == CellType.NUMERIC)) {\n\t\t\t\t\tcell.setCellType(CellType.STRING);\n\t\t\t\t\tString text = cell.getStringCellValue();\n\t\t\t\t\tif (text.contains(IF_DELETE)) {\n\t\t\t\t\t\tif (Boolean.valueOf(eval(text.substring(text.indexOf(START_STR) + 2, text.indexOf(END_STR)).trim(), map).toString())) {\n\t\t\t\t\t\t\tPoiSheetUtility.deleteColumn(sheet, i);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcell.setCellValue(\"\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 给每个Cell通过解析方式set值\n\t *\n\t * @param cell\n\t * @param map\n\t */\n\tprivate void setValueForCellByMap(Cell cell, Map<String, Object> map) throws Exception {\n       CellType cellType = cell.getCellType();\n       if (cellType != CellType.STRING && cellType != CellType.NUMERIC) {\n\t\t\treturn;\n\t\t}\n\t\tString oldString;\n\t\tcell.setCellType(CellType.STRING);\n\t\toldString = cell.getStringCellValue();\n\t\tif (oldString != null && oldString.indexOf(START_STR) != -1 && !oldString.contains(FOREACH)) {\n\t\t\t// step 2. 判断是否含有解析函数\n\t\t\tString params = null;\n\t\t\tboolean isNumber = false;\n\t\t\tif (isNumber(oldString)) {\n\t\t\t\tisNumber = true;\n\t\t\t\toldString = oldString.replace(NUMBER_SYMBOL, \"\");\n\t\t\t}\n\t\t\twhile (oldString.indexOf(START_STR) != -1) {\n\t\t\t\tparams = oldString.substring(oldString.indexOf(START_STR) + 2, oldString.indexOf(END_STR));\n\n\t\t\t\t//update-begin-author:liusq---date:2025-06-04--for: [issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错，导出失败\n\t\t\t\toldString = oldString.replace(START_STR + params + END_STR, ObjectUtils.isNotEmpty(eval(params, map))?eval(params, map).toString():\"\");\n\t\t\t\t//update-end-author:liusq---date:2025-06-04--for:[issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错，导出失败\n\n\t\t\t}\n\t\t\t// 如何是数值 类型,就按照数值类型进行设置\n\t\t\tif (isNumber && StringUtils.isNotBlank(oldString)) {\n\t\t\t\tcell.setCellValue(Double.parseDouble(oldString));\n\t\t\t\tcell.setCellType(CellType.NUMERIC);\n\t\t\t} else {\n\t\t\t\tcell.setCellValue(oldString);\n\t\t\t}\n\t\t}\n\t\t// 判断foreach 这种方法\n\t\tif (oldString != null && oldString.contains(FOREACH)) {\n\t\t\taddListDataToExcel(cell, map, oldString.trim());\n\t\t}\n\n\t}\n\n\tprivate boolean isNumber(String text) {\n\t\treturn text.startsWith(NUMBER_SYMBOL) || text.contains(\"{\" + NUMBER_SYMBOL) || text.contains(\" \" + NUMBER_SYMBOL);\n\t}\n\n\t/**\n\t * 利用foreach循环输出数据\n\t *\n\t * @param cell\n\t * @param map\n\t * @param oldString\n\t * @throws Exception\n\t */\n\tprivate void addListDataToExcel(Cell cell, Map<String, Object> map, String name) throws Exception {\n\t\tboolean isCreate = !name.contains(FOREACH_NOT_CREATE);\n\t\tboolean isShift = name.contains(FOREACH_AND_SHIFT);\n\t\tname = name.replace(FOREACH_NOT_CREATE, EMPTY).replace(FOREACH_AND_SHIFT, EMPTY).replace(FOREACH, EMPTY).replace(START_STR, EMPTY);\n\t\tString[] keys = name.replaceAll(\"\\\\s{1,}\", \" \").trim().split(\" \");\n\t\tCollection<?> datas = (Collection<?>) PoiPublicUtil.getParamsValue(keys[0], map);\n     //update-begin-author:liusq---date:20220609--for: [issues/3328]autopoi模板导出Excel功能，$fe: 遍历不好用 ---\n\t\tObject[] columnsInfo = getAllDataColumns(cell, name.replace(keys[0], EMPTY),\n\t\t\t\tmergedRegionHelper);\n\t\tint         rowspan = (Integer) columnsInfo[0], colspan = (Integer) columnsInfo[1];\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tList<ExcelForEachParams> columns = (List<ExcelForEachParams>) columnsInfo[2];\n\t\tif (datas == null) {\n\t\t\treturn;\n\t\t}\n\t\tIterator<?> its = datas.iterator();\n\t\tRow row;\n\t\tint rowIndex = cell.getRow().getRowNum() + 1;\n\t\t//处理当前行\n\t\tint loopSize = 0;\n\t\tif (its.hasNext()) {\n\t\t\tObject t = its.next();\n\t\t\tcell.getRow().setHeight(columns.get(0).getHeight());\n\t\t\tloopSize = setForeachRowCellValue(isCreate, cell.getRow(), cell.getColumnIndex(), t, columns, map,\n\t\t\t\t\trowspan, colspan, mergedRegionHelper)[0];\n\t\t\trowIndex += rowspan - 1 + loopSize - 1;\n\t\t}\n\t\t//修复不论后面有没有数据,都应该执行的是插入操作\n\t\tif (isShift && datas.size() * rowspan > 1 && cell.getRowIndex() + rowspan <= cell.getRow().getSheet().getLastRowNum()) {\n\t\t\tint lastRowNum = cell.getRow().getSheet().getLastRowNum();\n\t\t\tint shiftRows  = lastRowNum - cell.getRowIndex() - rowspan;\n\t\t\tcell.getRow().getSheet().shiftRows(cell.getRowIndex() + rowspan, lastRowNum, (datas.size() - 1) * rowspan, true, true);\n\t\t\t//update-begin-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格，第二个表格读取错误 ---\n\t\t\tmergedRegionHelper.shiftRows(cell.getSheet(), cell.getRowIndex() + rowspan, (datas.size() - 1) * rowspan, shiftRows);\n\t\t\tPoiExcelTempUtil.reset(cell.getSheet(), cell.getRowIndex() + rowspan + (datas.size() - 1) * rowspan, cell.getRow().getSheet().getLastRowNum());\n\t\t\t//update-end-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格，第二个表格读取错误 ---\n\t\t}\n\t\twhile (its.hasNext()) {\n\t\t\tObject t = its.next();\n\t\t\trow = createRow(rowIndex, cell.getSheet(), isCreate, rowspan);\n\t\t\trow.setHeight(columns.get(0).getHeight());\n\t\t\tloopSize = setForeachRowCellValue(isCreate, row, cell.getColumnIndex(), t, columns, map, rowspan,\n\t\t\t\t\tcolspan, mergedRegionHelper)[0];\n\t\t\trowIndex += rowspan + loopSize - 1;\n\t\t}\n\t\t//update-end-author:liusq---date:20220609--for: [issues/3328]autopoi模板导出Excel功能，$fe: 遍历不好用 ---\n\t}\n\n\tprivate void setForEeachCellValue(boolean isCreate, Row row, int columnIndex, Object t, List<ExcelTemplateParams> columns, Map<String, Object> map) throws Exception {\n\t\tfor (int i = 0, max = columnIndex + columns.size(); i < max; i++) {\n\t\t\tif (row.getCell(i) == null)\n\t\t\t\trow.createCell(i);\n\t\t}\n\t\tfor (int i = 0, max = columns.size(); i < max; i++) {\n\t\t\tboolean isNumber = false;\n\t\t\tString tempStr = new String(columns.get(i).getName());\n\t\t\tif (isNumber(tempStr)) {\n\t\t\t\tisNumber = true;\n\t\t\t\ttempStr = tempStr.replace(NUMBER_SYMBOL, \"\");\n\t\t\t}\n\t\t\tmap.put(teplateParams.getTempParams(), t);\n\t\t\tString val = eval(tempStr, map).toString();\n\t\t\tif (isNumber && StringUtils.isNotEmpty(val)) {\n\t\t\t\trow.getCell(i + columnIndex).setCellValue(Double.parseDouble(val));\n\t\t\t\trow.getCell(i + columnIndex).setCellType(CellType.NUMERIC);\n\t\t\t} else {\n\t\t\t\trow.getCell(i + columnIndex).setCellValue(val);\n\t\t\t}\n\t\t\trow.getCell(i + columnIndex).setCellStyle(columns.get(i).getCellStyle());\n\t\t\ttempCreateCellSet.add(row.getRowNum() + \"_\" + (i + columnIndex));\n\t\t}\n\n\t}\n\n\t/**\n\t * 获取迭代的数据的值\n\t *\n\t * @param cell\n\t * @param name\n\t * @return\n\t */\n\tprivate List<ExcelTemplateParams> getAllDataColumns(Cell cell, String name) {\n\t\tList<ExcelTemplateParams> columns = new ArrayList<ExcelTemplateParams>();\n\t\tcell.setCellValue(\"\");\n\t\tif (name.contains(END_STR)) {\n\t\t\tcolumns.add(new ExcelTemplateParams(name.replace(END_STR, EMPTY).trim(), cell.getCellStyle(), cell.getRow().getHeight()));\n\t\t\treturn columns;\n\t\t}\n\t\tcolumns.add(new ExcelTemplateParams(name.trim(), cell.getCellStyle(), cell.getRow().getHeight()));\n\t\tint index = cell.getColumnIndex();\n\t\t//列数\n\t\tint  lastCellNum = cell.getRow().getLastCellNum();\n\t\tCell tempCell;\n\t\twhile (true) {\n\t\t\ttempCell = cell.getRow().getCell(++index);\n\t\t\t//--begin--date：2020/09/18---for：增加列数判断，防止提前跳出\n\t\t\tif (tempCell == null&&index>=lastCellNum) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tString cellStringString;\n\t\t\ttry {// 允许为空,单表示已经完结了,因为可能被删除了\n\t\t\t\tcellStringString = tempCell.getStringCellValue();\n\t\t\t\tif (StringUtils.isBlank(cellStringString)&&index>=lastCellNum) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tthrow new ExcelExportException(\"for each 当中存在空字符串,请检查模板\");\n\t\t\t}\n\t\t\t//--end--date：2020/09/18---for：增加列数判断，防止提前跳出\n\t\t\t// 把读取过的cell 置为空\n\t\t\ttempCell.setCellValue(\"\");\n\t\t\tif (cellStringString.contains(END_STR)) {\n\t\t\t\tcolumns.add(new ExcelTemplateParams(cellStringString.trim().replace(END_STR, \"\"), tempCell.getCellStyle(), tempCell.getRow().getHeight()));\n\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tif (cellStringString.trim().contains(teplateParams.getTempParams())) {\n\t\t\t\t\tcolumns.add(new ExcelTemplateParams(cellStringString.trim(), tempCell.getCellStyle(), tempCell.getRow().getHeight()));\n\t\t\t\t}else if(cellStringString.trim().equals(EMPTY)){\n\t\t\t\t\t//可能是合并的单元格，允许空数据的设置\n\t\t\t\t\tcolumns.add(new ExcelTemplateParams(EMPTY, tempCell.getCellStyle(), tempCell.getRow().getHeight()));\n\t\t\t\t} else {\n\t\t\t\t\t// 最后一行被删除了\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\t\treturn columns;\n\t}\n\n\t/**\n\t * 对导出序列进行排序和塞选\n\t *\n\t * @param excelParams\n\t * @param titlemap\n\t * @return\n\t */\n\tprivate void sortAndFilterExportField(List<ExcelExportEntity> excelParams, Map<String, Integer> titlemap) {\n\t\tfor (int i = excelParams.size() - 1; i >= 0; i--) {\n\t\t\tif (excelParams.get(i).getList() != null && excelParams.get(i).getList().size() > 0) {\n\t\t\t\tsortAndFilterExportField(excelParams.get(i).getList(), titlemap);\n\t\t\t\tif (excelParams.get(i).getList().size() == 0) {\n\t\t\t\t\texcelParams.remove(i);\n\t\t\t\t} else {\n\t\t\t\t\texcelParams.get(i).setOrderNum(i);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (titlemap.containsKey(excelParams.get(i).getName())) {\n\t\t\t\t\texcelParams.get(i).setOrderNum(i);\n\t\t\t\t} else {\n\t\t\t\t\texcelParams.remove(i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsortAllParams(excelParams);\n\t}\n\n\t//-----------------update-begin-author:liusq---date:20220527--for: 以下方法是模板导出列循环功能新增的方法 ---\n\t/**\n\t * 先进行列的循环,因为涉及很多数据\n\t *\n\t * @param sheet\n\t * @param map\n\t */\n\tprivate void colForeach(Sheet sheet, Map<String, Object> map) throws Exception {\n\t\tRow  row   = null;\n\t\tCell cell  = null;\n\t\tint  index = 0;\n\t\twhile (index <= sheet.getLastRowNum()) {\n\t\t\trow = sheet.getRow(index++);\n\t\t\tif (row == null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {\n\t\t\t\tcell = row.getCell(i);\n\t\t\t\tif (row.getCell(i) != null && (cell.getCellType() == CellType.STRING\n\t\t\t\t\t\t|| cell.getCellType() == CellType.NUMERIC)) {\n\t\t\t\t\tString text = PoiCellUtil.getCellValue(cell);\n\t\t\t\t\tif (text.contains(FOREACH_COL) || text.contains(FOREACH_COL_VALUE)) {\n\t\t\t\t\t\tforeachCol(cell, map, text);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 循环列表\n\t *\n\t * @param cell\n\t * @param map\n\t * @param name\n\t * @throws Exception\n\t */\n\tprivate void foreachCol(Cell cell, Map<String, Object> map, String name) throws Exception {\n\t\tboolean isCreate = name.contains(FOREACH_COL_VALUE);\n\t\tname = name.replace(FOREACH_COL_VALUE, EMPTY).replace(FOREACH_COL, EMPTY).replace(START_STR,\n\t\t\t\tEMPTY);\n\t\tString[]      keys  = name.replaceAll(\"\\\\s{1,}\", \" \").trim().split(\" \");\n\t\tCollection<?> datas = (Collection<?>) PoiPublicUtil.getParamsValue(keys[0], map);\n\t\tObject[] columnsInfo = getAllDataColumns(cell, name.replace(keys[0], EMPTY),\n\t\t\t\tmergedRegionHelper);\n\t\tif (datas == null) {\n\t\t\treturn;\n\t\t}\n\t\tIterator<?> its     = datas.iterator();\n\t\tint         rowspan = (Integer) columnsInfo[0], colspan = (Integer) columnsInfo[1];\n\t\t@SuppressWarnings(\"unchecked\")\n\t\tList<ExcelForEachParams> columns = (List<ExcelForEachParams>) columnsInfo[2];\n\t\twhile (its.hasNext()) {\n\t\t\tObject t = its.next();\n\t\t\tsetForeachRowCellValue(true, cell.getRow(), cell.getColumnIndex(), t, columns, map,\n\t\t\t\t\trowspan, colspan, mergedRegionHelper);\n\t\t\tif (cell.getRow().getCell(cell.getColumnIndex() + colspan) == null) {\n\t\t\t\tcell.getRow().createCell(cell.getColumnIndex() + colspan);\n\t\t\t}\n\t\t\tcell = cell.getRow().getCell(cell.getColumnIndex() + colspan);\n\t\t}\n\t\tif (isCreate) {\n\t\t\tcell = cell.getRow().getCell(cell.getColumnIndex() - 1);\n\t\t\tcell.setCellValue(cell.getStringCellValue() + END_STR);\n\t\t}\n\t}\n\t/**\n\t * 循环迭代创建,遍历row\n\t *\n\t * @param isCreate\n\t * @param row\n\t * @param columnIndex\n\t * @param t\n\t * @param columns\n\t * @param map\n\t * @param rowspan\n\t * @param colspan\n\t * @param mergedRegionHelper\n\t * @return rowSize, cellSize\n\t * @throws Exception\n\t */\n\tprivate int[] setForeachRowCellValue(boolean isCreate, Row row, int columnIndex, Object t,\n\t\t\t\t\t\t\t\t\t\t List<ExcelForEachParams> columns, Map<String, Object> map,\n\t\t\t\t\t\t\t\t\t\t int rowspan, int colspan,\n\t\t\t\t\t\t\t\t\t\t MergedRegionHelper mergedRegionHelper) throws Exception {\n\t\tcreateRowCellSetStyle(row, columnIndex, columns, rowspan, colspan);\n\t\t//填写数据\n\t\tExcelForEachParams params;\n\t\tint                loopSize = 1;\n\t\tint                loopCi   = 1;\n\t\trow = row.getSheet().getRow(row.getRowNum() - rowspan + 1);\n\t\tfor (int k = 0; k < rowspan; k++) {\n\t\t\tint ci = columnIndex;\n\t\t\trow.setHeight(getMaxHeight(k, colspan, columns));\n\t\t\tfor (int i = 0; i < colspan && i < columns.size(); i++) {\n\t\t\t\tboolean isNumber = false;\n\t\t\t\tparams = columns.get(colspan * k + i);\n\t\t\t\ttempCreateCellSet.add(row.getRowNum() + \"_\" + (ci));\n\t\t\t\tif (params == null) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (StringUtils.isEmpty(params.getName())\n\t\t\t\t\t\t&& StringUtils.isEmpty(params.getConstValue())) {\n\t\t\t\t\trow.getCell(ci).setCellStyle(params.getCellStyle());\n\t\t\t\t\tci = ci + params.getColspan();\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString val;\n\t\t\t\tObject obj = null;\n\t\t\t\t//是不是常量\n\t\t\t\tString tempStr = params.getName();\n\t\t\t\tif (StringUtils.isEmpty(params.getName())) {\n\t\t\t\t\tval = params.getConstValue();\n\t\t\t\t} else {\n\t\t\t\t\tif (isHasSymbol(tempStr, NUMBER_SYMBOL)) {\n\t\t\t\t\t\tisNumber = true;\n\t\t\t\t\t\ttempStr = tempStr.replaceFirst(NUMBER_SYMBOL, \"\");\n\t\t\t\t\t}\n\t\t\t\t\tmap.put(teplateParams.getTempParams(), t);\n\t\t\t\t\tboolean isDict = false;\n\t\t\t\t\tString  dict   = null;\n\t\t\t\t\tif (isHasSymbol(tempStr, DICT_HANDLER)) {\n\t\t\t\t\t\tisDict = true;\n\t\t\t\t\t\tdict = tempStr.substring(tempStr.indexOf(DICT_HANDLER) + 5).split(\";\")[0];\n\t\t\t\t\t\ttempStr = tempStr.replaceFirst(DICT_HANDLER, \"\");\n\t\t\t\t\t\ttempStr = tempStr.replaceFirst(dict + \";\", \"\");\n\t\t\t\t\t}\n\t\t\t\t\tobj = eval(tempStr, map);\n\t\t\t\t\tif (isDict && !(obj instanceof Collection)) {\n\t\t\t\t\t\tobj = dictHandler.toName(dict, t, tempStr, obj);\n\t\t\t\t\t}\n\t\t\t\t\tval = obj.toString();\n\t\t\t\t}\n\t\t\t\tif (obj != null && obj instanceof Collection) {\n\t\t\t\t\t// 需要找到哪一级别是集合 ,方便后面的replace\n\t\t\t\t\tString collectName = evalFindName(tempStr, map);\n\t\t\t\t\tint[] loop = setForEachLoopRowCellValue(row, ci, (Collection) obj, columns,\n\t\t\t\t\t\t\tparams, map, rowspan, colspan, mergedRegionHelper, collectName);\n\t\t\t\t\tloopSize = Math.max(loopSize, loop[0]);\n\t\t\t\t\ti += loop[1] - 1;\n\t\t\t\t\tci = loop[2] - params.getColspan();\n\t\t\t\t} else if (obj != null && obj instanceof ImageEntity) {\n\t\t\t\t\tImageEntity img = (ImageEntity) obj;\n\t\t\t\t\trow.getCell(ci).setCellValue(\"\");\n\t\t\t\t\tif (img.getRowspan() > 1 || img.getColspan() > 1) {\n\t\t\t\t\t\timg.setHeight(0);\n\t\t\t\t\t\trow.getCell(ci).getSheet().addMergedRegion(new CellRangeAddress(row.getCell(ci).getRowIndex(),\n\t\t\t\t\t\t\t\trow.getCell(ci).getRowIndex() + img.getRowspan() - 1, row.getCell(ci).getColumnIndex(), row.getCell(ci).getColumnIndex() + img.getColspan() - 1));\n\t\t\t\t\t}\n\t\t\t\t\tcreateImageCell(row.getCell(ci), img.getHeight(), img.getRowspan(), img.getColspan(), img.getUrl(), img.getData(), img.getScaleModeEnum());\n\t\t\t\t} else if (isNumber && StringUtils.isNotEmpty(val)) {\n\t\t\t\t\trow.getCell(ci).setCellValue(Double.parseDouble(val));\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\trow.getCell(ci).setCellValue(val);\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (params.getCellStyle() != null) {\n\t\t\t\t\trow.getCell(ci).setCellStyle(params.getCellStyle());\n\t\t\t\t}\n\t\t\t\t//如果合并单元格,就把这个单元格的样式和之前的保持一致\n\t\t\t\tsetMergedRegionStyle(row, ci, params);\n\t\t\t\t//合并对应单元格\n\t\t\t\t//update-begin-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格，第二个表格读取错误 ---\n\t\t\t\tboolean isNeedMerge = (params.getRowspan() != 1 || params.getColspan() != 1)\n\t\t\t\t\t\t&& !mergedRegionHelper.isMergedRegion(row.getRowNum() + 1, ci);\n\t\t\t\t//update-end-author:liusq---date:20221103--for: [issues/4142]exlce模板导出如果模板中有多个合并单元格的循环表格，第二个表格读取错误 ---\n\t\t\t\tif (isNeedMerge) {\n\t\t\t\t\tPoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),\n\t\t\t\t\t\t\trow.getRowNum() + params.getRowspan() - 1, ci,\n\t\t\t\t\t\t\tci + params.getColspan() - 1);\n\t\t\t\t}\n\t\t\t\tci = ci + params.getColspan();\n\t\t\t}\n\t\t\tloopCi = Math.max(loopCi, ci);\n\t\t\t// 需要把需要合并的单元格合并了  --- 不是集合的栏位合并了\n\t\t\tif (loopSize > 1) {\n\t\t\t\thandlerLoopMergedRegion(row, columnIndex, columns, loopSize);\n\t\t\t}\n\t\t\trow = row.getSheet().getRow(row.getRowNum() + 1);\n\t\t}\n\t\treturn new int[]{loopSize, loopCi};\n\t}\n\t/**\n\t * 图片类型的Cell\n\t */\n\tpublic void createImageCell(Cell cell, double height, int rowspan, int colspan,\n\t\t\t\t\t\t\t\tString imagePath, byte[] data) throws Exception {\n\t\tcreateImageCell(cell, height, rowspan, colspan, imagePath, data, ImageScaleMode.STRETCH);\n\t}\n\t\n\t/**\n\t * 图片类型的Cell（带缩放功能）\n     *\n     * @param cell\n     * @param height\n     * @param rowspan\n     * @param colspan\n     * @param imagePath\n     * @param data\n     * @param scaleMode 缩放模式：0=拉伸填充, 1=等比例缩放适应, 2=不缩放（原始大小） for [issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式\n     * @throws Exception\n     * @author chenrui\n     * @date 2025/10/28 17:36\n     * @deprecated 使用 {@link #createImageCell(Cell, double, int, int, String, byte[], ImageScaleMode)} 代替\n     */\n\t@Deprecated\n\tpublic void createImageCell(Cell cell, double height, int rowspan, int colspan,\n\t\t\t\t\t\t\t\tString imagePath, byte[] data, int scaleMode) throws Exception {\n\t\tcreateImageCell(cell, height, rowspan, colspan, imagePath, data, ImageScaleMode.valueOf(scaleMode));\n\t}\n\t\n\t/**\n\t * 图片类型的Cell（带缩放功能，使用枚举）\n     *\n     * @param cell 单元格对象\n     * @param height 高度\n     * @param rowspan 行跨度\n     * @param colspan 列跨度\n     * @param imagePath 图片路径\n     * @param data 图片数据\n     * @param scaleMode 缩放模式枚举 for [issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式\n     * @throws Exception\n     * @author chenrui\n     * @date 2025/10/28 17:36\n     */\n\tpublic void createImageCell(Cell cell, double height, int rowspan, int colspan,\n\t\t\t\t\t\t\t\tString imagePath, byte[] data, ImageScaleMode scaleMode) throws Exception {\n\t\tif (height > cell.getRow().getHeight()) {\n\t\t\tcell.getRow().setHeight((short) height);\n\t\t}\n\t\tClientAnchor anchor;\n\t\tif (type.equals(ExcelType.HSSF)) {\n\t\t\tanchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan),\n\t\t\t\t\tcell.getRow().getRowNum() + rowspan);\n\t\t} else {\n\t\t\tanchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan),\n\t\t\t\t\tcell.getRow().getRowNum() + rowspan);\n\t\t}\n\t\tif (StringUtils.isNotEmpty(imagePath)) {\n\t\t\tdata = ImageCache.getImage(imagePath);\n\t\t}\n\t\tif (data != null) {\n            //update-begin---author:chenrui ---date:20251028  for：[issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式 ------------\n\t\t\t// 创建图片\n\t\t\tPicture picture = PoiExcelGraphDataUtil.getDrawingPatriarch(cell.getSheet()).createPicture(anchor,\n\t\t\t\t\tcell.getSheet().getWorkbook().addPicture(data, getImageType(data)));\n\t\t\t\n\t\t\t// 根据缩放模式处理图片\n\t\t\tif (scaleMode == ImageScaleMode.FIT) {\n\t\t\t\t// 等比例缩放适应单元格\n\t\t\t\tpicture.resize();\n\t\t\t\tpicture.resize(getImageScale(cell, rowspan, colspan, data));\n\t\t\t} else if (scaleMode == ImageScaleMode.ORIGINAL) {\n\t\t\t\t// 不缩放，保持原始大小\n\t\t\t\tpicture.resize();\n\t\t\t}\n            //update-end---author:chenrui ---date:20251028  for：[issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式 ------------\n\t\t}\n\t}\n\t\n\t/**\n\t * 获取图片缩放比例，确保图片等比例适应单元格大小\n     * for [issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式\n\t * @param cell 单元格对象\n\t * @param rowspan 行跨度\n\t * @param colspan 列跨度\n\t * @param data 图片数据\n\t * @return\n     * @author chenrui\n     * @date 2025/10/28 17:38\n     */\n\tprivate double getImageScale(Cell cell, int rowspan, int colspan, byte[] data) {\n\t\ttry {\n\t\t\tBufferedImage image = ImageIO.read(new java.io.ByteArrayInputStream(data));\n\t\t\t\n\t\t\tint imageWidth = image.getWidth();\n\t\t\tint imageHeight = image.getHeight();\n\t\t\t\n\t\t\t// 获取单元格的实际尺寸（像素）\n\t\t\tdouble cellWidth = 0;\n\t\t\tfor (int i = 0; i < colspan; i++) {\n\t\t\t\tcellWidth += cell.getSheet().getColumnWidthInPixels(cell.getColumnIndex() + i);\n\t\t\t}\n\t\t\t\n\t\t\tdouble cellHeight = 0;\n\t\t\tfor (int i = 0; i < rowspan; i++) {\n\t\t\t\tRow row = cell.getSheet().getRow(cell.getRowIndex() + i);\n\t\t\t\tif (row != null) {\n\t\t\t\t\tcellHeight += row.getHeightInPoints() * 96.0 / 72.0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\t// 如果图片或单元格尺寸无效，返回默认缩放比例\n\t\t\tif (imageWidth <= 0 || imageHeight <= 0 || cellWidth <= 0 || cellHeight <= 0) {\n\t\t\t\treturn 1.0;\n\t\t\t}\n\t\t\t\n\t\t\t// 计算宽度和高度的缩放比例\n\t\t\tdouble widthScale = cellWidth / imageWidth;\n\t\t\tdouble heightScale = cellHeight / imageHeight;\n\t\t\t\n\t\t\t// 取较小的缩放比例，确保图片完全适配在单元格内（等比例缩放）\n\t\t\tdouble scale = Math.min(widthScale, heightScale);\n\t\t\t\n\t\t\t// 确保缩放比例不超过1.0（不放大图片，只缩小）\n\t\t\treturn Math.min(scale, 1.0);\n\t\t\t\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.warn(\"创建图片锚点失败: \" + e.getMessage());\n\t\t\treturn 1.0;\n\t\t}\n\t}\n\t\n\t/**\n\t * 处理内循环\n\t *\n\t * @param row\n\t * @param columnIndex\n\t * @param obj\n\t * @param columns\n\t * @param params\n\t * @param map\n\t * @param rowspan\n\t * @param colspan\n\t * @param mergedRegionHelper\n\t * @param collectName\n\t * @return [rowNums, columnsNums, ciIndex]\n\t * @throws Exception\n\t */\n\tprivate int[] setForEachLoopRowCellValue(Row row, int columnIndex, Collection obj, List<ExcelForEachParams> columns,\n\t\t\t\t\t\t\t\t\t\t\t ExcelForEachParams params, Map<String, Object> map,\n\t\t\t\t\t\t\t\t\t\t\t int rowspan, int colspan,\n\t\t\t\t\t\t\t\t\t\t\t MergedRegionHelper mergedRegionHelper, String collectName) throws Exception {\n\n\t\t//多个一起遍历 -去掉第一层 把所有的数据遍历一遍\n\t\t//STEP 1拿到所有的和当前一样项目的字段\n\t\tList<ExcelForEachParams> temp    = getLoopEachParams(columns, columnIndex, collectName);\n\t\tIterator<?>              its     = obj.iterator();\n\t\tRow                      tempRow = row;\n\t\tint                      nums    = 0;\n\t\tint                      ci      = columnIndex;\n\t\twhile (its.hasNext()) {\n\t\t\tObject data = its.next();\n\t\t\tmap.put(\"loop_\" + columnIndex, data);\n\t\t\tint[] loopArr = setForeachRowCellValue(false, tempRow, columnIndex, data, temp, map, rowspan,\n\t\t\t\t\tcolspan, mergedRegionHelper);\n\t\t\tnums += loopArr[0];\n\t\t\tci = Math.max(ci, loopArr[1]);\n\t\t\tmap.remove(\"loop_\" + columnIndex);\n\t\t\ttempRow = createRow(tempRow.getRowNum() + loopArr[0], row.getSheet(), false, rowspan);\n\t\t}\n\t\tfor (int i = 0; i < temp.size(); i++) {\n\t\t\ttemp.get(i).setName(temp.get(i).getTempName().pop());\n\t\t\t//都是集合\n\t\t\ttemp.get(i).setCollectCell(true);\n\n\t\t}\n\t\treturn new int[]{nums, temp.size(), ci};\n\t}\n\t/**\n\t * 创建并返回第一个Row\n\t *\n\t * @param sheet\n\t * @param rowIndex\n\t * @param isCreate\n\t * @param rows\n\t * @return\n\t */\n\tprivate Row createRow(int rowIndex, Sheet sheet, boolean isCreate, int rows) {\n\t\tfor (int i = 0; i < rows; i++) {\n\t\t\tif (isCreate) {\n\t\t\t\tsheet.createRow(rowIndex++);\n\t\t\t} else if (sheet.getRow(rowIndex++) == null) {\n\t\t\t\tsheet.createRow(rowIndex - 1);\n\t\t\t}\n\t\t}\n\t\treturn sheet.getRow(rowIndex - rows);\n\t}\n\t/**\n\t * 根据 当前是集合的信息,把后面整个集合的迭代获取出来,并替换掉集合的前缀方便后面取数\n\t *\n\t * @param columns\n\t * @param columnIndex\n\t * @param collectName\n\t * @return\n\t */\n\tprivate List<ExcelForEachParams> getLoopEachParams(List<ExcelForEachParams> columns, int columnIndex, String collectName) {\n\t\tList<ExcelForEachParams> temp = new ArrayList<>();\n\t\tfor (int i = 0; i < columns.size(); i++) {\n\t\t\t//先置为不是集合\n\t\t\tcolumns.get(i).setCollectCell(false);\n\t\t\tif (columns.get(i) == null || columns.get(i).getName().contains(collectName)) {\n\t\t\t\ttemp.add(columns.get(i));\n\t\t\t\tif (columns.get(i).getTempName() == null) {\n\t\t\t\t\tcolumns.get(i).setTempName(new Stack<>());\n\t\t\t\t}\n\t\t\t\tcolumns.get(i).setCollectCell(true);\n\t\t\t\tcolumns.get(i).getTempName().push(columns.get(i).getName());\n\t\t\t\tcolumns.get(i).setName(columns.get(i).getName().replace(collectName, \"loop_\" + columnIndex));\n\t\t\t}\n\t\t}\n\t\treturn temp;\n\t}\n\n\t/**\n\t * 设置行样式\n\t * @param row\n\t * @param columnIndex\n\t * @param columns\n\t * @param rowspan\n\t * @param colspan\n\t */\n\tprivate void createRowCellSetStyle(Row row, int columnIndex, List<ExcelForEachParams> columns,\n\t\t\t\t\t\t\t\t\t   int rowspan, int colspan) {\n\t\t//所有的cell创建一遍\n\t\tfor (int i = 0; i < rowspan; i++) {\n\t\t\tint size = columns.size();\n\t\t\tfor (int j = columnIndex, max = columnIndex + colspan; j < max; j++) {\n\t\t\t\tif (row.getCell(j) == null) {\n\t\t\t\t\trow.createCell(j);\n\t\t\t\t\tCellStyle style = row.getRowNum() % 2 == 0\n\t\t\t\t\t\t\t? getStyles(false,\n\t\t\t\t\t\t\tsize <= j - columnIndex ? null : columns.get(j - columnIndex))\n\t\t\t\t\t\t\t: getStyles(true,\n\t\t\t\t\t\t\tsize <= j - columnIndex ? null : columns.get(j - columnIndex));\n\t\t\t\t\t//返回的styler不为空时才使用,否则使用Excel设置的,更加推荐Excel设置的样式\n\t\t\t\t\tif (style != null) {\n\t\t\t\t\t\trow.getCell(j).setCellStyle(style);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tif (i < rowspan - 1) {\n\t\t\t\trow = row.getSheet().getRow(row.getRowNum() + 1);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 获取CellStyle\n\t * @param isSingle\n\t * @param excelForEachParams\n\t * @return\n\t */\n\tprivate CellStyle getStyles(boolean isSingle, ExcelForEachParams excelForEachParams) {\n\t\treturn excelExportStyler.getTemplateStyles(isSingle, excelForEachParams);\n\t}\n\n\t/**\n\t * 获取最大高度\n\t * @param k\n\t * @param colspan\n\t * @param columns\n\t * @return\n\t */\n\tprivate short getMaxHeight(int k, int colspan, List<ExcelForEachParams> columns) {\n\t\tshort high = columns.get(0).getHeight();\n\t\tint   n    = k;\n\t\twhile (n > 0) {\n\t\t\tif (columns.get(n * colspan).getHeight() == 0) {\n\t\t\t\tn--;\n\t\t\t} else {\n\t\t\t\thigh = columns.get(n * colspan).getHeight();\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn high;\n\t}\n\n\tprivate boolean isHasSymbol(String text, String symbol) {\n\t\treturn text.startsWith(symbol) || text.contains(\"{\" + symbol)\n\t\t\t\t|| text.contains(\" \" + symbol);\n\t}\n\t/**\n\t * 迭代把不是集合的数据都合并了\n\t *\n\t * @param row\n\t * @param columnIndex\n\t * @param columns\n\t * @param loopSize\n\t */\n\tprivate void handlerLoopMergedRegion(Row row, int columnIndex, List<ExcelForEachParams> columns, int loopSize) {\n\t\tfor (int i = 0; i < columns.size(); i++) {\n\t\t\tif (!columns.get(i).isCollectCell()) {\n\t\t\t\tPoiMergeCellUtil.addMergedRegion(row.getSheet(), row.getRowNum(),\n\t\t\t\t\t\trow.getRowNum() + loopSize - 1, columnIndex,\n\t\t\t\t\t\tcolumnIndex + columns.get(i).getColspan() - 1);\n\t\t\t}\n\t\t\tcolumnIndex = columnIndex + columns.get(i).getColspan();\n\t\t}\n\t}\n\t/**\n\t * 设置合并单元格的样式\n\t *\n\t * @param row\n\t * @param ci\n\t * @param params\n\t */\n\tprivate void setMergedRegionStyle(Row row, int ci, ExcelForEachParams params) {\n\t\t//第一行数据\n\t\tfor (int i = 1; i < params.getColspan(); i++) {\n\t\t\tif (params.getCellStyle() != null) {\n\t\t\t\trow.getCell(ci + i).setCellStyle(params.getCellStyle());\n\t\t\t}\n\t\t}\n\t\tfor (int i = 1; i < params.getRowspan(); i++) {\n\t\t\tfor (int j = 0; j < params.getColspan(); j++) {\n\t\t\t\tif (params.getCellStyle() != null) {\n\t\t\t\t\trow.getCell(ci + j).setCellStyle(params.getCellStyle());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t * 获取迭代的数据的值\n\t *\n\t * @param cell\n\t * @param name\n\t * @param mergedRegionHelper\n\t * @return\n\t */\n\tprivate Object[] getAllDataColumns(Cell cell, String name,\n\t\t\t\t\t\t\t\t\t   MergedRegionHelper mergedRegionHelper) {\n\t\tList<ExcelForEachParams> columns = new ArrayList<ExcelForEachParams>();\n\t\tcell.setCellValue(\"\");\n\t\tcolumns.add(getExcelTemplateParams(name.replace(END_STR, EMPTY), cell, mergedRegionHelper));\n\t\tint rowspan = 1, colspan = 1;\n\t\tif (!name.contains(END_STR)) {\n\t\t\tint index = cell.getColumnIndex();\n\t\t\t//保存col 的开始列\n\t\t\tint startIndex = cell.getColumnIndex();\n\t\t\tRow row        = cell.getRow();\n\t\t\twhile (index < row.getLastCellNum()) {\n\t\t\t\tint colSpan = columns.get(columns.size() - 1) != null\n\t\t\t\t\t\t? columns.get(columns.size() - 1).getColspan() : 1;\n\t\t\t\tindex += colSpan;\n\n\n\t\t\t\tfor (int i = 1; i < colSpan; i++) {\n\t\t\t\t\t//添加合并的单元格,这些单元可能不是空,但是没有值,所以也需要跳过\n\t\t\t\t\tcolumns.add(null);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcell = row.getCell(index);\n\t\t\t\t//可能是合并的单元格\n\t\t\t\tif (cell == null) {\n\t\t\t\t\t//读取是判断,跳过\n\t\t\t\t\tcolumns.add(null);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tString cellStringString;\n\t\t\t\ttry {//不允许为空 便利单元格必须有结尾和值\n\t\t\t\t\tcellStringString = cell.getStringCellValue();\n\t\t\t\t\tif (StringUtils.isBlank(cellStringString) && colspan + startIndex <= index) {\n\t\t\t\t\t\tthrow new ExcelExportException(\"for each 当中存在空字符串,请检查模板\");\n\t\t\t\t\t} else if (StringUtils.isBlank(cellStringString)\n\t\t\t\t\t\t\t&& colspan + startIndex > index) {\n\t\t\t\t\t\t//读取是判断,跳过,数据为空,但是不是第一次读这一列,所以可以跳过\n\t\t\t\t\t\tcolumns.add(new ExcelForEachParams(null, cell.getCellStyle(), (short) 0));\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch (Exception e) {\n\t\t\t\t\tthrow new ExcelExportException(ExcelExportEnum.TEMPLATE_ERROR, e);\n\t\t\t\t}\n\t\t\t\t//把读取过的cell 置为空\n\t\t\t\tcell.setCellValue(\"\");\n\t\t\t\tif (cellStringString.contains(END_STR)) {\n\t\t\t\t\tcolumns.add(getExcelTemplateParams(cellStringString.replace(END_STR, EMPTY),\n\t\t\t\t\t\t\tcell, mergedRegionHelper));\n\t\t\t\t\t//补全缺失的cell(合并单元格后面的)\n\t\t\t\t\tint lastCellColspan = columns.get(columns.size() - 1).getColspan();\n\t\t\t\t\tfor (int i = 1; i < lastCellColspan; i++) {\n\t\t\t\t\t\t//添加合并的单元格,这些单元可能不是空,但是没有值,所以也需要跳过\n\t\t\t\t\t\tcolumns.add(null);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (cellStringString.contains(WRAP)) {\n\t\t\t\t\tcolumns.add(getExcelTemplateParams(cellStringString.replace(WRAP, EMPTY), cell,\n\t\t\t\t\t\t\tmergedRegionHelper));\n\t\t\t\t\t//发现换行符,执行换行操作\n\t\t\t\t\tcolspan = index - startIndex + 1;\n\t\t\t\t\tindex = startIndex - columns.get(columns.size() - 1).getColspan();\n\t\t\t\t\trow = row.getSheet().getRow(row.getRowNum() + 1);\n\t\t\t\t\trowspan++;\n\t\t\t\t} else {\n\t\t\t\t\tcolumns.add(getExcelTemplateParams(cellStringString.replace(WRAP, EMPTY), cell,\n\t\t\t\t\t\t\tmergedRegionHelper));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcolspan = 0;\n\t\tfor (int i = 0; i < columns.size(); i++) {\n\t\t\tcolspan += columns.get(i) != null ? columns.get(i).getColspan() : 0;\n\t\t}\n\t\tcolspan = colspan / rowspan;\n\t\treturn new Object[]{rowspan, colspan, columns};\n\t}\n\t/**\n\t * 获取模板参数\n\t *\n\t * @param name\n\t * @param cell\n\t * @param mergedRegionHelper\n\t * @return\n\t */\n\tprivate ExcelForEachParams getExcelTemplateParams(String name, Cell cell,\n\t\t\t\t\t\t\t\t\t\t\t\t\t  MergedRegionHelper mergedRegionHelper) {\n\t\tname = name.trim();\n\t\tExcelForEachParams params = new ExcelForEachParams(name, cell.getCellStyle(),\n\t\t\t\tcell.getRow().getHeight());\n\t\t//判断是不是常量\n\t\tif (name.startsWith(CONST) && name.endsWith(CONST)) {\n\t\t\tparams.setName(null);\n\t\t\tparams.setConstValue(name.substring(1, name.length() - 1));\n\t\t}\n\t\t//判断是不是空\n\t\tif (NULL.equals(name)) {\n\t\t\tparams.setName(null);\n\t\t\tparams.setConstValue(EMPTY);\n\t\t}\n\t\t//获取合并单元格的数据\n\t\tif (mergedRegionHelper.isMergedRegion(cell.getRowIndex() + 1, cell.getColumnIndex())) {\n\t\t\tInteger[] colAndrow = mergedRegionHelper.getRowAndColSpan(cell.getRowIndex() + 1,\n\t\t\t\t\tcell.getColumnIndex());\n\t\t\tparams.setRowspan(colAndrow[0]);\n\t\t\tparams.setColspan(colAndrow[1]);\n\t\t}\n\t\treturn params;\n\t}\n\t//-----------------update-end-author:liusq---date:20220527--for: 以上方法是模板导出列循环功能新增的方法 ---\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/builder/ExcelChartBuildService.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.builder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.ss.usermodel.ClientAnchor;\nimport org.apache.poi.ss.usermodel.Drawing;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.usermodel.Workbook;\n// POI 5.x Chart API 已重构，以下导入暂时注释\n// import org.apache.poi.ss.usermodel.Chart;\n// import org.apache.poi.ss.usermodel.charts.AxisCrosses;\n// import org.apache.poi.ss.usermodel.charts.AxisPosition;\n// import org.apache.poi.ss.usermodel.charts.ChartAxis;\n// import org.apache.poi.ss.usermodel.charts.ChartDataSource;\n// import org.apache.poi.ss.usermodel.charts.ChartLegend;\n// import org.apache.poi.ss.usermodel.charts.DataSources;\n// import org.apache.poi.ss.usermodel.charts.LegendPosition;\n// import org.apache.poi.ss.usermodel.charts.LineChartData;\n// import org.apache.poi.ss.usermodel.charts.ScatterChartData;\n// import org.apache.poi.ss.usermodel.charts.ValueAxis;\nimport org.apache.poi.ss.util.CellRangeAddress;\n\nimport org.jeecgframework.poi.excel.graph.constant.ExcelGraphElementType;\nimport org.jeecgframework.poi.excel.graph.constant.ExcelGraphType;\nimport org.jeecgframework.poi.excel.graph.entity.ExcelGraph;\nimport org.jeecgframework.poi.excel.graph.entity.ExcelGraphElement;\nimport org.jeecgframework.poi.excel.graph.entity.ExcelTitleCell;\nimport org.jeecgframework.poi.util.PoiCellUtil;\nimport org.jeecgframework.poi.util.PoiExcelGraphDataUtil;\n\n/**\n * @Description Excel图表功能\n * @author liusq\n * @data 2022年1月4号\n * @deprecated Chart API 在 POI 5.x 中已被完全重构，此类暂时不支持 POI 5.x\n * 如需使用图表功能，请降级到 POI 4.x 或等待后续版本适配\n */\n@Deprecated\npublic class ExcelChartBuildService\n{\n\t/**\n\t * \n\t * @param workbook\n\t * @param graphList\n\t * @param build 通过实时数据行来重新计算图形定义\n\t * @param append\n\t * @deprecated Chart API 在 POI 5.x 中已被完全重构，暂不支持\n\t */\n\t@Deprecated\n\tpublic static void createExcelChart(Workbook workbook, List<ExcelGraph> graphList, Boolean build, Boolean append)\n\t{\n\t\tthrow new UnsupportedOperationException(\"Chart API 在 POI 5.x 中已被完全重构，此功能暂不支持。请降级到 POI 4.x 或等待后续版本适配。\");\n\t}\n\t\n\t/**\n\t * 构建基础图形\n\t * @param drawing \n\t * @param anchor\n\t * @param dataSourceSheet\n\t * @param graph\n\t * @deprecated Chart API 在 POI 5.x 中已被完全重构，暂不支持\n\t */\n\t@Deprecated\n\tprivate static void buildExcelChart(Drawing drawing,ClientAnchor anchor,Sheet dataSourceSheet,ExcelGraph graph){\n\t\tthrow new UnsupportedOperationException(\"Chart API 在 POI 5.x 中已被完全重构，此功能暂不支持。\");\n\t\t/* POI 5.x Chart API 已重构，以下代码暂时注释\n\t\tChart chart = null;\n\t\t// TODO  图表没有成功\n\t\t//drawing.createChart(anchor);\n\t\tChartLegend legend = chart.getOrCreateLegend();\n\t\tlegend.setPosition(LegendPosition.TOP_RIGHT);\n\t\t\n\t\tChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);\n        ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);\n        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);\n        ExcelGraphElement categoryElement=graph.getCategory();\n        \n        ChartDataSource categoryChart;\n        if(categoryElement!=null&& categoryElement.getElementType().equals(ExcelGraphElementType.STRING_TYPE)){\n        \tcategoryChart=DataSources.fromStringCellRange(dataSourceSheet, new CellRangeAddress(categoryElement.getStartRowNum(),categoryElement.getEndRowNum(),categoryElement.getStartColNum(),categoryElement.getEndColNum()));\n        }else{\n        \tcategoryChart=DataSources.fromNumericCellRange(dataSourceSheet, new CellRangeAddress(categoryElement.getStartRowNum(),categoryElement.getEndRowNum(),categoryElement.getStartColNum(),categoryElement.getEndColNum()));\n        }\n        \n        List<ExcelGraphElement> valueList=graph.getValueList();\n        List<ChartDataSource<Number>> chartValueList= new ArrayList<>();\n        if(valueList!=null&&valueList.size()>0){\n        \tfor(ExcelGraphElement ele:valueList){\n        \t\tChartDataSource<Number> source=DataSources.fromNumericCellRange(dataSourceSheet, new CellRangeAddress(ele.getStartRowNum(),ele.getEndRowNum(),ele.getStartColNum(),ele.getEndColNum()));\n        \t\tchartValueList.add(source);\n        \t}\n        }\n        \n\t\tif(graph.getGraphType().equals(ExcelGraphType.LINE_CHART)){\n\t\t\tLineChartData data = chart.getChartDataFactory().createLineChartData();\n\t\t\tbuildLineChartData(data, categoryChart, chartValueList, graph.getTitle());\n\t\t\tchart.plot(data, bottomAxis, leftAxis);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tScatterChartData data=chart.getChartDataFactory().createScatterChartData();\n\t\t\tbuildScatterChartData(data, categoryChart, chartValueList,graph.getTitle());\n\t\t\tchart.plot(data, bottomAxis, leftAxis);\n\t\t} \n\t\t*/\n\t}\n\t\n\t\n\t\n\t\n\t/**\n\t * 构建多个图形对象\n\t * @param dataSourceSheet\n\t * @param tragetSheet\n\t * @param graphList\n\t */\n\tprivate static void buildExcelChart(Sheet dataSourceSheet,Sheet tragetSheet,List<ExcelGraph> graphList){\n\t\tint len=graphList.size();\n\t\tif(len==1)\n\t\t{\n\t\t\tbuildExcelChart(dataSourceSheet, tragetSheet, graphList.get(0));\n\t\t}\n\t\telse\n\t\t{\n\t\t\tint drawStart=0;\n\t\t\tint drawEnd=20;\n\t\t\tDrawing drawing = PoiExcelGraphDataUtil.getDrawingPatriarch(tragetSheet);\n\t\t\tfor(int i=0;i<len;i++){\n\t\t\t\tExcelGraph graph=graphList.get(i);\n\t\t\t\tClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, drawStart, 15, drawEnd);\n\t\t\t\tbuildExcelChart(drawing, anchor, dataSourceSheet, graph);\n\t\t\t\tdrawStart=drawStart+drawEnd;\n\t\t\t\tdrawEnd=drawEnd+drawEnd;\n\t\t\t}\n\t\t}\n\t}\n\t\n\t\n\t\n\t\n\t/**\n\t * 构建图形对象\n\t * @param dataSourceSheet\n\t * @param tragetSheet\n\t * @param graph\n\t */\n\tprivate static void buildExcelChart(Sheet dataSourceSheet,Sheet tragetSheet,ExcelGraph graph){\n\t\tDrawing drawing = PoiExcelGraphDataUtil.getDrawingPatriarch(tragetSheet);\n\t\tClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 0, 15, 20);\n\t\tbuildExcelChart(drawing, anchor, dataSourceSheet, graph);\n\t}\n\t\n\t\n\t\n\t\n\t/**\n\t * 构建Title\n\t * @param sheet\n\t * @param graph\n\t */\n\tprivate static void buildTitle(Sheet sheet,ExcelGraph graph){\n\t\tint cellTitleLen=graph.getTitleCell().size();\n\t\tint titleLen=graph.getTitle().size();\n\t\tif(titleLen>0){\n\t\t\t\n\t\t}else{\n\t\t\tfor(int i=0;i<cellTitleLen;i++){\n\t\t\t\tExcelTitleCell titleCell=graph.getTitleCell().get(i);\n\t\t\t\tif(titleCell!=null){\n\t\t\t\t\tgraph.getTitle().add(PoiCellUtil.getCellValue(sheet,titleCell.getRow(),titleCell.getCol()));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * 构建Title\n\t * @param sheet\n\t * @param graphList\n\t */\n\tprivate static void buildTitle(Sheet sheet,List<ExcelGraph> graphList){\n\t\tif(graphList!=null&&graphList.size()>0){\n\t\t\tfor(ExcelGraph graph:graphList){\n\t\t\t\tif(graph!=null)\n\t\t\t\t{\n\t\t\t\t\tbuildTitle(sheet, graph);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t\n\t/**\n\t * \n\t * @param data\n\t * @param categoryChart\n\t * @param chartValueList\n\t * @param title\n\t * @deprecated Chart API 在 POI 5.x 中已被完全重构，暂不支持\n\t */\n\t@Deprecated\n\tprivate static void buildLineChartData(Object data, Object categoryChart, List chartValueList, List<String> title){\n\t\tthrow new UnsupportedOperationException(\"Chart API 在 POI 5.x 中已被完全重构，此功能暂不支持。\");\n\t\t/* POI 5.x Chart API 已重构，以下代码暂时注释\n\t\tif(chartValueList.size()==title.size())\n\t\t{\n\t\t\tint len=title.size();\n\t\t\tfor(int i=0;i<len;i++){\n\t\t\t    //TODO 更新版本\n\t\t\t\t//data.addSerie(categoryChart, chartValueList.get(i)).setTitle(title.get(i));\n\t\t\t}\n\t\t}\t\n\t\telse\n\t\t{\n\t\t\tint i=0;\n\t\t\tfor(ChartDataSource<Number> source:chartValueList){\n\t\t\t\tString temp_title=title.get(i);\n\t\t\t\tif(StringUtils.isNotBlank(temp_title)){\n\t\t\t\t\t//data.addSerie(categoryChart, source).setTitle(_title);\n\t\t\t\t}else{\n\t\t\t\t\t//data.addSerie(categoryChart, source);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t*/\n\t}\n\t\n\t/**\n\t * \n\t * @param data\n\t * @param categoryChart\n\t * @param chartValueList\n\t * @param title\n\t * @deprecated Chart API 在 POI 5.x 中已被完全重构，暂不支持\n\t */\n\t@Deprecated\n\tprivate static void buildScatterChartData(Object data, Object categoryChart, List chartValueList, List<String> title){\n\t\tthrow new UnsupportedOperationException(\"Chart API 在 POI 5.x 中已被完全重构，此功能暂不支持。\");\n\t\t/* POI 5.x Chart API 已重构，以下代码暂时注释\n\t\tif(chartValueList.size()==title.size())\n\t\t{\n\t\t\tint len=title.size();\n\t\t\tfor(int i=0;i<len;i++){\n\t\t\t\tdata.addSerie(categoryChart, chartValueList.get(i)).setTitle(title.get(i));\n\t\t\t}\n\t\t}\t\n\t\telse\n\t\t{\n\t\t\tint i=0;\n\t\t\tfor(ChartDataSource<Number> source:chartValueList){\n\t\t\t\tString temp_title=title.get(i);\n\t\t\t\tif(StringUtils.isNotBlank(temp_title)){\n\t\t\t\t\tdata.addSerie(categoryChart, source).setTitle(temp_title);\n\t\t\t\t}else{\n\t\t\t\t\tdata.addSerie(categoryChart, source);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t*/\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/constant/ExcelGraphElementType.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.constant;\n\n/**\n * @Description 定义元素类型\n * @author liusq\n * @date  2022年1月4号\n */\npublic interface ExcelGraphElementType\n{\n\tpublic static final Integer STRING_TYPE =1;\n\tpublic static final Integer NUMERIC_TYPE =2;\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/constant/ExcelGraphType.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.constant;\n\n/**\n * @Description 定义图形类型\n * @author liusq\n * @date  2022年1月4号\n */\npublic interface ExcelGraphType\n{\n\tpublic static final Integer LINE_CHART =1;\n\tpublic static final Integer SCATTER_CHART =2;\n\t\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelGraph.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.entity;\n\nimport java.util.List;\n\n/**\n * @Description Excel 图形构造服务\n * @author liusq\n * @date  2022年1月4号\n */\npublic interface ExcelGraph\n{\n\tpublic ExcelGraphElement getCategory();\n\tpublic List<ExcelGraphElement> getValueList();\n\tpublic Integer getGraphType();\n\tpublic List<ExcelTitleCell> getTitleCell();\n\tpublic List<String> getTitle();\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelGraphDefined.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.entity;\n\nimport org.jeecgframework.poi.excel.graph.constant.ExcelGraphType;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n\n/**\n * @Description Excel 图形构造服务\n * @author liusq\n * @date  2022年1月4号\n */\npublic class ExcelGraphDefined implements ExcelGraph\n{\n\tprivate ExcelGraphElement category;\n\tpublic List<ExcelGraphElement> valueList= new ArrayList<>();\n\tpublic List<ExcelTitleCell> titleCell= new ArrayList<>();\n\tprivate Integer graphType= ExcelGraphType.LINE_CHART;\n\tpublic List<String> title= new ArrayList<>();\n\t\n\t@Override\n    public ExcelGraphElement getCategory()\n\t{\n\t\treturn category;\n\t}\n\tpublic void setCategory(ExcelGraphElement category)\n\t{\n\t\tthis.category = category;\n\t}\n\t@Override\n    public List<ExcelGraphElement> getValueList()\n\t{\n\t\treturn valueList;\n\t}\n\tpublic void setValueList(List<ExcelGraphElement> valueList)\n\t{\n\t\tthis.valueList = valueList;\n\t}\n\n\t@Override\n    public Integer getGraphType()\n\t{\n\t\treturn graphType;\n\t}\n\tpublic void setGraphType(Integer graphType)\n\t{\n\t\tthis.graphType = graphType;\n\t}\n\t@Override\n    public List<ExcelTitleCell> getTitleCell()\n\t{\n\t\treturn titleCell;\n\t}\n\tpublic void setTitleCell(List<ExcelTitleCell> titleCell)\n\t{\n\t\tthis.titleCell = titleCell;\n\t}\n\t@Override\n    public List<String> getTitle()\n\t{\n\t\treturn title;\n\t}\n\tpublic void setTitle(List<String> title)\n\t{\n\t\tthis.title = title;\n\t}\n\t\n\t\n\t\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelGraphElement.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.entity;\n\n\nimport org.jeecgframework.poi.excel.graph.constant.ExcelGraphElementType;\n\n/**\n * @Description Excel 图形构造服务\n * @author liusq\n * @date  2022年1月4号\n */\npublic class ExcelGraphElement\n{\n\tprivate Integer startRowNum;\n\tprivate Integer endRowNum;\n\tprivate Integer startColNum;\n\tprivate Integer endColNum;\n\tprivate Integer elementType= ExcelGraphElementType.STRING_TYPE;\n\t\n\t\n\tpublic Integer getStartRowNum()\n\t{\n\t\treturn startRowNum;\n\t}\n\tpublic void setStartRowNum(Integer startRowNum)\n\t{\n\t\tthis.startRowNum = startRowNum;\n\t}\n\tpublic Integer getEndRowNum()\n\t{\n\t\treturn endRowNum;\n\t}\n\tpublic void setEndRowNum(Integer endRowNum)\n\t{\n\t\tthis.endRowNum = endRowNum;\n\t}\n\tpublic Integer getStartColNum()\n\t{\n\t\treturn startColNum;\n\t}\n\tpublic void setStartColNum(Integer startColNum)\n\t{\n\t\tthis.startColNum = startColNum;\n\t}\n\tpublic Integer getEndColNum()\n\t{\n\t\treturn endColNum;\n\t}\n\tpublic void setEndColNum(Integer endColNum)\n\t{\n\t\tthis.endColNum = endColNum;\n\t}\n\tpublic Integer getElementType()\n\t{\n\t\treturn elementType;\n\t}\n\tpublic void setElementType(Integer elementType)\n\t{\n\t\tthis.elementType = elementType;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/entity/ExcelTitleCell.java",
    "content": "/**\n * \n */\npackage org.jeecgframework.poi.excel.graph.entity;\n\n/**\n * @Description Excel 图形构造服务\n * @author liusq\n * @date  2022年1月4号\n */\npublic class ExcelTitleCell\n{\n\tprivate Integer row;\n\tprivate Integer col;\n\t\n\tpublic ExcelTitleCell(){\n\t\t\n\t}\n\t\n\tpublic ExcelTitleCell(Integer row,Integer col){\n\t\tthis.row=row;\n\t\tthis.col=col;\n\t}\n\t\n\tpublic Integer getRow()\n\t{\n\t\treturn row;\n\t}\n\tpublic void setRow(Integer row)\n\t{\n\t\tthis.row = row;\n\t}\n\tpublic Integer getCol()\n\t{\n\t\treturn col;\n\t}\n\tpublic void setCol(Integer col)\n\t{\n\t\tthis.col = col;\n\t}\n\t\n\t\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/graph/package-info.java",
    "content": "/**\n * \n */\n/**\n * @Description Excel 图形构造服务\n * @author liusq\n * @date  2022年1月4号\n */\npackage org.jeecgframework.poi.excel.graph;"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/html/ExcelToHtmlServer.java",
    "content": "package org.jeecgframework.poi.excel.html;\n\nimport java.util.Formatter;\nimport java.util.Iterator;\n\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellStyle;\nimport org.apache.poi.ss.usermodel.Row;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.html.helper.CellValueHelper;\nimport org.jeecgframework.poi.excel.html.helper.MergedRegionHelper;\nimport org.jeecgframework.poi.excel.html.helper.StylerHelper;\n\n/**\n * Excel转换成Html 服务\n * \n * @author JEECG\n * @date 2015年5月10日 上午11:41:15\n */\npublic class ExcelToHtmlServer {\n\n\tprivate Workbook wb;\n\tprivate int sheetNum;\n\tprivate int cssRandom;\n\n\t/* 是不是完成界面 */\n\tprivate boolean completeHTML;\n\tprivate Formatter out;\n\t/* 已经完成范围处理 */\n\tprivate boolean gotBounds;\n\tprivate int firstColumn;\n\tprivate int endColumn;\n\tprivate static final String COL_HEAD_CLASS = \"colHeader\";\n\t// private static final String ROW_HEAD_CLASS = \"rowHeader\";\n\n\tprivate static final String DEFAULTS_CLASS = \"excelDefaults\";\n\n\tpublic ExcelToHtmlServer(Workbook wb, boolean completeHTML, int sheetNum) {\n\t\tthis.wb = wb;\n\t\tthis.completeHTML = completeHTML;\n\t\tthis.sheetNum = sheetNum;\n\t\tcssRandom = (int) Math.ceil(Math.random() * 1000);\n\t}\n\n\tpublic String printPage() {\n\t\ttry {\n\t\t\tensureOut();\n\t\t\tif (completeHTML) {\n\t\t\t\tout.format(\"<!DOCTYPE HTML>%n\");\n\t\t\t\tout.format(\"<html>%n\");\n\t\t\t\tout.format(\"<meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html; charset=UTF-8\\\">%n\");\n\t\t\t\tout.format(\"<head>%n\");\n\t\t\t}\n\t\t\tnew StylerHelper(wb, out, sheetNum, cssRandom);\n\t\t\tif (completeHTML) {\n\t\t\t\tout.format(\"</head>%n\");\n\t\t\t\tout.format(\"<body>%n\");\n\t\t\t}\n\t\t\tprint();\n\t\t\tif (completeHTML) {\n\t\t\t\tout.format(\"</body>%n\");\n\t\t\t\tout.format(\"</html>%n\");\n\t\t\t}\n\t\t\treturn out.toString();\n\t\t} finally {\n\t\t\tif (out != null)\n\t\t\t\tout.close();\n\t\t}\n\t}\n\n\tprivate void print() {\n\t\tprintSheets();\n\t}\n\n\tprivate void ensureOut() {\n\t\tif (out == null)\n\t\t\tout = new Formatter(new StringBuilder());\n\t}\n\n\tprivate void printSheets() {\n\t\tSheet sheet = wb.getSheetAt(sheetNum);\n\t\tprintSheet(sheet);\n\t}\n\n\tprivate void printSheet(Sheet sheet) {\n\t\tout.format(\"<table class='%s' width='%s'>%n\", DEFAULTS_CLASS, getTableWidth(sheet));\n\t\tprintCols(sheet);\n\t\tprintSheetContent(sheet);\n\t\tout.format(\"</table>%n\");\n\t}\n\n\tprivate void printCols(Sheet sheet) {\n\t\t// out.format(\"<col/>%n\");\n\t\tensureColumnBounds(sheet);\n\t\tfor (int i = firstColumn; i < endColumn; i++) {\n\t\t\tout.format(\"<col style='width:%spx;' />%n\", sheet.getColumnWidth(i) / 32);\n\t\t}\n\t}\n\n\tprivate int getTableWidth(Sheet sheet) {\n\t\tensureColumnBounds(sheet);\n\t\tint width = 0;\n\t\tfor (int i = firstColumn; i < endColumn; i++) {\n\t\t\twidth = width + (sheet.getColumnWidth(i) / 32);\n\t\t}\n\t\treturn width;\n\t}\n\n\tprivate void ensureColumnBounds(Sheet sheet) {\n\t\tif (gotBounds)\n\t\t\treturn;\n\n\t\tIterator<Row> iter = sheet.rowIterator();\n\t\tfirstColumn = (iter.hasNext() ? Integer.MAX_VALUE : 0);\n\t\tendColumn = 0;\n\t\twhile (iter.hasNext()) {\n\t\t\tRow row = iter.next();\n\t\t\tshort firstCell = row.getFirstCellNum();\n\t\t\tif (firstCell >= 0) {\n\t\t\t\tfirstColumn = Math.min(firstColumn, firstCell);\n\t\t\t\tendColumn = Math.max(endColumn, row.getLastCellNum());\n\t\t\t}\n\t\t}\n\t\tgotBounds = true;\n\t}\n\n\t@SuppressWarnings(\"unused\")\n\t/**本来是用来生成 A，B 那个列名称的**/\n\tprivate void printColumnHeads(Sheet sheet) {\n\t\tout.format(\"<thead>%n\");\n\t\tout.format(\"  <tr class=%s>%n\", COL_HEAD_CLASS);\n\t\tout.format(\"    <th class=%s>&#x25CA;</th>%n\", COL_HEAD_CLASS);\n\t\tStringBuilder colName = new StringBuilder();\n\t\tfor (int i = firstColumn; i < endColumn; i++) {\n\t\t\tcolName.setLength(0);\n\t\t\tint cnum = i;\n\t\t\tdo {\n\t\t\t\tcolName.insert(0, (char) ('A' + cnum % 26));\n\t\t\t\tcnum /= 26;\n\t\t\t} while (cnum > 0);\n\t\t\tout.format(\"    <th class=%s>%s</th>%n\", COL_HEAD_CLASS, colName);\n\t\t}\n\t\tout.format(\"  </tr>%n\");\n\t\tout.format(\"</thead>%n\");\n\t}\n\n\tprivate void printSheetContent(Sheet sheet) {\n\t\t// printColumnHeads(sheet);\n\t\tMergedRegionHelper mergedRegionHelper = new MergedRegionHelper(sheet);\n\t\tCellValueHelper cellValueHelper = new CellValueHelper(wb, cssRandom);\n\t\tout.format(\"<tbody>%n\");\n\t\tIterator<Row> rows = sheet.rowIterator();\n\t\tint rowIndex = 1;\n\t\twhile (rows.hasNext()) {\n\t\t\tRow row = rows.next();\n\t\t\tout.format(\"  <tr style='height:%spx;'>%n\", row.getHeight() / 15);\n\t\t\t// out.format(\"    <td class='%s'>%d</td>%n\", ROW_HEAD_CLASS,\n\t\t\t// row.getRowNum() + 1);\n\t\t\tfor (int i = firstColumn; i < endColumn; i++) {\n\t\t\t\tif (mergedRegionHelper.isNeedCreate(rowIndex, i)) {\n\t\t\t\t\tString content = \"&nbsp;\";\n\t\t\t\t\tCellStyle style = null;\n\t\t\t\t\tif (i >= row.getFirstCellNum() && i < row.getLastCellNum()) {\n\t\t\t\t\t\tCell cell = row.getCell(i);\n\t\t\t\t\t\tif (cell != null) {\n\t\t\t\t\t\t\tstyle = cell.getCellStyle();\n\t\t\t\t\t\t\tcontent = cellValueHelper.getHtmlValue(cell);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (mergedRegionHelper.isMergedRegion(rowIndex, i)) {\n\t\t\t\t\t\tInteger[] rowAndColSpan = mergedRegionHelper.getRowAndColSpan(rowIndex, i);\n\t\t\t\t\t\tout.format(\"    <td rowspan='%s' colspan='%s' class='%s' >%s</td>%n\", rowAndColSpan[0], rowAndColSpan[1], styleName(style), content);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tout.format(\"    <td class='%s'>%s</td>%n\", styleName(style), content);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t\tout.format(\"  </tr>%n\");\n\t\t\trowIndex++;\n\t\t}\n\t\tout.format(\"</tbody>%n\");\n\t}\n\n\tprivate String styleName(CellStyle style) {\n\t\tif (style == null)\n\t\t\treturn \"\";\n\t\treturn String.format(\"style_%02x_%s font_%s_%s\", style.getIndex(), cssRandom, style.getFontIndex(), cssRandom);\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/CellValueHelper.java",
    "content": "package org.jeecgframework.poi.excel.html.helper;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport org.apache.poi.hssf.usermodel.HSSFRichTextString;\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellType;\nimport org.apache.poi.ss.usermodel.Font;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.apache.poi.xssf.usermodel.XSSFFont;\nimport org.apache.poi.xssf.usermodel.XSSFRichTextString;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\n\nimport com.google.common.xml.XmlEscapers;\n\n/**\n * Cell值帮助类\n * \n * @author JEECG\n * @date 2015年5月9日 下午10:31:32\n */\npublic class CellValueHelper {\n\t/**\n\t * Excel 格式\n\t */\n\tprivate boolean is07;\n\n\tprivate int cssRandom;\n\n\tprivate Map<String, String> fontCache = new HashMap<String, String>();\n\n\tpublic CellValueHelper(Workbook wb, int cssRandom) {\n\t\tthis.cssRandom = cssRandom;\n\t\tif (wb instanceof HSSFWorkbook)\n\t\t\tis07 = false;\n\t\telse if (wb instanceof XSSFWorkbook) {\n\t\t\tis07 = true;\n\t\t\tcacheFontInfo(wb);\n\t\t} else\n\t\t\tthrow new IllegalArgumentException(\"unknown workbook type: \" + wb.getClass().getSimpleName());\n\t}\n\n\t/**\n\t * O7 版本坑爹bug\n\t * \n\t * @param wb\n\t */\n\tprivate void cacheFontInfo(Workbook wb) {\n\t\tfor (int i = 0, le = wb.getNumberOfFonts(); i < le; i++) {\n\t\t\tFont font = wb.getFontAt(i);\n\t\t\tfontCache.put(font.getBold() + \"_\" + font.getItalic() + \"_\" + font.getFontName() + \"_\" + font.getFontHeightInPoints() + \"_\" + font.getColor(), font.getIndex() + \"\");\n\t\t}\n\n\t}\n\n\tpublic String getHtmlValue(Cell cell) {\n\t\tif (CellType.BOOLEAN == cell.getCellType() || CellType.NUMERIC == cell.getCellType()) {\n\t\t\tcell.setCellType( CellType.STRING);\n\t\t\treturn cell.getStringCellValue();\n\t\t} else if ( CellType.STRING == cell.getCellType()) {\n\t\t\tif (cell.getRichStringCellValue().numFormattingRuns() == 0) {\n\t\t\t\treturn XmlEscapers.xmlContentEscaper().escape(cell.getStringCellValue());\n\t\t\t} else if (is07) {\n\t\t\t\treturn getXSSFRichString((XSSFRichTextString) cell.getRichStringCellValue());\n\t\t\t} else {\n\t\t\t\treturn getHSSFRichString((HSSFRichTextString) cell.getRichStringCellValue());\n\t\t\t}\n\t\t}\n\t\treturn \"\";\n\t}\n\n\t/**\n\t * 03版本复杂数据\n\t * \n\t * @param rich\n\t * @return\n\t */\n\tprivate String getHSSFRichString(HSSFRichTextString rich) {\n\t\tint nums = rich.numFormattingRuns();\n\t\tStringBuilder sb = new StringBuilder();\n\t\tString text = rich.toString();\n\t\tint currentIndex = 0;\n\t\tsb.append(text.substring(0, rich.getIndexOfFormattingRun(0)));\n\t\tfor (int i = 0; i < nums; i++) {\n\t\t\tsb.append(\"<span \");\n\t\t\tsb.append(\"class='font_\" + rich.getFontOfFormattingRun(i));\n\t\t\tsb.append(\"_\");\n\t\t\tsb.append(cssRandom);\n\t\t\tsb.append(\"'>\");\n\t\t\tcurrentIndex = rich.getIndexOfFormattingRun(i);\n\t\t\tif (i < nums - 1) {\n\t\t\t\tsb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(currentIndex, rich.getIndexOfFormattingRun(i + 1))));\n\t\t\t} else {\n\t\t\t\tsb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(currentIndex, text.length())));\n\t\t\t}\n\t\t\tsb.append(\"</span>\");\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\t/**\n\t * 07版本复杂数据\n\t * \n\t * @param rich\n\t * @return\n\t */\n\tprivate String getXSSFRichString(XSSFRichTextString rich) {\n\t\tint nums = rich.numFormattingRuns();\n\t\tStringBuilder sb = new StringBuilder();\n\t\tString text = rich.toString();\n\t\tint currentIndex = 0, lastIndex = 0;\n\t\tfor (int i = 1; i <= nums; i++) {\n\t\t\tsb.append(\"<span \");\n\t\t\ttry {\n\t\t\t\tsb.append(\"class='font_\" + getFontIndex(rich.getFontOfFormattingRun(i - 1)));\n\t\t\t\tsb.append(\"_\");\n\t\t\t\tsb.append(cssRandom);\n\t\t\t\tsb.append(\"'\");\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t\tsb.append(\">\");\n\t\t\tcurrentIndex = rich.getIndexOfFormattingRun(i) == -1 ? text.length() : rich.getIndexOfFormattingRun(i);\n\t\t\tsb.append(XmlEscapers.xmlContentEscaper().escape(text.substring(lastIndex, currentIndex)));\n\t\t\tsb.append(\"</span>\");\n\t\t\tlastIndex = currentIndex;\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate String getFontIndex(XSSFFont font) {\n\t\treturn fontCache.get(font.getBold() + \"_\" + font.getItalic() + \"_\" + font.getFontName() + \"_\" + font.getFontHeightInPoints() + \"_\" + font.getColor());\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/MergedRegionHelper.java",
    "content": "package org.jeecgframework.poi.excel.html.helper;\n\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.jeecgframework.poi.util.PoiCellUtil;\nimport org.jeecgframework.poi.util.PoiMergeCellUtil;\n\n/**\n * 合并单元格帮助类\n * \n * @author JEECG\n * @date 2015年5月9日 下午2:13:35\n */\npublic class MergedRegionHelper {\n\n\tprivate Map<String, Integer[]> mergedCache = new HashMap<String, Integer[]>();\n\n\tprivate Set<String> notNeedCread = new HashSet<String>();\n\n\tpublic MergedRegionHelper(Sheet sheet) {\n\t\tgetAllMergedRegion(sheet);\n\t}\n\n\tprivate void getAllMergedRegion(Sheet sheet) {\n\t\tint nums = sheet.getNumMergedRegions();\n\t\tfor (int i = 0; i < nums; i++) {\n\t\t\thanderMergedString(sheet.getMergedRegion(i).formatAsString());\n\t\t}\n\t}\n\n\t/**\n\t * 根据合并输出内容,处理合并单元格事情\n\t *\n\t * @param formatAsString\n\t */\n\tprivate void handerMergedString(String formatAsString) {\n\t\tString[] strArr = formatAsString.split(\":\");\n\t\tif (strArr.length == 2) {\n\t\t\tint startCol = strArr[0].charAt(0) - 65;\n\t\t\tif (strArr[0].charAt(1) >= 65) {\n\t\t\t\tstartCol = (startCol + 1) * 26 + (strArr[0].charAt(1) - 65);\n\t\t\t}\n\t\t\tint startRol = Integer.valueOf(strArr[0].substring(strArr[0].charAt(1) >= 65 ? 2 : 1));\n\t\t\tint endCol = strArr[1].charAt(0) - 65;\n\t\t\tif (strArr[1].charAt(1) >= 65) {\n\t\t\t\tendCol = (endCol + 1) * 26 + (strArr[1].charAt(1) - 65);\n\t\t\t}\n\t\t\tint endRol = Integer.valueOf(strArr[1].substring(strArr[1].charAt(1) >= 65 ? 2 : 1));\n\t\t\tmergedCache.put(startRol + \"_\" + startCol, new Integer[] { endRol - startRol + 1, endCol - startCol + 1 });\n\t\t\tfor (int i = startRol; i <= endRol; i++) {\n\t\t\t\tfor (int j = startCol; j <= endCol; j++) {\n\t\t\t\t\tnotNeedCread.add(i + \"_\" + j);\n\t\t\t\t}\n\t\t\t}\n\t\t\tnotNeedCread.remove(startRol + \"_\" + startCol);\n\t\t}\n\n\t}\n\n\t/**\n\t * 是不是需要创建这个TD\n\t *\n\t * @param row\n\t * @param col\n\t * @return\n\t */\n\tpublic boolean isNeedCreate(int row, int col) {\n\t\treturn !notNeedCread.contains(row + \"_\" + col);\n\t}\n\n\t/**\n\t * 是不是合并区域\n\t *\n\t * @param row\n\t * @param col\n\t * @return\n\t */\n\tpublic boolean isMergedRegion(int row, int col) {\n\t\treturn mergedCache.containsKey(row + \"_\" + col);\n\t}\n\n\t/**\n\t * 获取合并区域\n\t *\n\t * @param row\n\t * @param col\n\t * @return\n\t */\n\tpublic Integer[] getRowAndColSpan(int row, int col) {\n\t\treturn mergedCache.get(row + \"_\" + col);\n\t}\n\n\t/**\n\t * 插入之后还原之前的合并单元格\n\t *\n\t * @param rowIndex\n\t * @param size\n\t */\n\tpublic void shiftRows(Sheet sheet, int rowIndex, int size, int shiftRows) {\n\t\tSet<String> keys = new HashSet<String>();\n\t\tkeys.addAll(mergedCache.keySet());\n\t\tfor (String key : keys) {\n\t\t\tString[] temp = key.split(\"_\");\n\t\t\t//update-begin---author:chenrui ---date:20240102  for：[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------\n\t\t\tif (Integer.parseInt(temp[0]) > rowIndex) {\n\t\t\t//update-end---author:chenrui ---date:20240102  for：[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------\n\t\t\t\tInteger[] data   = mergedCache.get(key);\n\t\t\t\tString    newKey = (Integer.parseInt(temp[0]) + size) + \"_\" + temp[1];\n\t\t\t\tif (!mergedCache.containsKey(newKey)) {\n\t\t\t\t\tmergedCache.put(newKey, mergedCache.get(key));\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// 还原合并单元格\n\t\t\t\t\t\tif (!PoiCellUtil.isMergedRegion(sheet, Integer.parseInt(temp[0]) + size - 1, Integer.parseInt(temp[1]))) {\n\t\t\t\t\t\t\tPoiMergeCellUtil.addMergedRegion(sheet,\n\t\t\t\t\t\t\t\t\tInteger.parseInt(temp[0]) + size - 1, Integer.parseInt(temp[0]) + data[0] + size - 2,\n\t\t\t\t\t\t\t\t\tInteger.parseInt(temp[1]), Integer.parseInt(temp[1]) + data[1] - 1\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (Exception e) {\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t//删除掉原始的缓存KEY\n\t\tfor (String key : keys) {\n\t\t\tString[] temp = key.split(\"_\");\n\t\t\t//update-begin---author:chenrui ---date:20240102  for：[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------\n\t\t\tif (Integer.parseInt(temp[0]) >= rowIndex && Integer.parseInt(temp[0]) <= rowIndex + size ) {\n\t\t\t//update-end---author:chenrui ---date:20240102  for：[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题------------\n\t\t\t\tmergedCache.remove(key);\n\t\t\t}\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/StylerHelper.java",
    "content": "package org.jeecgframework.poi.excel.html.helper;\n\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.Formatter;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.poi.hssf.usermodel.HSSFCellStyle;\nimport org.apache.poi.hssf.usermodel.HSSFPalette;\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.hssf.util.HSSFColor;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.xssf.usermodel.XSSFCellStyle;\nimport org.apache.poi.xssf.usermodel.XSSFColor;\nimport org.apache.poi.xssf.usermodel.XSSFFont;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\n\n/**\n * 样式帮助类\n * \n * @author JEECG\n * @date 2015年5月9日 下午4:04:24\n */\npublic class StylerHelper {\n\n\tprivate static String DEFAULTS_CLASS_CSS = \".excelDefaults {background-color: white;color: black;text-decoration: none;direction: ltr;text-transform: none;text-indent: 0;letter-spacing: 0;word-spacing: 0;white-space: normal;unicode-bidi: normal;vertical-align: 0;text-shadow: none;padding: 0;margin: 0;border-collapse: collapse;white-space: pre-wrap;word-wrap: break-word;word-break: break-all;}.excelDefaults td {padding: 1px 5px;border: 1px solid silver;border-color: #000000;text-align: center;vertical-align: middle;font-size: 12pt;}.excelDefaults .colHeader {background-color: silver;font-weight: bold;border: 1px solid black;text-align: center;padding: 1px 5px;}.excelDefaults .rowHeader {background-color: silver;font-weight: bold;border: 1px solid black;text-align: right;padding: 1px 5px;}\";\n\n\tprivate static final String DEFAULTS_CLASS = \"excelDefaults\";\n\n\tprivate static final Map<Short, String> ALIGN = PoiPublicUtil.mapFor(HorizontalAlignment.LEFT.getCode(), \"left\",HorizontalAlignment.CENTER.getCode(), \"center\",HorizontalAlignment.RIGHT.getCode(), \"right\", HorizontalAlignment.FILL.getCode(), \"left\",HorizontalAlignment.JUSTIFY.getCode(), \"left\",HorizontalAlignment.CENTER_SELECTION.getCode(), \"center\");\n\n\tprivate static final Map<Short, String> VERTICAL_ALIGN = PoiPublicUtil.mapFor(VerticalAlignment.BOTTOM.getCode(), \"bottom\", VerticalAlignment.CENTER.getCode(), \"middle\",VerticalAlignment.TOP.getCode(), \"top\");\n\n\tprivate Formatter out;\n\n\tprivate Sheet sheet;\n\n\tprivate HtmlHelper helper;\n\n\tprivate int sheetNum;\n\n\tprivate int cssRandom;\n\n\tpublic StylerHelper(Workbook wb, Formatter out, int sheetNum, int cssRandom) {\n\t\tthis.out = out;\n\t\tthis.sheetNum = sheetNum;\n\t\tthis.cssRandom = cssRandom;\n\t\tif (wb instanceof HSSFWorkbook)\n\t\t\thelper = new HSSFHtmlHelper((HSSFWorkbook) wb);\n\t\telse if (wb instanceof XSSFWorkbook)\n\t\t\thelper = new XSSFHtmlHelper((XSSFWorkbook) wb);\n\t\telse\n\t\t\tthrow new IllegalArgumentException(\"unknown workbook type: \" + wb.getClass().getSimpleName());\n\t\tprintInlineStyle(wb);\n\t}\n\n\tprivate void printInlineStyle(Workbook wb) {\n\t\tout.format(\"<style type=\\\"text/css\\\">%n\");\n\t\tprintStyles(wb);\n\t\tprontFonts(wb);\n\t\tout.format(\"</style>%n\");\n\t}\n\n\tprivate void prontFonts(Workbook wb) {\n        //update-begin---author:liusq  Date:20220228  for：[I4I3ZY]issue AutoPOi Workbook对象转HTML字符串 数组下标越界异常----\n\t\tfor (int i = 0, le = wb.getNumberOfFonts(); i < le; i++) {\n\t\t\tFont font = wb.getFontAt(i);\n\t\t\tout.format(\".%s .%s {%n\", DEFAULTS_CLASS, \"font_\" + i + \"_\" + cssRandom);\n\t\t\tfontStyle(font);\n\t\t\tout.format(\"}%n\");\n\t\t}\n        //update-end---author:liusq  Date:20220228  for：[I4I3ZY]issue AutoPOi Workbook对象转HTML字符串 数组下标越界异常整----\n    }\n\n\tpublic void printStyles(Workbook wb) {\n\t\tif (DEFAULTS_CLASS_CSS == null) {\n\t\t\tDEFAULTS_CLASS_CSS = getDefaultsClassCss();\n\t\t}\n\t\tout.format(DEFAULTS_CLASS_CSS);\n\t\tSet<CellStyle> seen = new HashSet<CellStyle>();\n\t\tsheet = wb.getSheetAt(sheetNum);\n\t\tIterator<Row> rows = sheet.rowIterator();\n\t\twhile (rows.hasNext()) {\n\t\t\tRow row = rows.next();\n\t\t\tfor (Cell cell : row) {\n\t\t\t\tCellStyle style = cell.getCellStyle();\n\t\t\t\tif (!seen.contains(style)) {\n\t\t\t\t\tprintStyle(style);\n\t\t\t\t\tseen.add(style);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate String getDefaultsClassCss() {\n\t\tBufferedReader in = null;\n\t\tStringBuilder sb = new StringBuilder();\n\t\tFormatter formatter = new Formatter(sb);\n\t\ttry {\n\t\t\tin = new BufferedReader(new InputStreamReader(StylerHelper.class.getResourceAsStream(\"excelStyle.css\")));\n\t\t\tString line;\n\t\t\twhile ((line = in.readLine()) != null) {\n\t\t\t\tformatter.format(\"%s%n\", line);\n\t\t\t}\n\t\t\treturn formatter.toString();\n\t\t} catch (IOException e) {\n\t\t\tthrow new IllegalStateException(\"Reading standard css\", e);\n\t\t} finally {\n\t\t\tif (in != null) {\n\t\t\t\ttry {\n\t\t\t\t\tin.close();\n\t\t\t\t} catch (IOException e) {\n\t\t\t\t\tthrow new IllegalStateException(\"Reading standard css\", e);\n\t\t\t\t}\n\t\t\t}\n\t\t\tformatter.close();\n\t\t}\n\t}\n\n\tprivate void printStyle(CellStyle style) {\n\t\tout.format(\".%s .%s {%n\", DEFAULTS_CLASS, styleName(style));\n\t\tstyleContents(style);\n\t\tout.format(\"}%n\");\n\t}\n\n\tprivate void styleContents(CellStyle style) {\n\t\tif (style.getAlignment().getCode() != 2) {\n\t\t\tstyleOut(\"text-align\", style.getAlignment().getCode(), ALIGN);\n\t\t\tstyleOut(\"vertical-align\", style.getAlignment().getCode(), VERTICAL_ALIGN);\n\t\t}\n\t\thelper.colorStyles(style, out);\n\t}\n\n\tprivate void fontStyle(Font font) {\n\t\tif (font.getBold())\n\t\t\tout.format(\"  font-weight: bold;%n\");\n\t\tif (font.getItalic())\n\t\t\tout.format(\"  font-style: italic;%n\");\n\t\tout.format(\"  font-family: %s;%n\", font.getFontName());\n\n\t\tint fontheight = font.getFontHeightInPoints();\n\t\tif (fontheight == 9) {\n\t\t\tfontheight = 10;\n\t\t}\n\t\tout.format(\"  font-size: %dpt;%n\", fontheight);\n\t\thelper.styleColor(out, \"color\", getColor(font));\n\t}\n\n\tprivate Color getColor(Font font) {\n\t\tif (helper instanceof HSSFHtmlHelper) {\n\t\t\treturn ((HSSFWorkbook) sheet.getWorkbook()).getCustomPalette().getColor(font.getColor());\n\t\t} else {\n\t\t\treturn ((XSSFFont) font).getXSSFColor();\n\t\t}\n\t}\n\n\tprivate String styleName(CellStyle style) {\n\t\tif (style == null)\n\t\t\treturn \"\";\n\t\treturn String.format(\"style_%02x_%s\", style.getIndex(), cssRandom);\n\t}\n\n\tprivate <K> void styleOut(String attr, K key, Map<K, String> mapping) {\n\t\tString value = mapping.get(key);\n\t\tif (value != null) {\n\t\t\tout.format(\"  %s: %s;%n\", attr, value);\n\t\t}\n\t}\n\n\tprivate interface HtmlHelper {\n\t\t/**\n\t\t * Outputs the appropriate CSS style for the given cell style.\n\t\t * \n\t\t * @param style\n\t\t *            The cell style.\n\t\t * @param out\n\t\t *            The place to write the output.\n\t\t */\n\t\tvoid colorStyles(CellStyle style, Formatter out);\n\n\t\tvoid styleColor(Formatter out, String attr, Color color);\n\t}\n\n\tprivate class HSSFHtmlHelper implements HtmlHelper {\n\t\tprivate final HSSFWorkbook wb;\n\t\tprivate final HSSFPalette colors;\n\n\t\t//-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\tprivate HSSFColor HSSF_AUTO = new HSSFColor(0x40,   -1, java.awt.Color.black);\n\t\t//-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\n\t\tpublic HSSFHtmlHelper(HSSFWorkbook wb) {\n\t\t\tthis.wb = wb;\n\t\t\tcolors = wb.getCustomPalette();\n\t\t}\n\n\t\tpublic void colorStyles(CellStyle style, Formatter out) {\n\t\t\tHSSFCellStyle cs = (HSSFCellStyle) style;\n\t\t\tif (cs.getFillPattern() != FillPatternType.NO_FILL) {\n\t\t\t\tout.format(\"  /* fill pattern = %s */%n\", cs.getFillPattern());\n\t\t\t}\n\t\t\tstyleColor(out, \"background-color\", cs.getFillForegroundColor());\n\t\t\tstyleColor(out, \"color\", colors.getColor(cs.getFont(wb).getColor()));\n\t\t}\n\n\t\tprivate void styleColor(Formatter out, String attr, short index) {\n\t\t\tHSSFColor color = colors.getColor(index);\n\t\t\tif (index == HSSF_AUTO.getIndex() || color == null) {\n\t\t\t\tout.format(\"  /* %s: index = %d */%n\", attr, index);\n\t\t\t} else {\n\t\t\t\tshort[] rgb = color.getTriplet();\n\t\t\t\tout.format(\"  %s: #%02x%02x%02x; /* index = %d */%n\", attr, rgb[0], rgb[1], rgb[2], index);\n\t\t\t}\n\t\t}\n\n\t\tpublic void styleColor(Formatter out, String attr, Color color) {\n\t\t\tif (color == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tHSSFColor hSSFColor = (HSSFColor) color;\n\t\t\tshort[] rgb = hSSFColor.getTriplet();\n\t\t\tout.format(\"  %s: #%02x%02x%02x; %n\", attr, rgb[0], rgb[1], rgb[2]);\n\t\t}\n\t}\n\n\t/**\n\t * Implementation of {@link HtmlHelper} for XSSF files.\n\t * \n\t * @author Ken Arnold, Industrious Media LLC\n\t */\n\tprivate class XSSFHtmlHelper implements HtmlHelper {\n\n\t\tpublic XSSFHtmlHelper(XSSFWorkbook wb) {\n\t\t}\n\n\t\tpublic void colorStyles(CellStyle style, Formatter out) {\n\t\t\tXSSFCellStyle cs = (XSSFCellStyle) style;\n\t\t\tstyleColor(out, \"background-color\", cs.getFillForegroundXSSFColor());\n\t\t\tstyleColor(out, \"color\", cs.getFont().getXSSFColor());\n\t\t}\n\n\t\tpublic void styleColor(Formatter out, String attr, Color color) {\n\t\t\tXSSFColor xSSFColor = (XSSFColor) color;\n\t\t\tif (color == null || xSSFColor.isAuto())\n\t\t\t\treturn;\n\n\t\t\tbyte[] rgb = xSSFColor.getRGB();\n\t\t\tif (rgb == null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tout.format(\"  %s: #%02x%02x%02x;%n\", attr, rgb[0], rgb[1], rgb[2]);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/excelStyle.css",
    "content": ".excelDefaults {\n\tbackground-color: white;\n\tcolor: black;\n\ttext-decoration: none;\n\tdirection: ltr;\n\ttext-transform: none;\n\ttext-indent: 0;\n\tletter-spacing: 0;\n\tword-spacing: 0;\n\twhite-space: normal;\n\tunicode-bidi: normal;\n\tvertical-align: 0;\n\ttext-shadow: none;\n\tpadding: 0;\n\tmargin: 0;\n\tborder-collapse: collapse;\n\twhite-space: pre-wrap;\n\tword-wrap: break-word;\n\tword-break: break-all;\n}\n\n.excelDefaults td {\n\tpadding: 1px 5px;\n\tborder: 1px solid silver;\n\tborder-color: #000000;\n\ttext-align: center;\n\tvertical-align: middle;\n\tfont-size: 12pt;\n}\n\n.excelDefaults .colHeader {\n\tbackground-color: silver;\n\tfont-weight: bold;\n\tborder: 1px solid black;\n\ttext-align: center;\n\tpadding: 1px 5px;\n}\n\n.excelDefaults .rowHeader {\n\tbackground-color: silver;\n\tfont-weight: bold;\n\tborder: 1px solid black;\n\ttext-align: right;\n\tpadding: 1px 5px;\n}"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/CellValueServer.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;\nimport org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity;\nimport org.jeecgframework.poi.exception.excel.ExcelImportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;\nimport org.jeecgframework.poi.handler.inter.IExcelDataHandler;\nimport org.jeecgframework.poi.util.ExcelUtil;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.sql.Time;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.time.ZoneId;\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Cell 取值服务 判断类型处理数据 1.判断Excel中的类型 2.根据replace替换值 3.handler处理数据 4.判断返回类型转化数据返回\n * \n * @author JEECG\n * @date 2014年6月26日 下午10:42:28\n */\npublic class CellValueServer {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(CellValueServer.class);\n\n\tprivate List<String> hanlderList = null;\n\n\t/**\n\t * 获取单元格内的值\n\t * \n\t * @param xclass\n\t * @param cell\n\t * @param entity\n\t * @return\n\t */\n\tprivate Object getCellValue(String xclass, Cell cell, ExcelImportEntity entity) {\n\t\tif (cell == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tObject result = null;\n\t\t// 日期格式比较特殊,和cell格式不一致\n\t\tif (\"class java.util.Date\".equals(xclass)\n\t\t\t\t|| (\"class java.sql.Time\").equals(xclass)\n\t\t\t\t|| (\"class java.time.LocalDate\").equals(xclass)\n\t\t\t\t|| (\"class java.time.LocalDateTime\").equals(xclass)) {\n\t\t\tif ( CellType.NUMERIC == cell.getCellType()) {\n\t\t\t\t// 日期格式\n\t\t\t\tresult = cell.getDateCellValue();\n\t\t\t} else {\n\t\t\t\tcell.setCellType( CellType.STRING);\n\t\t\t\tresult = getDateData(entity, cell.getStringCellValue());\n\t\t\t}\n\t\t\tif ((\"class java.sql.Time\").equals(xclass)) {\n\t\t\t\tresult = new Time(((Date) result).getTime());\n\t\t\t}else if ((\"class java.time.LocalDate\").equals(xclass)) {\n\t\t\t\tresult = ((Date) result).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();\n\t\t\t} else if ((\"class java.time.LocalDateTime\").equals(xclass)) {\n\t\t\tresult = ((Date) result).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();\n\t\t}\n\t} else if ( CellType.NUMERIC == cell.getCellType()) {\n\t\tresult = cell.getNumericCellValue();\n\t} else if ( CellType.BOOLEAN == cell.getCellType()) {\n\t\t\tresult = cell.getBooleanCellValue();\n\t\t} else if ( CellType.FORMULA == cell.getCellType() && PoiPublicUtil.isNumber(xclass)) {\n\t\t\t//如果单元格是表达式 且 字段是数字类型\n\t\t\tdouble cellValue = cell.getNumericCellValue();\n\t\t\t//---author:liusq---date:20221102-----for: [issues/3369]Excel导入 带公式的时候精度丢失---\n\t\t\t//setScale方法的第一个参数设置小数点保留位数，第二个参数设置进位方法、此处是四舍五入\n\t\t\tBigDecimal bigDecimal= new BigDecimal(cellValue).setScale(4, RoundingMode.HALF_UP);\n           //stripTrailingZeros方法去除末尾的0，toPlainString避免输出科学计数法的字符串\n\t\t\tresult = bigDecimal.stripTrailingZeros().toPlainString();\n\t\t\t//---author:liusq---date:20221102-----for:[issues/3369] Excel导入 带公式的时候精度丢失---\n\t\t} else {\n\t\t\t//设置单元格类型\n\t\t\tcell.setCellType(CellType.STRING);\n\t\t\tresult = cell.getStringCellValue();\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * 获取日期类型数据\n\t * \n\t * @Author JEECG\n\t * @date 2013年11月26日\n\t * @param entity\n\t * @param value\n\t * @return\n\t */\n\tprivate Date getDateData(ExcelImportEntity entity, String value) {\n\t\tif (StringUtils.isNotEmpty(entity.getFormat()) && StringUtils.isNotEmpty(value)) {\n\t\t\tSimpleDateFormat format = new SimpleDateFormat(entity.getFormat());\n\t\t\ttry {\n\t\t\t\treturn format.parse(value);\n\t\t\t} catch (ParseException e) {\n\t\t\t\tLOGGER.error(\"时间格式化失败,格式化:{},值:{}\", entity.getFormat(), value);\n\t\t\t\tthrow new ExcelImportException(ExcelImportEnum.GET_VALUE_ERROR);\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 获取cell的值\n\t * \n\t * @param object\n\t * @param excelParams\n\t * @param cell\n\t * @param titleString\n\t */\n\tpublic Object getValue(IExcelDataHandler dataHanlder, Object object, Cell cell, Map<String, ExcelImportEntity> excelParams, String titleString) throws Exception {\n\t\tExcelImportEntity entity = excelParams.get(titleString);\n\t\tString xclass = \"class java.lang.Object\";\n\t\tif (!(object instanceof Map)) {\n\t\t\tMethod setMethod = entity.getMethods() != null && entity.getMethods().size() > 0 ? entity.getMethods().get(entity.getMethods().size() - 1) : entity.getMethod();\n\t\t\tType[] ts = setMethod.getGenericParameterTypes();\n\t\t\txclass = ts[0].toString();\n\t\t}\n\t\tObject result = getCellValue(xclass, cell, entity);\n\t\tif (entity != null) {\n\t\t\tresult = hanlderSuffix(entity.getSuffix(), result);\n\t\t\t//update-begin-author:taoYan date:20180807 for:多值替换\n\t\t\tresult = replaceValue(entity.getReplace(), result,entity.isMultiReplace());\n\t\t\t//update-end-author:taoYan date:20180807 for:多值替换\n\t\t}\n\t\tresult = hanlderValue(dataHanlder, object, result, titleString);\n\t\treturn getValueByType(xclass, result, entity);\n\t}\n\n\t/**\n\t * 获取cell值\n\t * \n\t * @param dataHanlder\n\t * @param object\n\t * @param cellEntity\n\t * @param excelParams\n\t * @param titleString\n\t * @return\n\t */\n\tpublic Object getValue(IExcelDataHandler dataHanlder, Object object, SaxReadCellEntity cellEntity, Map<String, ExcelImportEntity> excelParams, String titleString) {\n\t\tExcelImportEntity entity = excelParams.get(titleString);\n\t\tMethod setMethod = entity.getMethods() != null && entity.getMethods().size() > 0 ? entity.getMethods().get(entity.getMethods().size() - 1) : entity.getMethod();\n\t\tType[] ts = setMethod.getGenericParameterTypes();\n\t\tString xclass = ts[0].toString();\n\t\tObject result = cellEntity.getValue();\n\t\tresult = hanlderSuffix(entity.getSuffix(), result);\n\t\t//update-begin-auhtor:taoyan date:20180807 for:多值替换\n\t\tresult = replaceValue(entity.getReplace(), result,entity.isMultiReplace());\n\t\t//update-end-auhtor:taoyan date:20180807 for:多值替换\n\t\tresult = hanlderValue(dataHanlder, object, result, titleString);\n\t\treturn getValueByType(xclass, result, entity);\n\t}\n\n\t/**\n\t * 把后缀删除掉\n\t * \n\t * @param result\n\t * @param suffix\n\t * @return\n\t */\n\tprivate Object hanlderSuffix(String suffix, Object result) {\n\t\tif (StringUtils.isNotEmpty(suffix) && result != null && result.toString().endsWith(suffix)) {\n\t\t\tString temp = result.toString();\n\t\t\treturn temp.substring(0, temp.length() - suffix.length());\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * 根据返回类型获取返回值\n\t * \n\t * @param xclass\n\t * @param result\n\t * @param entity\n\t * @return\n\t */\n\tprivate Object getValueByType(String xclass, Object result, ExcelImportEntity entity) {\n\t\ttry {\n\t\t\t//update-begin-author:scott date:20180711 for:TASK #2950 【bug】excel 导入报错，空指针问题\n\t\t\tif(result==null || \"\".equals(String.valueOf(result))){\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t//update-end-author:scott date:20180711 for:TASK #2950 【bug】excel 导入报错，空指针问题\n\t\t\tif (\"class java.util.Date\".equals(xclass)) {\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tif (\"class java.lang.Boolean\".equals(xclass) || \"boolean\".equals(xclass)) {\n\t\t\t\t//update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t\tBoolean temp = Boolean.valueOf(String.valueOf(result));\n\t\t\t\t//if(StringUtils.isNotEmpty(entity.getNumFormat())){\n\t\t\t\t//\treturn Boolean.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));\n\t\t\t\t//}else{\n\t\t\t\t\treturn temp;\n\t\t\t\t//}\n\t\t\t\t//update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t}\n\t\t\tif (\"class java.lang.Double\".equals(xclass) || \"double\".equals(xclass)) {\n\t\t\t\t//update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t\tDouble temp = Double.valueOf(String.valueOf(result));\n\t\t\t\t//if(StringUtils.isNotEmpty(entity.getNumFormat())){\n\t\t\t\t//\treturn Double.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));\n\t\t\t\t//}else{\n\t\t\t\t\treturn temp;\n\t\t\t\t//}\n\t\t\t\t//update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t}\n\t\t\tif (\"class java.lang.Long\".equals(xclass) || \"long\".equals(xclass)) {\n\t\t\t\t//update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t\tLong temp = Long.valueOf(ExcelUtil.remove0Suffix(String.valueOf(result)));\n\t\t\t\t//if(StringUtils.isNotEmpty(entity.getNumFormat())){\n\t\t\t\t//\treturn Long.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));\n\t\t\t\t//}else{\n\t\t\t\t\treturn temp;\n\t\t\t\t//}\n\t\t\t\t//update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t}\n\t\t\tif (\"class java.lang.Float\".equals(xclass) || \"float\".equals(xclass)) {\n\t\t\t\t//update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t\tFloat temp = Float.valueOf(String.valueOf(result));\n\t\t\t\t//if(StringUtils.isNotEmpty(entity.getNumFormat())){\n\t\t\t\t//\treturn Float.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));\n\t\t\t\t//}else{\n\t\t\t\t\treturn temp;\n\t\t\t\t//}\n\t\t\t\t//update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t}\n\t\t\tif (\"class java.lang.Integer\".equals(xclass) || \"int\".equals(xclass)) {\n\t\t\t\t//update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t\tInteger temp = Integer.valueOf(ExcelUtil.remove0Suffix(String.valueOf(result)));\n\t\t\t\t//if(StringUtils.isNotEmpty(entity.getNumFormat())){\n\t\t\t\t//\treturn Integer.valueOf(new DecimalFormat(entity.getNumFormat()).format(temp));\n\t\t\t\t//}else{\n\t\t\t\t\treturn temp;\n\t\t\t\t//}\n\t\t\t\t//update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t}\n\t\t\tif (\"class java.math.BigDecimal\".equals(xclass)) {\n\t\t\t\t//update-begin-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t\tBigDecimal temp = new BigDecimal(String.valueOf(result));\n\t\t\t\t//if(StringUtils.isNotEmpty(entity.getNumFormat())){\n\t\t\t\t//\treturn new BigDecimal(new DecimalFormat(entity.getNumFormat()).format(temp));\n\t\t\t\t//}else{\n\t\t\t\t\treturn temp;\n\t\t\t\t//}\n\t\t\t\t//update-end-author:taoYan date:20200319 for:Excel注解的numFormat方法似乎未实现 #970\n\t\t\t}\n\t\t\tif (\"class java.lang.String\".equals(xclass)) {\n\t\t\t\t// 针对String 类型,但是Excel获取的数据却不是String,比如Double类型,防止科学计数法\n\t\t\t\tif (result instanceof String) {\n\t\t\t\t\t//---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型，去掉后缀.0------\n\t\t\t\t\treturn ExcelUtil.remove0Suffix(result);\n\t\t\t\t\t//---update-end-----autor:scott------date:20191016-------for:excel导入数字类型，去掉后缀.0------\n\t\t\t\t}\n\t\t\t\t// double类型防止科学计数法\n\t\t\t\tif (result instanceof Double) {\n\t\t\t\t\treturn PoiPublicUtil.doubleToString((Double) result);\n\t\t\t\t}\n\t\t\t\t//---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型，去掉后缀.0------\n\t\t\t\treturn ExcelUtil.remove0Suffix(String.valueOf(result));\n\t\t\t\t//---update-end-----autor:scott------date:20191016-------for:excel导入数字类型，去掉后缀.0------\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelImportException(ExcelImportEnum.GET_VALUE_ERROR);\n\t\t}\n\t}\n\n\t/**\n\t * 调用处理接口处理值\n\t * \n\t * @param dataHanlder\n\t * @param object\n\t * @param result\n\t * @param titleString\n\t * @return\n\t */\n\tprivate Object hanlderValue(IExcelDataHandler dataHanlder, Object object, Object result, String titleString) {\n\t\tif (dataHanlder == null || dataHanlder.getNeedHandlerFields() == null || dataHanlder.getNeedHandlerFields().length == 0) {\n\t\t\treturn result;\n\t\t}\n\t\tif (hanlderList == null) {\n\t\t\thanlderList = Arrays.asList(dataHanlder.getNeedHandlerFields());\n\t\t}\n\t\tif (hanlderList.contains(titleString)) {\n\t\t\treturn dataHanlder.importHandler(object, titleString, result);\n\t\t}\n\t\treturn result;\n\t}\n\n\t//update-begin-author:taoyan date:20180807 for:导入多值替换--\n\t/**\n\t * 导入支持多值替换\n\t * @param replace 数据库中字典查询出来的数组\n\t * @param result excel单元格获取的值\n\t * @param multiReplace 是否支持多值替换\n\t * @author taoYan\n\t * @since 2018年8月7日\n\t */\n\tprivate Object replaceValue(String[] replace, Object result,boolean multiReplace) {\n\t\tif(result == null){\n\t\t\treturn \"\";\n\t\t}\n\t\tif(replace == null || replace.length<=0){\n\t\t\treturn result;\n\t\t}\n\t\tString temp = String.valueOf(result);\n\t\tString backValue = \"\";\n\t\tif(temp.indexOf(\",\")>0 && multiReplace){\n\t\t\t//原值中带有逗号，认为他是多值的\n\t\t\tString multiReplaces[] = temp.split(\",\");\n\t\t\tfor (String str : multiReplaces) {\n\t\t\t\tbackValue = backValue.concat(replaceSingleValue(replace, str)+\",\");\n\t\t\t}\n\t\t\tif(backValue.equals(\"\")){\n\t\t\t\tbackValue = temp;\n\t\t\t}else{\n\t\t\t\tbackValue = backValue.substring(0, backValue.length()-1);\n\t\t\t}\n\t\t}else{\n\t\t\tbackValue = replaceSingleValue(replace, temp);\n\t\t}\n\t\t//update-begin-author:liusq date:20210204 for:字典替换失败提示日志\n\t\tif(replace.length>0 && backValue.equals(temp)){\n\t\t\tLOGGER.warn(\"====================字典替换失败,字典值:{},要转换的导入值:{}====================\", replace, temp);\n\t\t}\n\t\t//update-end-author:liusq date:20210204 for:字典替换失败提示日志\n\t\treturn backValue;\n\t}\n\t/**\n\t * 单值替换 ,若没找到则原值返回\n\t */\n\tprivate String replaceSingleValue(String[] replace, String temp){\n\t\tString[] tempArr;\n\t\tfor (int i = 0; i < replace.length; i++) {\n\t\t\t//update-begin---author:scott   Date:20211220  for：[issues/I4MBB3]@Excel dicText字段的值有下划线时，导入功能不能正确解析---\n\t\t\t//tempArr = replace[i].split(\"_\");\n\t\t\ttempArr = getValueArr(replace[i]);\n\t\t\tif (temp.equals(tempArr[0]) || temp.replace(\"_\",\"---\").equals(tempArr[0])) {\n                //update-begin---author:wangshuai ---date:20220422  for：导入字典替换需要将---替换成_，不然数据库会存--- ------------\n                if(tempArr[1].contains(\"---\")){\n                    return tempArr[1].replace(\"---\",\"_\");\n                }\n                //update-end---author:wangshuai ---date:20220422  for：导入字典替换需要将---替换成_，不然数据库会存--- --------------\n                return tempArr[1];\n\t\t\t}\n\t\t\t//update-end---author:scott   Date:20211220  for：[issues/I4MBB3]@Excel dicText字段的值有下划线时，导入功能不能正确解析---\n\t\t}\n\t\treturn temp;\n\t}\n\t//update-end-author:taoyan date:20180807 for:导入多值替换--\n\n\t/**\n\t * 字典文本中含多个下划线横岗，取最后一个（解决空值情况）\n\t *\n\t * @param val\n\t * @return\n\t */\n\tpublic String[] getValueArr(String val) {\n\t\tint i = val.lastIndexOf(\"_\");//最后一个分隔符的位置\n\t\tString[] c = new String[2];\n\t\tc[0] = val.substring(0, i); //label\n\t\tc[1] = val.substring(i + 1); //key\n\t\treturn c;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.hssf.usermodel.HSSFSheet;\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.formula.functions.T;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.xssf.usermodel.XSSFSheet;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.jeecgframework.core.util.ApplicationContextUtil;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;\nimport org.jeecgframework.poi.excel.entity.result.ExcelImportResult;\nimport org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult;\nimport org.jeecgframework.poi.excel.imports.base.ImportBaseService;\nimport org.jeecgframework.poi.excel.imports.base.ImportFileServiceI;\nimport org.jeecgframework.poi.excel.imports.verifys.VerifyHandlerServer;\nimport org.jeecgframework.poi.exception.excel.ExcelImportException;\nimport org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;\nimport org.jeecgframework.poi.util.ExcelUtil;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.io.*;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Excel 导入服务\n * \n * @author JEECG\n * @date 2014年6月26日 下午9:20:51\n */\n@SuppressWarnings({ \"rawtypes\", \"unchecked\", \"hiding\" })\npublic class ExcelImportServer extends ImportBaseService {\n\n\tprivate final static Logger LOGGER = LoggerFactory.getLogger(ExcelImportServer.class);\n\n\tprivate CellValueServer cellValueServer;\n\n\tprivate VerifyHandlerServer verifyHandlerServer;\n\n\tprivate boolean verfiyFail = false;\n\t//仅允许字母数字字符的正则表达式\n\tprivate static final Pattern lettersAndNumbersPattern = Pattern.compile(\"^[a-zA-Z0-9]+$\") ;\n\t/**\n\t * 异常数据styler\n\t */\n\tprivate CellStyle errorCellStyle;\n\n\tpublic ExcelImportServer() {\n\t\tthis.cellValueServer = new CellValueServer();\n\t\tthis.verifyHandlerServer = new VerifyHandlerServer();\n\t}\n\n\t/***\n\t * 向List里面继续添加元素\n\t * \n\t * @param object\n\t * @param param\n\t * @param row\n\t * @param titlemap\n\t * @param targetId\n\t * @param pictures\n\t * @param params\n\t */\n\tprivate void addListContinue(Object object, ExcelCollectionParams param, Row row, Map<Integer, String> titlemap, String targetId, Map<String, PictureData> pictures, ImportParams params) throws Exception {\n\t\tCollection collection = (Collection) PoiPublicUtil.getMethod(param.getName(), object.getClass()).invoke(object, new Object[] {});\n\t\tObject entity = PoiPublicUtil.createObject(param.getType(), targetId);\n\t\tString picId;\n\t\tboolean isUsed = false;// 是否需要加上这个对象\n\t\tfor (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {\n\t\t\tCell cell = row.getCell(i);\n\t\t\tString titleString = (String) titlemap.get(i);\n\t\t\tif (param.getExcelParams().containsKey(titleString)) {\n\t\t\t\tif (param.getExcelParams().get(titleString).getType() == 2) {\n\t\t\t\t\tpicId = row.getRowNum() + \"_\" + i;\n                    //update-begin---author:chenrui ---date:20240402  for：[issue/#6025/#6040]子表图片导入报错------------\n\t\t\t\t\tsaveImage(entity, picId, param.getExcelParams(), titleString, pictures, params);\n                    //update-end---author:chenrui ---date:20240402  for：[issue/#6025/#6040]子表图片导入报错------------\n\t\t\t\t} else {\n\t\t\t\t\tsaveFieldValue(params, entity, cell, param.getExcelParams(), titleString, row);\n\t\t\t\t}\n\t\t\t\tisUsed = true;\n\t\t\t}\n\t\t}\n\t\tif (isUsed) {\n\t\t\tcollection.add(entity);\n\t\t}\n\t}\n\n\t/**\n\t * 获取key的值,针对不同类型获取不同的值\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-21\n\t * @param cell\n\t * @return\n\t */\n\tprivate String getKeyValue(Cell cell) {\n\t\tif(cell==null){\n\t\t\treturn null;\n\t\t}\n\tObject obj = null;\n\tswitch (cell.getCellType()) {\n\tcase STRING:\n\t\t\tobj = cell.getStringCellValue();\n\t\t\tbreak;\n\t\tcase BOOLEAN:\n\t\t\tobj = cell.getBooleanCellValue();\n\t\t\tbreak;\n\t\tcase NUMERIC:\n\t\t\tobj = cell.getNumericCellValue();\n\t\t\tbreak;\n\t\tcase FORMULA:\n\t\t\tobj = cell.getCellFormula();\n\t\t\tbreak;\n\t\t}\n\t\treturn obj == null ? null : obj.toString().trim();\n\t}\n\n\t/**\n\t * 获取保存的真实路径\n\t * \n\t * @param excelImportEntity\n\t * @param object\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate String getSaveUrl(ExcelImportEntity excelImportEntity, Object object) throws Exception {\n\t\tString url = \"\";\n\t\tif (excelImportEntity.getSaveUrl().equals(\"upload\")) {\n\t\t\tif (excelImportEntity.getMethods() != null && excelImportEntity.getMethods().size() > 0) {\n\t\t\t\tobject = getFieldBySomeMethod(excelImportEntity.getMethods(), object);\n\t\t\t}\n\t\t\turl = object.getClass().getName().split(\"\\\\.\")[object.getClass().getName().split(\"\\\\.\").length - 1];\n\t\t\treturn excelImportEntity.getSaveUrl() + \"/\" + url.substring(0, url.lastIndexOf(\"Entity\"));\n\t\t}\n\t\treturn excelImportEntity.getSaveUrl();\n\t}\n\t//update-begin--Author:xuelin  Date:20171205 for：TASK #2098 【excel问题】 Online 一对多导入失败--------------------\n\tprivate <T> List<T> importExcel(Collection<T> result, Sheet sheet, Class<?> pojoClass, ImportParams params, Map<String, PictureData> pictures) throws Exception {\n\t\tList collection = new ArrayList();\n\t\tMap<String, ExcelImportEntity> excelParams = new HashMap<String, ExcelImportEntity>();\n\t\tList<ExcelCollectionParams> excelCollection = new ArrayList<ExcelCollectionParams>();\n\t\tString targetId = null;\n\t\tif (!Map.class.equals(pojoClass)) {\n\t\t\tField fileds[] = PoiPublicUtil.getClassFields(pojoClass);\n\t\t\tExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);\n\t\t\tif (etarget != null) {\n\t\t\t\ttargetId = etarget.value();\n\t\t\t}\n\t\t\tgetAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);\n\t\t}\n\t\tignoreHeaderHandler(excelParams, params);\n\t\tIterator<Row> rows = sheet.rowIterator();\n\t\tMap<Integer, String> titlemap = getTitleMap(sheet, rows, params, excelCollection);\n\t\t//update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex\n\t\tSet<String> keys = excelParams.keySet();\n\t\tfor (String key : keys) {\n\t\t\tif (key.startsWith(\"FIXED_\")) {\n\t\t\t\tString[] arr = key.split(\"_\");\n\t\t\t\ttitlemap.put(Integer.parseInt(arr[1]), key);\n\t\t\t}\n\t\t}\n\t\t//update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex\n\t\tSet<Integer> columnIndexSet = titlemap.keySet();\n        Integer maxColumnIndex = Collections.max(columnIndexSet);\n        Integer minColumnIndex = Collections.min(columnIndexSet);\n\t\tRow row = null;\n\t\t//跳过表头和标题行\n\t\t//update-begin---author:chenrui ---date:20250715  for：[issues/3943]导入excel时，标题区域的空行会导致下方列表数据被吞------------\n\t\tint skipRowNum = params.getTitleRows() + params.getHeadRows();\n        do {\n            row = rows.next();\n        } while (row.getRowNum() != skipRowNum - 1);\n\t\t//update-end---author:chenrui ---date:20250715  for：[issues/3943]导入excel时，标题区域的空行会导致下方列表数据被吞------------\n\t\tObject object = null;\n\t\tString picId;\n\t\twhile (rows.hasNext() && (row == null || sheet.getLastRowNum() - row.getRowNum() > params.getLastOfInvalidRow())) {\n\t\t\trow = rows.next();\n\t\t\t//update-begin--Author:xuelin  Date:20171017 for：TASK #2373 【bug】表改造问题，导致 3.7.1批量导入用户bug-导入不成功--------------------\n\t\t\t// 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象\n\t\t\t//update-begin--Author:xuelin  Date:20171206 for：TASK #2451 【excel导出bug】online 一对多导入成功， 但是现在代码生成后的一对多online导入有问题了\n\t\t\tCell keyIndexCell = row.getCell(params.getKeyIndex());\n\t\t\tif (excelCollection.size()>0 && StringUtils.isEmpty(getKeyValue(keyIndexCell)) && object != null && !Map.class.equals(pojoClass)) {\n\t\t\t\t//update-end--Author:xuelin  Date:20171206 for：TASK #2451 【excel导出bug】online 一对多导入成功， 但是现在代码生成后的一对多online导入有问题了\n\t\t\t\tfor (ExcelCollectionParams param : excelCollection) {\n\t\t\t\t\taddListContinue(object, param, row, titlemap, targetId, pictures, params);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t} else {\t\t\t\n\t\t\t\tobject = PoiPublicUtil.createObject(pojoClass, targetId);\n\t\t\t\ttry {\n                    //update-begin-author:taoyan date:20200303 for:导入图片\n\t\t\t\t    int firstCellNum = row.getFirstCellNum();\n\t\t\t\t    if(firstCellNum>minColumnIndex){\n                        firstCellNum = minColumnIndex;\n                    }\n\t\t\t\t\t//update-begin---author:chenrui ---date:20250320  for：[issues/7947]autopoi导入 报错Cell index must be >= 0 ------------\n\t\t\t\t\tif (firstCellNum < 0) {\n\t\t\t\t\t\tfirstCellNum = 0;\n\t\t\t\t\t}\n\t\t\t\t\t//update-end---author:chenrui ---date:20250320  for：[issues/7947]autopoi导入 报错Cell index must be >= 0 ------------\n                    int lastCellNum = row.getLastCellNum();\n                    if(lastCellNum<maxColumnIndex+1){\n                        lastCellNum = maxColumnIndex+1;\n                    }\n\t\t\t\t\t//update-begin---author:chenrui ---date:20240306  for：[QQYUN-8394]Excel导入时空行校验问题------------\n\t\t\t\t\tint noneCellNum = 0;\n\t\t\t\t\tfor (int i = firstCellNum, le = lastCellNum; i < le; i++) {\n\t\t\t\t\t\tCell cell = row.getCell(i);\n\t\t\t\t\t\tString titleString = (String) titlemap.get(i);\n\t\t\t\t\t\tif (excelParams.containsKey(titleString) || Map.class.equals(pojoClass)) {\n\t\t\t\t\t\t\tif (excelParams.get(titleString) != null && excelParams.get(titleString).getType() == 2) {\n\t\t\t\t\t\t\t\tpicId = row.getRowNum() + \"_\" + i;\n\t\t\t\t\t\t\t\tsaveImage(object, picId, excelParams, titleString, pictures, params);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tif(params.getImageList()!=null && params.getImageList().contains(titleString)){\n\t\t\t\t\t\t\t\t\tif (pictures != null) {\n\t\t\t\t\t\t\t\t\t\tpicId = row.getRowNum() + \"_\" + i;\n\t\t\t\t\t\t\t\t\t\tPictureData image = pictures.get(picId);\n\t\t\t\t\t\t\t\t\t\tif(image!=null){\n\t\t\t\t\t\t\t\t\t\t\tbyte[] data = image.getData();\n\t\t\t\t\t\t\t\t\t\t\tparams.getDataHanlder().setMapValue((Map) object, titleString, data);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\t\tObject value = saveFieldValue(params, object, cell, excelParams, titleString, row);\n\t\t\t\t\t\t\t\t\tif(null == value){\n\t\t\t\t\t\t\t\t\t\tnoneCellNum++;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n                        //update-end-author:taoyan date:20200303 for:导入图片\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (ExcelCollectionParams param : excelCollection) {\n\t\t\t\t\t\taddListContinue(object, param, row, titlemap, targetId, pictures, params);\n\t\t\t\t\t}\n\t\t\t\t\t//update-begin-author:taoyan date:20210526 for:autopoi导入excel 如果单元格被设置边框，即使没有内容也会被当做是一条数据导入 #2484\n                    if (isNotNullObject(pojoClass, object) && noneCellNum < (lastCellNum - firstCellNum)) {\n\t\t\t\t\t//update-end---author:chenrui ---date:20240306  for：[QQYUN-8394]Excel导入时空行校验问题------------\n\t\t\t\t\t\tcollection.add(object);\n\t\t\t\t\t}\n\t\t\t\t\t//update-end-author:taoyan date:20210526 for:autopoi导入excel 如果单元格被设置边框，即使没有内容也会被当做是一条数据导入 #2484\n\t\t\t\t} catch (ExcelImportException e) {\n\t\t\t\t\tif (!e.getType().equals(ExcelImportEnum.VERIFY_ERROR)) {\n\t\t\t\t\t\tthrow new ExcelImportException(e.getType(), e);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t//update-end--Author:xuelin  Date:20171017 for：TASK #2373 【bug】表改造问题，导致 3.7.1批量导入用户bug-导入不成功--------------------\n\t\t}\n\t\treturn collection;\n\t}\n\n\t/**\n\t * 判断当前对象不是空\n\t * @param pojoClass\n\t * @param object\n\t * @return\n\t */\n\tprivate boolean isNotNullObject(Class pojoClass, Object object){\n\t\ttry {\n\t\t\tMethod method = pojoClass.getMethod(\"isNullObject\");\n\t\t\tif(method!=null){\n\t\t\t\tObject flag = method.invoke(object);\n\t\t\t\tif(flag!=null && true == Boolean.parseBoolean(flag.toString())){\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (NoSuchMethodException e) {\n\t\t\tLOGGER.debug(\"未定义方法 isNullObject\");\n\t\t} catch (IllegalAccessException e) {\n\t\t\tLOGGER.warn(\"没有权限访问该方法 isNullObject\");\n\t\t} catch (InvocationTargetException e) {\n\t\t\tLOGGER.warn(\"方法调用失败 isNullObject\");\n\t\t}\n\t\treturn true;\n\t}\n\n\t/**\n\t * 获取忽略的表头信息\n\t * @param excelParams\n\t * @param params\n\t */\n\tprivate void ignoreHeaderHandler(Map<String, ExcelImportEntity> excelParams,ImportParams params){\n\t\tList<String> ignoreList = new ArrayList<>();\n\t\tfor(String key:excelParams.keySet()){\n\t\t\tString temp = excelParams.get(key).getGroupName();\n\t\t\tif(temp!=null && temp.length()>0){\n\t\t\t\tignoreList.add(temp);\n\t\t\t}\n\t\t}\n\t\tparams.setIgnoreHeaderList(ignoreList);\n\t}\n\n\t/**\n\t * 获取表格字段列名对应信息\n\t * \n\t * @param rows\n\t * @param params\n\t * @param excelCollection\n\t * @return\n\t */\n\tprivate Map<Integer, String> getTitleMap(Sheet sheet, Iterator<Row> rows, ImportParams params, List<ExcelCollectionParams> excelCollection) throws Exception {\n\t\tMap<Integer, String> titlemap = new HashMap<Integer, String>();\n\t\tIterator<Cell> cellTitle = null;\n\t\tString collectionName = null;\n\t\tExcelCollectionParams collectionParams = null;\n\t\tRow headRow = null;\n\t\tint headBegin = params.getTitleRows();\n\t\t//update_begin-author:taoyan date:2020622 for：当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环\n\t\tint allRowNum = sheet.getPhysicalNumberOfRows();\n\t\t//找到首行表头，每个sheet都必须至少有一行表头\n\t\twhile(headRow == null && headBegin < allRowNum){\n\t\t\theadRow = sheet.getRow(headBegin++);\n\t\t}\n\t\tif(headRow==null){\n\t\t\tthrow new Exception(\"不识别该文件\");\n\t\t}\n\t\t//update-end-author:taoyan date:2020622 for：当文件行数小于代码里设置的TitleRows时headRow一直为空就会出现死循环\n\n\t\t//设置表头行数\n\t\tif (ExcelUtil.isMergedRegion(sheet, headRow.getRowNum(), 0)) {\n\t\t\tparams.setHeadRows(2);\n\t\t}else{\n\t\t\tparams.setHeadRows(1);\n\t\t}\n\t\tcellTitle = headRow.cellIterator();\n\t\twhile (cellTitle.hasNext()) {\n\t\t\tCell cell = cellTitle.next();\n\t\t\tString value = getKeyValue(cell);\n\t\t\tif (StringUtils.isNotEmpty(value)) {\n\t\t\t\ttitlemap.put(cell.getColumnIndex(), value);//加入表头列表\n\t\t\t}\n\t\t}\n\t\t\n\t\t//多行表头\n\t\tfor (int j = headBegin; j < headBegin + params.getHeadRows()-1; j++) {\n\t\t\theadRow = sheet.getRow(j);\n\t\t\tcellTitle = headRow.cellIterator();\n\t\t\twhile (cellTitle.hasNext()) {\n\t\t\t\tCell cell = cellTitle.next();\n\t\t\t\tString value = getKeyValue(cell);\n\t\t\t\tif (StringUtils.isNotEmpty(value)) {\n\t\t\t\t\tint columnIndex = cell.getColumnIndex();\n\t\t\t\t\t//当前cell的上一行是否为合并单元格\n\t\t\t\t\tif(ExcelUtil.isMergedRegion(sheet, cell.getRowIndex()-1, columnIndex)){\n\t\t\t\t\t\tcollectionName = ExcelUtil.getMergedRegionValue(sheet, cell.getRowIndex()-1, columnIndex);\n\t\t\t\t\t\tif(params.isIgnoreHeader(collectionName)){\n\t\t\t\t\t\t\ttitlemap.put(cell.getColumnIndex(), value);\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\ttitlemap.put(cell.getColumnIndex(), collectionName + \"_\" + value);\n\t\t\t\t\t\t}\n\t\t\t\t\t}else{\n\t\t\t\t\t\t//update-begin-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据\n\t\t\t\t\t\t// 上一行不是合并的情况下另有一种特殊的场景： 如果当前单元格和上面的单元格同一列 即子表字段只有一个 所以标题没有出现跨列\n\t\t\t\t\t\tString prefixTitle = titlemap.get(cell.getColumnIndex());\n\t\t\t\t\t\tif(prefixTitle!=null && !\"\".equals(prefixTitle)){\n\t\t\t\t\t\t\ttitlemap.put(cell.getColumnIndex(), prefixTitle + \"_\" +value);\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\ttitlemap.put(cell.getColumnIndex(), value);\n\t\t\t\t\t\t}\n\t\t\t\t\t\t//update-end-author:taoyan date:20220112 for: JT640 【online】导入 无论一对一还是一对多 如果子表只有一个字段 则子表无数据\n\t\t\t\t\t}\n\t\t\t\t\t/*int i = cell.getColumnIndex();\n\t\t\t\t\t// 用以支持重名导入\n\t\t\t\t\tif (titlemap.containsKey(i)) {\n\t\t\t\t\t\tcollectionName = titlemap.get(i);\n\t\t\t\t\t\tcollectionParams = getCollectionParams(excelCollection, collectionName);\n\t\t\t\t\t\ttitlemap.put(i, collectionName + \"_\" + value);\n\t\t\t\t\t} else if (StringUtils.isNotEmpty(collectionName) && collectionParams.getExcelParams().containsKey(collectionName + \"_\" + value)) {\n\t\t\t\t\t\ttitlemap.put(i, collectionName + \"_\" + value);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcollectionName = null;\n\t\t\t\t\t\tcollectionParams = null;\n\t\t\t\t\t}\n\t\t\t\t\tif (StringUtils.isEmpty(collectionName)) {\n\t\t\t\t\t\ttitlemap.put(i, value);\n\t\t\t\t\t}*/\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn titlemap;\n\t}\n\t//update-end--Author:xuelin  Date:20171205 for：TASK #2098 【excel问题】 Online 一对多导入失败--------------------\n\t/**\n\t * 获取这个名称对应的集合信息\n\t * \n\t * @param excelCollection\n\t * @param collectionName\n\t * @return\n\t */\n\tprivate ExcelCollectionParams getCollectionParams(List<ExcelCollectionParams> excelCollection, String collectionName) {\n\t\tfor (ExcelCollectionParams excelCollectionParams : excelCollection) {\n\t\t\tif (collectionName.equals(excelCollectionParams.getExcelName())) {\n\t\t\t\treturn excelCollectionParams;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * Excel 导入 field 字段类型 Integer,Long,Double,Date,String,Boolean\n\t * \n\t * @param inputstream\n\t * @param pojoClass\n\t * @param params\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic ExcelImportResult importExcelByIs(InputStream inputstream, Class<?> pojoClass, ImportParams params) throws Exception {\n\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\tLOGGER.debug(\"Excel import start ,class is {}\", pojoClass);\n\t\t}\n\t\tList<T> result = new ArrayList<T>();\n\t\tWorkbook book = null;\n\t\tboolean isXSSFWorkbook = false;\n\t\t//update-begin---author:chenrui ---date:20240403  for：[issue/#5987]嵌入单元格图片无法导入------------\n\t\t// 复制输入流,防止在读取嵌入图片时流为空\n        ByteArrayOutputStream inCopy = new ByteArrayOutputStream();\n        IOUtils.copy(inputstream, inCopy);\n        inputstream = new ByteArrayInputStream(inCopy.toByteArray());\n        if (!(inputstream.markSupported())) {\n            inputstream = new PushbackInputStream(inputstream, 8);\n        }\n\t\t//update-end---author:chenrui ---date:20240403  for：[issue/#5987]嵌入单元格图片无法导入------------\n\t\t//begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\t//------poi4.x begin----\n//\t\tFileMagic fm = FileMagic.valueOf(FileMagic.prepareToCheckMagic(inputstream));\n//\t\tif(FileMagic.OLE2 == fm){\n//\t\t\tisXSSFWorkbook=false;\n//\t\t}\n\t\tbook = WorkbookFactory.create(inputstream);\n\t\tif(book instanceof XSSFWorkbook){\n\t\t\tisXSSFWorkbook=true;\n\t\t}\n\t\tLOGGER.info(\"  >>>  poi3升级到4.0兼容改造工作, isXSSFWorkbook = \" +isXSSFWorkbook);\n\t\t//end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\n\t\t//begin-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------\n\t\t//获取导入文本的sheet数\n\t\t//update-begin-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错\n\t\tif(params.getSheetNum()==0){\n\t\t\tint sheetNum = book.getNumberOfSheets();\n\t\t\tif(sheetNum>0){\n\t\t\t\tparams.setSheetNum(sheetNum);\n\t\t\t}\n\t\t}\n\t\t//update-end-author:taoyan date:20211210 for:https://gitee.com/jeecg/jeecg-boot/issues/I45C32 导入空白sheet报错\n\t\t//end-------author:liusq------date:20210313-----for:-------多sheet导入改造点--------\n\t\tcreateErrorCellStyle(book);\n\t\tMap<String, PictureData> pictures;\n\t\t// 获取指定的sheet名称\n\t\tString sheetName = params.getSheetName();\n\n\t\t//update-begin-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数\n\t\tfor (int i = params.getStartSheetIndex(); i < params.getStartSheetIndex()\n\t\t\t\t+ params.getSheetNum(); i++) {\n\t\t//update-end-author:liusq date:20220609 for:issues/I57UPC excel导入 ImportParams 中没有startSheetIndex参数\n\n\t\t\t//update-begin-author:taoyan date:2023-3-4 for: 导入数据支持指定sheet名称\n\t\t\tif(sheetName!=null && !\"\".equals(sheetName)){\n\t\t\t\tSheet tempSheet = book.getSheetAt(i);\n\t\t\t\tif(!sheetName.equals(tempSheet.getSheetName())){\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\t//update-end-author:taoyan date:2023-3-4 for: 导入数据支持指定sheet名称\n\t\t\t\t\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\" start to read excel by is ,startTime is {}\", System.currentTimeMillis());\n\t\t\t}\n\t\t\tif (isXSSFWorkbook) {\n\t\t\t\tpictures = PoiPublicUtil.getSheetPictrues07((XSSFSheet) book.getSheetAt(i), (XSSFWorkbook) book);\n\t\t\t\t//update-begin---author:chenrui ---date:20240403  for：[issue/#5987]嵌入单元格图片无法导入------------\n                Map<String, PictureData> cellImages = PoiPublicUtil.getCellImages(book.getSheetAt(i), inCopy, book);\n                if (!cellImages.isEmpty()) {\n                    pictures.putAll(cellImages);\n                }\n\t\t\t\t//update-end---author:chenrui ---date:20240403  for：[issue/#5987]嵌入单元格图片无法导入------------\n\t\t\t} else {\n\t\t\t\tpictures = PoiPublicUtil.getSheetPictrues03((HSSFSheet) book.getSheetAt(i), (HSSFWorkbook) book);\n\t\t\t}\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\" end to read excel by is ,endTime is {}\", new Date().getTime());\n\t\t\t}\n\t\t\tresult.addAll(importExcel(result, book.getSheetAt(i), pojoClass, params, pictures));\n\t\t\tif (LOGGER.isDebugEnabled()) {\n\t\t\t\tLOGGER.debug(\" end to read excel list by pos ,endTime is {}\", new Date().getTime());\n\t\t\t}\n\t\t}\n\t\tif (params.isNeedSave()) {\n\t\t\tsaveThisExcel(params, pojoClass, isXSSFWorkbook, book);\n\t\t}\n\t\treturn new ExcelImportResult(result, verfiyFail, book);\n\t}\n\t/**\n\t *\n\t * @param is\n\t * @return\n\t * @throws IOException\n\t */\n\tpublic static byte[] getBytes(InputStream is) throws IOException {\n\t\tByteArrayOutputStream buffer = new ByteArrayOutputStream();\n\n\t\tint len;\n\t\tbyte[] data = new byte[100000];\n\t\twhile ((len = is.read(data, 0, data.length)) != -1) {\n\t\t\tbuffer.write(data, 0, len);\n\t\t}\n\n\t\tbuffer.flush();\n\t\treturn buffer.toByteArray();\n\t}\n\n\t/**\n\t * 保存字段值(获取值,校验值,追加错误信息)\n\t * \n\t * @param params\n\t * @param object\n\t * @param cell\n\t * @param excelParams\n\t * @param titleString\n\t * @param row\n\t * @throws Exception\n\t * @return\n\t */\n\tprivate Object saveFieldValue(ImportParams params, Object object, Cell cell, Map<String, ExcelImportEntity> excelParams, String titleString, Row row) throws Exception {\n\t\tObject value = cellValueServer.getValue(params.getDataHanlder(), object, cell, excelParams, titleString);\n\t\tif (object instanceof Map) {\n\t\t\tif (params.getDataHanlder() != null) {\n\t\t\t\tparams.getDataHanlder().setMapValue((Map) object, titleString, value);\n\t\t\t} else {\n\t\t\t\t((Map) object).put(titleString, value);\n\t\t\t}\n\t\t} else {\n\t\t\tExcelVerifyHanlderResult verifyResult = verifyHandlerServer.verifyData(object, value, titleString, excelParams.get(titleString).getVerify(), params.getVerifyHanlder());\n\t\t\tif (verifyResult.isSuccess()) {\n\t\t\t\tsetValues(excelParams.get(titleString), object, value);\n\t\t\t} else {\n\t\t\t\tCell errorCell = row.createCell(row.getLastCellNum());\n\t\t\t\terrorCell.setCellValue(verifyResult.getMsg());\n\t\t\t\terrorCell.setCellStyle(errorCellStyle);\n\t\t\t\tverfiyFail = true;\n\t\t\t\tthrow new ExcelImportException(ExcelImportEnum.VERIFY_ERROR);\n\t\t\t}\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * \n\t * @param object\n\t * @param picId\n\t * @param excelParams\n\t * @param titleString\n\t * @param pictures\n\t * @param params\n\t * @throws Exception\n\t */\n\tprivate void saveImage(Object object, String picId, Map<String, ExcelImportEntity> excelParams, String titleString, Map<String, PictureData> pictures, ImportParams params) throws Exception {\n\t\tif (pictures == null || pictures.get(picId)==null) {\n\t\t\treturn;\n\t\t}\n\t\tPictureData image = pictures.get(picId);\n\t\tbyte[] data = image.getData();\n\t\tString fileName = \"pic\" + Math.round(Math.random() * 100000000000L);\n\t\tfileName += \".\" + PoiPublicUtil.getFileExtendName(data);\n\t\t//update-beign-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159\n\t\tint saveType = excelParams.get(titleString).getSaveType();\n\t\tif ( saveType == 1) {\n\t\t\tString path = PoiPublicUtil.getWebRootPath(getSaveUrl(excelParams.get(titleString), object));\n\t\t\tFile savefile = new File(path);\n\t\t\tif (!savefile.exists()) {\n\t\t\t\tsavefile.mkdirs();\n\t\t\t}\n\t\t\tsavefile = new File(path + \"/\" + fileName);\n\t\t\tFileOutputStream fos = new FileOutputStream(savefile);\n\t\t\tfos.write(data);\n\t\t\tfos.close();\n\t\t\tsetValues(excelParams.get(titleString), object, getSaveUrl(excelParams.get(titleString), object) + \"/\" + fileName);\n\t\t} else if(saveType==2) {\n\t\t\tsetValues(excelParams.get(titleString), object, data);\n\t\t} else {\n\t\t\tImportFileServiceI importFileService = null;\n\t\t\ttry {\n\t\t\t\timportFileService = ApplicationContextUtil.getContext().getBean(ImportFileServiceI.class);\n\t\t\t} catch (Exception e) {\n\t\t\t\tSystem.err.println(e.getMessage());\n\t\t\t}\n\t\t\tif(importFileService!=null){\n\t\t\t\t//update-beign-author:liusq date:20230411 for:【issue/4415】autopoi-web 导入图片字段时无法指定保存路径\n\t\t\t\tString saveUrl = excelParams.get(titleString).getSaveUrl();\n\t\t\t\tString dbPath;\n\t\t\t\tif(StringUtils.isNotBlank(saveUrl)){\n\t\t\t\t\tLOGGER.debug(\"图片保存路径saveUrl = \"+saveUrl);\n\t\t\t\t\tMatcher matcher = lettersAndNumbersPattern.matcher(saveUrl);\n\t\t\t\t\tif(!matcher.matches()){\n\t\t\t\t\t\tLOGGER.warn(\"图片保存路径格式错误，只能设置字母和数字的组合!\");\n\t\t\t\t\t\tdbPath = importFileService.doUpload(data);\n\t\t\t\t\t}else{\n\t\t\t\t\t\tdbPath = importFileService.doUpload(data,saveUrl);\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tdbPath = importFileService.doUpload(data);\n\t\t\t\t}\n\t\t\t\t//update-end-author:liusq date:20230411 for:【issue/4415】autopoi-web 导入图片字段时无法指定保存路径\n\t\t\t\tsetValues(excelParams.get(titleString), object, dbPath);\n\t\t\t}\n\t\t}\n\t\t//update-end-author:taoyan date:20200302 for:【多任务】online 专项集中问题 LOWCOD-159\n\t}\n\n\tprivate void createErrorCellStyle(Workbook workbook) {\n\t\terrorCellStyle = workbook.createCellStyle();\n\t\tFont font = workbook.createFont();\n\t\tfont.setColor(Font.COLOR_RED);\n\t\terrorCellStyle.setFont(font);\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/base/ImportBaseService.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.base;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.core.util.ApplicationContextUtil;\nimport org.jeecgframework.dict.service.AutoPoiDictServiceI;\nimport org.jeecgframework.poi.excel.annotation.Excel;\nimport org.jeecgframework.poi.excel.annotation.ExcelCollection;\nimport org.jeecgframework.poi.excel.annotation.ExcelEntity;\nimport org.jeecgframework.poi.excel.annotation.ExcelVerify;\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;\nimport org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\n\n/**\n * 导入基础和,普通方法和Sax共用\n * \n * @author JEECG\n * @date 2015年1月9日 下午10:25:53\n */\npublic class ImportBaseService {\n\n\t/**\n\t * 把这个注解解析放到类型对象中\n\t * \n\t * @param targetId\n\t * @param field\n\t * @param excelEntity\n\t * @param pojoClass\n\t * @param getMethods\n\t * @param temp\n\t * @throws Exception\n\t */\n\tpublic void addEntityToMap(String targetId, Field field, ExcelImportEntity excelEntity, Class<?> pojoClass, List<Method> getMethods, Map<String, ExcelImportEntity> temp) throws Exception {\n\t\tExcel excel = field.getAnnotation(Excel.class);\n\t\texcelEntity = new ExcelImportEntity();\n\t\texcelEntity.setType(excel.type());\n\t\texcelEntity.setSaveUrl(excel.savePath());\n\t\texcelEntity.setSaveType(excel.imageType());\n\t\texcelEntity.setReplace(excel.replace());\n\t\texcelEntity.setDatabaseFormat(excel.databaseFormat());\n\t\texcelEntity.setVerify(getImportVerify(field));\n\t\texcelEntity.setSuffix(excel.suffix());\n\t\texcelEntity.setNumFormat(excel.numFormat());\n\t\texcelEntity.setGroupName(excel.groupName());\n\t\t//update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex\n\t\texcelEntity.setFixedIndex(excel.fixedIndex());\n\t\t//update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex\n\n\t\t//update-begin-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题\n\t\texcelEntity.setMultiReplace(excel.multiReplace());\n\t\tif(StringUtils.isNotEmpty(excel.dicCode())){\n\t\t\tAutoPoiDictServiceI jeecgDictService = null;\n\t\t\ttry {\n\t\t\t\tjeecgDictService = ApplicationContextUtil.getContext().getBean(AutoPoiDictServiceI.class);\n\t\t\t} catch (Exception e) {\n\t\t\t}\n\t\t\tif(jeecgDictService!=null){\n\t\t\t\t String[] dictReplace = jeecgDictService.queryDict(excel.dictTable(), excel.dicCode(), excel.dicText());\n\t\t\t\t if(excelEntity.getReplace()!=null && dictReplace!=null && dictReplace.length!=0){\n\t\t\t\t\t excelEntity.setReplace(dictReplace);\n\t\t\t\t }\n\t\t\t}\n\t\t}\n\t\t//update-end-author:taoYan date:20180202 for:TASK #2067 【bug excel 问题】excel导入字典文本翻译问题\n\t\tgetExcelField(targetId, field, excelEntity, excel, pojoClass);\n\t\tif (getMethods != null) {\n\t\t\tList<Method> newMethods = new ArrayList<Method>();\n\t\t\tnewMethods.addAll(getMethods);\n\t\t\tnewMethods.add(excelEntity.getMethod());\n\t\t\texcelEntity.setMethods(newMethods);\n\t\t}\n\t\t//update-begin-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex\n\t\tif (excelEntity.getFixedIndex() != -1) {\n\t\t\ttemp.put(\"FIXED_\" + excelEntity.getFixedIndex(), excelEntity);\n\t\t} else {\n\t\t\ttemp.put(excelEntity.getName(), excelEntity);\n\t\t}\n\t\t//update-end-author:liusq date:20220310 for:[issues/I4PU45]@excel里面新增属性fixedIndex\n\t}\n\n\t/**\n\t * 获取导入校验参数\n\t * \n\t * @param field\n\t * @return\n\t */\n\tpublic ExcelVerifyEntity getImportVerify(Field field) {\n\t\tExcelVerify verify = field.getAnnotation(ExcelVerify.class);\n\t\tif (verify != null) {\n\t\t\tExcelVerifyEntity entity = new ExcelVerifyEntity();\n\t\t\tentity.setEmail(verify.isEmail());\n\t\t\tentity.setInterHandler(verify.interHandler());\n\t\t\tentity.setMaxLength(verify.maxLength());\n\t\t\tentity.setMinLength(verify.minLength());\n\t\t\tentity.setMobile(verify.isMobile());\n\t\t\tentity.setNotNull(verify.notNull());\n\t\t\tentity.setRegex(verify.regex());\n\t\t\tentity.setRegexTip(verify.regexTip());\n\t\t\tentity.setTel(verify.isTel());\n\t\t\treturn entity;\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 获取需要导出的全部字段\n\t * \n\t * @param targetId\n\t *            目标ID\n\t * @param fields\n\t * @param excelCollection\n\t * @throws Exception\n\t */\n\tpublic void getAllExcelField(String targetId, Field[] fields, Map<String, ExcelImportEntity> excelParams, List<ExcelCollectionParams> excelCollection, Class<?> pojoClass, List<Method> getMethods) throws Exception {\n\t\tExcelImportEntity excelEntity = null;\n\t\tfor (int i = 0; i < fields.length; i++) {\n\t\t\tField field = fields[i];\n\t\t\tif (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (PoiPublicUtil.isCollection(field.getType())) {\n\t\t\t\t// 集合对象设置属性\n\t\t\t\tExcelCollectionParams collection = new ExcelCollectionParams();\n\t\t\t\tcollection.setName(field.getName());\n\t\t\t\tMap<String, ExcelImportEntity> temp = new HashMap<String, ExcelImportEntity>();\n\t\t\t\tParameterizedType pt = (ParameterizedType) field.getGenericType();\n\t\t\t\tClass<?> clz = (Class<?>) pt.getActualTypeArguments()[0];\n\t\t\t\tcollection.setType(clz);\n\t\t\t\tgetExcelFieldList(targetId, PoiPublicUtil.getClassFields(clz), clz, temp, null);\n\t\t\t\tcollection.setExcelParams(temp);\n\t\t\t\tcollection.setExcelName(field.getAnnotation(ExcelCollection.class).name());\n\t\t\t\tadditionalCollectionName(collection);\n\t\t\t\texcelCollection.add(collection);\n\t\t\t} else if (PoiPublicUtil.isJavaClass(field)) {\n\t\t\t\taddEntityToMap(targetId, field, excelEntity, pojoClass, getMethods, excelParams);\n\t\t\t} else {\n\t\t\t\tList<Method> newMethods = new ArrayList<Method>();\n\t\t\t\tif (getMethods != null) {\n\t\t\t\t\tnewMethods.addAll(getMethods);\n\t\t\t\t}\n\t\t\t\tnewMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass));\n\t\t\t\t//update-begin-author:taoyan date:20210531 for:excel导入支持 注解@ExcelEntity显示合并表头\n\t\t\t\tExcelEntity excel = field.getAnnotation(ExcelEntity.class);\n\t\t\t\tif(excel.show()==true){\n\t\t\t\t\tMap<String, ExcelImportEntity> subExcelParams = new HashMap<>();\n\t\t\t\t\t// 这里有个设计的坑，导出的时候最后一个参数是null, 即getgetMethods获取的是空，导入的时候需要设置层级getmethod\n\t\t\t\t\tgetAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), subExcelParams, excelCollection, field.getType(), newMethods);\n\t\t\t\t\tfor(String key: subExcelParams.keySet()){\n\t\t\t\t\t\texcelParams.put(excel.name()+\"_\"+key, subExcelParams.get(key));\n\t\t\t\t\t}\n\t\t\t\t}else{\n\t\t\t\t\tgetAllExcelField(targetId, PoiPublicUtil.getClassFields(field.getType()), excelParams, excelCollection, field.getType(), newMethods);\n\t\t\t\t}\n\t\t\t\t//update-end-author:taoyan date:20210531 for:excel导入支持 注解@ExcelEntity显示合并表头\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 追加集合名称到前面\n\t * \n\t * @param collection\n\t */\n\tprivate void additionalCollectionName(ExcelCollectionParams collection) {\n\t\tSet<String> keys = new HashSet<String>();\n\t\tkeys.addAll(collection.getExcelParams().keySet());\n\t\tfor (String key : keys) {\n\t\t\tcollection.getExcelParams().put(collection.getExcelName() + \"_\" + key, collection.getExcelParams().get(key));\n\t\t\tcollection.getExcelParams().remove(key);\n\t\t}\n\t}\n\n\tpublic void getExcelField(String targetId, Field field, ExcelImportEntity excelEntity, Excel excel, Class<?> pojoClass) throws Exception {\n\t\texcelEntity.setName(getExcelName(excel.name(), targetId));\n\t\tString fieldname = field.getName();\n\t\t//update-begin-author:taoyan for:TASK #2798 【例子】导入扩展方法，支持自定义导入字段转换规则\n\t\texcelEntity.setMethod(PoiPublicUtil.getMethod(fieldname, pojoClass, field.getType(),excel.importConvert()));\n\t\t//update-end-author:taoyan for:TASK #2798 【例子】导入扩展方法，支持自定义导入字段转换规则\n\t\tif (StringUtils.isNotEmpty(excel.importFormat())) {\n\t\t\texcelEntity.setFormat(excel.importFormat());\n\t\t} else {\n\t\t\texcelEntity.setFormat(excel.format());\n\t\t}\n\t}\n\n\tpublic void getExcelFieldList(String targetId, Field[] fields, Class<?> pojoClass, Map<String, ExcelImportEntity> temp, List<Method> getMethods) throws Exception {\n\t\tExcelImportEntity excelEntity = null;\n\t\tfor (int i = 0; i < fields.length; i++) {\n\t\t\tField field = fields[i];\n\t\t\tif (PoiPublicUtil.isNotUserExcelUserThis(null, field, targetId)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (PoiPublicUtil.isJavaClass(field)) {\n\t\t\t\taddEntityToMap(targetId, field, excelEntity, pojoClass, getMethods, temp);\n\t\t\t} else {\n\t\t\t\tList<Method> newMethods = new ArrayList<Method>();\n\t\t\t\tif (getMethods != null) {\n\t\t\t\t\tnewMethods.addAll(getMethods);\n\t\t\t\t}\n\t\t\t\tnewMethods.add(PoiPublicUtil.getMethod(field.getName(), pojoClass, field.getType()));\n\t\t\t\tgetExcelFieldList(targetId, PoiPublicUtil.getClassFields(field.getType()), field.getType(), temp, newMethods);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 判断在这个单元格显示的名称\n\t * \n\t * @param exportName\n\t * @param targetId\n\t * @return\n\t */\n\tpublic String getExcelName(String exportName, String targetId) {\n\t\tif (exportName.indexOf(\"_\") < 0) {\n\t\t\treturn exportName;\n\t\t}\n\t\tif (StringUtils.isBlank(targetId)) {\n\t\t\treturn exportName;\n\t\t}\n\t\tString[] arr = exportName.split(\",\");\n\t\tfor (String str : arr) {\n\t\t\tif (str.indexOf(targetId) != -1) {\n\t\t\t\t//update-begin-author:liusq date：20210127 for:targetId问题处理\n\t\t\t\t//return str.split(\"_\")[0];\n\t\t\t\treturn str.replace(\"_\"+targetId,\"\");\n\t\t\t\t//update-end-author:liusq date：20210127 for:targetId问题处理\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tpublic Object getFieldBySomeMethod(List<Method> list, Object t) throws Exception {\n\t\tMethod m;\n\t\tfor (int i = 0; i < list.size() - 1; i++) {\n\t\t\tm = list.get(i);\n\t\t\tt = m.invoke(t, new Object[] {});\n\t\t}\n\t\treturn t;\n\t}\n\n\tpublic void saveThisExcel(ImportParams params, Class<?> pojoClass, boolean isXSSFWorkbook, Workbook book) throws Exception {\n\t\tString path = PoiPublicUtil.getWebRootPath(getSaveExcelUrl(params, pojoClass));\n\t\tFile savefile = new File(path);\n\t\tif (!savefile.exists()) {\n\t\t\tsavefile.mkdirs();\n\t\t}\n\t\tSimpleDateFormat format = new SimpleDateFormat(\"yyyMMddHHmmss\");\n\t\tFileOutputStream fos = new FileOutputStream(path + \"/\" + format.format(new Date()) + \"_\" + Math.round(Math.random() * 100000) + (isXSSFWorkbook == true ? \".xlsx\" : \".xls\"));\n\t\tbook.write(fos);\n\t\tfos.close();\n\t}\n\n\t/**\n\t * 获取保存的Excel 的真实路径\n\t * \n\t * @param params\n\t * @param pojoClass\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic String getSaveExcelUrl(ImportParams params, Class<?> pojoClass) throws Exception {\n\t\tString url = \"\";\n\t\tif (params.getSaveUrl().equals(\"upload/excelUpload\")) {\n\t\t\turl = pojoClass.getName().split(\"\\\\.\")[pojoClass.getName().split(\"\\\\.\").length - 1];\n\t\t\treturn params.getSaveUrl() + \"/\" + url;\n\t\t}\n\t\treturn params.getSaveUrl();\n\t}\n\n\t/**\n\t * 多个get 最后再set\n\t * \n\t * @param setMethods\n\t * @param object\n\t */\n\tpublic void setFieldBySomeMethod(List<Method> setMethods, Object object, Object value) throws Exception {\n\t\tObject t = getFieldBySomeMethod(setMethods, object);\n\t\tsetMethods.get(setMethods.size() - 1).invoke(t, value);\n\t}\n\n\t/**\n\t * \n\t * @param entity\n\t * @param object\n\t * @param value\n\t * @throws Exception\n\t */\n\tpublic void setValues(ExcelImportEntity entity, Object object, Object value) throws Exception {\n\t\tif (entity.getMethods() != null) {\n\t\t\tsetFieldBySomeMethod(entity.getMethods(), object, value);\n\t\t} else {\n\t\t\tentity.getMethod().invoke(object, value);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/base/ImportFileServiceI.java",
    "content": "package org.jeecgframework.poi.excel.imports.base;\n\npublic interface ImportFileServiceI {\n\n    /**\n     * 上传文件 返回文件地址字符串\n     * @param data\n     * @return\n     */\n    String doUpload(byte[] data);\n\n    /**\n     * 上传文件 返回文件地址字符串\n     * @param data\n     * @param saveUrl 保存路径\n     * @return\n     */\n     String doUpload(byte[] data,String saveUrl);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/package-info.java",
    "content": "/**\n * 导入类\n * @author JEECG\n * @date 2014年6月23日 下午11:05:59\n */\npackage org.jeecgframework.poi.excel.imports;"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SaxReadExcel.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.sax;\n\nimport java.io.InputStream;\nimport java.util.Iterator;\nimport java.util.List;\n\nimport org.apache.poi.openxml4j.opc.OPCPackage;\nimport org.apache.poi.xssf.eventusermodel.XSSFReader;\nimport org.apache.poi.xssf.model.SharedStrings;\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead;\nimport org.jeecgframework.poi.excel.imports.sax.parse.SaxRowRead;\nimport org.jeecgframework.poi.exception.excel.ExcelImportException;\nimport org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.xml.sax.ContentHandler;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.XMLReader;\nimport org.xml.sax.helpers.XMLReaderFactory;\n\n/**\n * 基于SAX Excel大数据读取,读取Excel 07版本,不支持图片读取\n * \n * @author JEECG\n * @date 2014年12月29日 下午9:41:38\n * @version 1.0\n */\n@SuppressWarnings(\"rawtypes\")\npublic class SaxReadExcel {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(SaxReadExcel.class);\n\n\tpublic <T> List<T> readExcel(InputStream inputstream, Class<?> pojoClass, ImportParams params, ISaxRowRead rowRead, IExcelReadRowHanlder hanlder) {\n\t\ttry {\n\t\t\tOPCPackage opcPackage = OPCPackage.open(inputstream);\n\t\t\treturn readExcel(opcPackage, pojoClass, params, rowRead, hanlder);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelImportException(e.getMessage());\n\t\t}\n\t}\n\n\tprivate <T> List<T> readExcel(OPCPackage opcPackage, Class<?> pojoClass, ImportParams params, ISaxRowRead rowRead, IExcelReadRowHanlder hanlder) {\n\t\ttry {\n\t\t\tXSSFReader xssfReader = new XSSFReader(opcPackage);\n\t\t\tSharedStrings sst = xssfReader.getSharedStringsTable();\n\t\t\tif (rowRead == null) {\n\t\t\t\trowRead = new SaxRowRead(pojoClass, params, hanlder);\n\t\t\t}\n\t\t\tXMLReader parser = fetchSheetParser(sst, rowRead);\n\t\t\tIterator<InputStream> sheets = xssfReader.getSheetsData();\n\t\t\tint sheetIndex = 0;\n\t\t\twhile (sheets.hasNext() && sheetIndex < params.getSheetNum()) {\n\t\t\t\tsheetIndex++;\n\t\t\t\tInputStream sheet = sheets.next();\n\t\t\t\tInputSource sheetSource = new InputSource(sheet);\n\t\t\t\tparser.parse(sheetSource);\n\t\t\t\tsheet.close();\n\t\t\t}\n\t\t\treturn rowRead.getList();\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelImportException(\"SAX导入数据失败\");\n\t\t}\n\t}\n\n\tprivate XMLReader fetchSheetParser(SharedStrings sst, ISaxRowRead rowRead) throws SAXException {\n\t\tXMLReader parser = XMLReaderFactory.createXMLReader(\"org.apache.xerces.parsers.SAXParser\");\n\t\tContentHandler handler = new SheetHandler(sst, rowRead);\n\t\tparser.setContentHandler(handler);\n\t\treturn parser;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/SheetHandler.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.sax;\n\nimport java.math.BigDecimal;\nimport java.util.Date;\nimport java.util.List;\n\nimport org.apache.poi.ss.usermodel.DateUtil;\nimport org.apache.poi.xssf.model.SharedStrings;\nimport org.jeecgframework.poi.excel.entity.enmus.CellValueType;\nimport org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity;\nimport org.jeecgframework.poi.excel.imports.sax.parse.ISaxRowRead;\nimport org.xml.sax.Attributes;\nimport org.xml.sax.SAXException;\nimport org.xml.sax.helpers.DefaultHandler;\n\nimport com.google.common.collect.Lists;\n\n/**\n * 回调接口\n * \n * @author JEECG\n * @date 2014年12月29日 下午9:50:09\n */\npublic class SheetHandler extends DefaultHandler {\n\n\tprivate SharedStrings sst;\n\tprivate String lastContents;\n\n\t// 当前行\n\tprivate int curRow = 0;\n\t// 当前列\n\tprivate int curCol = 0;\n\n\tprivate CellValueType type;\n\n\tprivate ISaxRowRead read;\n\n\t// 存储行记录的容器\n\tprivate List<SaxReadCellEntity> rowlist = Lists.newArrayList();\n\n\tpublic SheetHandler(SharedStrings sst, ISaxRowRead rowRead) {\n\t\tthis.sst = sst;\n\t\tthis.read = rowRead;\n\t}\n\n\t@Override\n\tpublic void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {\n\t\t// 置空\n\t\tlastContents = \"\";\n\t\t// c => 单元格\n\t\tif (\"c\".equals(name)) {\n\t\t\t// 如果下一个元素是 SST 的索引，则将nextIsString标记为true\n\t\t\tString cellType = attributes.getValue(\"t\");\n\t\t\tif (\"s\".equals(cellType)) {\n\t\t\t\ttype = CellValueType.String;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// 日期格式\n\t\t\tcellType = attributes.getValue(\"s\");\n\t\t\tif (\"1\".equals(cellType)) {\n\t\t\t\ttype = CellValueType.Date;\n\t\t\t} else if (\"2\".equals(cellType)) {\n\t\t\t\ttype = CellValueType.Number;\n\t\t\t}\n\t\t} else if (\"t\".equals(name)) {// 当元素为t时\n\t\t\ttype = CellValueType.TElement;\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void endElement(String uri, String localName, String name) throws SAXException {\n\n\t\t// 根据SST的索引值的到单元格的真正要存储的字符串\n\t\t// 这时characters()方法可能会被调用多次\n\t\tif (CellValueType.String.equals(type)) {\n\t\t\ttry {\n\t\t\t\tint idx = Integer.parseInt(lastContents);\n\t\t\t\tlastContents = sst.getItemAt(idx).toString();\n\t\t\t} catch (Exception e) {\n\n\t\t\t}\n\t\t}\n\t\t// t元素也包含字符串\n\t\tif (CellValueType.TElement.equals(type)) {\n\t\t\tString value = lastContents.trim();\n\t\t\trowlist.add(curCol, new SaxReadCellEntity(CellValueType.String, value));\n\t\t\tcurCol++;\n\t\t\ttype = CellValueType.None;\n\t\t\t// v => 单元格的值，如果单元格是字符串则v标签的值为该字符串在SST中的索引\n\t\t\t// 将单元格内容加入rowlist中，在这之前先去掉字符串前后的空白符\n\t\t} else if (\"v\".equals(name)) {\n\t\t\tString value = lastContents.trim();\n\t\t\tvalue = value.equals(\"\") ? \" \" : value;\n\t\t\tif (CellValueType.Date.equals(type)) {\n\t\t\t\tDate date = DateUtil.getJavaDate(Double.valueOf(value));\n\t\t\t\trowlist.add(curCol, new SaxReadCellEntity(CellValueType.Date, date));\n\t\t\t} else if (CellValueType.Number.equals(type)) {\n\t\t\t\tBigDecimal bd = new BigDecimal(value);\n\t\t\t\trowlist.add(curCol, new SaxReadCellEntity(CellValueType.Number, bd));\n\t\t\t} else if (CellValueType.String.equals(type)) {\n\t\t\t\trowlist.add(curCol, new SaxReadCellEntity(CellValueType.String, value));\n\t\t\t}\n\t\t\tcurCol++;\n\t\t} else if (name.equals(\"row\")) {// 如果标签名称为 row ，这说明已到行尾，调用 optRows() 方法\n\t\t\tread.parse(curRow, rowlist);\n\t\t\trowlist.clear();\n\t\t\tcurRow++;\n\t\t\tcurCol = 0;\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic void characters(char[] ch, int start, int length) throws SAXException {\n\t\t// 得到单元格内容的值\n\t\tlastContents += new String(ch, start, length);\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/parse/ISaxRowRead.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.sax.parse;\n\nimport java.util.List;\n\nimport org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity;\n\npublic interface ISaxRowRead {\n\t/**\n\t * 获取返回数据\n\t * \n\t * @param <T>\n\t * @return\n\t */\n\tpublic <T> List<T> getList();\n\n\t/**\n\t * 解析数据\n\t * \n\t * @param index\n\t * @param datas\n\t */\n\tpublic void parse(int index, List<SaxReadCellEntity> datas);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/sax/parse/SaxRowRead.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.sax.parse;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelCollectionParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelImportEntity;\nimport org.jeecgframework.poi.excel.entity.sax.SaxReadCellEntity;\nimport org.jeecgframework.poi.excel.imports.CellValueServer;\nimport org.jeecgframework.poi.excel.imports.base.ImportBaseService;\nimport org.jeecgframework.poi.exception.excel.ExcelImportException;\nimport org.jeecgframework.poi.handler.inter.IExcelReadRowHanlder;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport com.google.common.collect.Lists;\n\n/**\n * 当行读取数据\n * \n * @author JEECG\n * @param <T>\n * @date 2015年1月1日 下午7:59:39\n */\n@SuppressWarnings({ \"rawtypes\", \"unchecked\" })\npublic class SaxRowRead extends ImportBaseService implements ISaxRowRead {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(SaxRowRead.class);\n\t/** 需要返回的数据 **/\n\tprivate List list;\n\t/** 导出的对象 **/\n\tprivate Class<?> pojoClass;\n\t/** 导入参数 **/\n\tprivate ImportParams params;\n\t/** 列表头对应关系 **/\n\tprivate Map<Integer, String> titlemap = new HashMap<Integer, String>();\n\t/** 当前的对象 **/\n\tprivate Object object = null;\n\n\tprivate Map<String, ExcelImportEntity> excelParams = new HashMap<String, ExcelImportEntity>();\n\n\tprivate List<ExcelCollectionParams> excelCollection = new ArrayList<ExcelCollectionParams>();\n\n\tprivate String targetId;\n\n\tprivate CellValueServer cellValueServer;\n\n\tprivate IExcelReadRowHanlder hanlder;\n\n\tpublic SaxRowRead(Class<?> pojoClass, ImportParams params, IExcelReadRowHanlder hanlder) {\n\t\tlist = Lists.newArrayList();\n\t\tthis.params = params;\n\t\tthis.pojoClass = pojoClass;\n\t\tcellValueServer = new CellValueServer();\n\t\tthis.hanlder = hanlder;\n\t\tinitParams(pojoClass, params);\n\t}\n\n\tprivate void initParams(Class<?> pojoClass, ImportParams params) {\n\t\ttry {\n\n\t\t\tField fileds[] = PoiPublicUtil.getClassFields(pojoClass);\n\t\t\tExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class);\n\t\t\tif (etarget != null) {\n\t\t\t\ttargetId = etarget.value();\n\t\t\t}\n\t\t\tgetAllExcelField(targetId, fileds, excelParams, excelCollection, pojoClass, null);\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelImportException(e.getMessage());\n\t\t}\n\n\t}\n\n\t@Override\n\tpublic <T> List<T> getList() {\n\t\treturn list;\n\t}\n\n\t@Override\n\tpublic void parse(int index, List<SaxReadCellEntity> datas) {\n\t\ttry {\n\t\t\tif (datas == null || datas.size() == 0) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// 标题行跳过\n\t\t\tif (index < params.getTitleRows()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// 表头行\n\t\t\tif (index < params.getTitleRows() + params.getHeadRows()) {\n\t\t\t\taddHeadData(datas);\n\t\t\t} else {\n\t\t\t\taddListData(datas);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new ExcelImportException(e.getMessage());\n\t\t}\n\t}\n\n\t/**\n\t * 集合元素处理\n\t * \n\t * @param datas\n\t */\n\tprivate void addListData(List<SaxReadCellEntity> datas) throws Exception {\n\t\t// 判断是集合元素还是不是集合元素,如果是就继续加入这个集合,不是就创建新的对象\n\t\tif ((datas.get(params.getKeyIndex()) == null || StringUtils.isEmpty(String.valueOf(datas.get(params.getKeyIndex()).getValue()))) && object != null) {\n\t\t\tfor (ExcelCollectionParams param : excelCollection) {\n\t\t\t\taddListContinue(object, param, datas, titlemap, targetId, params);\n\t\t\t}\n\t\t} else {\n\t\t\tif (object != null && hanlder != null) {\n\t\t\t\thanlder.hanlder(object);\n\t\t\t}\n\t\t\tobject = PoiPublicUtil.createObject(pojoClass, targetId);\n\t\t\tSaxReadCellEntity entity;\n\t\t\tfor (int i = 0, le = datas.size(); i < le; i++) {\n\t\t\t\tentity = datas.get(i);\n\t\t\t\tString titleString = (String) titlemap.get(i);\n\t\t\t\tif (excelParams.containsKey(titleString)) {\n\t\t\t\t\tsaveFieldValue(params, object, entity, excelParams, titleString);\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor (ExcelCollectionParams param : excelCollection) {\n\t\t\t\taddListContinue(object, param, datas, titlemap, targetId, params);\n\t\t\t}\n\t\t\tif (hanlder == null) {\n\t\t\t\tlist.add(object);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/***\n\t * 向List里面继续添加元素\n\t * \n\t * @param exclusions\n\t * @param object\n\t * @param param\n\t * @param datas\n\t * @param titlemap\n\t * @param targetId\n\t * @param params\n\t */\n\tprivate void addListContinue(Object object, ExcelCollectionParams param, List<SaxReadCellEntity> datas, Map<Integer, String> titlemap, String targetId, ImportParams params) throws Exception {\n\t\tCollection collection = (Collection) PoiPublicUtil.getMethod(param.getName(), object.getClass()).invoke(object, new Object[] {});\n\t\tObject entity = PoiPublicUtil.createObject(param.getType(), targetId);\n\t\tboolean isUsed = false;// 是否需要加上这个对象\n\t\tfor (int i = 0; i < datas.size(); i++) {\n\t\t\tString titleString = (String) titlemap.get(i);\n\t\t\tif (param.getExcelParams().containsKey(titleString)) {\n\t\t\t\tsaveFieldValue(params, entity, datas.get(i), param.getExcelParams(), titleString);\n\t\t\t\tisUsed = true;\n\t\t\t}\n\t\t}\n\t\tif (isUsed) {\n\t\t\tcollection.add(entity);\n\t\t}\n\t}\n\n\t/**\n\t * 设置值\n\t * \n\t * @param params\n\t * @param object\n\t * @param entity\n\t * @param excelParams\n\t * @param titleString\n\t * @throws Exception\n\t */\n\tprivate void saveFieldValue(ImportParams params, Object object, SaxReadCellEntity entity, Map<String, ExcelImportEntity> excelParams, String titleString) throws Exception {\n\t\tObject value = cellValueServer.getValue(params.getDataHanlder(), object, entity, excelParams, titleString);\n\t\tsetValues(excelParams.get(titleString), object, value);\n\t}\n\n\t/**\n\t * put 表头数据\n\t * \n\t * @param datas\n\t */\n\tprivate void addHeadData(List<SaxReadCellEntity> datas) {\n\t\tfor (int i = 0; i < datas.size(); i++) {\n\t\t\tif (StringUtils.isNotEmpty(String.valueOf(datas.get(i).getValue()))) {\n\t\t\t\ttitlemap.put(i, String.valueOf(datas.get(i).getValue()));\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/verifys/BaseVerifyHandler.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.verifys;\n\nimport java.util.regex.Pattern;\n\nimport org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult;\n\n/**\n * 基础校验工具类\n * \n * @author JEECG\n * @date 2014年6月23日 下午11:10:12\n */\npublic class BaseVerifyHandler {\n\n\tprivate static String NOT_NULL = \"不允许为空\";\n\tprivate static String IS_MOBILE = \"不是手机号\";\n\tprivate static String IS_TEL = \"不是电话号码\";\n\tprivate static String IS_EMAIL = \"不是邮箱地址\";\n\tprivate static String MIN_LENGHT = \"小于规定长度\";\n\tprivate static String MAX_LENGHT = \"超过规定长度\";\n\n\tprivate static Pattern mobilePattern = Pattern.compile(\"^[1][3,4,5,8,7][0-9]{9}$\");\n\n\tprivate static Pattern telPattern = Pattern.compile(\"^([0][1-9]{2,3}-)?[0-9]{5,10}$\");\n\n\tprivate static Pattern emailPattern = Pattern.compile(\"^([a-zA-Z0-9]+[_|\\\\_|\\\\.]?)*[a-zA-Z0-9]+@([a-zA-Z0-9]+[_|\\\\_|\\\\.]?)*[a-zA-Z0-9]+\\\\.[a-zA-Z]{2,3}$\");\n\n\t/**\n\t * email校验\n\t * \n\t * @param name\n\t * @param val\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult isEmail(String name, Object val) {\n\t\tif (!emailPattern.matcher(String.valueOf(val)).matches()) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + IS_EMAIL);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n\t/**\n\t * 手机校验\n\t * \n\t * @param name\n\t * @param val\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult isMobile(String name, Object val) {\n\t\tif (!mobilePattern.matcher(String.valueOf(val)).matches()) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + IS_MOBILE);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n\t/**\n\t * 电话校验\n\t * \n\t * @param name\n\t * @param val\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult isTel(String name, Object val) {\n\t\tif (!telPattern.matcher(String.valueOf(val)).matches()) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + IS_TEL);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n\t/**\n\t * 最大长度校验\n\t * \n\t * @param name\n\t * @param val\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult maxLength(String name, Object val, int maxLength) {\n\t\tif (notNull(name, val).isSuccess() && String.valueOf(val).length() > maxLength) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + MAX_LENGHT);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n\t/**\n\t * 最小长度校验\n\t * \n\t * @param name\n\t * @param val\n\t * @param minLength\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult minLength(String name, Object val, int minLength) {\n\t\tif (notNull(name, val).isSuccess() && String.valueOf(val).length() < minLength) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + MIN_LENGHT);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n\t/**\n\t * 非空校验\n\t * \n\t * @param name\n\t * @param val\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult notNull(String name, Object val) {\n\t\tif (val == null || val.toString().equals(\"\")) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + NOT_NULL);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n\t/**\n\t * 正则表达式校验\n\t * \n\t * @param name\n\t * @param val\n\t * @param regex\n\t * @param regexTip\n\t * @return\n\t */\n\tpublic static ExcelVerifyHanlderResult regex(String name, Object val, String regex, String regexTip) {\n\t\tPattern pattern = Pattern.compile(regex);\n\t\tif (!pattern.matcher(String.valueOf(val)).matches()) {\n\t\t\treturn new ExcelVerifyHanlderResult(false, name + regexTip);\n\t\t}\n\t\treturn new ExcelVerifyHanlderResult(true);\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/excel/imports/verifys/VerifyHandlerServer.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.imports.verifys;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jeecgframework.poi.excel.entity.params.ExcelVerifyEntity;\nimport org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult;\nimport org.jeecgframework.poi.handler.inter.IExcelVerifyHandler;\n\n/**\n * 校验服务\n * \n * @author JEECG\n * @date 2014年6月29日 下午4:37:56\n */\npublic class VerifyHandlerServer {\n\n\tprivate final static ExcelVerifyHanlderResult DEFAULT_RESULT = new ExcelVerifyHanlderResult(true);\n\n\tprivate void addVerifyResult(ExcelVerifyHanlderResult hanlderResult, ExcelVerifyHanlderResult result) {\n\t\tif (!hanlderResult.isSuccess()) {\n\t\t\tresult.setSuccess(false);\n\t\t\tresult.setMsg((StringUtils.isEmpty(result.getMsg()) ? \"\" : result.getMsg() + \" , \") + hanlderResult.getMsg());\n\t\t}\n\t}\n\n\t/**\n\t * 校驗數據\n\t * \n\t * @param object\n\t * @param value\n\t * @param titleString\n\t * @param verify\n\t * @param excelVerifyHandler\n\t */\n\tpublic ExcelVerifyHanlderResult verifyData(Object object, Object value, String name, ExcelVerifyEntity verify, IExcelVerifyHandler excelVerifyHandler) {\n\t\tif (verify == null) {\n\t\t\treturn DEFAULT_RESULT;\n\t\t}\n\t\tExcelVerifyHanlderResult result = new ExcelVerifyHanlderResult(true, \"\");\n\t\tif (verify.isNotNull()) {\n\t\t\taddVerifyResult(BaseVerifyHandler.notNull(name, value), result);\n\t\t}\n\t\tif (verify.isEmail()) {\n\t\t\taddVerifyResult(BaseVerifyHandler.isEmail(name, value), result);\n\t\t}\n\t\tif (verify.isMobile()) {\n\t\t\taddVerifyResult(BaseVerifyHandler.isMobile(name, value), result);\n\t\t}\n\t\tif (verify.isTel()) {\n\t\t\taddVerifyResult(BaseVerifyHandler.isTel(name, value), result);\n\t\t}\n\t\tif (verify.getMaxLength() != -1) {\n\t\t\taddVerifyResult(BaseVerifyHandler.maxLength(name, value, verify.getMaxLength()), result);\n\t\t}\n\t\tif (verify.getMinLength() != -1) {\n\t\t\taddVerifyResult(BaseVerifyHandler.minLength(name, value, verify.getMinLength()), result);\n\t\t}\n\t\tif (StringUtils.isNotEmpty(verify.getRegex())) {\n\t\t\taddVerifyResult(BaseVerifyHandler.regex(name, value, verify.getRegex(), verify.getRegexTip()), result);\n\t\t}\n\t\tif (verify.isInterHandler()) {\n\t\t\taddVerifyResult(excelVerifyHandler.verifyHandler(object, name, value), result);\n\t\t}\n\t\treturn result;\n\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/exception/excel/ExcelExportException.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.exception.excel;\n\nimport org.jeecgframework.poi.exception.excel.enums.ExcelExportEnum;\n\n/**\n * 导出异常\n * \n * @author JEECG\n * @date 2014年6月19日 下午10:56:18\n */\npublic class ExcelExportException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate ExcelExportEnum type;\n\n\tpublic ExcelExportException() {\n\t\tsuper();\n\t}\n\n\tpublic ExcelExportException(ExcelExportEnum type) {\n\t\tsuper(type.getMsg());\n\t\tthis.type = type;\n\t}\n\n\tpublic ExcelExportException(ExcelExportEnum type, Throwable cause) {\n\t\tsuper(type.getMsg(), cause);\n\t}\n\n\tpublic ExcelExportException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ExcelExportException(String message, ExcelExportEnum type) {\n\t\tsuper(message);\n\t\tthis.type = type;\n\t}\n\n\tpublic ExcelExportEnum getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(ExcelExportEnum type) {\n\t\tthis.type = type;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/exception/excel/ExcelImportException.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.exception.excel;\n\nimport org.jeecgframework.poi.exception.excel.enums.ExcelImportEnum;\n\n/**\n * 导入异常\n * \n * @author JEECG\n * @date 2014年6月29日 下午2:23:43\n */\npublic class ExcelImportException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tprivate ExcelImportEnum type;\n\n\tpublic ExcelImportException() {\n\t\tsuper();\n\t}\n\n\tpublic ExcelImportException(ExcelImportEnum type) {\n\t\tsuper(type.getMsg());\n\t\tthis.type = type;\n\t}\n\n\tpublic ExcelImportException(ExcelImportEnum type, Throwable cause) {\n\t\tsuper(type.getMsg(), cause);\n\t\tthis.type = type;\n\t}\n\n\tpublic ExcelImportException(String message) {\n\t\tsuper(message);\n\t}\n\n\tpublic ExcelImportException(String message, ExcelImportEnum type) {\n\t\tsuper(message);\n\t\tthis.type = type;\n\t}\n\n\tpublic ExcelImportEnum getType() {\n\t\treturn type;\n\t}\n\n\tpublic void setType(ExcelImportEnum type) {\n\t\tthis.type = type;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/exception/excel/enums/ExcelExportEnum.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.exception.excel.enums;\n\n/**\n * 导出异常类型枚举\n * \n * @author JEECG\n * @date 2014年6月19日 下午10:59:51\n */\npublic enum ExcelExportEnum {\n\n\tPARAMETER_ERROR(\"Excel 导出   参数错误\"), EXPORT_ERROR(\"Excel导出错误\"),TEMPLATE_ERROR (\"Excel 模板错误\");;\n\n\tprivate String msg;\n\n\tExcelExportEnum(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n\tpublic String getMsg() {\n\t\treturn msg;\n\t}\n\n\tpublic void setMsg(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/exception/excel/enums/ExcelImportEnum.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.exception.excel.enums;\n\n/**\n * 导出异常类型枚举\n * \n * @author JEECG\n * @date 2014年6月19日 下午10:59:51\n */\npublic enum ExcelImportEnum {\n\n\tGET_VALUE_ERROR(\"Excel 值获取失败\"), VERIFY_ERROR(\"值校验失败\");\n\n\tprivate String msg;\n\n\tExcelImportEnum(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n\tpublic String getMsg() {\n\t\treturn msg;\n\t}\n\n\tpublic void setMsg(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/exception/word/WordExportException.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.exception.word;\n\nimport org.jeecgframework.poi.exception.word.enmus.WordExportEnum;\n\n/**\n * word导出异常\n * \n * @author JEECG\n * @date 2014年8月9日 下午10:32:51\n */\npublic class WordExportException extends RuntimeException {\n\n\tprivate static final long serialVersionUID = 1L;\n\n\tpublic WordExportException() {\n\t\tsuper();\n\t}\n\n\tpublic WordExportException(String msg) {\n\t\tsuper(msg);\n\t}\n\n\tpublic WordExportException(WordExportEnum exception) {\n\t\tsuper(exception.getMsg());\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/exception/word/enmus/WordExportEnum.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.exception.word.enmus;\n\n/**\n * 导出异常枚举\n * \n * @author JEECG\n * @date 2014年8月9日 下午10:34:58\n */\npublic enum WordExportEnum {\n\n\tEXCEL_PARAMS_ERROR(\"Excel 导出 参数错误\"), EXCEL_HEAD_HAVA_NULL(\"Excel 表头 有的字段为空\"), EXCEL_NO_HEAD(\"Excel 没有表头\");\n\n\tprivate String msg;\n\n\tWordExportEnum(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n\tpublic String getMsg() {\n\t\treturn msg;\n\t}\n\n\tpublic void setMsg(String msg) {\n\t\tthis.msg = msg;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/impl/ExcelDataHandlerDefaultImpl.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.handler.impl;\n\nimport java.util.Map;\n\nimport org.jeecgframework.poi.handler.inter.IExcelDataHandler;\nimport org.apache.poi.ss.usermodel.CreationHelper;\nimport org.apache.poi.ss.usermodel.Hyperlink;\n/**\n * 数据处理默认实现,返回空\n * \n * @author JEECG\n * @date 2014年6月20日 上午12:11:52\n */\npublic abstract class ExcelDataHandlerDefaultImpl implements IExcelDataHandler {\n\t/**\n\t * 需要处理的字段\n\t */\n\tprivate String[] needHandlerFields;\n\n\t@Override\n\tpublic Object exportHandler(Object obj, String name, Object value) {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic String[] getNeedHandlerFields() {\n\t\treturn needHandlerFields;\n\t}\n\n\t@Override\n\tpublic Object importHandler(Object obj, String name, Object value) {\n\t\treturn value;\n\t}\n\n\t@Override\n\tpublic void setNeedHandlerFields(String[] needHandlerFields) {\n\t\tthis.needHandlerFields = needHandlerFields;\n\t}\n\n\t@Override\n\tpublic void setMapValue(Map<String, Object> map, String originKey, Object value) {\n\t\tmap.put(originKey, value);\n\t}\n\n\t@Override\n\tpublic Hyperlink getHyperlink(CreationHelper creationHelper, Object obj, String name, Object value) {\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/impl/package-info.java",
    "content": "/**\n * 对接口的抽象的默认实现,避免用户实现过多方法\n * @author JEECG\n * @date 2014年6月20日 上午12:09:27\n */\npackage org.jeecgframework.poi.handler.impl;"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelDataHandler.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.handler.inter;\n\nimport java.util.Map;\nimport org.apache.poi.ss.usermodel.CreationHelper;\nimport org.apache.poi.ss.usermodel.Hyperlink;\n/**\n * Excel 导入导出 数据处理接口\n * \n * @author JEECG\n * @date 2014年6月19日 下午11:59:45\n */\npublic interface IExcelDataHandler {\n\n\t/**\n\t * 导出处理方法\n\t * \n\t * @param obj\n\t *            当前对象\n\t * @param name\n\t *            当前字段名称\n\t * @param value\n\t *            当前值\n\t * @return\n\t */\n\tpublic Object exportHandler(Object obj, String name, Object value);\n\n\t/**\n\t * 获取需要处理的字段,导入和导出统一处理了, 减少书写的字段\n\t * \n\t * @return\n\t */\n\tpublic String[] getNeedHandlerFields();\n\n\t/**\n\t * 导入处理方法 当前对象,当前字段名称,当前值\n\t * \n\t * @param obj\n\t *            当前对象\n\t * @param name\n\t *            当前字段名称\n\t * @param value\n\t *            当前值\n\t * @return\n\t */\n\tpublic Object importHandler(Object obj, String name, Object value);\n\n\t/**\n\t * 设置需要处理的属性列表\n\t * \n\t * @param fields\n\t */\n\tpublic void setNeedHandlerFields(String[] fields);\n\n\t/**\n\t * 设置Map导入,自定义 put\n\t * \n\t * @param map\n\t * @param originKey\n\t * @param value\n\t */\n\tpublic void setMapValue(Map<String, Object> map, String originKey, Object value);\n\t/**\n\t * 获取这个字段的 Hyperlink ,07版本需要,03版本不需要\n\t * @param creationHelper\n\t * @param obj\n\t * @param name\n\t * @param value\n\t * @return\n\t */\n\tpublic Hyperlink getHyperlink(CreationHelper creationHelper, Object obj, String name, Object value);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelDictHandler.java",
    "content": "package org.jeecgframework.poi.handler.inter;\n\n/**\n * 字典翻译处理\n * @author liusq\n * @date 2022-05-27\n */\npublic interface IExcelDictHandler {\n\n    /**\n     * 从值翻译到名称\n     *\n     * @param dict  字典Key\n     * @param obj   对象\n     * @param name  属性名称\n     * @param value 属性值\n     * @return\n     */\n    public String toName(String dict, Object obj, String name, Object value);\n\n    /**\n     * 从名称翻译到值\n     *\n     * @param dict  字典Key\n     * @param obj   对象\n     * @param name  属性名称\n     * @param value 属性值\n     * @return\n     */\n    public String toValue(String dict, Object obj, String name, Object value);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelExportServer.java",
    "content": "package org.jeecgframework.poi.handler.inter;\n\nimport java.util.List;\n\n/**\n * 导出数据接口\n * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】\n * @author liusq\n * @date  2022年1月4号\n */\npublic interface IExcelExportServer {\n    /**\n     * 查询数据接口\n     *\n     * @param queryParams 查询条件\n     * @param page        当前页数从1开始\n     * @data 2022年1月4号\n     * @return\n     */\n    public List<Object> selectListForExcelExport(Object queryParams, int page);\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelExportServerEnhanced.java",
    "content": "package org.jeecgframework.poi.handler.inter;\n\nimport java.util.List;\n\n/**\n * 增强的导出数据接口 - 支持游标分页,解决大数据量导出性能问题\n * for [QQYUN-13964]演示系统数据量大，点击没反应\n * \n * 推荐实现方式:\n * 1. 使用主键ID或其他有序字段作为游标\n * 2. 每次查询条件: WHERE id > lastId ORDER BY id LIMIT pageSize\n * 3. 避免使用 LIMIT offset, size 这种会随着offset增大而变慢的方式\n * \n * @Description 解决40万+数据导出查询效率问题\n * @author chenrui\n * @date 2025-11-03\n */\npublic interface IExcelExportServerEnhanced<T> {\n    \n    /**\n     * 基于游标的分页查询 - 高性能方案\n     * \n     * 实现示例:\n     * <pre>\n     * public List<SysLog> selectListForExcelExport(Object queryParams, SysLog lastRecord, int pageSize) {\n     *     Long lastId = lastRecord != null ? ((YourEntity)lastRecord).getId() : 0L;\n     *     return mapper.selectList(new QueryWrapper<YourEntity>()\n     *         .gt(\"id\", lastId)\n     *         .orderByAsc(\"id\")\n     *         .last(\"LIMIT \" + pageSize));\n     * }\n     * </pre>\n     * \n     * @param queryParams 查询条件\n     * @param lastRecord  上一批次的最后一条记录(首次查询时为null)\n     * @param pageSize    每批次查询数量\n     * @return 当前批次的数据列表,返回null或空列表表示没有更多数据\n     */\n    List<T> selectListForExcelExport(Object queryParams, T lastRecord, int pageSize);\n    \n    /**\n     * 获取默认的每批次查询数量\n     * 可以根据业务情况调整,建议5000-20000之间\n     * \n     * @return 每批次查询数量,默认10000\n     */\n    default int getPageSize() {\n        return 10000;\n    }\n}\n\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelReadRowHanlder.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.handler.inter;\n\n/**\n * 接口自定义处理类\n * \n * @author JEECG\n * @date 2015年1月16日 下午8:06:26\n * @param <T>\n */\npublic interface IExcelReadRowHanlder<T> {\n\t/**\n\t * 处理解析对象\n\t * \n\t * @param t\n\t */\n\tpublic void hanlder(T t);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelVerifyHandler.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.handler.inter;\n\nimport org.jeecgframework.poi.excel.entity.result.ExcelVerifyHanlderResult;\n\n/**\n * 导入校验接口\n * \n * @author JEECG\n * @date 2014年6月23日 下午11:08:21\n */\npublic interface IExcelVerifyHandler {\n\n\t/**\n\t * 获取需要处理的字段,导入和导出统一处理了, 减少书写的字段\n\t * \n\t * @return\n\t */\n\tpublic String[] getNeedVerifyFields();\n\n\t/**\n\t * 获取需要处理的字段,导入和导出统一处理了, 减少书写的字段\n\t * \n\t * @return\n\t */\n\tpublic void setNeedVerifyFields(String[] arr);\n\n\t/**\n\t * 导出处理方法\n\t * \n\t * @param obj\n\t *            当前对象\n\t * @param name\n\t *            当前字段名称\n\t * @param value\n\t *            当前值\n\t * @return\n\t */\n\tpublic ExcelVerifyHanlderResult verifyHandler(Object obj, String name, Object value);\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/inter/IWriter.java",
    "content": "package org.jeecgframework.poi.handler.inter;\n\nimport java.util.Collection;\n/**\n * 大数据写出服务接口\n *\n * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】\n * @author liusq\n * @date 2022年1月4号\n */\npublic interface IWriter<T> {\n    /**\n     * 获取输出对象\n     *\n     * @return\n     */\n    default public T get() {\n        return null;\n    }\n\n    /**\n     * 写入数据\n     *\n     * @param data\n     * @return\n     */\n    public IWriter<T> write(Collection data);\n\n    /**\n     * 关闭流,完成业务\n     *\n     * @return\n     */\n    public T close();\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/handler/package-info.java",
    "content": "/**\n * 数据处理中心,对导入导出进行数据处理\n * @author JEECG\n * @date 2014年6月20日 上午12:08:09\n */\npackage org.jeecgframework.poi.handler;"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/package-info.java",
    "content": "/**\n * @author JEECG\n * @date 2014年2月10日\n * @version 1.0\n * 对POI进行封装,通过注解的使用,完成POI的简易重复操作\n * 通过模板完成较为复杂的操作\n * 进阶步骤:<br/>\n * <p>1.了解注解,图片类</p>\n * <p>2.模板语言</p>\n * <p>3.模板组合</p>\n * \n */\npackage org.jeecgframework.poi;"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/ExcelUtil.java",
    "content": "package org.jeecgframework.poi.util;\n/**\n * @author Link Xue \n * @version 20171025\n * POI对EXCEL操作工具\n */\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport org.apache.poi.EncryptedDocumentException;\nimport org.apache.poi.hssf.usermodel.HSSFCell;\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.apache.poi.openxml4j.exceptions.InvalidFormatException;\nimport org.apache.poi.ss.util.CellRangeAddress;\n\npublic class ExcelUtil {\n\n    public static void main(String[] args){\n        //读取excel数据\n        ArrayList<Map<String,String>> result = ExcelUtil.readExcelToObj(\"D:\\\\上传表.xlsx\");\n        for(Map<String,String> map:result){\n            System.out.println(map);\n        }\n\n    }\n    /**\n     * 读取excel数据\n     * @param path\n     */\n    public static ArrayList<Map<String,String>> readExcelToObj(String path) {\n\n        Workbook wb = null;\n        ArrayList<Map<String,String>> result = null;\n        try {\n            wb = WorkbookFactory.create(new File(path));\n            result = readExcel(wb, 0, 2, 0);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return result;\n    }\n\n    /**\n     * 读取excel文件\n     * @param wb\n     * @param sheetIndex sheet页下标：从0开始\n     * @param startReadLine 开始读取的行:从0开始\n     * @param tailLine 去除最后读取的行\n     */\n    public static ArrayList<Map<String,String>> readExcel(Workbook wb,int sheetIndex, int startReadLine, int tailLine) {\n        Sheet sheet = wb.getSheetAt(sheetIndex);\n        Row row = null;\n        ArrayList<Map<String,String>> result = new ArrayList<Map<String,String>>();\n        for(int i=startReadLine; i<sheet.getLastRowNum()-tailLine+1; i++) {\n\n            row = sheet.getRow(i);\n            Map<String,String> map = new HashMap<String,String>();\n            for(Cell c : row) {\n                String returnStr = \"\";\n\n                boolean isMerge = isMergedRegion(sheet, i, c.getColumnIndex());\n                //判断是否具有合并单元格\n                if(isMerge) {\n                    String rs = getMergedRegionValue(sheet, row.getRowNum(), c.getColumnIndex());\n//                    System.out.print(rs + \"------ \");\n                    returnStr = rs;\n                }else {\n//                    System.out.print(c.getRichStringCellValue()+\"++++ \");\n                    returnStr = c.getRichStringCellValue().getString();\n                }\n                if(c.getColumnIndex()==0){\n                    map.put(\"id\",returnStr);\n                }else if(c.getColumnIndex()==1){\n                    map.put(\"base\",returnStr);\n                }else if(c.getColumnIndex()==2){\n                    map.put(\"siteName\",returnStr);\n                }else if(c.getColumnIndex()==3){\n                    map.put(\"articleName\",returnStr);\n                }else if(c.getColumnIndex()==4){\n                    map.put(\"mediaName\",returnStr);\n                }else if(c.getColumnIndex()==5){\n                    map.put(\"mediaUrl\",returnStr);\n                }else if(c.getColumnIndex()==6){\n                    map.put(\"newsSource\",returnStr);\n                }else if(c.getColumnIndex()==7){\n                    map.put(\"isRecord\",returnStr);\n                }else if(c.getColumnIndex()==8){\n                    map.put(\"recordTime\",returnStr);\n                }else if(c.getColumnIndex()==9){\n                    map.put(\"remark\",returnStr);\n                }\n\n            }\n            result.add(map);\n//            System.out.println();\n\n        }\n        return result;\n\n    }\n\n    /**\n     * 获取合并单元格的值\n     * @param sheet\n     * @param row\n     * @param column\n     * @return\n     */\n    public static String getMergedRegionValue(Sheet sheet ,int row , int column){\n        int sheetMergeCount = sheet.getNumMergedRegions();\n\n        for(int i = 0 ; i < sheetMergeCount ; i++){\n            CellRangeAddress ca = sheet.getMergedRegion(i);\n            int firstColumn = ca.getFirstColumn();\n            int lastColumn = ca.getLastColumn();\n            int firstRow = ca.getFirstRow();\n            int lastRow = ca.getLastRow();\n\n            if(row >= firstRow && row <= lastRow){\n\n                if(column >= firstColumn && column <= lastColumn){\n                    Row fRow = sheet.getRow(firstRow);\n                    Cell fCell = fRow.getCell(firstColumn);\n                    return getCellValue(fCell) ;\n                }\n            }\n        }\n\n        return null ;\n    }\n\n    /**\n     * 判断合并了行\n     * @param sheet\n     * @param row\n     * @param column\n     * @return\n     */\n    public static boolean isMergedRow(Sheet sheet,int row ,int column) {\n        int sheetMergeCount = sheet.getNumMergedRegions();\n        for (int i = 0; i < sheetMergeCount; i++) {\n            CellRangeAddress range = sheet.getMergedRegion(i);\n            int firstColumn = range.getFirstColumn();\n            int lastColumn = range.getLastColumn();\n            int firstRow = range.getFirstRow();\n            int lastRow = range.getLastRow();\n            if(row == firstRow && row == lastRow){\n                if(column >= firstColumn && column <= lastColumn){\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断指定的单元格是否是合并单元格\n     * @param sheet\n     * @param row 行下标\n     * @param column 列下标\n     * @return\n     */\n    public static boolean isMergedRegion(Sheet sheet,int row ,int column) {\n        int sheetMergeCount = sheet.getNumMergedRegions();\n        for (int i = 0; i < sheetMergeCount; i++) {\n            CellRangeAddress range = sheet.getMergedRegion(i);\n            int firstColumn = range.getFirstColumn();\n            int lastColumn = range.getLastColumn();\n            int firstRow = range.getFirstRow();\n            int lastRow = range.getLastRow();\n            if(row >= firstRow && row <= lastRow){\n                if(column >= firstColumn && column <= lastColumn){\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * 判断sheet页中是否含有合并单元格\n     * @param sheet\n     * @return\n     */\n    public static boolean hasMerged(Sheet sheet) {\n        return sheet.getNumMergedRegions() > 0 ? true : false;\n    }\n\n    /**\n     * 合并单元格\n     * @param sheet\n     * @param firstRow 开始行\n     * @param lastRow 结束行\n     * @param firstCol 开始列\n     * @param lastCol 结束列\n     */\n    public static void mergeRegion(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {\n        //update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n        try{\n        sheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));\n        }catch (IllegalArgumentException e){\n            e.fillInStackTrace();\n        }\n        //update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n    }\n\n    /**\n     * 获取单元格的值\n     * @param cell\n     * @return\n     */\n    public static String getCellValue(Cell cell){\n\n        if(cell == null) {\n            return \"\";\n        }\n\n        if(cell.getCellType() == CellType.STRING){\n\n            return cell.getStringCellValue();\n\n        }else if(cell.getCellType() == CellType.BOOLEAN){\n\n            return String.valueOf(cell.getBooleanCellValue());\n\n        }else if(cell.getCellType() ==  CellType.FORMULA){\n\n            return cell.getCellFormula() ;\n\n        }else if(cell.getCellType() == CellType.NUMERIC){\n\n            return String.valueOf(cell.getNumericCellValue());\n\n        }\n        return \"\";\n    }\n    \n    /**\n     * 数字值，去掉.0后缀\n     * @param cell\n     * @return\n     */\n    public static String remove0Suffix(Object value){\n    \tif(value!=null) {\n    \t    // update-begin-author:taoyan date:20210526 for:对于特殊的字符串 V1.0 也进行了去.0操作 这是不合理的\n\t\t\tString val = value.toString();\n\t\t\tif(val.endsWith(\".0\") && isNumberString(val)) {\n\t\t\t\tval = val.replace(\".0\", \"\");\n\t\t\t}\n            // update-end-author:taoyan date:20210526 for:对于特殊的字符串 V1.0 也进行了去.0操作 这是不合理的\n\t\t\treturn val;\n\t\t}\n        return null;\n    }\n\n    /**\n     * 判断给定的字符串是不是只有数字\n     * @param str\n     * @return\n     */\n    private static boolean isNumberString(String str){\n        //update-begin---author:chenrui ---date:20240424  for：[QQYUN-9048]负数被识别成非数字------------\n        String regex = \"^-?[0-9]+\\\\.0+$\";\n        //update-end---author:chenrui ---date:20240424  for：[QQYUN-9048]负数被识别成非数字------------\n        Pattern pattern = Pattern.compile(regex);\n        Matcher m = pattern.matcher(str);\n        if (m.find()) {\n            return true;\n        }\n        return false;\n    }\n    \n    /**\n     * 从excel读取内容\n     */\n    public static void readContent(String fileName)  {\n        boolean isE2007 = false;    //判断是否是excel2007格式\n        if(fileName.endsWith(\"xlsx\"))\n            isE2007 = true;\n        try {\n            InputStream input = new FileInputStream(fileName);  //建立输入流\n            Workbook wb  = null;\n            //根据文件格式(2003或者2007)来初始化\n            if(isE2007)\n                wb = new XSSFWorkbook(input);\n            else\n                wb = new HSSFWorkbook(input);\n            Sheet sheet = wb.getSheetAt(0);     //获得第一个表单\n            Iterator<Row> rows = sheet.rowIterator(); //获得第一个表单的迭代器\n            while (rows.hasNext()) {\n                Row row = rows.next();  //获得行数据\n                System.out.println(\"Row #\" + row.getRowNum());  //获得行号从0开始\n                Iterator<Cell> cells = row.cellIterator();    //获得第一行的迭代器\n                while (cells.hasNext()) {\n                    Cell cell = cells.next();\n                    System.out.println(\"Cell #\" + cell.getColumnIndex());\n                    switch (cell.getCellType()) {   //根据cell中的类型来输出数据\n                        case NUMERIC:\n                            System.out.println(cell.getNumericCellValue());\n                            break;\n                        case STRING:\n                            System.out.println(cell.getStringCellValue());\n                            break;\n                        case BOOLEAN:\n                            System.out.println(cell.getBooleanCellValue());\n                            break;\n                        case FORMULA:\n                            System.out.println(cell.getCellFormula());\n                            break;\n                        default:\n                            System.out.println(\"unsuported sell type=======\"+cell.getCellType());\n                            break;\n                    }\n                }\n            }\n        } catch (IOException ex) {\n            ex.printStackTrace();\n        }\n    }\n\n}"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/JsonParser.java",
    "content": "package org.jeecgframework.poi.util;\n\nimport java.util.*;\nimport java.lang.reflect.*;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\n\n/**\n * json格式处理\n */\npublic class JsonParser {\n\n    /**\n     * 将JSON数组字符串解析为List<Object>\n     * @param json JSON数组字符串\n     * @return List集合，包含解析后的对象\n     */\n    public static List<Object> parseJsonArrayToList(String json) {\n        if (json == null || json.trim().isEmpty()) {\n            return new ArrayList<>();\n        }\n\n        String trimmed = json.trim();\n        if (!trimmed.startsWith(\"[\") || !trimmed.endsWith(\"]\")) {\n            throw new IllegalArgumentException(\"Input is not a valid JSON array\");\n        }\n\n        // 移除外层的方括号\n        String content = trimmed.substring(1, trimmed.length() - 1).trim();\n        if (content.isEmpty()) {\n            return new ArrayList<>();\n        }\n\n        List<Object> result = new ArrayList<>();\n        List<String> elements = splitJsonElements(content);\n\n        for (String element : elements) {\n            result.add(parseJsonValue(element.trim()));\n        }\n\n        return result;\n    }\n\n    /**\n     * 分割JSON数组中的元素\n     */\n    private static List<String> splitJsonElements(String content) {\n        List<String> elements = new ArrayList<>();\n        StringBuilder current = new StringBuilder();\n        int braceDepth = 0;\n        int bracketDepth = 0;\n        boolean inString = false;\n        char stringChar = '\"';\n        boolean escapeNext = false;\n\n        for (int i = 0; i < content.length(); i++) {\n            char c = content.charAt(i);\n\n            if (escapeNext) {\n                current.append(c);\n                escapeNext = false;\n                continue;\n            }\n\n            if (c == '\\\\') {\n                escapeNext = true;\n                current.append(c);\n                continue;\n            }\n\n            if (!inString) {\n                if (c == ',') {\n                    if (braceDepth == 0 && bracketDepth == 0) {\n                        elements.add(current.toString());\n                        current = new StringBuilder();\n                        continue;\n                    }\n                } else if (c == '{') {\n                    braceDepth++;\n                } else if (c == '}') {\n                    braceDepth--;\n                } else if (c == '[') {\n                    bracketDepth++;\n                } else if (c == ']') {\n                    bracketDepth--;\n                } else if (c == '\"' || c == '\\'') {\n                    inString = true;\n                    stringChar = c;\n                }\n            } else {\n                if (c == stringChar) {\n                    inString = false;\n                }\n            }\n\n            current.append(c);\n        }\n\n        if (current.length() > 0) {\n            elements.add(current.toString());\n        }\n\n        return elements;\n    }\n\n    /**\n     * 解析JSON值\n     */\n    private static Object parseJsonValue(String jsonValue) {\n        if (jsonValue == null || jsonValue.isEmpty()) {\n            return null;\n        }\n\n        String trimmed = jsonValue.trim();\n\n        // 处理null\n        if (\"null\".equalsIgnoreCase(trimmed)) {\n            return null;\n        }\n\n        // 处理布尔值\n        if (\"true\".equalsIgnoreCase(trimmed)) {\n            return true;\n        }\n        if (\"false\".equalsIgnoreCase(trimmed)) {\n            return false;\n        }\n\n        // 处理字符串\n        if (isQuotedString(trimmed)) {\n            return parseString(trimmed);\n        }\n\n        // 处理数字\n        if (isNumber(trimmed)) {\n            return parseNumber(trimmed);\n        }\n\n        // 处理对象\n        if (trimmed.startsWith(\"{\")) {\n            return parseJsonObject(trimmed);\n        }\n\n        // 处理数组\n        if (trimmed.startsWith(\"[\")) {\n            return parseJsonArray(trimmed);\n        }\n\n        // 如果都不是，尝试作为数字处理，否则返回字符串\n        try {\n            return parseNumber(trimmed);\n        } catch (NumberFormatException e) {\n            return trimmed;\n        }\n    }\n\n    /**\n     * 解析JSON对象\n     */\n    private static Map<String, Object> parseJsonObject(String json) {\n        if (!json.startsWith(\"{\") || !json.endsWith(\"}\")) {\n            throw new IllegalArgumentException(\"Invalid JSON object: \" + json);\n        }\n\n        String content = json.substring(1, json.length() - 1).trim();\n        if (content.isEmpty()) {\n            return new LinkedHashMap<>();\n        }\n\n        Map<String, Object> result = new LinkedHashMap<>();\n        List<String> pairs = splitJsonPairs(content);\n\n        for (String pair : pairs) {\n            String[] keyValue = splitKeyValue(pair.trim());\n            if (keyValue.length == 2) {\n                String key = parseString(keyValue[0].trim());\n                Object value = parseJsonValue(keyValue[1].trim());\n                result.put(key, value);\n            }\n        }\n\n        return result;\n    }\n\n    /**\n     * 解析JSON数组\n     */\n    private static List<Object> parseJsonArray(String json) {\n        if (!json.startsWith(\"[\") || !json.endsWith(\"]\")) {\n            throw new IllegalArgumentException(\"Invalid JSON array: \" + json);\n        }\n\n        String content = json.substring(1, json.length() - 1).trim();\n        if (content.isEmpty()) {\n            return new ArrayList<>();\n        }\n\n        List<Object> result = new ArrayList<>();\n        List<String> elements = splitJsonElements(content);\n\n        for (String element : elements) {\n            result.add(parseJsonValue(element.trim()));\n        }\n\n        return result;\n    }\n\n    /**\n     * 分割JSON对象中的键值对\n     */\n    private static List<String> splitJsonPairs(String content) {\n        List<String> pairs = new ArrayList<>();\n        StringBuilder current = new StringBuilder();\n        int braceDepth = 0;\n        int bracketDepth = 0;\n        boolean inString = false;\n        char stringChar = '\"';\n        boolean escapeNext = false;\n\n        for (int i = 0; i < content.length(); i++) {\n            char c = content.charAt(i);\n\n            if (escapeNext) {\n                current.append(c);\n                escapeNext = false;\n                continue;\n            }\n\n            if (c == '\\\\') {\n                escapeNext = true;\n                current.append(c);\n                continue;\n            }\n\n            if (!inString) {\n                if (c == ',') {\n                    if (braceDepth == 0 && bracketDepth == 0) {\n                        pairs.add(current.toString());\n                        current = new StringBuilder();\n                        continue;\n                    }\n                } else if (c == '{') {\n                    braceDepth++;\n                } else if (c == '}') {\n                    braceDepth--;\n                } else if (c == '[') {\n                    bracketDepth++;\n                } else if (c == ']') {\n                    bracketDepth--;\n                } else if (c == '\"' || c == '\\'') {\n                    inString = true;\n                    stringChar = c;\n                }\n            } else {\n                if (c == stringChar) {\n                    inString = false;\n                }\n            }\n\n            current.append(c);\n        }\n\n        if (current.length() > 0) {\n            pairs.add(current.toString());\n        }\n\n        return pairs;\n    }\n\n    /**\n     * 分割键值对\n     */\n    private static String[] splitKeyValue(String pair) {\n        int colonIndex = -1;\n        boolean inString = false;\n        char stringChar = '\"';\n        boolean escapeNext = false;\n\n        for (int i = 0; i < pair.length(); i++) {\n            char c = pair.charAt(i);\n\n            if (escapeNext) {\n                escapeNext = false;\n                continue;\n            }\n\n            if (c == '\\\\') {\n                escapeNext = true;\n                continue;\n            }\n\n            if (!inString) {\n                if (c == ':') {\n                    colonIndex = i;\n                    break;\n                } else if (c == '\"' || c == '\\'') {\n                    inString = true;\n                    stringChar = c;\n                }\n            } else {\n                if (c == stringChar) {\n                    inString = false;\n                }\n            }\n        }\n\n        if (colonIndex == -1) {\n            throw new IllegalArgumentException(\"Invalid key-value pair: \" + pair);\n        }\n\n        return new String[] {\n                pair.substring(0, colonIndex),\n                pair.substring(colonIndex + 1)\n        };\n    }\n\n    /**\n     * 解析字符串，处理转义字符\n     */\n    private static String parseString(String str) {\n        if (!isQuotedString(str)) {\n            return str;\n        }\n\n        char quoteChar = str.charAt(0);\n        String content = str.substring(1, str.length() - 1);\n        StringBuilder result = new StringBuilder();\n\n        for (int i = 0; i < content.length(); i++) {\n            char c = content.charAt(i);\n\n            if (c == '\\\\' && i + 1 < content.length()) {\n                char next = content.charAt(i + 1);\n                switch (next) {\n                    case '\"': result.append('\"'); i++; break;\n                    case '\\'': result.append('\\''); i++; break;\n                    case '\\\\': result.append('\\\\'); i++; break;\n                    case '/': result.append('/'); i++; break;\n                    case 'b': result.append('\\b'); i++; break;\n                    case 'f': result.append('\\f'); i++; break;\n                    case 'n': result.append('\\n'); i++; break;\n                    case 'r': result.append('\\r'); i++; break;\n                    case 't': result.append('\\t'); i++; break;\n                    case 'u': // Unicode转义\n                        if (i + 5 < content.length()) {\n                            String hex = content.substring(i + 2, i + 6);\n                            try {\n                                int codePoint = Integer.parseInt(hex, 16);\n                                result.append((char) codePoint);\n                                i += 5;\n                            } catch (NumberFormatException e) {\n                                result.append(c);\n                            }\n                        } else {\n                            result.append(c);\n                        }\n                        break;\n                    default:\n                        result.append(c);\n                }\n            } else {\n                result.append(c);\n            }\n        }\n\n        return result.toString();\n    }\n\n    /**\n     * 解析数字\n     */\n    private static Object parseNumber(String numStr) {\n        try {\n            // 尝试解析为整数\n            if (numStr.indexOf('.') == -1 && numStr.indexOf('e') == -1 && numStr.indexOf('E') == -1) {\n                try {\n                    long longValue = Long.parseLong(numStr);\n                    if (longValue >= Integer.MIN_VALUE && longValue <= Integer.MAX_VALUE) {\n                        return (int) longValue;\n                    }\n                    return longValue;\n                } catch (NumberFormatException e) {\n                    // 可能是大整数\n                    return new BigInteger(numStr);\n                }\n            } else {\n                // 尝试解析为浮点数\n                double doubleValue = Double.parseDouble(numStr);\n                if (Math.abs(doubleValue) <= Float.MAX_VALUE) {\n                    float floatValue = (float) doubleValue;\n                    // 检查是否实际上是整数\n                    if (floatValue == Math.floor(floatValue) && !Double.isInfinite(floatValue)) {\n                        if (floatValue >= Integer.MIN_VALUE && floatValue <= Integer.MAX_VALUE) {\n                            return (int) floatValue;\n                        } else {\n                            return (long) floatValue;\n                        }\n                    }\n                    return floatValue;\n                }\n                return doubleValue;\n            }\n        } catch (NumberFormatException e) {\n            throw new IllegalArgumentException(\"Invalid number: \" + numStr, e);\n        }\n    }\n\n    /**\n     * 判断是否为引号包裹的字符串\n     */\n    private static boolean isQuotedString(String str) {\n        return (str.startsWith(\"\\\"\") && str.endsWith(\"\\\"\")) ||\n                (str.startsWith(\"'\") && str.endsWith(\"'\"));\n    }\n\n    /**\n     * 判断是否为数字\n     */\n    private static boolean isNumber(String str) {\n        if (str == null || str.isEmpty()) {\n            return false;\n        }\n\n        String trimmed = str.trim();\n        if (trimmed.isEmpty()) {\n            return false;\n        }\n\n        char firstChar = trimmed.charAt(0);\n        if (firstChar != '-' && firstChar != '+' && !Character.isDigit(firstChar)) {\n            return false;\n        }\n\n        boolean hasDecimalPoint = false;\n        boolean hasExponent = false;\n        boolean hasDigit = false;\n\n        for (int i = (firstChar == '-' || firstChar == '+') ? 1 : 0; i < trimmed.length(); i++) {\n            char c = trimmed.charAt(i);\n\n            if (c >= '0' && c <= '9') {\n                hasDigit = true;\n            } else if (c == '.') {\n                if (hasDecimalPoint || hasExponent) {\n                    return false;\n                }\n                hasDecimalPoint = true;\n            } else if (c == 'e' || c == 'E') {\n                if (hasExponent || !hasDigit) {\n                    return false;\n                }\n                hasExponent = true;\n                hasDigit = false; // 指数后面必须有数字\n            } else if (c == '+' || c == '-') {\n                // 只允许在指数符号后出现\n                char prev = (i > 0) ? trimmed.charAt(i - 1) : '\\0';\n                if (!(prev == 'e' || prev == 'E')) {\n                    return false;\n                }\n            } else {\n                return false;\n            }\n        }\n\n        return hasDigit;\n    }\n\n    // 测试方法\n    public static void main(String[] args) {\n        // 测试用例1：基本数组\n        String json1 = \"[{\\\"name\\\":\\\"John\\\",\\\"age\\\":30}, {\\\"name\\\":\\\"Jane\\\",\\\"age\\\":25}]\";\n        List<Object> result1 = parseJsonArrayToList(json1);\n        System.out.println(\"Test 1 Result: \" + result1);\n\n        // 测试用例2：嵌套结构\n        String json2 = \"[{\\\"name\\\":\\\"John\\\",\\\"scores\\\":[85,90,78],\\\"address\\\":{\\\"city\\\":\\\"NY\\\",\\\"zip\\\":10001}}]\";\n        List<Object> result2 = parseJsonArrayToList(json2);\n        System.out.println(\"Test 2 Result: \" + result2);\n\n        // 测试用例3：各种数据类型\n        String json3 = \"[123, 45.67, \\\"hello\\\", true, false, null]\";\n        List<Object> result3 = parseJsonArrayToList(json3);\n        System.out.println(\"Test 3 Result: \" + result3);\n\n        // 测试用例4：转义字符\n        String json4 = \"[\\\"Line1\\\\nLine2\\\", \\\"Quote:\\\\\\\"Hello\\\\\\\"\\\", \\\"Backslash:\\\\\\\\\\\"]\";\n        List<Object> result4 = parseJsonArrayToList(json4);\n        System.out.println(\"Test 4 Result: \" + result4);\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/MyX509TrustManager.java",
    "content": "package org.jeecgframework.poi.util;\n\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\n\nimport javax.net.ssl.X509TrustManager;\n\n/**\n * https请求用到\n */\npublic class MyX509TrustManager implements X509TrustManager{\n\n    @Override\n    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {\n        // TODO Auto-generated method stub\n    }\n\n    @Override\n    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {\n        // TODO Auto-generated method stub\n\n    }\n\n    @Override\n    public X509Certificate[] getAcceptedIssuers() {\n        // TODO Auto-generated method stub\n        return null;\n    }\n\n}"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiCellUtil.java",
    "content": "/**\n *\n */\npackage org.jeecgframework.poi.util;\n\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.CellType;\nimport org.apache.poi.ss.usermodel.Row;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.util.CellRangeAddress;\n\n/**\n * 处理单元格的数值\n * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】\n * @author liusq\n * @date  2022年1月4号\n */\npublic class PoiCellUtil {\n    /**\n     * 读取单元格的值\n     *\n     * @param sheet\n     * @param row\n     * @param column\n     * @return\n     */\n    public static String getCellValue(Sheet sheet, int row, int column) {\n        String value = null;\n        if (isMergedRegion(sheet, row, column)) {\n            value = getMergedRegionValue(sheet, row, column);\n        } else {\n            Row  rowData = sheet.getRow(row);\n            Cell cell    = rowData.getCell(column);\n            value = getCellValue(cell);\n        }\n        return value;\n    }\n\n    /**\n     * 获取合并单元格的值\n     *\n     * @param sheet\n     * @param row\n     * @param column\n     * @return\n     */\n    public static String getMergedRegionValue(Sheet sheet, int row, int column) {\n        int sheetMergeCount = sheet.getNumMergedRegions();\n\n        for (int i = 0; i < sheetMergeCount; i++) {\n            CellRangeAddress ca          = sheet.getMergedRegion(i);\n            int              firstColumn = ca.getFirstColumn();\n            int              lastColumn  = ca.getLastColumn();\n            int              firstRow    = ca.getFirstRow();\n            int              lastRow     = ca.getLastRow();\n\n            if (row >= firstRow && row <= lastRow) {\n\n                if (column >= firstColumn && column <= lastColumn) {\n                    Row  fRow  = sheet.getRow(firstRow);\n                    Cell fCell = fRow.getCell(firstColumn);\n\n                    return getCellValue(fCell);\n                }\n            }\n        }\n\n        return null;\n    }\n\n    /**\n     * 判断指定的单元格是否是合并单元格\n     *\n     * @param sheet\n     * @param row\n     * @param column\n     * @return\n     */\n    public static boolean isMergedRegion(Sheet sheet, int row, int column) {\n        int sheetMergeCount = sheet.getNumMergedRegions();\n\n        for (int i = 0; i < sheetMergeCount; i++) {\n            CellRangeAddress ca          = sheet.getMergedRegion(i);\n            int              firstColumn = ca.getFirstColumn();\n            int              lastColumn  = ca.getLastColumn();\n            int              firstRow    = ca.getFirstRow();\n            int              lastRow     = ca.getLastRow();\n\n            if (row >= firstRow && row <= lastRow) {\n                if (column >= firstColumn && column <= lastColumn) {\n\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * 获取单元格的值\n     *      _NONE(-1),\n     *     NUMERIC(0),\n     *     STRING(1),\n     *     FORMULA(2),\n     *     BLANK(3),\n     *     BOOLEAN(4),\n     *     ERROR(5);\n     * @param cell\n     * @return\n     */\n    public static String getCellValue(Cell cell) {\n        if (cell == null) {\n            return \"\";\n        }\n        if (cell.getCellType() == CellType.STRING) {\n            return cell.getStringCellValue();\n        } else if (cell.getCellType() == CellType.BOOLEAN) {\n            return String.valueOf(cell.getBooleanCellValue());\n        } else if (cell.getCellType() == CellType.FORMULA) {\n            try {\n                return cell.getCellFormula();\n            } catch (Exception e) {\n                return String.valueOf(cell.getNumericCellValue());\n            }\n        } else if (cell.getCellType() == CellType.NUMERIC) {\n            return String.valueOf(cell.getNumericCellValue());\n        } else if (cell.getCellType() == CellType.ERROR) {\n            return String.valueOf(cell.getErrorCellValue());\n        }\n        return  \"\";\n    }\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiElUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.util;\n\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\n\n/**\n * AutoPoi的el 表达式支持工具类\n * \n * @author JEECG\n * @date 2015年4月25日 下午12:13:21\n */\npublic final class PoiElUtil {\n\n\tpublic static final String LENGTH = \"le:\";\n\tpublic static final String FOREACH = \"fe:\";\n\tpublic static final String FOREACH_NOT_CREATE = \"!fe:\";\n\tpublic static final String FOREACH_AND_SHIFT = \"$fe:\";\n\tpublic static final String FOREACH_COL        = \"#fe:\";\n\tpublic static final String FOREACH_COL_VALUE  = \"v_fe:\";\n\tpublic static final String START_STR = \"{{\";\n\tpublic static final String END_STR = \"}}\";\n\tpublic static final String WRAP               = \"]]\";\n\tpublic static final String NUMBER_SYMBOL = \"n:\";\n\tpublic static final String FORMAT_DATE = \"fd:\";\n\tpublic static final String FORMAT_NUMBER = \"fn:\";\n\tpublic static final String IF_DELETE = \"!if:\";\n\tpublic static final String EMPTY = \"\";\n\tpublic static final String CONST              = \"'\";\n\tpublic static final String NULL               = \"&NULL&\";\n\tpublic static final String LEFT_BRACKET = \"(\";\n\tpublic static final String RIGHT_BRACKET = \")\";\n\tpublic static final String DICT_HANDLER       = \"dict:\";\n\n\tprivate PoiElUtil() {\n\t}\n\n\t/**\n\t * 解析字符串,支持 le,fd,fn,!if,三目\n\t * \n\t * @param obj\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Object eval(String text, Map<String, Object> map) throws Exception {\n\t\tString tempText = new String(text);\n\t\tObject obj = innerEval(text, map);\n\t\t// 如果没有被处理而且这个值找map中存在就处理这个值\n\t\tif (tempText.equals(obj.toString())) {\n\t\t\tif (map.containsKey(tempText.split(\"\\\\.\")[0])) {\n\t\t\t\treturn PoiPublicUtil.getParamsValue(text, map);\n\t\t\t} else {\n\t\t\t\treturn \"\";\n\t\t\t}\n\t\t}\n\t\treturn obj;\n\t}\n\n\t/**\n\t * 解析字符串,支持 le,fd,fn,!if,三目\n\t * \n\t * @param obj\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Object innerEval(String text, Map<String, Object> map) throws Exception {\n\t\tif (text.indexOf(\"?\") != -1 && text.indexOf(\":\") != -1) {\n\t\t\treturn trinocular(text, map);\n\t\t}\n\t\tif (text.indexOf(LENGTH) != -1) {\n\t\t\treturn length(text, map);\n\t\t}\n\t\tif (text.indexOf(FORMAT_DATE) != -1) {\n\t\t\treturn formatDate(text, map);\n\t\t}\n\t\tif (text.indexOf(FORMAT_NUMBER) != -1) {\n\t\t\treturn formatNumber(text, map);\n\t\t}\n\t\tif (text.indexOf(IF_DELETE) != -1) {\n\t\t\treturn ifDelete(text, map);\n\t\t}\n\t\tif (text.startsWith(\"'\")) {\n\t\t\treturn text.replace(\"'\", \"\");\n\t\t}\n\t\treturn text;\n\t}\n\n\t/**\n\t * 是不是删除列\n\t * \n\t * @param text\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate static Object ifDelete(String text, Map<String, Object> map) throws Exception {\n\t\t// 把多个空格变成一个空格\n\t\ttext = text.replaceAll(\"\\\\s{1,}\", \" \").trim();\n\t\tString[] keys = getKey(IF_DELETE, text).split(\" \");\n\t\ttext = text.replace(IF_DELETE, EMPTY);\n\t\treturn isTrue(keys, map);\n\t}\n\n\t/**\n\t * 是不是真\n\t * 这个方法两个地方用到\n\t * 1.三目表达式的判断,表达式需要设置空格 {{field == 1? field1:field2 }} 或者 {{field?field1:field2 }}\n\t * 2.取非表达式（判断为真则当前excel的一整列会被干掉）  {{!if:(field == 1)}} 或者 {{!if:(field)}}\n\t *\n\t * 如果字段field本身就能判断true或者false 他会走len==1的逻辑处理\n\t * 如果字段field需要结合其他固定值来判断true或者false 那么记住一定要再表达式里打空格 然后他会split空格 走len==3的逻辑处理\n\t * @param keys\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate static Boolean isTrue(String[] keys, Map<String, Object> map) throws Exception {\n\t\t//update-author:taoyan date:20200622 for:此处判断len当为1\n\t\tif (keys.length == 1) {\n\t\t\tString constant = null;\n\t\t\tif ((constant = isConstant(keys[0])) != null) {\n\t\t\t\treturn Boolean.valueOf(constant);\n\t\t\t}\n\t\t\treturn Boolean.valueOf(PoiPublicUtil.getParamsValue(keys[0], map).toString());\n\t\t}\n\t\tif (keys.length == 3) {\n\t\t\tif(keys[0]==null || keys[2]==null){\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tObject first = String.valueOf(eval(keys[0], map));\n\t\t\tObject second = String.valueOf(eval(keys[2], map));\n\t\t\treturn PoiFunctionUtil.isTrue(first, keys[1], second);\n\t\t}\n\t\tthrow new ExcelExportException(\"判断参数不对\");\n\t}\n\n\t/**\n\t * 判断是不是常量\n\t * \n\t * @param string\n\t * @return\n\t */\n\tprivate static String isConstant(String param) {\n\t\tif (param.indexOf(\"'\") != -1) {\n\t\t\treturn param.replace(\"'\", \"\");\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * 格式化数字\n\t * \n\t * @param text\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate static Object formatNumber(String text, Map<String, Object> map) throws Exception {\n\t\tString[] key = getKey(FORMAT_NUMBER, text).split(\";\");\n\t\ttext = text.replace(FORMAT_NUMBER, EMPTY);\n\t\treturn innerEval(replacinnerEvalue(text, PoiFunctionUtil.formatNumber(PoiPublicUtil.getParamsValue(key[0], map), key[1])), map);\n\t}\n\n\t/**\n\t * 格式化时间\n\t * \n\t * @param text\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate static Object formatDate(String text, Map<String, Object> map) throws Exception {\n\t\tString[] key = getKey(FORMAT_DATE, text).split(\";\");\n\t\ttext = text.replace(FORMAT_DATE, EMPTY);\n\t\treturn innerEval(replacinnerEvalue(text, PoiFunctionUtil.formatDate(PoiPublicUtil.getParamsValue(key[0], map), key[1])), map);\n\t}\n\n\t/**\n\t * 计算这个的长度\n\t * \n\t * @param text\n\t * @param map\n\t * @throws Exception\n\t */\n\tprivate static Object length(String text, Map<String, Object> map) throws Exception {\n\t\tString key = getKey(LENGTH, text);\n\t\ttext = text.replace(LENGTH, EMPTY);\n\t\tObject val = PoiPublicUtil.getParamsValue(key, map);\n\t\treturn innerEval(replacinnerEvalue(text, PoiFunctionUtil.length(val)), map);\n\t}\n\n\tprivate static String replacinnerEvalue(String text, Object val) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(text.substring(0, text.indexOf(LEFT_BRACKET)));\n\t\tsb.append(\" \");\n\t\tsb.append(val);\n\t\tsb.append(\" \");\n\t\tsb.append(text.substring(text.indexOf(RIGHT_BRACKET) + 1, text.length()));\n\t\treturn sb.toString().trim();\n\t}\n\n\tprivate static String getKey(String prefix, String text) {\n\t\tint leftBracket = 1, rigthBracket = 0, position = 0;\n\t\tint index = text.indexOf(prefix) + prefix.length();\n\t\twhile (text.charAt(index) == \" \".charAt(0)) {\n\t\t\ttext = text.substring(0, index) + text.substring(index + 1, text.length());\n\t\t}\n\t\tfor (int i = text.indexOf(prefix + LEFT_BRACKET) + prefix.length() + 1; i < text.length(); i++) {\n\t\t\tif (text.charAt(i) == LEFT_BRACKET.charAt(0)) {\n\t\t\t\tleftBracket++;\n\t\t\t}\n\t\t\tif (text.charAt(i) == RIGHT_BRACKET.charAt(0)) {\n\t\t\t\trigthBracket++;\n\t\t\t}\n\t\t\tif (leftBracket == rigthBracket) {\n\t\t\t\tposition = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn text.substring(text.indexOf(prefix + LEFT_BRACKET) + 1 + prefix.length(), position).trim();\n\t}\n\n\tpublic static void main(String[] args) {\n\t\tSystem.out.println(getKey(IF_DELETE, \"测试 \" + IF_DELETE + \" (小明)\"));\n\t}\n\n\t/**\n\t * 三目运算\n\t * \n\t * @return\n\t * @throws Exception\n\t */\n\tprivate static Object trinocular(String text, Map<String, Object> map) throws Exception {\n\t\t//update-begin-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时：三目、求长、常量、日期转换没起效果\n\t\t//把多个空格变成一个空格\n\t\ttext = text.replaceAll(\"\\\\s{1,}\", \" \").trim();\n\t\tString testText = text.substring(0, text.indexOf(\"?\"));\n\t\ttext = text.substring(text.indexOf(\"?\") + 1, text.length()).trim();\n\t\ttext = innerEval(text, map).toString();\n\t\tString[] keys  = text.split(\":\");\n\t\tObject   first = null, second = null;\n\t\tif (keys.length > 2) {\n\t\t\tif (keys[0].trim().contains(\"?\")) {\n\t\t\t\tString trinocular = keys[0];\n\t\t\t\tfor (int i = 1; i < keys.length - 1; i++) {\n\t\t\t\t\ttrinocular += \":\" + keys[i];\n\t\t\t\t}\n\t\t\t\tfirst = evalNoParse(trinocular, map);\n\t\t\t\tsecond = evalNoParse(keys[keys.length - 1].trim(), map);\n\t\t\t} else {\n\t\t\t\tfirst = evalNoParse(keys[0].trim(), map);\n\t\t\t\tString trinocular = keys[1];\n\t\t\t\tfor (int i = 2; i < keys.length; i++) {\n\t\t\t\t\ttrinocular += \":\" + keys[i];\n\t\t\t\t}\n\t\t\t\tsecond = evalNoParse(trinocular, map);\n\t\t\t}\n\t\t} else {\n\t\t\tfirst = evalNoParse(keys[0].trim(), map);\n\t\t\tsecond = evalNoParse(keys[1].trim(), map);\n\t\t}\n\t\treturn isTrue(testText.split(\" \"), map) ? first : second;\n\t\t//update-end-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时：三目、求长、常量、日期转换没起效果\n\t}\n\t/**\n\t * 解析字符串,支持 le,fd,fn,!if,三目  找不到返回原值\n\t *\n\t * @param text\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Object evalNoParse(String text, Map<String, Object> map) throws Exception {\n\t\tString tempText = new String(text);\n\t\tObject obj      = innerEval(text, map);\n\t\t//如果没有被处理而且这个值找map中存在就处理这个值,找不到就返回空字符串\n\t\tif (tempText.equals(obj.toString())) {\n\t\t\tif (map.containsKey(tempText.split(\"\\\\.\")[0])) {\n\t\t\t\treturn PoiPublicUtil.getParamsValue(tempText, map);\n\t\t\t} else {\n\t\t\t\treturn obj;\n\t\t\t}\n\t\t}\n\t\treturn obj;\n\t}\n\t/**\n\t * 解析字符串, 不支持 le,fd,fn,!if,三目 ,获取是集合的字段前缀\n\t *\n\t * @param text\n\t * @param map\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static String evalFindName(String text, Map<String, Object> map) throws Exception {\n\t\tString[]      keys = text.split(\"\\\\.\");\n\t\tStringBuilder sb   = new StringBuilder().append(keys[0]);\n\t\tfor (int i = 1; i < keys.length; i++) {\n\t\t\tsb.append(\".\").append(keys[i]);\n\t\t\tif (eval(sb.toString(), map) instanceof Collection) {\n\t\t\t\treturn sb.toString();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiExcelGraphDataUtil.java",
    "content": "package org.jeecgframework.poi.util;\n\nimport org.apache.poi.ss.usermodel.Drawing;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.jeecgframework.poi.excel.graph.entity.ExcelGraph;\nimport org.jeecgframework.poi.excel.graph.entity.ExcelGraphElement;\n\nimport java.util.List;\n\n/**\n * 构建特殊数据结构\n * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】\n * @author liusq\n * @date  2022年1月4号\n */\npublic class PoiExcelGraphDataUtil {\n    /**\n     * 构建获取数据最后行数  并写入到定义对象中\n     * @param dataSourceSheet\n     * @param graph\n     */\n    public static void buildGraphData(Sheet dataSourceSheet, ExcelGraph graph) {\n        if (graph != null && graph.getCategory() != null && graph.getValueList() != null\n                && graph.getValueList().size() > 0) {\n            graph.getCategory().setEndRowNum(dataSourceSheet.getLastRowNum());\n            for (ExcelGraphElement e : graph.getValueList()) {\n                if (e != null) {\n                    e.setEndRowNum(dataSourceSheet.getLastRowNum());\n                }\n            }\n        }\n    }\n\n    /**\n     * 构建多个图形对象\n     * @param dataSourceSheet\n     * @param graphList\n     */\n    public static void buildGraphData(Sheet dataSourceSheet, List<ExcelGraph> graphList) {\n        if (graphList != null && graphList.size() > 0) {\n            for (ExcelGraph graph : graphList) {\n                buildGraphData(dataSourceSheet, graph);\n            }\n        }\n    }\n\n    /**\n     * 获取画布,没有就创建一个\n     * @param sheet\n     * @return\n     */\n    public static Drawing getDrawingPatriarch(Sheet sheet){\n        if(sheet.getDrawingPatriarch() == null){\n            sheet.createDrawingPatriarch();\n        }\n        return sheet.getDrawingPatriarch();\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiExcelTempUtil.java",
    "content": "package org.jeecgframework.poi.util;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.*;\n\nimport java.util.Date;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * poi 4.0 07版本在 shift操作下有bug,不移动了单元格以及单元格样式,没有移动cell\n * cell还是复用的原理的cell,导致wb输出的时候没有输出值\n * 等待修复的时候删除这个问题\n *\n * @author by jueyue on 19-6-17.\n */\npublic class PoiExcelTempUtil {\n\n    /**\n     * 把这N行的数据,cell重新设置下,修复因为shift的浅复制问题,导致文本不显示的错误\n     *\n     * @param sheet\n     * @param startRow\n     * @param endRow\n     */\n    public static void reset(Sheet sheet, int startRow, int endRow) {\n        if (sheet.getWorkbook() instanceof HSSFWorkbook) {\n            return;\n        }\n        for (int i = startRow; i <= endRow; i++) {\n            Row row = sheet.getRow(i);\n            if (row == null) {\n                continue;\n            }\n            int cellNum = row.getLastCellNum();\n            for (int j = 0; j < cellNum; j++) {\n                if (row.getCell(j) == null) {\n                    continue;\n                }\n                Map<String, Object> map = copyCell(row.getCell(j));\n                row.removeCell(row.getCell(j));\n                Cell cell = row.createCell(j);\n                cell.setCellStyle((CellStyle) map.get(\"cellStyle\"));\n                if ((boolean) map.get(\"isDate\")) {\n                    cell.setCellValue((Date) map.get(\"value\"));\n                } else {\n                    CellType cellType = (CellType) map.get(\"cellType\");\n                    switch (cellType) {\n                        case NUMERIC:\n                            cell.setCellValue((double) map.get(\"value\"));\n                            break;\n                        case STRING:\n                            cell.setCellValue((String) map.get(\"value\"));\n                            break;\n                        case FORMULA:\n                            cell.setCellFormula((String) map.get(\"value\"));\n                            break;\n                        case BLANK:\n                            break;\n                        case BOOLEAN:\n                            cell.setCellValue((boolean) map.get(\"value\"));\n                            break;\n                        case ERROR:\n                            break;\n                    }\n                }\n            }\n        }\n\n    }\n\n    private static Map copyCell(Cell cell) {\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"cellType\", cell.getCellType());\n        map.put(\"isDate\", CellType.NUMERIC == cell.getCellType() && DateUtil.isCellDateFormatted(cell));\n        map.put(\"value\", getValue(cell));\n        map.put(\"cellStyle\", cell.getCellStyle());\n        return map;\n    }\n\n    private static Object getValue(Cell cell) {\n        if (CellType.NUMERIC == cell.getCellType() && DateUtil.isCellDateFormatted(cell)) {\n            return cell.getDateCellValue();\n        }\n        switch (cell.getCellType()) {\n            case _NONE:\n                return null;\n            case NUMERIC:\n                return cell.getNumericCellValue();\n            case STRING:\n                return cell.getStringCellValue();\n            case FORMULA:\n                return cell.getCellFormula();\n            case BLANK:\n                break;\n            case BOOLEAN:\n                return cell.getBooleanCellValue();\n            case ERROR:\n                break;\n        }\n        return null;\n    }\n\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiFunctionUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.util;\n\nimport java.lang.reflect.Array;\nimport java.text.DecimalFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Collection;\nimport java.util.Map;\n\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\n\n/**\n * if else,length,for each,fromatNumber,formatDate 满足模板的 el 表达式支持\n * \n * @author JEECG\n * @date 2015年4月24日 下午8:04:02\n */\npublic final class PoiFunctionUtil {\n\n\tprivate static final String TWO_DECIMAL_STR = \"###.00\";\n\tprivate static final String THREE_DECIMAL_STR = \"###.000\";\n\n\tprivate static final DecimalFormat TWO_DECIMAL = new DecimalFormat(TWO_DECIMAL_STR);\n\tprivate static final DecimalFormat THREE_DECIMAL = new DecimalFormat(THREE_DECIMAL_STR);\n\n\tprivate static final String DAY_STR = \"yyyy-MM-dd\";\n\tprivate static final String TIME_STR = \"yyyy-MM-dd HH:mm:ss\";\n\tprivate static final String TIME__NO_S_STR = \"yyyy-MM-dd HH:mm\";\n\n\tprivate static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat(DAY_STR);\n\tprivate static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat(TIME_STR);\n\tprivate static final SimpleDateFormat TIME__NO_S_FORMAT = new SimpleDateFormat(TIME__NO_S_STR);\n\n\tprivate PoiFunctionUtil() {\n\t}\n\n\t/**\n\t * 获取对象的长度\n\t * \n\t * @param obj\n\t * @return\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic static int length(Object obj) {\n\t\tif (obj == null) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (obj instanceof Map) {\n\t\t\treturn ((Map) obj).size();\n\t\t}\n\t\tif (obj instanceof Collection) {\n\t\t\treturn ((Collection) obj).size();\n\t\t}\n\t\tif (obj.getClass().isArray()) {\n\t\t\treturn Array.getLength(obj);\n\t\t}\n\t\treturn String.valueOf(obj).length();\n\t}\n\n\t/**\n\t * 格式化数字\n\t * \n\t * @param obj\n\t * @throws NumberFormatException\n\t * @return\n\t */\n\tpublic static String formatNumber(Object obj, String format) {\n\t\tif (obj == null || obj.toString() == \"\") {\n\t\t\treturn \"\";\n\t\t}\n\t\tdouble number = Double.valueOf(obj.toString());\n\t\tDecimalFormat decimalFormat = null;\n\t\tif (TWO_DECIMAL.equals(format)) {\n\t\t\tdecimalFormat = TWO_DECIMAL;\n\t\t} else if (THREE_DECIMAL_STR.equals(format)) {\n\t\t\tdecimalFormat = THREE_DECIMAL;\n\t\t} else {\n\t\t\tdecimalFormat = new DecimalFormat(format);\n\t\t}\n\t\treturn decimalFormat.format(number);\n\t}\n\n\t/**\n\t * 格式化时间\n\t * \n\t * @param obj\n\t * @return\n\t */\n\tpublic static String formatDate(Object obj, String format) {\n\t\tif (obj == null || obj.toString() == \"\") {\n\t\t\treturn \"\";\n\t\t}\n\t\tSimpleDateFormat dateFormat = null;\n\t\tif (DAY_STR.equals(format)) {\n\t\t\tdateFormat = DAY_FORMAT;\n\t\t} else if (TIME_STR.equals(format)) {\n\t\t\tdateFormat = TIME_FORMAT;\n\t\t} else if (TIME__NO_S_STR.equals(format)) {\n\t\t\tdateFormat = TIME__NO_S_FORMAT;\n\t\t} else {\n\t\t\tdateFormat = new SimpleDateFormat(format);\n\t\t}\n\t\treturn dateFormat.format(obj);\n\t}\n\n\t/**\n\t * 判断是不是成功\n\t * \n\t * @param first\n\t * @param operator\n\t * @param second\n\t * @return\n\t */\n\tpublic static boolean isTrue(Object first, String operator, Object second) {\n\t\tif (\">\".endsWith(operator)) {\n\t\t\treturn isGt(first, second);\n\t\t} else if (\"<\".endsWith(operator)) {\n\t\t\treturn isGt(second, first);\n\t\t} else if (\"==\".endsWith(operator)) {\n\t\t\tif (first != null && second != null) {\n\t\t\t\treturn first.equals(second);\n\t\t\t}\n\t\t\treturn first == second;\n\t\t} else if (\"!=\".endsWith(operator)) {\n\t\t\tif (first != null && second != null) {\n\t\t\t\treturn !first.equals(second);\n\t\t\t}\n\t\t\treturn first != second;\n\t\t} else {\n\t\t\tthrow new ExcelExportException(\"占不支持改操作符\");\n\t\t}\n\t}\n\n\t/**\n\t * 前者是不是大于后者\n\t * \n\t * @param first\n\t * @param second\n\t * @return\n\t */\n\tprivate static boolean isGt(Object first, Object second) {\n\t\tif (first == null || first.toString() == \"\") {\n\t\t\treturn false;\n\t\t}\n\t\tif (second == null || second.toString() == \"\") {\n\t\t\treturn true;\n\t\t}\n\t\tdouble one = Double.valueOf(first.toString());\n\t\tdouble two = Double.valueOf(second.toString());\n\t\treturn one > two;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiMergeCellUtil.java",
    "content": "package org.jeecgframework.poi.util;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.Row;\nimport org.apache.poi.ss.usermodel.Sheet;\nimport org.apache.poi.ss.util.CellRangeAddress;\nimport org.jeecgframework.poi.excel.entity.params.MergeEntity;\nimport org.jeecgframework.poi.exception.excel.ExcelExportException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 纵向合并单元格工具类\n * \n * @author JEECG\n * @date 2015年6月21日 上午11:21:40\n */\npublic final class PoiMergeCellUtil {\n\tprivate final static Logger LOGGER = LoggerFactory.getLogger(PoiMergeCellUtil.class);\n\tprivate PoiMergeCellUtil() {\n\t}\n\n\t/**\n\t * 纵向合并相同内容的单元格\n\t * \n\t * @param sheet\n\t * @param startRow\n\t *            开始行\n\t * @param columns\n\t *            需要处理的列\n\t */\n\tpublic static void mergeCells(Sheet sheet, int startRow, Integer... columns) {\n\t\tif (columns == null) {\n\t\t\tthrow new ExcelExportException(\"至少需要处理1列\");\n\t\t}\n\t\tMap<Integer, int[]> mergeMap = new HashMap<Integer, int[]>();\n\t\tfor (int i = 0; i < columns.length; i++) {\n\t\t\tmergeMap.put(columns[i], null);\n\t\t}\n\t\tmergeCells(sheet, mergeMap, startRow, sheet.getLastRowNum());\n\t}\n\n\t/**\n\t * 纵向合并相同内容的单元格\n\t * \n\t * @param sheet\n\t * @param mergeMap\n\t *            key--列,value--依赖的列,没有传空\n\t * @param startRow\n\t *            开始行\n\t */\n\tpublic static void mergeCells(Sheet sheet, Map<Integer, int[]> mergeMap, int startRow) {\n\t\tmergeCells(sheet, mergeMap, startRow, sheet.getLastRowNum());\n\t}\n\n\t/**\n\t * 纵向合并相同内容的单元格\n\t * \n\t * @param sheet\n\t * @param mergeMap\n\t *            key--列,value--依赖的列,没有传空\n\t * @param startRow\n\t *            开始行\n\t * @param endRow\n\t *            结束行\n\t */\n\tpublic static void mergeCells(Sheet sheet, Map<Integer, int[]> mergeMap, int startRow, int endRow) {\n\t\tMap<Integer, MergeEntity> mergeDataMap = new HashMap<Integer, MergeEntity>();\n\t\tif (mergeMap.size() == 0) {\n\t\t\treturn;\n\t\t}\n\t\tRow row;\n\t\tSet<Integer> sets = mergeMap.keySet();\n\t\tString text;\n\t\tfor (int i = startRow; i <= endRow; i++) {\n\t\t\trow = sheet.getRow(i);\n\t\t\tfor (Integer index : sets) {\n\t\t\t\tif (row == null || row.getCell(index) == null) {\n\t\t\t\t\tif (mergeDataMap.get(index) == null) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (mergeDataMap.get(index).getEndRow() == 0) {\n\t\t\t\t\t\tmergeDataMap.get(index).setEndRow(i - 1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttext = row.getCell(index).getStringCellValue();\n\t\t\t\t\tif (StringUtils.isNotEmpty(text)) {\n\t\t\t\t\t\thanlderMergeCells(index, i, text, mergeDataMap, sheet, row.getCell(index), mergeMap.get(index));\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmergeCellOrContinue(index, mergeDataMap, sheet);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (mergeDataMap.size() > 0) {\n\t\t\tfor (Integer index : mergeDataMap.keySet()) {\n\t\t\t\t//update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\ttry{\n\t\t\t\tsheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index));\n\t\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t\t\te.fillInStackTrace();\n\t\t\t\t}\n\t\t\t\t//update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * 处理合并单元格\n\t * \n\t * @param index\n\t * @param rowNum\n\t * @param text\n\t * @param mergeDataMap\n\t * @param sheet\n\t * @param cell\n\t * @param delys\n\t */\n\tprivate static void hanlderMergeCells(Integer index, int rowNum, String text, Map<Integer, MergeEntity> mergeDataMap, Sheet sheet, Cell cell, int[] delys) {\n\t\tif (mergeDataMap.containsKey(index)) {\n\t\t\tif (checkIsEqualByCellContents(mergeDataMap.get(index), text, cell, delys, rowNum)) {\n\t\t\t\tmergeDataMap.get(index).setEndRow(rowNum);\n\t\t\t} else {\n\t\t\t\t//update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\ttry{\n\t\t\t\tsheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index));\n\t\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t\t\te.fillInStackTrace();\n\t\t\t\t}\n\t\t\t\t//update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\t\tmergeDataMap.put(index, createMergeEntity(text, rowNum, cell, delys));\n\t\t\t}\n\t\t} else {\n\t\t\tmergeDataMap.put(index, createMergeEntity(text, rowNum, cell, delys));\n\t\t}\n\t}\n\n\t/**\n\t * 字符为空的情况下判断\n\t * \n\t * @param index\n\t * @param mergeDataMap\n\t * @param sheet\n\t */\n\tprivate static void mergeCellOrContinue(Integer index, Map<Integer, MergeEntity> mergeDataMap, Sheet sheet) {\n\t\tif (mergeDataMap.containsKey(index) && mergeDataMap.get(index).getEndRow() != mergeDataMap.get(index).getStartRow()) {\n\t\t\t//update-begin-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\ttry{\n\t\t\tsheet.addMergedRegion(new CellRangeAddress(mergeDataMap.get(index).getStartRow(), mergeDataMap.get(index).getEndRow(), index, index));\n\t\t\t}catch (IllegalArgumentException e){\n\t\t\t\tLOGGER.error(\"合并单元格错误日志：\"+e.getMessage());\n\t\t\t\te.fillInStackTrace();\n\t\t\t}\n\t\t\t//update-end-author:wangshuai date:20201118 for:一对多导出needMerge 子表数据对应数量小于2时报错 github#1840、gitee I1YH6B\n\t\t\tmergeDataMap.remove(index);\n\t\t}\n\t}\n\n\tprivate static MergeEntity createMergeEntity(String text, int rowNum, Cell cell, int[] delys) {\n\t\tMergeEntity mergeEntity = new MergeEntity(text, rowNum, rowNum);\n\t\t// 存在依赖关系\n\t\tif (delys != null && delys.length != 0) {\n\t\t\tList<String> list = new ArrayList<String>(delys.length);\n\t\t\tmergeEntity.setRelyList(list);\n\t\t\tfor (int i = 0; i < delys.length; i++) {\n\t\t\t\tlist.add(getCellNotNullText(cell, delys[i], rowNum));\n\t\t\t}\n\t\t}\n\t\treturn mergeEntity;\n\t}\n\n\tprivate static boolean checkIsEqualByCellContents(MergeEntity mergeEntity, String text, Cell cell, int[] delys, int rowNum) {\n\t\t// 没有依赖关系\n\t\tif (delys == null || delys.length == 0) {\n\t\t\treturn mergeEntity.getText().equals(text);\n\t\t}\n\t\t// 存在依赖关系\n\t\tif (mergeEntity.getText().equals(text)) {\n\t\t\tfor (int i = 0; i < delys.length; i++) {\n\t\t\t\tif (!getCellNotNullText(cell, delys[i], rowNum).equals(mergeEntity.getRelyList().get(i))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * 获取一个单元格的值,确保这个单元格必须有值,不然向上查询\n\t * \n\t * @param cell\n\t * @param index\n\t * @param rowNum\n\t * @return\n\t */\n\tprivate static String getCellNotNullText(Cell cell, int index, int rowNum) {\n\t\tString temp = cell.getRow().getCell(index).getStringCellValue();\n\t\twhile (StringUtils.isEmpty(temp)) {\n\t\t\ttemp = cell.getRow().getSheet().getRow(--rowNum).getCell(index).getStringCellValue();\n\t\t}\n\t\treturn temp;\n\t}\n\t//update-begin---author:liusq  Date:20220104  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n    /**\n     *处理合并的单元格区域\n     * @param sheet\n     * @param firstRow 开始行\n     * @param lastRow 结束行\n     * @param firstCol 开始列\n     * @param lastCol 结束列\n\t * @date    2022年1月4号\n     */\n\tpublic static void addMergedRegion(Sheet sheet, int firstRow, int lastRow, int firstCol, int lastCol) {\n\t\ttry {\n\t\t\t//添加合并的单元格区域\n\t\t\tsheet.addMergedRegion(new CellRangeAddress(firstRow, lastRow, firstCol, lastCol));\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.debug(\"发生了一次合并单元格错误,{},{},{},{}\", new Integer[]{\n\t\t\t\t\tfirstRow, lastRow, firstCol, lastCol\n\t\t\t});\n\t\t\t// 忽略掉合并的错误,不打印异常\n\t\t\tLOGGER.debug(e.getMessage(), e);\n\t\t}\n\t}\n\t//update-end---author:liusq  Date:20220104  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.util;\n\nimport java.awt.image.BufferedImage;\nimport java.io.*;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.math.BigDecimal;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.charset.StandardCharsets;\nimport java.util.*;\nimport java.util.stream.Collectors;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\nimport javax.imageio.ImageIO;\nimport javax.xml.parsers.DocumentBuilder;\nimport javax.xml.parsers.DocumentBuilderFactory;\nimport javax.xml.parsers.ParserConfigurationException;\n\nimport org.apache.commons.io.IOUtils;\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.hssf.usermodel.HSSFClientAnchor;\nimport org.apache.poi.hssf.usermodel.HSSFPicture;\nimport org.apache.poi.hssf.usermodel.HSSFPictureData;\nimport org.apache.poi.hssf.usermodel.HSSFShape;\nimport org.apache.poi.hssf.usermodel.HSSFSheet;\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ooxml.POIXMLDocumentPart;\nimport org.apache.poi.openxml4j.opc.PackagePartName;\nimport org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.xssf.usermodel.*;\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.jeecgframework.poi.excel.annotation.Excel;\nimport org.jeecgframework.poi.excel.annotation.ExcelCollection;\nimport org.jeecgframework.poi.excel.annotation.ExcelEntity;\nimport org.jeecgframework.poi.excel.annotation.ExcelIgnore;\nimport org.jeecgframework.poi.excel.entity.vo.PoiBaseConstants;\nimport org.jeecgframework.poi.word.entity.WordImageEntity;\nimport org.jeecgframework.poi.word.entity.params.ExcelListEntity;\nimport org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.util.ClassUtils;\nimport org.w3c.dom.Document;\nimport org.w3c.dom.Element;\nimport org.w3c.dom.Node;\nimport org.w3c.dom.NodeList;\nimport org.xml.sax.InputSource;\nimport org.xml.sax.SAXException;\n\n/**\n * AutoPoi 的公共基础类\n * \n * @author JEECG\n * @date 2015年4月5日 上午12:59:22\n */\npublic final class PoiPublicUtil {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(PoiPublicUtil.class);\n\n\tprivate PoiPublicUtil() {\n\n\t}\n\n\t@SuppressWarnings({ \"unchecked\" })\n\tpublic static <K, V> Map<K, V> mapFor(Object... mapping) {\n\t\tMap<K, V> map = new HashMap<K, V>();\n\t\tfor (int i = 0; i < mapping.length; i += 2) {\n\t\t\tmap.put((K) mapping[i], (V) mapping[i + 1]);\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * 彻底创建一个对象\n\t * \n\t * @param clazz\n\t * @return\n\t */\n\tpublic static Object createObject(Class<?> clazz, String targetId) {\n\t\tObject obj = null;\n\t\tMethod setMethod;\n\t\ttry {\n\t\t\tif (clazz.equals(Map.class)) {\n\t\t\t\treturn new LinkedHashMap<String, Object>();\n\t\t\t}\n\t\t\tobj = clazz.newInstance();\n\t\t\tField[] fields = getClassFields(clazz);\n\t\t\tfor (Field field : fields) {\n\t\t\t\tif (isNotUserExcelUserThis(null, field, targetId)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (isCollection(field.getType())) {\n\t\t\t\t\tExcelCollection collection = field.getAnnotation(ExcelCollection.class);\n\t\t\t\t\tsetMethod = getMethod(field.getName(), clazz, field.getType());\n\t\t\t\t\tsetMethod.invoke(obj, collection.type().newInstance());\n\t\t\t\t} else if (!isJavaClass(field)) {\n\t\t\t\t\tsetMethod = getMethod(field.getName(), clazz, field.getType());\n\t\t\t\t\tsetMethod.invoke(obj, createObject(field.getType(), targetId));\n\t\t\t\t}\n\t\t\t}\n\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t\tthrow new RuntimeException(\"创建对象异常\");\n\t\t}\n\t\treturn obj;\n\n\t}\n\n\t/**\n\t * 获取class的 包括父类的\n\t * \n\t * @param clazz\n\t * @return\n\t */\n\tpublic static Field[] getClassFields(Class<?> clazz) {\n\t\tList<Field> list = new ArrayList<Field>();\n\t\tField[] fields;\n\t\tdo {\n\t\t\tfields = clazz.getDeclaredFields();\n\t\t\tfor (int i = 0; i < fields.length; i++) {\n\t\t\t\tlist.add(fields[i]);\n\t\t\t}\n\t\t\tclazz = clazz.getSuperclass();\n\t\t} while (clazz != Object.class && clazz != null);\n\t\treturn list.toArray(fields);\n\t}\n\n\t/**\n\t * @param photoByte\n\t * @return\n\t */\n\tpublic static String getFileExtendName(byte[] photoByte) {\n\t\tString strFileExtendName = \"JPG\";\n\t\tif ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {\n\t\t\tstrFileExtendName = \"GIF\";\n\t\t} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {\n\t\t\tstrFileExtendName = \"JPG\";\n\t\t} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {\n\t\t\tstrFileExtendName = \"BMP\";\n\t\t} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {\n\t\t\tstrFileExtendName = \"PNG\";\n\t\t}\n\t\treturn strFileExtendName;\n\t}\n\n\t/**\n\t * 获取GET方法\n\t * \n\t * @param name\n\t * @param pojoClass\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Method getMethod(String name, Class<?> pojoClass) throws Exception {\n\t\tStringBuffer getMethodName = new StringBuffer(PoiBaseConstants.GET);\n\t\tgetMethodName.append(name.substring(0, 1).toUpperCase());\n\t\tgetMethodName.append(name.substring(1));\n\t\tMethod method = null;\n\t\ttry {\n\t\t\tmethod = pojoClass.getMethod(getMethodName.toString(), new Class[] {});\n\t\t} catch (Exception e) {\n\t\t\tmethod = pojoClass.getMethod(getMethodName.toString().replace(PoiBaseConstants.GET, PoiBaseConstants.IS), new Class[] {});\n\t\t}\n\t\treturn method;\n\t}\n\n\t/**\n\t * 获取SET方法\n\t * \n\t * @param name\n\t * @param pojoClass\n\t * @param type\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Method getMethod(String name, Class<?> pojoClass, Class<?> type) throws Exception {\n\t\tStringBuffer getMethodName = new StringBuffer(PoiBaseConstants.SET);\n\t\tgetMethodName.append(name.substring(0, 1).toUpperCase());\n\t\tgetMethodName.append(name.substring(1));\n\t\treturn pojoClass.getMethod(getMethodName.toString(), new Class[] { type });\n\t}\n\t\n\t//update-begin-author:taoyan date:20180615 for:TASK #2798 导入扩展方法，支持自定义导入字段转换规则\n\t/**\n\t * 获取get方法 通过EXCEL注解exportConvert判断是否支持值的转换\n\t * @param name\n\t * @param pojoClass\n\t * @param convert\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Method getMethod(String name, Class<?> pojoClass,boolean convert) throws Exception {\n\t\tStringBuffer getMethodName = new StringBuffer();\n\t\tif(convert){\n\t\t\tgetMethodName.append(PoiBaseConstants.CONVERT);\n\t\t}\n\t\tgetMethodName.append(PoiBaseConstants.GET);\n\t\tgetMethodName.append(name.substring(0, 1).toUpperCase());\n\t\tgetMethodName.append(name.substring(1));\n\t\tMethod method = null;\n\t\ttry {\n\t\t\tmethod = pojoClass.getMethod(getMethodName.toString(), new Class[] {});\n\t\t} catch (Exception e) {\n\t\t\tmethod = pojoClass.getMethod(getMethodName.toString().replace(PoiBaseConstants.GET, PoiBaseConstants.IS), new Class[] {});\n\t\t}\n\t\treturn method;\n\t}\n\t\n\t/**\n\t * 获取set方法  通过EXCEL注解importConvert判断是否支持值的转换\n\t * @param name\n\t * @param pojoClass\n\t * @param type\n\t * @param convert\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic static Method getMethod(String name, Class<?> pojoClass, Class<?> type,boolean convert) throws Exception {\n\t\tStringBuffer setMethodName = new StringBuffer();\n\t\tif(convert){\n\t\t\tsetMethodName.append(PoiBaseConstants.CONVERT);\n\t\t}\n\t\tsetMethodName.append(PoiBaseConstants.SET);\n\t\tsetMethodName.append(name.substring(0, 1).toUpperCase());\n\t\tsetMethodName.append(name.substring(1));\n\t\treturn pojoClass.getMethod(setMethodName.toString(), new Class[] { type });\n\t}\n\t//update-end-author:taoyan date:20180615 for:TASK #2798 导入扩展方法，支持自定义导入字段转换规则\n\t\n\t/**\n\t * 获取Excel2003图片\n\t * \n\t * @param sheet\n\t *            当前sheet对象\n\t * @param workbook\n\t *            工作簿对象\n\t * @return Map key:图片单元格索引（1_1）String，value:图片流PictureData\n\t */\n\tpublic static Map<String, PictureData> getSheetPictrues03(HSSFSheet sheet, HSSFWorkbook workbook) {\n\t\tMap<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();\n\t\tList<HSSFPictureData> pictures = workbook.getAllPictures();\n\t\tif (!pictures.isEmpty()) {\n\t\t\tfor (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) {\n\t\t\t\tHSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();\n\t\t\t\tif (shape instanceof HSSFPicture) {\n\t\t\t\t\tHSSFPicture pic = (HSSFPicture) shape;\n\t\t\t\t\tint pictureIndex = pic.getPictureIndex() - 1;\n\t\t\t\t\tHSSFPictureData picData = pictures.get(pictureIndex);\n\t\t\t\t\tString picIndex = String.valueOf(anchor.getRow1()) + \"_\" + String.valueOf(anchor.getCol1());\n\t\t\t\t\tsheetIndexPicMap.put(picIndex, picData);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn sheetIndexPicMap;\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * 获取Excel2007图片\n\t * \n\t * @param sheet\n\t *            当前sheet对象\n\t * @param workbook\n\t *            工作簿对象\n\t * @return Map key:图片单元格索引（1_1）String，value:图片流PictureData\n\t */\n\tpublic static Map<String, PictureData> getSheetPictrues07(XSSFSheet sheet, XSSFWorkbook workbook) {\n\t\tMap<String, PictureData> sheetIndexPicMap = new HashMap<String, PictureData>();\n\t\tfor (POIXMLDocumentPart dr : sheet.getRelations()) {\n\t\t\tif (dr instanceof XSSFDrawing) {\n\t\t\t\tXSSFDrawing drawing = (XSSFDrawing) dr;\n\t\t\t\tList<XSSFShape> shapes = drawing.getShapes();\n\t\t\t\tfor (XSSFShape shape : shapes) {\n\t\t\t\t\tXSSFPicture pic = (XSSFPicture) shape;\n\t\t\t\t\tXSSFClientAnchor anchor = pic.getPreferredSize();\n\t\t\t\t\tCTMarker ctMarker = anchor.getFrom();\n\t\t\t\t\tString picIndex = ctMarker.getRow() + \"_\" + ctMarker.getCol();\n\t\t\t\t\tsheetIndexPicMap.put(picIndex, pic.getPictureData());\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn sheetIndexPicMap;\n\t}\n\n\t/**\n\t *\n\t * 获取嵌入图片 <br/>\n\t * 支持excel2007+版本.\n\t * @param sheet\n\t * @param isCopy\n\t * @param book\n\t * @return\n\t * @author chenrui\n\t * @date 2024/4/2 20:25\n\t */\n    public static Map<String, PictureData> getCellImages(Sheet sheet, ByteArrayOutputStream isCopy,Workbook book) {\n        // 获取所有嵌入图片的单元格的内容 date:2024/4/2\n        Map<String, CellImage> cellImageMap = new HashMap<>();\n        Iterator<Row> rows = sheet.rowIterator();\n        while (rows.hasNext()) {\n            Row row = rows.next();\n            Iterator<Cell> cells = row.cellIterator();\n            while (cells.hasNext()) {\n                Cell cell = cells.next();\n\t\t\t\tCellType cellType = cell.getCellType();\n\t\t\t\tif(cellType.equals(CellType.FORMULA)) {\n\t\t\t\t\tCellType resultType = cell.getCachedFormulaResultType();\n\t\t\t\t\tif(!resultType.equals(CellType.STRING)){\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tString cellVal = cell.getStringCellValue();\n\t\t\t\t\tif (null != cellVal && cellVal.startsWith(\"=DISPIMG\")) {\n\t\t\t\t\t\tint start = cellVal.indexOf(\"\\\"\");\n\t\t\t\t\t\tint end = cellVal.lastIndexOf(\"\\\"\");\n\t\t\t\t\t\tif (start != -1 && end != -1) {\n\t\t\t\t\t\t\tString imgId = cellVal.substring(start + 1, end);\n\t\t\t\t\t\t\tCellImage cellImage = new CellImage();\n\t\t\t\t\t\t\tcellImage.setImgId(imgId);\n\t\t\t\t\t\t\tcellImage.setCellStr(cellVal);\n\t\t\t\t\t\t\tcellImageMap.put(row.getRowNum() + \"_\" + cell.getColumnIndex(), cellImage);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n            }\n        }\n\n        try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(isCopy.toByteArray()));\n\t\t\t ZipInputStream fzis = new ZipInputStream(new ByteArrayInputStream(isCopy.toByteArray()))) {\n\t\t\t//update-begin---author:chenrui ---date:20240407  for：[QQYUN-8898]不依赖hutool,xml解析改为dom------------\n\t\t\tDocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();\n\t\t\tZipEntry entry;\n\t\t\t// 获取嵌入单元格图片的rid\n            while ((entry = zis.getNextEntry()) != null) {\n\t\t\t\ttry {\n                    final String fileName = entry.getName();\n                    if (Objects.equals(fileName, \"xl/cellimages.xml\")) {\n                        String content = IOUtils.toString(zis, StandardCharsets.UTF_8);\n\t\t\t\t\t\tDocument document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))));\n\t\t\t\t\t\tNodeList cellImages = document.getElementsByTagName(\"etc:cellImage\");\n\t\t\t\t\t\tif (Objects.isNull(cellImages)) {\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfor (int i = 0; i < cellImages.getLength(); i++) {\n\t\t\t\t\t\t\tNode cellImageNode = cellImages.item(i);\n\t\t\t\t\t\t\tNodeList cNvPr = ((Element) cellImageNode).getElementsByTagName(\"xdr:cNvPr\");\n\t\t\t\t\t\t\tif(cNvPr.getLength()<1){\n\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tNode cNvPrNode = cNvPr.item(0);\n\t\t\t\t\t\t\tString name = ((Element) cNvPrNode).getAttribute(\"name\");\n\t\t\t\t\t\t\tif (StringUtils.isNotEmpty(name)) {\n\t\t\t\t\t\t\t\tCellImage tempCellimage = cellImageMap.values().stream().filter(item -> Objects.equals(item.getImgId(), name)).findFirst().orElse(null);\n\t\t\t\t\t\t\t\tif (Objects.nonNull(tempCellimage)) {\n\t\t\t\t\t\t\t\t\tNodeList blips = ((Element) cellImageNode).getElementsByTagName(\"a:blip\");\n\t\t\t\t\t\t\t\t\tif(blips.getLength()<1){\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tNode blip = blips.item(0);\n\t\t\t\t\t\t\t\t\tString embed =((Element) blip).getAttribute(\"r:embed\");\n\t\t\t\t\t\t\t\t\tif(embed.isEmpty()){\n\t\t\t\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\ttempCellimage.setRId(embed);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n                    }\n                } catch (SAXException e) {\n                    throw new RuntimeException(e);\n                } finally {\n                    zis.closeEntry();\n                }\n            }\n\t\t\t// 获取嵌入单元格图片的存放位置\n            while ((entry = fzis.getNextEntry()) != null) {\n                try {\n                    final String fileName = entry.getName();\n                    if (Objects.equals(fileName, \"xl/_rels/cellimages.xml.rels\")) {\n                        String content = IOUtils.toString(fzis, StandardCharsets.UTF_8);\n\n\t\t\t\t\t\tDocument document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8))));\n\t\t\t\t\t\tNodeList relationships = document.getElementsByTagName(\"Relationship\");\n                        if (Objects.isNull(relationships)) {\n                            continue;\n                        }\n\t\t\t\t\t\tfor (int i = 0; i < relationships.getLength(); i++) {\n\t\t\t\t\t\t\tNode relationshipNode = relationships.item(i);\n\t\t\t\t\t\t\tif(relationshipNode instanceof Element){\n\t\t\t\t\t\t\t\tElement relationshipEl = (Element) relationshipNode;\n\t\t\t\t\t\t\t\tString id = relationshipEl.getAttribute(\"Id\");\n\t\t\t\t\t\t\t\tString target = \"/xl/\" + relationshipEl.getAttribute(\"Target\");\n\t\t\t\t\t\t\t\tif (StringUtils.isNotEmpty(id)) {\n\t\t\t\t\t\t\t\t\tList<CellImage> cellImages = cellImageMap.values().stream().filter(item -> Objects.equals(item.getRId(), id)).collect(Collectors.toList());\n\t\t\t\t\t\t\t\t\tcellImages.stream().filter(Objects::nonNull).forEach(cellImage -> cellImage.setImgName(target));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n                    }\n                } catch (SAXException e) {\n                    throw new RuntimeException(e);\n                } finally {\n                    fzis.closeEntry();\n                }\n            }\n\t\t\t// 获取嵌入单元格图片的图片数据\n            List<XSSFPictureData> allPictures = (List<XSSFPictureData>) book.getAllPictures();\n            for (XSSFPictureData pictureData : allPictures) {\n                PackagePartName partName = pictureData.getPackagePart().getPartName();\n                URI uri = partName.getURI();\n                List<CellImage> cellImages = cellImageMap.values().stream().filter(i -> Objects.equals(i.getImgName(), uri.toString())).collect(Collectors.toList());\n\t\t\t\tcellImages.stream().filter(Objects::nonNull).forEach(cellImage -> cellImage.setPictureData(pictureData));\n            }\n\t\t\t//update-end---author:chenrui ---date:20240407  for：[QQYUN-8898]不依赖hutool,xml解析改为dom------------\n\t\t} catch (IOException | ParserConfigurationException e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\n        Map<String, PictureData> resp = new HashMap<>();\n        if (!cellImageMap.isEmpty()) {\n            cellImageMap.forEach((key, cellImage) -> {\n                resp.put(key, cellImage.getPictureData());\n            });\n        }\n        return resp;\n    }\n\n\n\t/**\n\t * 嵌入单元格图片对象\n\t *\n\t * @author chenrui\n\t * @date 2024/4/3 18:27\n\t */\n    static class CellImage {\n\t\t/**\n\t\t * 图片id\n\t\t */\n        private String imgId;\n\t\t/**\n\t\t * 单元格内容\n\t\t */\n        private String cellStr;\n\t\t/**\n\t\t * RId\n\t\t */\n        private String rId;\n\t\t/**\n\t\t * 图片名称\n\t\t */\n        private String imgName;\n\t\t/**\n\t\t * 图片对象\n\t\t */\n        private XSSFPictureData pictureData;\n\n        public String getImgId() {\n            return imgId;\n        }\n\n        public void setImgId(String imgId) {\n            this.imgId = imgId;\n        }\n\n        public String getCellStr() {\n            return cellStr;\n        }\n\n        public void setCellStr(String cellStr) {\n            this.cellStr = cellStr;\n        }\n\n        public String getRId() {\n            return rId;\n        }\n\n        public void setRId(String rId) {\n            this.rId = rId;\n        }\n\n        public String getImgName() {\n            return imgName;\n        }\n\n        public void setImgName(String imgName) {\n            this.imgName = imgName;\n        }\n\n        public XSSFPictureData getPictureData() {\n            return pictureData;\n        }\n\n        public void setPictureData(XSSFPictureData pictureData) {\n            this.pictureData = pictureData;\n        }\n\n    }\n\n\tpublic static String getWebRootPath(String filePath) {\n\t\ttry {\n\t\t\tString path = null;\n\t\t\ttry {\n\t\t\t\tpath = PoiPublicUtil.class.getClassLoader().getResource(\"\").toURI().getPath();\n\t\t\t} catch (URISyntaxException e) {\n\t\t\t\t//e.printStackTrace();\n\t\t\t//update-begin-author:taoyan date:20211116 for: JAR包分离 发布出空指针 https://gitee.com/jeecg/jeecg-boot/issues/I4CMHK\n\t\t\t}catch (NullPointerException e) {\n\t\t\t\tpath =  PoiPublicUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();\n\t\t\t}\n\t\t\t//update-end-author:taoyan date:20211116 for: JAR包分离 发布出空指针 https://gitee.com/jeecg/jeecg-boot/issues/I4CMHK\n\t\t\t//update-begin--Author:zhangdaihao  Date:20190424 for：解决springboot 启动模式，上传路径获取为空问题---------------------\n\t\t\tif (path == null || path == \"\") {\n\t\t\t\t//解决springboot 启动模式，上传路径获取为空问题\n\t\t\t\tpath = ClassUtils.getDefaultClassLoader().getResource(\"\").getPath();\n\t\t\t}\n\t\t\t//update-end--Author:zhangdaihao  Date:20190424 for：解决springboot 启动模式，上传路径获取为空问题----------------------\n\t\t\tLOGGER.debug(\"--- getWebRootPath ----filePath--- \" + path);\n\t\t\tpath = path.replace(\"WEB-INF/classes/\", \"\");\n\t\t\tpath = path.replace(\"file:/\", \"\");\n\t\t\tLOGGER.debug(\"--- path---  \" + path);\n\t\t\tLOGGER.debug(\"--- filePath---  \" + filePath);\n\t\t\treturn path + filePath;\n\t\t} catch (Exception e) {\n\t\t\tthrow new RuntimeException(e);\n\t\t}\n\t}\n\n\t/**\n\t * 判断是不是集合的实现类\n\t * \n\t * @param clazz\n\t * @return\n\t */\n\tpublic static boolean isCollection(Class<?> clazz) {\n\t\treturn Collection.class.isAssignableFrom(clazz);\n\t}\n\n\t/**\n\t * 是不是java基础类\n\t * \n\t * @param field\n\t * @return\n\t */\n\tpublic static boolean isJavaClass(Field field) {\n\t\tClass<?> fieldType = field.getType();\n\t\tboolean isBaseClass = false;\n\t\tif (fieldType.isArray()) {\n\t\t\tisBaseClass = false;\n\t\t} else if (fieldType.isPrimitive()\n\t\t\t\t|| fieldType.getPackage() == null\n\t\t\t\t|| fieldType.getPackage().getName().equals(\"java.lang\")\n\t\t\t\t|| fieldType.getPackage().getName().equals(\"java.math\")\n\t\t\t\t|| fieldType.getPackage().getName().equals(\"java.sql\")\n\t\t\t\t|| fieldType.getPackage().getName().equals(\"java.util\")\n\t\t\t\t|| fieldType.getPackage().getName().equals(\"java.time\")) {\n\t\t\tisBaseClass = true;\n\t\t}\n\t\treturn isBaseClass;\n\t}\n\n\t/**\n\t * 判断是否不要在这个excel操作中\n\t * \n\t * @param\n\t * @param field\n\t * @param targetId\n\t * @return\n\t */\n\tpublic static boolean isNotUserExcelUserThis(List<String> exclusionsList, Field field, String targetId) {\n\t\tboolean boo = true;\n\t\tif (field.getAnnotation(ExcelIgnore.class) != null) {\n\t\t\tboo = true;\n\t\t} else if (boo && field.getAnnotation(ExcelCollection.class) != null && isUseInThis(field.getAnnotation(ExcelCollection.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(ExcelCollection.class).name()))) {\n\t\t\tboo = false;\n\t\t} else if (boo && field.getAnnotation(Excel.class) != null && isUseInThis(field.getAnnotation(Excel.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(Excel.class).name()))) {\n\t\t\tboo = false;\n\t\t} else if (boo && field.getAnnotation(ExcelEntity.class) != null && isUseInThis(field.getAnnotation(ExcelEntity.class).name(), targetId) && (exclusionsList == null || !exclusionsList.contains(field.getAnnotation(ExcelEntity.class).name()))) {\n\t\t\tboo = false;\n\t\t}\n\t\treturn boo;\n\t}\n\n\t/**\n\t * 判断是不是使用\n\t * \n\t * @param exportName\n\t * @param targetId\n\t * @return\n\t */\n\tprivate static boolean isUseInThis(String exportName, String targetId) {\n\t\treturn targetId == null || exportName.equals(\"\") || exportName.indexOf(\"_\") < 0 || exportName.indexOf(targetId) != -1;\n\t}\n\n\tprivate static Integer getImageType(String type) {\n\t\tif (type.equalsIgnoreCase(\"JPG\") || type.equalsIgnoreCase(\"JPEG\")) {\n\t\t\treturn XWPFDocument.PICTURE_TYPE_JPEG;\n\t\t}\n\t\tif (type.equalsIgnoreCase(\"GIF\")) {\n\t\t\treturn XWPFDocument.PICTURE_TYPE_GIF;\n\t\t}\n\t\tif (type.equalsIgnoreCase(\"BMP\")) {\n\t\t\treturn XWPFDocument.PICTURE_TYPE_GIF;\n\t\t}\n\t\tif (type.equalsIgnoreCase(\"PNG\")) {\n\t\t\treturn XWPFDocument.PICTURE_TYPE_PNG;\n\t\t}\n\t\treturn XWPFDocument.PICTURE_TYPE_JPEG;\n\t}\n\n\t/**\n\t * 返回流和图片类型\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-20\n\t * @param entity\n\t * @return (byte[]) isAndType[0],(Integer)isAndType[1]\n\t * @throws Exception\n\t */\n\tpublic static Object[] getIsAndType(WordImageEntity entity) throws Exception {\n\t\tObject[] result = new Object[2];\n\t\tString type;\n\t\tif (entity.getType().equals(WordImageEntity.URL)) {\n\t\t\tByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();\n\t\t\tBufferedImage bufferImg;\n\t\t\tString path = Thread.currentThread().getContextClassLoader().getResource(\"\").toURI().getPath() + entity.getUrl();\n\t\t\tpath = path.replace(\"WEB-INF/classes/\", \"\");\n\t\t\tpath = path.replace(\"file:/\", \"\");\n\t\t\tbufferImg = ImageIO.read(new File(path));\n\t\t\t//update-begin-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR\n\t\t\tImageIO.write(bufferImg, entity.getUrl().substring(entity.getUrl().lastIndexOf(\".\") + 1, entity.getUrl().length()), byteArrayOut);\n\t\t\t//update-end-author:taoYan date:20211203 for: Excel 导出图片的文件带小数点符号 导出报错 https://gitee.com/jeecg/jeecg-boot/issues/I4JNHR\n\t\t\tresult[0] = byteArrayOut.toByteArray();\n\t\t\ttype = entity.getUrl().split(\"/.\")[entity.getUrl().split(\"/.\").length - 1];\n\t\t} else {\n\t\t\tresult[0] = entity.getData();\n\t\t\ttype = PoiPublicUtil.getFileExtendName(entity.getData());\n\t\t}\n\t\tresult[1] = getImageType(type);\n\t\treturn result;\n\t}\n\n\t/**\n\t * 获取参数值\n\t * \n\t * @param params\n\t * @param map\n\t * @return\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic static Object getParamsValue(String params, Object object) throws Exception {\n\t\tif (params.indexOf(\".\") != -1) {\n\t\t\tString[] paramsArr = params.split(\"\\\\.\");\n\t\t\treturn getValueDoWhile(object, paramsArr, 0);\n\t\t}\n\t\tif (object instanceof Map) {\n\t\t\treturn ((Map) object).get(params);\n\t\t}\n\t\treturn getMethod(params, object.getClass()).invoke(object, new Object[] {});\n\t}\n\n\t/**\n\t * 解析数据\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-16\n\t * @return\n\t */\n\tpublic static Object getRealValue(String currentText, Map<String, Object> map) throws Exception {\n\t\tString params = \"\";\n\t\twhile (currentText.indexOf(\"{{\") != -1) {\n\t\t\tparams = currentText.substring(currentText.indexOf(\"{{\") + 2, currentText.indexOf(\"}}\"));\n\t\t\t//update-begin-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时：三目、求长、常量、日期转换没起效果\n\t\t\tObject obj = PoiElUtil.eval(params.trim(), map);\n\t\t\t//update-end-author:liusq---date:2024-08-07--for: [issues/6925]autopoi通过word模板生成word时：三目、求长、常量、日期转换没起效果\n\t\t\t// 判断图片或者是集合\n\t\t\t// update-begin-author:taoyan date:20210914 for:autopoi模板导出，赋值的方法建议增加空判断或抛出异常说明。 /issues/3005\n\t\t\tif(obj==null){\n\t\t\t\tobj = \"\";\n\t\t\t}\n\t\t\t// update-end-author:taoyan date:20210914 for:autopoi模板导出，赋值的方法建议增加空判断或抛出异常说明。/issues/3005\n\t\t\tif (obj instanceof WordImageEntity || obj instanceof List || obj instanceof ExcelListEntity) {\n\t\t\t\treturn obj;\n\t\t\t} else {\n\t\t\t\tcurrentText = currentText.replace(\"{{\" + params + \"}}\", obj.toString());\n\t\t\t}\n\t\t}\n\t\treturn currentText;\n\t}\n\n\t/**\n\t * 通过遍历过去对象值\n\t * \n\t * @param object\n\t * @param paramsArr\n\t * @param index\n\t * @return\n\t * @throws Exception\n\t */\n\t@SuppressWarnings(\"rawtypes\")\n\tpublic static Object getValueDoWhile(Object object, String[] paramsArr, int index) throws Exception {\n\t\tif (object == null) {\n\t\t\treturn \"\";\n\t\t}\n\t\tif (object instanceof WordImageEntity) {\n\t\t\treturn object;\n\t\t}\n\t\tif (object instanceof Map) {\n\t\t\tobject = ((Map) object).get(paramsArr[index]);\n\t\t} else {\n\t\t\tobject = getMethod(paramsArr[index], object.getClass()).invoke(object, new Object[] {});\n\t\t}\n\t\treturn (index == paramsArr.length - 1) ? (object == null ? \"\" : object) : getValueDoWhile(object, paramsArr, ++index);\n\t}\n\n\t/**\n\t * double to String 防止科学计数法\n\t * \n\t * @param value\n\t * @return\n\t */\n\tpublic static String doubleToString(Double value) {\n\t\tString temp = value.toString();\n\t\tif (temp.contains(\"E\")) {\n\t\t\tBigDecimal bigDecimal = new BigDecimal(temp);\n\t\t\ttemp = bigDecimal.toPlainString();\n\t\t}\n\t\t//---update-begin-----autor:scott------date:20191016-------for:excel导入数字类型，去掉后缀.0------\n\t\treturn ExcelUtil.remove0Suffix(temp);\n\t\t//---update-end-----autor:scott------date:20191016-------for:excel导入数字类型，去掉后缀.0------\n\t}\n\n\t/**\n\t * 判断是否是数值类型\n\t * @param xclass\n\t * @return\n\t */\n\tpublic static boolean isNumber(String xclass){\n\t\tif(xclass==null){\n\t\t\treturn false;\n\t\t}\n\t\tString temp = xclass.toLowerCase();\n\t\tif(temp.indexOf(\"int\")>=0 || temp.indexOf(\"double\")>=0 || temp.indexOf(\"decimal\")>=0){\n\t\t\treturn true;\n\t\t}\n\t\treturn false;\n\t}\n\t//update-begin---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\t/**\n\t * 统一 key的获取规则\n\t * @param key\n\t * @param targetId\n\t * @date  2022年1月4号\n\t * @return\n\t */\n\tpublic static String getValueByTargetId(String key, String targetId, String defalut) {\n\t\tif (StringUtils.isEmpty(targetId) || key.indexOf(\"_\") < 0) {\n\t\t\treturn key;\n\t\t}\n\t\tString[] arr = key.split(\",\");\n\t\tString[] tempArr;\n\t\tfor (String str : arr) {\n\t\t\ttempArr = str.split(\"_\");\n\t\t\tif (tempArr == null || tempArr.length < 2) {\n\t\t\t\treturn defalut;\n\t\t\t}\n\t\t\tif (targetId.equals(tempArr[1])) {\n\t\t\t\treturn tempArr[0];\n\t\t\t}\n\t\t}\n\t\treturn defalut;\n\t}\n\t//update-end---author:liusq  Date:20211217  for：[LOWCOD-2521]【autopoi】大数据导出方法【全局】----\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiReflectorUtil.java",
    "content": "package org.jeecgframework.poi.util;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ReflectPermission;\nimport java.util.*;\nimport java.util.concurrent.ConcurrentHashMap;\n\n\n/**\n * 反射工具类,缓存读取的class信息,省的一直获取\n * @Description [LOWCOD-2521]【autopoi】大数据导出方法【全局】\n * @author liusq\n * @date  2022年1月4号\n */\npublic final class PoiReflectorUtil {\n\n    private static final Map<Class<?>, PoiReflectorUtil> CACHE_REFLECTOR = new ConcurrentHashMap<Class<?>, PoiReflectorUtil>();\n\n    private Map<String, Method> getMethods = new HashMap<String, Method>();\n    private Map<String, Method> setMethods = new HashMap<String, Method>();\n    private Map<String, Method> enumMethods = new HashMap<String, Method>();\n    private List<Field> fieldList = new ArrayList<Field>();\n\n    private Class<?> type;\n\n    private PoiReflectorUtil(Class<?> clazz) {\n        this.type = clazz;\n        addGetMethods(clazz);\n        addFields(clazz);\n        addSetMethods(clazz);\n    }\n\n    public static PoiReflectorUtil forClass(Class<?> clazz) {\n        return new PoiReflectorUtil(clazz);\n    }\n\n    public static PoiReflectorUtil fromCache(Class<?> clazz) {\n        if (!CACHE_REFLECTOR.containsKey(clazz)) {\n            CACHE_REFLECTOR.put(clazz, new PoiReflectorUtil(clazz));\n        }\n        return CACHE_REFLECTOR.get(clazz);\n    }\n\n    private void addGetMethods(Class<?> cls) {\n        Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();\n        Method[] methods = getClassMethods(cls);\n        for (Method method : methods) {\n            String name = method.getName();\n            if (name.startsWith(\"get\") && name.length() > 3) {\n                if (method.getParameterTypes().length == 0) {\n                    name = methodToProperty(name);\n                    addMethodConflict(conflictingGetters, name, method);\n                }\n            } else if (name.startsWith(\"is\") && name.length() > 2) {\n                if (method.getParameterTypes().length == 0) {\n                    name = methodToProperty(name);\n                    addMethodConflict(conflictingGetters, name, method);\n                }\n            }\n        }\n        resolveGetterConflicts(conflictingGetters);\n    }\n\n    private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {\n        for (String propName : conflictingGetters.keySet()) {\n            List<Method> getters = conflictingGetters.get(propName);\n            Iterator<Method> iterator = getters.iterator();\n            Method firstMethod = iterator.next();\n            if (getters.size() == 1) {\n                addGetMethod(propName, firstMethod);\n            } else {\n                Method getter = firstMethod;\n                Class<?> getterType = firstMethod.getReturnType();\n                while (iterator.hasNext()) {\n                    Method method = iterator.next();\n                    Class<?> methodType = method.getReturnType();\n                    if (methodType.equals(getterType)) {\n                        throw new RuntimeException(\n                                \"Illegal overloaded getter method with ambiguous type for property \"\n                                        + propName + \" in class \"\n                                        + firstMethod.getDeclaringClass()\n                                        + \".  This breaks the JavaBeans \"\n                                        + \"specification and can cause unpredicatble results.\");\n                    } else if (methodType.isAssignableFrom(getterType)) {\n                        // OK getter type is descendant\n                    } else if (getterType.isAssignableFrom(methodType)) {\n                        getter = method;\n                        getterType = methodType;\n                    } else {\n                        throw new RuntimeException(\n                                \"Illegal overloaded getter method with ambiguous type for property \"\n                                        + propName + \" in class \"\n                                        + firstMethod.getDeclaringClass()\n                                        + \".  This breaks the JavaBeans \"\n                                        + \"specification and can cause unpredicatble results.\");\n                    }\n                }\n                addGetMethod(propName, getter);\n            }\n        }\n    }\n\n    private void addGetMethod(String name, Method method) {\n        if (isValidPropertyName(name)) {\n            getMethods.put(name, method);\n        }\n    }\n\n    private void addSetMethods(Class<?> cls) {\n        Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();\n        Method[] methods = getClassMethods(cls);\n        for (Method method : methods) {\n            String name = method.getName();\n            if (name.startsWith(\"set\") && name.length() > 3) {\n                if (method.getParameterTypes().length == 1) {\n                    name = methodToProperty(name);\n                    addMethodConflict(conflictingSetters, name, method);\n                }\n            }\n        }\n        resolveSetterConflicts(conflictingSetters);\n    }\n\n    private static String methodToProperty(String name) {\n        if (name.startsWith(\"is\")) {\n            name = name.substring(2);\n        } else if (name.startsWith(\"get\") || name.startsWith(\"set\")) {\n            name = name.substring(3);\n        } else {\n            throw new RuntimeException(\"Error parsing property name '\" + name\n                    + \"'.  Didn't start with 'is', 'get' or 'set'.\");\n        }\n\n        if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {\n            name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);\n        }\n\n        return name;\n    }\n\n    private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name,\n                                   Method method) {\n        List<Method> list = conflictingMethods.get(name);\n        if (list == null) {\n            list = new ArrayList<Method>();\n            conflictingMethods.put(name, list);\n        }\n        list.add(method);\n    }\n\n    private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {\n        for (String propName : conflictingSetters.keySet()) {\n            List<Method> setters = conflictingSetters.get(propName);\n            Method firstMethod = setters.get(0);\n            if (setters.size() == 1) {\n                addSetMethod(propName, firstMethod);\n            } else {\n                Iterator<Method> methods = setters.iterator();\n                Method setter = null;\n                while (methods.hasNext()) {\n                    Method method = methods.next();\n                    if (method.getParameterTypes().length == 1) {\n                        setter = method;\n                        break;\n                    }\n                }\n                if (setter == null) {\n                    throw new RuntimeException(\n                            \"Illegal overloaded setter method with ambiguous type for property \"\n                                    + propName + \" in class \"\n                                    + firstMethod.getDeclaringClass()\n                                    + \".  This breaks the JavaBeans \"\n                                    + \"specification and can cause unpredicatble results.\");\n                }\n                addSetMethod(propName, setter);\n            }\n        }\n    }\n\n    private void addSetMethod(String name, Method method) {\n        if (isValidPropertyName(name)) {\n            setMethods.put(name, method);\n        }\n    }\n\n    private void addFields(Class<?> clazz) {\n        Field[] fields = clazz.getDeclaredFields();\n        for (Field field : fields) {\n            if (canAccessPrivateMethods()) {\n                try {\n                    field.setAccessible(true);\n                } catch (Exception e) {\n                    // Ignored. This is only a final precaution, nothing we can do.\n                }\n            }\n            if (field.isAccessible() && !\"serialVersionUID\".equalsIgnoreCase(field.getName())) {\n                this.fieldList.add(field);\n            }\n        }\n        if (clazz.getSuperclass() != null) {\n            addFields(clazz.getSuperclass());\n        }\n    }\n\n    private boolean isValidPropertyName(String name) {\n        return !(name.startsWith(\"$\") || \"serialVersionUID\".equals(name) || \"class\".equals(name));\n    }\n\n    /*\n     * This method returns an array containing all methods\n     * declared in this class and any superclass.\n     * We use this method, instead of the simpler Class.getMethods(),\n     * because we want to look for private methods as well.\n     *\n     * @param cls The class\n     * @return An array containing all methods in this class\n     */\n    private Method[] getClassMethods(Class<?> cls) {\n        HashMap<String, Method> uniqueMethods = new HashMap<String, Method>();\n        Class<?> currentClass = cls;\n        while (currentClass != null) {\n            addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());\n\n            // we also need to look for interface methods - \n            // because the class may be abstract\n            Class<?>[] interfaces = currentClass.getInterfaces();\n            for (Class<?> anInterface : interfaces) {\n                addUniqueMethods(uniqueMethods, anInterface.getMethods());\n            }\n\n            currentClass = currentClass.getSuperclass();\n        }\n\n        Collection<Method> methods = uniqueMethods.values();\n\n        return methods.toArray(new Method[methods.size()]);\n    }\n\n    private void addUniqueMethods(HashMap<String, Method> uniqueMethods, Method[] methods) {\n        for (Method currentMethod : methods) {\n            if (!currentMethod.isBridge()) {\n                String signature = getSignature(currentMethod);\n                // check to see if the method is already known\n                // if it is known, then an extended class must have\n                // overridden a method\n                if (!uniqueMethods.containsKey(signature)) {\n                    if (canAccessPrivateMethods()) {\n                        try {\n                            currentMethod.setAccessible(true);\n                        } catch (Exception e) {\n                            // Ignored. This is only a final precaution, nothing we can do.\n                        }\n                    }\n\n                    uniqueMethods.put(signature, currentMethod);\n                }\n            }\n        }\n    }\n\n    private String getSignature(Method method) {\n        StringBuilder sb = new StringBuilder();\n        Class<?> returnType = method.getReturnType();\n        if (returnType != null) {\n            sb.append(returnType.getName()).append('#');\n        }\n        sb.append(method.getName());\n        Class<?>[] parameters = method.getParameterTypes();\n        for (int i = 0; i < parameters.length; i++) {\n            if (i == 0) {\n                sb.append(':');\n            } else {\n                sb.append(',');\n            }\n            sb.append(parameters[i].getName());\n        }\n        return sb.toString();\n    }\n\n    private boolean canAccessPrivateMethods() {\n        try {\n            SecurityManager securityManager = System.getSecurityManager();\n            if (null != securityManager) {\n                securityManager.checkPermission(new ReflectPermission(\"suppressAccessChecks\"));\n            }\n        } catch (SecurityException e) {\n            return false;\n        }\n        return true;\n    }\n\n    public Method getGetMethod(String propertyName) {\n        Method method = getMethods.get(propertyName);\n        if (method == null) {\n            throw new RuntimeException(\n                    \"There is no getter for property named '\" + propertyName + \"' in '\" + type + \"'\");\n        }\n        return method;\n    }\n\n    public Method getSetMethod(String propertyName) {\n        Method method = setMethods.get(propertyName);\n        if (method == null) {\n            throw new RuntimeException(\n                    \"There is no setter for property named '\" + propertyName + \"' in '\" + type + \"'\");\n        }\n        return method;\n    }\n\n    /**\n     * 获取field 值\n     *\n     * @param obj\n     * @param property\n     * @return\n     */\n    public Object getValue(Object obj, String property) {\n        Object value = null;\n        Method m = getMethods.get(property);\n        if (m != null) {\n            try {\n                value = m.invoke(obj, new Object[]{});\n            } catch (Exception ex) {\n            }\n        }\n        return value;\n    }\n\n    /**\n     * 设置field值\n     *\n     * @param obj      对象\n     * @param property\n     * @param object   属性值\n     * @return\n     */\n    public boolean setValue(Object obj, String property, Object object) {\n        Method m = setMethods.get(property);\n        if (m != null) {\n            try {\n                m.invoke(obj, object);\n                return true;\n            } catch (Exception ex) {\n                return false;\n            }\n        }\n        return false;\n    }\n\n    public Map<String, Method> getGetMethods() {\n        return getMethods;\n    }\n\n    public List<Field> getFieldList() {\n        return fieldList;\n    }\n\n    public Object execEnumStaticMethod(String staticMethod, Object params) {\n        if (!enumMethods.containsKey(setMethods)) {\n            try {\n                enumMethods.put(staticMethod,type.getMethod(staticMethod,params.getClass()));\n            } catch (NoSuchMethodException e) {\n                throw new RuntimeException(\n                        \"There is no enum for property named '\" + staticMethod + \"' in '\" + type + \"'\");\n            }\n        }\n        try {\n            return enumMethods.get(staticMethod).invoke(null,params);\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/util/PoiSheetUtility.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.util;\n\nimport org.apache.poi.ss.usermodel.Cell;\nimport org.apache.poi.ss.usermodel.Row;\nimport org.apache.poi.ss.usermodel.Sheet;\n\n/**\n * 国外高手的,不过也不好,慎用,效率不行 Helper functions to aid in the management of sheets\n */\npublic class PoiSheetUtility extends Object {\n\n\t/**\n\t * Given a sheet, this method deletes a column from a sheet and moves all\n\t * the columns to the right of it to the left one cell.\n\t * \n\t * Note, this method will not update any formula references.\n\t * \n\t * @param sheet\n\t * @param column\n\t */\n\tpublic static void deleteColumn(Sheet sheet, int columnToDelete) {\n\t\tint maxColumn = 0;\n\t\tfor (int r = 0; r < sheet.getLastRowNum() + 1; r++) {\n\t\t\tRow row = sheet.getRow(r);\n\n\t\t\t// if no row exists here; then nothing to do; next!\n\t\t\tif (row == null)\n\t\t\t\tcontinue;\n\n\t\t\t// if the row doesn't have this many columns then we are good; next!\n\t\t\tint lastColumn = row.getLastCellNum();\n\t\t\tif (lastColumn > maxColumn)\n\t\t\t\tmaxColumn = lastColumn;\n\n\t\t\tif (lastColumn < columnToDelete)\n\t\t\t\tcontinue;\n\n\t\t\tfor (int x = columnToDelete + 1; x < lastColumn + 1; x++) {\n\t\t\t\tCell oldCell = row.getCell(x - 1);\n\t\t\t\tif (oldCell != null)\n\t\t\t\t\trow.removeCell(oldCell);\n\n\t\t\t\tCell nextCell = row.getCell(x);\n\t\t\t\tif (nextCell != null) {\n\t\t\t\t\tCell newCell = row.createCell(x - 1, nextCell.getCellType());\n\t\t\t\t\tcloneCell(newCell, nextCell);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Adjust the column widths\n\t\tfor (int c = 0; c < maxColumn; c++) {\n\t\t\tsheet.setColumnWidth(c, sheet.getColumnWidth(c + 1));\n\t\t}\n\t}\n\n\t/*\n\t * Takes an existing Cell and merges all the styles and forumla into the new\n\t * one\n\t */\n\tprivate static void cloneCell(Cell cNew, Cell cOld) {\n\t\tcNew.setCellComment(cOld.getCellComment());\n\tcNew.setCellStyle(cOld.getCellStyle());\n\n\tswitch (cNew.getCellType()) {\n\tcase BOOLEAN: {\n\t\t\tcNew.setCellValue(cOld.getBooleanCellValue());\n\t\t\tbreak;\n\t\t}\n\t\tcase NUMERIC: {\n\t\t\tcNew.setCellValue(cOld.getNumericCellValue());\n\t\t\tbreak;\n\t\t}\n\t\tcase STRING: {\n\t\t\tcNew.setCellValue(cOld.getStringCellValue());\n\t\t\tbreak;\n\t\t}\n\t\tcase ERROR: {\n\t\t\tcNew.setCellValue(cOld.getErrorCellValue());\n\t\t\tbreak;\n\t\t}\n\t\tcase FORMULA: {\n\t\t\tcNew.setCellFormula(cOld.getCellFormula());\n\t\t\tbreak;\n\t\t}\n\t\t}\n\n\t}\n}"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/WordExportUtil.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word;\n\nimport java.util.Map;\n\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.jeecgframework.poi.word.parse.ParseWord07;\n\n/**\n * Word使用模板导出工具类\n * \n * @author JEECG\n * @date 2013-11-16\n * @version 1.0\n */\npublic final class WordExportUtil {\n\n\tprivate WordExportUtil() {\n\n\t}\n\n\t/**\n\t * 解析Word2007版本\n\t * \n\t * @param url\n\t *            模板地址\n\t * @param map\n\t *            解析数据源\n\t * @return\n\t */\n\tpublic static XWPFDocument exportWord07(String url, Map<String, Object> map) throws Exception {\n\t\treturn new ParseWord07().parseWord(url, map);\n\t}\n\n\t/**\n\t * 解析Word2007版本\n\t * \n\t * @param XWPFDocument\n\t *            模板\n\t * @param map\n\t *            解析数据源\n\t * @return\n\t */\n\tpublic static void exportWord07(XWPFDocument document, Map<String, Object> map) throws Exception {\n\t\tnew ParseWord07().parseWord(document, map);\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/entity/MyXWPFDocument.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.entity;\n\nimport java.io.InputStream;\n\nimport org.apache.poi.openxml4j.opc.OPCPackage;\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.apache.poi.xwpf.usermodel.XWPFRun;\nimport org.apache.xmlbeans.XmlException;\nimport org.apache.xmlbeans.XmlToken;\nimport org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;\nimport org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;\nimport org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 扩充document,修复图片插入失败问题问题\n * \n * @author JEECG\n * @date 2013-11-20\n * @version 1.0\n */\npublic class MyXWPFDocument extends XWPFDocument {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(MyXWPFDocument.class);\n\n\tprivate static String PICXML = \"\" + \"<a:graphic xmlns:a=\\\"http://schemas.openxmlformats.org/drawingml/2006/main\\\">\" + \"   <a:graphicData uri=\\\"http://schemas.openxmlformats.org/drawingml/2006/picture\\\">\" + \"      <pic:pic xmlns:pic=\\\"http://schemas.openxmlformats.org/drawingml/2006/picture\\\">\" + \"         <pic:nvPicPr>\" + \"            <pic:cNvPr id=\\\"%s\\\" name=\\\"Generated\\\"/>\" + \"            <pic:cNvPicPr/>\" + \"         </pic:nvPicPr>\" + \"         <pic:blipFill>\" + \"            <a:blip r:embed=\\\"%s\\\" xmlns:r=\\\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\\\"/>\" + \"            <a:stretch>\" + \"               <a:fillRect/>\" + \"            </a:stretch>\" + \"         </pic:blipFill>\" + \"         <pic:spPr>\" + \"            <a:xfrm>\"\n\t\t\t+ \"               <a:off x=\\\"0\\\" y=\\\"0\\\"/>\" + \"               <a:ext cx=\\\"%s\\\" cy=\\\"%s\\\"/>\" + \"            </a:xfrm>\" + \"            <a:prstGeom prst=\\\"rect\\\">\" + \"               <a:avLst/>\" + \"            </a:prstGeom>\" + \"         </pic:spPr>\" + \"      </pic:pic>\" + \"   </a:graphicData>\" + \"</a:graphic>\";\n\n\tpublic MyXWPFDocument() {\n\t\tsuper();\n\t}\n\n\tpublic MyXWPFDocument(InputStream in) throws Exception {\n\t\tsuper(in);\n\t}\n\n\tpublic MyXWPFDocument(OPCPackage opcPackage) throws Exception {\n\t\tsuper(opcPackage);\n\t}\n\n\tpublic void createPicture(String blipId, int id, int width, int height) {\n\t\tfinal int EMU = 9525;\n\t\twidth *= EMU;\n\t\theight *= EMU;\n\t\tCTInline inline = createParagraph().createRun().getCTR().addNewDrawing().addNewInline();\n\t\tString picXml = String.format(PICXML, id, blipId, width, height);\n\t\tXmlToken xmlToken = null;\n\t\ttry {\n\t\t\txmlToken = XmlToken.Factory.parse(picXml);\n\t\t} catch (XmlException xe) {\n\t\t\tLOGGER.error(xe.getMessage(), xe.fillInStackTrace());\n\t\t}\n\t\tinline.set(xmlToken);\n\n\t\tinline.setDistT(0);\n\t\tinline.setDistB(0);\n\t\tinline.setDistL(0);\n\t\tinline.setDistR(0);\n\n\t\tCTPositiveSize2D extent = inline.addNewExtent();\n\t\textent.setCx(width);\n\t\textent.setCy(height);\n\n\t\tCTNonVisualDrawingProps docPr = inline.addNewDocPr();\n\t\tdocPr.setId(id);\n\t\tdocPr.setName(\"Picture \" + id);\n\t\tdocPr.setDescr(\"Generated\");\n\t}\n\n\tpublic void createPicture(XWPFRun run, String blipId, int id, int width, int height) {\n\t\tfinal int EMU = 9525;\n\t\twidth *= EMU;\n\t\theight *= EMU;\n\t\tCTInline inline = run.getCTR().addNewDrawing().addNewInline();\n\t\tString picXml = String.format(PICXML, id, blipId, width, height);\n\t\tXmlToken xmlToken = null;\n\t\ttry {\n\t\t\txmlToken = XmlToken.Factory.parse(picXml);\n\t\t} catch (XmlException xe) {\n\t\t\tLOGGER.error(xe.getMessage(), xe.fillInStackTrace());\n\t\t}\n\t\tinline.set(xmlToken);\n\n\t\tinline.setDistT(0);\n\t\tinline.setDistB(0);\n\t\tinline.setDistL(0);\n\t\tinline.setDistR(0);\n\n\t\tCTPositiveSize2D extent = inline.addNewExtent();\n\t\textent.setCx(width);\n\t\textent.setCy(height);\n\n\t\tCTNonVisualDrawingProps docPr = inline.addNewDocPr();\n\t\tdocPr.setId(id);\n\t\tdocPr.setName(\"Picture \" + id);\n\t\tdocPr.setDescr(\"Generated\");\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/entity/WordImageEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.entity;\n\n/**\n * word导出,图片设置和图片信息\n * \n * @author JEECG\n * @date 2013-11-17\n * @version 1.0\n */\npublic class WordImageEntity {\n\n\tpublic static String URL = \"url\";\n\tpublic static String Data = \"data\";\n\t/**\n\t * 图片输入方式\n\t */\n\tprivate String type = URL;\n\t/**\n\t * 图片宽度\n\t */\n\tprivate int width;\n\t// 图片高度\n\tprivate int height;\n\t// 图片地址\n\tprivate String url;\n\t// 图片信息\n\tprivate byte[] data;\n\n\tpublic WordImageEntity() {\n\n\t}\n\n\tpublic WordImageEntity(byte[] data, int width, int height) {\n\t\tthis.data = data;\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t\tthis.type = Data;\n\t}\n\n\tpublic WordImageEntity(String url, int width, int height) {\n\t\tthis.url = url;\n\t\tthis.width = width;\n\t\tthis.height = height;\n\t}\n\n\tpublic byte[] getData() {\n\t\treturn data;\n\t}\n\n\tpublic int getHeight() {\n\t\treturn height;\n\t}\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic String getUrl() {\n\t\treturn url;\n\t}\n\n\tpublic int getWidth() {\n\t\treturn width;\n\t}\n\n\tpublic void setData(byte[] data) {\n\t\tthis.data = data;\n\t}\n\n\tpublic void setHeight(int height) {\n\t\tthis.height = height;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic void setUrl(String url) {\n\t\tthis.url = url;\n\t}\n\n\tpublic void setWidth(int width) {\n\t\tthis.width = width;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/entity/params/ExcelListEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.entity.params;\n\nimport java.util.List;\n\nimport org.jeecgframework.poi.excel.entity.ExcelBaseParams;\nimport org.jeecgframework.poi.handler.inter.IExcelDataHandler;\n\n/**\n * Excel 导出对象\n * \n * @author JEECG\n * @date 2014年8月9日 下午10:21:13\n */\npublic class ExcelListEntity extends ExcelBaseParams {\n\n\t/**\n\t * 数据源\n\t */\n\tprivate List<?> list;\n\n\t/**\n\t * 实体类对象\n\t */\n\tprivate Class<?> clazz;\n\n\t/**\n\t * 表头行数\n\t */\n\tprivate int headRows = 1;\n\n\tpublic ExcelListEntity() {\n\n\t}\n\n\tpublic ExcelListEntity(List<?> list, Class<?> clazz) {\n\t\tthis.list = list;\n\t\tthis.clazz = clazz;\n\t}\n\n\tpublic ExcelListEntity(List<?> list, Class<?> clazz, IExcelDataHandler dataHanlder) {\n\t\tthis.list = list;\n\t\tthis.clazz = clazz;\n\t\tsetDataHanlder(dataHanlder);\n\t}\n\n\tpublic ExcelListEntity(List<?> list, Class<?> clazz, IExcelDataHandler dataHanlder, int headRows) {\n\t\tthis.list = list;\n\t\tthis.clazz = clazz;\n\t\tthis.headRows = headRows;\n\t\tsetDataHanlder(dataHanlder);\n\t}\n\n\tpublic ExcelListEntity(List<?> list, Class<?> clazz, int headRows) {\n\t\tthis.list = list;\n\t\tthis.clazz = clazz;\n\t\tthis.headRows = headRows;\n\t}\n\n\tpublic Class<?> getClazz() {\n\t\treturn clazz;\n\t}\n\n\tpublic int getHeadRows() {\n\t\treturn headRows;\n\t}\n\n\tpublic List<?> getList() {\n\t\treturn list;\n\t}\n\n\tpublic void setClazz(Class<?> clazz) {\n\t\tthis.clazz = clazz;\n\t}\n\n\tpublic void setHeadRows(int headRows) {\n\t\tthis.headRows = headRows;\n\t}\n\n\tpublic void setList(List<?> list) {\n\t\tthis.list = list;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/entity/params/ListParamEntity.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.entity.params;\n\n/**\n * Excel 对象导出结构\n * \n * @author JEECG\n * @date 2014年7月26日 下午11:14:48\n */\npublic class ListParamEntity {\n\t// 唯一值,在遍历中重复使用\n\tpublic static final String SINGLE = \"single\";\n\t// 属于数组类型\n\tpublic static final String LIST = \"list\";\n\t/**\n\t * 属性名称\n\t */\n\tprivate String name;\n\t/**\n\t * 目标\n\t */\n\tprivate String target;\n\t/**\n\t * 当是唯一值的时候直接求出值\n\t */\n\tprivate Object value;\n\t/**\n\t * 数据类型,SINGLE || LIST\n\t */\n\tprivate String type;\n\n\tpublic ListParamEntity() {\n\n\t}\n\n\tpublic ListParamEntity(String name, Object value) {\n\t\tthis.name = name;\n\t\tthis.value = value;\n\t\tthis.type = LIST;\n\t}\n\n\tpublic ListParamEntity(String name, String target) {\n\t\tthis.name = name;\n\t\tthis.target = target;\n\t\tthis.type = LIST;\n\t}\n\n\tpublic String getName() {\n\t\treturn name;\n\t}\n\n\tpublic String getTarget() {\n\t\treturn target;\n\t}\n\n\tpublic String getType() {\n\t\treturn type;\n\t}\n\n\tpublic Object getValue() {\n\t\treturn value;\n\t}\n\n\tpublic void setName(String name) {\n\t\tthis.name = name;\n\t}\n\n\tpublic void setTarget(String target) {\n\t\tthis.target = target;\n\t}\n\n\tpublic void setType(String type) {\n\t\tthis.type = type;\n\t}\n\n\tpublic void setValue(Object value) {\n\t\tthis.value = value;\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/parse/ParseWord07.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.parse;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.apache.poi.xwpf.usermodel.XWPFFooter;\nimport org.apache.poi.xwpf.usermodel.XWPFHeader;\nimport org.apache.poi.xwpf.usermodel.XWPFParagraph;\nimport org.apache.poi.xwpf.usermodel.XWPFRun;\nimport org.apache.poi.xwpf.usermodel.XWPFTable;\nimport org.apache.poi.xwpf.usermodel.XWPFTableCell;\nimport org.apache.poi.xwpf.usermodel.XWPFTableRow;\nimport org.jeecgframework.poi.cache.WordCache;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.jeecgframework.poi.word.entity.MyXWPFDocument;\nimport org.jeecgframework.poi.word.entity.WordImageEntity;\nimport org.jeecgframework.poi.word.entity.params.ExcelListEntity;\nimport org.jeecgframework.poi.word.parse.excel.ExcelEntityParse;\nimport org.jeecgframework.poi.word.parse.excel.ExcelMapParse;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 解析07版的Word,替换文字,生成表格,生成图片\n * \n * @author JEECG\n * @date 2013-11-16\n * @version 1.0\n */\n@SuppressWarnings({ \"unchecked\", \"rawtypes\" })\npublic class ParseWord07 {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ParseWord07.class);\n\n\t/**\n\t * 添加图片\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-20\n\t * @param obj\n\t * @param currentRun\n\t * @throws Exception\n\t */\n\tprivate void addAnImage(WordImageEntity obj, XWPFRun currentRun) throws Exception {\n\t\tObject[] isAndType = PoiPublicUtil.getIsAndType(obj);\n\t\tString picId;\n\t\ttry {\n\t\t\tpicId = currentRun.getParagraph().getDocument().addPictureData((byte[]) isAndType[0], (Integer) isAndType[1]);\n\t\t\t((MyXWPFDocument) currentRun.getParagraph().getDocument()).createPicture(currentRun, picId, currentRun.getParagraph().getDocument().getNextPicNameNumber((Integer) isAndType[1]), obj.getWidth(), obj.getHeight());\n\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t}\n\n\t}\n\n\t/**\n\t * 根据条件改变值\n\t * \n\t * @param map\n\t * @Author JEECG\n\t * @date 2013-11-16\n\t */\n\tprivate void changeValues(XWPFParagraph paragraph, XWPFRun currentRun, String currentText, List<Integer> runIndex, Map<String, Object> map) throws Exception {\n\t\tObject obj = PoiPublicUtil.getRealValue(currentText, map);\n\t\tif (obj instanceof WordImageEntity) {// 如果是图片就设置为图片\n\t\t\tcurrentRun.setText(\"\", 0);\n\t\t\taddAnImage((WordImageEntity) obj, currentRun);\n\t\t} else {\n\t\t\tcurrentText = obj.toString();\n\t\t\tcurrentRun.setText(currentText, 0);\n\t\t}\n\t\tfor (int k = 0; k < runIndex.size(); k++) {\n\t\t\tparagraph.getRuns().get(runIndex.get(k)).setText(\"\", 0);\n\t\t}\n\t\trunIndex.clear();\n\t}\n\n\t/**\n\t * 判断是不是迭代输出\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-18\n\t * @return\n\t * @throws Exception\n\t */\n\tprivate Object checkThisTableIsNeedIterator(XWPFTableCell cell, Map<String, Object> map) throws Exception {\n\t\tString text = cell.getText().trim();\n        //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\t// 判断是不是迭代输出\n\t\tif (text != null&&text.startsWith(\"{{\") && text.indexOf(\"$fe:\") != -1) {\n\t\t\treturn PoiPublicUtil.getRealValue(text.replace(\"$fe:\", \"\").trim(), map);\n\t\t}\n        //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\treturn null;\n\t}\n\n\t/**\n\t * 解析所有的文本\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-17\n\t * @param paragraphs\n\t * @param map\n\t */\n\tprivate void parseAllParagraphic(List<XWPFParagraph> paragraphs, Map<String, Object> map) throws Exception {\n\t\tXWPFParagraph paragraph;\n\t\tfor (int i = 0; i < paragraphs.size(); i++) {\n\t\t\tparagraph = paragraphs.get(i);\n\t\t\tif (paragraph.getText().indexOf(\"{{\") != -1) {\n\t\t\t\tparseThisParagraph(paragraph, map);\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * 解析这个段落\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-16\n\t * @param paragraph\n\t * @param map\n\t */\n\tprivate void parseThisParagraph(XWPFParagraph paragraph, Map<String, Object> map) throws Exception {\n\t\tXWPFRun run;\n\t\tXWPFRun currentRun = null;// 拿到的第一个run,用来set值,可以保存格式\n\t\tString currentText = \"\";// 存放当前的text\n\t\tString text;\n\t\tBoolean isfinde = false;// 判断是不是已经遇到{{\n\t\tList<Integer> runIndex = new ArrayList<Integer>();// 存储遇到的run,把他们置空\n\t\tfor (int i = 0; i < paragraph.getRuns().size(); i++) {\n\t\t\trun = paragraph.getRuns().get(i);\n\t\t\ttext = run.getText(0);\n\t\t\tif (StringUtils.isEmpty(text)) {\n\t\t\t\tcontinue;\n\t\t\t}// 如果为空或者\"\"这种这继续循环跳过\n\t\t\tif (isfinde) {\n\t\t\t\tcurrentText += text;\n\t\t\t\tif (currentText.indexOf(\"{{\") == -1) {\n\t\t\t\t\tisfinde = false;\n\t\t\t\t\trunIndex.clear();\n\t\t\t\t} else {\n\t\t\t\t\trunIndex.add(i);\n\t\t\t\t}\n\t\t\t\tif (currentText.indexOf(\"}}\") != -1) {\n\t\t\t\t\tchangeValues(paragraph, currentRun, currentText, runIndex, map);\n\t\t\t\t\tcurrentText = \"\";\n\t\t\t\t\tisfinde = false;\n\t\t\t\t}\n\t\t\t} else if (text.indexOf(\"{\") >= 0) {// 判断是不是开始\n\t\t\t\tcurrentText = text;\n\t\t\t\tisfinde = true;\n\t\t\t\tcurrentRun = run;\n\t\t\t} else {\n\t\t\t\tcurrentText = \"\";\n\t\t\t}\n\t\t\tif (currentText.indexOf(\"}}\") != -1) {\n\t\t\t\tchangeValues(paragraph, currentRun, currentText, runIndex, map);\n\t\t\t\tisfinde = false;\n\t\t\t}\n\t\t}\n\n\t}\n\n\tprivate void parseThisRow(List<XWPFTableCell> cells, Map<String, Object> map) throws Exception {\n\t\tfor (XWPFTableCell cell : cells) {\n\t\t\tparseAllParagraphic(cell.getParagraphs(), map);\n\t\t}\n\t}\n\n\t/**\n\t * 解析这个表格\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-17\n\t * @param table\n\t * @param map\n\t */\n\tprivate void parseThisTable(XWPFTable table, Map<String, Object> map) throws Exception {\n\t\tXWPFTableRow row;\n\t\tList<XWPFTableCell> cells;\n\t\tObject listobj;\n\t\tfor (int i = 0; i < table.getNumberOfRows(); i++) {\n\t\t\trow = table.getRow(i);\n\t\t\tcells = row.getTableCells();\n\t\t\t//begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\t\tlistobj = checkThisTableIsNeedIterator(cells.get(0), map);\n\t\t\tif (listobj == null) {\n\t\t\t\tparseThisRow(cells, map);\n\t\t\t} else if (listobj instanceof ExcelListEntity) {\n\t\t\t\tnew ExcelEntityParse().parseNextRowAndAddRow(table, i, (ExcelListEntity) listobj);\n\t\t\t\ti = i + ((ExcelListEntity) listobj).getList().size() - 1;//删除之后要往上挪一行,然后加上跳过新建的行数\n\t\t\t} else {\n\t\t\t\tExcelMapParse.parseNextRowAndAddRow(table, i, (List) listobj);\n\t\t\t\ti = i + ((List) listobj).size() - 1;//删除之后要往上挪一行,然后加上跳过新建的行数\n\t\t\t}\n\t\t\t/*if (cells.size() == 1) {\n\t\t\t\tlistobj = checkThisTableIsNeedIterator(cells.get(0), map);\n\t\t\t\tif (listobj == null) {\n\t\t\t\t\tparseThisRow(cells, map);\n\t\t\t\t} else if (listobj instanceof ExcelListEntity) {\n\t\t\t\t\ttable.removeRow(i);// 删除这一行\n\t\t\t\t\texcelEntityParse.parseNextRowAndAddRow(table, i, (ExcelListEntity) listobj);\n\t\t\t\t} else {\n\t\t\t\t\ttable.removeRow(i);// 删除这一行\n\t\t\t\t\tExcelMapParse.parseNextRowAndAddRow(table, i, (List) listobj);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tparseThisRow(cells, map);\n\t\t\t}*/\n\t\t\t//end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\t}\n\t}\n\n\t/**\n\t * 解析07版的Word并且进行赋值\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-16\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic XWPFDocument parseWord(String url, Map<String, Object> map) throws Exception {\n\t\tMyXWPFDocument doc = WordCache.getXWPFDocumen(url);\n\t\tparseWordSetValue(doc, map);\n\t\treturn doc;\n\t}\n\n\t/**\n\t * 解析07版的Word并且进行赋值\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-16\n\t * @return\n\t * @throws Exception\n\t */\n\tpublic void parseWord(XWPFDocument document, Map<String, Object> map) throws Exception {\n\t\tparseWordSetValue((MyXWPFDocument) document, map);\n\t}\n\n\tprivate void parseWordSetValue(MyXWPFDocument doc, Map<String, Object> map) throws Exception {\n\t\t// 第一步解析文档\n\t\tparseAllParagraphic(doc.getParagraphs(), map);\n\t\t// 第二步解析页眉,页脚\n\t\tparseHeaderAndFoot(doc, map);\n\t\t// 第三步解析所有表格\n\t\tXWPFTable table;\n\t\tIterator<XWPFTable> itTable = doc.getTablesIterator();\n\t\twhile (itTable.hasNext()) {\n\t\t\ttable = itTable.next();\n\t\t\tif (table.getText().indexOf(\"{{\") != -1) {\n\t\t\t\tparseThisTable(table, map);\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * 解析页眉和页脚\n\t * \n\t * @param doc\n\t * @param map\n\t * @throws Exception\n\t */\n\tprivate void parseHeaderAndFoot(MyXWPFDocument doc, Map<String, Object> map) throws Exception {\n\t\tList<XWPFHeader> headerList = doc.getHeaderList();\n\t\tfor (XWPFHeader xwpfHeader : headerList) {\n\t\t\tfor (int i = 0; i < xwpfHeader.getListParagraph().size(); i++) {\n\t\t\t\tparseThisParagraph(xwpfHeader.getListParagraph().get(i), map);\n\t\t\t}\n\t\t}\n\t\tList<XWPFFooter> footerList = doc.getFooterList();\n\t\tfor (XWPFFooter xwpfFooter : footerList) {\n\t\t\tfor (int i = 0; i < xwpfFooter.getListParagraph().size(); i++) {\n\t\t\t\tparseThisParagraph(xwpfFooter.getListParagraph().get(i), map);\n\t\t\t}\n\t\t}\n\n\t}\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/parse/excel/ExcelEntityParse.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.parse.excel;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.apache.poi.xwpf.usermodel.XWPFTable;\nimport org.apache.poi.xwpf.usermodel.XWPFTableCell;\nimport org.apache.poi.xwpf.usermodel.XWPFTableRow;\nimport org.jeecgframework.poi.excel.annotation.ExcelTarget;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.excel.export.base.ExportBase;\nimport org.jeecgframework.poi.exception.word.WordExportException;\nimport org.jeecgframework.poi.exception.word.enmus.WordExportEnum;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\nimport org.jeecgframework.poi.word.entity.params.ExcelListEntity;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * 解析实体类对象 复用注解\n * \n * @author JEECG\n * @date 2014年8月9日 下午10:30:57\n */\npublic class ExcelEntityParse extends ExportBase {\n\n\tprivate static final Logger LOGGER = LoggerFactory.getLogger(ExcelEntityParse.class);\n\n\tprivate static void checkExcelParams(ExcelListEntity entity) {\n\t\tif (entity.getList() == null || entity.getClazz() == null) {\n\t\t\tthrow new WordExportException(WordExportEnum.EXCEL_PARAMS_ERROR);\n\t\t}\n\n\t}\n\n\tprivate int createCells(int index, Object t, List<ExcelExportEntity> excelParams, XWPFTable table, short rowHeight) throws Exception {\n\t\tExcelExportEntity entity;\n\t\tXWPFTableRow row = table.createRow();\n\t\trow.setHeight(rowHeight);\n\t\tint maxHeight = 1, cellNum = 0;\n\t\tfor (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tCollection<?> list = (Collection<?>) entity.getMethod().invoke(t, new Object[] {});\n\t\t\t\tint listC = 0;\n\t\t\t\tfor (Object obj : list) {\n\t\t\t\t\tcreateListCells(index + listC, cellNum, obj, entity.getList(), table);\n\t\t\t\t\tlistC++;\n\t\t\t\t}\n\t\t\t\tcellNum += entity.getList().size();\n\t\t\t\tif (list != null && list.size() > maxHeight) {\n\t\t\t\t\tmaxHeight = list.size();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tObject value = getCellValue(entity, t);\n\t\t\t\tif (entity.getType() == 1) {\n\t\t\t\t\tsetCellValue(row, value, cellNum++);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// 合并需要合并的单元格\n\t\tcellNum = 0;\n\t\tfor (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\tif (entity.getList() != null) {\n\t\t\t\tcellNum += entity.getList().size();\n\t\t\t} else if (entity.isNeedMerge()) {\n\t\t\t\ttable.setCellMargins(index, index + maxHeight - 1, cellNum, cellNum);\n\t\t\t\tcellNum++;\n\t\t\t}\n\t\t}\n\t\treturn maxHeight;\n\t}\n\n\t/**\n\t * 创建List之后的各个Cells\n\t * \n\t * @param styles\n\t */\n\tpublic void createListCells(int index, int cellNum, Object obj, List<ExcelExportEntity> excelParams, XWPFTable table) throws Exception {\n\t\tExcelExportEntity entity;\n\t\tXWPFTableRow row;\n\t\tif (table.getRow(index) == null) {\n\t\t\trow = table.createRow();\n\t\t\trow.setHeight(getRowHeight(excelParams));\n\t\t} else {\n\t\t\trow = table.getRow(index);\n\t\t}\n\t\tfor (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) {\n\t\t\tentity = excelParams.get(k);\n\t\t\tObject value = getCellValue(entity, obj);\n\t\t\tif (entity.getType() == 1) {\n\t\t\t\tsetCellValue(row, value, cellNum++);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * 获取表头数据\n\t * \n\t * @param table\n\t * @param index\n\t * @return\n\t */\n\tprivate Map<String, Integer> getTitleMap(XWPFTable table, int index, int headRows) {\n\t\tif (index < headRows) {\n\t\t\tthrow new WordExportException(WordExportEnum.EXCEL_NO_HEAD);\n\t\t}\n\t\tMap<String, Integer> map = new HashMap<String, Integer>();\n\t\tString text;\n\t\tfor (int j = 0; j < headRows; j++) {\n\t\t\tList<XWPFTableCell> cells = table.getRow(index - j - 1).getTableCells();\n\t\t\tfor (int i = 0; i < cells.size(); i++) {\n\t\t\t\ttext = cells.get(i).getText();\n\t\t\t\tif (StringUtils.isEmpty(text)) {\n\t\t\t\t\tthrow new WordExportException(WordExportEnum.EXCEL_HEAD_HAVA_NULL);\n\t\t\t\t}\n\t\t\t\tmap.put(text, i);\n\t\t\t}\n\t\t}\n\t\treturn map;\n\t}\n\n\t/**\n\t * 解析上一行并生成更多行\n\t * \n\t * @param table\n\t * @param i\n\t * @param listobj\n\t */\n\tpublic void parseNextRowAndAddRow(XWPFTable table, int index, ExcelListEntity entity) {\n\t\tcheckExcelParams(entity);\n\t\t// 获取表头数据\n\t\tMap<String, Integer> titlemap = getTitleMap(table, index, entity.getHeadRows());\n\t\ttry {\n\t\t\t// 得到所有字段\n\t\t\tField fileds[] = PoiPublicUtil.getClassFields(entity.getClazz());\n\t\t\tExcelTarget etarget = entity.getClazz().getAnnotation(ExcelTarget.class);\n\t\t\tString targetId = null;\n\t\t\tif (etarget != null) {\n\t\t\t\ttargetId = etarget.value();\n\t\t\t}\n\t\t\t// 获取实体对象的导出数据\n\t\t\tList<ExcelExportEntity> excelParams = new ArrayList<ExcelExportEntity>();\n\t\t\tgetAllExcelField(null, targetId, fileds, excelParams, entity.getClazz(), null);\n\t\t\t// 根据表头进行筛选排序\n\t\t\tsortAndFilterExportField(excelParams, titlemap);\n\t\t\tshort rowHeight = getRowHeight(excelParams);\n\t\t\tIterator<?> its = entity.getList().iterator();\n\t\t\twhile (its.hasNext()) {\n\t\t\t\tObject t = its.next();\n\t\t\t\tindex += createCells(index, t, excelParams, table, rowHeight);\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\tLOGGER.error(e.getMessage(), e);\n\t\t}\n\t}\n\n\tprivate void setCellValue(XWPFTableRow row, Object value, int cellNum) {\n\t\tif (row.getCell(cellNum++) != null) {\n\t\t\trow.getCell(cellNum - 1).setText(value == null ? \"\" : value.toString());\n\t\t} else {\n\t\t\trow.createCell().setText(value == null ? \"\" : value.toString());\n\t\t}\n\t}\n\n\t/**\n\t * 对导出序列进行排序和塞选\n\t * \n\t * @param excelParams\n\t * @param titlemap\n\t * @return\n\t */\n\tprivate void sortAndFilterExportField(List<ExcelExportEntity> excelParams, Map<String, Integer> titlemap) {\n\t\tfor (int i = excelParams.size() - 1; i >= 0; i--) {\n\t\t\tif (excelParams.get(i).getList() != null && excelParams.get(i).getList().size() > 0) {\n\t\t\t\tsortAndFilterExportField(excelParams.get(i).getList(), titlemap);\n\t\t\t\tif (excelParams.get(i).getList().size() == 0) {\n\t\t\t\t\texcelParams.remove(i);\n\t\t\t\t} else {\n\t\t\t\t\texcelParams.get(i).setOrderNum(i);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (titlemap.containsKey(excelParams.get(i).getName())) {\n\t\t\t\t\texcelParams.get(i).setOrderNum(i);\n\t\t\t\t} else {\n\t\t\t\t\texcelParams.remove(i);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tsortAllParams(excelParams);\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/main/java/org/jeecgframework/poi/word/parse/excel/ExcelMapParse.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.word.parse.excel;\n\nimport java.util.List;\n\nimport org.apache.poi.xwpf.usermodel.XWPFTable;\nimport org.apache.poi.xwpf.usermodel.XWPFTableCell;\nimport org.apache.poi.xwpf.usermodel.XWPFTableRow;\nimport org.jeecgframework.poi.util.PoiPublicUtil;\n\n/**\n * 处理和生成Map 类型的数据变成表格\n * \n * @author JEECG\n * @date 2014年8月9日 下午10:28:46\n */\npublic final class ExcelMapParse {\n\n\t/**\n\t * 解析参数行,获取参数列表\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-18\n\t * @param currentRow\n\t * @return\n\t */\n\tprivate static String[] parseCurrentRowGetParams(XWPFTableRow currentRow) {\n\t\tList<XWPFTableCell> cells = currentRow.getTableCells();\n\t\tString[] params = new String[cells.size()];\n\t\tString text;\n\t\tfor (int i = 0; i < cells.size(); i++) {\n\t\t\ttext = cells.get(i).getText();\n\t\t\tparams[i] = text == null ? \"\" : text.trim().replace(\"{{\", \"\").replace(\"}}\", \"\");\n\t\t}\n\t\treturn params;\n\t}\n\n\t/**\n\t * 解析下一行,并且生成更多的行\n\t * \n\t * @Author JEECG\n\t * @date 2013-11-18\n\t * @param table\n\t * @param listobj2\n\t */\n\tpublic static void parseNextRowAndAddRow(XWPFTable table, int index, List<Object> list) throws Exception {\n\t\tXWPFTableRow currentRow = table.getRow(index);\n\t\tString[] params = parseCurrentRowGetParams(currentRow);\n\t\ttable.removeRow(index);// 移除这一行\n\t\tint cellIndex = 0;// 创建完成对象一行好像多了一个cell\n\t\tfor (Object obj : list) {\n            //begin-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\t\tcurrentRow = table.insertNewTableRow(index++);\n\t\t\tfor (cellIndex = 0; cellIndex < currentRow.getTableCells().size(); cellIndex++) {\n\t\t\t\tString text = PoiPublicUtil.getValueDoWhile(obj, params[cellIndex].split(\"\\\\.\"), 0).toString();\n\t\t\t\tcurrentRow.getTableCells().get(cellIndex).setText(text);\n\t\t\t}\n            //end-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------//-------author:liusq------date:20210129-----for:-------poi3升级到4兼容改造工作【重要敏感修改点】--------\n\t\t\tfor (; cellIndex < params.length; cellIndex++) {\n\t\t\t\tcurrentRow.createCell().setText(PoiPublicUtil.getValueDoWhile(obj, params[cellIndex].split(\"\\\\.\"), 0).toString());\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "autopoi/src/test/java/DaoChuBigDataTest.java",
    "content": "import org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServer;\nimport vo.TestEntity;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * @Description: 大数据量 Excel 导出测试\n * 测试功能：\n * 1. 测试百万级数据的 Excel 导出性能\n * 2. 测试分批次导出大数据，避免内存溢出\n * 3. 验证 exportBigExcel 方法的实际应用效果\n * \n * 技术要点：\n * - 使用 IExcelExportServer 接口实现分批数据加载\n * - 每批次处理 10万条数据，避免一次性加载导致内存溢出\n * - 适用于数据量超过 10万条的大数据导出场景\n * \n * 参考文档：\n * - http://doc.wupaas.com/docs/easypoi/easypoi-1c10lbsojh62f\n * - https://blog.csdn.net/weixin_45214729/article/details/118552415\n * \n * @author: liusq\n * @date: 2022年1月4日\n */\npublic class DaoChuBigDataTest {\n    // 生成文件的保存路径\n    private static final String generatePath = \"D:/excel/\";\n    \n    /**\n     * 大数据导出测试方法\n     * \n     * 测试内容：\n     * - 模拟生成 100万条测试数据\n     * - 使用分批导出方式，每批处理 10万条数据\n     * - 记录导出耗时，验证性能表现\n     * \n     * 核心逻辑：\n     * 1. 准备100万条测试数据（实际应用中应从数据库分批查询）\n     * 2. 计算总页数（100万 / 10万 = 10页）\n     * 3. 通过 IExcelExportServer 接口实现分批数据返回\n     * 4. 每次返回10万条数据，POI 自动写入 Excel\n     * 5. 当返回 null 时，表示数据处理完成\n     * \n     * 性能优化建议：\n     * - 每批数据量建议控制在 1-10万条之间\n     * - 数据量过大（如30万/批）可能导致内存溢出\n     * - 实际应用中应从数据库分页查询，而不是一次性加载到内存\n     * \n     * @throws Exception 文件操作或数据处理异常\n     */\n    public static void bigDataExport() throws Exception {\n        Workbook workbook = null;\n        List<TestEntity> aList = new ArrayList<TestEntity>();\n        ExportParams exportParams = new ExportParams();\n        Date start = new Date();\n        \n        // 模拟100万条数据（实际应用中应该分批从数据库查询，避免内存溢出）\n        System.out.println(\"开始准备100万条测试数据...\");\n        for(int j=0;j<1000000;j++){\n            TestEntity testEntity = new TestEntity();\n            testEntity.setName(\"李四\"+j);\n            testEntity.setAge(j);\n            aList.add(testEntity);\n        }\n        \n        // 计算分页参数：总页数和每页大小\n        int totalPage = (aList.size() / 100000) + 1;  // 总共10页\n        int pageSize = 100000;  // 每页10万条数据\n        \n        System.out.println(\"数据准备完成，开始分批导出，总页数：\" + totalPage);\n        \n        /**\n         * 使用 exportBigExcel 方法进行大数据导出\n         * \n         * @param exportParams 导出参数配置\n         * @param TestEntity.class 数��实体类\n         * @param IExcelExportServer 数据服务接口，用于分批返回数据\n         * @param totalPage 总页数，传递给 selectListForExcelExport 方法作为终止条件\n         */\n        workbook = ExcelExportUtil.exportBigExcel(exportParams, TestEntity.class, new IExcelExportServer() {\n            /**\n             * 分批数据查询接口实现\n             * \n             * 该方法会被多次调用，每次返回一批数据，直到返回 null 为止\n             * \n             * @param obj 就是上面传入的 totalPage，用于控制循环终止条件\n             * @param page 当前页码（从1开始），每次自动+1\n             * @return 当前批次的数据列表，返回 null 表示数据处理完成\n             */\n            @Override\n            public List<Object> selectListForExcelExport(Object obj, int page) {\n                System.out.println(\"正在处理第 \" + page + \" 页数据...\");\n                \n                // 终止条件：当页码超过总页数时，返回 null\n                if (page > totalPage) {\n                    return null;\n                }\n                \n                // 计算当前批次的数据范围\n                int fromIndex = (page - 1) * pageSize;  // 起始索引\n                int toIndex = page != totalPage ? fromIndex + pageSize : aList.size();  // 结束索引\n                \n                // 返回当前批次的数据（使用 subList 截取）\n                // 重要提示：实际应用中应该从数据库分页查询，而不是从内存中截取\n                List<Object> list = new ArrayList<>();\n                list.addAll(aList.subList(fromIndex, toIndex));\n                return list;\n            }\n        }, totalPage);\n        \n        // 保存文件\n        File savefile = new File(generatePath);\n        if (!savefile.exists()) {\n            savefile.mkdirs();\n        }\n        FileOutputStream fos = new FileOutputStream(\"D:/excel/ExcelExportBigData.bigDataExport.xlsx\");\n        workbook.write(fos);\n        fos.close();\n        \n        // 输出性能统计\n        long timeUsed = new Date().getTime() - start.getTime();\n        System.out.println(\"导出完成！耗时(秒)：\" + (timeUsed / 1000) + \"，文件保存在：D:/excel/ExcelExportBigData.bigDataExport.xlsx\");\n    }\n\n    /**\n     * 主方法：执行大数据导出测试\n     * \n     * 预期结果：\n     * - 在 D:/excel/ 目录下生成 ExcelExportBigData.bigDataExport.xlsx 文件\n     * - 文件包含100万行数据\n     * - 控制台输出各批次处理进度和总耗时\n     * \n     * @param args 命令行参数\n     * @throws Exception 导出过程异常\n     */\n    public static void main(String[] args) throws Exception {\n       bigDataExport();\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/DaoChuSheetTest.java",
    "content": "import org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport vo.TestEntity;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @Description: 多Sheet导出测试\n * 测试功能：\n * 1. 测试一个 Workbook 中创建多个 Sheet 页签\n * 2. 测试使用实体类（TestEntity）方式导出数据到不同 Sheet\n * 3. 测试 XSSF 格式（.xlsx）的多 Sheet 导出\n * \n * 参考文档：http://doc.jeecg.com/2044223\n * \n * @author: scott\n * @date: 2020年09月16日 11:46\n */\npublic class DaoChuSheetTest {\n    // 生成文件的保存路径\n    private static final String generatePath = \"D:/excel/\";\n\n    /**\n     * 获取导出参数配置\n     * @param name 表格名称和Sheet名称\n     * @return ExportParams 配置对象，使用 XSSF 格式\n     */\n    public static ExportParams getExportParams(String name) {\n        return  new ExportParams(name,name,ExcelType.XSSF);\n    }\n    \n    /**\n     * 测试多Sheet导出功能\n     * \n     * 测试内容：\n     * - 创建3个Sheet页签\n     * - 每个Sheet使用相同的实体类（TestEntity）\n     * - 每个Sheet包含10条测试数据\n     * - 验证多Sheet在同一个Excel文件中的导出效果\n     * \n     * @return Workbook 对象，包含3个Sheet页签\n     */\n    public static Workbook test() {\n        /**\n         * 多个Map配置：\n         * - title: 对应表格标题和Sheet名称（ExportParams对象）\n         * - entity: 对应表格数据的实体类（如 TestEntity.class）\n         * - data: 对应实际数据集合（Collection类型）\n         * \n         * 注意：也可以使用 Map 数据替代实体类，示例中 ls2 展示了这种方式\n         */\n        List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();\n        \n        // 创建3个Sheet\n        for(int i=0;i<3;i++){\n            Map<String, Object> map = new HashMap<String, Object>();\n            map.put(\"title\", getExportParams(\"测试\"+i));//表格Title\n            map.put(\"entity\", TestEntity.class);//表格对应实体\n            \n            // 方式1：使用实体类数据\n            List<TestEntity> ls=new ArrayList<TestEntity> ();\n            for(int j=0;j<10;j++){\n                TestEntity testEntity = new TestEntity();\n                testEntity.setName(\"张三\"+i+j);\n                testEntity.setAge(18+i+j);\n                ls.add(testEntity);\n            }\n            \n            // 方式2：使用Map数据（示例代码，未使用）\n            List<Map> ls2=new ArrayList<Map> ();\n            for(int j=0;j<10;j++){\n                Map map1 = new HashMap();\n                map1.put(\"name\",\"李四\"+i+j);\n                map1.put(\"age\",18+i+j);\n                ls2.add(map1);\n            }\n            \n            map.put(\"data\", ls);//可选：ls（实体类数据） or ls2（Map数据）\n            listMap.add(map);\n        }\n        \n        // 导出多个Sheet到同一个Workbook\n        Workbook workbook = ExcelExportUtil.exportExcel(listMap, ExcelType.XSSF);\n        return workbook;\n    }\n\n    /**\n     * 主方法：执行测试并保存Excel文件\n     * \n     * 执行步骤：\n     * 1. 调用test()方法生成包含3个Sheet的Workbook\n     * 2. 检查保存目录是否存在，不存在则创建\n     * 3. 将Workbook写入到文件 testSheet.xlsx\n     * 4. 关闭文件输出流\n     * \n     * 预期结果：\n     * - 在 D:/excel/ 目录下生成 testSheet.xlsx 文件\n     * - 文件包含3个Sheet：测试0、测试1、测试2\n     * - 每个Sheet包含10行数据\n     * \n     * @param args 命令行参数\n     * @throws IOException 文件写入异常\n     */\n    public static void main(String[] args) throws IOException {\n        Workbook workbook = test();\n        File savefile = new File(generatePath);\n        if (!savefile.exists()) {\n            savefile.mkdirs();\n        }\n        FileOutputStream fos = new FileOutputStream(generatePath + \"testSheet.xlsx\");\n        workbook.write(fos);\n        fos.close();\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/DaoChuTest.java",
    "content": "import org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.annotation.Excel;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @Description: Excel 模板导出测试\n * 测试功能：\n * 1. 测试基于 Excel 模板文件的数据导出功能\n * 2. 测试模板中的变量替换和列表数据循环填充\n * 3. 验证模板导出在实际业务场景中的应用\n *\n * 测试场景：\n * - 使用预定义的 Excel 模板（test.xlsx 或 testNextMarge.xlsx）\n * - 将 Map 数据填充到模板的占位符位置\n * - 支持单个变量替换和列表数据的循环生成\n *\n * @author: scott\n * @date: 2020年09月16日 11:46\n */\npublic class DaoChuTest {\n    // 模板文件路径：项目根目录/autopoi/src/test/resources/templates/\n    private static final String TEMPLATE_PATH = System.getProperty(\"user.dir\") + File.separator + \"autopoi\" + File.separator + \"src\" + File.separator + \"test\" + File.separator + \"resources\" + File.separator + \"templates\" + File.separator;\n\n    /**\n     * 获取模板导出参数配置\n     *\n     * @param name 模板文件名（不含扩展名），如 \"test\" 或 \"testNextMarge\"\n     * @return TemplateExportParams 模板导出参数对象\n     */\n    public static TemplateExportParams getTemplateParams(String name) {\n        return new TemplateExportParams(TEMPLATE_PATH + name + \".xlsx\");\n    }\n\n    /**\n     * 测试模板导出功能\n     *\n     * 测试内容：\n     * - 加载指定名称的 Excel 模板文件\n     * - 构造测试数据（包含3条记录的列表）\n     * - 将数据填充到模板的 autoList 循环区域\n     * - 生成包含实际数据的 Excel 文件\n     *\n     * 数据结构：\n     * - autoList: 列表数据，会在模板中循环生成多行\n     *   - name: 姓名字段\n     *   - isTts: 序号字段\n     *   - sname: 简称字段\n     *   - ttsContent: 内容字段\n     *   - rate: 费率字段\n     *\n     * @param name 模板文件名\n     * @return Workbook 填充数据后的工作簿对象\n     */\n    public static Workbook test(String name) {\n        TemplateExportParams params = getTemplateParams(name);\n        Map<String, Object> map = new HashMap<String, Object>();\n\n        // 构造列表数据（3条记录）\n        List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();\n        for (int i = 0; i < 3; i++) {\n            Map<String, Object> lm = new HashMap<String, Object>();\n            lm.put(\"name\", \"姓名1\" + i);\n            lm.put(\"isTts\", i);\n            lm.put(\"sname\", \"s姓名\");\n            lm.put(\"ttsContent\", \"ttsContent内容\");\n            lm.put(\"rate\", 1000 + i);\n            listMap.add(lm);\n        }\n\n        // 将列表数据放入 map，对应模板中的 {{$fe: autoList}} 循环标记\n        map.put(\"autoList\", listMap);\n\n        // 基于模板和数据生成 Excel\n        Workbook workbook = ExcelExportUtil.exportExcel(params, map);\n        return workbook;\n    }\n\n    /**\n     * 测试 Map 数据导出功能（使用 ExcelExportEntity）\n     *\n     * 测试内容：\n     * - 手动构造 ExcelExportEntity 定义 Excel 列结构\n     * - 使用 Map 方式填充数据（适用于动态列场景）\n     * - 设置 sheetName 和 ExcelType.XSSF 格式\n     * - 验证基于 ExportParams + ExcelExportEntity + Map 的导出方式\n     *\n     * 数据结构：\n     * - ExcelExportEntity：定义列名、字段名、列宽\n     * - Map数据：key 对应 ExcelExportEntity 的字段名\n     *\n     * @return Workbook 填充数据后的工作簿对象\n     */\n    public static Workbook testMapDataExport() {\n        // 定义 Excel 列结构\n        List<ExcelExportEntity> entityList = new ArrayList<>();\n        entityList.add(new ExcelExportEntity(\"姓名\", \"name\", 15));\n        entityList.add(new ExcelExportEntity(\"年龄\", \"age\", 10));\n        entityList.add(new ExcelExportEntity(\"部门\", \"dept\", 20));\n        entityList.add(new ExcelExportEntity(\"薪资\", \"salary\", 15));\n\n        // 构造 Map 数据列表\n        List<Map<String, Object>> result = new ArrayList<>();\n        for (int i = 0; i < 20; i++) {\n            Map<String, Object> map = new HashMap<>();\n            map.put(\"name\", \"员工\" + i);\n            map.put(\"age\", 25 + i);\n            map.put(\"dept\", i % 3 == 0 ? \"研发部\" : i % 3 == 1 ? \"市场部\" : \"行政部\");\n            map.put(\"salary\", 8000 + i * 500);\n            result.add(map);\n        }\n\n        // 设置导出参数\n        String sheetName = \"员工信息表\";\n        ExportParams exportParams = new ExportParams(null, sheetName);\n        exportParams.setType(ExcelType.XSSF);\n\n        // 导出 Excel\n        Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, result);\n        return workbook;\n    }\n\n    /**\n     * 主方法：执行模板导出测试并保存文件\n     *\n     * 执行步骤：\n     * 1. 选择模板文件（test 或 testNextMarge）\n     * 2. 调用 test() 方法基于模板生成 Workbook\n     * 3. 检查保存目录是否存在\n     * 4. 将生成的 Workbook 保存到 D:/excel/testNew.xlsx\n     *\n     * 预期结果：\n     * - 在 D:/excel/ 目录下生成 testNew.xlsx 文件\n     * - 文件内容基于模板结构，包含3行数据记录\n     * - 模板中的占位符被实际数据替换\n     *\n     * @param args 命令行参数\n     * @throws IOException 文件操作异常\n     */\n    public static void main(String[] args) throws IOException {\n        String temName = \"test\";  // 简单模板\n        String temNameNextM = \"testNextMarge\";  // 带合并单元格的复杂模板\n\n        // 测试1：使用复杂模板进行测试\n        Workbook workbook = test(temNameNextM);\n\n        File savefile = new File(TEMPLATE_PATH);\n        if (!savefile.exists()) {\n            savefile.mkdirs();\n        }\n\n        FileOutputStream fos = new FileOutputStream(\"D:/excel/testNew.xlsx\");\n        workbook.write(fos);\n        fos.close();\n\n        // 测试2：Map 数据导出测试\n        Workbook workbook2 = testMapDataExport();\n        FileOutputStream fos2 = new FileOutputStream(\"D:/excel/testMapDataExport.xlsx\");\n        workbook2.write(fos2);\n        fos2.close();\n\n        // 测试3：动态列导出示例\n        dynamicExport();\n        \n        System.out.println(\"✅ 所有测试完成，文件已保存到 D:/excel/ 目录\");\n    }\n\n    /**\n     * 动态列导出示例：@Excel(dynamic=true)\n     */\n    public static void dynamicExport() throws IOException {\n        List<DynamicRow> data = new ArrayList<DynamicRow>();\n        // 示例数据：每行的假期列表都包含 name/value 字段\n        DynamicRow row = new DynamicRow();\n        row.setUser(\"张三\");\n        row.setHolidaysJson(\"[{\\\"name\\\":\\\"年假\\\",\\\"value\\\":\\\"10天\\\"},{\\\"name\\\":\\\"事假\\\",\\\"value\\\":\\\"31小时\\\"}]\");\n        data.add(row);\n        // 示例数据：每行的假期列表都包含 name/value 字段\n        DynamicRow row1 = new DynamicRow();\n        row1.setUser(\"李四\");\n        row1.setHolidaysJson(\"[{\\\"name\\\":\\\"年假\\\",\\\"value\\\":\\\"20天\\\"},{\\\"name\\\":\\\"事假\\\",\\\"value\\\":\\\"41小时\\\"}]\");\n        data.add(row1);\n\n        ExportParams params = new ExportParams(\"动态列示例\", \"sheet1\");\n        Workbook workbook = ExcelExportUtil.exportExcel(params, DynamicRow.class, data);\n        File savefile = new File(\"D:\\\\excel\");\n        if (!savefile.exists()) {\n            savefile.mkdirs();\n        }\n        FileOutputStream fos = new FileOutputStream( \"D:\\\\excel\\\\dynamic.xlsx\");\n        workbook.write(fos);\n        fos.close();\n    }\n\n    /**\n     * 演示动态列的实体\n     */\n    public static class DynamicRow {\n        @Excel(name = \"姓名\")\n        private String user;\n\n        @Excel(name = \"假期json\", dynamic = true, dynamicField = \"name\", dynamicVal = \"value\", dynamicKeepSelf = false)\n        private String holidaysJson;\n\n        public String getUser() {\n            return user;\n        }\n        public void setUser(String user) {\n            this.user = user;\n        }\n        public String getHolidaysJson() {\n            return holidaysJson;\n        }\n\n        public void setHolidaysJson(String holidaysJson) {\n            this.holidaysJson = holidaysJson;\n        }\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/DaoChuWordTest.java",
    "content": "import org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.jeecgframework.poi.word.WordExportUtil;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.text.SimpleDateFormat;\nimport java.util.*;\n\n/**\n * @Description: Word 文档导出测试\n * 测试功能：\n * 1. 测试基于 Word 模板的数据导出功能\n * 2. 测试 Word 文档中的变量替换和列表数据填充\n * 3. 验证 .docx 格式文档的模板导出效果\n *\n * 测试场景：\n * - 使用预定义的 Word 模板（纳税信息.docx）\n * - 将 Map 数据填充到模板的占位符位置\n * - 支持单个变量替换和列表数据循环生成\n *\n * 应用场景：\n * - 生成标准化的合同文档\n * - 批量生成报表文档\n * - 根据数据自动生成通知、证明等文档\n *\n * @author: scott\n * @date: 2020年09月16日 11:46\n */\npublic class DaoChuWordTest {\n    // 模板文件路径：项目根目录/autopoi/src/test/resources/templates/\n    private static final String TEMPLATE_PATH = System.getProperty(\"user.dir\") + File.separator + \"autopoi\" + File.separator + \"src\" + File.separator + \"test\" + File.separator + \"resources\" + File.separator + \"templates\" + File.separator;\n\n    /**\n     * 主方法：执行 Word 文档导出测试\n     *\n     * 测试内容：\n     * - 准备纳税信息数据（单条记录和列表数据）\n     * - 使用 Word 模板填充数据\n     * - 生成新的 Word 文档\n     *\n     * 数据结构说明：\n     * - taxlist: 纳税列表数据（可循环生成多行，当前为空）\n     * - totalpreyear: 去年总额\n     * - totalthisyear: 今年总额\n     * - type: 税种类型\n     * - presum: 上期金额\n     * - thissum: 本期金额\n     * - curmonth: 当前月份\n     * - now: 当前时间\n     *\n     * 模板语法：\n     * - 单个变量：{{variableName}}\n     * - 列表循环：{{$fe: listName t.field}}\n     *\n     * 预期结果：\n     * - 在 D:/excel/ 目录下生成 纳税信息new.docx 文件\n     * - 文档中的占位符被实际数据替换\n     * - 如果有列表数据，会在文档中循环生成多行\n     *\n     * @param args 命令行参数\n     * @throws Exception 文件操作异常\n     */\n    public static void main(String[] args) throws Exception {\n        // 格式化当前时间\n        SimpleDateFormat format = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        String curTime = format.format(new Date());\n\n        // 准备数据 Map\n        Map<String, Object> map = new HashMap<String, Object>();\n\n        // 准备列表数据（示例中被注释，可用于循环生成表格行）\n        List<Map<String, Object>> mapList = new ArrayList<Map<String, Object>>();\n        // 示例：添加多条纳税记录到列表\n        // Map<String, Object> map1 = new HashMap<String, Object>();\n        // map1.put(\"type\", \"个人所得税\");\n        // map1.put(\"presum\", \"1580\");\n        // map1.put(\"thissum\", \"1750\");\n        // map1.put(\"curmonth\", \"1-11月\");\n        // map1.put(\"now\", curTime);\n        // mapList.add(map1);\n\n        map.put(\"taxlist\", mapList);  // 列表数据（当前为空）\n        map.put(\"totalpreyear\", \"2660\");  // 去年总额\n        map.put(\"totalthisyear\", \"3400\");  // 今年总额\n\n        // 单条记录数据\n        map.put(\"type\", \"增值税\");\n        map.put(\"presum\", \"1080\");\n        map.put(\"thissum\", \"1650\");\n        map.put(\"curmonth\", \"1-11月\");\n        map.put(\"now\", curTime);\n\n        // 基于模板导出 Word 文档（.docx 格式）\n        XWPFDocument document = WordExportUtil.exportWord07(TEMPLATE_PATH + \"纳税信息.docx\", map);\n\n        // 创建保存目录\n        File savefile = new File(\"D:\\\\excel\");\n        if (!savefile.exists()) {\n            savefile.mkdirs();\n        }\n\n        // 保存生成的 Word 文档\n        FileOutputStream fos = new FileOutputStream(\"D:\\\\excel\\\\纳税信息new.docx\");\n        document.write(fos);\n        fos.close();\n\n        System.out.println(\"Word 文档导出完成！文件保存在：D:\\\\excel\\\\纳税信息new.docx\");\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/ExcelToHtmlTest.java",
    "content": "import org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelToHtmlUtil;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileWriter;\n\n/**\n * @Description: Excel 转 HTML 测试\n * 测试功能：\n * 1. 测试将 Excel 文件转换为 HTML 表格格式\n * 2. 验证 Excel 样式（字体、颜色、边框等）在 HTML 中的呈现\n * 3. 测试复杂表格结构（合并单元格、多行表头）的转换效果\n *\n * 应用场景：\n * - 在网页中预览 Excel 内容\n * - 将 Excel 报表转换为 HTML 格式发送邮件\n * - 在线展示 Excel 数据，无需下载文件\n *\n * @author: autopoi\n */\npublic class ExcelToHtmlTest {\n\n    // 模板文件路径：项目根目录/autopoi/src/test/resources/templates/\n    private static final String TEMPLATE_PATH = System.getProperty(\"user.dir\") + File.separator + \"autopoi\" + File.separator + \"src\" + File.separator + \"test\" + File.separator + \"resources\" + File.separator + \"templates\" + File.separator;\n\n    /**\n     * 主方法：执行 Excel 转 HTML 测试\n     *\n     * 测试内容：\n     * - 读取 .xls 格式的 Excel 文件（专项支出用款申请书.xls）\n     * - 将 Excel 内容转换为 HTML 表格格式\n     * - 保留原 Excel 的样式和布局\n     * - 将生成的 HTML 保存到文件\n     *\n     * 转换特性：\n     * - 自动处理合并单元格\n     * - 保留字体样式（大小、颜色、粗体等）\n     * - 保留单元格边框和背景色\n     * - 保持表格布局和对齐方式\n     *\n     * 预期结果：\n     * - 在 D:/excel/ 目录下生成 专项支出用款申请书.html 文件\n     * - HTML 文件可在浏览器中打开查看\n     * - 表格样式与原 Excel 文件基本一致\n     *\n     * @param args 命令行参数\n     * @throws Exception 文件读取或转换异常\n     */\n    public static void main(String[] args) throws Exception {\n        // 读取 Excel 文件\n        File file = new File(TEMPLATE_PATH + \"专项支出用款申请书.xls\");\n        Workbook wb = new HSSFWorkbook(new FileInputStream(file));\n\n        // 将 Excel 转换为 HTML 表格\n        String html = ExcelToHtmlUtil.toTableHtml(wb);\n\n        // 保存 HTML 文件\n        FileWriter fw = new FileWriter(\"D:/excel/专项支出用款申请书.html\");\n        fw.write(html);\n        fw.close();\n\n        System.out.println(\"Excel 转 HTML 完成！文件保存在：D:/excel/专项支出用款申请书.html\");\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/ImportExcelTest.java",
    "content": "import org.apache.commons.lang3.builder.ReflectionToStringBuilder;\nimport org.jeecgframework.poi.excel.ExcelImportUtil;\nimport org.jeecgframework.poi.excel.entity.ImportParams;\nimport vo.TestDateEntity;\n\nimport java.io.File;\nimport java.util.List;\n\n/**\n * @Description: Excel 数据导入测试\n * 测试功能：\n * 1. 测试从 Excel 文件中读取数据并转换为 Java 对象\n * 2. 测试日期类型数据的导入和解析\n * 3. 验证导入参数配置（标题行、表头行）的正确性\n *\n * 测试场景：\n * - 读取 ExcelImportDateTest.xlsx 文件\n * - 将 Excel 数据映射到 TestDateEntity 实体类\n * - 支持自动类型转换和日期格式解析\n *\n * @author: scott\n * @date: 2020年09月16日 11:46\n */\npublic class ImportExcelTest {\n    // 模板文件路径：项目根目录/autopoi/src/test/resources/templates/\n    private static final String TEMPLATE_PATH = System.getProperty(\"user.dir\") + File.separator + \"autopoi\" + File.separator + \"src\" + File.separator + \"test\" + File.separator + \"resources\" + File.separator + \"templates\" + File.separator;\n\n    /**\n     * 主方法：执行 Excel 导入测试\n     *\n     * 测试内容：\n     * - 配置导入参数（标题行数、表头行数）\n     * - 读取 Excel 文件并解析为实体对象列表\n     * - 验证导入的数据数量和内容正确性\n     *\n     * 导入参数说明：\n     * - TitleRows(1): 标题行占1行（通常是表格大标题）\n     * - HeadRows(1): 表头行占1行（字段名称行）\n     * - 实际数据从第3行开始读取\n     *\n     * 预期结果：\n     * - 成功读取 Excel 文件中的所有数据行\n     * - 数据正确映射到 TestDateEntity 对象\n     * - 日期字段正确解析为 Date 类型\n     * - 控制台输出数据总数和第2条数据的详细信息\n     *\n     * @param args 命令行参数\n     * @throws Exception 文件读取或数据解析异常\n     */\n    public static void main(String[] args) throws Exception {\n        // 配置导入参数\n        ImportParams params = new ImportParams();\n        params.setTitleRows(1);  // 标题行数：1行\n        params.setHeadRows(1);   // 表头行数：1行\n\n        // 指定要导入的 Excel 文件\n        File importFile = new File(TEMPLATE_PATH + \"ExcelImportDateTest.xlsx\");\n\n        // 执行导入：将 Excel 数据转换为 TestDateEntity 对象列表\n        List<TestDateEntity> list = ExcelImportUtil.importExcel(importFile, TestDateEntity.class, params);\n\n        // 输出导入结果\n        System.out.println(\"导入数据总数：\" + list.size());\n\n        // 输出第2条数据的详细信息（索引为1）\n        if (list.size() > 1) {\n            System.out.println(\"第2条数据详情：\");\n            System.out.println(ReflectionToStringBuilder.toString(list.get(1)));\n        }\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/Poi541ApiCompatibilityTest.java",
    "content": "import org.apache.poi.ss.usermodel.*;\nimport org.apache.poi.xssf.usermodel.XSSFWorkbook;\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Date;\n\n/**\n * POI 5.4.1 API 兼容性测试\n * 测试从 4.1.2 升级到 5.4.1 后的 API 变化\n */\npublic class Poi541ApiCompatibilityTest {\n\n    /**\n     * 测试1: getCellType() 方法（替代 getCellTypeEnum()）\n     */\n    @Test\n    public void testGetCellType() {\n        System.out.println(\"=== 测试 getCellType() 方法 ===\");\n        \n        Workbook workbook = new XSSFWorkbook();\n        Sheet sheet = workbook.createSheet(\"测试\");\n        Row row = sheet.createRow(0);\n        \n        // 测试字符串类型\n        Cell cell1 = row.createCell(0);\n        cell1.setCellValue(\"测试文本\");\n        Assert.assertEquals(\"字符串类型判断\", CellType.STRING, cell1.getCellType());\n        System.out.println(\"  ✅ STRING 类型: \" + cell1.getCellType());\n        \n        // 测试数字类型\n        Cell cell2 = row.createCell(1);\n        cell2.setCellValue(123.45);\n        Assert.assertEquals(\"数字类型判断\", CellType.NUMERIC, cell2.getCellType());\n        System.out.println(\"  ✅ NUMERIC 类型: \" + cell2.getCellType());\n        \n        // 测试布尔类型\n        Cell cell3 = row.createCell(2);\n        cell3.setCellValue(true);\n        Assert.assertEquals(\"布尔类型判断\", CellType.BOOLEAN, cell3.getCellType());\n        System.out.println(\"  ✅ BOOLEAN 类型: \" + cell3.getCellType());\n        \n        // 测试公式类型\n        Cell cell4 = row.createCell(3);\n        cell4.setCellFormula(\"SUM(A1:B1)\");\n        Assert.assertEquals(\"公式类型判断\", CellType.FORMULA, cell4.getCellType());\n        System.out.println(\"  ✅ FORMULA 类型: \" + cell4.getCellType());\n        \n        System.out.println(\"✅ getCellType() 方法测试通过\\n\");\n    }\n\n    /**\n     * 测试2: DateUtil 类（替代 HSSFDateUtil）\n     */\n    @Test\n    public void testDateUtil() {\n        System.out.println(\"=== 测试 DateUtil 类 ===\");\n        \n        Workbook workbook = new XSSFWorkbook();\n        Sheet sheet = workbook.createSheet(\"日期测试\");\n        Row row = sheet.createRow(0);\n        \n        // 创建日期单元格\n        Cell cell = row.createCell(0);\n        Date now = new Date();\n        cell.setCellValue(now);\n        \n        // 使用 DateUtil 判断是否为日期格式\n        CellStyle cellStyle = workbook.createCellStyle();\n        cellStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat(\"yyyy-MM-dd\"));\n        cell.setCellStyle(cellStyle);\n        \n        // 验证 DateUtil.isCellDateFormatted 方法\n        boolean isDate = DateUtil.isCellDateFormatted(cell);\n        System.out.println(\"  ✅ DateUtil.isCellDateFormatted() 工作正常: \" + isDate);\n        \n        // 测试数字转日期\n        double dateValue = cell.getNumericCellValue();\n        Date convertedDate = DateUtil.getJavaDate(dateValue);\n        Assert.assertNotNull(\"日期转换不应为空\", convertedDate);\n        System.out.println(\"  ✅ DateUtil.getJavaDate() 转换成功: \" + convertedDate);\n        \n        System.out.println(\"✅ DateUtil 类测试通过\\n\");\n    }\n\n    /**\n     * 测试3: getAlignment() 方法（替代 getAlignmentEnum()）\n     */\n    @Test\n    public void testGetAlignment() {\n        System.out.println(\"=== 测试 getAlignment() 方法 ===\");\n        \n        Workbook workbook = new XSSFWorkbook();\n        CellStyle style = workbook.createCellStyle();\n        \n        // 设置不同的对齐方式\n        style.setAlignment(HorizontalAlignment.CENTER);\n        Assert.assertEquals(\"水平居中对齐\", HorizontalAlignment.CENTER, style.getAlignment());\n        System.out.println(\"  ✅ 水平对齐: \" + style.getAlignment());\n        \n        style.setVerticalAlignment(VerticalAlignment.CENTER);\n        Assert.assertEquals(\"垂直居中对齐\", VerticalAlignment.CENTER, style.getVerticalAlignment());\n        System.out.println(\"  ✅ 垂直对齐: \" + style.getVerticalAlignment());\n        \n        System.out.println(\"✅ getAlignment() 方法测试通过\\n\");\n    }\n\n    /**\n     * 测试4: Font 索引类型变化（short -> int）\n     */\n    @Test\n    public void testFontIndexType() {\n        System.out.println(\"=== 测试 Font 索引类型变化 ===\");\n        \n        Workbook workbook = new XSSFWorkbook();\n        \n        // POI 5.x 中 getNumberOfFonts() 返回 int 类型\n        int fontCount = workbook.getNumberOfFonts();\n        System.out.println(\"  ✅ 字体数量 (int 类型): \" + fontCount);\n        \n        // 遍历所有字体（使用 int 而不是 short）\n        for (int i = 0; i < fontCount && i < 3; i++) {\n            Font font = workbook.getFontAt(i);\n            System.out.println(\"  ✅ 字体 \" + i + \": \" + font.getFontName());\n        }\n        \n        System.out.println(\"✅ Font 索引类型测试通过\\n\");\n    }\n\n    /**\n     * 测试5: Cell 值读取兼容性\n     */\n    @Test\n    public void testCellValueCompatibility() {\n        System.out.println(\"=== 测试 Cell 值读取兼容性 ===\");\n        \n        Workbook workbook = new XSSFWorkbook();\n        Sheet sheet = workbook.createSheet(\"值读取测试\");\n        Row row = sheet.createRow(0);\n        \n        // 字符串值\n        Cell cell1 = row.createCell(0);\n        cell1.setCellValue(\"测试\");\n        if (cell1.getCellType() == CellType.STRING) {\n            String value = cell1.getStringCellValue();\n            Assert.assertEquals(\"字符串值读取\", \"测试\", value);\n            System.out.println(\"  ✅ 字符串值: \" + value);\n        }\n        \n        // 数字值\n        Cell cell2 = row.createCell(1);\n        cell2.setCellValue(100.5);\n        if (cell2.getCellType() == CellType.NUMERIC) {\n            double value = cell2.getNumericCellValue();\n            Assert.assertEquals(\"数字值读取\", 100.5, value, 0.001);\n            System.out.println(\"  ✅ 数字值: \" + value);\n        }\n        \n        // 布尔值\n        Cell cell3 = row.createCell(2);\n        cell3.setCellValue(false);\n        if (cell3.getCellType() == CellType.BOOLEAN) {\n            boolean value = cell3.getBooleanCellValue();\n            Assert.assertFalse(\"布尔值读取\", value);\n            System.out.println(\"  ✅ 布尔值: \" + value);\n        }\n        \n        System.out.println(\"✅ Cell 值读取兼容性测试通过\\n\");\n    }\n\n    /**\n     * 测试6: 单元格样式兼容性\n     */\n    @Test\n    public void testCellStyleCompatibility() {\n        System.out.println(\"=== 测试单元格样式兼容性 ===\");\n        \n        Workbook workbook = new XSSFWorkbook();\n        Sheet sheet = workbook.createSheet(\"样式测试\");\n        \n        // 创建样式\n        CellStyle style = workbook.createCellStyle();\n        Font font = workbook.createFont();\n        font.setBold(true);\n        font.setFontHeightInPoints((short) 14);\n        font.setColor(IndexedColors.RED.getIndex());\n        style.setFont(font);\n        \n        // 设置对齐\n        style.setAlignment(HorizontalAlignment.CENTER);\n        style.setVerticalAlignment(VerticalAlignment.CENTER);\n        \n        // 应用样式\n        Row row = sheet.createRow(0);\n        Cell cell = row.createCell(0);\n        cell.setCellValue(\"样式测试\");\n        cell.setCellStyle(style);\n        \n        // 验证样式\n        CellStyle appliedStyle = cell.getCellStyle();\n        Assert.assertEquals(\"水平对齐\", HorizontalAlignment.CENTER, appliedStyle.getAlignment());\n        Assert.assertEquals(\"垂直对齐\", VerticalAlignment.CENTER, appliedStyle.getVerticalAlignment());\n        \n        System.out.println(\"  ✅ 样式创建和应用正常\");\n        System.out.println(\"  ✅ 对齐方式: \" + appliedStyle.getAlignment());\n        System.out.println(\"✅ 单元格样式兼容性测试通过\\n\");\n    }\n\n    /**\n     * 测试7: Workbook 创建和基本操作\n     */\n    @Test\n    public void testWorkbookBasicOperations() {\n        System.out.println(\"=== 测试 Workbook 基本操作 ===\");\n        \n        // 创建 Workbook\n        Workbook workbook = new XSSFWorkbook();\n        Assert.assertNotNull(\"Workbook 创建\", workbook);\n        System.out.println(\"  ✅ Workbook 创建成功\");\n        \n        // 创建 Sheet\n        Sheet sheet1 = workbook.createSheet(\"Sheet1\");\n        Sheet sheet2 = workbook.createSheet(\"Sheet2\");\n        Assert.assertEquals(\"Sheet 数量\", 2, workbook.getNumberOfSheets());\n        System.out.println(\"  ✅ 创建了 \" + workbook.getNumberOfSheets() + \" 个 Sheet\");\n        \n        // 创建行和单元格\n        Row row = sheet1.createRow(0);\n        Cell cell = row.createCell(0);\n        cell.setCellValue(\"POI 5.4.1 测试\");\n        \n        Assert.assertEquals(\"单元格值\", \"POI 5.4.1 测试\", cell.getStringCellValue());\n        System.out.println(\"  ✅ 单元格值设置成功: \" + cell.getStringCellValue());\n        \n        System.out.println(\"✅ Workbook 基本操作测试通过\\n\");\n    }\n\n    /**\n     * 运行所有 API 兼容性测试\n     */\n    @Test\n    public void runAllApiTests() {\n        System.out.println(\"\\n==========================================\");\n        System.out.println(\"  POI 5.4.1 API 兼容性全面测试\");\n        System.out.println(\"==========================================\\n\");\n        \n        long startTime = System.currentTimeMillis();\n        \n        testGetCellType();\n        testDateUtil();\n        testGetAlignment();\n        testFontIndexType();\n        testCellValueCompatibility();\n        testCellStyleCompatibility();\n        testWorkbookBasicOperations();\n        \n        long totalTime = System.currentTimeMillis() - startTime;\n        \n        System.out.println(\"==========================================\");\n        System.out.println(\"✅ 所有 API 兼容性测试通过！\");\n        System.out.println(\"   总耗时: \" + totalTime + \"ms\");\n        System.out.println(\"   POI 5.4.1 升级成功，API 完全兼容\");\n        System.out.println(\"==========================================\\n\");\n    }\n}\n\n"
  },
  {
    "path": "autopoi/src/test/java/Poi541ComprehensiveTest.java",
    "content": "import org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\nimport org.jeecgframework.poi.excel.entity.enmus.ExcelType;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServer;\nimport org.junit.Test;\nimport vo.TestEntity;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.util.*;\n\n/**\n * POI 5.4.1 综合功能测试\n * 测试升级后的各种导出功能是否正常\n */\npublic class Poi541ComprehensiveTest {\n\n    private static final String OUTPUT_DIR = \"D:/excel/poi541test/\";\n\n    /**\n     * 测试1：多表头导出（Map数据 + 手动封装ExcelExportEntity）\n     */\n    @Test\n    public void testMultiHeaderExport() throws Exception {\n        System.out.println(\"=== 测试多表头导出 ===\");\n\n        List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();\n        Map<String, Object> map1 = new HashMap<>();\n        map1.put(\"name\", \"小明\");\n        map1.put(\"age\", 21);\n        map1.put(\"degree\", 36);\n        map1.put(\"link_name\", \"小八\");\n        map1.put(\"link_age\", 33);\n        dataList.add(map1);\n\n        Map<String, Object> map2 = new HashMap<>();\n        map2.put(\"name\", \"小王\");\n        map2.put(\"age\", 24);\n        map2.put(\"degree\", 37);\n        map2.put(\"link_name\", \"小六\");\n        map2.put(\"link_age\", 26);\n        dataList.add(map2);\n\n        List<ExcelExportEntity> entityList = new ArrayList<>();\n        // 一般表头\n        entityList.add(new ExcelExportEntity(\"姓名\", \"name\"));\n        entityList.add(new ExcelExportEntity(\"年龄\", \"age\"));\n        entityList.add(new ExcelExportEntity(\"体温\", \"degree\"));\n        \n        // 多表头方式1：需要先添加子列，再添加父列（Map数据专用）\n        // 子列需要使用三个参数的构造器，第三个参数为 true\n        entityList.add(new ExcelExportEntity(\"姓名\", \"link_name\", true));\n        entityList.add(new ExcelExportEntity(\"年龄\", \"link_age\", true));\n        \n        // 父列也需要设置 colspan=true，并设置 SubColumnList\n        ExcelExportEntity contactEntity = new ExcelExportEntity(\"紧急联系人\", \"linkman\", true);\n        List<String> subKeys = new ArrayList<>();\n        subKeys.add(\"link_name\");\n        subKeys.add(\"link_age\");\n        contactEntity.setSubColumnList(subKeys);\n        entityList.add(contactEntity);\n\n        // 导出 - 使用 XSSF 格式对应 .xlsx 文件\n        Workbook wb = ExcelExportUtil.exportExcel(new ExportParams(\"测试多表头\", \"sheetName\", ExcelType.XSSF), entityList, dataList);\n\n        // 保存文件 - 使用 .xlsx 扩展名\n        saveWorkbook(wb, \"test1_multiheader.xlsx\");\n        System.out.println(\"✅ 多表头导出测试完成\");\n    }\n\n    /**\n     * 测试2：多Sheet导出（实体类方式）\n     */\n    @Test\n    public void testMultiSheetExport() throws Exception {\n        System.out.println(\"=== 测试多Sheet导出 ===\");\n        \n        // 多个map，对应了多个sheet\n        List<Map<String, Object>> listMap = new ArrayList<>();\n\n        for (int i = 0; i < 3; i++) {\n            Map<String, Object> map = new HashMap<>();\n            \n            // 表格title\n            map.put(\"title\", getExportParams(\"测试Sheet\" + (i + 1)));\n            \n            // 表格对应实体\n            map.put(\"entity\", TestEntity.class);\n\n            // 准备数据（实体类方式）\n            List<TestEntity> ls = new ArrayList<>();\n            for (int j = 0; j < 10; j++) {\n                TestEntity testEntity = new TestEntity();\n                testEntity.setName(\"张三\" + j);\n                testEntity.setAge(18 + j);\n                ls.add(testEntity);\n            }\n            map.put(\"data\", ls);\n            listMap.add(map);\n        }\n\n        // 导出\n        Workbook wb = ExcelExportUtil.exportExcel(listMap, ExcelType.HSSF);\n\n        // 保存文件\n        saveWorkbook(wb, \"test2_multisheet.xls\");\n        System.out.println(\"✅ 多Sheet导出测试完成\");\n    }\n\n    /**\n     * 测试3：模板导出（简单模板）\n     */\n    @Test\n    public void testTemplateExport() throws Exception {\n        System.out.println(\"=== 测试模板导出 ===\");\n        \n        try {\n            // 创建简单的测试模板（如果模板文件存在）\n            String templatePath = \"src/test/resources/templates/test.xlsx\";\n            File templateFile = new File(templatePath);\n            \n            if (!templateFile.exists()) {\n                System.out.println(\"⚠️  模板文件不存在，跳过模板导出测试: \" + templatePath);\n                return;\n            }\n\n            TemplateExportParams params = new TemplateExportParams(templatePath);\n            Map<String, Object> map = new HashMap<>();\n            map.put(\"title\", \"员工个人信息\");\n            map.put(\"name\", \"大熊\");\n            map.put(\"age\", 22);\n            map.put(\"company\", \"北京机器猫科技有限公司\");\n            map.put(\"date\", \"2020-07-13\");\n            \n            Workbook workbook = ExcelExportUtil.exportExcel(params, map);\n\n            // 保存文件\n            saveWorkbook(workbook, \"test3_template.xlsx\");\n            System.out.println(\"✅ 模板导出测试完成\");\n        } catch (Exception e) {\n            System.out.println(\"⚠️  模板导出测试失败: \" + e.getMessage());\n        }\n    }\n\n    /**\n     * 测试4：复杂模板导出（带循环）\n     */\n    @Test\n    public void testComplexTemplateExport() throws Exception {\n        System.out.println(\"=== 测试复杂模板导出（带循环）===\");\n        \n        try {\n            String templatePath = \"src/test/resources/templates/testNextMarge.xlsx\";\n            File templateFile = new File(templatePath);\n            \n            if (!templateFile.exists()) {\n                System.out.println(\"⚠️  模板文件不存在，跳过复杂模板导出测试: \" + templatePath);\n                return;\n            }\n\n            TemplateExportParams params = new TemplateExportParams(templatePath);\n            Map<String, Object> map = new HashMap<>();\n            map.put(\"title\", \"员工信息\");\n            \n            List<Map<String, Object>> listMap = new ArrayList<>();\n            for (int i = 0; i < 6; i++) {\n                Map<String, Object> lm = new HashMap<>();\n                lm.put(\"name\", \"王\" + i);\n                lm.put(\"age\", \"2\" + i);\n                lm.put(\"sex\", i % 2 == 0 ? \"1\" : \"2\");\n                lm.put(\"date\", new Date());\n                lm.put(\"salary\", 1000 + i);\n                listMap.add(lm);\n            }\n            map.put(\"maplist\", listMap);\n            \n            Workbook workbook = ExcelExportUtil.exportExcel(params, map);\n\n            // 保存文件\n            saveWorkbook(workbook, \"test4_complex_template.xlsx\");\n            System.out.println(\"✅ 复杂模板导出测试完成\");\n        } catch (Exception e) {\n            System.out.println(\"⚠️  复杂模板导出测试失败: \" + e.getMessage());\n        }\n    }\n\n    /**\n     * 测试5：大数据导出（测试POI 5.4.1性能）\n     */\n    @Test\n    public void testBigDataExport() throws Exception {\n        System.out.println(\"=== 测试大数据导出 ===\");\n        \n        Date start = new Date();\n        \n        // 设置表格标题\n        ExportParams params = new ExportParams(\"POI 5.4.1大数据测试\", \"性能测试\");\n        \n        /**\n         * 导出10万条数据（分10次，每次1万条）\n         * 测试POI 5.4.1的性能\n         */\n        Workbook workbook = ExcelExportUtil.exportBigExcel(\n            params, \n            TestEntity.class, \n            new IExcelExportServer() {\n                @Override\n                public List<Object> selectListForExcelExport(Object obj, int page) {\n                    System.out.println(\"  正在导出第 \" + (page + 1) + \" 批数据...\");\n                    \n                    // page每次加一，当等于obj的值时返回空，代码结束\n                    if (((int) obj) == page) {\n                        return null;\n                    }\n                    \n                    // 每次返回1万条数据\n                    List<Object> list = new ArrayList<>();\n                    for (int i = 0; i < 10000; i++) {\n                        TestEntity client = new TestEntity();\n                        client.setName(\"测试用户\" + (page * 10000 + i));\n                        client.setAge(18 + (i % 50));\n                        list.add(client);\n                    }\n                    return list;\n                }\n            }, \n            10  // 总共导出10批\n        );\n\n        long timeUsed = new Date().getTime() - start.getTime();\n        System.out.println(\"  导出10万条数据耗时: \" + timeUsed + \"ms (\" + (timeUsed / 1000.0) + \"秒)\");\n\n        // 保存文件\n        saveWorkbook(workbook, \"test5_bigdata_100k.xlsx\");\n        System.out.println(\"✅ 大数据导出测试完成\");\n    }\n\n    /**\n     * 测试6：Cell类型API兼容性测试\n     */\n    @Test\n    public void testCellTypeCompatibility() throws Exception {\n        System.out.println(\"=== 测试Cell类型API兼容性 ===\");\n        \n        List<TestEntity> dataList = new ArrayList<>();\n        for (int i = 0; i < 5; i++) {\n            TestEntity entity = new TestEntity();\n            entity.setName(\"用户\" + i);\n            entity.setAge(20 + i);\n            dataList.add(entity);\n        }\n\n        ExportParams params = new ExportParams(\"API兼容性测试\", \"Sheet1\", ExcelType.XSSF);\n        Workbook wb = ExcelExportUtil.exportExcel(params, TestEntity.class, dataList);\n\n        // 保存文件\n        saveWorkbook(wb, \"test6_api_compatibility.xlsx\");\n        System.out.println(\"✅ Cell类型API兼容性测试完成\");\n    }\n\n    /**\n     * 测试7：Map数据导出\n     */\n    @Test\n    public void testMapDataExport() throws Exception {\n        System.out.println(\"=== 测试Map数据导出 ===\");\n        \n        List<ExcelExportEntity> entityList = new ArrayList<>();\n        entityList.add(new ExcelExportEntity(\"姓名\", \"name\", 15));\n        entityList.add(new ExcelExportEntity(\"年龄\", \"age\", 10));\n        entityList.add(new ExcelExportEntity(\"部门\", \"dept\", 20));\n        entityList.add(new ExcelExportEntity(\"薪资\", \"salary\", 15));\n\n        List<Map<String, Object>> dataList = new ArrayList<>();\n        for (int i = 0; i < 20; i++) {\n            Map<String, Object> map = new HashMap<>();\n            map.put(\"name\", \"员工\" + i);\n            map.put(\"age\", 25 + i);\n            map.put(\"dept\", i % 3 == 0 ? \"研发部\" : i % 3 == 1 ? \"市场部\" : \"行政部\");\n            map.put(\"salary\", 8000 + i * 500);\n            dataList.add(map);\n        }\n\n        Workbook wb = ExcelExportUtil.exportExcel(\n            new ExportParams(\"Map数据导出\", \"员工表\", ExcelType.XSSF), \n            entityList, \n            dataList\n        );\n\n        saveWorkbook(wb, \"test7_map_data.xlsx\");\n        System.out.println(\"✅ Map数据导出测试完成\");\n    }\n\n    /**\n     * 运行所有测试\n     */\n    @Test\n    public void runAllTests() throws Exception {\n        System.out.println(\"\\n==========================================\");\n        System.out.println(\"    POI 5.4.1 升级后功能综合测试\");\n        System.out.println(\"==========================================\\n\");\n        \n        long startTime = System.currentTimeMillis();\n        \n        try {\n            testMultiHeaderExport();\n            System.out.println();\n            \n            testMultiSheetExport();\n            System.out.println();\n            \n            testTemplateExport();\n            System.out.println();\n            \n            testComplexTemplateExport();\n            System.out.println();\n            \n            testCellTypeCompatibility();\n            System.out.println();\n            \n            testMapDataExport();\n            System.out.println();\n            \n            // 大数据测试放在最后（比较耗时）\n            testBigDataExport();\n            \n        } catch (Exception e) {\n            System.err.println(\"❌ 测试过程中出现异常: \" + e.getMessage());\n            e.printStackTrace();\n            throw e;\n        }\n        \n        long totalTime = System.currentTimeMillis() - startTime;\n        \n        System.out.println(\"\\n==========================================\");\n        System.out.println(\"✅ 所有测试完成！\");\n        System.out.println(\"   总耗时: \" + totalTime + \"ms (\" + (totalTime / 1000.0) + \"秒)\");\n        System.out.println(\"   输出目录: \" + OUTPUT_DIR);\n        System.out.println(\"==========================================\\n\");\n    }\n\n    /**\n     * 获取导出参数\n     */\n    private static ExportParams getExportParams(String name) {\n        // 表格名称,sheet名称,导出版本\n        return new ExportParams(name, name, ExcelType.HSSF);\n    }\n\n    /**\n     * 保存Workbook到文件\n     */\n    private void saveWorkbook(Workbook workbook, String fileName) throws Exception {\n        File saveDir = new File(OUTPUT_DIR);\n        if (!saveDir.exists()) {\n            saveDir.mkdirs();\n        }\n        \n        String filePath = OUTPUT_DIR + fileName;\n        FileOutputStream fos = null;\n        try {\n            fos = new FileOutputStream(filePath);\n            workbook.write(fos);\n            fos.flush();\n        } finally {\n            // 先关闭输出流\n            if (fos != null) {\n                try {\n                    fos.close();\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n            // 再关闭 Workbook (POI 5.x 需要显式关闭)\n            if (workbook != null) {\n                try {\n                    workbook.close();\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        \n        System.out.println(\"  文件已保存: \" + filePath);\n    }\n}\n\n"
  },
  {
    "path": "autopoi/src/test/java/TestImageScale.java",
    "content": "import org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.consts.ImageScaleMode;\nimport org.jeecgframework.poi.entity.ImageEntity;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 图片缩放功能测试类\n * 测试模板导出中的图片缩放功能\n * for [issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式\n * \n * @author chenrui\n * @date 2025-10-28\n */\npublic class TestImageScale {\n    \n    // 模板文件路径 - 使用相对路径，确保所有人都能使用\n    private static final String TEMPLATE_PATH = \"autopoi/src/test/resources/templates/\";\n\n    // 测试图片路径 - 使用相对路径，图片已放在 resources/templates 下\n    private static final String TEST_IMAGE_PATH = \"autopoi/src/test/resources/templates/dakytot.jpeg\";\n    \n    /**\n     * 获取模板导出参数配置\n     */\n    public static TemplateExportParams getTemplateParams(String name) {\n        return new TemplateExportParams(TEMPLATE_PATH + name + \".xlsx\");\n    }\n    \n    /**\n     * 测试图片缩放功能\n     * 测试3种不同的缩放模式：\n     * - imageLSTC: 拉伸填充 (scaleMode = ImageScaleMode.STRETCH)\n     * - imageDBL: 等比例缩放适应 (scaleMode = ImageScaleMode.FIT) \n     * - imageYT: 不缩放（原始大小） (scaleMode = ImageScaleMode.ORIGINAL)\n     */\n    public static Workbook testImageScaling(String templateName) {\n        try {\n            System.out.println(\"开始测试图片缩放功能，模板: \" + templateName);\n            TemplateExportParams params = getTemplateParams(templateName);\n            System.out.println(\"模板路径: \" + params.getTemplateUrl());\n            \n            Map<String, Object> map = new HashMap<String, Object>();\n\n            // 构造列表数据（3条记录）\n            List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();\n            for (int i = 0; i < 3; i++) {\n                Map<String, Object> lm = new HashMap<String, Object>();\n                lm.put(\"name\", \"用户\" + (i + 1));\n                lm.put(\"id\", i + 1);\n                \n                // 1. 拉伸填充\n                ImageEntity imageLSTC = new ImageEntity();\n                imageLSTC.setHeight(200);\n                imageLSTC.setWidth(300);\n                imageLSTC.setUrl(TEST_IMAGE_PATH);\n                imageLSTC.setScaleModeEnum(ImageScaleMode.STRETCH); // 拉伸填充\n                lm.put(\"imageLSTC\", imageLSTC);\n                \n                // 2. 等比例缩放适应\n                ImageEntity imageDBL = new ImageEntity();\n                imageDBL.setHeight(200);\n                imageDBL.setWidth(300);\n                imageDBL.setUrl(TEST_IMAGE_PATH);\n                imageDBL.setScaleModeEnum(ImageScaleMode.FIT); // 等比例缩放适应\n                lm.put(\"imageDBL\", imageDBL);\n                \n                // 3. 不缩放（原始大小）\n                ImageEntity imageYT = new ImageEntity();\n                imageYT.setHeight(200);\n                imageYT.setWidth(300);\n                imageYT.setUrl(TEST_IMAGE_PATH);\n                imageYT.setScaleModeEnum(ImageScaleMode.ORIGINAL); // 不缩放（原始大小）\n                lm.put(\"imageYT\", imageYT);\n                \n                listMap.add(lm);\n            }\n\n            // 将列表数据放入 map，对应模板中的 {{$fe: autoList}} 循环标记\n            map.put(\"autoList\", listMap);\n            System.out.println(\"数据准备完成，开始导出Excel...\");\n\n            // 基于模板和数据生成 Excel\n            Workbook workbook = ExcelExportUtil.exportExcel(params, map);\n            System.out.println(\"Excel导出完成，workbook: \" + (workbook != null ? \"成功\" : \"失败\"));\n            return workbook;\n        } catch (Exception e) {\n            System.err.println(\"测试图片缩放功能失败: \" + e.getMessage());\n            e.printStackTrace();\n            return null;\n        }\n    }\n    \n    /**\n     * 主方法：执行图片缩放测试\n     */\n    public static void main(String[] args) throws IOException {\n        System.out.println(\"=== 图片缩放功能测试开始 ===\");\n        \n        String templateName = \"testImageScale\";  // 使用test模板\n\n        String outputDir = \"/Users/chenrui/Downloads\";\n\n        try {\n            // 测试1：本地图片缩放\n            System.out.println(\"测试1：本地图片缩放功能\");\n            Workbook workbook1 = testImageScaling(templateName);\n\n            File saveDir = new File(outputDir);\n            if (!saveDir.exists()) {\n                saveDir.mkdirs();\n            }\n\n            FileOutputStream fos1 = new FileOutputStream(outputDir + \"/ImageScaleTest_Local.xlsx\");\n            workbook1.write(fos1);\n            fos1.close();\n            workbook1.close();\n\n            System.out.println(\"=== 图片缩放功能测试完成 ===\");\n            System.out.println(\"测试说明：\");\n            System.out.println(\"- imageLSTC: 拉伸填充 (scaleMode = ImageScaleMode.STRETCH)\");\n            System.out.println(\"- imageDBL: 等比例缩放适应 (scaleMode = ImageScaleMode.FIT)\");\n            System.out.println(\"- imageYT: 不缩放（原始大小） (scaleMode = ImageScaleMode.ORIGINAL)\");\n\n        } catch (Exception e) {\n            System.err.println(\"❌ 测试失败: \" + e.getMessage());\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/vo/TestDateEntity.java",
    "content": "package vo;\n\nimport org.jeecgframework.poi.excel.annotation.Excel;\n\nimport java.time.LocalDate;\nimport java.time.LocalDateTime;\n\n/**\n * @Description: TODO\n * @author: lsq\n * @date: 2024年07月31日 10:31\n */\npublic class TestDateEntity {\n    @Excel(name = \"localdate\", format = \"yyyy-MM-dd\")\n    private LocalDate localDate;\n\n    @Excel(name = \"localdatetime\", format = \"yyyy-MM-dd\")\n    private LocalDateTime localDateTime;\n\n\n    public LocalDate getLocalDate() {\n        return localDate;\n    }\n\n    public void setLocalDate(LocalDate localDate) {\n        this.localDate = localDate;\n    }\n\n    public LocalDateTime getLocalDateTime() {\n        return localDateTime;\n    }\n\n    public void setLocalDateTime(LocalDateTime localDateTime) {\n        this.localDateTime = localDateTime;\n    }\n}\n"
  },
  {
    "path": "autopoi/src/test/java/vo/TestEntity.java",
    "content": "package vo;\n\nimport org.jeecgframework.poi.excel.annotation.Excel;\n\n/**\n * @Description: TODO\n * @author: lsq\n * @date: 2021年02月02日 16:31\n */\npublic class TestEntity {\n    @Excel(name = \"姓名\", width = 15)\n    private String name;\n    @Excel(name = \"年龄\", width = 15)\n    private Integer age;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public Integer getAge() {\n        return age;\n    }\n\n    public void setAge(Integer age) {\n        this.age = age;\n    }\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.jeecgframework</groupId>\n\t\t<artifactId>autopoi-parent</artifactId>\n\t\t<version>2.0.4</version>\n\t</parent>\n\t<artifactId>autopoi-spring-boot-2-starter</artifactId>\n\t<name>autopoi-spring-boot-2-starter</name>\n\n\t<properties>\n\t\t<!-- Spring Boot 2.x requires JDK 8+ -->\n\t\t<maven.compiler.source>1.8</maven.compiler.source>\n\t\t<maven.compiler.target>1.8</maven.compiler.target>\n\t\t<java.version>1.8</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<!-- autopoi -->\n\t\t<dependency>\n\t\t\t<groupId>org.jeecgframework</groupId>\n\t\t\t<artifactId>autopoi</artifactId>\n\t\t\t<version>${autopoi.version}</version>\n\t\t</dependency>\n\n\t\t<!-- commons-io: Required by Apache POI 5.x -->\n\t\t<dependency>\n\t\t\t<groupId>commons-io</groupId>\n\t\t\t<artifactId>commons-io</artifactId>\n\t\t</dependency>\n\t\n\t\t<!--spring -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-webmvc</artifactId>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\t\n\t\t<!-- servlet -->\n\t\t<dependency>\n\t\t\t<groupId>javax.servlet</groupId>\n\t\t\t<artifactId>servlet-api</artifactId>\n\t\t\t<version>2.5</version>\n\t\t\t<scope>provided</scope>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\n</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<!-- JDK 8 编译配置 - 关键：release 参数确保只使用 JDK 8 API -->\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<version>3.11.0</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>1.8</source>\n\t\t\t\t\t<target>1.8</target>\n\t\t\t\t\t<release>8</release>\n\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/BasePOIConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 基础POI常量\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:23:37\n */\ninterface BasePOIConstants {\n\n\t/**\n\t * 注解对象\n\t */\n\tpublic final static String CLASS = \"entity\";\n\t/**\n\t * 表格参数\n\t */\n\tpublic final static String PARAMS = \"params\";\n\t/**\n\t * 下载文件名称\n\t */\n\tpublic final static String FILE_NAME = \"fileName\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/MapExcelConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 正常导出Excel\n * \n * @Author JEECG on 14-3-8. 静态常量\n */\npublic interface MapExcelConstants extends BasePOIConstants {\n\t/**\n\t * 单Sheet导出\n\t */\n\tpublic final static String JEECG_MAP_EXCEL_VIEW = \"jeecgMapExcelView\";\n\t/**\n\t * Entity List\n\t */\n\tpublic final static String ENTITY_LIST = \"data\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String MAP_LIST = \"mapList\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/NormalExcelConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 正常导出Excel\n * \n * @Author JEECG on 14-3-8. 静态常量\n */\npublic interface NormalExcelConstants extends BasePOIConstants {\n\t/**\n\t * 单Sheet导出\n\t */\n\tpublic final static String JEECG_ENTITY_EXCEL_VIEW = \"jeecgEntityExcelView\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String DATA_LIST = \"data\";\n\n\t/**\n\t * 多Sheet 对象\n\t */\n\tpublic final static String MAP_LIST = \"mapList\";\n\n\t/**\n\t * 导出字段自定义\n\t */\n\tpublic final static String EXPORT_FIELDS = \"exportFields\";\n\n\n\t/**\n\t * 自定义导出服务\n\t * for [issues/8652]excel导出大数据问题 #8652\n\t */\n\tpublic final static String EXPORT_SERVER = \"excelExportServer\";\n\n\n\n\t/**\n\t * 查询参数\n\t * for [issues/8652]excel导出大数据问题 #8652\n\t */\n\tpublic final static String QUERY_PARAMS = \"queryParams\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateExcelConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 模板Excel导出常量\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:26:52\n */\npublic interface TemplateExcelConstants extends BasePOIConstants {\n\t/**\n\t * 模板导出\n\t */\n\tpublic final static String JEECG_TEMPLATE_EXCEL_VIEW = \"jeecgTemplateExcelView\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String LIST_DATA = \"list\";\n\t/**\n\t * 模板参数\n\t */\n\tpublic final static String MAP_DATA = \"map\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateWordConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * Word 导出模板常量\n * \n * @author JEECG\n * @date 2014年7月24日 下午11:26:46\n */\npublic interface TemplateWordConstants extends BasePOIConstants {\n\t/**\n\t * 模板导出\n\t */\n\tpublic final static String JEECG_TEMPLATE_WORD_VIEW = \"jeecgTemplateWordView\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String URL = \"url\";\n\t/**\n\t * 模板参数\n\t */\n\tpublic final static String MAP_DATA = \"map\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.def.NormalExcelConstants;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.export.ExcelExportServer;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServer;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced;\nimport org.springframework.stereotype.Controller;\n\n/**\n * Entity 实体数据对象导出\n * @Author JEECG\n * \n */\n@SuppressWarnings(\"unchecked\")\n@Controller(NormalExcelConstants.JEECG_ENTITY_EXCEL_VIEW)\npublic class JeecgEntityExcelView extends MiniAbstractExcelView {\n\n\tpublic JeecgEntityExcelView() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件\";\n\t\tWorkbook workbook = null;\n\t\t//---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\t\tString[] exportFields = null;\n\n\t\tObject exportFieldStr =  model.get(NormalExcelConstants.EXPORT_FIELDS);\n\t\tif(exportFieldStr!=null && exportFieldStr!=\"\"){\n\t\t\texportFields = exportFieldStr.toString().split(\",\");\n\t\t}\n        //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\t\tif (model.containsKey(NormalExcelConstants.MAP_LIST)) {\n\t\t\tList<Map<String, Object>> list = (List<Map<String, Object>>) model.get(NormalExcelConstants.MAP_LIST);\n\t\t\tif (list.size() == 0) {\n\t\t\t\tthrow new RuntimeException(\"MAP_LIST IS NULL\");\n\t\t\t}\n\t\t\tworkbook = ExcelExportUtil.exportExcel((ExportParams) list.get(0).get(NormalExcelConstants.PARAMS), (Class<?>) list.get(0).get(NormalExcelConstants.CLASS), (Collection<?>) list.get(0).get(NormalExcelConstants.DATA_LIST),exportFields);\n\t\t\tfor (int i = 1; i < list.size(); i++) {\n\t\t\t\tnew ExcelExportServer().createSheet(workbook, (ExportParams) list.get(i).get(NormalExcelConstants.PARAMS), (Class<?>) list.get(i).get(NormalExcelConstants.CLASS), (Collection<?>) list.get(i).get(NormalExcelConstants.DATA_LIST),exportFields);\n\t\t\t}\n\t\t} else if(model.containsKey(NormalExcelConstants.EXPORT_SERVER)){\n            //update-begin---author:chenrui ---date:20251104  for：[QQYUN-13964]演示系统数据量大，点击没反应------------\n\t\t\tObject exportServerObj = model.get(NormalExcelConstants.EXPORT_SERVER);\n\t\t\tif(exportServerObj instanceof IExcelExportServer){\n\t\t\t\t//update-begin---author:chenrui ---date:20250812  for：[issues/8652]excel导出大数据问题 #8652------------\n\t\t\t\tworkbook = ExcelExportUtil.exportBigExcel((ExportParams) model.get(NormalExcelConstants.PARAMS),\n\t\t\t\t(Class<?>) model.get(NormalExcelConstants.CLASS),\n\t\t\t\t(IExcelExportServer) model.get(NormalExcelConstants.EXPORT_SERVER),\n\t\t\t\tmodel.get(NormalExcelConstants.QUERY_PARAMS));\n\t\t\t\t//update-end---author:chenrui ---date:20250812  for：[issues/8652]excel导出大数据问题 #8652------------\n\t\t\t} else if(exportServerObj instanceof IExcelExportServerEnhanced){\n\t\t\t\tworkbook = ExcelExportUtil.exportBigExcelEnhanced((ExportParams) model.get(NormalExcelConstants.PARAMS),\n\t\t\t\t\t\t(Class<?>) model.get(NormalExcelConstants.CLASS),\n\t\t\t\t\t\t(IExcelExportServerEnhanced) exportServerObj,\n\t\t\t\t\t\tmodel.get(NormalExcelConstants.QUERY_PARAMS));\n\t\t\t}\n            //update-end---author:chenrui ---date:20251104  for：[QQYUN-13964]演示系统数据量大，点击没反应------------\n\t\t} else {\n\t\t\tworkbook = ExcelExportUtil.exportExcel((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class<?>) model.get(NormalExcelConstants.CLASS), (Collection<?>) model.get(NormalExcelConstants.DATA_LIST),exportFields);\n\t\t}\n\t\tif (model.containsKey(NormalExcelConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(NormalExcelConstants.FILE_NAME);\n\t\t}\n\t\tif (workbook instanceof HSSFWorkbook) {\n\t\t\tcodedFileName += HSSF;\n\t\t} else {\n\t\t\tcodedFileName += XSSF;\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tworkbook.write(out);\n\t\tout.flush();\n\t}\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgMapExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.def.MapExcelConstants;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.springframework.stereotype.Controller;\n\n/**\n * Map 数据对象接口导出\n * \n * @author JEECG\n * @date 2014年11月25日 下午3:26:32\n */\n@SuppressWarnings(\"unchecked\")\n@Controller(MapExcelConstants.JEECG_MAP_EXCEL_VIEW)\npublic class JeecgMapExcelView extends MiniAbstractExcelView {\n\n\tpublic JeecgMapExcelView() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件\";\n\t\tWorkbook workbook = ExcelExportUtil.exportExcel((ExportParams) model.get(MapExcelConstants.PARAMS), (List<ExcelExportEntity>) model.get(MapExcelConstants.ENTITY_LIST), (Collection<? extends Map<?, ?>>) model.get(MapExcelConstants.MAP_LIST));\n\t\tif (model.containsKey(MapExcelConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(MapExcelConstants.FILE_NAME);\n\t\t}\n\t\tif (workbook instanceof HSSFWorkbook) {\n\t\t\tcodedFileName += HSSF;\n\t\t} else {\n\t\t\tcodedFileName += XSSF;\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tworkbook.write(out);\n\t\tout.flush();\n\t}\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.def.NormalExcelConstants;\nimport org.jeecgframework.poi.excel.def.TemplateExcelConstants;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\nimport org.springframework.stereotype.Controller;\n\n/**\n * Excel 模板导出\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:15:49\n */\n@SuppressWarnings(\"unchecked\")\n@Controller(TemplateExcelConstants.JEECG_TEMPLATE_EXCEL_VIEW)\npublic class JeecgTemplateExcelView extends MiniAbstractExcelView {\n\n\tpublic JeecgTemplateExcelView() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件\";\n\t\tWorkbook workbook = ExcelExportUtil.exportExcel((TemplateExportParams) model.get(TemplateExcelConstants.PARAMS), (Class<?>) model.get(TemplateExcelConstants.CLASS), (List<?>) model.get(TemplateExcelConstants.LIST_DATA), (Map<String, Object>) model.get(TemplateExcelConstants.MAP_DATA));\n\t\tif (model.containsKey(NormalExcelConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(NormalExcelConstants.FILE_NAME);\n\t\t}\n\t\tif (workbook instanceof HSSFWorkbook) {\n\t\t\tcodedFileName += HSSF;\n\t\t} else {\n\t\t\tcodedFileName += XSSF;\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tworkbook.write(out);\n\t\tout.flush();\n\t}\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateWordView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.Map;\n\nimport javax.servlet.ServletOutputStream;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.jeecgframework.poi.excel.def.TemplateWordConstants;\nimport org.jeecgframework.poi.word.WordExportUtil;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.servlet.view.AbstractView;\n\n/**\n * Word模板导出\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:15:49\n */\n@SuppressWarnings(\"unchecked\")\n@Controller(TemplateWordConstants.JEECG_TEMPLATE_WORD_VIEW)\npublic class JeecgTemplateWordView extends AbstractView {\n\n\tprivate static final String CONTENT_TYPE = \"application/msword\";\n\n\tpublic JeecgTemplateWordView() {\n\t\tsetContentType(CONTENT_TYPE);\n\t}\n\n\tpublic boolean isIE(HttpServletRequest request) {\n\t\treturn (request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"msie\") > 0 || request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"rv:11.0\") > 0) ? true : false;\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件.docx\";\n\t\tif (model.containsKey(TemplateWordConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(TemplateWordConstants.FILE_NAME) + \".docx\";\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tXWPFDocument document = WordExportUtil.exportWord07((String) model.get(TemplateWordConstants.URL), (Map<String, Object>) model.get(TemplateWordConstants.MAP_DATA));\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tdocument.write(out);\n\t\tout.flush();\n\t}\n}\n"
  },
  {
    "path": "autopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/MiniAbstractExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport javax.servlet.http.HttpServletRequest;\n\nimport org.springframework.web.servlet.view.AbstractView;\n\n/**\n * 基础抽象Excel View\n * \n * @author JEECG\n * @date 2015年2月28日 下午1:41:05\n */\npublic abstract class MiniAbstractExcelView extends AbstractView {\n\n\tprivate static final String CONTENT_TYPE = \"application/vnd.ms-excel\";\n\n\tprotected static final String HSSF = \".xls\";\n\tprotected static final String XSSF = \".xlsx\";\n\n\tpublic MiniAbstractExcelView() {\n\t\tsetContentType(CONTENT_TYPE);\n\t}\n\n\tprotected boolean isIE(HttpServletRequest request) {\n\t\treturn (request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"msie\") > 0 || request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"rv:11.0\") > 0) ? true : false;\n\t}\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n\txmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<parent>\n\t\t<groupId>org.jeecgframework</groupId>\n\t\t<artifactId>autopoi-parent</artifactId>\n\t\t<version>2.0.4</version>\n\t</parent>\n\t<artifactId>autopoi-spring-boot-3-starter</artifactId>\n\t<name>autopoi-spring-boot-3-starter</name>\n\n\t<properties>\n\t\t<!-- Spring Boot 3.x requires JDK 17+ -->\n\t\t<maven.compiler.source>17</maven.compiler.source>\n\t\t<maven.compiler.target>17</maven.compiler.target>\n\t\t<maven.compiler.release>17</maven.compiler.release>\n\t\t<java.version>17</java.version>\n\t</properties>\n\n\t<dependencies>\n\t\t<!-- autopoi -->\n\t\t<dependency>\n\t\t\t<groupId>org.jeecgframework</groupId>\n\t\t\t<artifactId>autopoi</artifactId>\n\t\t\t<version>${autopoi.version}</version>\n\t\t</dependency>\n\t\n\t\t<!-- commons-io: Required by Apache POI 5.x -->\n\t\t<dependency>\n\t\t\t<groupId>commons-io</groupId>\n\t\t\t<artifactId>commons-io</artifactId>\n\t\t</dependency>\n\t\n\t\t<!--spring 6.x for Spring Boot 3.x -->\n\t\t<dependency>\n\t\t\t<groupId>org.springframework</groupId>\n\t\t\t<artifactId>spring-webmvc</artifactId>\n\t\t\t<version>6.2.1</version>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\t\n\t\t<!-- Jakarta Servlet (Spring Boot 3.x uses Jakarta EE) -->\n\t\t<dependency>\n\t\t\t<groupId>jakarta.servlet</groupId>\n\t\t\t<artifactId>jakarta.servlet-api</artifactId>\n\t\t\t<version>6.1.0</version>\n\t\t\t<scope>provided</scope>\n\t\t\t<optional>true</optional>\n\t\t</dependency>\n\n</dependencies>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<!-- JDK 17 编译配置 - 关键：release 参数确保使用 JDK 17 API -->\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<version>3.11.0</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>17</source>\n\t\t\t\t\t<target>17</target>\n\t\t\t\t\t<release>17</release>\n\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/BasePOIConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 基础POI常量\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:23:37\n */\ninterface BasePOIConstants {\n\n\t/**\n\t * 注解对象\n\t */\n\tpublic final static String CLASS = \"entity\";\n\t/**\n\t * 表格参数\n\t */\n\tpublic final static String PARAMS = \"params\";\n\t/**\n\t * 下载文件名称\n\t */\n\tpublic final static String FILE_NAME = \"fileName\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/MapExcelConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 正常导出Excel\n * \n * @Author JEECG on 14-3-8. 静态常量\n */\npublic interface MapExcelConstants extends BasePOIConstants {\n\t/**\n\t * 单Sheet导出\n\t */\n\tpublic final static String JEECG_MAP_EXCEL_VIEW = \"jeecgMapExcelView\";\n\t/**\n\t * Entity List\n\t */\n\tpublic final static String ENTITY_LIST = \"data\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String MAP_LIST = \"mapList\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/NormalExcelConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 正常导出Excel\n * \n * @Author JEECG on 14-3-8. 静态常量\n */\npublic interface NormalExcelConstants extends BasePOIConstants {\n\t/**\n\t * 单Sheet导出\n\t */\n\tpublic final static String JEECG_ENTITY_EXCEL_VIEW = \"jeecgEntityExcelView\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String DATA_LIST = \"data\";\n\n\t/**\n\t * 多Sheet 对象\n\t */\n\tpublic final static String MAP_LIST = \"mapList\";\n\n\t/**\n\t * 导出字段自定义\n\t */\n\tpublic final static String EXPORT_FIELDS = \"exportFields\";\n\n\n\t/**\n\t * 自定义导出服务\n\t * for [issues/8652]excel导出大数据问题 #8652\n\t */\n\tpublic final static String EXPORT_SERVER = \"excelExportServer\";\n\n\n\n\t/**\n\t * 查询参数\n\t * for [issues/8652]excel导出大数据问题 #8652\n\t */\n\tpublic final static String QUERY_PARAMS = \"queryParams\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateExcelConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * 模板Excel导出常量\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:26:52\n */\npublic interface TemplateExcelConstants extends BasePOIConstants {\n\t/**\n\t * 模板导出\n\t */\n\tpublic final static String JEECG_TEMPLATE_EXCEL_VIEW = \"jeecgTemplateExcelView\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String LIST_DATA = \"list\";\n\t/**\n\t * 模板参数\n\t */\n\tpublic final static String MAP_DATA = \"map\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/def/TemplateWordConstants.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.def;\n\n/**\n * Word 导出模板常量\n * \n * @author JEECG\n * @date 2014年7月24日 下午11:26:46\n */\npublic interface TemplateWordConstants extends BasePOIConstants {\n\t/**\n\t * 模板导出\n\t */\n\tpublic final static String JEECG_TEMPLATE_WORD_VIEW = \"jeecgTemplateWordView\";\n\t/**\n\t * 数据列表\n\t */\n\tpublic final static String URL = \"url\";\n\t/**\n\t * 模板参数\n\t */\n\tpublic final static String MAP_DATA = \"map\";\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.def.NormalExcelConstants;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.export.ExcelExportServer;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServer;\nimport org.jeecgframework.poi.handler.inter.IExcelExportServerEnhanced;\nimport org.springframework.stereotype.Controller;\n\n/**\n * Entity 实体数据对象导出\n * @Author JEECG\n * \n */\n@SuppressWarnings(\"unchecked\")\n@Controller(NormalExcelConstants.JEECG_ENTITY_EXCEL_VIEW)\npublic class JeecgEntityExcelView extends MiniAbstractExcelView {\n\n\tpublic JeecgEntityExcelView() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件\";\n\t\tWorkbook workbook = null;\n\t\t//---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\t\tString[] exportFields = null;\n\n\t\tObject exportFieldStr =  model.get(NormalExcelConstants.EXPORT_FIELDS);\n\t\tif(exportFieldStr!=null && exportFieldStr!=\"\"){\n\t\t\texportFields = exportFieldStr.toString().split(\",\");\n\t\t}\n        //---update-end-----autor:scott------date:20191016-------for:导出字段支持自定义--------\n\t\tif (model.containsKey(NormalExcelConstants.MAP_LIST)) {\n\t\t\tList<Map<String, Object>> list = (List<Map<String, Object>>) model.get(NormalExcelConstants.MAP_LIST);\n\t\t\tif (list.size() == 0) {\n\t\t\t\tthrow new RuntimeException(\"MAP_LIST IS NULL\");\n\t\t\t}\n\t\t\tworkbook = ExcelExportUtil.exportExcel((ExportParams) list.get(0).get(NormalExcelConstants.PARAMS), (Class<?>) list.get(0).get(NormalExcelConstants.CLASS), (Collection<?>) list.get(0).get(NormalExcelConstants.DATA_LIST),exportFields);\n\t\t\tfor (int i = 1; i < list.size(); i++) {\n\t\t\t\tnew ExcelExportServer().createSheet(workbook, (ExportParams) list.get(i).get(NormalExcelConstants.PARAMS), (Class<?>) list.get(i).get(NormalExcelConstants.CLASS), (Collection<?>) list.get(i).get(NormalExcelConstants.DATA_LIST),exportFields);\n\t\t\t}\n\t\t} else if(model.containsKey(NormalExcelConstants.EXPORT_SERVER)){\n            //update-begin---author:chenrui ---date:20251104  for：[QQYUN-13964]演示系统数据量大，点击没反应------------\n\t\t\tObject exportServerObj = model.get(NormalExcelConstants.EXPORT_SERVER);\n\t\t\tif(exportServerObj instanceof IExcelExportServer){\n\t\t\t\t//update-begin---author:chenrui ---date:20250812  for：[issues/8652]excel导出大数据问题 #8652------------\n\t\t\t\tworkbook = ExcelExportUtil.exportBigExcel((ExportParams) model.get(NormalExcelConstants.PARAMS),\n\t\t\t\t(Class<?>) model.get(NormalExcelConstants.CLASS),\n\t\t\t\t(IExcelExportServer) model.get(NormalExcelConstants.EXPORT_SERVER),\n\t\t\t\tmodel.get(NormalExcelConstants.QUERY_PARAMS));\n\t\t\t\t//update-end---author:chenrui ---date:20250812  for：[issues/8652]excel导出大数据问题 #8652------------\n\t\t\t} else if(exportServerObj instanceof IExcelExportServerEnhanced){\n\t\t\t\tworkbook = ExcelExportUtil.exportBigExcelEnhanced((ExportParams) model.get(NormalExcelConstants.PARAMS),\n\t\t\t\t\t\t(Class<?>) model.get(NormalExcelConstants.CLASS),\n\t\t\t\t\t\t(IExcelExportServerEnhanced) exportServerObj,\n\t\t\t\t\t\tmodel.get(NormalExcelConstants.QUERY_PARAMS));\n\t\t\t}\n            //update-end---author:chenrui ---date:20251104  for：[QQYUN-13964]演示系统数据量大，点击没反应------------\n\t\t} else {\n\t\t\tworkbook = ExcelExportUtil.exportExcel((ExportParams) model.get(NormalExcelConstants.PARAMS), (Class<?>) model.get(NormalExcelConstants.CLASS), (Collection<?>) model.get(NormalExcelConstants.DATA_LIST),exportFields);\n\t\t}\n\t\tif (model.containsKey(NormalExcelConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(NormalExcelConstants.FILE_NAME);\n\t\t}\n\t\tif (workbook instanceof HSSFWorkbook) {\n\t\t\tcodedFileName += HSSF;\n\t\t} else {\n\t\t\tcodedFileName += XSSF;\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tworkbook.write(out);\n\t\tout.flush();\n\t}\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgMapExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.def.MapExcelConstants;\nimport org.jeecgframework.poi.excel.entity.ExportParams;\nimport org.jeecgframework.poi.excel.entity.params.ExcelExportEntity;\nimport org.springframework.stereotype.Controller;\n\n/**\n * Map 数据对象接口导出\n * \n * @author JEECG\n * @date 2014年11月25日 下午3:26:32\n */\n@SuppressWarnings(\"unchecked\")\n@Controller(MapExcelConstants.JEECG_MAP_EXCEL_VIEW)\npublic class JeecgMapExcelView extends MiniAbstractExcelView {\n\n\tpublic JeecgMapExcelView() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件\";\n\t\tWorkbook workbook = ExcelExportUtil.exportExcel((ExportParams) model.get(MapExcelConstants.PARAMS), (List<ExcelExportEntity>) model.get(MapExcelConstants.ENTITY_LIST), (Collection<? extends Map<?, ?>>) model.get(MapExcelConstants.MAP_LIST));\n\t\tif (model.containsKey(MapExcelConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(MapExcelConstants.FILE_NAME);\n\t\t}\n\t\tif (workbook instanceof HSSFWorkbook) {\n\t\t\tcodedFileName += HSSF;\n\t\t} else {\n\t\t\tcodedFileName += XSSF;\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tworkbook.write(out);\n\t\tout.flush();\n\t}\n\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.List;\nimport java.util.Map;\n\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.hssf.usermodel.HSSFWorkbook;\nimport org.apache.poi.ss.usermodel.Workbook;\nimport org.jeecgframework.poi.excel.ExcelExportUtil;\nimport org.jeecgframework.poi.excel.def.NormalExcelConstants;\nimport org.jeecgframework.poi.excel.def.TemplateExcelConstants;\nimport org.jeecgframework.poi.excel.entity.TemplateExportParams;\nimport org.springframework.stereotype.Controller;\n\n/**\n * Excel 模板导出\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:15:49\n */\n@SuppressWarnings(\"unchecked\")\n@Controller(TemplateExcelConstants.JEECG_TEMPLATE_EXCEL_VIEW)\npublic class JeecgTemplateExcelView extends MiniAbstractExcelView {\n\n\tpublic JeecgTemplateExcelView() {\n\t\tsuper();\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件\";\n\t\tWorkbook workbook = ExcelExportUtil.exportExcel((TemplateExportParams) model.get(TemplateExcelConstants.PARAMS), (Class<?>) model.get(TemplateExcelConstants.CLASS), (List<?>) model.get(TemplateExcelConstants.LIST_DATA), (Map<String, Object>) model.get(TemplateExcelConstants.MAP_DATA));\n\t\tif (model.containsKey(NormalExcelConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(NormalExcelConstants.FILE_NAME);\n\t\t}\n\t\tif (workbook instanceof HSSFWorkbook) {\n\t\t\tcodedFileName += HSSF;\n\t\t} else {\n\t\t\tcodedFileName += XSSF;\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tworkbook.write(out);\n\t\tout.flush();\n\t}\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgTemplateWordView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport java.util.Map;\n\nimport jakarta.servlet.ServletOutputStream;\nimport jakarta.servlet.http.HttpServletRequest;\nimport jakarta.servlet.http.HttpServletResponse;\n\nimport org.apache.poi.xwpf.usermodel.XWPFDocument;\nimport org.jeecgframework.poi.excel.def.TemplateWordConstants;\nimport org.jeecgframework.poi.word.WordExportUtil;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.servlet.view.AbstractView;\n\n/**\n * Word模板导出\n * \n * @author JEECG\n * @date 2014年6月30日 下午9:15:49\n */\n@SuppressWarnings(\"unchecked\")\n@Controller(TemplateWordConstants.JEECG_TEMPLATE_WORD_VIEW)\npublic class JeecgTemplateWordView extends AbstractView {\n\n\tprivate static final String CONTENT_TYPE = \"application/msword\";\n\n\tpublic JeecgTemplateWordView() {\n\t\tsetContentType(CONTENT_TYPE);\n\t}\n\n\tpublic boolean isIE(HttpServletRequest request) {\n\t\treturn (request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"msie\") > 0 || request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"rv:11.0\") > 0) ? true : false;\n\t}\n\n\t@Override\n\tprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {\n\t\tString codedFileName = \"临时文件.docx\";\n\t\tif (model.containsKey(TemplateWordConstants.FILE_NAME)) {\n\t\t\tcodedFileName = (String) model.get(TemplateWordConstants.FILE_NAME) + \".docx\";\n\t\t}\n\t\tif (isIE(request)) {\n\t\t\tcodedFileName = java.net.URLEncoder.encode(codedFileName, \"UTF8\");\n\t\t} else {\n\t\t\tcodedFileName = new String(codedFileName.getBytes(\"UTF-8\"), \"ISO-8859-1\");\n\t\t}\n\t\tresponse.setHeader(\"content-disposition\", \"attachment;filename=\" + codedFileName);\n\t\tXWPFDocument document = WordExportUtil.exportWord07((String) model.get(TemplateWordConstants.URL), (Map<String, Object>) model.get(TemplateWordConstants.MAP_DATA));\n\t\tServletOutputStream out = response.getOutputStream();\n\t\tdocument.write(out);\n\t\tout.flush();\n\t}\n}\n"
  },
  {
    "path": "autopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/MiniAbstractExcelView.java",
    "content": "/**\n * Copyright 2013-2015 JEECG (jeecgos@163.com)\n *   \n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.jeecgframework.poi.excel.view;\n\nimport jakarta.servlet.http.HttpServletRequest;\n\nimport org.springframework.web.servlet.view.AbstractView;\n\n/**\n * 基础抽象Excel View\n * \n * @author JEECG\n * @date 2015年2月28日 下午1:41:05\n */\npublic abstract class MiniAbstractExcelView extends AbstractView {\n\n\tprivate static final String CONTENT_TYPE = \"application/vnd.ms-excel\";\n\n\tprotected static final String HSSF = \".xls\";\n\tprotected static final String XSSF = \".xlsx\";\n\n\tpublic MiniAbstractExcelView() {\n\t\tsetContentType(CONTENT_TYPE);\n\t}\n\n\tprotected boolean isIE(HttpServletRequest request) {\n\t\treturn (request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"msie\") > 0 || request.getHeader(\"USER-AGENT\").toLowerCase().indexOf(\"rv:11.0\") > 0) ? true : false;\n\t}\n\n}\n"
  },
  {
    "path": "deploy.bat",
    "content": "cmd /k mvn clean deploy -P release -Dmaven.test.skip=true"
  },
  {
    "path": "docs/修改日志.log",
    "content": "-- author: liusq---date:20230407---for: [issue/4342]autopoi导出带副标题的数据表，副标题缺左边框\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\export\\ExcelExportServer.java\n-- author: liusq---date:20230407---for: [issue/4342]autopoi导出带副标题的数据表，副标题缺左边框\n\n-- author: liusq---date:20230410---for: [issue/4415]autopoi-web 导入图片字段时无法指定保存路径\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\imports\\ExcelImportServer.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\imports\\base\\ImportFileServiceI.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\annotation\\Excel.java\n-- author: liusq---date:20230410---for: [issue/4415]autopoi-web 导入图片字段时无法指定保存路径\n\n---author:scott ---date:2023-12-05  for：【jeecg-boot/issues/5538】导出表格设置了数字格式导出之后仍然是文本格式，并且无法进行计算 #5538-\norg\\jeecgframework\\poi\\excel\\export\\base\\ExcelExportBase.java\n---author:scott ---date::2023-12-05  for：【jeecg-boot/issues/5538】导出表格设置了数字格式导出之后仍然是文本格式，并且无法进行计算 #5538--\n\n---author:scott ---date::2023-12-05  for：【jeecg-boot/issues/5528】POI导出一对多只有一条数据时，疯狂打印错误日志 #5528\norg\\jeecgframework\\poi\\excel\\export\\base\\ExcelExportBase.java\n---author:scott ---date::2023-12-05  for：【jeecg-boot/issues/5528】POI导出一对多只有一条数据时，疯狂打印错误日志 #5528\n\n---author:liusq ---date::2023-12-07  for：[issues/5538]导出表格设置了数字格式导出之后仍然是文本格式，并且无法进行计算\nautopoi-framework\\autopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\export\\base\\ExcelExportBase.java\n---author:liusq ---date::2023-12-07  for：[issues/5538]导出表格设置了数字格式导出之后仍然是文本格式，并且无法进行计算\n\n---author:scott ---date:2023-12-08  for：代码写法兼容改造---\norg\\jeecgframework\\poi\\excel\\annotation\\Excel.java\norg\\jeecgframework\\poi\\excel\\imports\\ExcelImportServer.java\n---author:scott ---date:2023-12-08  for：代码写法兼容改造---\n\n---author:chenrui---date:2024/1/2-----for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题---\nautopoi/src/main/java/org/jeecgframework/poi/excel/html/helper/MergedRegionHelper.java\nautopoi-web/src/test/java/DaoChuTest.java\nautopoi-web/src/test/resources/templates/testNextMarge.xlsx\n---author:chenrui---date:2024/1/2-----for:[issue/5167]遍历单元格次行原本的合并缓存未正确删除导致最终输出合并样式有问题---\n\n---author:chenrui---date:2024/1/3-----for:[issue/#5248]加强继承扩展便利性---\nautopoi/src/main/java/org/jeecgframework/poi/excel/annotation/Excel.java\nautopoi/src/main/java/org/jeecgframework/poi/excel/ExcelExportUtil.java\n---author:chenrui---date:2024/1/3-----for:[issue/#5248]加强继承扩展便利性---\n\n---author:chenrui---date:2024/3/8-----for:[QQYUN-8394]Excel导入时空行校验问题---\nautopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java\n---author:chenrui---date:2024/3/8-----for:[QQYUN-8394]Excel导入时空行校验问题---\n\n---author:chenrui---date:2024/4/3-----for:[issue/#5933]增加清除缓存方法---\nautopoi/src/main/java/org/jeecgframework/poi/cache/manager/POICacheManager.java\n---author:chenrui---date:2024/4/3-----for:[issue/#5933]增加清除缓存方法---\n\n---author:chenrui---date:2024/4/3-----for:生成代码后子表图片无法导出(流)---\nautopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java\n---author:chenrui---date:2024/4/3-----for:生成代码后子表图片无法导出(流)---\n\n---author:chenrui---date:2024/4/3-----for:[issue/#6025/#6040]子表图片导入报错---\nautopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java\n---author:chenrui---date:2024/4/3-----for:[issue/#6025/#6040]子表图片导入报错---\n\n---author:chenrui---date:2024/4/3-----for:[issue/#5987]嵌入单元格图片无法导入---\nautopoi/src/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java\nautopoi/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java\nautopoi/pom.xml\npom.xml\n---author:chenrui---date:2024/4/3-----for:[issue/#5987]嵌入单元格图片无法导入---\n\n---author:chenrui---date:2024/4/7-----for:[QQYUN-8898]不依赖hutool,xml解析改为dom---\npom.xml\nautopoi/pom.xml\nautopoi/src/main/java/org/jeecgframework/poi/util/PoiPublicUtil.java\n---author:chenrui---date:2024/4/7-----for:[QQYUN-8898]不依赖hutool,xml解析改为dom---\n\n---author:chenrui---date:2024/4/24-----for:[QQYUN-9048]负数被识别成非数字---\nsrc/main/java/org/jeecgframework/poi/util/ExcelUtil.java\n---author:chenrui---date:2024/4/24-----for:[QQYUN-9048]负数被识别成非数字---\n\n---author:liusq---date:2024/7/30-----for:TV360X-1953 execl转html报错d != org.apache.poi.ss.usermodel.FillPatternType---\nsrc\\main\\java\\org\\jeecgframework\\poi\\excel\\html\\helper\\StylerHelper.java\nsrc\\test\\resources\\templates\\专项支出用款申请书.xls\nsrc\\test\\java\\ExcelToHtmlTest.java\n---author:liusq---date:2024/7/30-----for:TV360X-1953 execl转html报错d != org.apache.poi.ss.usermodel.FillPatternType---\n\n---author:liusq---date:2024/7/30-----for:TV360X-1292 对象的属性为LocalDate、LocalDateTIme类型对象兼容---\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\util\\PoiPublicUtil.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\imports\\CellValueServer.java\nsrc\\test\\resources\\templates\\ExcelImportDateTest.xlsx\nsrc\\test\\java\\TestDateEntity.java\nsrc\\test\\java\\ImportExcelTest.java\n---author:liusq---date:2024/7/30-----for:TV360X-1292 对象的属性为LocalDate、LocalDateTIme类型对象兼容---\n\n---author:chenrui---date:2024/8/1-----for:[issues/6925]xlsx模版导出图片---\nsrc/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java\n---author:chenrui---date:2024/8/1-----for:[issues/6925]xlsx模版导出图片---\n\n---author:liusq---date:2024/8/7-----for:[issues/6925]autopoi通过word模板生成word时：三目、求长、常量、日期转换没起效果---\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\util\\PoiElUtil.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\util\\PoiPublicUtil.java\n---author:liusq---date:2024/8/7-----for:[issues#6096]autopoi通过word模板生成word时：三目、求长、常量、日期转换没起效果---\n\n---author:liusq---date:2024/9/3-----for:[issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式---\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\export\\template\\ExcelExportOfTemplateUtil.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\entity\\TemplateExportParams.java\n---author:liusq---date:2024/9/3-----for:[issues/7048]TemplateExportParams类建议增加传入模板文件InputStream的方式---\n\n---author:chenrui---date:2025/3/20-----for:[issues/7947]autopoi导入 报错Cell index must be >= 0  ---\nsrc/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java\n---author:chenrui---date:2025/3/20-----for:[issues/7947]autopoi导入 报错Cell index must be >= 0  ---\n\n---author:liusq---date:2025/6/04-----for:[issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错，导出失败---\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\export\\template\\ExcelExportOfTemplateUtil.java\n---author:liusq---date:2024/6/04-----for:[issues/8230] autopoi使用模板导出时,如果传入的map中存在值为null时会导致异常出错，导出失败---\n\n---author:liusq---date:2024/6/04-----for:[issues/8148] autopoi使用模板导出时,如果在循环列后存在公式单元格,导出来后该单元格未空，公式没了---\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\util\\PoiExcelTempUtil.java\n---author:liusq---date:2024/6/04-----for:[issues/8148] autopoi使用模板导出时,如果在循环列后存在公式单元格,导出来后该单元格未空，公式没了--\n\n---author:chenrui---date:2025/6/4-----for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248---\nsrc/main/java/org/jeecgframework/poi/excel/annotation/Excel.java\nsrc/main/java/org/jeecgframework/poi/excel/export/base/ExcelExportBase.java\n---author:chenrui---date:2025/6/4-----for:[issues/8248]AutoPOI导出的单元格格式建议加一个常规类型 #8248---\n\n\n---author:liusq---date:2024/6/24-----for:[issues/8489] autopoi模板导出虽然公式单元格没问题了，但是正常的字符串单元格出错了 #8489--\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\util\\PoiExcelTempUtil.java\n---author:liusq---date:2024/6/24-----for:[issues/8489] autopoi模板导出虽然公式单元格没问题了，但是正常的字符串单元格出错了 #8489--\n\n---author:chenrui---date:2025/7/16-----for:[issues/3943]导入excel时，标题区域的空行会导致下方列表数据被吞---\nsrc/main/java/org/jeecgframework/poi/excel/imports/ExcelImportServer.java\n---author:chenrui---date:2025/7/16-----for:[issues/3943]导入excel时，标题区域的空行会导致下方列表数据被吞---\n\n---author:chenrui---date:2025/8/19-----for:[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错---\nsrc/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java\n---author:chenrui---date:2025/8/19-----for:[issues/8699]AutoPoi在使用@ExcelEntity当设置show=true并且该项为null时报错---\n\n\n---author:chenrui---date:2025/11/4-----for:[QQYUN-13964]演示系统数据量大，点击没反应---\nautopoi/src/main/java/org/jeecgframework/poi/excel/export/ExcelBatchExportServer.java\nautopoi/src/main/java/org/jeecgframework/poi/excel/ExcelExportUtil.java\nautopoi/src/main/java/org/jeecgframework/poi/handler/inter/IExcelExportServerEnhanced.java (+)\nautopoi-spring-boot-3-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java\nautopoi-spring-boot-2-starter/src/main/java/org/jeecgframework/poi/excel/view/JeecgEntityExcelView.java\ndocs/修改日志.log\n.gitignore\n---author:chenrui---date:2025/11/4-----for:[QQYUN-13964]演示系统数据量大，点击没反应---\n\n---author:chenrui---date:2025/10/29-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式---\nsrc/main/java/org/jeecgframework/poi/consts/ImageScaleMode.java\nsrc/main/java/org/jeecgframework/poi/entity/ImageEntity.java\nsrc/main/java/org/jeecgframework/poi/excel/export/template/ExcelExportOfTemplateUtil.java\nsrc/test/java/TestImageScale.java\nsrc/test/resources/templates/dakytot.jpeg\nsrc/test/resources/templates/testImageScale.xlsx\n---author:chenrui---date:2025/10/29-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式---\n\n---author:chenrui---date:2025/11/5-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式-补提代码---\nsrc/main/java/org/jeecgframework/poi/entity/ImageEntity.java\n---author:chenrui---date:2025/11/5-----for:[issues/8892] AutoPoi ImageEntity建议添加scale属性，控制图片导出缩放模式-补提代码---\n\n---author:liusq---date:2025/12/11-----for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列---\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\annotation\\Excel.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\entity\\params\\ExcelExportEntity.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\export\\ExcelExportServer.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\excel\\export\\base\\ExportBase.java\nautopoi\\src\\main\\java\\org\\jeecgframework\\poi\\util\\JsonParser.java\n---author:liusq---date:2025/12/11-----for:JHHB-1212【AutoPoi】导出时，支持动态生成Excel的列---\n\n-- author:sjlei---date:20251218--for: AutoPoi 导出动态列继承父列的 orderNum，不应动态增加，否则会导致列顺序错乱 ---\nautopoi/src/main/java/org/jeecgframework/poi/excel/export/base/ExportBase.java\n-- author:sjlei---date:20251218--for: AutoPoi 导出动态列继承父列的 orderNum，不应动态增加，否则会导致列顺序错乱 ---\n"
  },
  {
    "path": "install.bat",
    "content": "cmd /k mvn clean install -D skipTest"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\txsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\t<modelVersion>4.0.0</modelVersion>\n\t<groupId>org.jeecgframework</groupId>\n\t<artifactId>autopoi-parent</artifactId>\n\n\t<version>2.0.4</version>\n\t<packaging>pom</packaging>\n\n\t<name>autopoi-parent</name>\n\t<url>http://www.jeecg.com</url>\n\t\n\t<modules>\n\t\t<module>autopoi</module>\n\t\t<module>autopoi-spring-boot-2-starter</module>\n\t\t<module>autopoi-spring-boot-3-starter</module>\n\t</modules>\n\n\t<description> office 工具类 基于 poi</description>\n\t<licenses>\n\t\t<license>\n\t\t\t<name>The Apache License, Version 2.0</name>\n\t\t\t<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n\t\t</license>\n\t</licenses>\n\t\n\t<scm>  \n      <connection>scm:git:https://github.com/zhangdaiscott/autopoi.git</connection>\n      <developerConnection>scm:git:https://github.com/zhangdaiscott/autopoi.git</developerConnection>  \n      <url>https://github.com/zhangdaiscott/autopoi</url>  \n\t</scm>\n\t<developers>\n        <developer>\n            <name>jeecg</name>\n            <email>jeecgos@163.com</email>\n        </developer>\n    </developers>\n\n\t<properties>\n\t\t<autopoi.version>2.0.4</autopoi.version>\n\t\t<poi.version>5.4.1</poi.version>\n\t\t<xerces.version>2.9.1</xerces.version>\n\t\t<guava.version>32.1.3-jre</guava.version>\n\t\t<commons-lang.version>3.10</commons-lang.version>\n\t\t<commons-io.version>2.18.0</commons-io.version>\n\t\t<slf4j.version>1.7.30</slf4j.version>\n\t\t<spring.version>5.1.0.RELEASE</spring.version>\n\t</properties>\n\t<dependencyManagement>\n\t\t<dependencies>\n\t\t\t<!-- poi -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.poi</groupId>\n\t\t\t\t<artifactId>poi</artifactId>\n\t\t\t\t<version>${poi.version}</version>\n\t\t\t</dependency>\n\t\t<dependency>\n\t\t\t<groupId>org.apache.poi</groupId>\n\t\t\t<artifactId>poi-ooxml</artifactId>\n\t\t\t<version>${poi.version}</version>\n\t\t</dependency>\n\t\t<!-- sax 读取时候用到的 -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>xerces</groupId>\n\t\t\t\t<artifactId>xercesImpl</artifactId>\n\t\t\t\t<version>${xerces.version}</version>\n\t\t\t\t<optional>true</optional>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.poi</groupId>\n\t\t\t\t<artifactId>poi-scratchpad</artifactId>\n\t\t\t\t<version>${poi.version}</version>\n\t\t\t</dependency>\n\n\t\t\t<!-- excel背景\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.poi</groupId>\n\t\t\t\t<artifactId>ooxml-schemas</artifactId>\n\t\t\t\t<version>1.4</version>\n\t\t\t</dependency>-->\n\n\t\t\t<!-- google 工具类 -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>com.google.guava</groupId>\n\t\t\t\t<artifactId>guava</artifactId>\n\t\t\t\t<version>${guava.version}</version>\n\t\t\t</dependency>\n\n\t\t\t<!-- commons -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.apache.commons</groupId>\n\t\t\t\t<artifactId>commons-lang3</artifactId>\n\t\t\t\t<version>${commons-lang.version}</version>\n\t\t\t</dependency>\n\n\t\t\t<dependency>\n\t\t\t\t<groupId>commons-io</groupId>\n\t\t\t\t<artifactId>commons-io</artifactId>\n\t\t\t\t<version>${commons-io.version}</version>\n\t\t\t</dependency>\n\n\t\t\t<!--日志 -->\n\t\t\t<!-- slf4j -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t<artifactId>slf4j-api</artifactId>\n\t\t\t\t<version>${slf4j.version}</version>\n\t\t\t</dependency>\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.slf4j</groupId>\n\t\t\t\t<artifactId>slf4j-log4j12</artifactId>\n\t\t\t\t<version>${slf4j.version}</version>\n\t\t\t\t<scope>provided</scope>\n\t\t\t</dependency>\n\n\t\t\t<!--spring-web -->\n\t\t\t<dependency>\n\t\t      <groupId>org.springframework</groupId>\n\t\t      <artifactId>spring-webmvc</artifactId>\n\t\t      <version>${spring.version}</version>\n\t\t      <optional>true</optional>\n\t\t    </dependency>\n\t\t\t\n\t\t\t<!-- 模块版本 -->\n\t\t\t<dependency>\n\t\t\t\t<groupId>org.jeecgframework</groupId>\n\t\t\t\t<artifactId>autopoi</artifactId>\n\t\t\t\t<version>${autopoi.version}</version>\n\t\t\t</dependency>\n\t\t</dependencies>\n\t</dependencyManagement>\n\n\t<profiles>\n\t\t<profile>\n\t\t\t<id>jeecg</id>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<source>1.8</source>\n\t\t\t\t\t\t\t<target>1.8</target>\n\t\t\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t\t<!-- Source -->\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t\t<artifactId>maven-source-plugin</artifactId>\n\t\t\t\t\t\t<version>2.2.1</version>\n\t\t\t\t\t\t<executions>\n\t\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t<goal>jar-no-fork</goal>\n\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t</execution>\n\t\t\t\t\t\t</executions>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t\t<distributionManagement>\n\t\t\t\t<repository>\n\t\t\t\t\t<id>jeecg</id>\n\t\t\t\t\t<name>jeecg Repository</name>\n\t\t\t\t\t<url>http://maven.jeecg.com:8090/nexus/content/repositories/jeecg</url>\n\t\t\t\t</repository>\n\t\t\t\t<snapshotRepository>\n\t\t\t\t\t<id>jeecg-snapshots</id>\n\t\t\t\t\t<name>jeecg Snapshot Repository</name>\n\t\t\t\t\t<url>http://maven.jeecg.com:8090/nexus/content/repositories/snapshots/</url>\n\t\t\t\t</snapshotRepository>\n\t\t\t</distributionManagement>\n\t\t</profile>\n\t\t<profile>\n\t\t\t<id>release</id>\n\t\t\t<build>\n\t\t\t\t<plugins>\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<source>1.8</source>\n\t\t\t\t\t\t\t<target>1.8</target>\n\t\t\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t\t<!-- Source -->\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t\t<artifactId>maven-source-plugin</artifactId>\n\t\t\t\t\t\t<version>2.2.1</version>\n\t\t\t\t\t\t<executions>\n\t\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t<goal>jar-no-fork</goal>\n\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t</execution>\n\t\t\t\t\t\t</executions>\n\t\t\t\t\t</plugin>\n\t\t\t\t\t<!-- Javadoc -->\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t\t<artifactId>maven-javadoc-plugin</artifactId>\n\t\t\t\t\t\t<version>2.9.1</version>\n\t\t\t\t\t\t<executions>\n\t\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t\t<phase>package</phase>\n\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t<goal>jar</goal>\n\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t\t\t\t\t\t<additionalparam>-Xdoclint:none</additionalparam>\n\t\t\t\t\t\t\t\t</configuration>\n\t\t\t\t\t\t\t</execution>\n\t\t\t\t\t\t</executions>\n\t\t\t\t\t</plugin>\n\t\t\t\t\t<!--Maven GPG插件用于使用以下配置对组件进行签名-->\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t\t\t<artifactId>maven-gpg-plugin</artifactId>\n\t\t\t\t\t\t<version>1.6</version>\n\t\t\t\t\t\t<executions>\n\t\t\t\t\t\t\t<execution>\n\t\t\t\t\t\t\t\t<id>sign-artifacts</id>\n\t\t\t\t\t\t\t\t<phase>verify</phase>\n\t\t\t\t\t\t\t\t<goals>\n\t\t\t\t\t\t\t\t\t<goal>sign</goal>\n\t\t\t\t\t\t\t\t</goals>\n\t\t\t\t\t\t\t</execution>\n\t\t\t\t\t\t</executions>\n\t\t\t\t\t</plugin>\n\t\t\t\t\t<!--Nexus Staging Maven插件是将组件部署到OSS并将其发布到Central Repository的推荐方法-->\n\t\t\t\t\t<plugin>\n\t\t\t\t\t\t<groupId>org.sonatype.central</groupId>\n\t\t\t\t\t\t<artifactId>central-publishing-maven-plugin</artifactId>\n\t\t\t\t\t\t<version>0.7.0</version>\n\t\t\t\t\t\t<extensions>true</extensions>\n\t\t\t\t\t\t<configuration>\n\t\t\t\t\t\t\t<!-- 注意：这类必须要和Maven配置环境中的server id对应上 -->\n\t\t\t\t\t\t\t<publishingServerId>oss</publishingServerId>\n\t\t\t\t\t\t</configuration>\n\t\t\t\t\t</plugin>\n\t\t\t\t</plugins>\n\t\t\t</build>\n\t\t</profile>\n\t</profiles>\n\n\t<build>\n\t\t<plugins>\n\t\t\t<!-- \n\t\t\t  注意：父 POM 的 packaging=pom，不会编译任何代码\n\t\t\t  此配置仅供子模块继承使用（如果子模块没有自定义配置）\n\t\t\t  实际上各子模块都有自己的 <release> 参数，会覆盖这里的配置\n\t\t\t-->\n\t\t\t<plugin>\n\t\t\t\t<artifactId>maven-compiler-plugin</artifactId>\n\t\t\t\t<version>3.8.1</version>\n\t\t\t\t<configuration>\n\t\t\t\t\t<source>1.8</source>\n\t\t\t\t\t<target>1.8</target>\n\t\t\t\t\t<encoding>UTF-8</encoding>\n\t\t\t\t</configuration>\n\t\t\t</plugin>\n\t\t\t<plugin>\n\t\t\t\t<groupId>org.apache.maven.plugins</groupId>\n\t\t\t\t<artifactId>maven-surefire-plugin</artifactId>\n\t\t\t\t<version>3.0.0-M5</version>\n\t\t\t</plugin>\n\t\t</plugins>\n\t</build>\n\n</project>"
  },
  {
    "path": "打包构建.md",
    "content": "# AutoPOI 打包构建指南\n\n## 📋 JDK 版本要求\n\n不同模块对 JDK 版本有严格要求：\n\n| 模块 | 编译 JDK | 运行 JDK | 说明 |\n|------|---------|---------|------|\n| `autopoi` | **JDK 8+** | JDK 8+ | 核心模块，使用 JDK 8 API |\n| `autopoi-spring-boot-2-starter` | **JDK 8+** | JDK 8+ | Spring Boot 2.x 兼容 |\n| `autopoi-spring-boot-3-starter` | **JDK 17+** | JDK 17+ | Spring Boot 3.x 要求 |\n\n## 🔧 关键配置：`<release>` 参数\n\n项目中使用了 `<release>` 参数来严格控制 API 版本：\n\n```xml\n<plugin>\n    <groupId>org.apache.maven.plugins</groupId>\n    <artifactId>maven-compiler-plugin</artifactId>\n    <configuration>\n        <release>8</release>  <!-- autopoi 和 spring-boot-2-starter -->\n        <!-- 或 -->\n        <release>17</release> <!-- spring-boot-3-starter -->\n    </configuration>\n</plugin>\n```\n\n**这个参数的神奇之处：**\n- ✅ **即使你在 IDEA 中选择了 JDK 17，编译时也只能使用 JDK 8 的 API**\n- ✅ 如果代码中使用了 JDK 9+ 的 API（如 `List.of()`），编译会直接失败\n- ✅ 生成的字节码与指定的 JDK 版本兼容\n\n## 🚀 推荐的打包方式\n\n### 方式一：使用 Maven 命令行（推荐）\n\n```bash\n# 完整构建所有模块\nmvn clean install\n\n# 跳过测试快速构建\nmvn clean install -DskipTests\n\n# 只构建特定模块\ncd autopoi\nmvn clean install\n```\n\n### 方式二：在 IntelliJ IDEA 中打包\n\n#### ⚠️ 重要提示\n\n由于配置了 `<release>` 参数，**你可以用任何 JDK 版本打包，但每个模块会强制使用对应版本的 API：**\n\n| IDEA 选择的 JDK | autopoi | spring-boot-2-starter | spring-boot-3-starter |\n|----------------|---------|---------------------|---------------------|\n| JDK 8          | ✅ JDK 8 API | ✅ JDK 8 API | ❌ 需要 JDK 17+ |\n| JDK 11         | ✅ JDK 8 API | ✅ JDK 8 API | ❌ 需要 JDK 17+ |\n| JDK 17         | ✅ JDK 8 API | ✅ JDK 8 API | ✅ JDK 17 API |\n\n**推荐：使用 JDK 17 打包所有模块！**\n\n#### IDEA 配置步骤\n\n1. 打开 IDEA，点击右上角 Maven 面板\n2. 选择 JDK 17（可以构建所有模块）\n3. 双击 `Lifecycle` -> `install` 或 `package`\n\n## 🧪 验证构建结果\n\n### 验证字节码版本\n\n```bash\n# Windows PowerShell\njavap -v autopoi\\target\\classes\\org\\jeecgframework\\poi\\excel\\ExcelExportUtil.class | Select-String \"major version\"\n# 输出应该是: major version: 52 (JDK 8)\n\njavap -v autopoi-spring-boot-3-starter\\target\\classes\\org\\jeecgframework\\poi\\view\\JeecgEntityExcelView.class | Select-String \"major version\"\n# 输出应该是: major version: 61 (JDK 17)\n```\n\n### 检查依赖\n\n```bash\n# 验证没有 poi-ooxml-schemas\nmvn dependency:tree | findstr poi-ooxml-schemas\n# 应该没有输出\n```\n\n## ❌ 常见错误\n\n### 错误：使用了 JDK 9+ 的 API\n\n```java\n// ❌ 错误：autopoi 模块中使用了 JDK 9 的 API\nList<String> list = List.of(\"a\", \"b\", \"c\");\n\n// ✅ 正确：使用 JDK 8 兼容的写法\nList<String> list = Arrays.asList(\"a\", \"b\", \"c\");\n```\n\n**编译时会报错：**\n```\n[ERROR] cannot find symbol: method of(java.lang.String,java.lang.String,java.lang.String)\n```\n\n**解决方案：** 修改代码，使用 JDK 8 兼容的 API。\n\n## 📦 发布打包\n\n```bash\n# 打包到本地仓库\nmvn clean install\n\n# 打包到 Maven 中央仓库（使用 release profile）\nmvn clean deploy -P release\n```\n\n## 🎯 总结\n\n1. **`<release>` 参数是关键** - 无论用什么 JDK 编译，都只能使用指定版本的 API\n2. **推荐使用 JDK 17 打包** - 可以一次性构建所有模块\n3. **每个模块的 API 受到严格限制** - autopoi 和 spring-boot-2-starter 只能用 JDK 8 API\n\n## 📚 参考文档\n\n- [Maven Compiler Plugin - release parameter](https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#release)\n"
  }
]