diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..17b8aab81 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*.{groovy, java, kt, xml}] +#缩进风格:空格 +indent_style = space +#缩进大小 +indent_size = 4 +#换行符lf +end_of_line = lf +#字符集utf-8 +charset = utf-8 +#是否删除行尾的空格 +trim_trailing_whitespace = true +#是否在文件的最后插入一个空行 +insert_final_newline = true \ No newline at end of file diff --git a/.gitee/ISSUE_TEMPLATE.zh-CN.md b/.gitee/ISSUE_TEMPLATE.zh-CN.md new file mode 100644 index 000000000..8d7f460f9 --- /dev/null +++ b/.gitee/ISSUE_TEMPLATE.zh-CN.md @@ -0,0 +1,9 @@ +# 建议先去看文档 +[快速开始](https://easyexcel.opensource.alibaba.com/docs/current/) 、[常见问题](https://easyexcel.opensource.alibaba.com/qa/) +# 异常代码 +```java + 这里写你的代码 +``` +# 异常提示 +大家尽量把问题一次性描述清楚,然后贴上全部异常,这样方便把问题一次性解决掉。 +# 其他描述 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md new file mode 100644 index 000000000..f30eacb55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -0,0 +1,20 @@ +--- +name: bug +about: 发现一个新的Bug +title: '' +labels: bug +assignees: zhuangjiaju + +--- + +# 建议先去看文档 +[快速开始](https://easyexcel.opensource.alibaba.com/docs/current/) 、[常见问题](https://easyexcel.opensource.alibaba.com/qa/) +# 触发场景描述 + +# 触发Bug的代码 +```java + 这里写代码 +``` +# 提示的异常或者没有达到的效果 +大家尽量把问题一次性描述清楚,然后贴上全部异常,这样方便把问题一次性解决掉。 +至少大家要符合一个原则就是,能让其他人复现出这个问题,如果无法复现,肯定无法解决。 diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 000000000..daf6eacda --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,19 @@ +--- +name: question +about: 有使用疑问,请先阅读“快速开始”,上面无法解决再提交问题。处理完请关闭问题。 +title: '' +labels: help wanted +assignees: '' + +--- + +# 建议先去看文档 +[快速开始](https://easyexcel.opensource.alibaba.com/docs/current/) 、[常见问题](https://easyexcel.opensource.alibaba.com/qa/) +# 异常代码 +```java + 这里写你的代码 +``` +# 异常提示 +大家尽量把问题一次性描述清楚,然后贴上全部异常,这样方便把问题一次性解决掉。 +至少大家要符合一个原则就是,能让其他人复现出这个问题,如果无法复现,肯定无法解决。 +# 问题描述 diff --git a/.github/ISSUE_TEMPLATE/suggest.md b/.github/ISSUE_TEMPLATE/suggest.md new file mode 100644 index 000000000..41884ceb8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/suggest.md @@ -0,0 +1,12 @@ +--- +name: suggest +about: 给出一些建议的能 +title: '' +labels: suggest +assignees: '' + +--- + +# 建议先去看文档 +[快速开始](https://easyexcel.opensource.alibaba.com/docs/current/) 、[常见问题](https://easyexcel.opensource.alibaba.com/qa/) +# 建议描述 diff --git a/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md new file mode 100644 index 000000000..b89c0b867 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md @@ -0,0 +1,10 @@ +## 关联的ISSUE +TODO 请说明关联的异常 +## 问题描述&解决 +TODO 描述下出了什么问题,必要的话提供excel。在PR合并前,审核同学一定会复现问题,然后再PR合并的。 +## 代码注意点 +* 确认已经安装了阿里巴巴代码规约插件:[alibaba-java-coding-guidelines ](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines) +* src/main 下面不要出现中文注释 +* src/test/core && src/test/demo 下面要新增测试案例请严格参照其他的,包括注释等等什么都不要错 +* 如果只是自己测试 全部加入到 src/test/temp +* 如果改动量较大(5+文件)尽量先和作者沟通,这个风险很大 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..f4e30b607 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +# +# Copyright 2009-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Java CI + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + java: [ 8, 11, 17, 21] + distribution: [ 'adopt' ] + fail-fast: false + max-parallel: 4 + name: Test JDK ${{ matrix.java }} + + steps: + - uses: actions/checkout@main + - name: Set up JDK + uses: actions/setup-java@main + with: + java-version: ${{ matrix.java }} + distribution: ${{ matrix.distribution }} + - name: Cache local Maven repository + uses: actions/cache@main + with: + path: ~/.m2/repository + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-m2 + - name: Chmod + run: chmod +x mvnw + - name: Test with Maven + if: ${{ matrix.java == '8' }} + run: ./mvnw test -B -Dmaven.test.skip=false + - name: Test with Maven + if: ${{ matrix.java != '8' }} + run: ./mvnw test -B -Dmaven.test.skip=false -DargLine="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED" + - name: Maven Build + run: ./mvnw install -B -V + - name: Java Doc + run: ./mvnw javadoc:javadoc diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..d6bba3908 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,55 @@ +# +# Copyright 2009-2021 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +name: Publish package to the Maven Central Repository + + + +on: + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Check out Git repository + uses: actions/checkout@main + - name: Install Java and Maven + uses: actions/setup-java@main + with: + java-version: 8 + distribution: 'adopt' + server-id: ossrh + server-username: MAVEN_USERNAME + server-password: MAVEN_PASSWORD + - name: Cache local Maven repository + uses: actions/cache@main + with: + path: ~/.m2/repository + key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-m2 + - id: install-secret-key + name: Install GPG secret key + run: | + cat <(echo -e "${{ secrets.GPG_PRIVATE_KEY }}") | gpg --batch --import + - name: Publish package + run: | + mvn --batch-mode -Dgpg.passphrase=${{ secrets.GPG_PASSPHRASE }} clean deploy -Dmaven.test.skip=true -Dmaven.javadoc.skip=false -Dgpg.skip=false + env: + MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} + MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/sync2gitee.yml b/.github/workflows/sync2gitee.yml new file mode 100644 index 000000000..5b48b08bf --- /dev/null +++ b/.github/workflows/sync2gitee.yml @@ -0,0 +1,26 @@ +# 通过 Github action, 在仓库的每一次 commit 后自动同步到 Gitee 上 +name: Mirror the Github organization repos to Gitee +on: [push] + +jobs: + repo-sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + with: + persist-credentials: false + - name: Mirror the Github organization repos to Gitee. + uses: Yikun/hub-mirror-action@master + with: + # 必选,需要同步的 Github 这里记住选择的是仓库 或者账号 而不是具体的项目 + src: github/alibaba + # 必选,需要同步到的 Gitee 这里记住选择的是仓库 或者账号 而不是具体的项目 + dst: gitee/easyexcel + # 必选,Gitee公钥对应的私钥,https://gitee.com/profile/sshkeys + dst_key: ${{ secrets.GITEE_PRIVATE_KEY }} + # 必选,Gitee对应的用于创建仓库的token,https://gitee.com/profile/personal_access_tokens + dst_token: ${{ secrets.GITEE_TOKEN }} + # 如果是组织,指定组织即可,默认为用户 user + account_type: org + # 需要同步的仓库里面的项目 + static_list: "easyexcel" \ No newline at end of file diff --git a/.gitignore b/.gitignore index fac9ab5f2..93472b7d0 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ target/ *.iws antx.properties output/ +.flattened-pom.xml +dependency-reduced-pom.xml diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 000000000..c1dd12f17 Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..e83fa6959 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,18 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..997623f6e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +## 前言 +非常感谢您愿意协助EasyExcel的开发,EasyExcel成长离不开大家的贡献。但是为了合作的更有效率,希望我们在贡献代码的时候能按照如下约定。 +## 提前沟通 +尽量把自己的想法和实现思路提前沟通,可以通过issue,钉钉,QQ都可以,可能很多问题我们内部已经讨论过,由于各种原因后续不会支持,但是您这边又开发好了,这样容易浪费您的时间。 +## 代码规范 +请先安装阿里巴巴代码规约插件!!!https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines +目前代码规范已经集成了自动校验,然后源代码尽量不要有中文注释。在新增功能的时候,尽量注意补充junit。core代表每次travis-ci都会跑的测试案例,然后demo用于对外看到,temp里面随便写。 +## 提交分支 +建议提交到最新的版本号.x上面,比如 3.x之类的版本,为了方便其他同学阅读源代码,所以目前的思路是master和maven center的最新版本代码保持一致,然后您提交过来的代码我们可能会稍微做一些修改。所以提交到开发分支会比较好。fork也可以直接fork该分支。 diff --git a/README.md b/README.md index 728fb233e..01e74cb0c 100644 --- a/README.md +++ b/README.md @@ -1,151 +1,188 @@ -# JAVA解析Excel工具easyexcel -Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便 -## 相关文档 -* [关于软件](/abouteasyexcel.md) -* [快速使用](/quickstart.md) -* [常见问题](/problem.md) -* [更新记事](/update.md) -* [English-README](/easyexcel_en.md) -## 二方包 + + +EasyExcel +====================== +[![Build Status](https://github.com/alibaba/easyexcel/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/alibaba/easyexcel/actions/workflows/ci.yml?query=branch%3Amaster) +[![Maven central](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel) +[![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) +[![](https://img.shields.io/badge/EasyExcel-%E6%9F%A5%E7%9C%8B%E8%B4%A1%E7%8C%AE%E6%8E%92%E8%A1%8C%E6%A6%9C-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=easyexcel) + +# 维护公告 +尊敬的EasyExcel用户们: + +首先,我们想表达最深的谢意,感谢您长期以来对EasyExcel的信任与支持。在这段旅程中,EasyExcel有幸陪伴众多开发者和企业共同成长,见证了无数数据处理任务的高效完成。 + +近期,我们注意到市场上出现了更多优秀的数据处理工具和解决方案,为用户提供了更丰富的选择。为了确保每位用户都能享受到最佳的体验和服务,我们决定采取一项重要措施:EasyExcel将逐步进入维护模式,并给予用户充足的时间评估并迁移到其他产品。同时,我们也欢迎并乐于见到社区内外分享关于同类优秀产品的使用心得和迁移经验,共同促进技术生态的健康发展。 + +请放心,即使进入维护模式,我们仍会确保EasyExcel的基本功能稳定运行,会进行Bug修复,但不再主动新增功能。我们相信,通过大家的共同努力,每一段代码、每一个项目都将继续在各自的领域发光发热,服务于更广泛的用户群体。 +再次感谢您对EasyExcel的支持与理解!期待在技术的广阔天地里,我们能以新的形式再度携手,共创辉煌。 + +祝您在未来的工作与学习中一切顺利! + +阿里巴巴EasyExcel团队敬上 + +# 新手必读 + +* 官方网站:[https://easyexcel.opensource.alibaba.com/](https://easyexcel.opensource.alibaba.com/) +* github地址:[https://github.com/alibaba/easyexcel](https://github.com/alibaba/easyexcel) +* gitee地址:[https://gitee.com/easyexcel/easyexcel](https://gitee.com/easyexcel/easyexcel) + + +# JAVA解析Excel工具 + +Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。 +easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便 + + +# 16M内存23秒读取75M(46W行25列)的Excel(3.2.1+版本) + +当然还有[极速模式](https://easyexcel.opensource.alibaba.com/qa/read#%E5%BC%80%E5%90%AF%E6%80%A5%E9%80%9F%E6%A8%A1%E5%BC%8F) +能更快,但是内存占用会在100M多一点 +![img](img/readme/large.png) + +# 最新版本 + +```xml com.alibaba easyexcel - {latestVersion} + 4.0.3 +``` -## 最新版本:1.1.2-beta4 -## 维护者 -姬朋飞(玉霄) -## 快速开始 -### 读Excel -测试代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/ReadTest.java) +# 帮忙点个⭐Star -读07版小于1000行数据返回List> -``` -List data = EasyExcelFactory.read(inputStream, new Sheet(1, 0)); -``` -读07版小于1000行数据返回List -``` -List data = EasyExcelFactory.read(inputStream, new Sheet(2, 1,JavaModel.class)); -``` -读07版大于1000行数据返回List> -``` -ExcelListener excelListener = new ExcelListener(); -EasyExcelFactory.readBySax(inputStream, new Sheet(1, 1), excelListener); -``` +开源不易,如果觉得EasyExcel对您的工作还是有帮助的话,请帮忙在github star +的右上角点个⭐Star,您的支持是使EasyExcel变得更好最大的动力。 -读07版大于1000行数据返回List -``` -ExcelListener excelListener = new ExcelListener(); -EasyExcelFactory.readBySax(inputStream, new Sheet(2, 1,JavaModel.class), excelListener); -``` -读03版方法同上 -### 写Excel -测试代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/WriteTest.java](/src/test/java/com/alibaba/easyexcel/test/WriteTest.java) -没有模板 -```OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); -ExcelWriter writer = EasyExcelFactory.getWriter(out); - -//写第一个sheet, sheet1 数据全是List 无模型映射关系 -Sheet sheet1 = new Sheet(1, 3); -sheet1.setSheetName("第一个sheet"); -//设置列宽 设置每列的宽度 -Map columnWidth = new HashMap(); -columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); -sheet1.setColumnWidthMap(columnWidth); -sheet1.setHead(createTestListStringHead()); -//or 设置自适应宽度 -//sheet1.setAutoWidth(Boolean.TRUE); -writer.write1(createTestListObject(), sheet1); - -//写第二个sheet sheet2 模型上打有表头的注解,合并单元格 -Sheet sheet2 = new Sheet(2, 3, JavaModel1.class, "第二个sheet", null); -sheet2.setTableStyle(createTableStyle()); -writer.write(createTestListJavaMode(), sheet2); - -//写第三个sheet包含多个table情况 -Sheet sheet3 = new Sheet(3, 0); -sheet3.setSheetName("第三个sheet"); -Table table1 = new Table(1); -table1.setHead(createTestListStringHead()); -writer.write1(createTestListObject(), sheet3, table1); - -//写sheet2 模型上打有表头的注解 -Table table2 = new Table(2); -table2.setTableStyle(createTableStyle()); -table2.setClazz(JavaModel1.class); -writer.write(createTestListJavaMode(), sheet3, table2); - -//关闭资源 -writer.finish(); -out.close(); -``` -有模板 -```InputStream inputStream = new BufferedInputStream(new FileInputStream("/Users/jipengfei/temp.xlsx")); -OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); -ExcelWriter writer = EasyExcelFactory.getWriterWithTemp(inputStream,out,ExcelTypeEnum.XLSX,true); - -//写第一个sheet, sheet1 数据全是List 无模型映射关系 -Sheet sheet1 = new Sheet(1, 3); -sheet1.setSheetName("第一个sheet"); -//设置列宽 设置每列的宽度 -Map columnWidth = new HashMap(); -columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); -sheet1.setColumnWidthMap(columnWidth); -sheet1.setHead(createTestListStringHead()); -//or 设置自适应宽度 -//sheet1.setAutoWidth(Boolean.TRUE); -writer.write1(createTestListObject(), sheet1); - -//写第二个sheet sheet2 模型上打有表头的注解,合并单元格 -Sheet sheet2 = new Sheet(2, 3, JavaModel1.class, "第二个sheet", null); -sheet2.setTableStyle(createTableStyle()); -writer.write(createTestListJavaMode(), sheet2); - -//写第三个sheet包含多个table情况 -Sheet sheet3 = new Sheet(3, 0); -sheet3.setSheetName("第三个sheet"); -Table table1 = new Table(1); -table1.setHead(createTestListStringHead()); -writer.write1(createTestListObject(), sheet3, table1); - -//写sheet2 模型上打有表头的注解 -Table table2 = new Table(2); -table2.setTableStyle(createTableStyle()); -table2.setClazz(JavaModel1.class); -writer.write(createTestListJavaMode(), sheet3, table2); - -//关闭资源 -writer.finish(); -out.close(); +# 如何获取帮助 + +## 优先建议自己通过文档来解决问题 + +* [快速开始](https://easyexcel.opensource.alibaba.com/docs/current/) +* [常见问题](https://easyexcel.opensource.alibaba.com/docs/qa/) +* [API](https://easyexcel.opensource.alibaba.com/docs/current/api/) + +## 其次建议通过`issues`来解决解决问题 + +可以尝试在以下2个链接搜索问题,如果不存在可以尝试创建`issue`。 + +* 去 [github](https://github.com/alibaba/easyexcel/issues) 搜索`issues` +* 去 [gitee](https://gitee.com/easyexcel/easyexcel/issues) 搜索`issues` + +通过 `issues` 解决问题,可以给后面遇到相同问题的同学查看,所以比较推荐这种方式。 +不管`github`、`gitee`都会定期有人回答您的问题,比较紧急可以在提完`issue`以后在钉钉群艾特群主并发送`issue`地址帮忙解决。 +`QQ` 公司不让用,有时候也会去看,但是核心肯定还是在钉钉。 + +## 也可以加入钉钉&QQ群来解决问题 + +加入钉钉或QQ群,看完公告可以获得帮助 。 +比较推荐钉钉群,`QQ` 公司不让用,当然QQ群也会有热心网友帮忙解决。 +[QQ1群(已满): 662022184](https://jq.qq.com/?_wv=1027&k=1T21jJxh) +[QQ2群(已满): 1097936804](https://jq.qq.com/?_wv=1027&k=j5zEy6Xl) +[QQ3群(已满): 453928496](https://qm.qq.com/cgi-bin/qm/qr?k=e2ULsA5A0GldhV2CXJ8sIbAyu9I6qqs7&jump_from=webapi) +[QQ4群(已满): 496594404](https://qm.qq.com/cgi-bin/qm/qr?k=e_aVG1Q7gi0PJUBkbrUGAgbeO3kUEInK&jump_from=webapi) +[QQ5群(已满): 451925680](https://jq.qq.com/?_wv=1027&k=6VHhvxyf) +[QQ6群(已满): 784741035](https://jq.qq.com/?_wv=1027&k=BbLBIo9P) +[QQ7群(已满): 667889383](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XdTLw3Z3pr63VT0IkyoY-2t25TG7WxbG&authKey=gQKvTXipsjfUO1aNfL9zdHTfOmkqC6E%2BQ2zDg2jym8h3qXuQ7RtkpeAHeg9I4UhL&noverify=0&group_code=667889383) +[QQ8群: 113968681](https://qm.qq.com/q/qwfl5RRBAG) +[钉钉1群(已满): 21960511](https://qr.dingtalk.com/action/joingroup?code=v1,k1,cchz6k12ci9B08NNqhNRFGXocNVHrZtW0kaOtTKg/Rk=&_dt_no_comment=1&origin=11) +[钉钉2群(已满): 32796397](https://qr.dingtalk.com/action/joingroup?code=v1,k1,jyU9GtEuNU5S0QTyklqYcYJ8qDZtUuTPMM7uPZTS8Hs=&_dt_no_comment=1&origin=11) +[钉钉3群(已满): 33797247](https://qr.dingtalk.com/action/joingroup?code=v1,k1,3UGlEScTGQaHpW2cIRo+gkxJ9EVZ5fz26M6nW3uFP30=&_dt_no_comment=1&origin=11) +[钉钉4群(已满): 33491624](https://qr.dingtalk.com/action/joingroup?code=v1,k1,V14Pb65Too70rQkEaJ9ohb6lZBZbtp6jIL/q9EWh9vA=&_dt_no_comment=1&origin=11) +[钉钉5群(已满): 32134498](https://h5.dingtalk.com/circle/healthCheckin.html?dtaction=os&corpId=dingb9fa1325d9dccc3ecac589edd02f1650&5233a=71a83&cbdbhh=qwertyuiop) +[钉钉6群(已满): 34707941](https://h5.dingtalk.com/circle/healthCheckin.html?dtaction=os&corpId=dingcf68008a1d443ac012d5427bdb061b7a&6ae36c3d-0c80-4=22398493-6c2a-4&cbdbhh=qwertyuiop) +[钉钉7群(已满): 35235427](https://h5.dingtalk.com/circle/healthCheckin.html?dtaction=os&corpId=ding532b9018c06c7fc8660273c4b78e6440&167fb=ed003&cbdbhh=qwertyuiop) +[钉钉8群(已满): 44752220](https://h5.dingtalk.com/circle/healthCheckin.html?dtaction=os&corpId=dingea96808beee421693fd4ba7542d6e5da&0380092a-fa46=a6a40905-7951&cbdbhh=qwertyuiop) +[钉钉9群(已满): 11045002277](https://h5.dingtalk.com/circle/healthCheckin.html?dtaction=os&corpId=dinge338d2215891c0459c13cd6b2cb108a6&6972d=b92f9&cbdbhh=qwertyuiop) +[钉钉10群(已满): 27360019755](https://qr.dingtalk.com/action/joingroup?code=v1,k1,v25LHn2liWmrWUKlkhIzOTcK7s7onp/sZP8mO5oIYSs=&_dt_no_comment=1&origin=11) +[钉钉11群(已满):24330026964](https://qr.dingtalk.com/action/joingroup?code=v1,k1,63PjvTncu81oQ3X6XmGEJqnwQHCQxi/jaVlbUStq79o=&_dt_no_comment=1&origin=11) +[钉钉12群(已满):27210038956](https://qr.dingtalk.com/action/joingroup?code=v1,k1,3mKi7VTGlYO+IsDX5n7sYYm2Qrlm220kMBPsJFzKRis=&_dt_no_comment=1&origin=11) +[钉钉13群:83695000992](https://qr.dingtalk.com/action/joingroup?code=v1,k1,2JFUbWfxD1fGiq7LRW+mYjcK7s7onp/s1ZqOvfzkFGE=&_dt_no_comment=1&origin=11) + + +# 维护者 + +姬朋飞(玉霄)、庄家钜 + +# 快速开始 + +## 读Excel + +demo代码地址:[https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java](https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java) +详细文档地址:[https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read](https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read) + +```java + /** + * 最简单的读 + *

1. 创建excel对应的实体对象 参照{@link DemoData} + *

2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + *

3. 直接读即可 + */ + @Test + public void simpleRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); + } ``` -### web下载实例写法 +## 写Excel + +demo代码地址:[https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java](https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java) +详细文档地址:[https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write](https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write) + +```java + /** + * 最简单的写 + *

1. 创建excel对应的实体对象 参照{@link com.alibaba.easyexcel.test.demo.write.DemoData} + *

2. 直接写即可 + */ + @Test + public void simpleWrite() { + String fileName=TestFileUtil.getPath()+"write"+System.currentTimeMillis()+".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName,DemoData.class).sheet("模板").doWrite(data()); + } ``` -public class Down { - @GetMapping("/a.htm") - public void cooperation(HttpServletRequest request, HttpServletResponse response) { - ServletOutputStream out = response.getOutputStream(); - ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX, true); - String fileName = new String(("UserInfo " + new SimpleDateFormat("yyyy-MM-dd").format(new Date())) - .getBytes(), "UTF-8"); - Sheet sheet1 = new Sheet(1, 0); - sheet1.setSheetName("第一个sheet"); - writer.write0(getListString(), sheet1); - writer.finish(); - response.setContentType("multipart/form-data"); + +## web上传、下载 + +demo代码地址:[https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) + +```java + /** + * 文件下载(失败了会返回一个有部分数据的Excel) + *

+ * 1. 创建excel对应的实体对象 参照{@link DownloadData} + *

+ * 2. 设置返回的 参数 + *

+ * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 + */ + @GetMapping("download") + public void download(HttpServletResponse response) throws IOException { + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); - response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx"); - out.flush(); - } + // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 + String fileName=URLEncoder.encode("测试","UTF-8").replaceAll("\\+","%20"); + response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx"); + EasyExcel.write(response.getOutputStream(),DownloadData.class).sheet("模板").doWrite(data()); + } + + /** + * 文件上传 + *

1. 创建excel对应的实体对象 参照{@link UploadData} + *

2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} + *

3. 直接读即可 + */ + @PostMapping("upload") + @ResponseBody + public String upload(MultipartFile file)throws IOException{ + EasyExcel.read(file.getInputStream(),UploadData.class,new UploadDataListener(uploadDAO)).sheet().doRead(); + return"success"; } -} ``` -### 联系我们 -有问题阿里同事可以通过钉钉找到我,阿里外同学可以通过git留言。其他技术非技术相关的也欢迎一起探讨。 -### 招聘&交流 -阿里巴巴新零售事业部--诚招JAVA资深开发、技术专家。有意向可以微信联系,简历可以发我邮箱jipengfei.jpf@alibaba-inc.com -或者加微信:18042000709 - - diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 000000000..10135fed8 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,157 @@ +EasyExcel +====================== +[![Build Status](https://github.com/alibaba/easyexcel/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/alibaba/easyexcel/actions/workflows/ci.yml?query=branch%3Amaster) +[![Maven central](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.alibaba/easyexcel) +[![License](http://img.shields.io/:license-apache-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) +[![](https://img.shields.io/badge/EasyExcel-Check%20Your%20Contribution-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=easyexcel) + +[Communication Group 1 in QQ(Full): 662022184](https://jq.qq.com/?_wv=1027&k=1T21jJxh) +[Communication Group 2 in QQ(Full): 1097936804](https://jq.qq.com/?_wv=1027&k=j5zEy6Xl) +[Communication Group 3 in QQ(Full): 453928496](https://qm.qq.com/cgi-bin/qm/qr?k=e2ULsA5A0GldhV2CXJ8sIbAyu9I6qqs7&jump_from=webapi) +[Communication Group 4 in QQ: 496594404](https://qm.qq.com/cgi-bin/qm/qr?k=e_aVG1Q7gi0PJUBkbrUGAgbeO3kUEInK&jump_from=webapi) +[Communication Group 1 in DingTalk(Full): 21960511](https://qr.dingtalk.com/action/joingroup?code=v1,k1,cchz6k12ci9B08NNqhNRFGXocNVHrZtW0kaOtTKg/Rk=&_dt_no_comment=1&origin=11) +[Communication Group 2 in DingTalk(Full): 32796397](https://qr.dingtalk.com/action/joingroup?code=v1,k1,jyU9GtEuNU5S0QTyklqYcYJ8qDZtUuTPMM7uPZTS8Hs=&_dt_no_comment=1&origin=11) +[Communication Group 3 in DingTalk(Full): 33797247](https://qr.dingtalk.com/action/joingroup?code=v1,k1,3UGlEScTGQaHpW2cIRo+gkxJ9EVZ5fz26M6nW3uFP30=&_dt_no_comment=1&origin=11) +[Communication Group 4 in DingTalk(Full): 33491624](https://qr.dingtalk.com/action/joingroup?code=v1,k1,V14Pb65Too70rQkEaJ9ohb6lZBZbtp6jIL/q9EWh9vA=&_dt_no_comment=1&origin=11) +[Communication Group 5 in DingTalk: 32134498](https://h5.dingtalk.com/circle/healthCheckin.html?dtaction=os&corpId=dingb9fa1325d9dccc3ecac589edd02f1650&5233a=71a83&cbdbhh=qwertyuiop) +[Official Website: https://yuque.com/easyexcel](https://www.yuque.com/easyexcel/doc/easyexcel) + +[FAQ](https://www.yuque.com/easyexcel/faq) +#### It is recommended to join a DingTalk group + +# EasyExcel, a java toolkit for parsing Excel easily +There are several java frameworks or toolkit which can parse and generate Excel, such as Apache POI or jxl. But they all have some difficulties to handle problems like excessive memory usage. Apache POI framework has a set of SAX mode API can fix some memory overflow problems at some extent, but it still has some flaws. For example, the unzipping and the storage of the unzipping of Excel file in version 07 are done in memory, so the memory consumption is still very high. The EasyExcel toolkit rewrites the logic of POI for parsing Excel version 07. One 3 megabytes Excel file parsed with POI still requires about 100M memory, which can be reduced to a few megabyte by using EasyExcel instead. And yes, there is no memory overflow for even larger excel with EasyExcel. EasyExcel version 03 depends on POI SAX model and does model transformation/encapsulation in the upper layer to make it simpler and more convenient for users. + +## Using EasyExcel version 3.0.2+, a machine with 64M RAM can read a 75 megabyte Excel file containing 460,000 rows and 25 columns in 20 seconds +Of course, there is also a very fast mode can be faster, but the memory consumption will be a little more than 100M +![img](img/readme/large.png) + +## Version support +* EasyExcel version 2+ works on Java7 or Java6 +* EasyExcel version 3+ works on Java8 or java8+ +### About version upgrade +* It is not recommended upgrading across major versions, especially across 2 major versions. +* There are some incompatibilities in upgrading from version 2+ to version 3+. + * Using a custom interceptor to modify the style can cause problems, even if it does not compile with errors. + * When reading the Excel file, the `invoke` function will throw an exception, there will not be an additional layer of `ExcelAnalysisException` wrapped here, and it will not compile with errors. + * Style and other annotations involving `boolean` or some enumeration values have been changed, adding the default value. The compiler will report an error, just change the annotation. +* It is recommended to re-test the relevant functions after upgrading across major versions. + +### Latest Version +```xml + + com.alibaba + easyexcel + 3.0.2 + +``` + +## Advertising space +### Alibaba New Retail Business Department Recruitment +Alibaba New Retail Business Department sincerely recruit JAVA senior development, technical experts. If you are interested, you can contact us by WeChat, or send your Resume to my email jipengfei.jpf@alibaba-inc.com. +### EasyExcel personnel recruitment +Anyone who wants to participate in this project can apply, mainly responsible for answering questions in the communication group and dealing with the issues. Of course, you can also do some PR. +Since there is no material reward for participating in the open source project, and the current maintainers are also maintaining the project in their spare time. So people who want to join this project need to be persistent, not just on a whim. +The requirements are as follows: +* There are certain java coding skills & good coding habits +* Understand the read&write principles of EasyExcel +* Has passion for open source projects +* Be able to do things consistently for a long time +* Your job is not so busy + +## Related Documents +* [Quick Start](https://www.yuque.com/easyexcel/doc/easyexcel) +* [About Us](/abouteasyexcel.md) +* [Update Notes](/update.md) +* [Code Contribution](https://www.yuque.com/easyexcel/doc/contribute) + +## Maintainers +姬朋飞(玉霄)、庄家钜、怀宇 +## Quick Start +### Read Excel File +DEMO:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/demo/read/ReadTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java) + +```java + /** + * The easiest way to read Excel file using EasyExcel toolkit + * + *

+ * 1. Create an entity object, such as {@link DemoData}, each property of the entity object corresponds to a specific field in any row of Excel. + * 2. When reading each row of an Excel file, create a callback listener for the corresponding row. Refer to{@link DemoDataListener} + * 3. Invoke the read function + *

+ */ + @Test + public void simpleRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // Specify which entity object class to use to read the Excel content. The file stream will close automatically after reading the first sheet of Excel. + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); + } +``` + +### Write Excel File +DEMO:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java) +```java + /** + * The easiest way to write Excel file using EasyExcel toolkit + * + *

+ * 1. Create an entity object, refer to{@link com.alibaba.easyexcel.test.demo.write.DemoData}. + * Each property of the entity object corresponds to a specific field of Excel + * 2. Invoke write function + *

+ */ + @Test + public void simpleWrite() { + String fileName = TestFileUtil.getPath() + "write" + System.currentTimeMillis() + ".xlsx"; + // Specify which entity object class to use to write Excel, it will write to the first sheet of Excel with the name template. Then the file stream will be closed automatically. + // With version 03, just pass in the excelType parameter + EasyExcel.write(fileName, DemoData.class).sheet("template").doWrite(data()); + } +``` + +### File Uploading&Downloading +DEMO:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) +```java + /** + * File downloading + * + * Note: returns an Excel with partial data if it fails + * + *

+ * 1. Create an entity object, refer to{@link DownloadData}. + * Each property of the entity object corresponds to a specific field of Excel + * 2. Specify the returned properties + * 3. Invoke write function, then the OutputStream is automatically closed when it ends. + *

+ */ + @GetMapping("download") + public void download(HttpServletResponse response) throws IOException { + // Using swagger may cause some problems, please use your browser directly or use postman to invoke this + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + // URLEncoder.encode function can prevent Chinese garbled code + String fileName = URLEncoder.encode("test", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("template").doWrite(data()); + } + + /** + * File uploading + * + *

+ * 1. Create an entity object, refer to{@link UploadData} + * Each property of the entity object corresponds to a specific field of Excel + * 2. When reading each row of an Excel file, create a callback listener for the corresponding row. Refer to{@link UploadDataListener} + * 3. Invoke read function + *

+ */ + @PostMapping("upload") + @ResponseBody + public String upload(MultipartFile file) throws IOException { + EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead(); + return "success"; + } +``` +### Contact Us +If you have any questions, Alibaba colleagues can find me in DingTalk, and others can leave messages here. All related questions are well welcomed. diff --git a/abouteasyexcel.md b/abouteasyexcel.md deleted file mode 100644 index 8c06a2a9c..000000000 --- a/abouteasyexcel.md +++ /dev/null @@ -1,53 +0,0 @@ -# easyexcel要去解决的问题 - -## Excel读写时候内存溢出 - -虽然POI是目前使用最多的用来做excel解析的框架,但这个框架并不那么完美。大部分使用POI都是使用他的userModel模式。userModel的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,相对比较好理解。然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉上百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc。 - -## 其他开源框架使用复杂 - -对POI有过深入了解的估计才知道原来POI还有SAX模式。但SAX模式相对比较复杂,excel有03和07两种版本,两个版本数据存储方式截然不同,sax解析方式也各不一样。想要了解清楚这两种解析方式,才去写代码测试,估计两天时间是需要的。再加上即使解析完,要转换到自己业务模型还要很多繁琐的代码。总体下来感觉至少需要三天,由于代码复杂,后续维护成本巨大。 - -## 其他开源框架存在一些BUG修复不及时 - -由于我们的系统大多数都是大并发的情况下运行的,在大并发情况下,我们会发现poi存在一些bug,如果让POI团队修复估计遥遥无期了。所以我们在easyexcel对这些bug做了规避。 -如下一段报错就是在大并发情况下poi抛的一个异常。 -``` -Caused by: java.io.IOException: Could not create temporary directory '/home/admin/dio2o/.default/temp/poifiles' - at org.apache.poi.util.DefaultTempFileCreationStrategy.createTempDirectory(DefaultTempFileCreationStrategy.java:93) ~[poi-3.15.jar:3.15] - at org.apache.poi.util.DefaultTempFileCreationStrategy.createPOIFilesDirectory(DefaultTempFileCreationStrategy.java:82) ~[poi-3.15.jar:3.15] -``` -报错地方poi源码如下 -``` - private void createTempDirectory(File directory) throws IOException { - if (!(directory.exists() || directory.mkdirs()) || !directory.isDirectory()) { - throw new IOException("Could not create temporary directory '" + directory + "'"); - } - } -``` -仔细看代码容易明白如果在并发情况下,如果2个线程同时判断directory.exists()都 为false,但执行directory.mkdirs()如果一些线程优先执行完,另外一个线程就会返回false。最终 throw new IOException("Could not create temporary directory '" + directory + "'")。针对这个问题easyexcel在写文件时候首先创建了该临时目录,避免poi在并发创建时候引起不该有的报错。 - -## Excel格式分析格式分析 - -- xls是Microsoft Excel2007前excel的文件存储格式,实现原理是基于微软的ole db是微软com组件的一种实现,本质上也是一个微型数据库,由于微软的东西很多不开源,另外也已经被淘汰,了解它的细节意义不大,底层的编程都是基于微软的com组件去开发的。 -- xlsx是Microsoft Excel2007后excel的文件存储格式,实现是基于openXml和zip技术。这种存储简单,安全传输方便,同时处理数据也变的简单。 -- csv 我们可以理解为纯文本文件,可以被excel打开。他的格式非常简单,解析起来和解析文本文件一样。 - -## 核心原理 - -写有大量数据的xlsx文件时,POI为我们提供了SXSSFWorkBook类来处理,这个类的处理机制是当内存中的数据条数达到一个极限数量的时候就flush这部分数据,再依次处理余下的数据,这个在大多数场景能够满足需求。 -读有大量数据的文件时,使用WorkBook处理就不行了,因为POI对文件是先将文件中的cell读入内存,生成一个树的结构(针对Excel中的每个sheet,使用TreeMap存储sheet中的行)。如果数据量比较大,则同样会产生java.lang.OutOfMemoryError: Java heap space错误。POI官方推荐使用“XSSF and SAX(event API)”方式来解决。 -分析清楚POI后要解决OOM有3个关键。 - -### 1、文件解压文件读取通过文件形式 - -![屏幕快照 2018-01-22 上午8.52.08.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/e3a3500014c95f7118d8c200a51acab4.png) - -### 2、避免将全部全部数据一次加载到内存 - -采用sax模式一行一行解析,并将一行的解析结果以观察者的模式通知处理。 -![基础模板1 (2).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/82bb195ac62532963b2364d2e4da23e5.png) - -### 3、抛弃不重要的数据 - -Excel解析时候会包含样式,字体,宽度等数据,但这些数据是我们不关系的,如果将这部分数据抛弃可以大大降低内存使用。Excel中数据如下Style占了相当大的空间。 \ No newline at end of file diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 000000000..13635841f --- /dev/null +++ b/docs/API.md @@ -0,0 +1,65 @@ +# 详细参数介绍 +## 关于常见类解析 +* EasyExcel 入口类,用于构建开始各种操作 +* ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个 +* ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个 +* ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据 +* WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据 +* 所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel...sheet()方法之前作用域是整个sheet,之后针对单个sheet +## 读 +### 注解 +* `ExcelProperty` 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。 +* `ExcelIgnore` 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段 +* `DateTimeFormat` 日期转换,用`String`去接收excel日期格式的数据会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat` +* `NumberFormat` 数字转换,用`String`去接收excel数字格式的数据会调用这个注解。里面的`value`参照`java.text.DecimalFormat` +* `ExcelIgnoreUnannotated` 默认不加`ExcelProperty` 的注解的都会参与读写,加了不会参与 +### 参数 +#### 通用参数 +`ReadWorkbook`,`ReadSheet` 都会有的参数,如果为空,默认使用上级。 +* `converter` 转换器,默认加载了很多转换器。也可以自定义。 +* `readListener` 监听器,在读取数据的过程中会不断的调用监听器。 +* `headRowNumber` 需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。 +* `head` 与`clazz`二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。 +* `clazz` 与`head`二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。 +* `autoTrim` 字符串、表头等数据自动trim +* `password` 读的时候是否需要使用密码 +#### ReadWorkbook(理解成excel对象)参数 +* `excelType` 当前excel的类型 默认会自动判断 +* `inputStream` 与`file`二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用`file`参数。因为使用了`inputStream` easyexcel会帮忙创建临时文件,最终还是`file` +* `file` 与`inputStream`二选一。读取文件的文件。 +* `autoCloseStream` 自动关闭流。 +* `readCache` 默认小于5M用 内存,超过5M会使用 `EhCache`,这里不建议使用这个参数。 +#### ReadSheet(就是excel的一个Sheet)参数 +* `sheetNo` 需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet +* `sheetName` 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配 +## 写 +### 注解 +* `ExcelProperty` index 指定写到第几列,默认根据成员变量排序。`value`指定写入的名称,默认成员变量的名字,多个`value`可以参照快速开始中的复杂头 +* `ExcelIgnore` 默认所有字段都会写入excel,这个注解会忽略这个字段 +* `DateTimeFormat` 日期转换,将`Date`写到excel会调用这个注解。里面的`value`参照`java.text.SimpleDateFormat` +* `NumberFormat` 数字转换,用`Number`写excel会调用这个注解。里面的`value`参照`java.text.DecimalFormat` +* `ExcelIgnoreUnannotated` 默认不加`ExcelProperty` 的注解的都会参与读写,加了不会参与 +### 参数 +#### 通用参数 +`WriteWorkbook`,`WriteSheet` ,`WriteTable`都会有的参数,如果为空,默认使用上级。 +* `converter` 转换器,默认加载了很多转换器。也可以自定义。 +* `writeHandler` 写的处理器。可以实现`WorkbookWriteHandler`,`SheetWriteHandler`,`RowWriteHandler`,`CellWriteHandler`,在写入excel的不同阶段会调用 +* `relativeHeadRowIndex` 距离多少行后开始。也就是开头空几行 +* `needHead` 是否导出头 +* `head` 与`clazz`二选一。写入文件的头列表,建议使用class。 +* `clazz` 与`head`二选一。写入文件的头对应的class,也可以使用注解。 +* `autoTrim` 字符串、表头等数据自动trim +#### WriteWorkbook(理解成excel对象)参数 +* `excelType` 当前excel的类型 默认`xlsx` +* `outputStream` 与`file`二选一。写入文件的流 +* `file` 与`outputStream`二选一。写入的文件 +* `templateInputStream` 模板的文件流 +* `templateFile` 模板文件 +* `autoCloseStream` 自动关闭流。 +* `password` 写的时候是否需要使用密码 +* `useDefaultStyle` 写的时候是否是使用默认头 +#### WriteSheet(就是excel的一个Sheet)参数 +* `sheetNo` 需要写入的编码。默认0 +* `sheetName` 需要些的Sheet名称,默认同`sheetNo` +#### WriteTable(就把excel的一个Sheet,一块区域看一个table)参数 +* `tableNo` 需要写入的编码。默认0 \ No newline at end of file diff --git a/docs/LARGEREAD.md b/docs/LARGEREAD.md new file mode 100644 index 000000000..861472771 --- /dev/null +++ b/docs/LARGEREAD.md @@ -0,0 +1,26 @@ +# 10M以上文件读取说明 +03版没有办法处理,相对内存占用大很多。excel 07版本有个共享字符串[共享字符串](https://docs.microsoft.com/zh-cn/office/open-xml/working-with-the-shared-string-table)的概念,这个会非常占用内存,如果全部读取到内存的话,大概是excel文件的大小的3-10倍,所以easyexcel用存储文件的,然后再反序列化去读取的策略来节约内存。当然需要通过文件反序列化以后,效率会降低,大概降低30-50%(不一定,也看命中率,可能会超过100%) +## 如果对读取效率感觉还能接受,就用默认的,永久占用(单个excel读取整个过程)一般不会超过50M(大概率就30M),剩下临时的GC会很快回收 +## 默认大文件处理 +默认大文件处理会自动判断,共享字符串5M以下会使用内存存储,大概占用15-50M的内存,超过5M则使用文件存储,然后文件存储也要设置多内存M用来存放临时的共享字符串,默认20M。除了共享字符串占用内存外,其他占用较少,所以可以预估10M,所以默认大概30M就能读取一个超级大的文件。 +## 根据实际需求配置内存 +想自定义设置,首先要确定你大概愿意花多少内存来读取一个超级大的excel,比如希望读取excel最多占用100M内存(是读取过程中永久占用,新生代马上回收的不算),那就设置使用文件来存储共享字符串的大小判断为20M(小于20M存内存,大于存临时文件),然后设置文件存储时临时共享字符串占用内存大小90M差不多 +### 如果最大文件条数也就十几二十万,然后excel也就是十几二十M,而且不会有很高的并发,并且内存也较大 +```java + // 强制使用内存存储,这样大概一个20M的excel使用150M(很多临时对象,所以100M会一直GC)的内存 +// 这样效率会比上面的复杂的策略高很多 + // 这里再说明下 就是加了个readCache(new MapCache()) 参数而已,其他的参照其他demo写 这里没有写全 + EasyExcel.read().readCache(new MapCache()); +``` +### 对并发要求较高,而且都是经常有超级大文件 +```java + // 第一个参数的意思是 多少M共享字符串以后 采用文件存储 单位MB 默认5M +// 第二个参数 文件存储时,内存存放多少M缓存数据 默认20M +// 比如 你希望用100M内存(这里说的是解析过程中的永久占用,临时对象不算)来解析excel,前面算过了 大概是 20M+90M 所以设置参数为:20 和 90 + // 这里再说明下 就是加了个readCacheSelector(new SimpleReadCacheSelector(5, 20))参数而已,其他的参照其他demo写 这里没有写全 +EasyExcel.read().readCacheSelector(new SimpleReadCacheSelector(5, 20)); +``` +### 关于maxCacheActivateSize 也就是前面第二个参数的详细说明 +easyexcel在使用文件存储的时候,会把共享字符串拆分成1000条一批,然后放到文件存储。然后excel来读取共享字符串大概率是按照顺序的,所以默认20M的1000条的数据放在内存,命中后直接返回,没命中去读文件。所以不能设置太小,太小了,很难命中,一直去读取文件,太大了的话会占用过多的内存。 +### 如何判断 maxCacheActivateSize是否需要调整 +开启debug日志会输出`Already put :4000000` 最后一次输出,大概可以得出值为400W,然后看`Cache misses count:4001`得到值为4K,400W/4K=1000 这代表已经`maxCacheActivateSize` 已经非常合理了。如果小于500 问题就非常大了,500到1000 应该都还行。 diff --git a/easyexcel-core/pom.xml b/easyexcel-core/pom.xml new file mode 100644 index 000000000..93d013cc5 --- /dev/null +++ b/easyexcel-core/pom.xml @@ -0,0 +1,40 @@ + + + 4.0.0 + + com.alibaba + easyexcel-parent + ${revision} + ../pom.xml + + + https://github.com/alibaba/easyexcel + jar + easyexcel-core + easyexcel-core + + + + com.alibaba + easyexcel-support + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + + org.apache.commons + commons-csv + + + org.ehcache + ehcache + + + diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/EasyExcel.java b/easyexcel-core/src/main/java/com/alibaba/excel/EasyExcel.java new file mode 100644 index 000000000..432c5138f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/EasyExcel.java @@ -0,0 +1,8 @@ +package com.alibaba.excel; + +/** + * This is actually {@link EasyExcelFactory}, and short names look better. + * + * @author jipengfei + */ +public class EasyExcel extends EasyExcelFactory {} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/EasyExcelFactory.java b/easyexcel-core/src/main/java/com/alibaba/excel/EasyExcelFactory.java new file mode 100644 index 000000000..80c408513 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/EasyExcelFactory.java @@ -0,0 +1,356 @@ +package com.alibaba.excel; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; + +import com.alibaba.excel.read.builder.ExcelReaderBuilder; +import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; +import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder; +import com.alibaba.excel.write.builder.ExcelWriterTableBuilder; + +/** + * Reader and writer factory class + * + * @author jipengfei + */ +public class EasyExcelFactory { + + /** + * Build excel the write + * + * @return + */ + public static ExcelWriterBuilder write() { + return new ExcelWriterBuilder(); + } + + /** + * Build excel the write + * + * @param file File to write + * @return Excel writer builder + */ + public static ExcelWriterBuilder write(File file) { + return write(file, null); + } + + /** + * Build excel the write + * + * @param file File to write + * @param head Annotate the class for configuration information + * @return Excel writer builder + */ + public static ExcelWriterBuilder write(File file, Class head) { + ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder(); + excelWriterBuilder.file(file); + if (head != null) { + excelWriterBuilder.head(head); + } + return excelWriterBuilder; + } + + /** + * Build excel the write + * + * @param pathName File path to write + * @return Excel writer builder + */ + public static ExcelWriterBuilder write(String pathName) { + return write(pathName, null); + } + + /** + * Build excel the write + * + * @param pathName File path to write + * @param head Annotate the class for configuration information + * @return Excel writer builder + */ + public static ExcelWriterBuilder write(String pathName, Class head) { + ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder(); + excelWriterBuilder.file(pathName); + if (head != null) { + excelWriterBuilder.head(head); + } + return excelWriterBuilder; + } + + /** + * Build excel the write + * + * @param outputStream Output stream to write + * @return Excel writer builder + */ + public static ExcelWriterBuilder write(OutputStream outputStream) { + return write(outputStream, null); + } + + /** + * Build excel the write + * + * @param outputStream Output stream to write + * @param head Annotate the class for configuration information. + * @return Excel writer builder + */ + public static ExcelWriterBuilder write(OutputStream outputStream, Class head) { + ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder(); + excelWriterBuilder.file(outputStream); + if (head != null) { + excelWriterBuilder.head(head); + } + return excelWriterBuilder; + } + + /** + * Build excel the writerSheet + * + * @return Excel sheet writer builder + */ + public static ExcelWriterSheetBuilder writerSheet() { + return writerSheet(null, null); + } + + /** + * Build excel the writerSheet + * + * @param sheetNo Index of sheet,0 base. + * @return Excel sheet writer builder. + */ + public static ExcelWriterSheetBuilder writerSheet(Integer sheetNo) { + return writerSheet(sheetNo, null); + } + + /** + * Build excel the 'writerSheet' + * + * @param sheetName The name of sheet. + * @return Excel sheet writer builder. + */ + public static ExcelWriterSheetBuilder writerSheet(String sheetName) { + return writerSheet(null, sheetName); + } + + /** + * Build excel the 'writerSheet' + * + * @param sheetNo Index of sheet,0 base. + * @param sheetName The name of sheet. + * @return Excel sheet writer builder. + */ + public static ExcelWriterSheetBuilder writerSheet(Integer sheetNo, String sheetName) { + ExcelWriterSheetBuilder excelWriterSheetBuilder = new ExcelWriterSheetBuilder(); + if (sheetNo != null) { + excelWriterSheetBuilder.sheetNo(sheetNo); + } + if (sheetName != null) { + excelWriterSheetBuilder.sheetName(sheetName); + } + return excelWriterSheetBuilder; + } + + /** + * Build excel the writerTable + * + * @return Excel table writer builder. + */ + public static ExcelWriterTableBuilder writerTable() { + return writerTable(null); + } + + /** + * Build excel the 'writerTable' + * + * @param tableNo Index of table,0 base. + * @return Excel table writer builder. + */ + public static ExcelWriterTableBuilder writerTable(Integer tableNo) { + ExcelWriterTableBuilder excelWriterTableBuilder = new ExcelWriterTableBuilder(); + if (tableNo != null) { + excelWriterTableBuilder.tableNo(tableNo); + } + return excelWriterTableBuilder; + } + + /** + * Build excel the read + * + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read() { + return new ExcelReaderBuilder(); + } + + /** + * Build excel the read + * + * @param file File to read. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(File file) { + return read(file, null, null); + } + + /** + * Build excel the read + * + * @param file File to read. + * @param readListener Read listener. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(File file, ReadListener readListener) { + return read(file, null, readListener); + } + + /** + * Build excel the read + * + * @param file File to read. + * @param head Annotate the class for configuration information. + * @param readListener Read listener. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(File file, Class head, ReadListener readListener) { + ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder(); + excelReaderBuilder.file(file); + if (head != null) { + excelReaderBuilder.head(head); + } + if (readListener != null) { + excelReaderBuilder.registerReadListener(readListener); + } + return excelReaderBuilder; + } + + /** + * Build excel the read + * + * @param pathName File path to read. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(String pathName) { + return read(pathName, null, null); + } + + /** + * Build excel the read + * + * @param pathName File path to read. + * @param readListener Read listener. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(String pathName, ReadListener readListener) { + return read(pathName, null, readListener); + } + + /** + * Build excel the read + * + * @param pathName File path to read. + * @param head Annotate the class for configuration information. + * @param readListener Read listener. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(String pathName, Class head, ReadListener readListener) { + ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder(); + excelReaderBuilder.file(pathName); + if (head != null) { + excelReaderBuilder.head(head); + } + if (readListener != null) { + excelReaderBuilder.registerReadListener(readListener); + } + return excelReaderBuilder; + } + + /** + * Build excel the read + * + * @param inputStream Input stream to read. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(InputStream inputStream) { + return read(inputStream, null, null); + } + + /** + * Build excel the read + * + * @param inputStream Input stream to read. + * @param readListener Read listener. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(InputStream inputStream, ReadListener readListener) { + return read(inputStream, null, readListener); + } + + /** + * Build excel the read + * + * @param inputStream Input stream to read. + * @param head Annotate the class for configuration information. + * @param readListener Read listener. + * @return Excel reader builder. + */ + public static ExcelReaderBuilder read(InputStream inputStream, Class head, ReadListener readListener) { + ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder(); + excelReaderBuilder.file(inputStream); + if (head != null) { + excelReaderBuilder.head(head); + } + if (readListener != null) { + excelReaderBuilder.registerReadListener(readListener); + } + return excelReaderBuilder; + } + + /** + * Build excel the 'readSheet' + * + * @return Excel sheet reader builder. + */ + public static ExcelReaderSheetBuilder readSheet() { + return readSheet(null, null); + } + + /** + * Build excel the 'readSheet' + * + * @param sheetNo Index of sheet,0 base. + * @return Excel sheet reader builder. + */ + public static ExcelReaderSheetBuilder readSheet(Integer sheetNo) { + return readSheet(sheetNo, null); + } + + /** + * Build excel the 'readSheet' + * + * @param sheetName The name of sheet. + * @return Excel sheet reader builder. + */ + public static ExcelReaderSheetBuilder readSheet(String sheetName) { + return readSheet(null, sheetName); + } + + /** + * Build excel the 'readSheet' + * + * @param sheetNo Index of sheet,0 base. + * @param sheetName The name of sheet. + * @return Excel sheet reader builder. + */ + public static ExcelReaderSheetBuilder readSheet(Integer sheetNo, String sheetName) { + ExcelReaderSheetBuilder excelReaderSheetBuilder = new ExcelReaderSheetBuilder(); + if (sheetNo != null) { + excelReaderSheetBuilder.sheetNo(sheetNo); + } + if (sheetName != null) { + excelReaderSheetBuilder.sheetName(sheetName); + } + return excelReaderSheetBuilder; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/ExcelReader.java b/easyexcel-core/src/main/java/com/alibaba/excel/ExcelReader.java new file mode 100644 index 000000000..83b46f405 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/ExcelReader.java @@ -0,0 +1,123 @@ +package com.alibaba.excel; + +import java.io.Closeable; +import java.util.Arrays; +import java.util.List; + +import com.alibaba.excel.analysis.ExcelAnalyser; +import com.alibaba.excel.analysis.ExcelAnalyserImpl; +import com.alibaba.excel.analysis.ExcelReadExecutor; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.ReadWorkbook; + +import lombok.extern.slf4j.Slf4j; + +/** + * Excel readers are all read in event mode. + * + * @author jipengfei + */ +@Slf4j +public class ExcelReader implements Closeable { + + /** + * Analyser + */ + private final ExcelAnalyser excelAnalyser; + + public ExcelReader(ReadWorkbook readWorkbook) { + excelAnalyser = new ExcelAnalyserImpl(readWorkbook); + } + + /** + * Parse all sheet content by default + * + * @deprecated lease use {@link #readAll()} + */ + @Deprecated + public void read() { + readAll(); + } + + /*** + * Parse all sheet content by default + */ + public void readAll() { + excelAnalyser.analysis(null, Boolean.TRUE); + } + + /** + * Parse the specified sheet,SheetNo start from 0 + * + * @param readSheet Read sheet + */ + public ExcelReader read(ReadSheet... readSheet) { + return read(Arrays.asList(readSheet)); + } + + /** + * Read multiple sheets. + * + * @param readSheetList + * @return + */ + public ExcelReader read(List readSheetList) { + excelAnalyser.analysis(readSheetList, Boolean.FALSE); + return this; + } + + /** + * Context for the entire execution process + * + * @return + */ + public AnalysisContext analysisContext() { + return excelAnalyser.analysisContext(); + } + + /** + * Current executor + * + * @return + */ + public ExcelReadExecutor excelExecutor() { + return excelAnalyser.excelExecutor(); + } + + /** + * @return + * @deprecated please use {@link #analysisContext()} + */ + @Deprecated + public AnalysisContext getAnalysisContext() { + return analysisContext(); + } + + /** + * Complete the entire read file.Release the cache and close stream. + */ + public void finish() { + if (excelAnalyser != null) { + excelAnalyser.finish(); + } + } + + @Override + public void close() { + finish(); + } + + /** + * Prevents calls to {@link #finish} from freeing the cache + * + */ + @Override + protected void finalize() { + try { + finish(); + } catch (Throwable e) { + log.warn("Destroy object failed", e); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/ExcelWriter.java b/easyexcel-core/src/main/java/com/alibaba/excel/ExcelWriter.java new file mode 100644 index 000000000..3c0743840 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/ExcelWriter.java @@ -0,0 +1,172 @@ +package com.alibaba.excel; + +import java.io.Closeable; +import java.util.Collection; +import java.util.function.Supplier; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.write.ExcelBuilder; +import com.alibaba.excel.write.ExcelBuilderImpl; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.fill.FillConfig; + +import lombok.extern.slf4j.Slf4j; + +/** + * Excel Writer This tool is used to write value out to Excel via POI. This object can perform the following two + * functions. + * + *
+ *    1. Create a new empty Excel workbook, write the value to the stream after the value is filled.
+ *    2. Edit existing Excel, write the original Excel file, or write it to other places.}
+ * 
+ * + * @author jipengfei + */ +@Slf4j +public class ExcelWriter implements Closeable { + + private final ExcelBuilder excelBuilder; + + /** + * Create new writer + * + * @param writeWorkbook + */ + public ExcelWriter(WriteWorkbook writeWorkbook) { + excelBuilder = new ExcelBuilderImpl(writeWorkbook); + } + + /** + * Write data to a sheet + * + * @param data Data to be written + * @param writeSheet Write to this sheet + * @return this current writer + */ + public ExcelWriter write(Collection data, WriteSheet writeSheet) { + return write(data, writeSheet, null); + } + + /** + * Write data to a sheet + * + * @param supplier Data to be written + * @param writeSheet Write to this sheet + * @return this current writer + */ + public ExcelWriter write(Supplier> supplier, WriteSheet writeSheet) { + return write(supplier.get(), writeSheet, null); + } + + /** + * Write value to a sheet + * + * @param data Data to be written + * @param writeSheet Write to this sheet + * @param writeTable Write to this table + * @return this + */ + public ExcelWriter write(Collection data, WriteSheet writeSheet, WriteTable writeTable) { + excelBuilder.addContent(data, writeSheet, writeTable); + return this; + } + + /** + * Write value to a sheet + * + * @param supplier Data to be written + * @param writeSheet Write to this sheet + * @param writeTable Write to this table + * @return this + */ + public ExcelWriter write(Supplier> supplier, WriteSheet writeSheet, WriteTable writeTable) { + excelBuilder.addContent(supplier.get(), writeSheet, writeTable); + return this; + } + + /** + * Fill value to a sheet + * + * @param data + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, WriteSheet writeSheet) { + return fill(data, null, writeSheet); + } + + /** + * Fill value to a sheet + * + * @param data + * @param fillConfig + * @param writeSheet + * @return + */ + public ExcelWriter fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + excelBuilder.fill(data, fillConfig, writeSheet); + return this; + } + + /** + * Fill value to a sheet + * + * @param supplier + * @param writeSheet + * @return + */ + public ExcelWriter fill(Supplier supplier, WriteSheet writeSheet) { + return fill(supplier.get(), null, writeSheet); + } + + /** + * Fill value to a sheet + * + * @param supplier + * @param fillConfig + * @param writeSheet + * @return + */ + public ExcelWriter fill(Supplier supplier, FillConfig fillConfig, WriteSheet writeSheet) { + excelBuilder.fill(supplier.get(), fillConfig, writeSheet); + return this; + } + + /** + * Close IO + */ + public void finish() { + if (excelBuilder != null) { + excelBuilder.finish(false); + } + } + + /** + * The context of the entire writing process + * + * @return + */ + public WriteContext writeContext() { + return excelBuilder.writeContext(); + } + + @Override + public void close() { + finish(); + } + + /** + * Prevents calls to {@link #finish} from freeing the cache + */ + @Override + protected void finalize() { + try { + finish(); + } catch (Throwable e) { + log.warn("Destroy object failed", e); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java new file mode 100644 index 000000000..83de6ac57 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java @@ -0,0 +1,43 @@ +package com.alibaba.excel.analysis; + +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * Excel file analyser + * + * @author jipengfei + */ +public interface ExcelAnalyser { + /** + * parse the sheet + * + * @param readSheetList + * Which sheets you need to read. + * @param readAll + * The readSheetList parameter is ignored, and all sheets are read. + */ + void analysis(List readSheetList, Boolean readAll); + + /** + * Complete the entire read file.Release the cache and close stream + */ + void finish(); + + /** + * Acquisition excel executor + * + * @return Excel file Executor + */ + ExcelReadExecutor excelExecutor(); + + /** + * get the analysis context. + * + * @return analysis context + */ + AnalysisContext analysisContext(); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java new file mode 100644 index 000000000..8421a160e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java @@ -0,0 +1,236 @@ +package com.alibaba.excel.analysis; + +import com.alibaba.excel.analysis.csv.CsvExcelReadExecutor; +import com.alibaba.excel.analysis.v03.XlsSaxAnalyser; +import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.context.csv.CsvReadContext; +import com.alibaba.excel.context.csv.DefaultCsvReadContext; +import com.alibaba.excel.context.xls.DefaultXlsReadContext; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.context.xlsx.DefaultXlsxReadContext; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; +import com.alibaba.excel.util.StringUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.poifs.crypt.Decryptor; +import org.apache.poi.poifs.filesystem.DocumentFactoryHelper; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.List; + +/** + * @author jipengfei + */ +public class ExcelAnalyserImpl implements ExcelAnalyser { + private static final Logger LOGGER = LoggerFactory.getLogger(ExcelAnalyserImpl.class); + + private AnalysisContext analysisContext; + + private ExcelReadExecutor excelReadExecutor; + /** + * Prevent multiple shutdowns + */ + private boolean finished = false; + + public ExcelAnalyserImpl(ReadWorkbook readWorkbook) { + try { + choiceExcelExecutor(readWorkbook); + } catch (RuntimeException e) { + finish(); + throw e; + } catch (Throwable e) { + finish(); + throw new ExcelAnalysisException(e); + } + } + + private void choiceExcelExecutor(ReadWorkbook readWorkbook) throws Exception { + ExcelTypeEnum excelType = ExcelTypeEnum.valueOf(readWorkbook); + switch (excelType) { + case XLS: + POIFSFileSystem poifsFileSystem; + if (readWorkbook.getFile() != null) { + poifsFileSystem = new POIFSFileSystem(readWorkbook.getFile()); + } else { + poifsFileSystem = new POIFSFileSystem(readWorkbook.getInputStream()); + } + // So in encrypted excel, it looks like XLS but it's actually XLSX + if (poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)) { + InputStream decryptedStream = null; + try { + decryptedStream = DocumentFactoryHelper + .getDecryptedStream(poifsFileSystem.getRoot().getFileSystem(), readWorkbook.getPassword()); + XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX); + analysisContext = xlsxReadContext; + excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, decryptedStream); + return; + } finally { + IOUtils.closeQuietly(decryptedStream); + // as we processed the full stream already, we can close the filesystem here + // otherwise file handles are leaked + poifsFileSystem.close(); + } + } + if (readWorkbook.getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(readWorkbook.getPassword()); + } + XlsReadContext xlsReadContext = new DefaultXlsReadContext(readWorkbook, ExcelTypeEnum.XLS); + xlsReadContext.xlsReadWorkbookHolder().setPoifsFileSystem(poifsFileSystem); + analysisContext = xlsReadContext; + excelReadExecutor = new XlsSaxAnalyser(xlsReadContext); + break; + case XLSX: + XlsxReadContext xlsxReadContext = new DefaultXlsxReadContext(readWorkbook, ExcelTypeEnum.XLSX); + analysisContext = xlsxReadContext; + excelReadExecutor = new XlsxSaxAnalyser(xlsxReadContext, null); + break; + case CSV: + CsvReadContext csvReadContext = new DefaultCsvReadContext(readWorkbook, ExcelTypeEnum.CSV); + analysisContext = csvReadContext; + excelReadExecutor = new CsvExcelReadExecutor(csvReadContext); + break; + default: + break; + } + } + + @Override + public void analysis(List readSheetList, Boolean readAll) { + try { + if (!readAll && CollectionUtils.isEmpty(readSheetList)) { + throw new IllegalArgumentException("Specify at least one read sheet."); + } + analysisContext.readWorkbookHolder().setParameterSheetDataList(readSheetList); + analysisContext.readWorkbookHolder().setReadAll(readAll); + try { + excelReadExecutor.execute(); + } catch (ExcelAnalysisStopException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Custom stop!"); + } + } + } catch (RuntimeException e) { + finish(); + throw e; + } catch (Throwable e) { + finish(); + throw new ExcelAnalysisException(e); + } + } + + @Override + public void finish() { + if (finished) { + return; + } + finished = true; + if (analysisContext == null || analysisContext.readWorkbookHolder() == null) { + return; + } + ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); + + Throwable throwable = null; + + try { + if (readWorkbookHolder.getReadCache() != null) { + readWorkbookHolder.getReadCache().destroy(); + } + } catch (Throwable t) { + throwable = t; + } + try { + if ((readWorkbookHolder instanceof XlsxReadWorkbookHolder) + && ((XlsxReadWorkbookHolder) readWorkbookHolder).getOpcPackage() != null) { + ((XlsxReadWorkbookHolder) readWorkbookHolder).getOpcPackage().revert(); + } + } catch (Throwable t) { + throwable = t; + } + try { + if ((readWorkbookHolder instanceof XlsReadWorkbookHolder) + && ((XlsReadWorkbookHolder) readWorkbookHolder).getPoifsFileSystem() != null) { + ((XlsReadWorkbookHolder) readWorkbookHolder).getPoifsFileSystem().close(); + } + } catch (Throwable t) { + throwable = t; + } + + // close csv. + // https://github.com/alibaba/easyexcel/issues/2309 + try { + if ((readWorkbookHolder instanceof CsvReadWorkbookHolder) + && ((CsvReadWorkbookHolder) readWorkbookHolder).getCsvParser() != null + && analysisContext.readWorkbookHolder().getAutoCloseStream()) { + ((CsvReadWorkbookHolder) readWorkbookHolder).getCsvParser().close(); + } + } catch (Throwable t) { + throwable = t; + } + + try { + if (analysisContext.readWorkbookHolder().getAutoCloseStream() + && readWorkbookHolder.getInputStream() != null) { + readWorkbookHolder.getInputStream().close(); + } + } catch (Throwable t) { + throwable = t; + } + try { + if (readWorkbookHolder.getTempFile() != null) { + FileUtils.delete(readWorkbookHolder.getTempFile()); + } + } catch (Throwable t) { + throwable = t; + } + + clearEncrypt03(); + + removeThreadLocalCache(); + + if (throwable != null) { + throw new ExcelAnalysisException("Can not close IO.", throwable); + } + } + + private void removeThreadLocalCache() { + NumberDataFormatterUtils.removeThreadLocalCache(); + DateUtils.removeThreadLocalCache(); + ClassUtils.removeThreadLocalCache(); + } + + private void clearEncrypt03() { + if (StringUtils.isEmpty(analysisContext.readWorkbookHolder().getPassword()) + || !ExcelTypeEnum.XLS.equals(analysisContext.readWorkbookHolder().getExcelType())) { + return; + } + Biff8EncryptionKey.setCurrentUserPassword(null); + } + + @Override + public ExcelReadExecutor excelExecutor() { + return excelReadExecutor; + } + + @Override + public AnalysisContext analysisContext() { + return analysisContext; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java new file mode 100644 index 000000000..085caab81 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/ExcelReadExecutor.java @@ -0,0 +1,25 @@ +package com.alibaba.excel.analysis; + +import java.util.List; + +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * Excel file Executor + * + * @author Jiaju Zhuang + */ +public interface ExcelReadExecutor { + + /** + * Returns the actual sheet in excel + * + * @return Actual sheet in excel + */ + List sheetList(); + + /** + * Read the sheet. + */ + void execute(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/csv/CsvExcelReadExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/csv/CsvExcelReadExecutor.java new file mode 100644 index 000000000..5a901b5b1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/csv/CsvExcelReadExecutor.java @@ -0,0 +1,148 @@ +package com.alibaba.excel.analysis.csv; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.analysis.ExcelReadExecutor; +import com.alibaba.excel.context.csv.CsvReadContext; +import com.alibaba.excel.enums.ByteOrderMarkEnum; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.exception.ExcelAnalysisStopSheetException; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; +import com.alibaba.excel.util.SheetUtils; +import com.alibaba.excel.util.StringUtils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.io.input.BOMInputStream; + +/** + * read executor + * + * @author zhuangjiaju + */ +@Slf4j +public class CsvExcelReadExecutor implements ExcelReadExecutor { + + private final List sheetList; + private final CsvReadContext csvReadContext; + + public CsvExcelReadExecutor(CsvReadContext csvReadContext) { + this.csvReadContext = csvReadContext; + sheetList = new ArrayList<>(); + ReadSheet readSheet = new ReadSheet(); + sheetList.add(readSheet); + readSheet.setSheetNo(0); + } + + @Override + public List sheetList() { + return sheetList; + } + + @Override + public void execute() { + CSVParser csvParser; + try { + csvParser = csvParser(); + csvReadContext.csvReadWorkbookHolder().setCsvParser(csvParser); + } catch (IOException e) { + throw new ExcelAnalysisException(e); + } + for (ReadSheet readSheet : sheetList) { + readSheet = SheetUtils.match(readSheet, csvReadContext); + if (readSheet == null) { + continue; + } + try { + csvReadContext.currentSheet(readSheet); + + int rowIndex = 0; + + for (CSVRecord record : csvParser) { + dealRecord(record, rowIndex++); + } + } catch (ExcelAnalysisStopSheetException e) { + if (log.isDebugEnabled()) { + log.debug("Custom stop!", e); + } + } + + // The last sheet is read + csvReadContext.analysisEventProcessor().endSheet(csvReadContext); + } + } + + private CSVParser csvParser() throws IOException { + CsvReadWorkbookHolder csvReadWorkbookHolder = csvReadContext.csvReadWorkbookHolder(); + CSVFormat csvFormat = csvReadWorkbookHolder.getCsvFormat(); + ByteOrderMarkEnum byteOrderMark = ByteOrderMarkEnum.valueOfByCharsetName( + csvReadContext.csvReadWorkbookHolder().getCharset().name()); + if (csvReadWorkbookHolder.getMandatoryUseInputStream()) { + return buildCsvParser(csvFormat, csvReadWorkbookHolder.getInputStream(), byteOrderMark); + } + if (csvReadWorkbookHolder.getFile() != null) { + return buildCsvParser(csvFormat, Files.newInputStream(csvReadWorkbookHolder.getFile().toPath()), + byteOrderMark); + } + return buildCsvParser(csvFormat, csvReadWorkbookHolder.getInputStream(), byteOrderMark); + } + + private CSVParser buildCsvParser(CSVFormat csvFormat, InputStream inputStream, ByteOrderMarkEnum byteOrderMark) + throws IOException { + if (byteOrderMark == null) { + return csvFormat.parse( + new InputStreamReader(inputStream, csvReadContext.csvReadWorkbookHolder().getCharset())); + } + return csvFormat.parse(new InputStreamReader(new BOMInputStream(inputStream, byteOrderMark.getByteOrderMark()), + csvReadContext.csvReadWorkbookHolder().getCharset())); + } + + private void dealRecord(CSVRecord record, int rowIndex) { + Map cellMap = new LinkedHashMap<>(); + Iterator cellIterator = record.iterator(); + int columnIndex = 0; + Boolean autoTrim = csvReadContext.currentReadHolder().globalConfiguration().getAutoTrim(); + while (cellIterator.hasNext()) { + String cellString = cellIterator.next(); + ReadCellData readCellData = new ReadCellData<>(); + readCellData.setRowIndex(rowIndex); + readCellData.setColumnIndex(columnIndex); + + // csv is an empty string of whether ,, is read or ,"", + if (StringUtils.isNotBlank(cellString)) { + readCellData.setType(CellDataTypeEnum.STRING); + readCellData.setStringValue(autoTrim ? cellString.trim() : cellString); + } else { + readCellData.setType(CellDataTypeEnum.EMPTY); + } + cellMap.put(columnIndex++, readCellData); + } + + RowTypeEnum rowType = MapUtils.isEmpty(cellMap) ? RowTypeEnum.EMPTY : RowTypeEnum.DATA; + ReadRowHolder readRowHolder = new ReadRowHolder(rowIndex, rowType, + csvReadContext.readWorkbookHolder().getGlobalConfiguration(), cellMap); + csvReadContext.readRowHolder(readRowHolder); + + csvReadContext.csvReadSheetHolder().setCellMap(cellMap); + csvReadContext.csvReadSheetHolder().setRowIndex(rowIndex); + csvReadContext.analysisEventProcessor().endRow(csvReadContext); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/IgnorableXlsRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/IgnorableXlsRecordHandler.java new file mode 100644 index 000000000..cb7e4a8c5 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/IgnorableXlsRecordHandler.java @@ -0,0 +1,8 @@ +package com.alibaba.excel.analysis.v03; + +/** + * Need to ignore the current handler without reading the current sheet. + * + * @author Jiaju Zhuang + */ +public interface IgnorableXlsRecordHandler extends XlsRecordHandler {} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java new file mode 100644 index 000000000..14565e5eb --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsListSheetListener.java @@ -0,0 +1,64 @@ +package com.alibaba.excel.analysis.v03; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder; +import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; +import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; +import org.apache.poi.hssf.eventusermodel.HSSFListener; +import org.apache.poi.hssf.eventusermodel.HSSFRequest; +import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BoundSheetRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.exception.ExcelAnalysisException; + +/** + * In some cases, you need to know the number of sheets in advance and only read the file once in advance. + * + * @author Jiaju Zhuang + */ +public class XlsListSheetListener implements HSSFListener { + private final XlsReadContext xlsReadContext; + private static final Map XLS_RECORD_HANDLER_MAP = new HashMap(); + + static { + XLS_RECORD_HANDLER_MAP.put(BOFRecord.sid, new BofRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BoundSheetRecord.sid, new BoundSheetRecordHandler()); + } + + public XlsListSheetListener(XlsReadContext xlsReadContext) { + this.xlsReadContext = xlsReadContext; + xlsReadContext.xlsReadWorkbookHolder().setNeedReadSheet(Boolean.FALSE); + } + + @Override + public void processRecord(Record record) { + XlsRecordHandler handler = XLS_RECORD_HANDLER_MAP.get(record.getSid()); + if (handler == null) { + return; + } + handler.processRecord(xlsReadContext, record); + } + + public void execute() { + MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); + HSSFListener formatListener = new FormatTrackingHSSFListener(listener); + HSSFEventFactory factory = new HSSFEventFactory(); + HSSFRequest request = new HSSFRequest(); + EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener = + new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); + request.addListenerForAllRecords(workbookBuildingListener); + try { + factory.processWorkbookEvents(request, xlsReadContext.xlsReadWorkbookHolder().getPoifsFileSystem()); + } catch (IOException e) { + throw new ExcelAnalysisException(e); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java new file mode 100644 index 000000000..ca9218ac8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsRecordHandler.java @@ -0,0 +1,29 @@ +package com.alibaba.excel.analysis.v03; + +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Intercepts handle xls reads. + * + * @author Dan Zheng + */ +public interface XlsRecordHandler { + /** + * Whether to support + * + * @param xlsReadContext + * @param record + * @return + */ + boolean support(XlsReadContext xlsReadContext, Record record); + + /** + * Processing record + * + * @param xlsReadContext + * @param record + */ + void processRecord(XlsReadContext xlsReadContext, Record record); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java new file mode 100644 index 000000000..27e6f0101 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java @@ -0,0 +1,177 @@ +package com.alibaba.excel.analysis.v03; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.hssf.eventusermodel.EventWorkbookBuilder; +import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; +import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; +import org.apache.poi.hssf.eventusermodel.HSSFListener; +import org.apache.poi.hssf.eventusermodel.HSSFRequest; +import org.apache.poi.hssf.eventusermodel.MissingRecordAwareHSSFListener; +import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingRowDummyRecord; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BlankRecord; +import org.apache.poi.hssf.record.BoolErrRecord; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.EOFRecord; +import org.apache.poi.hssf.record.FormulaRecord; +import org.apache.poi.hssf.record.HyperlinkRecord; +import org.apache.poi.hssf.record.IndexRecord; +import org.apache.poi.hssf.record.LabelRecord; +import org.apache.poi.hssf.record.LabelSSTRecord; +import org.apache.poi.hssf.record.MergeCellsRecord; +import org.apache.poi.hssf.record.NoteRecord; +import org.apache.poi.hssf.record.NumberRecord; +import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.RKRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.SSTRecord; +import org.apache.poi.hssf.record.StringRecord; +import org.apache.poi.hssf.record.TextObjectRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.analysis.ExcelReadExecutor; +import com.alibaba.excel.analysis.v03.handlers.BlankRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BofRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BoolErrRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.BoundSheetRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.DummyRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.EofRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.FormulaRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.HyperlinkRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.IndexRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.LabelRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.LabelSstRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.MergeCellsRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.NoteRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.NumberRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.ObjRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.RkRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.SstRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.StringRecordHandler; +import com.alibaba.excel.analysis.v03.handlers.TextObjectRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.exception.ExcelAnalysisStopSheetException; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; + +/** + * A text extractor for Excel files. + *

+ * Returns the textual content of the file, suitable for indexing by something like Lucene, but not really intended for + * display to the user. + *

+ * + *

+ * To turn an excel file into a CSV or similar, then see the XLS2CSVmra example + *

+ * + * + * @author jipengfei + * @see XLS2CSVmra + */ +@Slf4j +public class XlsSaxAnalyser implements HSSFListener, ExcelReadExecutor { + + private static final Logger LOGGER = LoggerFactory.getLogger(XlsSaxAnalyser.class); + private static final short DUMMY_RECORD_SID = -1; + private final XlsReadContext xlsReadContext; + private static final Map XLS_RECORD_HANDLER_MAP = new HashMap(32); + + static { + XLS_RECORD_HANDLER_MAP.put(BlankRecord.sid, new BlankRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BOFRecord.sid, new BofRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BoolErrRecord.sid, new BoolErrRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(BoundSheetRecord.sid, new BoundSheetRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(DUMMY_RECORD_SID, new DummyRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(EOFRecord.sid, new EofRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(FormulaRecord.sid, new FormulaRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(HyperlinkRecord.sid, new HyperlinkRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(IndexRecord.sid, new IndexRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(LabelRecord.sid, new LabelRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(LabelSSTRecord.sid, new LabelSstRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(MergeCellsRecord.sid, new MergeCellsRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(NoteRecord.sid, new NoteRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(NumberRecord.sid, new NumberRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(ObjRecord.sid, new ObjRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(RKRecord.sid, new RkRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(SSTRecord.sid, new SstRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(StringRecord.sid, new StringRecordHandler()); + XLS_RECORD_HANDLER_MAP.put(TextObjectRecord.sid, new TextObjectRecordHandler()); + } + + public XlsSaxAnalyser(XlsReadContext xlsReadContext) { + this.xlsReadContext = xlsReadContext; + } + + @Override + public List sheetList() { + try { + if (xlsReadContext.readWorkbookHolder().getActualSheetDataList() == null) { + new XlsListSheetListener(xlsReadContext).execute(); + } + } catch (ExcelAnalysisStopException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Custom stop!"); + } + } + return xlsReadContext.readWorkbookHolder().getActualSheetDataList(); + } + + @Override + public void execute() { + XlsReadWorkbookHolder xlsReadWorkbookHolder = xlsReadContext.xlsReadWorkbookHolder(); + MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); + xlsReadWorkbookHolder.setFormatTrackingHSSFListener(new FormatTrackingHSSFListener(listener)); + EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener = + new EventWorkbookBuilder.SheetRecordCollectingListener( + xlsReadWorkbookHolder.getFormatTrackingHSSFListener()); + xlsReadWorkbookHolder.setHssfWorkbook(workbookBuildingListener.getStubHSSFWorkbook()); + HSSFEventFactory factory = new HSSFEventFactory(); + HSSFRequest request = new HSSFRequest(); + request.addListenerForAllRecords(xlsReadWorkbookHolder.getFormatTrackingHSSFListener()); + try { + factory.processWorkbookEvents(request, xlsReadWorkbookHolder.getPoifsFileSystem()); + } catch (IOException e) { + throw new ExcelAnalysisException(e); + } + + // There are some special xls that do not have the terminator "[EOF]", so an additional + xlsReadContext.analysisEventProcessor().endSheet(xlsReadContext); + } + + @Override + public void processRecord(Record record) { + XlsRecordHandler handler = XLS_RECORD_HANDLER_MAP.get(record.getSid()); + if (handler == null) { + return; + } + boolean ignoreRecord = + (handler instanceof IgnorableXlsRecordHandler) && xlsReadContext.xlsReadWorkbookHolder().getIgnoreRecord(); + if (ignoreRecord) { + // No need to read the current sheet + return; + } + if (!handler.support(xlsReadContext, record)) { + return; + } + + try { + handler.processRecord(xlsReadContext, record); + } catch (ExcelAnalysisStopSheetException e) { + if (log.isDebugEnabled()) { + log.debug("Custom stop!", e); + } + xlsReadContext.xlsReadWorkbookHolder().setIgnoreRecord(Boolean.TRUE); + xlsReadContext.xlsReadWorkbookHolder().setCurrentSheetStopped(Boolean.TRUE); + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/AbstractXlsRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/AbstractXlsRecordHandler.java new file mode 100644 index 000000000..93b3d1a3a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/AbstractXlsRecordHandler.java @@ -0,0 +1,19 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.XlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Abstract xls record handler + * + * @author Jiaju Zhuang + **/ +public abstract class AbstractXlsRecordHandler implements XlsRecordHandler { + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return true; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankRecordHandler.java new file mode 100644 index 000000000..25d1338e6 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BlankRecordHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.metadata.data.ReadCellData; + +import org.apache.poi.hssf.record.BlankRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BlankRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BlankRecord br = (BlankRecord)record; + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)br.getColumn(), + ReadCellData.newEmptyInstance(br.getRow(), (int)br.getColumn())); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java new file mode 100644 index 000000000..a05568ee6 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BofRecordHandler.java @@ -0,0 +1,75 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.util.SheetUtils; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BofRecordHandler extends AbstractXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BOFRecord br = (BOFRecord) record; + XlsReadWorkbookHolder xlsReadWorkbookHolder = xlsReadContext.xlsReadWorkbookHolder(); + if (br.getType() == BOFRecord.TYPE_WORKBOOK) { + xlsReadWorkbookHolder.setReadSheetIndex(null); + xlsReadWorkbookHolder.setIgnoreRecord(Boolean.FALSE); + return; + } + if (br.getType() != BOFRecord.TYPE_WORKSHEET) { + return; + } + // Init read sheet Data + initReadSheetDataList(xlsReadWorkbookHolder); + Integer readSheetIndex = xlsReadWorkbookHolder.getReadSheetIndex(); + if (readSheetIndex == null) { + readSheetIndex = 0; + xlsReadWorkbookHolder.setReadSheetIndex(readSheetIndex); + } + ReadSheet actualReadSheet = xlsReadWorkbookHolder.getActualSheetDataList().get(readSheetIndex); + assert actualReadSheet != null : "Can't find the sheet."; + // Copy the parameter to the current sheet + ReadSheet readSheet = SheetUtils.match(actualReadSheet, xlsReadContext); + if (readSheet != null) { + xlsReadContext.currentSheet(readSheet); + xlsReadContext.xlsReadWorkbookHolder().setIgnoreRecord(Boolean.FALSE); + } else { + xlsReadContext.xlsReadWorkbookHolder().setIgnoreRecord(Boolean.TRUE); + } + xlsReadContext.xlsReadWorkbookHolder().setCurrentSheetStopped(Boolean.FALSE); + // Go read the next one + xlsReadWorkbookHolder.setReadSheetIndex(xlsReadWorkbookHolder.getReadSheetIndex() + 1); + } + + private void initReadSheetDataList(XlsReadWorkbookHolder xlsReadWorkbookHolder) { + if (xlsReadWorkbookHolder.getActualSheetDataList() != null) { + return; + } + BoundSheetRecord[] boundSheetRecords = + BoundSheetRecord.orderByBofPosition(xlsReadWorkbookHolder.getBoundSheetRecordList()); + List readSheetDataList = new ArrayList(); + for (int i = 0; i < boundSheetRecords.length; i++) { + BoundSheetRecord boundSheetRecord = boundSheetRecords[i]; + ReadSheet readSheet = new ReadSheet(i, boundSheetRecord.getSheetname()); + readSheetDataList.add(readSheet); + } + xlsReadWorkbookHolder.setActualSheetDataList(readSheetDataList); + // Just need to get the list of sheets + if (!xlsReadWorkbookHolder.getNeedReadSheet()) { + throw new ExcelAnalysisStopException("Just need to get the list of sheets."); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoolErrRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoolErrRecordHandler.java new file mode 100644 index 000000000..196659076 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoolErrRecordHandler.java @@ -0,0 +1,25 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.data.ReadCellData; + +import org.apache.poi.hssf.record.BoolErrRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BoolErrRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BoolErrRecord ber = (BoolErrRecord)record; + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)ber.getColumn(), + ReadCellData.newInstance(ber.getBooleanValue(), ber.getRow(), (int)ber.getColumn())); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoundSheetRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoundSheetRecordHandler.java new file mode 100644 index 000000000..ad4755225 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/BoundSheetRecordHandler.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class BoundSheetRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + BoundSheetRecord bsr = (BoundSheetRecord)record; + xlsReadContext.xlsReadWorkbookHolder().getBoundSheetRecordList().add(bsr); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java new file mode 100644 index 000000000..b5c853c57 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/DummyRecordHandler.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.util.LinkedHashMap; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; + +import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord; +import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class DummyRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + if (record instanceof LastCellOfRowDummyRecord) { + // End of this row + LastCellOfRowDummyRecord lcrdr = (LastCellOfRowDummyRecord)record; + xlsReadSheetHolder.setRowIndex(lcrdr.getRow()); + xlsReadContext.readRowHolder(new ReadRowHolder(lcrdr.getRow(), xlsReadSheetHolder.getTempRowType(), + xlsReadContext.readSheetHolder().getGlobalConfiguration(), xlsReadSheetHolder.getCellMap())); + xlsReadContext.analysisEventProcessor().endRow(xlsReadContext); + xlsReadSheetHolder.setCellMap(new LinkedHashMap()); + xlsReadSheetHolder.setTempRowType(RowTypeEnum.EMPTY); + } else if (record instanceof MissingCellDummyRecord) { + MissingCellDummyRecord mcdr = (MissingCellDummyRecord)record; + // https://github.com/alibaba/easyexcel/issues/2236 + // Some abnormal XLS, in the case of data already exist, or there will be a "MissingCellDummyRecord" + // records, so if the existing data, empty data is ignored + xlsReadSheetHolder.getCellMap().putIfAbsent(mcdr.getColumn(), + ReadCellData.newEmptyInstance(mcdr.getRow(), mcdr.getColumn())); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/EofRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/EofRecordHandler.java new file mode 100644 index 000000000..6e162028e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/EofRecordHandler.java @@ -0,0 +1,51 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.util.LinkedHashMap; + +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.util.BooleanUtils; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class EofRecordHandler extends AbstractXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + if (xlsReadContext.readSheetHolder() == null) { + return; + } + + //Represents the current sheet does not need to be read or the user manually stopped reading the sheet. + if (BooleanUtils.isTrue(xlsReadContext.xlsReadWorkbookHolder().getIgnoreRecord())) { + // When the user manually stops reading the sheet, the method to end the sheet needs to be called. + if (BooleanUtils.isTrue(xlsReadContext.xlsReadWorkbookHolder().getCurrentSheetStopped())) { + xlsReadContext.analysisEventProcessor().endSheet(xlsReadContext); + } + return; + } + + // Sometimes tables lack the end record of the last column + if (!xlsReadContext.xlsReadSheetHolder().getCellMap().isEmpty()) { + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + // Forge a termination data + xlsReadContext.readRowHolder(new ReadRowHolder(xlsReadContext.xlsReadSheetHolder().getRowIndex() + 1, + xlsReadSheetHolder.getTempRowType(), + xlsReadContext.readSheetHolder().getGlobalConfiguration(), xlsReadSheetHolder.getCellMap())); + xlsReadContext.analysisEventProcessor().endRow(xlsReadContext); + xlsReadSheetHolder.setCellMap(new LinkedHashMap()); + xlsReadSheetHolder.setTempRowType(RowTypeEnum.EMPTY); + } + + xlsReadContext.analysisEventProcessor().endSheet(xlsReadContext); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java new file mode 100644 index 000000000..dcf7f5484 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/FormulaRecordHandler.java @@ -0,0 +1,89 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.math.BigDecimal; +import java.util.Map; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.constant.EasyExcelConstants; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.data.FormulaData; +import com.alibaba.excel.metadata.data.ReadCellData; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.hssf.model.HSSFFormulaParser; +import org.apache.poi.hssf.record.FormulaRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.ss.usermodel.CellType; + +/** + * Record handler + * + * @author Dan Zheng + */ +@Slf4j +public class FormulaRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + private static final String ERROR = "#VALUE!"; + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + FormulaRecord frec = (FormulaRecord)record; + Map cellMap = xlsReadContext.xlsReadSheetHolder().getCellMap(); + ReadCellData tempCellData = new ReadCellData<>(); + tempCellData.setRowIndex(frec.getRow()); + tempCellData.setColumnIndex((int)frec.getColumn()); + CellType cellType = CellType.forInt(frec.getCachedResultType()); + String formulaValue = null; + try { + formulaValue = HSSFFormulaParser.toFormulaString(xlsReadContext.xlsReadWorkbookHolder().getHssfWorkbook(), + frec.getParsedExpression()); + } catch (Exception e) { + log.debug("Get formula value error.", e); + } + FormulaData formulaData = new FormulaData(); + formulaData.setFormulaValue(formulaValue); + tempCellData.setFormulaData(formulaData); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + switch (cellType) { + case STRING: + // Formula result is a string + // This is stored in the next record + tempCellData.setType(CellDataTypeEnum.STRING); + xlsReadContext.xlsReadSheetHolder().setTempCellData(tempCellData); + break; + case NUMERIC: + tempCellData.setType(CellDataTypeEnum.NUMBER); + tempCellData.setOriginalNumberValue(BigDecimal.valueOf(frec.getValue())); + tempCellData.setNumberValue( + tempCellData.getOriginalNumberValue().round(EasyExcelConstants.EXCEL_MATH_CONTEXT)); + int dataFormat = + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex(frec); + DataFormatData dataFormatData = new DataFormatData(); + dataFormatData.setIndex((short)dataFormat); + dataFormatData.setFormat(BuiltinFormats.getBuiltinFormat(dataFormatData.getIndex(), + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatString(frec), + xlsReadContext.readSheetHolder().getGlobalConfiguration().getLocale())); + tempCellData.setDataFormatData(dataFormatData); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + case ERROR: + tempCellData.setType(CellDataTypeEnum.ERROR); + tempCellData.setStringValue(ERROR); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + case BOOLEAN: + tempCellData.setType(CellDataTypeEnum.BOOLEAN); + tempCellData.setBooleanValue(frec.getCachedBooleanValue()); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + default: + tempCellData.setType(CellDataTypeEnum.EMPTY); + cellMap.put((int)frec.getColumn(), tempCellData); + break; + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/HyperlinkRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/HyperlinkRecordHandler.java new file mode 100644 index 000000000..99c3a80af --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/HyperlinkRecordHandler.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.HyperlinkRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class HyperlinkRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.HYPERLINK); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + HyperlinkRecord hr = (HyperlinkRecord)record; + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.HYPERLINK, hr.getAddress(), hr.getFirstRow(), + hr.getLastRow(), hr.getFirstColumn(), hr.getLastColumn()); + xlsReadContext.xlsReadSheetHolder().setCellExtra(cellExtra); + xlsReadContext.analysisEventProcessor().extra(xlsReadContext); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java new file mode 100644 index 000000000..b95812eb7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/IndexRecordHandler.java @@ -0,0 +1,22 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.IndexRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Jiaju Zhuang + */ +public class IndexRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + if (xlsReadContext.readSheetHolder() == null) { + return; + } + xlsReadContext.readSheetHolder().setApproximateTotalRowNumber(((IndexRecord)record).getLastRowAdd1()); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java new file mode 100644 index 000000000..a90ad6bbf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelRecordHandler.java @@ -0,0 +1,28 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.data.ReadCellData; + +import org.apache.poi.hssf.record.LabelRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class LabelRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + LabelRecord lrec = (LabelRecord)record; + String data = lrec.getValue(); + if (data != null && xlsReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + data = data.trim(); + } + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)lrec.getColumn(), + ReadCellData.newInstance(data, lrec.getRow(), (int)lrec.getColumn())); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelSstRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelSstRecordHandler.java new file mode 100644 index 000000000..fd464e546 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/LabelSstRecordHandler.java @@ -0,0 +1,42 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.util.Map; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.data.ReadCellData; + +import org.apache.poi.hssf.record.LabelSSTRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class LabelSstRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + LabelSSTRecord lsrec = (LabelSSTRecord)record; + ReadCache readCache = xlsReadContext.readWorkbookHolder().getReadCache(); + Map cellMap = xlsReadContext.xlsReadSheetHolder().getCellMap(); + if (readCache == null) { + cellMap.put((int)lsrec.getColumn(), ReadCellData.newEmptyInstance(lsrec.getRow(), (int)lsrec.getColumn())); + return; + } + String data = readCache.get(lsrec.getSSTIndex()); + if (data == null) { + cellMap.put((int)lsrec.getColumn(), ReadCellData.newEmptyInstance(lsrec.getRow(), (int)lsrec.getColumn())); + return; + } + if (xlsReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + data = data.trim(); + } + cellMap.put((int)lsrec.getColumn(), ReadCellData.newInstance(data, lsrec.getRow(), (int)lsrec.getColumn())); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/MergeCellsRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/MergeCellsRecordHandler.java new file mode 100644 index 000000000..f9bf47215 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/MergeCellsRecordHandler.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.MergeCellsRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.ss.util.CellRangeAddress; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class MergeCellsRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.MERGE); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + MergeCellsRecord mcr = (MergeCellsRecord)record; + for (int i = 0; i < mcr.getNumAreas(); i++) { + CellRangeAddress cellRangeAddress = mcr.getAreaAt(i); + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.MERGE, null, cellRangeAddress.getFirstRow(), + cellRangeAddress.getLastRow(), cellRangeAddress.getFirstColumn(), cellRangeAddress.getLastColumn()); + xlsReadContext.xlsReadSheetHolder().setCellExtra(cellExtra); + xlsReadContext.analysisEventProcessor().extra(xlsReadContext); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java new file mode 100644 index 000000000..6b6d803e9 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NoteRecordHandler.java @@ -0,0 +1,31 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.NoteRecord; +import org.apache.poi.hssf.record.Record; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class NoteRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + NoteRecord nr = (NoteRecord)record; + String text = xlsReadContext.xlsReadSheetHolder().getObjectCacheMap().get(nr.getShapeId()); + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.COMMENT, text, nr.getRow(), nr.getColumn()); + xlsReadContext.xlsReadSheetHolder().setCellExtra(cellExtra); + xlsReadContext.analysisEventProcessor().extra(xlsReadContext); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java new file mode 100644 index 000000000..031b895f0 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/NumberRecordHandler.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import java.math.BigDecimal; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.constant.EasyExcelConstants; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.data.ReadCellData; + +import org.apache.poi.hssf.record.NumberRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class NumberRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + NumberRecord nr = (NumberRecord)record; + ReadCellData cellData = ReadCellData.newInstanceOriginal(BigDecimal.valueOf(nr.getValue()), nr.getRow(), + (int)nr.getColumn()); + short dataFormat = (short)xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatIndex( + nr); + DataFormatData dataFormatData = new DataFormatData(); + dataFormatData.setIndex(dataFormat); + dataFormatData.setFormat(BuiltinFormats.getBuiltinFormat(dataFormat, + xlsReadContext.xlsReadWorkbookHolder().getFormatTrackingHSSFListener().getFormatString(nr), + xlsReadContext.readSheetHolder().getGlobalConfiguration().getLocale())); + cellData.setDataFormatData(dataFormatData); + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)nr.getColumn(), cellData); + xlsReadContext.xlsReadSheetHolder().setTempRowType(RowTypeEnum.DATA); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/ObjRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/ObjRecordHandler.java new file mode 100644 index 000000000..97bcd20ff --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/ObjRecordHandler.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.CommonObjectDataSubRecord; +import org.apache.poi.hssf.record.ObjRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.SubRecord; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Jiaju Zhuang + */ +public class ObjRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + ObjRecord or = (ObjRecord)record; + for (SubRecord subRecord : or.getSubRecords()) { + if (subRecord instanceof CommonObjectDataSubRecord) { + CommonObjectDataSubRecord codsr = (CommonObjectDataSubRecord)subRecord; + if (CommonObjectDataSubRecord.OBJECT_TYPE_COMMENT == codsr.getObjectType()) { + xlsReadContext.xlsReadSheetHolder().setTempObjectIndex(codsr.getObjectId()); + } + break; + } + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java new file mode 100644 index 000000000..3fb458997 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/RkRecordHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.metadata.data.ReadCellData; + +import org.apache.poi.hssf.record.RKRecord; +import org.apache.poi.hssf.record.Record; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class RkRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + RKRecord re = (RKRecord)record; + xlsReadContext.xlsReadSheetHolder().getCellMap().put((int)re.getColumn(), + ReadCellData.newEmptyInstance(re.getRow(), (int)re.getColumn())); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java new file mode 100644 index 000000000..35727b66c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/SstRecordHandler.java @@ -0,0 +1,20 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.SSTRecord; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.cache.XlsCache; +import com.alibaba.excel.context.xls.XlsReadContext; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class SstRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + xlsReadContext.readWorkbookHolder().setReadCache(new XlsCache((SSTRecord)record)); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/StringRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/StringRecordHandler.java new file mode 100644 index 000000000..4e78a381c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/StringRecordHandler.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.StringRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; + +/** + * Record handler + * + * @author Dan Zheng + */ +public class StringRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(StringRecordHandler.class); + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + // String for formula + StringRecord srec = (StringRecord)record; + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + CellDatatempCellData = xlsReadSheetHolder.getTempCellData(); + if (tempCellData == null) { + LOGGER.warn("String type formula but no value found."); + return; + } + tempCellData.setStringValue(srec.getString()); + xlsReadSheetHolder.getCellMap().put(tempCellData.getColumnIndex(), tempCellData); + xlsReadSheetHolder.setTempCellData(null); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/TextObjectRecordHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/TextObjectRecordHandler.java new file mode 100644 index 000000000..949498f08 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v03/handlers/TextObjectRecordHandler.java @@ -0,0 +1,38 @@ +package com.alibaba.excel.analysis.v03.handlers; + +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.TextObjectRecord; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.analysis.v03.IgnorableXlsRecordHandler; +import com.alibaba.excel.context.xls.XlsReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; + +/** + * Record handler + * + * @author Jiaju Zhuang + */ +public class TextObjectRecordHandler extends AbstractXlsRecordHandler implements IgnorableXlsRecordHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(TextObjectRecordHandler.class); + + @Override + public boolean support(XlsReadContext xlsReadContext, Record record) { + return xlsReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT); + } + + @Override + public void processRecord(XlsReadContext xlsReadContext, Record record) { + TextObjectRecord tor = (TextObjectRecord)record; + XlsReadSheetHolder xlsReadSheetHolder = xlsReadContext.xlsReadSheetHolder(); + Integer tempObjectIndex = xlsReadSheetHolder.getTempObjectIndex(); + if (tempObjectIndex == null) { + LOGGER.debug("tempObjectIndex is null."); + return; + } + xlsReadSheetHolder.getObjectCacheMap().put(tempObjectIndex, tor.getStr().getString()); + xlsReadSheetHolder.setTempObjectIndex(null); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java new file mode 100644 index 000000000..beda85030 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java @@ -0,0 +1,293 @@ +package com.alibaba.excel.analysis.v07; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +import com.alibaba.excel.analysis.ExcelReadExecutor; +import com.alibaba.excel.analysis.v07.handlers.sax.SharedStringsTableHandler; +import com.alibaba.excel.analysis.v07.handlers.sax.XlsxRowHandler; +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.exception.ExcelAnalysisStopSheetException; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.util.SheetUtils; +import com.alibaba.excel.util.StringUtils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.ss.util.CellAddress; +import org.apache.poi.xssf.eventusermodel.XSSFReader; +import org.apache.poi.xssf.model.Comments; +import org.apache.poi.xssf.model.CommentsTable; +import org.apache.poi.xssf.usermodel.XSSFComment; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * @author jipengfei + */ +@Slf4j +public class XlsxSaxAnalyser implements ExcelReadExecutor { + + /** + * Storage sheet SharedStrings + */ + public static final PackagePartName SHARED_STRINGS_PART_NAME; + + static { + try { + SHARED_STRINGS_PART_NAME = PackagingURIHelper.createPartName("/xl/sharedStrings.xml"); + } catch (InvalidFormatException e) { + log.error("Initialize the XlsxSaxAnalyser failure", e); + throw new ExcelAnalysisException("Initialize the XlsxSaxAnalyser failure", e); + } + } + + private final XlsxReadContext xlsxReadContext; + private final List sheetList; + private final Map sheetMap; + /** + * excel comments key: sheetNo value: CommentsTable + */ + private final Map commentsTableMap; + + public XlsxSaxAnalyser(XlsxReadContext xlsxReadContext, InputStream decryptedStream) throws Exception { + this.xlsxReadContext = xlsxReadContext; + // Initialize cache + XlsxReadWorkbookHolder xlsxReadWorkbookHolder = xlsxReadContext.xlsxReadWorkbookHolder(); + + OPCPackage pkg = readOpcPackage(xlsxReadWorkbookHolder, decryptedStream); + xlsxReadWorkbookHolder.setOpcPackage(pkg); + + // Read the Shared information Strings + PackagePart sharedStringsTablePackagePart = pkg.getPart(SHARED_STRINGS_PART_NAME); + if (sharedStringsTablePackagePart != null) { + // Specify default cache + defaultReadCache(xlsxReadWorkbookHolder, sharedStringsTablePackagePart); + + // Analysis sharedStringsTable.xml + analysisSharedStringsTable(sharedStringsTablePackagePart.getInputStream(), xlsxReadWorkbookHolder); + } + + XSSFReader xssfReader = new XSSFReader(pkg); + analysisUse1904WindowDate(xssfReader, xlsxReadWorkbookHolder); + + // set style table + setStylesTable(xlsxReadWorkbookHolder, xssfReader); + + sheetList = new ArrayList<>(); + sheetMap = new HashMap<>(); + commentsTableMap = new HashMap<>(); + Map packageRelationshipCollectionMap = MapUtils.newHashMap(); + xlsxReadWorkbookHolder.setPackageRelationshipCollectionMap(packageRelationshipCollectionMap); + + XSSFReader.SheetIterator ite = (XSSFReader.SheetIterator)xssfReader.getSheetsData(); + int index = 0; + if (!ite.hasNext()) { + throw new ExcelAnalysisException("Can not find any sheet!"); + } + while (ite.hasNext()) { + InputStream inputStream = ite.next(); + sheetList.add(new ReadSheet(index, ite.getSheetName())); + sheetMap.put(index, inputStream); + if (xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT)) { + Comments comments = ite.getSheetComments(); + if (comments instanceof CommentsTable) { + commentsTableMap.put(index, (CommentsTable) comments); + } + } + if (xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.HYPERLINK)) { + PackageRelationshipCollection packageRelationshipCollection = Optional.ofNullable(ite.getSheetPart()) + .map(packagePart -> { + try { + return packagePart.getRelationships(); + } catch (InvalidFormatException e) { + log.warn("Reading the Relationship failed", e); + return null; + } + }).orElse(null); + if (packageRelationshipCollection != null) { + packageRelationshipCollectionMap.put(index, packageRelationshipCollection); + } + } + index++; + } + } + + private void setStylesTable(XlsxReadWorkbookHolder xlsxReadWorkbookHolder, XSSFReader xssfReader) { + try { + xlsxReadWorkbookHolder.setStylesTable(xssfReader.getStylesTable()); + } catch (Exception e) { + log.warn( + "Currently excel cannot get style information, but it doesn't affect the data analysis.You can try to" + + " save the file with office again or ignore the current error.", + e); + } + } + + private void defaultReadCache(XlsxReadWorkbookHolder xlsxReadWorkbookHolder, + PackagePart sharedStringsTablePackagePart) { + ReadCache readCache = xlsxReadWorkbookHolder.getReadCacheSelector().readCache(sharedStringsTablePackagePart); + xlsxReadWorkbookHolder.setReadCache(readCache); + readCache.init(xlsxReadContext); + } + + private void analysisUse1904WindowDate(XSSFReader xssfReader, XlsxReadWorkbookHolder xlsxReadWorkbookHolder) + throws Exception { + if (xlsxReadWorkbookHolder.globalConfiguration().getUse1904windowing() != null) { + return; + } + InputStream workbookXml = xssfReader.getWorkbookData(); + WorkbookDocument ctWorkbook = WorkbookDocument.Factory.parse(workbookXml); + CTWorkbook wb = ctWorkbook.getWorkbook(); + CTWorkbookPr prefix = wb.getWorkbookPr(); + if (prefix != null && prefix.getDate1904()) { + xlsxReadWorkbookHolder.getGlobalConfiguration().setUse1904windowing(Boolean.TRUE); + } else { + xlsxReadWorkbookHolder.getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); + } + } + + private void analysisSharedStringsTable(InputStream sharedStringsTableInputStream, + XlsxReadWorkbookHolder xlsxReadWorkbookHolder) { + ContentHandler handler = new SharedStringsTableHandler(xlsxReadWorkbookHolder.getReadCache()); + parseXmlSource(sharedStringsTableInputStream, handler); + xlsxReadWorkbookHolder.getReadCache().putFinished(); + } + + private OPCPackage readOpcPackage(XlsxReadWorkbookHolder xlsxReadWorkbookHolder, InputStream decryptedStream) + throws Exception { + if (decryptedStream == null && xlsxReadWorkbookHolder.getFile() != null) { + return OPCPackage.open(xlsxReadWorkbookHolder.getFile()); + } + if (xlsxReadWorkbookHolder.getMandatoryUseInputStream()) { + if (decryptedStream != null) { + return OPCPackage.open(decryptedStream); + } else { + return OPCPackage.open(xlsxReadWorkbookHolder.getInputStream()); + } + } + File readTempFile = FileUtils.createCacheTmpFile(); + xlsxReadWorkbookHolder.setTempFile(readTempFile); + File tempFile = new File(readTempFile.getPath(), UUID.randomUUID() + ".xlsx"); + if (decryptedStream != null) { + FileUtils.writeToFile(tempFile, decryptedStream, false); + } else { + FileUtils.writeToFile(tempFile, xlsxReadWorkbookHolder.getInputStream(), + xlsxReadWorkbookHolder.getAutoCloseStream()); + } + return OPCPackage.open(tempFile, PackageAccess.READ); + } + + @Override + public List sheetList() { + return sheetList; + } + + private void parseXmlSource(InputStream inputStream, ContentHandler handler) { + InputSource inputSource = new InputSource(inputStream); + try { + SAXParserFactory saxFactory; + String xlsxSAXParserFactoryName = xlsxReadContext.xlsxReadWorkbookHolder().getSaxParserFactoryName(); + if (StringUtils.isEmpty(xlsxSAXParserFactoryName)) { + saxFactory = SAXParserFactory.newInstance(); + } else { + saxFactory = SAXParserFactory.newInstance(xlsxSAXParserFactoryName, null); + } + try { + saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + } catch (Throwable ignore) {} + try { + saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + } catch (Throwable ignore) {} + try { + saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + } catch (Throwable ignore) {} + SAXParser saxParser = saxFactory.newSAXParser(); + XMLReader xmlReader = saxParser.getXMLReader(); + xmlReader.setContentHandler(handler); + xmlReader.parse(inputSource); + inputStream.close(); + } catch (IOException | ParserConfigurationException | SAXException e) { + throw new ExcelAnalysisException(e); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + throw new ExcelAnalysisException("Can not close 'inputStream'!"); + } + } + } + } + + @Override + public void execute() { + for (ReadSheet readSheet : sheetList) { + readSheet = SheetUtils.match(readSheet, xlsxReadContext); + if (readSheet != null) { + try { + xlsxReadContext.currentSheet(readSheet); + parseXmlSource(sheetMap.get(readSheet.getSheetNo()), new XlsxRowHandler(xlsxReadContext)); + // Read comments + readComments(readSheet); + } catch (ExcelAnalysisStopSheetException e) { + if (log.isDebugEnabled()) { + log.debug("Custom stop!", e); + } + } + // The last sheet is read + xlsxReadContext.analysisEventProcessor().endSheet(xlsxReadContext); + } + } + } + + private void readComments(ReadSheet readSheet) { + if (!xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.COMMENT)) { + return; + } + CommentsTable commentsTable = commentsTableMap.get(readSheet.getSheetNo()); + if (commentsTable == null) { + return; + } + Iterator cellAddresses = commentsTable.getCellAddresses(); + while (cellAddresses.hasNext()) { + CellAddress cellAddress = cellAddresses.next(); + XSSFComment cellComment = commentsTable.findCellComment(cellAddress); + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.COMMENT, cellComment.getString().toString(), + cellAddress.getRow(), cellAddress.getColumn()); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java new file mode 100644 index 000000000..7a982d5d7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractCellValueTagHandler.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * Cell Value Handler + * + * @author jipengfei + */ +public abstract class AbstractCellValueTagHandler extends AbstractXlsxTagHandler { + + @Override + public void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length) { + xlsxReadContext.xlsxReadSheetHolder().getTempData().append(ch, start, length); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractXlsxTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractXlsxTagHandler.java new file mode 100644 index 000000000..169fe00e7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/AbstractXlsxTagHandler.java @@ -0,0 +1,32 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * Abstract tag handler + * + * @author Jiaju Zhuang + */ +public abstract class AbstractXlsxTagHandler implements XlsxTagHandler { + @Override + public boolean support(XlsxReadContext xlsxReadContext) { + return true; + } + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + + } + + @Override + public void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length) { + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellFormulaTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellFormulaTagHandler.java new file mode 100644 index 000000000..6e5246d38 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellFormulaTagHandler.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.metadata.data.FormulaData; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; + +import org.xml.sax.Attributes; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class CellFormulaTagHandler extends AbstractXlsxTagHandler { + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + xlsxReadSheetHolder.setTempFormula(new StringBuilder()); + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + FormulaData formulaData = new FormulaData(); + formulaData.setFormulaValue(xlsxReadSheetHolder.getTempFormula().toString()); + xlsxReadSheetHolder.getTempCellData().setFormulaData(formulaData); + } + + @Override + public void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length) { + xlsxReadContext.xlsxReadSheetHolder().getTempFormula().append(ch, start, length); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellInlineStringValueTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellInlineStringValueTagHandler.java new file mode 100644 index 000000000..46cfc0d61 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellInlineStringValueTagHandler.java @@ -0,0 +1,10 @@ +package com.alibaba.excel.analysis.v07.handlers; + +/** + * Cell inline string value handler + * + * @author jipengfei + */ +public class CellInlineStringValueTagHandler extends AbstractCellValueTagHandler { + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java new file mode 100644 index 000000000..75e86aabe --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellTagHandler.java @@ -0,0 +1,110 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import java.math.BigDecimal; + +import com.alibaba.excel.constant.EasyExcelConstants; +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.util.PositionUtils; +import com.alibaba.excel.util.StringUtils; + +import org.xml.sax.Attributes; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class CellTagHandler extends AbstractXlsxTagHandler { + + private static final int DEFAULT_FORMAT_INDEX = 0; + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + xlsxReadSheetHolder.setColumnIndex(PositionUtils.getCol(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R), + xlsxReadSheetHolder.getColumnIndex())); + + // t="s" ,it means String + // t="str" ,it means String,but does not need to be read in the 'sharedStrings.xml' + // t="inlineStr" ,it means String,but does not need to be read in the 'sharedStrings.xml' + // t="b" ,it means Boolean + // t="e" ,it means Error + // t="n" ,it means Number + // t is null ,it means Empty or Number + CellDataTypeEnum type = CellDataTypeEnum.buildFromCellType(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_T)); + xlsxReadSheetHolder.setTempCellData(new ReadCellData<>(type)); + xlsxReadSheetHolder.setTempData(new StringBuilder()); + + // Put in data transformation information + String dateFormatIndex = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_S); + int dateFormatIndexInteger; + if (StringUtils.isEmpty(dateFormatIndex)) { + dateFormatIndexInteger = DEFAULT_FORMAT_INDEX; + } else { + dateFormatIndexInteger = Integer.parseInt(dateFormatIndex); + } + + xlsxReadSheetHolder.getTempCellData().setDataFormatData( + xlsxReadContext.xlsxReadWorkbookHolder().dataFormatData(dateFormatIndexInteger)); + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + ReadCellData tempCellData = xlsxReadSheetHolder.getTempCellData(); + StringBuilder tempData = xlsxReadSheetHolder.getTempData(); + String tempDataString = tempData.toString(); + CellDataTypeEnum oldType = tempCellData.getType(); + switch (oldType) { + case STRING: + // In some cases, although cell type is a string, it may be an empty tag + if (StringUtils.isEmpty(tempDataString)) { + break; + } + String stringValue = xlsxReadContext.readWorkbookHolder().getReadCache().get( + Integer.valueOf(tempDataString)); + tempCellData.setStringValue(stringValue); + break; + case DIRECT_STRING: + case ERROR: + tempCellData.setStringValue(tempDataString); + tempCellData.setType(CellDataTypeEnum.STRING); + break; + case BOOLEAN: + if (StringUtils.isEmpty(tempDataString)) { + tempCellData.setType(CellDataTypeEnum.EMPTY); + break; + } + tempCellData.setBooleanValue(BooleanUtils.valueOf(tempData.toString())); + break; + case NUMBER: + case EMPTY: + if (StringUtils.isEmpty(tempDataString)) { + tempCellData.setType(CellDataTypeEnum.EMPTY); + break; + } + tempCellData.setType(CellDataTypeEnum.NUMBER); + tempCellData.setOriginalNumberValue(new BigDecimal(tempDataString)); + tempCellData.setNumberValue( + tempCellData.getOriginalNumberValue().round(EasyExcelConstants.EXCEL_MATH_CONTEXT)); + break; + default: + throw new IllegalStateException("Cannot set values now"); + } + + if (tempCellData.getStringValue() != null + && xlsxReadContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + tempCellData.setStringValue(tempCellData.getStringValue().trim()); + } + + tempCellData.checkEmpty(); + tempCellData.setRowIndex(xlsxReadSheetHolder.getRowIndex()); + tempCellData.setColumnIndex(xlsxReadSheetHolder.getColumnIndex()); + xlsxReadSheetHolder.getCellMap().put(xlsxReadSheetHolder.getColumnIndex(), tempCellData); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java new file mode 100644 index 000000000..d9a54ff20 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CellValueTagHandler.java @@ -0,0 +1,10 @@ +package com.alibaba.excel.analysis.v07.handlers; + +/** + * Cell Value Handler + * + * @author jipengfei + */ +public class CellValueTagHandler extends AbstractCellValueTagHandler { + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountTagHandler.java new file mode 100644 index 000000000..f19794b26 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/CountTagHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.util.PositionUtils; + +import org.xml.sax.Attributes; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class CountTagHandler extends AbstractXlsxTagHandler { + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + String d = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_REF); + String totalStr = d.substring(d.indexOf(":") + 1); + xlsxReadContext.readSheetHolder().setApproximateTotalRowNumber(PositionUtils.getRow(totalStr) + 1); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/HyperlinkTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/HyperlinkTagHandler.java new file mode 100644 index 000000000..3f9e2ce70 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/HyperlinkTagHandler.java @@ -0,0 +1,58 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import java.util.Optional; + +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.util.StringUtils; + +/** + * Cell Handler + * + * @author Jiaju Zhuang + */ +public class HyperlinkTagHandler extends AbstractXlsxTagHandler { + + @Override + public boolean support(XlsxReadContext xlsxReadContext) { + return xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.HYPERLINK); + } + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + String ref = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_REF); + if (StringUtils.isEmpty(ref)) { + return; + } + // Hyperlink has 2 case: + // case 1,In the 'location' tag + String location = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_LOCATION); + if (location != null) { + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.HYPERLINK, location, ref); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + return; + } + // case 2, In the 'r:id' tag, Then go to 'PackageRelationshipCollection' to get inside + String rId = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_RID); + PackageRelationshipCollection packageRelationshipCollection = xlsxReadContext.xlsxReadSheetHolder() + .getPackageRelationshipCollection(); + if (rId == null || packageRelationshipCollection == null) { + return; + } + Optional.ofNullable(packageRelationshipCollection.getRelationshipByID(rId)) + .map(PackageRelationship::getTargetURI) + .ifPresent(uri -> { + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.HYPERLINK, uri.toString(), ref); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + }); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/MergeCellTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/MergeCellTagHandler.java new file mode 100644 index 000000000..8b0fa0411 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/MergeCellTagHandler.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.util.StringUtils; + +/** + * Cell Handler + * + * @author Jiaju Zhuang + */ +public class MergeCellTagHandler extends AbstractXlsxTagHandler { + + @Override + public boolean support(XlsxReadContext xlsxReadContext) { + return xlsxReadContext.readWorkbookHolder().getExtraReadSet().contains(CellExtraTypeEnum.MERGE); + } + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + String ref = attributes.getValue(ExcelXmlConstants.ATTRIBUTE_REF); + if (StringUtils.isEmpty(ref)) { + return; + } + CellExtra cellExtra = new CellExtra(CellExtraTypeEnum.MERGE, null, ref); + xlsxReadContext.readSheetHolder().setCellExtra(cellExtra); + xlsxReadContext.analysisEventProcessor().extra(xlsxReadContext); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java new file mode 100644 index 000000000..51c192cae --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/RowTagHandler.java @@ -0,0 +1,71 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import java.util.LinkedHashMap; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.util.PositionUtils; + +import org.apache.commons.collections4.MapUtils; +import org.xml.sax.Attributes; + +/** + * Cell Handler + * + * @author jipengfei + */ +public class RowTagHandler extends AbstractXlsxTagHandler { + + @Override + public void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + int rowIndex = PositionUtils.getRowByRowTagt(attributes.getValue(ExcelXmlConstants.ATTRIBUTE_R), + xlsxReadSheetHolder.getRowIndex()); + Integer lastRowIndex = xlsxReadContext.readSheetHolder().getRowIndex(); + while (lastRowIndex + 1 < rowIndex) { + xlsxReadContext.readRowHolder(new ReadRowHolder(lastRowIndex + 1, RowTypeEnum.EMPTY, + xlsxReadSheetHolder.getGlobalConfiguration(), new LinkedHashMap())); + xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext); + xlsxReadSheetHolder.setColumnIndex(null); + xlsxReadSheetHolder.setCellMap(new LinkedHashMap()); + lastRowIndex++; + } + xlsxReadSheetHolder.setRowIndex(rowIndex); + } + + @Override + public void endElement(XlsxReadContext xlsxReadContext, String name) { + XlsxReadSheetHolder xlsxReadSheetHolder = xlsxReadContext.xlsxReadSheetHolder(); + RowTypeEnum rowType = MapUtils.isEmpty(xlsxReadSheetHolder.getCellMap()) ? RowTypeEnum.EMPTY : RowTypeEnum.DATA; + // It's possible that all of the cells in the row are empty + if (rowType == RowTypeEnum.DATA) { + boolean hasData = false; + for (Cell cell : xlsxReadSheetHolder.getCellMap().values()) { + if (!(cell instanceof ReadCellData)) { + hasData = true; + break; + } + ReadCellData readCellData = (ReadCellData)cell; + if (readCellData.getType() != CellDataTypeEnum.EMPTY) { + hasData = true; + break; + } + } + if (!hasData) { + rowType = RowTypeEnum.EMPTY; + } + } + xlsxReadContext.readRowHolder(new ReadRowHolder(xlsxReadSheetHolder.getRowIndex(), rowType, + xlsxReadSheetHolder.getGlobalConfiguration(), xlsxReadSheetHolder.getCellMap())); + xlsxReadContext.analysisEventProcessor().endRow(xlsxReadContext); + xlsxReadSheetHolder.setColumnIndex(null); + xlsxReadSheetHolder.setCellMap(new LinkedHashMap<>()); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/XlsxTagHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/XlsxTagHandler.java new file mode 100644 index 000000000..fe392f9bf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/XlsxTagHandler.java @@ -0,0 +1,54 @@ +package com.alibaba.excel.analysis.v07.handlers; + +import org.xml.sax.Attributes; + +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +/** + * Tag handler + * + * @author Dan Zheng + */ +public interface XlsxTagHandler { + + /** + * Whether to support + * + * @param xlsxReadContext + * @return + */ + boolean support(XlsxReadContext xlsxReadContext); + + /** + * Start handle + * + * @param xlsxReadContext + * xlsxReadContext + * @param name + * Tag name + * @param attributes + * Tag attributes + */ + void startElement(XlsxReadContext xlsxReadContext, String name, Attributes attributes); + + /** + * End handle + * + * @param xlsxReadContext + * xlsxReadContext + * @param name + * Tag name + */ + void endElement(XlsxReadContext xlsxReadContext, String name); + + /** + * Read data + * + * @param xlsxReadContext + * @param ch + * @param start + * @param length + */ + void characters(XlsxReadContext xlsxReadContext, char[] ch, int start, int length); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java new file mode 100644 index 000000000..509430f8c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java @@ -0,0 +1,181 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package com.alibaba.excel.analysis.v07.handlers.sax; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.constant.ExcelXmlConstants; + +import org.xml.sax.Attributes; +import org.xml.sax.helpers.DefaultHandler; + +/** + * Sax read sharedStringsTable.xml + * + * @author Jiaju Zhuang + */ +public class SharedStringsTableHandler extends DefaultHandler { + + private static final Pattern UTF_PATTTERN = Pattern.compile("_x([0-9A-Fa-f]{4})_"); + + /** + * The final piece of data + */ + private StringBuilder currentData; + /** + * Current element data + */ + private StringBuilder currentElementData; + + private final ReadCache readCache; + /** + * Some fields in the T tag need to be ignored + */ + private boolean ignoreTagt = false; + /** + * The only time you need to read the characters in the T tag is when it is used + */ + private boolean isTagt = false; + + public SharedStringsTableHandler(ReadCache readCache) { + this.readCache = readCache; + } + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) { + if (name == null) { + return; + } + switch (name) { + case ExcelXmlConstants.SHAREDSTRINGS_T_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_X_T_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_NS2_T_TAG: + currentElementData = null; + isTagt = true; + break; + case ExcelXmlConstants.SHAREDSTRINGS_SI_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_X_SI_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_NS2_SI_TAG: + currentData = null; + break; + case ExcelXmlConstants.SHAREDSTRINGS_RPH_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_X_RPH_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_NS2_RPH_TAG: + ignoreTagt = true; + break; + default: + // ignore + } + } + + @Override + public void endElement(String uri, String localName, String name) { + if (name == null) { + return; + } + switch (name) { + case ExcelXmlConstants.SHAREDSTRINGS_T_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_X_T_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_NS2_T_TAG: + if (currentElementData != null) { + if (currentData == null) { + currentData = new StringBuilder(); + } + currentData.append(currentElementData); + } + isTagt = false; + break; + case ExcelXmlConstants.SHAREDSTRINGS_SI_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_X_SI_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_NS2_SI_TAG: + if (currentData == null) { + readCache.put(null); + } else { + readCache.put(utfDecode(currentData.toString())); + } + break; + case ExcelXmlConstants.SHAREDSTRINGS_RPH_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_X_RPH_TAG: + case ExcelXmlConstants.SHAREDSTRINGS_NS2_RPH_TAG: + ignoreTagt = false; + break; + default: + // ignore + } + } + + @Override + public void characters(char[] ch, int start, int length) { + if (!isTagt || ignoreTagt) { + return; + } + if (currentElementData == null) { + currentElementData = new StringBuilder(); + } + currentElementData.append(ch, start, length); + } + + /** + * from poi XSSFRichTextString + * + * @param value the string to decode + * @return the decoded string or null if the input string is null + *

+ * For all characters which cannot be represented in XML as defined by the XML 1.0 specification, + * the characters are escaped using the Unicode numerical character representation escape character + * format _xHHHH_, where H represents a hexadecimal character in the character's value. + *

+ * Example: The Unicode character 0D is invalid in an XML 1.0 document, + * so it shall be escaped as _x000D_. + *

+ * See section 3.18.9 in the OOXML spec. + * @see org.apache.poi.xssf.usermodel.XSSFRichTextString#utfDecode(String) + */ + static String utfDecode(String value) { + if (value == null || !value.contains("_x")) { + return value; + } + + StringBuilder buf = new StringBuilder(); + Matcher m = UTF_PATTTERN.matcher(value); + int idx = 0; + while (m.find()) { + int pos = m.start(); + if (pos > idx) { + buf.append(value, idx, pos); + } + + String code = m.group(1); + int icode = Integer.decode("0x" + code); + buf.append((char)icode); + + idx = m.end(); + } + + // small optimization: don't go via StringBuilder if not necessary, + // the encodings are very rare, so we should almost always go via this shortcut. + if (idx == 0) { + return value; + } + + buf.append(value.substring(idx)); + return buf.toString(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java new file mode 100644 index 000000000..7b48c8a62 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/analysis/v07/handlers/sax/XlsxRowHandler.java @@ -0,0 +1,103 @@ +package com.alibaba.excel.analysis.v07.handlers.sax; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.analysis.v07.handlers.CellFormulaTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CellInlineStringValueTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CellTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CellValueTagHandler; +import com.alibaba.excel.analysis.v07.handlers.CountTagHandler; +import com.alibaba.excel.analysis.v07.handlers.HyperlinkTagHandler; +import com.alibaba.excel.analysis.v07.handlers.MergeCellTagHandler; +import com.alibaba.excel.analysis.v07.handlers.RowTagHandler; +import com.alibaba.excel.analysis.v07.handlers.XlsxTagHandler; +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.context.xlsx.XlsxReadContext; + +import lombok.extern.slf4j.Slf4j; +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +/** + * @author jipengfei + */ +@Slf4j +public class XlsxRowHandler extends DefaultHandler { + private final XlsxReadContext xlsxReadContext; + private static final Map XLSX_CELL_HANDLER_MAP = new HashMap<>(64); + + static { + CellFormulaTagHandler cellFormulaTagHandler = new CellFormulaTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_FORMULA_TAG, cellFormulaTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_FORMULA_TAG, cellFormulaTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_FORMULA_TAG, cellFormulaTagHandler); + CellInlineStringValueTagHandler cellInlineStringValueTagHandler = new CellInlineStringValueTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_INLINE_STRING_VALUE_TAG, cellInlineStringValueTagHandler); + CellTagHandler cellTagHandler = new CellTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_TAG, cellTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_TAG, cellTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_TAG, cellTagHandler); + CellValueTagHandler cellValueTagHandler = new CellValueTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.CELL_VALUE_TAG, cellValueTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_CELL_VALUE_TAG, cellValueTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_CELL_VALUE_TAG, cellValueTagHandler); + CountTagHandler countTagHandler = new CountTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.DIMENSION_TAG, countTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_DIMENSION_TAG, countTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_DIMENSION_TAG, countTagHandler); + HyperlinkTagHandler hyperlinkTagHandler = new HyperlinkTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.HYPERLINK_TAG, hyperlinkTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_HYPERLINK_TAG, hyperlinkTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_HYPERLINK_TAG, hyperlinkTagHandler); + MergeCellTagHandler mergeCellTagHandler = new MergeCellTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.MERGE_CELL_TAG, mergeCellTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_MERGE_CELL_TAG, mergeCellTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_MERGE_CELL_TAG, mergeCellTagHandler); + RowTagHandler rowTagHandler = new RowTagHandler(); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.ROW_TAG, rowTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.X_ROW_TAG, rowTagHandler); + XLSX_CELL_HANDLER_MAP.put(ExcelXmlConstants.NS2_ROW_TAG, rowTagHandler); + } + + public XlsxRowHandler(XlsxReadContext xlsxReadContext) { + this.xlsxReadContext = xlsxReadContext; + } + + @Override + public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { + XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(name); + if (handler == null || !handler.support(xlsxReadContext)) { + return; + } + xlsxReadContext.xlsxReadSheetHolder().getTagDeque().push(name); + handler.startElement(xlsxReadContext, name, attributes); + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + String currentTag = xlsxReadContext.xlsxReadSheetHolder().getTagDeque().peek(); + if (currentTag == null) { + return; + } + XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(currentTag); + if (handler == null || !handler.support(xlsxReadContext)) { + return; + } + handler.characters(xlsxReadContext, ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String name) throws SAXException { + XlsxTagHandler handler = XLSX_CELL_HANDLER_MAP.get(name); + if (handler == null || !handler.support(xlsxReadContext)) { + return; + } + handler.endElement(xlsxReadContext, name); + xlsxReadContext.xlsxReadSheetHolder().getTagDeque().pop(); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnore.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnore.java new file mode 100644 index 000000000..3d3ce63c0 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnore.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Ignore convert excel + * + * @author Jiaju Zhuang + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelIgnore {} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java new file mode 100644 index 000000000..1475ad3a6 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelIgnoreUnannotated.java @@ -0,0 +1,18 @@ +package com.alibaba.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Ignore all unannotated fields. + * + * @author Jiaju Zhuang + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelIgnoreUnannotated { +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java new file mode 100644 index 000000000..db5392660 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java @@ -0,0 +1,68 @@ +package com.alibaba.excel.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.alibaba.excel.converters.AutoConverter; +import com.alibaba.excel.converters.Converter; + +/** + * @author jipengfei + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ExcelProperty { + + /** + * The name of the sheet header. + * + *

+ * write: It automatically merges when you have more than one head + *

+ * read: When you have multiple heads, take the last one + * + * @return The name of the sheet header + */ + String[] value() default {""}; + + /** + * Index of column + * + * Read or write it on the index of column, If it's equal to -1, it's sorted by Java class. + * + * priority: index > order > default sort + * + * @return Index of column + */ + int index() default -1; + + /** + * Defines the sort order for an column. + * + * priority: index > order > default sort + * + * @return Order of column + */ + int order() default Integer.MAX_VALUE; + + /** + * Force the current field to use this converter. + * + * @return Converter + */ + Class> converter() default AutoConverter.class; + + /** + * + * default @see com.alibaba.excel.util.TypeUtil if default is not meet you can set format + * + * @return Format string + * @deprecated please use {@link com.alibaba.excel.annotation.format.DateTimeFormat} + */ + @Deprecated + String format() default ""; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/DateTimeFormat.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/DateTimeFormat.java new file mode 100644 index 000000000..fefcededf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/DateTimeFormat.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.annotation.format; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.alibaba.excel.enums.BooleanEnum; + +/** + * Convert date format. + * + *

+ * write: It can be used on classes {@link java.util.Date} + *

+ * read: It can be used on classes {@link String} + * + * @author Jiaju Zhuang + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface DateTimeFormat { + + /** + * + * Specific format reference {@link java.text.SimpleDateFormat} + * + * @return Format pattern + */ + String value() default ""; + + /** + * True if date uses 1904 windowing, or false if using 1900 date windowing. + * + * @return True if date uses 1904 windowing, or false if using 1900 date windowing. + */ + BooleanEnum use1904windowing() default BooleanEnum.DEFAULT; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java new file mode 100644 index 000000000..a58ef4fb4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/format/NumberFormat.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.annotation.format; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.RoundingMode; + +/** + * Convert number format. + * + *

+ * write: It can be used on classes that inherit {@link Number} + *

+ * read: It can be used on classes {@link String} + * + * @author Jiaju Zhuang + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface NumberFormat { + + /** + * + * Specific format reference {@link java.text.DecimalFormat} + * + * @return Format pattern + */ + String value() default ""; + + /** + * Rounded by default + * + * @return RoundingMode + */ + RoundingMode roundingMode() default RoundingMode.HALF_UP; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java new file mode 100644 index 000000000..00306cc0c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java @@ -0,0 +1,27 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Set the width of the table + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ColumnWidth { + + /** + * Column width + *

+ * -1 means the default column width is used + * + * @return Column width + */ + int value() default -1; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java new file mode 100644 index 000000000..fd890d481 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentFontStyle.java @@ -0,0 +1,91 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.alibaba.excel.enums.BooleanEnum; + +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Custom content styles. + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentFontStyle { + + /** + * The name for the font (i.e. Arial) + */ + String fontName() default ""; + + /** + * Height in the familiar unit of measure - points + */ + short fontHeightInPoints() default -1; + + /** + * Whether to use italics or not + */ + BooleanEnum italic() default BooleanEnum.DEFAULT; + + /** + * Whether to use a strikeout horizontal line through the text or not + */ + BooleanEnum strikeout() default BooleanEnum.DEFAULT; + + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + short color() default -1; + + /** + * Set normal, super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + short typeOffset() default -1; + + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + byte underline() default -1; + + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + int charset() default -1; + + /** + * Bold + */ + BooleanEnum bold() default BooleanEnum.DEFAULT; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java new file mode 100644 index 000000000..60654fe55 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentLoopMerge.java @@ -0,0 +1,31 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * The regions of the loop merge + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentLoopMerge { + /** + * Each row + * + * @return + */ + int eachRow() default 1; + + /** + * Extend column + * + * @return + */ + int columnExtend() default 1; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentRowHeight.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentRowHeight.java new file mode 100644 index 000000000..8f338e86b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentRowHeight.java @@ -0,0 +1,27 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Set the height of each table + * + * @author Jiaju Zhuang + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentRowHeight { + + /** + * Set the content height + *

+ * -1 mean the auto set height + * + * @return Content height + */ + short value() default -1; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java new file mode 100644 index 000000000..bfa8aa4f7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/ContentStyle.java @@ -0,0 +1,162 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.alibaba.excel.enums.BooleanEnum; +import com.alibaba.excel.enums.poi.BorderStyleEnum; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; +import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum; +import com.alibaba.excel.enums.poi.VerticalAlignmentEnum; + +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Custom content styles + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface ContentStyle { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + short dataFormat() default -1; + + /** + * Set the cell's using this style to be hidden + */ + BooleanEnum hidden() default BooleanEnum.DEFAULT; + + /** + * Set the cell's using this style to be locked + */ + BooleanEnum locked() default BooleanEnum.DEFAULT; + + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + BooleanEnum quotePrefix() default BooleanEnum.DEFAULT; + + /** + * Set the type of horizontal alignment for the cell + */ + HorizontalAlignmentEnum horizontalAlignment() default HorizontalAlignmentEnum.DEFAULT; + + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + * + */ + BooleanEnum wrapped() default BooleanEnum.DEFAULT; + + /** + * Set the type of vertical alignment for the cell + */ + VerticalAlignmentEnum verticalAlignment() default VerticalAlignmentEnum.DEFAULT; + + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + short rotation() default -1; + + /** + * Set the number of spaces to indent the text in the cell + */ + short indent() default -1; + + /** + * Set the type of border to use for the left border of the cell + */ + BorderStyleEnum borderLeft() default BorderStyleEnum.DEFAULT; + + /** + * Set the type of border to use for the right border of the cell + */ + BorderStyleEnum borderRight() default BorderStyleEnum.DEFAULT; + + /** + * Set the type of border to use for the top border of the cell + */ + BorderStyleEnum borderTop() default BorderStyleEnum.DEFAULT; + + /** + * Set the type of border to use for the bottom border of the cell + */ + BorderStyleEnum borderBottom() default BorderStyleEnum.DEFAULT; + + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + short leftBorderColor() default -1; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + * + */ + short rightBorderColor() default -1; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + * + */ + short topBorderColor() default -1; + + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + * + */ + short bottomBorderColor() default -1; + + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + FillPatternTypeEnum fillPatternType() default FillPatternTypeEnum.DEFAULT; + + /** + * Set the background fill color. + * + * @see IndexedColors + * + */ + short fillBackgroundColor() default -1; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + * + */ + short fillForegroundColor() default -1; + + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + BooleanEnum shrinkToFit() default BooleanEnum.DEFAULT; + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java new file mode 100644 index 000000000..e7f0e1ba3 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadFontStyle.java @@ -0,0 +1,91 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.alibaba.excel.enums.BooleanEnum; + +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Custom header styles. + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface HeadFontStyle { + + /** + * The name for the font (i.e. Arial) + */ + String fontName() default ""; + + /** + * Height in the familiar unit of measure - points + */ + short fontHeightInPoints() default -1; + + /** + * Whether to use italics or not + */ + BooleanEnum italic() default BooleanEnum.DEFAULT; + + /** + * Whether to use a strikeout horizontal line through the text or not + */ + BooleanEnum strikeout() default BooleanEnum.DEFAULT; + + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + short color() default -1; + + /** + * Set normal, super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + short typeOffset() default -1; + + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + byte underline() default -1; + + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + int charset() default -1; + + /** + * Bold + */ + BooleanEnum bold() default BooleanEnum.DEFAULT; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadRowHeight.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadRowHeight.java new file mode 100644 index 000000000..828d2c06e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadRowHeight.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Set the height of each table + * + * @author Jiaju Zhuang + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface HeadRowHeight { + /** + * Set the header height + *

+ * -1 mean the auto set height + * + * @return Header height + */ + short value() default -1; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java new file mode 100644 index 000000000..85900eac5 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/HeadStyle.java @@ -0,0 +1,156 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.alibaba.excel.enums.BooleanEnum; +import com.alibaba.excel.enums.poi.BorderStyleEnum; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; +import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum; +import com.alibaba.excel.enums.poi.VerticalAlignmentEnum; + +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Custom header styles + * + * @author Jiaju Zhuang + */ +@Target({ElementType.FIELD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface HeadStyle { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + short dataFormat() default -1; + + /** + * Set the cell's using this style to be hidden + */ + BooleanEnum hidden() default BooleanEnum.DEFAULT; + + /** + * Set the cell's using this style to be locked + */ + BooleanEnum locked() default BooleanEnum.DEFAULT; + + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + BooleanEnum quotePrefix() default BooleanEnum.DEFAULT; + + /** + * Set the type of horizontal alignment for the cell + */ + HorizontalAlignmentEnum horizontalAlignment() default HorizontalAlignmentEnum.DEFAULT; + + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + */ + BooleanEnum wrapped() default BooleanEnum.DEFAULT; + + /** + * Set the type of vertical alignment for the cell + */ + VerticalAlignmentEnum verticalAlignment() default VerticalAlignmentEnum.DEFAULT; + + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + short rotation() default -1; + + /** + * Set the number of spaces to indent the text in the cell + */ + short indent() default -1; + + /** + * Set the type of border to use for the left border of the cell + */ + BorderStyleEnum borderLeft() default BorderStyleEnum.DEFAULT; + + /** + * Set the type of border to use for the right border of the cell + */ + BorderStyleEnum borderRight() default BorderStyleEnum.DEFAULT; + + /** + * Set the type of border to use for the top border of the cell + */ + BorderStyleEnum borderTop() default BorderStyleEnum.DEFAULT; + + /** + * Set the type of border to use for the bottom border of the cell + */ + BorderStyleEnum borderBottom() default BorderStyleEnum.DEFAULT; + + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + short leftBorderColor() default -1; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + */ + short rightBorderColor() default -1; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + */ + short topBorderColor() default -1; + + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + */ + short bottomBorderColor() default -1; + + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + FillPatternTypeEnum fillPatternType() default FillPatternTypeEnum.DEFAULT; + + /** + * Set the background fill color. + * + * @see IndexedColors + */ + short fillBackgroundColor() default -1; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + */ + short fillForegroundColor() default -1; + + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + BooleanEnum shrinkToFit() default BooleanEnum.DEFAULT; + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java new file mode 100644 index 000000000..6c3a14806 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/annotation/write/style/OnceAbsoluteMerge.java @@ -0,0 +1,45 @@ +package com.alibaba.excel.annotation.write.style; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Merge the cells once + * + * @author Jiaju Zhuang + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface OnceAbsoluteMerge { + /** + * First row + * + * @return + */ + int firstRowIndex() default -1; + + /** + * Last row + * + * @return + */ + int lastRowIndex() default -1; + + /** + * First column + * + * @return + */ + int firstColumnIndex() default -1; + + /** + * Last row + * + * @return + */ + int lastColumnIndex() default -1; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/Ehcache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/Ehcache.java new file mode 100644 index 000000000..6387d2a11 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/Ehcache.java @@ -0,0 +1,164 @@ +package com.alibaba.excel.cache; + +import java.io.File; +import java.util.ArrayList; +import java.util.Optional; +import java.util.UUID; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.ListUtils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.ehcache.CacheManager; +import org.ehcache.config.CacheConfiguration; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.EntryUnit; +import org.ehcache.config.units.MemoryUnit; + +/** + * Default cache + * + * @author Jiaju Zhuang + */ +@Slf4j +public class Ehcache implements ReadCache { + public static final int BATCH_COUNT = 100; + /** + * Key index + */ + private int activeIndex = 0; + public static final int DEBUG_CACHE_MISS_SIZE = 1000; + public static final int DEBUG_WRITE_SIZE = 100 * 10000; + private ArrayList dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + private static final CacheManager FILE_CACHE_MANAGER; + private static final CacheConfiguration FILE_CACHE_CONFIGURATION; + private static final CacheManager ACTIVE_CACHE_MANAGER; + private static final File CACHE_PATH_FILE; + + private final CacheConfiguration activeCacheConfiguration; + /** + * Bulk storage data + */ + private org.ehcache.Cache fileCache; + /** + * Currently active cache + */ + private org.ehcache.Cache activeCache; + private String cacheAlias; + /** + * Count the number of cache misses + */ + private int cacheMiss = 0; + + @Deprecated + public Ehcache(Integer maxCacheActivateSize) { + this(maxCacheActivateSize, null); + } + + public Ehcache(Integer maxCacheActivateSize, Integer maxCacheActivateBatchCount) { + // In order to be compatible with the code + // If the user set up `maxCacheActivateSize`, then continue using it + if (maxCacheActivateSize != null) { + this.activeCacheConfiguration = CacheConfigurationBuilder + .newCacheConfigurationBuilder(Integer.class, ArrayList.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(maxCacheActivateSize, MemoryUnit.MB)) + .build(); + } else { + this.activeCacheConfiguration = CacheConfigurationBuilder + .newCacheConfigurationBuilder(Integer.class, ArrayList.class, + ResourcePoolsBuilder.newResourcePoolsBuilder() + .heap(maxCacheActivateBatchCount, EntryUnit.ENTRIES)) + .build(); + } + } + + static { + CACHE_PATH_FILE = FileUtils.createCacheTmpFile(); + FILE_CACHE_MANAGER = + CacheManagerBuilder.newCacheManagerBuilder().with(CacheManagerBuilder.persistence(CACHE_PATH_FILE)).build( + true); + ACTIVE_CACHE_MANAGER = CacheManagerBuilder.newCacheManagerBuilder().build(true); + FILE_CACHE_CONFIGURATION = CacheConfigurationBuilder + .newCacheConfigurationBuilder(Integer.class, ArrayList.class, ResourcePoolsBuilder.newResourcePoolsBuilder() + .disk(20, MemoryUnit.GB)).build(); + } + + @Override + public void init(AnalysisContext analysisContext) { + cacheAlias = UUID.randomUUID().toString(); + try { + fileCache = FILE_CACHE_MANAGER.createCache(cacheAlias, FILE_CACHE_CONFIGURATION); + } catch (IllegalStateException e) { + //fix Issue #2693,Temporary files may be deleted if there is no operation for a long time, so they need + // to be recreated. + if (CACHE_PATH_FILE.exists()) { + throw e; + } + synchronized (Ehcache.class) { + if (!CACHE_PATH_FILE.exists()) { + if (log.isDebugEnabled()) { + log.debug("cache file dir is not exist retry create"); + } + FileUtils.createDirectory(CACHE_PATH_FILE); + } + } + fileCache = FILE_CACHE_MANAGER.createCache(cacheAlias, FILE_CACHE_CONFIGURATION); + } + activeCache = ACTIVE_CACHE_MANAGER.createCache(cacheAlias, activeCacheConfiguration); + } + + @Override + public void put(String value) { + dataList.add(value); + if (dataList.size() >= BATCH_COUNT) { + fileCache.put(activeIndex, dataList); + activeIndex++; + dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + if (log.isDebugEnabled()) { + int alreadyPut = activeIndex * BATCH_COUNT + dataList.size(); + if (alreadyPut % DEBUG_WRITE_SIZE == 0) { + log.debug("Already put :{}", alreadyPut); + } + } + } + + @Override + public String get(Integer key) { + if (key == null || key < 0) { + return null; + } + int route = key / BATCH_COUNT; + ArrayList dataList = activeCache.get(route); + if (dataList == null) { + dataList = fileCache.get(route); + activeCache.put(route, dataList); + if (log.isDebugEnabled()) { + if (cacheMiss++ % DEBUG_CACHE_MISS_SIZE == 0) { + log.debug("Cache misses count:{}", cacheMiss); + } + } + } + return dataList.get(key % BATCH_COUNT); + } + + @Override + public void putFinished() { + if (CollectionUtils.isEmpty(dataList)) { + return; + } + fileCache.put(activeIndex, dataList); + } + + @Override + public void destroy() { + FILE_CACHE_MANAGER.removeCache(cacheAlias); + ACTIVE_CACHE_MANAGER.removeCache(cacheAlias); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/MapCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/MapCache.java new file mode 100644 index 000000000..82ada960a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/MapCache.java @@ -0,0 +1,38 @@ +package com.alibaba.excel.cache; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * Putting temporary data directly into a map is a little more efficient but very memory intensive + * + * @author Jiaju Zhuang + */ +public class MapCache implements ReadCache { + private final List cache = new ArrayList<>(); + + @Override + public void init(AnalysisContext analysisContext) {} + + @Override + public void put(String value) { + cache.add(value); + } + + @Override + public String get(Integer key) { + if (key == null || key < 0) { + return null; + } + return cache.get(key); + } + + @Override + public void putFinished() {} + + @Override + public void destroy() {} + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/ReadCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/ReadCache.java new file mode 100644 index 000000000..48072fae2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/ReadCache.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.cache; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * Read cache + * + * @author Jiaju Zhuang + */ +public interface ReadCache { + + /** + * Initialize cache + * + * @param analysisContext + * A context is the main anchorage point of a excel reader. + */ + void init(AnalysisContext analysisContext); + + /** + * Automatically generate the key and put it in the cache.Key start from 0 + * + * @param value + * Cache value + */ + void put(String value); + + /** + * Get value + * + * @param key + * Index + * @return Value + */ + String get(Integer key); + + /** + * It's called when all the values are put in + */ + void putFinished(); + + /** + * Called when the excel read is complete + */ + void destroy(); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/XlsCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/XlsCache.java new file mode 100644 index 000000000..261bc1875 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/XlsCache.java @@ -0,0 +1,37 @@ +package com.alibaba.excel.cache; + +import org.apache.poi.hssf.record.SSTRecord; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * + * Use SSTRecord. + * + * @author Jiaju Zhuang + */ +public class XlsCache implements ReadCache { + private final SSTRecord sstRecord; + + public XlsCache(SSTRecord sstRecord) { + this.sstRecord = sstRecord; + } + + @Override + public void init(AnalysisContext analysisContext) {} + + @Override + public void put(String value) {} + + @Override + public String get(Integer key) { + return sstRecord.getString(key).toString(); + } + + @Override + public void putFinished() {} + + @Override + public void destroy() {} + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/EternalReadCacheSelector.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/EternalReadCacheSelector.java new file mode 100644 index 000000000..9730dc08a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/EternalReadCacheSelector.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.cache.selector; + +import org.apache.poi.openxml4j.opc.PackagePart; + +import com.alibaba.excel.cache.ReadCache; + +/** + * Choose a eternal cache + * + * @author Jiaju Zhuang + **/ +public class EternalReadCacheSelector implements ReadCacheSelector { + private ReadCache readCache; + + public EternalReadCacheSelector(ReadCache readCache) { + this.readCache = readCache; + } + + @Override + public ReadCache readCache(PackagePart sharedStringsTablePackagePart) { + return readCache; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/ReadCacheSelector.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/ReadCacheSelector.java new file mode 100644 index 000000000..3a2e50246 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/ReadCacheSelector.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.cache.selector; + +import org.apache.poi.openxml4j.opc.PackagePart; + +import com.alibaba.excel.cache.ReadCache; + +/** + * Select the cache + * + * @author Jiaju Zhuang + **/ +public interface ReadCacheSelector { + + /** + * Select a cache + * + * @param sharedStringsTablePackagePart + * @return + */ + ReadCache readCache(PackagePart sharedStringsTablePackagePart); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/SimpleReadCacheSelector.java b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/SimpleReadCacheSelector.java new file mode 100644 index 000000000..278c63bec --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/cache/selector/SimpleReadCacheSelector.java @@ -0,0 +1,112 @@ +package com.alibaba.excel.cache.selector; + +import java.io.IOException; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.cache.Ehcache; +import com.alibaba.excel.cache.MapCache; +import com.alibaba.excel.cache.ReadCache; + +/** + * Simple cache selector + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class SimpleReadCacheSelector implements ReadCacheSelector { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleReadCacheSelector.class); + /** + * Convert bytes to megabytes + */ + private static final long B2M = 1000 * 1000L; + /** + * If it's less than 5M, use map cache, or use ehcache.unit MB. + */ + private static final long DEFAULT_MAX_USE_MAP_CACHE_SIZE = 5; + + /** + * Maximum batch of `SharedStrings` stored in memory. + * The batch size is 100.{@link Ehcache#BATCH_COUNT} + */ + private static final int DEFAULT_MAX_EHCACHE_ACTIVATE_BATCH_COUNT = 20; + + /** + * Shared strings exceeding this value will use {@link Ehcache},or use {@link MapCache}.unit MB. + */ + private Long maxUseMapCacheSize; + + /** + * Maximum size of cache activation.unit MB. + * + * @deprecated Please use maxCacheActivateBatchCount to control the size of the occupied memory + */ + @Deprecated + private Integer maxCacheActivateSize; + + /** + * Maximum batch of `SharedStrings` stored in memory. + * The batch size is 100.{@link Ehcache#BATCH_COUNT} + */ + private Integer maxCacheActivateBatchCount; + + public SimpleReadCacheSelector() { + } + + /** + * Parameter maxCacheActivateSize has already been abandoned + * + * @param maxUseMapCacheSize + * @param maxCacheActivateSize + */ + @Deprecated + public SimpleReadCacheSelector(Long maxUseMapCacheSize, Integer maxCacheActivateSize) { + this.maxUseMapCacheSize = maxUseMapCacheSize; + this.maxCacheActivateSize = maxCacheActivateSize; + } + + @Override + public ReadCache readCache(PackagePart sharedStringsTablePackagePart) { + long size = sharedStringsTablePackagePart.getSize(); + if (size < 0) { + try { + size = sharedStringsTablePackagePart.getInputStream().available(); + } catch (IOException e) { + LOGGER.warn("Unable to get file size, default used MapCache"); + return new MapCache(); + } + } + if (maxUseMapCacheSize == null) { + maxUseMapCacheSize = DEFAULT_MAX_USE_MAP_CACHE_SIZE; + } + if (size < maxUseMapCacheSize * B2M) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Use map cache.size:{}", size); + } + return new MapCache(); + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Use ehcache.size:{}", size); + } + + // In order to be compatible with the code + // If the user set up `maxCacheActivateSize`, then continue using it + if (maxCacheActivateSize != null) { + return new Ehcache(maxCacheActivateSize, maxCacheActivateBatchCount); + } else { + if (maxCacheActivateBatchCount == null) { + maxCacheActivateBatchCount = DEFAULT_MAX_EHCACHE_ACTIVATE_BATCH_COUNT; + } + return new Ehcache(maxCacheActivateSize, maxCacheActivateBatchCount); + } + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java new file mode 100644 index 000000000..b6fb8e481 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/BuiltinFormats.java @@ -0,0 +1,530 @@ +package com.alibaba.excel.constant; + +import java.util.Locale; +import java.util.Map; + +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.util.StringUtils; + +/** + * Excel's built-in format conversion.Currently only supports Chinese. + * + *

+ * If it is not Chinese, it is recommended to directly modify the builtinFormats, which will better support + * internationalization in the future. + * + *

+ * Specific correspondence please see: + * https://docs.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.numberingformat?view=openxml-2.8.1 + * + * @author Jiaju Zhuang + **/ +public class BuiltinFormats { + + private static final String RESERVED = "reserved-"; + + public static short GENERAL = 0; + + public static final String[] BUILTIN_FORMATS_ALL_LANGUAGES = { + // 0 + "General", + // 1 + "0", + // 2 + "0.00", + // 3 + "#,##0", + // 4 + "#,##0.00", + // 5 + "\"¥\"#,##0_);(\"¥\"#,##0)", + // 6 + "\"¥\"#,##0_);[Red](\"¥\"#,##0)", + // 7 + "\"¥\"#,##0.00_);(\"¥\"#,##0.00)", + // 8 + "\"¥\"#,##0.00_);[Red](\"¥\"#,##0.00)", + // 9 + "0%", + // 10 + "0.00%", + // 11 + "0.00E+00", + // 12 + "# ?/?", + // 13 + "# ??/??", + // 14 + // The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d". + "yyyy/m/d", + // 15 + "d-mmm-yy", + // 16 + "d-mmm", + // 17 + "mmm-yy", + // 18 + "h:mm AM/PM", + // 19 + "h:mm:ss AM/PM", + // 20 + "h:mm", + // 21 + "h:mm:ss", + // 22 + // The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy-m-d h:mm". + "yyyy-m-d h:mm", + // 23-36 No specific correspondence found in the official documentation. + // 23 + null, + // 24 + null, + // 25 + null, + // 26 + null, + // 27 + null, + // 28 + null, + // 29 + null, + // 30 + null, + // 31 + null, + // 32 + null, + // 33 + null, + // 34 + null, + // 35 + null, + // 36 + null, + // 37 + "#,##0_);(#,##0)", + // 38 + "#,##0_);[Red](#,##0)", + // 39 + "#,##0.00_);(#,##0.00)", + // 40 + "#,##0.00_);[Red](#,##0.00)", + // 41 + "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", + // 42 + "_(\"¥\"* #,##0_);_(\"¥\"* (#,##0);_(\"¥\"* \"-\"_);_(@_)", + // 43 + "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", + // 44 + "_(\"¥\"* #,##0.00_);_(\"¥\"* (#,##0.00);_(\"¥\"* \"-\"??_);_(@_)", + // 45 + "mm:ss", + // 46 + "[h]:mm:ss", + // 47 + "mm:ss.0", + // 48 + "##0.0E+0", + // 49 + "@", + }; + + public static final String[] BUILTIN_FORMATS_CN = { + // 0 + "General", + // 1 + "0", + // 2 + "0.00", + // 3 + "#,##0", + // 4 + "#,##0.00", + // 5 + "\"¥\"#,##0_);(\"¥\"#,##0)", + // 6 + "\"¥\"#,##0_);[Red](\"¥\"#,##0)", + // 7 + "\"¥\"#,##0.00_);(\"¥\"#,##0.00)", + // 8 + "\"¥\"#,##0.00_);[Red](\"¥\"#,##0.00)", + // 9 + "0%", + // 10 + "0.00%", + // 11 + "0.00E+00", + // 12 + "# ?/?", + // 13 + "# ??/??", + // 14 + // The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d". + "yyyy/m/d", + // 15 + "d-mmm-yy", + // 16 + "d-mmm", + // 17 + "mmm-yy", + // 18 + "h:mm AM/PM", + // 19 + "h:mm:ss AM/PM", + // 20 + "h:mm", + // 21 + "h:mm:ss", + // 22 + // The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy-m-d h:mm". + "yyyy-m-d h:mm", + // 23-26 No specific correspondence found in the official documentation. + // 23 + null, + // 24 + null, + // 25 + null, + // 26 + null, + // 27 + "yyyy\"年\"m\"月\"", + // 28 + "m\"月\"d\"日\"", + // 29 + "m\"月\"d\"日\"", + // 30 + "m-d-yy", + // 31 + "yyyy\"年\"m\"月\"d\"日\"", + // 32 + "h\"时\"mm\"分\"", + // 33 + "h\"时\"mm\"分\"ss\"秒\"", + // 34 + "上午/下午h\"时\"mm\"分\"", + // 35 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 36 + "yyyy\"年\"m\"月\"", + // 37 + "#,##0_);(#,##0)", + // 38 + "#,##0_);[Red](#,##0)", + // 39 + "#,##0.00_);(#,##0.00)", + // 40 + "#,##0.00_);[Red](#,##0.00)", + // 41 + "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", + // 42 + "_(\"¥\"* #,##0_);_(\"¥\"* (#,##0);_(\"¥\"* \"-\"_);_(@_)", + // 43 + "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", + // 44 + "_(\"¥\"* #,##0.00_);_(\"¥\"* (#,##0.00);_(\"¥\"* \"-\"??_);_(@_)", + // 45 + "mm:ss", + // 46 + "[h]:mm:ss", + // 47 + "mm:ss.0", + // 48 + "##0.0E+0", + // 49 + "@", + // 50 + "yyyy\"年\"m\"月\"", + // 51 + "m\"月\"d\"日\"", + // 52 + "yyyy\"年\"m\"月\"", + // 53 + "m\"月\"d\"日\"", + // 54 + "m\"月\"d\"日\"", + // 55 + "上午/下午h\"时\"mm\"分\"", + // 56 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 57 + "yyyy\"年\"m\"月\"", + // 58 + "m\"月\"d\"日\"", + // 59 + "t0", + // 60 + "t0.00", + // 61 + "t#,##0", + // 62 + "t#,##0.00", + // 63-66 No specific correspondence found in the official documentation. + // 63 + null, + // 64 + null, + // 65 + null, + // 66 + null, + // 67 + "t0%", + // 68 + "t0.00%", + // 69 + "t# ?/?", + // 70 + "t# ??/??", + // 71 + "ว/ด/ปปปป", + // 72 + "ว-ดดด-ปป", + // 73 + "ว-ดดด", + // 74 + "ดดด-ปป", + // 75 + "ช:นน", + // 76 + "ช:นน:ทท", + // 77 + "ว/ด/ปปปป ช:นน", + // 78 + "นน:ทท", + // 79 + "[ช]:นน:ทท", + // 80 + "นน:ทท.0", + // 81 + "d/m/bb", + // end + }; + + public static final String[] BUILTIN_FORMATS_US = { + // 0 + "General", + // 1 + "0", + // 2 + "0.00", + // 3 + "#,##0", + // 4 + "#,##0.00", + // 5 + "\"$\"#,##0_);(\"$\"#,##0)", + // 6 + "\"$\"#,##0_);[Red](\"$\"#,##0)", + // 7 + "\"$\"#,##0.00_);(\"$\"#,##0.00)", + // 8 + "\"$\"#,##0.00_);[Red](\"$\"#,##0.00)", + // 9 + "0%", + // 10 + "0.00%", + // 11 + "0.00E+00", + // 12 + "# ?/?", + // 13 + "# ??/??", + // 14 + // The official documentation shows "m/d/yy", but the actual test is "yyyy/m/d". + "yyyy/m/d", + // 15 + "d-mmm-yy", + // 16 + "d-mmm", + // 17 + "mmm-yy", + // 18 + "h:mm AM/PM", + // 19 + "h:mm:ss AM/PM", + // 20 + "h:mm", + // 21 + "h:mm:ss", + // 22 + // The official documentation shows "m/d/yy h:mm", but the actual test is "yyyy-m-d h:mm". + "yyyy-m-d h:mm", + // 23-26 No specific correspondence found in the official documentation. + // 23 + null, + // 24 + null, + // 25 + null, + // 26 + null, + // 27 + "yyyy\"年\"m\"月\"", + // 28 + "m\"月\"d\"日\"", + // 29 + "m\"月\"d\"日\"", + // 30 + "m-d-yy", + // 31 + "yyyy\"年\"m\"月\"d\"日\"", + // 32 + "h\"时\"mm\"分\"", + // 33 + "h\"时\"mm\"分\"ss\"秒\"", + // 34 + "上午/下午h\"时\"mm\"分\"", + // 35 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 36 + "yyyy\"年\"m\"月\"", + // 37 + "#,##0_);(#,##0)", + // 38 + "#,##0_);[Red](#,##0)", + // 39 + "#,##0.00_);(#,##0.00)", + // 40 + "#,##0.00_);[Red](#,##0.00)", + // 41 + "_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)", + // 42 + "_(\"$\"* #,##0_);_(\"$\"* (#,##0);_(\"$\"* \"-\"_);_(@_)", + // 43 + "_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)", + // 44 + "_(\"$\"* #,##0.00_);_(\"$\"* (#,##0.00);_(\"$\"* \"-\"??_);_(@_)", + // 45 + "mm:ss", + // 46 + "[h]:mm:ss", + // 47 + "mm:ss.0", + // 48 + "##0.0E+0", + // 49 + "@", + // 50 + "yyyy\"年\"m\"月\"", + // 51 + "m\"月\"d\"日\"", + // 52 + "yyyy\"年\"m\"月\"", + // 53 + "m\"月\"d\"日\"", + // 54 + "m\"月\"d\"日\"", + // 55 + "上午/下午h\"时\"mm\"分\"", + // 56 + "上午/下午h\"时\"mm\"分\"ss\"秒\"", + // 57 + "yyyy\"年\"m\"月\"", + // 58 + "m\"月\"d\"日\"", + // 59 + "t0", + // 60 + "t0.00", + // 61 + "t#,##0", + // 62 + "t#,##0.00", + // 63-66 No specific correspondence found in the official documentation. + // 63 + null, + // 64 + null, + // 65 + null, + // 66 + null, + // 67 + "t0%", + // 68 + "t0.00%", + // 69 + "t# ?/?", + // 70 + "t# ??/??", + // 71 + "ว/ด/ปปปป", + // 72 + "ว-ดดด-ปป", + // 73 + "ว-ดดด", + // 74 + "ดดด-ปป", + // 75 + "ช:นน", + // 76 + "ช:นน:ทท", + // 77 + "ว/ด/ปปปป ช:นน", + // 78 + "นน:ทท", + // 79 + "[ช]:นน:ทท", + // 80 + "นน:ทท.0", + // 81 + "d/m/bb", + // end + }; + + public static final Map BUILTIN_FORMATS_MAP_CN = buildMap(BUILTIN_FORMATS_CN); + public static final Map BUILTIN_FORMATS_MAP_US = buildMap(BUILTIN_FORMATS_US); + public static final short MIN_CUSTOM_DATA_FORMAT_INDEX = 82; + + public static String getBuiltinFormat(Short index, String defaultFormat, Locale locale) { + if (index == null || index <= 0) { + return defaultFormat; + } + + // Give priority to checking if it is the default value for all languages + if (index < BUILTIN_FORMATS_ALL_LANGUAGES.length) { + String format = BUILTIN_FORMATS_ALL_LANGUAGES[index]; + if (format != null) { + return format; + } + } + + // In other cases, give priority to using the externally provided format + if (!StringUtils.isEmpty(defaultFormat) && !defaultFormat.startsWith(RESERVED)) { + return defaultFormat; + } + + // Finally, try using the built-in format + String[] builtinFormat = switchBuiltinFormats(locale); + if (index >= builtinFormat.length) { + return defaultFormat; + } + return builtinFormat[index]; + } + + public static String[] switchBuiltinFormats(Locale locale) { + if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) { + return BUILTIN_FORMATS_US; + } + return BUILTIN_FORMATS_CN; + } + + public static Map switchBuiltinFormatsMap(Locale locale) { + if (locale != null && Locale.US.getCountry().equals(locale.getCountry())) { + return BUILTIN_FORMATS_MAP_US; + } + return BUILTIN_FORMATS_MAP_CN; + } + + private static Map buildMap(String[] builtinFormats) { + Map map = MapUtils.newHashMapWithExpectedSize(builtinFormats.length); + for (int i = 0; i < builtinFormats.length; i++) { + map.put(builtinFormats[i], (short)i); + } + return map; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java new file mode 100644 index 000000000..cdc414fa9 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/EasyExcelConstants.java @@ -0,0 +1,19 @@ +package com.alibaba.excel.constant; + +import java.math.MathContext; +import java.math.RoundingMode; + +/** + * Used to store constant + * + * @author Jiaju Zhuang + */ +public class EasyExcelConstants { + + /** + * Excel by default with 15 to store Numbers, and the double in Java can use to store number 17, led to the accuracy + * will be a problem. So you need to set up 15 to deal with precision + */ + public static final MathContext EXCEL_MATH_CONTEXT = new MathContext(15, RoundingMode.HALF_UP); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java new file mode 100644 index 000000000..c1d335924 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java @@ -0,0 +1,99 @@ +package com.alibaba.excel.constant; + +/** + * @author jipengfei + */ +public class ExcelXmlConstants { + public static final String DIMENSION_TAG = "dimension"; + public static final String ROW_TAG = "row"; + public static final String CELL_FORMULA_TAG = "f"; + public static final String CELL_VALUE_TAG = "v"; + /** + * When the data is "inlineStr" his tag is "t" + */ + public static final String CELL_INLINE_STRING_VALUE_TAG = "t"; + public static final String CELL_TAG = "c"; + public static final String MERGE_CELL_TAG = "mergeCell"; + public static final String HYPERLINK_TAG = "hyperlink"; + + public static final String X_DIMENSION_TAG = "x:dimension"; + public static final String NS2_DIMENSION_TAG = "ns2:dimension"; + + public static final String X_ROW_TAG = "x:row"; + public static final String NS2_ROW_TAG = "ns2:row"; + + public static final String X_CELL_FORMULA_TAG = "x:f"; + public static final String NS2_CELL_FORMULA_TAG = "ns2:f"; + public static final String X_CELL_VALUE_TAG = "x:v"; + public static final String NS2_CELL_VALUE_TAG = "ns2:v"; + + /** + * When the data is "inlineStr" his tag is "t" + */ + public static final String X_CELL_INLINE_STRING_VALUE_TAG = "x:t"; + public static final String NS2_CELL_INLINE_STRING_VALUE_TAG = "ns2:t"; + + public static final String X_CELL_TAG = "x:c"; + public static final String NS2_CELL_TAG = "ns2:c"; + public static final String X_MERGE_CELL_TAG = "x:mergeCell"; + public static final String NS2_MERGE_CELL_TAG = "ns2:mergeCell"; + public static final String X_HYPERLINK_TAG = "x:hyperlink"; + public static final String NS2_HYPERLINK_TAG = "ns2:hyperlink"; + + /** + * s attribute + */ + public static final String ATTRIBUTE_S = "s"; + /** + * ref attribute + */ + public static final String ATTRIBUTE_REF = "ref"; + /** + * r attribute + */ + public static final String ATTRIBUTE_R = "r"; + /** + * t attribute + */ + public static final String ATTRIBUTE_T = "t"; + /** + * location attribute + */ + public static final String ATTRIBUTE_LOCATION = "location"; + + /** + * rId attribute + */ + public static final String ATTRIBUTE_RID = "r:id"; + + /** + * Cell range split + */ + public static final String CELL_RANGE_SPLIT = ":"; + + // The following is a constant read the `SharedStrings.xml` + + /** + * text + */ + public static final String SHAREDSTRINGS_T_TAG = "t"; + public static final String SHAREDSTRINGS_X_T_TAG = "x:t"; + public static final String SHAREDSTRINGS_NS2_T_TAG = "ns2:t"; + + + /** + * SharedStringItem + */ + public static final String SHAREDSTRINGS_SI_TAG = "si"; + public static final String SHAREDSTRINGS_X_SI_TAG = "x:si"; + public static final String SHAREDSTRINGS_NS2_SI_TAG = "ns2:si"; + + + /** + * Mac 2016 2017 will have this extra field to ignore + */ + public static final String SHAREDSTRINGS_RPH_TAG = "rPh"; + public static final String SHAREDSTRINGS_X_RPH_TAG = "x:rPh"; + public static final String SHAREDSTRINGS_NS2_RPH_TAG = "ns2:rPh"; + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/constant/OrderConstant.java b/easyexcel-core/src/main/java/com/alibaba/excel/constant/OrderConstant.java new file mode 100644 index 000000000..309b62971 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/constant/OrderConstant.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.constant; + +/** + * Order constant. + * + * @author Jiaju Zhuang + */ +public class OrderConstant { + + /** + * The system's own style + */ + public static int DEFAULT_DEFINE_STYLE = -70000; + + /** + * Annotation style definition + */ + public static int ANNOTATION_DEFINE_STYLE = -60000; + + /** + * Define style. + */ + public static final int DEFINE_STYLE = -50000; + + /** + * default order. + */ + public static int DEFAULT_ORDER = 0; + + /** + * Sorting of styles written to cells. + */ + public static int FILL_STYLE = 50000; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContext.java new file mode 100644 index 000000000..37039cbcf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContext.java @@ -0,0 +1,147 @@ +package com.alibaba.excel.context; + +import java.io.InputStream; +import java.util.List; + +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadHolder; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.processor.AnalysisEventProcessor; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * + * A context is the main anchorage point of a excel reader. + * + * @author jipengfei + */ +public interface AnalysisContext { + /** + * Select the current table + * + * @param readSheet + * sheet to read + */ + void currentSheet(ReadSheet readSheet); + + /** + * All information about the workbook you are currently working on + * + * @return Current workbook holder + */ + ReadWorkbookHolder readWorkbookHolder(); + + /** + * All information about the sheet you are currently working on + * + * @return Current sheet holder + */ + ReadSheetHolder readSheetHolder(); + + /** + * Set row of currently operated cell + * + * @param readRowHolder + * Current row holder + */ + void readRowHolder(ReadRowHolder readRowHolder); + + /** + * Row of currently operated cell + * + * @return Current row holder + */ + ReadRowHolder readRowHolder(); + + /** + * The current read operation corresponds to the readSheetHolder or readWorkbookHolder + * + * @return Current holder + */ + ReadHolder currentReadHolder(); + + /** + * Custom attribute + * + * @return + */ + Object getCustom(); + + /** + * Event processor + * + * @return + */ + AnalysisEventProcessor analysisEventProcessor(); + + /** + * Data that the customer needs to read + * + * @return + */ + List readSheetList(); + + /** + * Data that the customer needs to read + * + * @param readSheetList + */ + void readSheetList(List readSheetList); + + /** + * + * get excel type + * + * @return excel type + * @deprecated please use {@link #readWorkbookHolder()} + */ + @Deprecated + ExcelTypeEnum getExcelType(); + + /** + * get in io + * + * @return file io + * @deprecated please use {@link #readWorkbookHolder()} + */ + @Deprecated + InputStream getInputStream(); + + /** + * get current row + * + * @return + * @deprecated please use {@link #readRowHolder()} + */ + @Deprecated + Integer getCurrentRowNum(); + + /** + * get total row ,Data may be inaccurate + * + * @return + * @deprecated please use {@link #readRowHolder()} + */ + @Deprecated + Integer getTotalCount(); + + /** + * get current result + * + * @return get current result + * @deprecated please use {@link #readRowHolder()} + */ + @Deprecated + Object getCurrentRowAnalysisResult(); + + /** + * Interrupt execution + * + * @deprecated please use {@link AnalysisEventListener#hasNext(AnalysisContext)} + */ + @Deprecated + void interrupt(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java new file mode 100644 index 000000000..5385831bd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java @@ -0,0 +1,174 @@ +package com.alibaba.excel.context; + +import java.io.InputStream; +import java.util.List; + +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadHolder; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; +import com.alibaba.excel.read.processor.AnalysisEventProcessor; +import com.alibaba.excel.read.processor.DefaultAnalysisEventProcessor; +import com.alibaba.excel.support.ExcelTypeEnum; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author jipengfei + */ +@Slf4j +public class AnalysisContextImpl implements AnalysisContext { + /** + * The Workbook currently written + */ + private ReadWorkbookHolder readWorkbookHolder; + /** + * Current sheet holder + */ + private ReadSheetHolder readSheetHolder; + /** + * Current row holder + */ + private ReadRowHolder readRowHolder; + /** + * Configuration of currently operated cell + */ + private ReadHolder currentReadHolder; + /** + * Event processor + */ + private final AnalysisEventProcessor analysisEventProcessor; + + public AnalysisContextImpl(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { + if (readWorkbook == null) { + throw new IllegalArgumentException("Workbook argument cannot be null"); + } + switch (actualExcelType) { + case XLS: + readWorkbookHolder = new XlsReadWorkbookHolder(readWorkbook); + break; + case XLSX: + readWorkbookHolder = new XlsxReadWorkbookHolder(readWorkbook); + break; + case CSV: + readWorkbookHolder = new CsvReadWorkbookHolder(readWorkbook); + break; + default: + break; + } + currentReadHolder = readWorkbookHolder; + analysisEventProcessor = new DefaultAnalysisEventProcessor(); + if (log.isDebugEnabled()) { + log.debug("Initialization 'AnalysisContextImpl' complete"); + } + } + + @Override + public void currentSheet(ReadSheet readSheet) { + switch (readWorkbookHolder.getExcelType()) { + case XLS: + readSheetHolder = new XlsReadSheetHolder(readSheet, readWorkbookHolder); + break; + case XLSX: + readSheetHolder = new XlsxReadSheetHolder(readSheet, readWorkbookHolder); + break; + case CSV: + readSheetHolder = new CsvReadSheetHolder(readSheet, readWorkbookHolder); + break; + default: + break; + } + currentReadHolder = readSheetHolder; + if (readWorkbookHolder.getHasReadSheet().contains(readSheetHolder.getSheetNo())) { + throw new ExcelAnalysisException("Cannot read sheet repeatedly."); + } + readWorkbookHolder.getHasReadSheet().add(readSheetHolder.getSheetNo()); + if (log.isDebugEnabled()) { + log.debug("Began to read:{}", readSheetHolder); + } + } + + @Override + public ReadWorkbookHolder readWorkbookHolder() { + return readWorkbookHolder; + } + + @Override + public ReadSheetHolder readSheetHolder() { + return readSheetHolder; + } + + @Override + public ReadRowHolder readRowHolder() { + return readRowHolder; + } + + @Override + public void readRowHolder(ReadRowHolder readRowHolder) { + this.readRowHolder = readRowHolder; + } + + @Override + public ReadHolder currentReadHolder() { + return currentReadHolder; + } + + @Override + public Object getCustom() { + return readWorkbookHolder.getCustomObject(); + } + + @Override + public AnalysisEventProcessor analysisEventProcessor() { + return analysisEventProcessor; + } + + @Override + public List readSheetList() { + return null; + } + + @Override + public void readSheetList(List readSheetList) { + + } + + @Override + public ExcelTypeEnum getExcelType() { + return readWorkbookHolder.getExcelType(); + } + + @Override + public InputStream getInputStream() { + return readWorkbookHolder.getInputStream(); + } + + @Override + public Integer getCurrentRowNum() { + return readRowHolder.getRowIndex(); + } + + @Override + public Integer getTotalCount() { + return readSheetHolder.getTotal(); + } + + @Override + public Object getCurrentRowAnalysisResult() { + return readRowHolder.getCurrentRowAnalysisResult(); + } + + @Override + public void interrupt() { + throw new ExcelAnalysisException("interrupt error"); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContext.java new file mode 100644 index 000000000..0a59e1d09 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContext.java @@ -0,0 +1,110 @@ +package com.alibaba.excel.context; + +import java.io.OutputStream; + +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; + +import com.alibaba.excel.enums.WriteTypeEnum; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.holder.WriteHolder; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * Write context + * + * @author jipengfei + */ +public interface WriteContext { + /** + * If the current sheet already exists, select it; if not, create it + * + * @param writeSheet + * Current sheet + * @param writeType + */ + void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType); + + /** + * If the current table already exists, select it; if not, create it + * + * @param writeTable + */ + void currentTable(WriteTable writeTable); + + /** + * All information about the workbook you are currently working on + * + * @return + */ + WriteWorkbookHolder writeWorkbookHolder(); + + /** + * All information about the sheet you are currently working on + * + * @return + */ + WriteSheetHolder writeSheetHolder(); + + /** + * All information about the table you are currently working on + * + * @return + */ + WriteTableHolder writeTableHolder(); + + /** + * Configuration of currently operated cell. May be 'writeSheetHolder' or 'writeTableHolder' or + * 'writeWorkbookHolder' + * + * @return + */ + WriteHolder currentWriteHolder(); + + /** + * close + * + * @param onException + */ + void finish(boolean onException); + + /** + * Current sheet + * + * @return + * @deprecated please us e{@link #writeSheetHolder()} + */ + @Deprecated + Sheet getCurrentSheet(); + + /** + * Need head + * + * @return + * @deprecated please us e{@link #writeSheetHolder()} + */ + @Deprecated + boolean needHead(); + + /** + * Get outputStream + * + * @return + * @deprecated please us e{@link #writeWorkbookHolder()} ()} + */ + @Deprecated + OutputStream getOutputStream(); + + /** + * Get workbook + * + * @return + * @deprecated please us e{@link #writeWorkbookHolder()} ()} + */ + @Deprecated + Workbook getWorkbook(); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java new file mode 100644 index 000000000..9fff84491 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -0,0 +1,524 @@ +package com.alibaba.excel.context; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.Map; +import java.util.UUID; + +import com.alibaba.excel.enums.WriteTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.util.WorkBookUtil; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.holder.WriteHolder; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.alibaba.excel.write.property.ExcelWriteHeadProperty; + +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A context is the main anchorage point of a excel writer. + * + * @author jipengfei + */ +public class WriteContextImpl implements WriteContext { + + private static final Logger LOGGER = LoggerFactory.getLogger(WriteContextImpl.class); + private static final String NO_SHEETS = "no sheets"; + + /** + * The Workbook currently written + */ + private WriteWorkbookHolder writeWorkbookHolder; + /** + * Current sheet holder + */ + private WriteSheetHolder writeSheetHolder; + /** + * The table currently written + */ + private WriteTableHolder writeTableHolder; + /** + * Configuration of currently operated cell + */ + private WriteHolder currentWriteHolder; + /** + * Prevent multiple shutdowns + */ + private boolean finished = false; + + public WriteContextImpl(WriteWorkbook writeWorkbook) { + if (writeWorkbook == null) { + throw new IllegalArgumentException("Workbook argument cannot be null"); + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Begin to Initialization 'WriteContextImpl'"); + } + initCurrentWorkbookHolder(writeWorkbook); + + WorkbookWriteHandlerContext workbookWriteHandlerContext = WriteHandlerUtils.createWorkbookWriteHandlerContext( + this); + WriteHandlerUtils.beforeWorkbookCreate(workbookWriteHandlerContext); + try { + WorkBookUtil.createWorkBook(writeWorkbookHolder); + } catch (Exception e) { + throw new ExcelGenerateException("Create workbook failure", e); + } + WriteHandlerUtils.afterWorkbookCreate(workbookWriteHandlerContext); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Initialization 'WriteContextImpl' complete"); + } + } + + private void initCurrentWorkbookHolder(WriteWorkbook writeWorkbook) { + writeWorkbookHolder = new WriteWorkbookHolder(writeWorkbook); + currentWriteHolder = writeWorkbookHolder; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CurrentConfiguration is writeWorkbookHolder"); + } + } + + /** + * @param writeSheet + */ + @Override + public void currentSheet(WriteSheet writeSheet, WriteTypeEnum writeType) { + if (writeSheet == null) { + throw new IllegalArgumentException("Sheet argument cannot be null"); + } + if (selectSheetFromCache(writeSheet)) { + return; + } + + initCurrentSheetHolder(writeSheet); + + // Workbook handler need to supplementary execution + WorkbookWriteHandlerContext workbookWriteHandlerContext = WriteHandlerUtils.createWorkbookWriteHandlerContext( + this); + WriteHandlerUtils.beforeWorkbookCreate(workbookWriteHandlerContext, true); + WriteHandlerUtils.afterWorkbookCreate(workbookWriteHandlerContext, true); + + // Initialization current sheet + initSheet(writeType); + } + + private boolean selectSheetFromCache(WriteSheet writeSheet) { + writeSheetHolder = null; + Integer sheetNo = writeSheet.getSheetNo(); + if (sheetNo == null && StringUtils.isEmpty(writeSheet.getSheetName())) { + sheetNo = 0; + } + if (sheetNo != null) { + writeSheetHolder = writeWorkbookHolder.getHasBeenInitializedSheetIndexMap().get(sheetNo); + } + if (writeSheetHolder == null && !StringUtils.isEmpty(writeSheet.getSheetName())) { + writeSheetHolder = writeWorkbookHolder.getHasBeenInitializedSheetNameMap().get(writeSheet.getSheetName()); + } + if (writeSheetHolder == null) { + return false; + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Sheet:{},{} is already existed", writeSheet.getSheetNo(), writeSheet.getSheetName()); + } + writeSheetHolder.setNewInitialization(Boolean.FALSE); + writeTableHolder = null; + currentWriteHolder = writeSheetHolder; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CurrentConfiguration is writeSheetHolder"); + } + return true; + } + + private void initCurrentSheetHolder(WriteSheet writeSheet) { + writeSheetHolder = new WriteSheetHolder(writeSheet, writeWorkbookHolder); + writeTableHolder = null; + currentWriteHolder = writeSheetHolder; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CurrentConfiguration is writeSheetHolder"); + } + } + + private void initSheet(WriteTypeEnum writeType) { + SheetWriteHandlerContext sheetWriteHandlerContext = WriteHandlerUtils.createSheetWriteHandlerContext(this); + WriteHandlerUtils.beforeSheetCreate(sheetWriteHandlerContext); + Sheet currentSheet; + try { + if (writeSheetHolder.getSheetNo() != null) { + // When the add default sort order of appearance + if (WriteTypeEnum.ADD.equals(writeType) && writeWorkbookHolder.getTempTemplateInputStream() == null) { + currentSheet = createSheet(); + } else { + currentSheet = writeWorkbookHolder.getWorkbook().getSheetAt(writeSheetHolder.getSheetNo()); + writeSheetHolder + .setCachedSheet( + writeWorkbookHolder.getCachedWorkbook().getSheetAt(writeSheetHolder.getSheetNo())); + } + } else { + // sheet name must not null + currentSheet = writeWorkbookHolder.getWorkbook().getSheet(writeSheetHolder.getSheetName()); + writeSheetHolder + .setCachedSheet(writeWorkbookHolder.getCachedWorkbook().getSheet(writeSheetHolder.getSheetName())); + } + } catch (IllegalArgumentException e) { + if (e.getMessage() != null && e.getMessage().contains(NO_SHEETS)) { + currentSheet = createSheet(); + } else { + throw e; + } + } + if (currentSheet == null) { + currentSheet = createSheet(); + } + writeSheetHolder.setSheet(currentSheet); + WriteHandlerUtils.afterSheetCreate(sheetWriteHandlerContext); + if (WriteTypeEnum.ADD.equals(writeType)) { + // Initialization head + initHead(writeSheetHolder.excelWriteHeadProperty()); + } + writeWorkbookHolder.getHasBeenInitializedSheetIndexMap().put(writeSheetHolder.getSheetNo(), writeSheetHolder); + writeWorkbookHolder.getHasBeenInitializedSheetNameMap().put(writeSheetHolder.getSheetName(), writeSheetHolder); + } + + private Sheet createSheet() { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Can not find sheet:{} ,now create it", writeSheetHolder.getSheetNo()); + } + if (StringUtils.isEmpty(writeSheetHolder.getSheetName())) { + writeSheetHolder.setSheetName(writeSheetHolder.getSheetNo().toString()); + } + Sheet currentSheet = + WorkBookUtil.createSheet(writeWorkbookHolder.getWorkbook(), writeSheetHolder.getSheetName()); + writeSheetHolder.setCachedSheet(currentSheet); + return currentSheet; + } + + public void initHead(ExcelWriteHeadProperty excelWriteHeadProperty) { + if (!currentWriteHolder.needHead() || !currentWriteHolder.excelWriteHeadProperty().hasHead()) { + return; + } + int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); + newRowIndex += currentWriteHolder.relativeHeadRowIndex(); + // Combined head + if (currentWriteHolder.automaticMergeHead()) { + addMergedRegionToCurrentSheet(excelWriteHeadProperty, newRowIndex); + } + for (int relativeRowIndex = 0, i = newRowIndex; i < excelWriteHeadProperty.getHeadRowNumber() + + newRowIndex; i++, relativeRowIndex++) { + + RowWriteHandlerContext rowWriteHandlerContext = WriteHandlerUtils.createRowWriteHandlerContext(this, + newRowIndex, relativeRowIndex, Boolean.TRUE); + WriteHandlerUtils.beforeRowCreate(rowWriteHandlerContext); + + Row row = WorkBookUtil.createRow(writeSheetHolder.getSheet(), i); + rowWriteHandlerContext.setRow(row); + + WriteHandlerUtils.afterRowCreate(rowWriteHandlerContext); + addOneRowOfHeadDataToExcel(row, i, excelWriteHeadProperty.getHeadMap(), relativeRowIndex); + WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext); + } + } + + private void addMergedRegionToCurrentSheet(ExcelWriteHeadProperty excelWriteHeadProperty, int rowIndex) { + for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelWriteHeadProperty.headCellRangeList()) { + writeSheetHolder.getSheet() + .addMergedRegionUnsafe(new CellRangeAddress(cellRangeModel.getFirstRow() + rowIndex, + cellRangeModel.getLastRow() + rowIndex, cellRangeModel.getFirstCol(), cellRangeModel.getLastCol())); + } + } + + private void addOneRowOfHeadDataToExcel(Row row, Integer rowIndex, Map headMap, + int relativeRowIndex) { + for (Map.Entry entry : headMap.entrySet()) { + Head head = entry.getValue(); + int columnIndex = entry.getKey(); + ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null, + currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), head.getFieldName(), currentWriteHolder); + + CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(this, row, + rowIndex, head, columnIndex, relativeRowIndex, Boolean.TRUE, excelContentProperty); + WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); + + Cell cell = row.createCell(columnIndex); + cellWriteHandlerContext.setCell(cell); + + WriteHandlerUtils.afterCellCreate(cellWriteHandlerContext); + + WriteCellData writeCellData = new WriteCellData<>(head.getHeadNameList().get(relativeRowIndex)); + cell.setCellValue(writeCellData.getStringValue()); + cellWriteHandlerContext.setCellDataList(ListUtils.newArrayList(writeCellData)); + cellWriteHandlerContext.setFirstCellData(writeCellData); + + WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + } + } + + @Override + public void currentTable(WriteTable writeTable) { + if (writeTable == null) { + return; + } + if (writeTable.getTableNo() == null || writeTable.getTableNo() <= 0) { + writeTable.setTableNo(0); + } + if (writeSheetHolder.getHasBeenInitializedTable().containsKey(writeTable.getTableNo())) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Table:{} is already existed", writeTable.getTableNo()); + } + writeTableHolder = writeSheetHolder.getHasBeenInitializedTable().get(writeTable.getTableNo()); + writeTableHolder.setNewInitialization(Boolean.FALSE); + currentWriteHolder = writeTableHolder; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CurrentConfiguration is writeTableHolder"); + } + return; + } + + initCurrentTableHolder(writeTable); + + // Workbook and sheet handler need to supplementary execution + WorkbookWriteHandlerContext workbookWriteHandlerContext = WriteHandlerUtils.createWorkbookWriteHandlerContext( + this); + WriteHandlerUtils.beforeWorkbookCreate(workbookWriteHandlerContext, true); + WriteHandlerUtils.afterWorkbookCreate(workbookWriteHandlerContext, true); + + SheetWriteHandlerContext sheetWriteHandlerContext = WriteHandlerUtils.createSheetWriteHandlerContext(this); + WriteHandlerUtils.beforeSheetCreate(sheetWriteHandlerContext, true); + WriteHandlerUtils.afterSheetCreate(sheetWriteHandlerContext, true); + + initHead(writeTableHolder.excelWriteHeadProperty()); + } + + private void initCurrentTableHolder(WriteTable writeTable) { + writeTableHolder = new WriteTableHolder(writeTable, writeSheetHolder); + writeSheetHolder.getHasBeenInitializedTable().put(writeTable.getTableNo(), writeTableHolder); + currentWriteHolder = writeTableHolder; + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("CurrentConfiguration is writeTableHolder"); + } + } + + @Override + public WriteWorkbookHolder writeWorkbookHolder() { + return writeWorkbookHolder; + } + + @Override + public WriteSheetHolder writeSheetHolder() { + return writeSheetHolder; + } + + @Override + public WriteTableHolder writeTableHolder() { + return writeTableHolder; + } + + @Override + public WriteHolder currentWriteHolder() { + return currentWriteHolder; + } + + @Override + public void finish(boolean onException) { + if (finished) { + return; + } + finished = true; + WriteHandlerUtils.afterWorkbookDispose(writeWorkbookHolder.getWorkbookWriteHandlerContext()); + if (writeWorkbookHolder == null) { + return; + } + Throwable throwable = null; + boolean isOutputStreamEncrypt = false; + // Determine if you need to write excel + boolean writeExcel = !onException; + if (writeWorkbookHolder.getWriteExcelOnException()) { + writeExcel = Boolean.TRUE; + } + // No data is written if an exception is thrown + if (writeExcel) { + try { + isOutputStreamEncrypt = doOutputStreamEncrypt07(); + } catch (Throwable t) { + throwable = t; + } + } + if (!isOutputStreamEncrypt) { + try { + if (writeExcel) { + writeWorkbookHolder.getWorkbook().write(writeWorkbookHolder.getOutputStream()); + } + writeWorkbookHolder.getWorkbook().close(); + } catch (Throwable t) { + throwable = t; + } + } + try { + Workbook workbook = writeWorkbookHolder.getWorkbook(); + if (workbook instanceof SXSSFWorkbook) { + ((SXSSFWorkbook)workbook).dispose(); + } + } catch (Throwable t) { + throwable = t; + } + try { + if (writeWorkbookHolder.getAutoCloseStream() && writeWorkbookHolder.getOutputStream() != null) { + writeWorkbookHolder.getOutputStream().close(); + } + } catch (Throwable t) { + throwable = t; + } + if (writeExcel && !isOutputStreamEncrypt) { + try { + doFileEncrypt07(); + } catch (Throwable t) { + throwable = t; + } + } + try { + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeWorkbookHolder.getTempTemplateInputStream().close(); + } + } catch (Throwable t) { + throwable = t; + } + clearEncrypt03(); + removeThreadLocalCache(); + if (throwable != null) { + throw new ExcelGenerateException("Can not close IO.", throwable); + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Finished write."); + } + } + + private void removeThreadLocalCache() { + NumberDataFormatterUtils.removeThreadLocalCache(); + DateUtils.removeThreadLocalCache(); + ClassUtils.removeThreadLocalCache(); + } + + @Override + public Sheet getCurrentSheet() { + return writeSheetHolder.getSheet(); + } + + @Override + public boolean needHead() { + return writeSheetHolder.needHead(); + } + + @Override + public OutputStream getOutputStream() { + return writeWorkbookHolder.getOutputStream(); + } + + @Override + public Workbook getWorkbook() { + return writeWorkbookHolder.getWorkbook(); + } + + private void clearEncrypt03() { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLS.equals(writeWorkbookHolder.getExcelType())) { + return; + } + Biff8EncryptionKey.setCurrentUserPassword(null); + } + + /** + * To encrypt + */ + private boolean doOutputStreamEncrypt07() throws Exception { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { + return false; + } + if (writeWorkbookHolder.getFile() != null) { + return false; + } + File tempXlsx = FileUtils.createTmpFile(UUID.randomUUID() + ".xlsx"); + FileOutputStream tempFileOutputStream = new FileOutputStream(tempXlsx); + try { + writeWorkbookHolder.getWorkbook().write(tempFileOutputStream); + } finally { + try { + writeWorkbookHolder.getWorkbook().close(); + tempFileOutputStream.close(); + } catch (Exception e) { + if (!tempXlsx.delete()) { + throw new ExcelGenerateException("Can not delete temp File!"); + } + throw e; + } + } + try (POIFSFileSystem fileSystem = openFileSystemAndEncrypt(tempXlsx)) { + fileSystem.writeFilesystem(writeWorkbookHolder.getOutputStream()); + } finally { + if (!tempXlsx.delete()) { + throw new ExcelGenerateException("Can not delete temp File!"); + } + } + return true; + } + + /** + * To encrypt + */ + private void doFileEncrypt07() throws Exception { + if (StringUtils.isEmpty(writeWorkbookHolder.getPassword()) + || !ExcelTypeEnum.XLSX.equals(writeWorkbookHolder.getExcelType())) { + return; + } + if (writeWorkbookHolder.getFile() == null) { + return; + } + try (POIFSFileSystem fileSystem = openFileSystemAndEncrypt(writeWorkbookHolder.getFile()); + FileOutputStream fileOutputStream = new FileOutputStream(writeWorkbookHolder.getFile())) { + fileSystem.writeFilesystem(fileOutputStream); + } + } + + private POIFSFileSystem openFileSystemAndEncrypt(File file) throws Exception { + POIFSFileSystem fileSystem = new POIFSFileSystem(); + Encryptor encryptor = new EncryptionInfo(EncryptionMode.standard).getEncryptor(); + encryptor.confirmPassword(writeWorkbookHolder.getPassword()); + try (OPCPackage opcPackage = OPCPackage.open(file, PackageAccess.READ_WRITE); + OutputStream outputStream = encryptor.getDataStream(fileSystem)) { + opcPackage.save(outputStream); + } + return fileSystem; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/CsvReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/CsvReadContext.java new file mode 100644 index 000000000..00bcd54ae --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/CsvReadContext.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.context.csv; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; + +/** + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + **/ +public interface CsvReadContext extends AnalysisContext { + /** + * All information about the workbook you are currently working on. + * + * @return Current workbook holder + */ + CsvReadWorkbookHolder csvReadWorkbookHolder(); + + /** + * All information about the sheet you are currently working on. + * + * @return Current sheet holder + */ + CsvReadSheetHolder csvReadSheetHolder(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/DefaultCsvReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/DefaultCsvReadContext.java new file mode 100644 index 000000000..d8653a88b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/csv/DefaultCsvReadContext.java @@ -0,0 +1,29 @@ +package com.alibaba.excel.context.csv; + +import com.alibaba.excel.context.AnalysisContextImpl; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + */ +public class DefaultCsvReadContext extends AnalysisContextImpl implements CsvReadContext { + + public DefaultCsvReadContext(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { + super(readWorkbook, actualExcelType); + } + + @Override + public CsvReadWorkbookHolder csvReadWorkbookHolder() { + return (CsvReadWorkbookHolder)readWorkbookHolder(); + } + + @Override + public CsvReadSheetHolder csvReadSheetHolder() { + return (CsvReadSheetHolder)readSheetHolder(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java new file mode 100644 index 000000000..698bf6a40 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/DefaultXlsReadContext.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.context.xls; + +import com.alibaba.excel.context.AnalysisContextImpl; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + */ +public class DefaultXlsReadContext extends AnalysisContextImpl implements XlsReadContext { + + public DefaultXlsReadContext(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { + super(readWorkbook, actualExcelType); + } + + @Override + public XlsReadWorkbookHolder xlsReadWorkbookHolder() { + return (XlsReadWorkbookHolder)readWorkbookHolder(); + } + + @Override + public XlsReadSheetHolder xlsReadSheetHolder() { + return (XlsReadSheetHolder)readSheetHolder(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java new file mode 100644 index 000000000..4d1bcdb29 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/xls/XlsReadContext.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.context.xls; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xls.XlsReadWorkbookHolder; + +/** + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + **/ +public interface XlsReadContext extends AnalysisContext { + /** + * All information about the workbook you are currently working on. + * + * @return Current workbook holder + */ + XlsReadWorkbookHolder xlsReadWorkbookHolder(); + + /** + * All information about the sheet you are currently working on. + * + * @return Current sheet holder + */ + XlsReadSheetHolder xlsReadSheetHolder(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java new file mode 100644 index 000000000..5c9d638d4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/DefaultXlsxReadContext.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.context.xlsx; + +import com.alibaba.excel.context.AnalysisContextImpl; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * + * A context is the main anchorage point of a ls xls reader. + * + * @author Jiaju Zhuang + */ +public class DefaultXlsxReadContext extends AnalysisContextImpl implements XlsxReadContext { + + public DefaultXlsxReadContext(ReadWorkbook readWorkbook, ExcelTypeEnum actualExcelType) { + super(readWorkbook, actualExcelType); + } + + @Override + public XlsxReadWorkbookHolder xlsxReadWorkbookHolder() { + return (XlsxReadWorkbookHolder)readWorkbookHolder(); + } + + @Override + public XlsxReadSheetHolder xlsxReadSheetHolder() { + return (XlsxReadSheetHolder)readSheetHolder(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java new file mode 100644 index 000000000..58d8123b2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/context/xlsx/XlsxReadContext.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.context.xlsx; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadWorkbookHolder; + +/** + * A context is the main anchorage point of a ls xlsx reader. + * + * @author Jiaju Zhuang + **/ +public interface XlsxReadContext extends AnalysisContext { + /** + * All information about the workbook you are currently working on. + * + * @return Current workbook holder + */ + XlsxReadWorkbookHolder xlsxReadWorkbookHolder(); + + /** + * All information about the sheet you are currently working on. + * + * @return Current sheet holder + */ + XlsxReadSheetHolder xlsxReadSheetHolder(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/AutoConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/AutoConverter.java new file mode 100644 index 000000000..5981e7acf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/AutoConverter.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.converters; + +/** + * An empty converter.It's automatically converted by type. + * + * @author Jiaju Zhuang + */ +public class AutoConverter implements Converter { +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/Converter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/Converter.java new file mode 100644 index 000000000..df7f4719e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/Converter.java @@ -0,0 +1,86 @@ +package com.alibaba.excel.converters; + +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Convert between Java objects and excel objects + * + * @param + * @author Dan Zheng + */ +public interface Converter { + + /** + * Back to object types in Java + * + * @return Support for Java class + */ + default Class supportJavaTypeKey() { + throw new UnsupportedOperationException("The current operation is not supported by the current converter."); + } + + /** + * Back to object enum in excel + * + * @return Support for {@link CellDataTypeEnum} + */ + default CellDataTypeEnum supportExcelTypeKey() { + throw new UnsupportedOperationException("The current operation is not supported by the current converter."); + } + + /** + * Convert excel objects to Java objects + * + * @param cellData Excel cell data.NotNull. + * @param contentProperty Content property.Nullable. + * @param globalConfiguration Global configuration.NotNull. + * @return Data to put into a Java object + * @throws Exception Exception. + */ + default T convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws Exception { + throw new UnsupportedOperationException("The current operation is not supported by the current converter."); + } + + /** + * Convert excel objects to Java objects + * + * @param context read converter context + * @return Data to put into a Java object + * @throws Exception Exception. + */ + default T convertToJavaData(ReadConverterContext context) throws Exception { + return convertToJavaData(context.getReadCellData(), context.getContentProperty(), + context.getAnalysisContext().currentReadHolder().globalConfiguration()); + } + + /** + * Convert Java objects to excel objects + * + * @param value Java Data.NotNull. + * @param contentProperty Content property.Nullable. + * @param globalConfiguration Global configuration.NotNull. + * @return Data to put into a Excel + * @throws Exception Exception. + */ + default WriteCellData convertToExcelData(T value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws Exception { + throw new UnsupportedOperationException("The current operation is not supported by the current converter."); + } + + /** + * Convert Java objects to excel objects + * + * @param context write context + * @return Data to put into a Excel + * @throws Exception Exception. + */ + default WriteCellData convertToExcelData(WriteConverterContext context) throws Exception { + return convertToExcelData(context.getValue(), context.getContentProperty(), + context.getWriteContext().currentWriteHolder().globalConfiguration()); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java new file mode 100644 index 000000000..d6417be77 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/ConverterKeyBuild.java @@ -0,0 +1,53 @@ +package com.alibaba.excel.converters; + +import java.util.Map; + +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.util.MapUtils; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Converter unique key.Consider that you can just use class as the key. + * + * @author Jiaju Zhuang + */ +public class ConverterKeyBuild { + + private static final Map, Class> BOXING_MAP = MapUtils.newHashMap(); + + static { + BOXING_MAP.put(int.class, Integer.class); + BOXING_MAP.put(byte.class, Byte.class); + BOXING_MAP.put(long.class, Long.class); + BOXING_MAP.put(double.class, Double.class); + BOXING_MAP.put(float.class, Float.class); + BOXING_MAP.put(char.class, Character.class); + BOXING_MAP.put(short.class, Short.class); + BOXING_MAP.put(boolean.class, Boolean.class); + } + + public static ConverterKey buildKey(Class clazz) { + return buildKey(clazz, null); + } + + public static ConverterKey buildKey(Class clazz, CellDataTypeEnum cellDataTypeEnum) { + Class boxingClass = BOXING_MAP.get(clazz); + if (boxingClass != null) { + return new ConverterKey(boxingClass, cellDataTypeEnum); + } + return new ConverterKey(clazz, cellDataTypeEnum); + } + + @Getter + @Setter + @EqualsAndHashCode + @AllArgsConstructor + public static class ConverterKey { + private Class clazz; + private CellDataTypeEnum cellDataTypeEnum; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java new file mode 100644 index 000000000..8c3719323 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/DefaultConverterLoader.java @@ -0,0 +1,197 @@ +package com.alibaba.excel.converters; + +import java.util.Map; + +import com.alibaba.excel.converters.ConverterKeyBuild.ConverterKey; +import com.alibaba.excel.converters.bigdecimal.BigDecimalBooleanConverter; +import com.alibaba.excel.converters.bigdecimal.BigDecimalNumberConverter; +import com.alibaba.excel.converters.bigdecimal.BigDecimalStringConverter; +import com.alibaba.excel.converters.biginteger.BigIntegerBooleanConverter; +import com.alibaba.excel.converters.biginteger.BigIntegerNumberConverter; +import com.alibaba.excel.converters.biginteger.BigIntegerStringConverter; +import com.alibaba.excel.converters.booleanconverter.BooleanBooleanConverter; +import com.alibaba.excel.converters.booleanconverter.BooleanNumberConverter; +import com.alibaba.excel.converters.booleanconverter.BooleanStringConverter; +import com.alibaba.excel.converters.bytearray.BoxingByteArrayImageConverter; +import com.alibaba.excel.converters.bytearray.ByteArrayImageConverter; +import com.alibaba.excel.converters.byteconverter.ByteBooleanConverter; +import com.alibaba.excel.converters.byteconverter.ByteNumberConverter; +import com.alibaba.excel.converters.byteconverter.ByteStringConverter; +import com.alibaba.excel.converters.date.DateDateConverter; +import com.alibaba.excel.converters.date.DateNumberConverter; +import com.alibaba.excel.converters.date.DateStringConverter; +import com.alibaba.excel.converters.doubleconverter.DoubleBooleanConverter; +import com.alibaba.excel.converters.doubleconverter.DoubleNumberConverter; +import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; +import com.alibaba.excel.converters.file.FileImageConverter; +import com.alibaba.excel.converters.floatconverter.FloatBooleanConverter; +import com.alibaba.excel.converters.floatconverter.FloatNumberConverter; +import com.alibaba.excel.converters.floatconverter.FloatStringConverter; +import com.alibaba.excel.converters.inputstream.InputStreamImageConverter; +import com.alibaba.excel.converters.integer.IntegerBooleanConverter; +import com.alibaba.excel.converters.integer.IntegerNumberConverter; +import com.alibaba.excel.converters.integer.IntegerStringConverter; +import com.alibaba.excel.converters.localdate.LocalDateDateConverter; +import com.alibaba.excel.converters.localdate.LocalDateNumberConverter; +import com.alibaba.excel.converters.localdate.LocalDateStringConverter; +import com.alibaba.excel.converters.localdatetime.LocalDateTimeNumberConverter; +import com.alibaba.excel.converters.localdatetime.LocalDateTimeDateConverter; +import com.alibaba.excel.converters.localdatetime.LocalDateTimeStringConverter; +import com.alibaba.excel.converters.longconverter.LongBooleanConverter; +import com.alibaba.excel.converters.longconverter.LongNumberConverter; +import com.alibaba.excel.converters.longconverter.LongStringConverter; +import com.alibaba.excel.converters.shortconverter.ShortBooleanConverter; +import com.alibaba.excel.converters.shortconverter.ShortNumberConverter; +import com.alibaba.excel.converters.shortconverter.ShortStringConverter; +import com.alibaba.excel.converters.string.StringBooleanConverter; +import com.alibaba.excel.converters.string.StringErrorConverter; +import com.alibaba.excel.converters.string.StringNumberConverter; +import com.alibaba.excel.converters.string.StringStringConverter; +import com.alibaba.excel.converters.url.UrlImageConverter; +import com.alibaba.excel.util.MapUtils; + +/** + * Load default handler + * + * @author Jiaju Zhuang + */ +public class DefaultConverterLoader { + private static Map> defaultWriteConverter; + private static Map> allConverter; + + static { + initDefaultWriteConverter(); + initAllConverter(); + } + + private static void initAllConverter() { + allConverter = MapUtils.newHashMapWithExpectedSize(40); + putAllConverter(new BigDecimalBooleanConverter()); + putAllConverter(new BigDecimalNumberConverter()); + putAllConverter(new BigDecimalStringConverter()); + + putAllConverter(new BigIntegerBooleanConverter()); + putAllConverter(new BigIntegerNumberConverter()); + putAllConverter(new BigIntegerStringConverter()); + + putAllConverter(new BooleanBooleanConverter()); + putAllConverter(new BooleanNumberConverter()); + putAllConverter(new BooleanStringConverter()); + + putAllConverter(new ByteBooleanConverter()); + putAllConverter(new ByteNumberConverter()); + putAllConverter(new ByteStringConverter()); + + putAllConverter(new DateNumberConverter()); + putAllConverter(new DateStringConverter()); + + putAllConverter(new LocalDateNumberConverter()); + putAllConverter(new LocalDateStringConverter()); + + putAllConverter(new LocalDateTimeNumberConverter()); + putAllConverter(new LocalDateTimeStringConverter()); + + putAllConverter(new DoubleBooleanConverter()); + putAllConverter(new DoubleNumberConverter()); + putAllConverter(new DoubleStringConverter()); + + putAllConverter(new FloatBooleanConverter()); + putAllConverter(new FloatNumberConverter()); + putAllConverter(new FloatStringConverter()); + + putAllConverter(new IntegerBooleanConverter()); + putAllConverter(new IntegerNumberConverter()); + putAllConverter(new IntegerStringConverter()); + + putAllConverter(new LongBooleanConverter()); + putAllConverter(new LongNumberConverter()); + putAllConverter(new LongStringConverter()); + + putAllConverter(new ShortBooleanConverter()); + putAllConverter(new ShortNumberConverter()); + putAllConverter(new ShortStringConverter()); + + putAllConverter(new StringBooleanConverter()); + putAllConverter(new StringNumberConverter()); + putAllConverter(new StringStringConverter()); + putAllConverter(new StringErrorConverter()); + } + + private static void initDefaultWriteConverter() { + defaultWriteConverter = MapUtils.newHashMapWithExpectedSize(40); + putWriteConverter(new BigDecimalNumberConverter()); + putWriteConverter(new BigIntegerNumberConverter()); + putWriteConverter(new BooleanBooleanConverter()); + putWriteConverter(new ByteNumberConverter()); + putWriteConverter(new DateDateConverter()); + putWriteConverter(new LocalDateTimeDateConverter()); + putWriteConverter(new LocalDateDateConverter()); + putWriteConverter(new DoubleNumberConverter()); + putWriteConverter(new FloatNumberConverter()); + putWriteConverter(new IntegerNumberConverter()); + putWriteConverter(new LongNumberConverter()); + putWriteConverter(new ShortNumberConverter()); + putWriteConverter(new StringStringConverter()); + putWriteConverter(new FileImageConverter()); + putWriteConverter(new InputStreamImageConverter()); + putWriteConverter(new ByteArrayImageConverter()); + putWriteConverter(new BoxingByteArrayImageConverter()); + putWriteConverter(new UrlImageConverter()); + + // In some cases, it must be converted to string + putWriteStringConverter(new BigDecimalStringConverter()); + putWriteStringConverter(new BigIntegerStringConverter()); + putWriteStringConverter(new BooleanStringConverter()); + putWriteStringConverter(new ByteStringConverter()); + putWriteStringConverter(new DateStringConverter()); + putWriteStringConverter(new LocalDateStringConverter()); + putWriteStringConverter(new LocalDateTimeStringConverter()); + putWriteStringConverter(new DoubleStringConverter()); + putWriteStringConverter(new FloatStringConverter()); + putWriteStringConverter(new IntegerStringConverter()); + putWriteStringConverter(new LongStringConverter()); + putWriteStringConverter(new ShortStringConverter()); + putWriteStringConverter(new StringStringConverter()); + } + + /** + * Load default write converter + * + * @return + */ + public static Map> loadDefaultWriteConverter() { + return defaultWriteConverter; + } + + private static void putWriteConverter(Converter converter) { + defaultWriteConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); + } + + private static void putWriteStringConverter(Converter converter) { + defaultWriteConverter.put( + ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()), converter); + } + + /** + * Load default read converter + * + * @return + */ + public static Map> loadDefaultReadConverter() { + return loadAllConverter(); + } + + /** + * Load all converter + * + * @return + */ + public static Map> loadAllConverter() { + return allConverter; + } + + private static void putAllConverter(Converter converter) { + allConverter.put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()), + converter); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/NullableObjectConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/NullableObjectConverter.java new file mode 100644 index 000000000..fe24f63d2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/NullableObjectConverter.java @@ -0,0 +1,10 @@ +package com.alibaba.excel.converters; + +/** + * When implementing convertToExcelData method, pay attention to the reference value may be + * null + * + * @author JiaJu Zhuang + **/ +public interface NullableObjectConverter extends Converter { +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/ReadConverterContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/ReadConverterContext.java new file mode 100644 index 000000000..9c7970cd0 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/ReadConverterContext.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.converters; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * read converter context + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +public class ReadConverterContext { + /** + * Excel cell data.NotNull. + */ + private ReadCellData readCellData; + /** + * Content property.Nullable. + */ + private ExcelContentProperty contentProperty; + /** + * context.NotNull. + */ + private AnalysisContext analysisContext; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/WriteConverterContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/WriteConverterContext.java new file mode 100644 index 000000000..46abda474 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/WriteConverterContext.java @@ -0,0 +1,38 @@ +package com.alibaba.excel.converters; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * write converter context + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +@AllArgsConstructor +public class WriteConverterContext { + + /** + * Java Data.NotNull. + */ + private T value; + + /** + * Content property.Nullable. + */ + private ExcelContentProperty contentProperty; + + /** + * write context + */ + private WriteContext writeContext; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java new file mode 100644 index 000000000..314845624 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.bigdecimal; + +import java.math.BigDecimal; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * BigDecimal and boolean converter + * + * @author Jiaju Zhuang + */ +public class BigDecimalBooleanConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return BigDecimal.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public BigDecimal convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return BigDecimal.ONE; + } + return BigDecimal.ZERO; + } + + @Override + public WriteCellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (BigDecimal.ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java new file mode 100644 index 000000000..f1b1285c5 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalNumberConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.bigdecimal; + +import java.math.BigDecimal; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * BigDecimal and number converter + * + * @author Jiaju Zhuang + */ +public class BigDecimalNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return BigDecimal.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public BigDecimal convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue(); + } + + @Override + public WriteCellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellData(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalStringConverter.java new file mode 100644 index 000000000..3b1178125 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bigdecimal/BigDecimalStringConverter.java @@ -0,0 +1,42 @@ +package com.alibaba.excel.converters.bigdecimal; + +import java.math.BigDecimal; +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * BigDecimal and string converter + * + * @author Jiaju Zhuang + */ +public class BigDecimalStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return BigDecimal.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public BigDecimal convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseBigDecimal(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerBooleanConverter.java new file mode 100644 index 000000000..0ac0e6c90 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.biginteger; + +import java.math.BigInteger; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * BigInteger and boolean converter + * + * @author Jiaju Zhuang + */ +public class BigIntegerBooleanConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return BigInteger.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public BigInteger convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return BigInteger.ONE; + } + return BigInteger.ZERO; + } + + @Override + public WriteCellData convertToExcelData(BigInteger value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (BigInteger.ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerNumberConverter.java new file mode 100644 index 000000000..c5957a15e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerNumberConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.biginteger; + +import java.math.BigInteger; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * BigInteger and number converter + * + * @author Jiaju Zhuang + */ +public class BigIntegerNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return BigInteger.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public BigInteger convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().toBigInteger(); + } + + @Override + public WriteCellData convertToExcelData(BigInteger value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellData(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerStringConverter.java new file mode 100644 index 000000000..aa5531088 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/biginteger/BigIntegerStringConverter.java @@ -0,0 +1,42 @@ +package com.alibaba.excel.converters.biginteger; + +import java.math.BigInteger; +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * BigDecimal and string converter + * + * @author Jiaju Zhuang + */ +public class BigIntegerStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return BigInteger.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public BigInteger convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseBigDecimal(cellData.getStringValue(), contentProperty).toBigInteger(); + } + + @Override + public WriteCellData convertToExcelData(BigInteger value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanBooleanConverter.java new file mode 100644 index 000000000..884f65157 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanBooleanConverter.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.converters.booleanconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Boolean and boolean converter + * + * @author Jiaju Zhuang + */ +public class BooleanBooleanConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Boolean.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Boolean convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getBooleanValue(); + } + + @Override + public WriteCellData convertToExcelData(Boolean value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(value); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java new file mode 100644 index 000000000..b2438e833 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanNumberConverter.java @@ -0,0 +1,46 @@ +package com.alibaba.excel.converters.booleanconverter; + +import java.math.BigDecimal; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Boolean and number converter + * + * @author Jiaju Zhuang + */ +public class BooleanNumberConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Boolean.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Boolean convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (BigDecimal.ONE.compareTo(cellData.getNumberValue()) == 0) { + return Boolean.TRUE; + } + return Boolean.FALSE; + } + + @Override + public WriteCellData convertToExcelData(Boolean value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (value) { + return new WriteCellData<>(BigDecimal.ONE); + } + return new WriteCellData<>(BigDecimal.ZERO); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanStringConverter.java new file mode 100644 index 000000000..0141ef8d7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/booleanconverter/BooleanStringConverter.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.converters.booleanconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Boolean and string converter + * + * @author Jiaju Zhuang + */ +public class BooleanStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Boolean.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Boolean convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return Boolean.valueOf(cellData.getStringValue()); + } + + @Override + public WriteCellData convertToExcelData(Boolean value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(value.toString()); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java new file mode 100644 index 000000000..a1f8d3ac4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bytearray/BoxingByteArrayImageConverter.java @@ -0,0 +1,29 @@ +package com.alibaba.excel.converters.bytearray; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Boxing Byte array and image converter + * + * @author Jiaju Zhuang + */ +public class BoxingByteArrayImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Byte[].class; + } + + @Override + public WriteCellData convertToExcelData(Byte[] value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + byte[] byteValue = new byte[value.length]; + for (int i = 0; i < value.length; i++) { + byteValue[i] = value[i]; + } + return new WriteCellData<>(byteValue); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java new file mode 100644 index 000000000..9a871feaf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/bytearray/ByteArrayImageConverter.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.converters.bytearray; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Byte array and image converter + * + * @author Jiaju Zhuang + */ +public class ByteArrayImageConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return byte[].class; + } + + @Override + public WriteCellData convertToExcelData(byte[] value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(value); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteBooleanConverter.java new file mode 100644 index 000000000..c7da08c48 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.byteconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Byte and boolean converter + * + * @author Jiaju Zhuang + */ +public class ByteBooleanConverter implements Converter { + private static final Byte ONE = (byte)1; + private static final Byte ZERO = (byte)0; + + @Override + public Class supportJavaTypeKey() { + return Byte.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Byte convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return ONE; + } + return ZERO; + } + + @Override + public WriteCellData convertToExcelData(Byte value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java new file mode 100644 index 000000000..4103b9cb6 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteNumberConverter.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.converters.byteconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Byte and number converter + * + * @author Jiaju Zhuang + */ +public class ByteNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Byte.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Byte convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().byteValue(); + } + + @Override + public WriteCellData convertToExcelData(Byte value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellData(value, contentProperty); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteStringConverter.java new file mode 100644 index 000000000..988afbc64 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/byteconverter/ByteStringConverter.java @@ -0,0 +1,42 @@ +package com.alibaba.excel.converters.byteconverter; + +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Byte and string converter + * + * @author Jiaju Zhuang + */ +public class ByteStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Byte.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Byte convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseByte(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(Byte value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateDateConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateDateConverter.java new file mode 100644 index 000000000..299700702 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateDateConverter.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.converters.date; + +import java.util.Date; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.WorkBookUtil; + +/** + * Date and date converter + * + * @author Jiaju Zhuang + */ +public class DateDateConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Date.class; + } + + @Override + public WriteCellData convertToExcelData(Date value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws Exception { + WriteCellData cellData = new WriteCellData<>(value); + String format = null; + if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { + format = contentProperty.getDateTimeFormatProperty().getFormat(); + } + WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat); + return cellData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java new file mode 100644 index 000000000..02ba26aa2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateNumberConverter.java @@ -0,0 +1,56 @@ +package com.alibaba.excel.converters.date; + +import java.math.BigDecimal; +import java.util.Date; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.ss.usermodel.DateUtil; + +/** + * Date and number converter + * + * @author Jiaju Zhuang + */ +public class DateNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Date.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Date convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(), + globalConfiguration.getUse1904windowing()); + } else { + return DateUtils.getJavaDate(cellData.getNumberValue().doubleValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing()); + } + } + + @Override + public WriteCellData convertToExcelData(Date value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>( + BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing()))); + } else { + return new WriteCellData<>(BigDecimal.valueOf( + DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateStringConverter.java new file mode 100644 index 000000000..9941c70ea --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/date/DateStringConverter.java @@ -0,0 +1,50 @@ +package com.alibaba.excel.converters.date; + +import java.text.ParseException; +import java.util.Date; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; + +/** + * Date and string converter + * + * @author Jiaju Zhuang + */ +public class DateStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Date.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Date convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return DateUtils.parseDate(cellData.getStringValue(), null); + } else { + return DateUtils.parseDate(cellData.getStringValue(), + contentProperty.getDateTimeFormatProperty().getFormat()); + } + } + + @Override + public WriteCellData convertToExcelData(Date value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>(DateUtils.format(value, null)); + } else { + return new WriteCellData<>(DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat())); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleBooleanConverter.java new file mode 100644 index 000000000..3da808e1e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.doubleconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Double and boolean converter + * + * @author Jiaju Zhuang + */ +public class DoubleBooleanConverter implements Converter { + private static final Double ONE = 1.0; + private static final Double ZERO = 0.0; + + @Override + public Class supportJavaTypeKey() { + return Double.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Double convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return ONE; + } + return ZERO; + } + + @Override + public WriteCellData convertToExcelData(Double value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java new file mode 100644 index 000000000..70bdffb6b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleNumberConverter.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.converters.doubleconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Double and number converter + * + * @author Jiaju Zhuang + */ +public class DoubleNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Double.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Double convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().doubleValue(); + } + + @Override + public WriteCellData convertToExcelData(Double value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellData(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleStringConverter.java new file mode 100644 index 000000000..e1951130a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/doubleconverter/DoubleStringConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.doubleconverter; + +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Double and string converter + * + * @author Jiaju Zhuang + */ +public class DoubleStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Double.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Double convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseDouble(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(Double value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java new file mode 100644 index 000000000..c8fd6e8b9 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/file/FileImageConverter.java @@ -0,0 +1,28 @@ +package com.alibaba.excel.converters.file; + +import java.io.File; +import java.io.IOException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.FileUtils; + +/** + * File and image converter + * + * @author Jiaju Zhuang + */ +public class FileImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return File.class; + } + + @Override + public WriteCellData convertToExcelData(File value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + return new WriteCellData<>(FileUtils.readFileToByteArray(value)); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatBooleanConverter.java new file mode 100644 index 000000000..1ec6168cc --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.floatconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Float and boolean converter + * + * @author Jiaju Zhuang + */ +public class FloatBooleanConverter implements Converter { + private static final Float ONE = (float)1.0; + private static final Float ZERO = (float)0.0; + + @Override + public Class supportJavaTypeKey() { + return Float.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Float convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return ONE; + } + return ZERO; + } + + @Override + public WriteCellData convertToExcelData(Float value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java new file mode 100644 index 000000000..478086e3c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatNumberConverter.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.converters.floatconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Float and number converter + * + * @author Jiaju Zhuang + */ +public class FloatNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Float.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Float convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().floatValue(); + } + + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty()); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatStringConverter.java new file mode 100644 index 000000000..bd5e9762a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/floatconverter/FloatStringConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.floatconverter; + +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Float and string converter + * + * @author Jiaju Zhuang + */ +public class FloatStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Float.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Float convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseFloat(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(Float value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java new file mode 100644 index 000000000..ff8492f0e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/inputstream/InputStreamImageConverter.java @@ -0,0 +1,29 @@ +package com.alibaba.excel.converters.inputstream; + +import java.io.IOException; +import java.io.InputStream; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.IoUtils; + +/** + * File and image converter + * + * @author Jiaju Zhuang + */ +public class InputStreamImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return InputStream.class; + } + + @Override + public WriteCellData convertToExcelData(InputStream value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + return new WriteCellData<>(IoUtils.toByteArray(value)); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerBooleanConverter.java new file mode 100644 index 000000000..e3f130687 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.integer; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Integer and boolean converter + * + * @author Jiaju Zhuang + */ +public class IntegerBooleanConverter implements Converter { + private static final Integer ONE = 1; + private static final Integer ZERO = 0; + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return ONE; + } + return ZERO; + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java new file mode 100644 index 000000000..203aa1ace --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerNumberConverter.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.converters.integer; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Integer and number converter + * + * @author Jiaju Zhuang + */ +public class IntegerNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().intValue(); + } + + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty()); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerStringConverter.java new file mode 100644 index 000000000..62dce6e6a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/integer/IntegerStringConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.integer; + +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Integer and string converter + * + * @author Jiaju Zhuang + */ +public class IntegerStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseInteger(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateDateConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateDateConverter.java new file mode 100644 index 000000000..3663476ae --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateDateConverter.java @@ -0,0 +1,36 @@ +package com.alibaba.excel.converters.localdate; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.WorkBookUtil; + +/** + * LocalDate and date converter + * + * @author Jiaju Zhuang + */ +public class LocalDateDateConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return LocalDate.class; + } + + @Override + public WriteCellData convertToExcelData(LocalDate value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws Exception { + LocalDateTime localDateTime = value == null ? null : value.atTime(0, 0); + WriteCellData cellData = new WriteCellData<>(localDateTime); + String format = null; + if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { + format = contentProperty.getDateTimeFormatProperty().getFormat(); + } + WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultLocalDateFormat); + return cellData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateNumberConverter.java new file mode 100644 index 000000000..fbec7a4fe --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateNumberConverter.java @@ -0,0 +1,57 @@ +package com.alibaba.excel.converters.localdate; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.ss.usermodel.DateUtil; + +/** + * LocalDate and number converter + * + * @author Jiaju Zhuang + */ +public class LocalDateNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return LocalDate.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public LocalDate convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return DateUtils.getLocalDate(cellData.getNumberValue().doubleValue(), + globalConfiguration.getUse1904windowing()); + } else { + return DateUtils.getLocalDate(cellData.getNumberValue().doubleValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing()); + } + } + + @Override + public WriteCellData convertToExcelData(LocalDate value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>( + BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing()))); + } else { + return new WriteCellData<>(BigDecimal.valueOf( + DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateStringConverter.java new file mode 100644 index 000000000..68e3e0b0d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdate/LocalDateStringConverter.java @@ -0,0 +1,52 @@ +package com.alibaba.excel.converters.localdate; + +import java.text.ParseException; +import java.time.LocalDate; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; + +/** + * LocalDate and string converter + * + * @author Jiaju Zhuang + */ +public class LocalDateStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return LocalDate.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public LocalDate convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return DateUtils.parseLocalDate(cellData.getStringValue(), null, globalConfiguration.getLocale()); + } else { + return DateUtils.parseLocalDate(cellData.getStringValue(), + contentProperty.getDateTimeFormatProperty().getFormat(), globalConfiguration.getLocale()); + } + } + + @Override + public WriteCellData convertToExcelData(LocalDate value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>(DateUtils.format(value, null, globalConfiguration.getLocale())); + } else { + return new WriteCellData<>( + DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat(), + globalConfiguration.getLocale())); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeDateConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeDateConverter.java new file mode 100644 index 000000000..39276df9e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeDateConverter.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.converters.localdatetime; + +import java.time.LocalDateTime; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.WorkBookUtil; + +/** + * LocalDateTime and date converter + * + * @author Jiaju Zhuang + */ +public class LocalDateTimeDateConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return LocalDateTime.class; + } + + @Override + public WriteCellData convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws Exception { + WriteCellData cellData = new WriteCellData<>(value); + String format = null; + if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { + format = contentProperty.getDateTimeFormatProperty().getFormat(); + } + WorkBookUtil.fillDataFormat(cellData, format, DateUtils.defaultDateFormat); + return cellData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeNumberConverter.java new file mode 100644 index 000000000..51959fdef --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeNumberConverter.java @@ -0,0 +1,56 @@ +package com.alibaba.excel.converters.localdatetime; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.ss.usermodel.DateUtil; + +/** + * LocalDateTime and number converter + * + * @author Jiaju Zhuang + */ +public class LocalDateTimeNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return LocalDateTime.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public LocalDateTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(), + globalConfiguration.getUse1904windowing()); + } else { + return DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing()); + } + } + + @Override + public WriteCellData convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>( + BigDecimal.valueOf(DateUtil.getExcelDate(value, globalConfiguration.getUse1904windowing()))); + } else { + return new WriteCellData<>(BigDecimal.valueOf( + DateUtil.getExcelDate(value, contentProperty.getDateTimeFormatProperty().getUse1904windowing()))); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeStringConverter.java new file mode 100644 index 000000000..56aee3186 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/localdatetime/LocalDateTimeStringConverter.java @@ -0,0 +1,52 @@ +package com.alibaba.excel.converters.localdatetime; + +import java.text.ParseException; +import java.time.LocalDateTime; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; + +/** + * LocalDateTime and string converter + * + * @author Jiaju Zhuang + */ +public class LocalDateTimeStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return LocalDateTime.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public LocalDateTime convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return DateUtils.parseLocalDateTime(cellData.getStringValue(), null, globalConfiguration.getLocale()); + } else { + return DateUtils.parseLocalDateTime(cellData.getStringValue(), + contentProperty.getDateTimeFormatProperty().getFormat(), globalConfiguration.getLocale()); + } + } + + @Override + public WriteCellData convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (contentProperty == null || contentProperty.getDateTimeFormatProperty() == null) { + return new WriteCellData<>(DateUtils.format(value, null, globalConfiguration.getLocale())); + } else { + return new WriteCellData<>( + DateUtils.format(value, contentProperty.getDateTimeFormatProperty().getFormat(), + globalConfiguration.getLocale())); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongBooleanConverter.java new file mode 100644 index 000000000..d123ed66d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.longconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Long and boolean converter + * + * @author Jiaju Zhuang + */ +public class LongBooleanConverter implements Converter { + private static final Long ONE = 1L; + private static final Long ZERO = 0L; + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return ONE; + } + return ZERO; + } + + @Override + public WriteCellData convertToExcelData(Long value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java new file mode 100644 index 000000000..aee15b5b8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongNumberConverter.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.converters.longconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Long and number converter + * + * @author Jiaju Zhuang + */ +public class LongNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().longValue(); + } + + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty()); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongStringConverter.java new file mode 100644 index 000000000..26ffcc818 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/longconverter/LongStringConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.longconverter; + +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Long and string converter + * + * @author Jiaju Zhuang + */ +public class LongStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Long.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Long convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseLong(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(Long value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java new file mode 100644 index 000000000..1f0517ee7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortBooleanConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.shortconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Short and boolean converter + * + * @author Jiaju Zhuang + */ +public class ShortBooleanConverter implements Converter { + private static final Short ONE = 1; + private static final Short ZERO = 0; + + @Override + public Class supportJavaTypeKey() { + return Short.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public Short convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (cellData.getBooleanValue()) { + return ONE; + } + return ZERO; + } + + @Override + public WriteCellData convertToExcelData(Short value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + if (ONE.equals(value)) { + return new WriteCellData<>(Boolean.TRUE); + } + return new WriteCellData<>(Boolean.FALSE); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java new file mode 100644 index 000000000..d05686067 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortNumberConverter.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.converters.shortconverter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Short and number converter + * + * @author Jiaju Zhuang + */ +public class ShortNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Short.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public Short convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getNumberValue().shortValue(); + } + + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return NumberUtils.formatToCellData(context.getValue(), context.getContentProperty()); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortStringConverter.java new file mode 100644 index 000000000..497fdee9d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/shortconverter/ShortStringConverter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.converters.shortconverter; + +import java.text.ParseException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; + +/** + * Short and string converter + * + * @author Jiaju Zhuang + */ +public class ShortStringConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Short.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Short convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws ParseException { + return NumberUtils.parseShort(cellData.getStringValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(Short value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringBooleanConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringBooleanConverter.java new file mode 100644 index 000000000..704a18b40 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringBooleanConverter.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.converters.string; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * String and boolean converter + * + * @author Jiaju Zhuang + */ +public class StringBooleanConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; + } + + @Override + public String convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getBooleanValue().toString(); + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(Boolean.valueOf(value)); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringErrorConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringErrorConverter.java new file mode 100644 index 000000000..1456d80f2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringErrorConverter.java @@ -0,0 +1,38 @@ +package com.alibaba.excel.converters.string; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * String and error converter + * + * @author Jiaju Zhuang + */ +public class StringErrorConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.ERROR; + } + + @Override + public String convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getStringValue(); + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(CellDataTypeEnum.ERROR, value); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java new file mode 100644 index 000000000..fb262e195 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringImageConverter.java @@ -0,0 +1,29 @@ +package com.alibaba.excel.converters.string; + +import java.io.File; +import java.io.IOException; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.FileUtils; + +/** + * String and image converter + * + * @author Jiaju Zhuang + */ +public class StringImageConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + return new WriteCellData<>(FileUtils.readFileToByteArray(new File(value))); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java new file mode 100644 index 000000000..ff96317d4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringNumberConverter.java @@ -0,0 +1,65 @@ +package com.alibaba.excel.converters.string; + +import java.math.BigDecimal; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; +import com.alibaba.excel.util.NumberUtils; +import com.alibaba.excel.util.StringUtils; + +import org.apache.poi.ss.usermodel.DateUtil; + +/** + * String and number converter + * + * @author Jiaju Zhuang + */ +public class StringNumberConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.NUMBER; + } + + @Override + public String convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + // If there are "DateTimeFormat", read as date + if (contentProperty != null && contentProperty.getDateTimeFormatProperty() != null) { + return DateUtils.format(cellData.getNumberValue(), + contentProperty.getDateTimeFormatProperty().getUse1904windowing(), + contentProperty.getDateTimeFormatProperty().getFormat()); + } + // If there are "NumberFormat", read as number + if (contentProperty != null && contentProperty.getNumberFormatProperty() != null) { + return NumberUtils.format(cellData.getNumberValue(), contentProperty); + } + // Excel defines formatting + boolean hasDataFormatData = cellData.getDataFormatData() != null + && cellData.getDataFormatData().getIndex() != null && !StringUtils.isEmpty( + cellData.getDataFormatData().getFormat()); + if (hasDataFormatData) { + return NumberDataFormatterUtils.format(cellData.getNumberValue(), + cellData.getDataFormatData().getIndex(), cellData.getDataFormatData().getFormat(), globalConfiguration); + } + // Default conversion number + return NumberUtils.format(cellData.getNumberValue(), contentProperty); + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(new BigDecimal(value)); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringStringConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringStringConverter.java new file mode 100644 index 000000000..5edc6b162 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/string/StringStringConverter.java @@ -0,0 +1,38 @@ +package com.alibaba.excel.converters.string; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * String and string converter + * + * @author Jiaju Zhuang + */ +public class StringStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public String convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return cellData.getStringValue(); + } + + @Override + public WriteCellData convertToExcelData(String value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(value); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java b/easyexcel-core/src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java new file mode 100644 index 000000000..7daa7cabd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/converters/url/UrlImageConverter.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.converters.url; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.IoUtils; + +/** + * Url and image converter + * + * @author Jiaju Zhuang + * @since 2.1.1 + */ +public class UrlImageConverter implements Converter { + public static int urlConnectTimeout = 1000; + public static int urlReadTimeout = 5000; + + @Override + public Class supportJavaTypeKey() { + return URL.class; + } + + @Override + public WriteCellData convertToExcelData(URL value, ExcelContentProperty contentProperty, + GlobalConfiguration globalConfiguration) throws IOException { + InputStream inputStream = null; + try { + URLConnection urlConnection = value.openConnection(); + urlConnection.setConnectTimeout(urlConnectTimeout); + urlConnection.setReadTimeout(urlReadTimeout); + inputStream = urlConnection.getInputStream(); + byte[] bytes = IoUtils.toByteArray(inputStream); + return new WriteCellData<>(bytes); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/BooleanEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/BooleanEnum.java new file mode 100644 index 000000000..ee5274d2d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/BooleanEnum.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.enums; + +import lombok.Getter; + +/** + * Default values cannot be used for annotations. + * So an additional an enumeration to determine whether the user has added the enumeration. + * + * @author Jiaju Zhuang + */ +@Getter +public enum BooleanEnum { + /** + * NULL + */ + DEFAULT(null), + /** + * TRUE + */ + TRUE(Boolean.TRUE), + /** + * FALSE + */ + FALSE(Boolean.FALSE), + ; + + Boolean booleanValue; + + BooleanEnum(Boolean booleanValue) { + this.booleanValue = booleanValue; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/ByteOrderMarkEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/ByteOrderMarkEnum.java new file mode 100644 index 000000000..7c069099b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/ByteOrderMarkEnum.java @@ -0,0 +1,66 @@ +package com.alibaba.excel.enums; + +import java.nio.charset.Charset; +import java.util.Map; + +import com.alibaba.excel.util.MapUtils; + +import lombok.Getter; +import org.apache.commons.io.ByteOrderMark; + +/** + * byte order mark + * + * @author Jiaju Zhuang + */ +@Getter +public enum ByteOrderMarkEnum { + + /** + * UTF_8 + */ + UTF_8(ByteOrderMark.UTF_8), + /** + * UTF_16BE + */ + UTF_16BE(ByteOrderMark.UTF_16BE), + /** + * UTF_16LE + */ + UTF_16LE(ByteOrderMark.UTF_16LE), + /** + * UTF_32BE + */ + UTF_32BE(ByteOrderMark.UTF_32BE), + /** + * UTF_32LE + */ + UTF_32LE(ByteOrderMark.UTF_32LE), + + ; + + final ByteOrderMark byteOrderMark; + final String stringPrefix; + + ByteOrderMarkEnum(ByteOrderMark byteOrderMark) { + this.byteOrderMark = byteOrderMark; + Charset charset = Charset.forName(byteOrderMark.getCharsetName()); + this.stringPrefix = new String(byteOrderMark.getBytes(), charset); + } + + /** + * store character aliases corresponding to `ByteOrderMark` prefix + */ + private static final Map CHARSET_BYTE_ORDER_MARK_MAP = MapUtils.newHashMap(); + + static { + for (ByteOrderMarkEnum value : ByteOrderMarkEnum.values()) { + CHARSET_BYTE_ORDER_MARK_MAP.put(value.getByteOrderMark().getCharsetName(), value); + } + } + + public static ByteOrderMarkEnum valueOfByCharsetName(String charsetName) { + return CHARSET_BYTE_ORDER_MARK_MAP.get(charsetName); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java new file mode 100644 index 000000000..e2deed527 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CacheLocationEnum.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.enums; + +/** + * cache locaciton + * + * @author Jiaju Zhuang + **/ +public enum CacheLocationEnum { + /** + * The cache will be stored in {@code ThreadLocal}, and will be cleared when the excel read and write is completed. + */ + THREAD_LOCAL, + + /** + * The cache will not be cleared unless the app is stopped. + */ + MEMORY, + + /** + * No caching.It may lose some of performance. + */ + NONE; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java new file mode 100644 index 000000000..0f1a46cf3 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CellDataTypeEnum.java @@ -0,0 +1,72 @@ +package com.alibaba.excel.enums; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.util.StringUtils; + +/** + * excel internal data type + * + * @author Jiaju Zhuang + */ +public enum CellDataTypeEnum { + /** + * string + */ + STRING, + /** + * This type of data does not need to be read in the 'sharedStrings.xml', it is only used for overuse, and the data + * will be stored as a {@link #STRING} + */ + DIRECT_STRING, + /** + * number + */ + NUMBER, + /** + * boolean + */ + BOOLEAN, + /** + * empty + */ + EMPTY, + /** + * error + */ + ERROR, + /** + * date. Support only when writing. + */ + DATE, + /** + * rich text string.Support only when writing. + */ + RICH_TEXT_STRING, + ; + + private static final Map TYPE_ROUTING_MAP = new HashMap(16); + + static { + TYPE_ROUTING_MAP.put("s", STRING); + TYPE_ROUTING_MAP.put("str", DIRECT_STRING); + TYPE_ROUTING_MAP.put("inlineStr", DIRECT_STRING); + TYPE_ROUTING_MAP.put("e", ERROR); + TYPE_ROUTING_MAP.put("b", BOOLEAN); + TYPE_ROUTING_MAP.put("n", NUMBER); + } + + /** + * Build data types + * + * @param cellType + * @return + */ + public static CellDataTypeEnum buildFromCellType(String cellType) { + if (StringUtils.isEmpty(cellType)) { + return EMPTY; + } + return TYPE_ROUTING_MAP.get(cellType); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/CellExtraTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CellExtraTypeEnum.java new file mode 100644 index 000000000..1c13b7c06 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/CellExtraTypeEnum.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.enums; + +/** + * Extra data type + * + * @author Jiaju Zhuang + **/ +public enum CellExtraTypeEnum { + /** + * Comment + */ + COMMENT, + /** + * Hyperlink + */ + HYPERLINK, + /** + * Merge + */ + MERGE,; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/HeadKindEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/HeadKindEnum.java new file mode 100644 index 000000000..102367a54 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/HeadKindEnum.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.enums; + +/** + * The types of header + * + * @author Jiaju Zhuang + **/ +public enum HeadKindEnum { + /** + * none + */ + NONE, + /** + * class + */ + CLASS, + /** + * String + */ + STRING; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/HolderEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/HolderEnum.java new file mode 100644 index 000000000..ec3f04779 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/HolderEnum.java @@ -0,0 +1,25 @@ +package com.alibaba.excel.enums; + +/** + * The types of holder + * + * @author Jiaju Zhuang + **/ +public enum HolderEnum { + /** + * workbook + */ + WORKBOOK, + /** + * sheet + */ + SHEET, + /** + * table + */ + TABLE, + /** + * row + */ + ROW; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/NumericCellTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/NumericCellTypeEnum.java new file mode 100644 index 000000000..22001f139 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/NumericCellTypeEnum.java @@ -0,0 +1,22 @@ +package com.alibaba.excel.enums; + +import org.apache.poi.ss.usermodel.CellType; + +/** + * Used to supplement {@link CellType}. + * + * Cannot distinguish between date and number in write case. + * + * @author Jiaju Zhuang + */ +public enum NumericCellTypeEnum { + /** + * number + */ + NUMBER, + /** + * date. Support only when writing. + */ + DATE, + ; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java new file mode 100644 index 000000000..4c2a317fa --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/ReadDefaultReturnEnum.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.enums; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.util.StringUtils; + +/** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * + * @author Jiaju Zhuang + */ +public enum ReadDefaultReturnEnum { + /** + * default.The content of cells into string, is the same as you see in the excel. + */ + STRING, + + /** + * Returns the actual type. + * Will be automatically selected according to the cell contents what return type, will return the following class: + *
    + *
  1. {@link BigDecimal}
  2. + *
  3. {@link Boolean}
  4. + *
  5. {@link String}
  6. + *
  7. {@link LocalDateTime}
  8. + *
+ */ + ACTUAL_DATA, + + /** + * Return to {@link com.alibaba.excel.metadata.data.ReadCellData}, can decide which field you need. + */ + READ_CELL_DATA, + ; + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/RowTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/RowTypeEnum.java new file mode 100644 index 000000000..e371cbdc7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/RowTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * The types of row + * + * @author Jiaju Zhuang + **/ +public enum RowTypeEnum { + /** + * data + */ + DATA, + /** + * empty + */ + EMPTY,; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java new file mode 100644 index 000000000..78803f182 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteDirectionEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Direction of writing + * + * @author Jiaju Zhuang + **/ +public enum WriteDirectionEnum { + /** + * Vertical write. + */ + VERTICAL, + /** + * Horizontal write. + */ + HORIZONTAL,; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java new file mode 100644 index 000000000..d9a41aed0 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteLastRowTypeEnum.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.enums; + +/** + * The types of write last row + * + * @author Jiaju Zhuang + **/ +public enum WriteLastRowTypeEnum { + /** + * Excel are created without templates ,And any data has been written; + */ + COMMON_EMPTY, + /** + * Excel are created with templates ,And any data has been written; + */ + TEMPLATE_EMPTY, + /** + * Any data has been written; + */ + HAS_DATA,; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java new file mode 100644 index 000000000..e9d22a8b5 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteTemplateAnalysisCellTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Type of template to read when writing + * + * @author Jiaju Zhuang + **/ +public enum WriteTemplateAnalysisCellTypeEnum { + /** + * Common field. + */ + COMMON, + /** + * A collection of fields. + */ + COLLECTION,; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java new file mode 100644 index 000000000..9dbbd3141 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/WriteTypeEnum.java @@ -0,0 +1,17 @@ +package com.alibaba.excel.enums; + +/** + * Enumeration of write methods + * + * @author Jiaju Zhuang + **/ +public enum WriteTypeEnum { + /** + * Add. + */ + ADD, + /** + * Fill. + */ + FILL,; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/BorderStyleEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/BorderStyleEnum.java new file mode 100644 index 000000000..1033ef1a8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/BorderStyleEnum.java @@ -0,0 +1,95 @@ +package com.alibaba.excel.enums.poi; + +import lombok.Getter; +import org.apache.poi.ss.usermodel.BorderStyle; + +/** + * The enumeration value indicating the line style of a border in a cell, + * i.e., whether it is bordered dash dot, dash dot dot, dashed, dotted, double, hair, medium, + * medium dash dot, medium dash dot dot, medium dashed, none, slant dash dot, thick or thin. + * + * @author Jiaju Zhuang + */ +@Getter +public enum BorderStyleEnum { + /** + * null + */ + DEFAULT(null), + + /** + * No border (default) + */ + NONE(BorderStyle.NONE), + + /** + * Thin border + */ + THIN(BorderStyle.THIN), + + /** + * Medium border + */ + MEDIUM(BorderStyle.MEDIUM), + + /** + * dash border + */ + DASHED(BorderStyle.DASHED), + + /** + * dot border + */ + DOTTED(BorderStyle.DOTTED), + + /** + * Thick border + */ + THICK(BorderStyle.THICK), + + /** + * double-line border + */ + DOUBLE(BorderStyle.DOUBLE), + + /** + * hair-line border + */ + HAIR(BorderStyle.HAIR), + + /** + * Medium dashed border + */ + MEDIUM_DASHED(BorderStyle.MEDIUM_DASHED), + + /** + * dash-dot border + */ + DASH_DOT(BorderStyle.DASH_DOT), + + /** + * medium dash-dot border + */ + MEDIUM_DASH_DOT(BorderStyle.MEDIUM_DASH_DOT), + + /** + * dash-dot-dot border + */ + DASH_DOT_DOT(BorderStyle.DASH_DOT_DOT), + + /** + * medium dash-dot-dot border + */ + MEDIUM_DASH_DOT_DOT(BorderStyle.MEDIUM_DASH_DOT_DOT), + + /** + * slanted dash-dot border + */ + SLANTED_DASH_DOT(BorderStyle.SLANTED_DASH_DOT); + + BorderStyle poiBorderStyle; + + BorderStyleEnum(BorderStyle poiBorderStyle) { + this.poiBorderStyle = poiBorderStyle; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/FillPatternTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/FillPatternTypeEnum.java new file mode 100644 index 000000000..26fb8b324 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/FillPatternTypeEnum.java @@ -0,0 +1,119 @@ +package com.alibaba.excel.enums.poi; + +import lombok.Getter; +import org.apache.poi.ss.usermodel.FillPatternType; + +/** + * The enumeration value indicating the style of fill pattern being used for a cell format. + * + * @author Jiaju Zhuang + */ +@Getter +public enum FillPatternTypeEnum { + + /** + * null + */ + DEFAULT(null), + + /** + * No background + */ + NO_FILL(FillPatternType.NO_FILL), + + /** + * Solidly filled + */ + SOLID_FOREGROUND(FillPatternType.SOLID_FOREGROUND), + + /** + * Small fine dots + */ + FINE_DOTS(FillPatternType.FINE_DOTS), + + /** + * Wide dots + */ + ALT_BARS(FillPatternType.ALT_BARS), + + /** + * Sparse dots + */ + SPARSE_DOTS(FillPatternType.SPARSE_DOTS), + + /** + * Thick horizontal bands + */ + THICK_HORZ_BANDS(FillPatternType.THICK_HORZ_BANDS), + + /** + * Thick vertical bands + */ + THICK_VERT_BANDS(FillPatternType.THICK_VERT_BANDS), + + /** + * Thick backward facing diagonals + */ + THICK_BACKWARD_DIAG(FillPatternType.THICK_BACKWARD_DIAG), + + /** + * Thick forward facing diagonals + */ + THICK_FORWARD_DIAG(FillPatternType.THICK_FORWARD_DIAG), + + /** + * Large spots + */ + BIG_SPOTS(FillPatternType.BIG_SPOTS), + + /** + * Brick-like layout + */ + BRICKS(FillPatternType.BRICKS), + + /** + * Thin horizontal bands + */ + THIN_HORZ_BANDS(FillPatternType.THIN_HORZ_BANDS), + + /** + * Thin vertical bands + */ + THIN_VERT_BANDS(FillPatternType.THIN_VERT_BANDS), + + /** + * Thin backward diagonal + */ + THIN_BACKWARD_DIAG(FillPatternType.THIN_BACKWARD_DIAG), + + /** + * Thin forward diagonal + */ + THIN_FORWARD_DIAG(FillPatternType.THIN_FORWARD_DIAG), + + /** + * Squares + */ + SQUARES(FillPatternType.SQUARES), + + /** + * Diamonds + */ + DIAMONDS(FillPatternType.DIAMONDS), + + /** + * Less Dots + */ + LESS_DOTS(FillPatternType.LESS_DOTS), + + /** + * Least Dots + */ + LEAST_DOTS(FillPatternType.LEAST_DOTS); + + FillPatternType poiFillPatternType; + + FillPatternTypeEnum(FillPatternType poiFillPatternType) { + this.poiFillPatternType = poiFillPatternType; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/HorizontalAlignmentEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/HorizontalAlignmentEnum.java new file mode 100644 index 000000000..ecc1d4653 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/HorizontalAlignmentEnum.java @@ -0,0 +1,93 @@ +package com.alibaba.excel.enums.poi; + +import lombok.Getter; +import org.apache.poi.ss.usermodel.HorizontalAlignment; + +/** + * The enumeration value indicating horizontal alignment of a cell, + * i.e., whether it is aligned general, left, right, horizontally centered, filled (replicated), + * justified, centered across multiple cells, or distributed. + * @author Jiaju Zhuang + */ +@Getter +public enum HorizontalAlignmentEnum { + /** + * null + */ + DEFAULT(null), + /** + * The horizontal alignment is general-aligned. Text data is left-aligned. + * Numbers, dates, and times are rightaligned. Boolean types are centered. + * Changing the alignment does not change the type of data. + */ + GENERAL(HorizontalAlignment.GENERAL), + + /** + * The horizontal alignment is left-aligned, even in Rightto-Left mode. + * Aligns contents at the left edge of the cell. If an indent amount is specified, the contents of + * the cell is indented from the left by the specified number of character spaces. The character spaces are + * based on the default font and font size for the workbook. + */ + LEFT(HorizontalAlignment.LEFT), + + /** + * The horizontal alignment is centered, meaning the text is centered across the cell. + */ + CENTER(HorizontalAlignment.CENTER), + + /** + * The horizontal alignment is right-aligned, meaning that cell contents are aligned at the right edge of the cell, + * even in Right-to-Left mode. + */ + RIGHT(HorizontalAlignment.RIGHT), + + /** + * Indicates that the value of the cell should be filled + * across the entire width of the cell. If blank cells to the right also have the fill alignment, + * they are also filled with the value, using a convention similar to centerContinuous. + * + * Additional rules: + *
    + *
  1. Only whole values can be appended, not partial values.
  2. + *
  3. The column will not be widened to 'best fit' the filled value
  4. + *
  5. If appending an additional occurrence of the value exceeds the boundary of the cell + * left/right edge, don't append the additional occurrence of the value.
  6. + *
  7. The display value of the cell is filled, not the underlying raw number.
  8. + *
+ */ + FILL(HorizontalAlignment.FILL), + + /** + * The horizontal alignment is justified (flush left and right). + * For each line of text, aligns each line of the wrapped text in a cell to the right and left + * (except the last line). If no single line of text wraps in the cell, then the text is not justified. + */ + JUSTIFY(HorizontalAlignment.JUSTIFY), + + /** + * The horizontal alignment is centered across multiple cells. + * The information about how many cells to span is expressed in the Sheet Part, + * in the row of the cell in question. For each cell that is spanned in the alignment, + * a cell element needs to be written out, with the same style Id which references the centerContinuous alignment. + */ + CENTER_SELECTION(HorizontalAlignment.CENTER_SELECTION), + + /** + * Indicates that each 'word' in each line of text inside the cell is evenly distributed + * across the width of the cell, with flush right and left margins. + *

+ * When there is also an indent value to apply, both the left and right side of the cell + * are padded by the indent value. + *

+ *

A 'word' is a set of characters with no space character in them.

+ *

Two lines inside a cell are separated by a carriage return.

+ */ + DISTRIBUTED(HorizontalAlignment.DISTRIBUTED); + + HorizontalAlignment poiHorizontalAlignment; + + HorizontalAlignmentEnum(HorizontalAlignment poiHorizontalAlignment) { + this.poiHorizontalAlignment = poiHorizontalAlignment; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/VerticalAlignmentEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/VerticalAlignmentEnum.java new file mode 100644 index 000000000..d980beca1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/enums/poi/VerticalAlignmentEnum.java @@ -0,0 +1,71 @@ +package com.alibaba.excel.enums.poi; + +import lombok.Getter; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * This enumeration value indicates the type of vertical alignment for a cell, i.e., + * whether it is aligned top, bottom, vertically centered, justified or distributed. + * + * + * + * @author Jiaju Zhuang + */ +@Getter +public enum VerticalAlignmentEnum { + /** + * null + */ + DEFAULT(null), + /** + * The vertical alignment is aligned-to-top. + */ + TOP(VerticalAlignment.TOP), + + /** + * The vertical alignment is centered across the height of the cell. + */ + CENTER(VerticalAlignment.CENTER), + + /** + * The vertical alignment is aligned-to-bottom. (typically the default value) + */ + BOTTOM(VerticalAlignment.BOTTOM), + + /** + *

+ * When text direction is horizontal: the vertical alignment of lines of text is distributed vertically, + * where each line of text inside the cell is evenly distributed across the height of the cell, + * with flush top and bottom margins. + *

+ *

+ * When text direction is vertical: similar behavior as horizontal justification. + * The alignment is justified (flush top and bottom in this case). For each line of text, each + * line of the wrapped text in a cell is aligned to the top and bottom (except the last line). + * If no single line of text wraps in the cell, then the text is not justified. + *

+ */ + JUSTIFY(VerticalAlignment.JUSTIFY), + + /** + *

+ * When text direction is horizontal: the vertical alignment of lines of text is distributed vertically, + * where each line of text inside the cell is evenly distributed across the height of the cell, + * with flush top + *

+ *

+ * When text direction is vertical: behaves exactly as distributed horizontal alignment. + * The first words in a line of text (appearing at the top of the cell) are flush + * with the top edge of the cell, and the last words of a line of text are flush with the bottom edge of the cell, + * and the line of text is distributed evenly from top to bottom. + *

+ */ + DISTRIBUTED(VerticalAlignment.DISTRIBUTED); + + VerticalAlignment poiVerticalAlignmentEnum; + + VerticalAlignmentEnum(VerticalAlignment poiVerticalAlignmentEnum) { + this.poiVerticalAlignmentEnum = poiVerticalAlignmentEnum; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java new file mode 100644 index 000000000..365336b7b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/AbstractIgnoreExceptionReadListener.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.event; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.read.listener.ReadListener; + +/** + * Receives the return of each piece of data parsed + * + * @author jipengfei + * @deprecated Use directly {@link ReadListener} + */ +@Deprecated +public abstract class AbstractIgnoreExceptionReadListener implements ReadListener { + + /** + * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the + * entire read will terminate. + * + * @param exception + * @param context + */ + @Override + public void onException(Exception exception, AnalysisContext context) {} + + /** + * The current method is called when extra information is returned + * + * @param extra extra information + * @param context analysis context + */ + @Override + public void extra(CellExtra extra, AnalysisContext context) {} + + @Override + public boolean hasNext(AnalysisContext context) { + return true; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java new file mode 100644 index 000000000..96ccc6591 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.event; + +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ConverterUtils; + +/** + * Receives the return of each piece of data parsed + * + * @author jipengfei + */ +public abstract class AnalysisEventListener implements ReadListener { + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + invokeHeadMap(ConverterUtils.convertToStringMap(headMap, context), context); + } + + /** + * Returns the header as a map.Override the current method to receive header data. + * + * @param headMap + * @param context + */ + public void invokeHeadMap(Map headMap, AnalysisContext context) {} + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/Handler.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/Handler.java new file mode 100644 index 000000000..4b32be0cb --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/Handler.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.event; + +import com.alibaba.excel.constant.OrderConstant; + +/** + * Intercepts handle some business logic + * + * @author Jiaju Zhuang + **/ +public interface Handler extends Order { + + /** + * handler order + * + * @return order + */ + @Override + default int order() { + return OrderConstant.DEFAULT_ORDER; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/Listener.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/Listener.java new file mode 100644 index 000000000..e32651870 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/Listener.java @@ -0,0 +1,8 @@ +package com.alibaba.excel.event; + +/** + * Interface to listen for processing results + * + * @author Jiaju Zhuang + */ +public interface Listener {} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/NotRepeatExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/NotRepeatExecutor.java new file mode 100644 index 000000000..451655b04 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/NotRepeatExecutor.java @@ -0,0 +1,16 @@ +package com.alibaba.excel.event; + +/** + * There are multiple interceptors that execute only one of them when fired once.If you want to control which one to + * execute please use {@link Order} + * + * @author Jiaju Zhuang + **/ +public interface NotRepeatExecutor { + /** + * To see if it's the same executor + * + * @return + */ + String uniqueValue(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/Order.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/Order.java new file mode 100644 index 000000000..06f21a776 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/Order.java @@ -0,0 +1,15 @@ +package com.alibaba.excel.event; + +/** + * Implement this interface when sorting + * + * @author Jiaju Zhuang + */ +public interface Order { + /** + * The smaller the first implementation + * + * @return + */ + int order(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/event/SyncReadListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/event/SyncReadListener.java new file mode 100644 index 000000000..9de2335aa --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/event/SyncReadListener.java @@ -0,0 +1,31 @@ +package com.alibaba.excel.event; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * Synchronous data reading + * + * @author Jiaju Zhuang + */ +public class SyncReadListener extends AnalysisEventListener { + private List list = new ArrayList(); + + @Override + public void invoke(Object object, AnalysisContext context) { + list.add(object); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) {} + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } +} diff --git a/src/main/java/com/alibaba/excel/exception/ExcelAnalysisException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisException.java similarity index 76% rename from src/main/java/com/alibaba/excel/exception/ExcelAnalysisException.java rename to easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisException.java index 80d7a25fe..28a609016 100644 --- a/src/main/java/com/alibaba/excel/exception/ExcelAnalysisException.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisException.java @@ -4,10 +4,9 @@ * * @author jipengfei */ -public class ExcelAnalysisException extends RuntimeException { +public class ExcelAnalysisException extends ExcelRuntimeException { - public ExcelAnalysisException() { - } + public ExcelAnalysisException() {} public ExcelAnalysisException(String message) { super(message); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisStopException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisStopException.java new file mode 100644 index 000000000..fd05ff841 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisStopException.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.exception; + +/** + * Throw the exception when you need to stop + * This exception will stop the entire excel parsing. If you only want to stop the parsing of a certain sheet, please + * use ExcelAnalysisStopSheetException. + * + * @author Jiaju Zhuang + * @see ExcelAnalysisStopException + */ +public class ExcelAnalysisStopException extends ExcelAnalysisException { + + public ExcelAnalysisStopException() {} + + public ExcelAnalysisStopException(String message) { + super(message); + } + + public ExcelAnalysisStopException(String message, Throwable cause) { + super(message, cause); + } + + public ExcelAnalysisStopException(Throwable cause) { + super(cause); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisStopSheetException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisStopSheetException.java new file mode 100644 index 000000000..44ca9679c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelAnalysisStopSheetException.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.exception; + +/** + * Throw the exception when you need to stop + * This exception will only stop the parsing of the current sheet. If you want to stop the entire excel parsing, please + * use ExcelAnalysisStopException. + * + * The com.alibaba.excel.read.listener.ReadListener#doAfterAllAnalysed(com.alibaba.excel.context.AnalysisContext) method + * is called after the call is stopped. + * + * @author Jiaju Zhuang + * @see ExcelAnalysisStopException + * @since 3.3.4 + */ +public class ExcelAnalysisStopSheetException extends ExcelAnalysisException { + + public ExcelAnalysisStopSheetException() {} + + public ExcelAnalysisStopSheetException(String message) { + super(message); + } + + public ExcelAnalysisStopSheetException(String message, Throwable cause) { + super(message, cause); + } + + public ExcelAnalysisStopSheetException(Throwable cause) { + super(cause); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelCommonException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelCommonException.java new file mode 100644 index 000000000..4370159bc --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelCommonException.java @@ -0,0 +1,22 @@ +package com.alibaba.excel.exception; + +/** + * + * @author Jiaju Zhuang + */ +public class ExcelCommonException extends ExcelRuntimeException { + + public ExcelCommonException() {} + + public ExcelCommonException(String message) { + super(message); + } + + public ExcelCommonException(String message, Throwable cause) { + super(message, cause); + } + + public ExcelCommonException(Throwable cause) { + super(cause); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java new file mode 100644 index 000000000..f351287ac --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelDataConvertException.java @@ -0,0 +1,57 @@ +package com.alibaba.excel.exception; + +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Data convert exception + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExcelDataConvertException extends ExcelRuntimeException { + /** + * NotNull. + */ + private Integer rowIndex; + /** + * NotNull. + */ + private Integer columnIndex; + /** + * NotNull. + */ + private CellData cellData; + /** + * Nullable.Only when the header is configured and when the class header is used is not null. + * + * @see ExcelWriterBuilder#head(Class) + */ + private ExcelContentProperty excelContentProperty; + + public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData, + ExcelContentProperty excelContentProperty, String message) { + super(message); + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.cellData = cellData; + this.excelContentProperty = excelContentProperty; + } + + public ExcelDataConvertException(Integer rowIndex, Integer columnIndex, CellData cellData, + ExcelContentProperty excelContentProperty, String message, Throwable cause) { + super(message, cause); + this.rowIndex = rowIndex; + this.columnIndex = columnIndex; + this.cellData = cellData; + this.excelContentProperty = excelContentProperty; + } + +} diff --git a/src/main/java/com/alibaba/excel/exception/ExcelGenerateException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelGenerateException.java similarity index 83% rename from src/main/java/com/alibaba/excel/exception/ExcelGenerateException.java rename to easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelGenerateException.java index 266cf4652..95ac90831 100644 --- a/src/main/java/com/alibaba/excel/exception/ExcelGenerateException.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelGenerateException.java @@ -3,8 +3,7 @@ /** * @author jipengfei */ -public class ExcelGenerateException extends RuntimeException { - +public class ExcelGenerateException extends ExcelRuntimeException { public ExcelGenerateException(String message) { super(message); diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelRuntimeException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelRuntimeException.java new file mode 100644 index 000000000..5d0532014 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelRuntimeException.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.exception; + +/** + * Excel Exception + * @author Jiaju Zhuang + */ +public class ExcelRuntimeException extends RuntimeException { + public ExcelRuntimeException() {} + + public ExcelRuntimeException(String message) { + super(message); + } + + public ExcelRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public ExcelRuntimeException(Throwable cause) { + super(cause); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelWriteDataConvertException.java b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelWriteDataConvertException.java new file mode 100644 index 000000000..b8e3fcb37 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/exception/ExcelWriteDataConvertException.java @@ -0,0 +1,36 @@ +package com.alibaba.excel.exception; + +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Data convert exception + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExcelWriteDataConvertException extends ExcelDataConvertException { + /** + * handler. + */ + private CellWriteHandlerContext cellWriteHandlerContext; + + public ExcelWriteDataConvertException(CellWriteHandlerContext cellWriteHandlerContext, String message) { + super(cellWriteHandlerContext.getRowIndex(), cellWriteHandlerContext.getColumnIndex(), + cellWriteHandlerContext.getFirstCellData(), cellWriteHandlerContext.getExcelContentProperty(), message); + this.cellWriteHandlerContext = cellWriteHandlerContext; + } + + public ExcelWriteDataConvertException(CellWriteHandlerContext cellWriteHandlerContext, String message, + Throwable cause) { + super(cellWriteHandlerContext.getRowIndex(), cellWriteHandlerContext.getColumnIndex(), + cellWriteHandlerContext.getFirstCellData(), cellWriteHandlerContext.getExcelContentProperty(), message, + cause); + this.cellWriteHandlerContext = cellWriteHandlerContext; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractCell.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractCell.java new file mode 100644 index 000000000..9650b3033 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractCell.java @@ -0,0 +1,24 @@ +package com.alibaba.excel.metadata; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * cell + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class AbstractCell implements Cell { + /** + * Row index + */ + private Integer rowIndex; + /** + * Column index + */ + private Integer columnIndex; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java new file mode 100644 index 000000000..ac97c03ba --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java @@ -0,0 +1,112 @@ +package com.alibaba.excel.metadata; + +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild.ConverterKey; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Write/read holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public abstract class AbstractHolder implements ConfigurationHolder { + /** + * Record whether it's new or from cache + */ + private Boolean newInitialization; + /** + * You can only choose one of the {@link AbstractHolder#head} and {@link AbstractHolder#clazz} + */ + private List> head; + /** + * You can only choose one of the {@link AbstractHolder#head} and {@link AbstractHolder#clazz} + */ + private Class clazz; + /** + * Some global variables + */ + private GlobalConfiguration globalConfiguration; + /** + *

+ * Read key: + *

+ * Write key: + */ + private Map> converterMap; + + public AbstractHolder(BasicParameter basicParameter, AbstractHolder prentAbstractHolder) { + this.newInitialization = Boolean.TRUE; + if (basicParameter.getHead() == null && basicParameter.getClazz() == null && prentAbstractHolder != null) { + this.head = prentAbstractHolder.getHead(); + } else { + this.head = basicParameter.getHead(); + } + if (basicParameter.getHead() == null && basicParameter.getClazz() == null && prentAbstractHolder != null) { + this.clazz = prentAbstractHolder.getClazz(); + } else { + this.clazz = basicParameter.getClazz(); + } + this.globalConfiguration = new GlobalConfiguration(); + if (basicParameter.getAutoTrim() == null) { + if (prentAbstractHolder != null) { + globalConfiguration.setAutoTrim(prentAbstractHolder.getGlobalConfiguration().getAutoTrim()); + } + } else { + globalConfiguration.setAutoTrim(basicParameter.getAutoTrim()); + } + + if (basicParameter.getUse1904windowing() == null) { + if (prentAbstractHolder != null) { + globalConfiguration.setUse1904windowing( + prentAbstractHolder.getGlobalConfiguration().getUse1904windowing()); + } + } else { + globalConfiguration.setUse1904windowing(basicParameter.getUse1904windowing()); + } + + if (basicParameter.getLocale() == null) { + if (prentAbstractHolder != null) { + globalConfiguration.setLocale(prentAbstractHolder.getGlobalConfiguration().getLocale()); + } + } else { + globalConfiguration.setLocale(basicParameter.getLocale()); + } + + if (basicParameter.getFiledCacheLocation() == null) { + if (prentAbstractHolder != null) { + globalConfiguration.setFiledCacheLocation( + prentAbstractHolder.getGlobalConfiguration().getFiledCacheLocation()); + } + } else { + globalConfiguration.setFiledCacheLocation(basicParameter.getFiledCacheLocation()); + } + + } + + @Override + public Map> converterMap() { + return getConverterMap(); + } + + @Override + public GlobalConfiguration globalConfiguration() { + return getGlobalConfiguration(); + } + + @Override + public boolean isNew() { + return getNewInitialization(); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java new file mode 100644 index 000000000..242112113 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/AbstractParameterBuilder.java @@ -0,0 +1,111 @@ +package com.alibaba.excel.metadata; + +import java.util.List; +import java.util.Locale; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CacheLocationEnum; +import com.alibaba.excel.util.ListUtils; + +/** + * ExcelBuilder + * + * @author Jiaju Zhuang + */ +public abstract class AbstractParameterBuilder { + /** + * You can only choose one of the {@link #head(List)} and {@link #head(Class)} + * + * @param head + * @return + */ + public T head(List> head) { + parameter().setHead(head); + return self(); + } + + /** + * You can only choose one of the {@link #head(List)} and {@link #head(Class)} + * + * @param clazz + * @return + */ + public T head(Class clazz) { + parameter().setClazz(clazz); + return self(); + } + + /** + * Custom type conversions override the default. + * + * @param converter + * @return + */ + public T registerConverter(Converter converter) { + if (parameter().getCustomConverterList() == null) { + parameter().setCustomConverterList(ListUtils.newArrayList()); + } + parameter().getCustomConverterList().add(converter); + return self(); + } + + /** + * true if date uses 1904 windowing, or false if using 1900 date windowing. + * + * default is false + * + * @param use1904windowing + * @return + */ + public T use1904windowing(Boolean use1904windowing) { + parameter().setUse1904windowing(use1904windowing); + return self(); + } + + /** + * A Locale object represents a specific geographical, political, or cultural region. This parameter is + * used when formatting dates and numbers. + * + * @param locale + * @return + */ + public T locale(Locale locale) { + parameter().setLocale(locale); + return self(); + } + + /** + * The cache used when parsing fields such as head. + * + * default is THREAD_LOCAL. + * + * @since 3.3.0 + */ + public T filedCacheLocation(CacheLocationEnum filedCacheLocation) { + parameter().setFiledCacheLocation(filedCacheLocation); + return self(); + } + + /** + * Automatic trim includes sheet name and content + * + * @param autoTrim + * @return + */ + public T autoTrim(Boolean autoTrim) { + parameter().setAutoTrim(autoTrim); + return self(); + } + + @SuppressWarnings("unchecked") + protected T self() { + return (T)this; + } + + /** + * Get parameter + * + * @return + */ + protected abstract C parameter(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java new file mode 100644 index 000000000..e0127a8b1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/BasicParameter.java @@ -0,0 +1,65 @@ +package com.alibaba.excel.metadata; + +import java.util.List; +import java.util.Locale; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CacheLocationEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Basic parameter + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class BasicParameter { + /** + * You can only choose one of the {@link BasicParameter#head} and {@link BasicParameter#clazz} + */ + private List> head; + /** + * You can only choose one of the {@link BasicParameter#head} and {@link BasicParameter#clazz} + */ + private Class clazz; + /** + * Custom type conversions override the default + */ + private List> customConverterList; + /** + * Automatic trim includes sheet name and content + */ + private Boolean autoTrim; + /** + * true if date uses 1904 windowing, or false if using 1900 date windowing. + * + * default is false + * + * @return + */ + private Boolean use1904windowing; + /** + * A Locale object represents a specific geographical, political, or cultural region. This parameter is + * used when formatting dates and numbers. + */ + private Locale locale; + + /** + * Whether to use scientific Format. + * + * default is false + */ + private Boolean useScientificFormat; + + /** + * The cache used when parsing fields such as head. + * + * default is THREAD_LOCAL. + */ + private CacheLocationEnum filedCacheLocation; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Cell.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Cell.java new file mode 100644 index 000000000..259df7afd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Cell.java @@ -0,0 +1,22 @@ +package com.alibaba.excel.metadata; + +/** + * Cell + * + * @author Jiaju Zhuang + **/ +public interface Cell { + /** + * Row index + * + * @return + */ + Integer getRowIndex(); + + /** + * Column index + * + * @return + */ + Integer getColumnIndex(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/CellExtra.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/CellExtra.java new file mode 100644 index 000000000..778f95fc4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/CellExtra.java @@ -0,0 +1,121 @@ +package com.alibaba.excel.metadata; + +import org.apache.poi.ss.util.CellReference; + +import com.alibaba.excel.constant.ExcelXmlConstants; +import com.alibaba.excel.enums.CellExtraTypeEnum; + +/** + * Cell extra information. + * + * @author Jiaju Zhuang + */ +public class CellExtra extends AbstractCell { + /** + * Cell extra type + */ + private CellExtraTypeEnum type; + /** + * Cell extra data + */ + private String text; + /** + * First row index, if this object is an interval + */ + private Integer firstRowIndex; + /** + * Last row index, if this object is an interval + */ + private Integer lastRowIndex; + /** + * First column index, if this object is an interval + */ + private Integer firstColumnIndex; + /** + * Last column index, if this object is an interval + */ + private Integer lastColumnIndex; + + public CellExtra(CellExtraTypeEnum type, String text, String range) { + super(); + this.type = type; + this.text = text; + String[] ranges = range.split(ExcelXmlConstants.CELL_RANGE_SPLIT); + CellReference first = new CellReference(ranges[0]); + CellReference last = first; + this.firstRowIndex = first.getRow(); + this.firstColumnIndex = (int)first.getCol(); + setRowIndex(this.firstRowIndex); + setColumnIndex(this.firstColumnIndex); + if (ranges.length > 1) { + last = new CellReference(ranges[1]); + } + this.lastRowIndex = last.getRow(); + this.lastColumnIndex = (int)last.getCol(); + } + + public CellExtra(CellExtraTypeEnum type, String text, Integer rowIndex, Integer columnIndex) { + this(type, text, rowIndex, rowIndex, columnIndex, columnIndex); + } + + public CellExtra(CellExtraTypeEnum type, String text, Integer firstRowIndex, Integer lastRowIndex, + Integer firstColumnIndex, Integer lastColumnIndex) { + super(); + setRowIndex(firstRowIndex); + setColumnIndex(firstColumnIndex); + this.type = type; + this.text = text; + this.firstRowIndex = firstRowIndex; + this.firstColumnIndex = firstColumnIndex; + this.lastRowIndex = lastRowIndex; + this.lastColumnIndex = lastColumnIndex; + } + + public CellExtraTypeEnum getType() { + return type; + } + + public void setType(CellExtraTypeEnum type) { + this.type = type; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Integer getFirstRowIndex() { + return firstRowIndex; + } + + public void setFirstRowIndex(Integer firstRowIndex) { + this.firstRowIndex = firstRowIndex; + } + + public Integer getFirstColumnIndex() { + return firstColumnIndex; + } + + public void setFirstColumnIndex(Integer firstColumnIndex) { + this.firstColumnIndex = firstColumnIndex; + } + + public Integer getLastRowIndex() { + return lastRowIndex; + } + + public void setLastRowIndex(Integer lastRowIndex) { + this.lastRowIndex = lastRowIndex; + } + + public Integer getLastColumnIndex() { + return lastColumnIndex; + } + + public void setLastColumnIndex(Integer lastColumnIndex) { + this.lastColumnIndex = lastColumnIndex; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/CellRange.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/CellRange.java new file mode 100644 index 000000000..8552d09fe --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/CellRange.java @@ -0,0 +1,26 @@ +package com.alibaba.excel.metadata; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class CellRange { + + private int firstRow; + private int lastRow; + private int firstCol; + private int lastCol; + + public CellRange(int firstRow, int lastRow, int firstCol, int lastCol) { + this.firstRow = firstRow; + this.lastRow = lastRow; + this.firstCol = firstCol; + this.lastCol = lastCol; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/ConfigurationHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/ConfigurationHolder.java new file mode 100644 index 000000000..e842466da --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/ConfigurationHolder.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.metadata; + +import java.util.Map; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild.ConverterKey; + +/** + * Get the corresponding holder + * + * @author Jiaju Zhuang + **/ +public interface ConfigurationHolder extends Holder { + + /** + * Record whether it's new or from cache + * + * @return Record whether it's new or from cache + */ + boolean isNew(); + + /** + * Some global variables + * + * @return Global configuration + */ + GlobalConfiguration globalConfiguration(); + + /** + * What converter does the currently operated cell need to execute + * + * @return Converter + */ + Map> converterMap(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java new file mode 100644 index 000000000..b3707309d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldCache.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.metadata; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * filed cache + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +public class FieldCache { + + /** + * A field cache that has been sorted by a class. + * It will exclude fields that are not needed. + */ + private Map sortedFieldMap; + + /** + * Fields using the index attribute + */ + private Map indexFieldMap; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldWrapper.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldWrapper.java new file mode 100644 index 000000000..bfe9321e8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/FieldWrapper.java @@ -0,0 +1,43 @@ +package com.alibaba.excel.metadata; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * filed wrapper + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +@NoArgsConstructor +public class FieldWrapper { + + /** + * field + */ + private Field field; + + /** + * The field name matching cglib + */ + private String fieldName; + + /** + * The name of the sheet header. + * + * @see ExcelProperty + */ + private String[] heads; +} diff --git a/src/main/java/com/alibaba/excel/metadata/Font.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Font.java similarity index 88% rename from src/main/java/com/alibaba/excel/metadata/Font.java rename to easyexcel-core/src/main/java/com/alibaba/excel/metadata/Font.java index 45330119e..0ba014184 100644 --- a/src/main/java/com/alibaba/excel/metadata/Font.java +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Font.java @@ -3,7 +3,9 @@ /** * * @author jipengfei + * @deprecated please use {@link com.alibaba.excel.write.metadata.style.WriteFont} */ +@Deprecated public class Font { /** diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java new file mode 100644 index 000000000..fe823dada --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/GlobalConfiguration.java @@ -0,0 +1,59 @@ +package com.alibaba.excel.metadata; + +import java.util.Locale; + +import com.alibaba.excel.enums.CacheLocationEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Global configuration + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class GlobalConfiguration { + /** + * Automatic trim includes sheet name and content + */ + private Boolean autoTrim; + /** + * true if date uses 1904 windowing, or false if using 1900 date windowing. + * + * default is false + * + * @return + */ + private Boolean use1904windowing; + /** + * A Locale object represents a specific geographical, political, or cultural region. This parameter is + * used when formatting dates and numbers. + */ + private Locale locale; + + /** + * Whether to use scientific Format. + * + * default is false + */ + private Boolean useScientificFormat; + + /** + * The cache used when parsing fields such as head. + * + * default is THREAD_LOCAL. + */ + private CacheLocationEnum filedCacheLocation; + + public GlobalConfiguration() { + this.autoTrim = Boolean.TRUE; + this.use1904windowing = Boolean.FALSE; + this.locale = Locale.getDefault(); + this.useScientificFormat = Boolean.FALSE; + this.filedCacheLocation = CacheLocationEnum.THREAD_LOCAL; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java new file mode 100644 index 000000000..5aa1cc3af --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Head.java @@ -0,0 +1,87 @@ +package com.alibaba.excel.metadata; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.property.ColumnWidthProperty; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.metadata.property.StyleProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * excel head + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class Head { + /** + * Column index of head + */ + private Integer columnIndex; + /** + * It only has values when passed in {@link Sheet#setClazz(Class)} and {@link Table#setClazz(Class)} + */ + private Field field; + /** + * It only has values when passed in {@link Sheet#setClazz(Class)} and {@link Table#setClazz(Class)} + */ + private String fieldName; + /** + * Head name + */ + private List headNameList; + /** + * Whether index is specified + */ + private Boolean forceIndex; + /** + * Whether to specify a name + */ + private Boolean forceName; + + /** + * column with + */ + private ColumnWidthProperty columnWidthProperty; + + /** + * Loop merge + */ + private LoopMergeProperty loopMergeProperty; + /** + * Head style + */ + private StyleProperty headStyleProperty; + /** + * Head font + */ + private FontProperty headFontProperty; + + public Head(Integer columnIndex, Field field, String fieldName, List headNameList, Boolean forceIndex, + Boolean forceName) { + this.columnIndex = columnIndex; + this.field = field; + this.fieldName = fieldName; + if (headNameList == null) { + this.headNameList = new ArrayList<>(); + } else { + this.headNameList = headNameList; + for (String headName : headNameList) { + if (headName == null) { + throw new ExcelGenerateException("head name can not be null."); + } + } + } + this.forceIndex = forceIndex; + this.forceName = forceName; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Holder.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Holder.java new file mode 100644 index 000000000..f2f430f22 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/Holder.java @@ -0,0 +1,20 @@ +package com.alibaba.excel.metadata; + +import com.alibaba.excel.enums.HolderEnum; + +/** + * + * Get the corresponding holder + * + * @author Jiaju Zhuang + **/ +public interface Holder { + + /** + * What holder is the return + * + * @return Holder enum. + */ + HolderEnum holderType(); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/NullObject.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/NullObject.java new file mode 100644 index 000000000..845d98a18 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/NullObject.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.metadata; + +/** + * Null object. + * + * @author Jiaju Zhuang + */ +public class NullObject { +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java new file mode 100644 index 000000000..a75c5765d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvCell.java @@ -0,0 +1,351 @@ +package com.alibaba.excel.metadata.csv; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Calendar; +import java.util.Date; + +import com.alibaba.excel.enums.NumericCellTypeEnum; +import com.alibaba.excel.metadata.data.FormulaData; + +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.SpreadsheetVersion; +import org.apache.poi.ss.usermodel.CellBase; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.Hyperlink; +import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; + +/** + * csv cell + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvCell extends CellBase { + + /** + * column index + */ + @Getter(value = AccessLevel.NONE) + @Setter(value = AccessLevel.NONE) + private Integer columnIndex; + + /** + * cell type + */ + @Getter(value = AccessLevel.NONE) + @Setter(value = AccessLevel.NONE) + private CellType cellType; + + /** + * numeric cell type + */ + private NumericCellTypeEnum numericCellType; + + /** + * workbook + */ + private final CsvWorkbook csvWorkbook; + + /** + * sheet + */ + private final CsvSheet csvSheet; + + /** + * row + */ + private final CsvRow csvRow; + + /** + * {@link CellType#NUMERIC} + */ + private BigDecimal numberValue; + /** + * {@link CellType#STRING} and {@link CellType#ERROR} {@link CellType#FORMULA} + */ + private String stringValue; + /** + * {@link CellType#BOOLEAN} + */ + private Boolean booleanValue; + + /** + * {@link CellType#NUMERIC} + */ + private LocalDateTime dateValue; + + /** + * formula + */ + private FormulaData formulaData; + + /** + * rich text string + */ + private RichTextString richTextString; + + /** + * style + */ + private CellStyle cellStyle; + + public CsvCell(CsvWorkbook csvWorkbook, CsvSheet csvSheet, CsvRow csvRow, Integer columnIndex, CellType cellType) { + this.csvWorkbook = csvWorkbook; + this.csvSheet = csvSheet; + this.csvRow = csvRow; + this.columnIndex = columnIndex; + this.cellType = cellType; + if (this.cellType == null) { + this.cellType = CellType._NONE; + } + } + + @Override + protected void setCellTypeImpl(CellType cellType) { + this.cellType = cellType; + } + + @Override + protected void setCellFormulaImpl(String formula) { + FormulaData formulaData = new FormulaData(); + formulaData.setFormulaValue(formula); + this.formulaData = formulaData; + this.cellType = CellType.FORMULA; + } + + @Override + protected void removeFormulaImpl() { + this.formulaData = null; + } + + @Override + protected void setCellValueImpl(double value) { + numberValue = BigDecimal.valueOf(value); + this.cellType = CellType.NUMERIC; + } + + @Override + protected void setCellValueImpl(Date value) { + if (value == null) { + return; + } + this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); + this.cellType = CellType.NUMERIC; + this.numericCellType = NumericCellTypeEnum.DATE; + } + + @Override + protected void setCellValueImpl(LocalDateTime value) { + this.dateValue = value; + this.cellType = CellType.NUMERIC; + this.numericCellType = NumericCellTypeEnum.DATE; + } + + @Override + protected void setCellValueImpl(Calendar value) { + if (value == null) { + return; + } + this.dateValue = LocalDateTime.ofInstant(value.toInstant(), ZoneId.systemDefault()); + this.cellType = CellType.NUMERIC; + } + + @Override + protected void setCellValueImpl(String value) { + this.stringValue = value; + this.cellType = CellType.STRING; + } + + @Override + protected void setCellValueImpl(RichTextString value) { + richTextString = value; + this.cellType = CellType.STRING; + } + + @Override + public void setCellValue(String value) { + if (value == null) { + setBlank(); + return; + } + setCellValueImpl(value); + } + + @Override + public void setCellValue(RichTextString value) { + if (value == null || value.getString() == null) { + setBlank(); + return; + } + setCellValueImpl(value); + } + + @Override + protected SpreadsheetVersion getSpreadsheetVersion() { + return null; + } + + @Override + public int getColumnIndex() { + return columnIndex; + } + + @Override + public int getRowIndex() { + return csvRow.getRowNum(); + } + + @Override + public Sheet getSheet() { + return csvRow.getSheet(); + } + + @Override + public Row getRow() { + return csvRow; + } + + @Override + public CellType getCellType() { + return cellType; + } + + @Override + public CellType getCachedFormulaResultType() { + return getCellType(); + } + + @Override + public String getCellFormula() { + if (formulaData == null) { + return null; + } + return formulaData.getFormulaValue(); + } + + @Override + public double getNumericCellValue() { + if (numberValue == null) { + return 0; + } + return numberValue.doubleValue(); + } + + @Override + public Date getDateCellValue() { + if (dateValue == null) { + return null; + } + return Date.from(dateValue.atZone(ZoneId.systemDefault()).toInstant()); + } + + @Override + public LocalDateTime getLocalDateTimeCellValue() { + return dateValue; + } + + @Override + public RichTextString getRichStringCellValue() { + return richTextString; + } + + @Override + public String getStringCellValue() { + return stringValue; + } + + @Override + public void setCellValue(boolean value) { + this.booleanValue = value; + this.cellType = CellType.BOOLEAN; + } + + @Override + public void setCellErrorValue(byte value) { + this.numberValue = BigDecimal.valueOf(value); + this.cellType = CellType.ERROR; + } + + @Override + public boolean getBooleanCellValue() { + if (booleanValue == null) { + return false; + } + return booleanValue; + } + + @Override + public byte getErrorCellValue() { + if (numberValue == null) { + return 0; + } + return numberValue.byteValue(); + } + + @Override + public void setCellStyle(CellStyle style) { + this.cellStyle = style; + } + + @Override + public CellStyle getCellStyle() { + return cellStyle; + } + + @Override + public void setAsActiveCell() { + + } + + @Override + public void setCellComment(Comment comment) { + + } + + @Override + public Comment getCellComment() { + return null; + } + + @Override + public void removeCellComment() { + + } + + @Override + public Hyperlink getHyperlink() { + return null; + } + + @Override + public void setHyperlink(Hyperlink link) { + + } + + @Override + public void removeHyperlink() { + + } + + @Override + public CellRangeAddress getArrayFormulaRange() { + return null; + } + + @Override + public boolean isPartOfArrayFormulaGroup() { + return false; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java new file mode 100644 index 000000000..7a2714a04 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvCellStyle.java @@ -0,0 +1,312 @@ +package com.alibaba.excel.metadata.csv; + +import com.alibaba.excel.metadata.data.DataFormatData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Color; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * csv cell style + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvCellStyle implements CellStyle { + + /** + * data format + */ + private DataFormatData dataFormatData; + + /** + * index + */ + private Short index; + + public CsvCellStyle(Short index) { + this.index = index; + } + + @Override + public short getIndex() { + return index; + } + + @Override + public void setDataFormat(short fmt) { + initDataFormatData(); + dataFormatData.setIndex(fmt); + } + + private void initDataFormatData() { + if (dataFormatData == null) { + dataFormatData = new DataFormatData(); + } + } + + @Override + public short getDataFormat() { + if (dataFormatData == null) { + return 0; + } + return dataFormatData.getIndex(); + } + + @Override + public String getDataFormatString() { + if (dataFormatData == null) { + return null; + } + return dataFormatData.getFormat(); + } + + @Override + public void setFont(Font font) { + + } + + @Override + public int getFontIndex() { + return 0; + } + + @Override + public int getFontIndexAsInt() { + return 0; + } + + @Override + public void setHidden(boolean hidden) { + + } + + @Override + public boolean getHidden() { + return false; + } + + @Override + public void setLocked(boolean locked) { + + } + + @Override + public boolean getLocked() { + return false; + } + + @Override + public void setQuotePrefixed(boolean quotePrefix) { + + } + + @Override + public boolean getQuotePrefixed() { + return false; + } + + @Override + public void setAlignment(HorizontalAlignment align) { + + } + + @Override + public HorizontalAlignment getAlignment() { + return null; + } + + @Override + public void setWrapText(boolean wrapped) { + + } + + @Override + public boolean getWrapText() { + return false; + } + + @Override + public void setVerticalAlignment(VerticalAlignment align) { + + } + + @Override + public VerticalAlignment getVerticalAlignment() { + return null; + } + + @Override + public void setRotation(short rotation) { + + } + + @Override + public short getRotation() { + return 0; + } + + @Override + public void setIndention(short indent) { + + } + + @Override + public short getIndention() { + return 0; + } + + @Override + public void setBorderLeft(BorderStyle border) { + + } + + @Override + public BorderStyle getBorderLeft() { + return null; + } + + @Override + public void setBorderRight(BorderStyle border) { + + } + + @Override + public BorderStyle getBorderRight() { + return null; + } + + @Override + public void setBorderTop(BorderStyle border) { + + } + + @Override + public BorderStyle getBorderTop() { + return null; + } + + @Override + public void setBorderBottom(BorderStyle border) { + + } + + @Override + public BorderStyle getBorderBottom() { + return null; + } + + @Override + public void setLeftBorderColor(short color) { + + } + + @Override + public short getLeftBorderColor() { + return 0; + } + + @Override + public void setRightBorderColor(short color) { + + } + + @Override + public short getRightBorderColor() { + return 0; + } + + @Override + public void setTopBorderColor(short color) { + + } + + @Override + public short getTopBorderColor() { + return 0; + } + + @Override + public void setBottomBorderColor(short color) { + + } + + @Override + public short getBottomBorderColor() { + return 0; + } + + @Override + public void setFillPattern(FillPatternType fp) { + + } + + @Override + public FillPatternType getFillPattern() { + return null; + } + + @Override + public void setFillBackgroundColor(short bg) { + + } + + @Override + public void setFillBackgroundColor(Color color) { + + } + + @Override + public short getFillBackgroundColor() { + return 0; + } + + @Override + public Color getFillBackgroundColorColor() { + return null; + } + + @Override + public void setFillForegroundColor(short bg) { + + } + + @Override + public void setFillForegroundColor(Color color) { + + } + + @Override + public short getFillForegroundColor() { + return 0; + } + + @Override + public Color getFillForegroundColorColor() { + return null; + } + + @Override + public void cloneStyleFrom(CellStyle source) { + + } + + @Override + public void setShrinkToFit(boolean shrinkToFit) { + + } + + @Override + public boolean getShrinkToFit() { + return false; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java new file mode 100644 index 000000000..9efc7321a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvDataFormat.java @@ -0,0 +1,66 @@ +package com.alibaba.excel.metadata.csv; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; + +import org.apache.poi.ss.usermodel.DataFormat; + +/** + * format data + * + * @author Jiaju Zhuang + */ +public class CsvDataFormat implements DataFormat { + /** + * It is stored in both map and list for easy retrieval + */ + private final Map formatMap; + private final List formatList; + + /** + * Excel's built-in format conversion. + */ + private final Map builtinFormatsMap; + private final String[] builtinFormats; + + public CsvDataFormat(Locale locale) { + formatMap = MapUtils.newHashMap(); + formatList = ListUtils.newArrayList(); + builtinFormatsMap = BuiltinFormats.switchBuiltinFormatsMap(locale); + builtinFormats = BuiltinFormats.switchBuiltinFormats(locale); + } + + @Override + public short getFormat(String format) { + Short index = builtinFormatsMap.get(format); + if (index != null) { + return index; + } + index = formatMap.get(format); + if (index != null) { + return index; + } + short indexPrimitive = (short)(formatList.size() + BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX); + index = indexPrimitive; + formatList.add(format); + formatMap.put(format, index); + return indexPrimitive; + } + + @Override + public String getFormat(short index) { + if (index < BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX) { + return builtinFormats[index]; + } + int actualIndex = index - BuiltinFormats.MIN_CUSTOM_DATA_FORMAT_INDEX; + if (actualIndex < formatList.size()) { + return formatList.get(actualIndex); + } + return null; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java new file mode 100644 index 000000000..1c54b0c35 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvRichTextString.java @@ -0,0 +1,68 @@ +package com.alibaba.excel.metadata.csv; + +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.RichTextString; + +/** + * rich text string + * + * @author Jiaju Zhuang + */ +public class CsvRichTextString implements RichTextString { + /** + * string + */ + private final String string; + + public CsvRichTextString(String string) { + this.string = string; + } + + @Override + public void applyFont(int startIndex, int endIndex, short fontIndex) { + + } + + @Override + public void applyFont(int startIndex, int endIndex, Font font) { + + } + + @Override + public void applyFont(Font font) { + + } + + @Override + public void clearFormatting() { + + } + + @Override + public String getString() { + return string; + } + + @Override + public int length() { + if (string == null) { + return 0; + } + return string.length(); + } + + @Override + public int numFormattingRuns() { + return 0; + } + + @Override + public int getIndexOfFormattingRun(int index) { + return 0; + } + + @Override + public void applyFont(short fontIndex) { + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvRow.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvRow.java new file mode 100644 index 000000000..89e76a716 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvRow.java @@ -0,0 +1,196 @@ +package com.alibaba.excel.metadata.csv; + +import java.util.Iterator; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +/** + * csv row + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvRow implements Row { + + /** + * cell list + */ + private final List cellList; + + /** + * workbook + */ + private final CsvWorkbook csvWorkbook; + + /** + * sheet + */ + private final CsvSheet csvSheet; + + /** + * row index + */ + private Integer rowIndex; + + /** + * style + */ + private CellStyle cellStyle; + + public CsvRow(CsvWorkbook csvWorkbook, CsvSheet csvSheet, Integer rowIndex) { + cellList = Lists.newArrayList(); + this.csvWorkbook = csvWorkbook; + this.csvSheet = csvSheet; + this.rowIndex = rowIndex; + } + + @Override + public Cell createCell(int column) { + CsvCell cell = new CsvCell(csvWorkbook, csvSheet, this, column, null); + cellList.add(cell); + return cell; + } + + @Override + public Cell createCell(int column, CellType type) { + CsvCell cell = new CsvCell(csvWorkbook, csvSheet, this, column, type); + cellList.add(cell); + return cell; + } + + @Override + public void removeCell(Cell cell) { + cellList.remove(cell); + } + + @Override + public void setRowNum(int rowNum) { + this.rowIndex = rowNum; + } + + @Override + public int getRowNum() { + return rowIndex; + } + + @Override + public Cell getCell(int cellnum) { + if (cellnum >= cellList.size()) { + return null; + } + return cellList.get(cellnum - 1); + } + + @Override + public Cell getCell(int cellnum, MissingCellPolicy policy) { + return getCell(cellnum); + } + + @Override + public short getFirstCellNum() { + if (CollectionUtils.isEmpty(cellList)) { + return -1; + } + return 0; + } + + @Override + public short getLastCellNum() { + if (CollectionUtils.isEmpty(cellList)) { + return -1; + } + return (short)cellList.size(); + } + + @Override + public int getPhysicalNumberOfCells() { + return getRowNum(); + } + + @Override + public void setHeight(short height) { + + } + + @Override + public void setZeroHeight(boolean zHeight) { + + } + + @Override + public boolean getZeroHeight() { + return false; + } + + @Override + public void setHeightInPoints(float height) { + + } + + @Override + public short getHeight() { + return 0; + } + + @Override + public float getHeightInPoints() { + return 0; + } + + @Override + public boolean isFormatted() { + return false; + } + + @Override + public CellStyle getRowStyle() { + return cellStyle; + } + + @Override + public void setRowStyle(CellStyle style) { + this.cellStyle = style; + } + + @Override + public Iterator cellIterator() { + return (Iterator)(Iterator)cellList.iterator(); + } + + @Override + public Sheet getSheet() { + return csvSheet; + } + + @Override + public int getOutlineLevel() { + return 0; + } + + @Override + public void shiftCellsRight(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { + + } + + @Override + public void shiftCellsLeft(int firstShiftColumnIndex, int lastShiftColumnIndex, int step) { + + } + + @Override + public Iterator iterator() { + return cellIterator(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java new file mode 100644 index 000000000..5181583c8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvSheet.java @@ -0,0 +1,836 @@ +package com.alibaba.excel.metadata.csv; + +import java.io.Closeable; +import java.io.IOException; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.enums.ByteOrderMarkEnum; +import com.alibaba.excel.enums.NumericCellTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.util.NumberDataFormatterUtils; +import com.alibaba.excel.util.StringUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.io.ByteOrderMark; +import org.apache.poi.ss.usermodel.AutoFilter; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellRange; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Footer; +import org.apache.poi.ss.usermodel.Header; +import org.apache.poi.ss.usermodel.Hyperlink; +import org.apache.poi.ss.usermodel.PageMargin; +import org.apache.poi.ss.usermodel.PaneType; +import org.apache.poi.ss.usermodel.PrintSetup; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.SheetConditionalFormatting; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellAddress; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.PaneInformation; + +/** + * csv sheet + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvSheet implements Sheet, Closeable { + + /** + * workbook + */ + private CsvWorkbook csvWorkbook; + /** + * output + */ + private Appendable out; + /** + * row cache + */ + private Integer rowCacheCount; + /** + * format + */ + public CSVFormat csvFormat; + + /** + * last row index + */ + private Integer lastRowIndex; + + /** + * row cache + */ + private List rowCache; + /** + * csv printer + */ + private CSVPrinter csvPrinter; + + public CsvSheet(CsvWorkbook csvWorkbook, Appendable out) { + this.csvWorkbook = csvWorkbook; + this.out = out; + this.rowCacheCount = 100; + this.csvFormat = CSVFormat.DEFAULT; + this.lastRowIndex = -1; + } + + @Override + public Row createRow(int rownum) { + // Initialize the data when the row is first created + initSheet(); + + lastRowIndex++; + assert rownum == lastRowIndex : "csv create row must be in order."; + printData(); + CsvRow csvRow = new CsvRow(csvWorkbook, this, rownum); + rowCache.add(csvRow); + return csvRow; + } + + private void initSheet() { + if (csvPrinter != null) { + return; + } + rowCache = ListUtils.newArrayListWithExpectedSize(rowCacheCount); + try { + if (csvWorkbook.getWithBom()) { + ByteOrderMarkEnum byteOrderMark = ByteOrderMarkEnum.valueOfByCharsetName( + csvWorkbook.getCharset().name()); + if (byteOrderMark != null) { + out.append(byteOrderMark.getStringPrefix()); + } + } + csvPrinter = csvFormat.print(out); + } catch (IOException e) { + throw new ExcelGenerateException(e); + } + } + + @Override + public void removeRow(Row row) { + throw new UnsupportedOperationException("csv cannot move row."); + } + + @Override + public Row getRow(int rownum) { + int actualRowIndex = rownum - (lastRowIndex - rowCache.size()) - 1; + if (actualRowIndex < 0 || actualRowIndex > rowCache.size() - 1) { + throw new UnsupportedOperationException("The current data does not exist or has been flushed to disk\n."); + } + return rowCache.get(actualRowIndex); + } + + @Override + public int getPhysicalNumberOfRows() { + return lastRowIndex - rowCache.size(); + } + + @Override + public int getFirstRowNum() { + if (lastRowIndex < 0) { + return -1; + } + return 0; + } + + @Override + public int getLastRowNum() { + return lastRowIndex; + } + + @Override + public void setColumnHidden(int columnIndex, boolean hidden) { + + } + + @Override + public boolean isColumnHidden(int columnIndex) { + return false; + } + + @Override + public void setRightToLeft(boolean value) { + + } + + @Override + public boolean isRightToLeft() { + return false; + } + + @Override + public void setColumnWidth(int columnIndex, int width) { + + } + + @Override + public int getColumnWidth(int columnIndex) { + return 0; + } + + @Override + public float getColumnWidthInPixels(int columnIndex) { + return 0; + } + + @Override + public void setDefaultColumnWidth(int width) { + + } + + @Override + public int getDefaultColumnWidth() { + return 0; + } + + @Override + public short getDefaultRowHeight() { + return 0; + } + + @Override + public float getDefaultRowHeightInPoints() { + return 0; + } + + @Override + public void setDefaultRowHeight(short height) { + + } + + @Override + public void setDefaultRowHeightInPoints(float height) { + + } + + @Override + public CellStyle getColumnStyle(int column) { + return null; + } + + @Override + public int addMergedRegion(CellRangeAddress region) { + return 0; + } + + @Override + public int addMergedRegionUnsafe(CellRangeAddress region) { + return 0; + } + + @Override + public void validateMergedRegions() { + + } + + @Override + public void setVerticallyCenter(boolean value) { + + } + + @Override + public void setHorizontallyCenter(boolean value) { + + } + + @Override + public boolean getHorizontallyCenter() { + return false; + } + + @Override + public boolean getVerticallyCenter() { + return false; + } + + @Override + public void removeMergedRegion(int index) { + + } + + @Override + public void removeMergedRegions(Collection indices) { + + } + + @Override + public int getNumMergedRegions() { + return 0; + } + + @Override + public CellRangeAddress getMergedRegion(int index) { + return null; + } + + @Override + public List getMergedRegions() { + return null; + } + + @Override + public Iterator rowIterator() { + return (Iterator)(Iterator)rowCache.iterator(); + } + + @Override + public void setForceFormulaRecalculation(boolean value) { + + } + + @Override + public boolean getForceFormulaRecalculation() { + return false; + } + + @Override + public void setAutobreaks(boolean value) { + + } + + @Override + public void setDisplayGuts(boolean value) { + + } + + @Override + public void setDisplayZeros(boolean value) { + + } + + @Override + public boolean isDisplayZeros() { + return false; + } + + @Override + public void setFitToPage(boolean value) { + + } + + @Override + public void setRowSumsBelow(boolean value) { + + } + + @Override + public void setRowSumsRight(boolean value) { + + } + + @Override + public boolean getAutobreaks() { + return false; + } + + @Override + public boolean getDisplayGuts() { + return false; + } + + @Override + public boolean getFitToPage() { + return false; + } + + @Override + public boolean getRowSumsBelow() { + return false; + } + + @Override + public boolean getRowSumsRight() { + return false; + } + + @Override + public boolean isPrintGridlines() { + return false; + } + + @Override + public void setPrintGridlines(boolean show) { + + } + + @Override + public boolean isPrintRowAndColumnHeadings() { + return false; + } + + @Override + public void setPrintRowAndColumnHeadings(boolean show) { + + } + + @Override + public PrintSetup getPrintSetup() { + return null; + } + + @Override + public Header getHeader() { + return null; + } + + @Override + public Footer getFooter() { + return null; + } + + @Override + public void setSelected(boolean value) { + + } + + @Override + public double getMargin(short margin) { + return 0; + } + + @Override + public double getMargin(PageMargin pageMargin) { + return 0; + } + + @Override + public void setMargin(short margin, double size) { + + } + + @Override + public void setMargin(PageMargin pageMargin, double v) { + + } + + @Override + public boolean getProtect() { + return false; + } + + @Override + public void protectSheet(String password) { + + } + + @Override + public boolean getScenarioProtect() { + return false; + } + + @Override + public void setZoom(int scale) { + + } + + @Override + public short getTopRow() { + return 0; + } + + @Override + public short getLeftCol() { + return 0; + } + + @Override + public void showInPane(int topRow, int leftCol) { + + } + + @Override + public void shiftRows(int startRow, int endRow, int n) { + + } + + @Override + public void shiftRows(int startRow, int endRow, int n, boolean copyRowHeight, boolean resetOriginalRowHeight) { + + } + + @Override + public void shiftColumns(int startColumn, int endColumn, int n) { + + } + + @Override + public void createFreezePane(int colSplit, int rowSplit, int leftmostColumn, int topRow) { + + } + + @Override + public void createFreezePane(int colSplit, int rowSplit) { + + } + + @Override + public void createSplitPane(int xSplitPos, int ySplitPos, int leftmostColumn, int topRow, int activePane) { + + } + + @Override + public void createSplitPane(int i, int i1, int i2, int i3, PaneType paneType) { + + } + + @Override + public PaneInformation getPaneInformation() { + return null; + } + + @Override + public void setDisplayGridlines(boolean show) { + + } + + @Override + public boolean isDisplayGridlines() { + return false; + } + + @Override + public void setDisplayFormulas(boolean show) { + + } + + @Override + public boolean isDisplayFormulas() { + return false; + } + + @Override + public void setDisplayRowColHeadings(boolean show) { + + } + + @Override + public boolean isDisplayRowColHeadings() { + return false; + } + + @Override + public void setRowBreak(int row) { + + } + + @Override + public boolean isRowBroken(int row) { + return false; + } + + @Override + public void removeRowBreak(int row) { + + } + + @Override + public int[] getRowBreaks() { + return new int[0]; + } + + @Override + public int[] getColumnBreaks() { + return new int[0]; + } + + @Override + public void setColumnBreak(int column) { + + } + + @Override + public boolean isColumnBroken(int column) { + return false; + } + + @Override + public void removeColumnBreak(int column) { + + } + + @Override + public void setColumnGroupCollapsed(int columnNumber, boolean collapsed) { + + } + + @Override + public void groupColumn(int fromColumn, int toColumn) { + + } + + @Override + public void ungroupColumn(int fromColumn, int toColumn) { + + } + + @Override + public void groupRow(int fromRow, int toRow) { + + } + + @Override + public void ungroupRow(int fromRow, int toRow) { + + } + + @Override + public void setRowGroupCollapsed(int row, boolean collapse) { + + } + + @Override + public void setDefaultColumnStyle(int column, CellStyle style) { + + } + + @Override + public void autoSizeColumn(int column) { + + } + + @Override + public void autoSizeColumn(int column, boolean useMergedCells) { + + } + + @Override + public Comment getCellComment(CellAddress ref) { + return null; + } + + @Override + public Map getCellComments() { + return null; + } + + @Override + public Drawing getDrawingPatriarch() { + return null; + } + + @Override + public Drawing createDrawingPatriarch() { + return null; + } + + @Override + public Workbook getWorkbook() { + return csvWorkbook; + } + + @Override + public String getSheetName() { + return null; + } + + @Override + public boolean isSelected() { + return false; + } + + @Override + public CellRange setArrayFormula( + String formula, CellRangeAddress range) { + return null; + } + + @Override + public CellRange removeArrayFormula(Cell cell) { + return null; + } + + @Override + public DataValidationHelper getDataValidationHelper() { + return null; + } + + @Override + public List getDataValidations() { + return null; + } + + @Override + public void addValidationData(DataValidation dataValidation) { + + } + + @Override + public AutoFilter setAutoFilter(CellRangeAddress range) { + return null; + } + + @Override + public SheetConditionalFormatting getSheetConditionalFormatting() { + return null; + } + + @Override + public CellRangeAddress getRepeatingRows() { + return null; + } + + @Override + public CellRangeAddress getRepeatingColumns() { + return null; + } + + @Override + public void setRepeatingRows(CellRangeAddress rowRangeRef) { + + } + + @Override + public void setRepeatingColumns(CellRangeAddress columnRangeRef) { + + } + + @Override + public int getColumnOutlineLevel(int columnIndex) { + return 0; + } + + @Override + public Hyperlink getHyperlink(int row, int column) { + return null; + } + + @Override + public Hyperlink getHyperlink(CellAddress addr) { + return null; + } + + @Override + public List getHyperlinkList() { + return null; + } + + @Override + public CellAddress getActiveCell() { + return null; + } + + @Override + public void setActiveCell(CellAddress address) { + + } + + @Override + public Iterator iterator() { + return rowIterator(); + } + + @Override + public void close() throws IOException { + // Avoid empty sheets + initSheet(); + + flushData(); + csvPrinter.flush(); + csvPrinter.close(); + } + + public void printData() { + if (rowCache.size() >= rowCacheCount) { + flushData(); + } + } + + public void flushData() { + try { + for (CsvRow row : rowCache) { + Iterator cellIterator = row.cellIterator(); + int columnIndex = 0; + while (cellIterator.hasNext()) { + CsvCell csvCell = (CsvCell)cellIterator.next(); + while (csvCell.getColumnIndex() > columnIndex++) { + csvPrinter.print(null); + } + csvPrinter.print(buildCellValue(csvCell)); + } + csvPrinter.println(); + } + rowCache.clear(); + } catch (IOException e) { + throw new ExcelGenerateException(e); + } + } + + private String buildCellValue(CsvCell csvCell) { + switch (csvCell.getCellType()) { + case STRING: + case ERROR: + return csvCell.getStringCellValue(); + case NUMERIC: + Short dataFormat = null; + String dataFormatString = null; + if (csvCell.getCellStyle() != null) { + dataFormat = csvCell.getCellStyle().getDataFormat(); + dataFormatString = csvCell.getCellStyle().getDataFormatString(); + } + if (csvCell.getNumericCellType() == NumericCellTypeEnum.DATE) { + if (csvCell.getDateValue() == null) { + return null; + } + // date + if (dataFormat == null) { + dataFormatString = DateUtils.defaultDateFormat; + dataFormat = csvWorkbook.createDataFormat().getFormat(dataFormatString); + } + if (dataFormatString == null) { + dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat); + } + return NumberDataFormatterUtils.format(BigDecimal.valueOf( + DateUtil.getExcelDate(csvCell.getDateValue(), csvWorkbook.getUse1904windowing())), + dataFormat, dataFormatString, csvWorkbook.getUse1904windowing(), csvWorkbook.getLocale(), + csvWorkbook.getUseScientificFormat()); + } else { + if (csvCell.getNumberValue() == null) { + return null; + } + //number + if (dataFormat == null) { + dataFormat = BuiltinFormats.GENERAL; + dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat); + } + if (dataFormatString == null) { + dataFormatString = csvWorkbook.createDataFormat().getFormat(dataFormat); + } + return NumberDataFormatterUtils.format(csvCell.getNumberValue(), dataFormat, dataFormatString, + csvWorkbook.getUse1904windowing(), csvWorkbook.getLocale(), + csvWorkbook.getUseScientificFormat()); + } + case BOOLEAN: + return csvCell.getBooleanValue().toString(); + case BLANK: + return StringUtils.EMPTY; + default: + return null; + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java new file mode 100644 index 000000000..31724d99a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/csv/CsvWorkbook.java @@ -0,0 +1,419 @@ +package com.alibaba.excel.metadata.csv; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.compress.utils.Lists; +import org.apache.poi.ss.SpreadsheetVersion; +import org.apache.poi.ss.formula.EvaluationWorkbook; +import org.apache.poi.ss.formula.udf.UDFFinder; +import org.apache.poi.ss.usermodel.CellReferenceType; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.usermodel.PictureData; +import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.SheetVisibility; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * csv workbook + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvWorkbook implements Workbook { + /** + * output + */ + private Appendable out; + + /** + * true if date uses 1904 windowing, or false if using 1900 date windowing. + *

+ * default is false + * + */ + private Boolean use1904windowing; + + /** + * locale + */ + private Locale locale; + + /** + * Whether to use scientific Format. + *

+ * default is false + */ + private Boolean useScientificFormat; + + /** + * data format + */ + private CsvDataFormat csvDataFormat; + /** + * sheet + */ + private CsvSheet csvSheet; + /** + * cell style + */ + private List csvCellStyleList; + + /** + * charset. + */ + private Charset charset; + + /** + * Set the encoding prefix in the csv file, otherwise the office may open garbled characters. + * Default true. + */ + private Boolean withBom; + + public CsvWorkbook(Appendable out, Locale locale, Boolean use1904windowing, Boolean useScientificFormat, + Charset charset, Boolean withBom) { + this.out = out; + this.locale = locale; + this.use1904windowing = use1904windowing; + this.useScientificFormat = useScientificFormat; + this.charset = charset; + this.withBom = withBom; + } + + @Override + public int getActiveSheetIndex() { + return 0; + } + + @Override + public void setActiveSheet(int sheetIndex) { + + } + + @Override + public int getFirstVisibleTab() { + return 0; + } + + @Override + public void setFirstVisibleTab(int sheetIndex) { + + } + + @Override + public void setSheetOrder(String sheetname, int pos) { + + } + + @Override + public void setSelectedTab(int index) { + + } + + @Override + public void setSheetName(int sheet, String name) { + + } + + @Override + public String getSheetName(int sheet) { + return null; + } + + @Override + public int getSheetIndex(String name) { + return 0; + } + + @Override + public int getSheetIndex(Sheet sheet) { + return 0; + } + + @Override + public Sheet createSheet() { + assert csvSheet == null : "CSV repeat creation is not allowed."; + csvSheet = new CsvSheet(this, out); + return csvSheet; + } + + @Override + public Sheet createSheet(String sheetname) { + assert csvSheet == null : "CSV repeat creation is not allowed."; + csvSheet = new CsvSheet(this, out); + return csvSheet; + } + + @Override + public Sheet cloneSheet(int sheetNum) { + return null; + } + + @Override + public Iterator sheetIterator() { + return null; + } + + @Override + public int getNumberOfSheets() { + return 0; + } + + @Override + public Sheet getSheetAt(int index) { + assert index == 0 : "CSV exists only in one sheet."; + return csvSheet; + } + + @Override + public Sheet getSheet(String name) { + return csvSheet; + } + + @Override + public void removeSheetAt(int index) { + + } + + @Override + public Font createFont() { + return null; + } + + @Override + public Font findFont(boolean bold, short color, short fontHeight, String name, boolean italic, boolean strikeout, + short typeOffset, byte underline) { + return null; + } + + @Override + public int getNumberOfFonts() { + return 0; + } + + @Override + public int getNumberOfFontsAsInt() { + return 0; + } + + @Override + public Font getFontAt(int idx) { + return null; + } + + @Override + public CellStyle createCellStyle() { + if (csvCellStyleList == null) { + csvCellStyleList = Lists.newArrayList(); + } + CsvCellStyle csvCellStyle = new CsvCellStyle((short)csvCellStyleList.size()); + csvCellStyleList.add(csvCellStyle); + return csvCellStyle; + } + + @Override + public int getNumCellStyles() { + return csvCellStyleList.size(); + } + + @Override + public CellStyle getCellStyleAt(int idx) { + if (idx < 0 || idx >= csvCellStyleList.size()) { + return null; + } + return csvCellStyleList.get(idx); + } + + @Override + public void write(OutputStream stream) throws IOException { + csvSheet.close(); + } + + @Override + public void close() throws IOException { + + } + + @Override + public int getNumberOfNames() { + return 0; + } + + @Override + public Name getName(String name) { + return null; + } + + @Override + public List getNames(String name) { + return null; + } + + @Override + public List getAllNames() { + return null; + } + + @Override + public Name createName() { + return null; + } + + @Override + public void removeName(Name name) { + + } + + @Override + public int linkExternalWorkbook(String name, Workbook workbook) { + return 0; + } + + @Override + public void setPrintArea(int sheetIndex, String reference) { + + } + + @Override + public void setPrintArea(int sheetIndex, int startColumn, int endColumn, int startRow, int endRow) { + + } + + @Override + public String getPrintArea(int sheetIndex) { + return null; + } + + @Override + public void removePrintArea(int sheetIndex) { + + } + + @Override + public MissingCellPolicy getMissingCellPolicy() { + return null; + } + + @Override + public void setMissingCellPolicy(MissingCellPolicy missingCellPolicy) { + + } + + @Override + public DataFormat createDataFormat() { + if (csvDataFormat != null) { + return csvDataFormat; + } + csvDataFormat = new CsvDataFormat(locale); + return csvDataFormat; + } + + @Override + public int addPicture(byte[] pictureData, int format) { + return 0; + } + + @Override + public List getAllPictures() { + return null; + } + + @Override + public CreationHelper getCreationHelper() { + return null; + } + + @Override + public boolean isHidden() { + return false; + } + + @Override + public void setHidden(boolean hiddenFlag) { + + } + + @Override + public boolean isSheetHidden(int sheetIx) { + return false; + } + + @Override + public boolean isSheetVeryHidden(int sheetIx) { + return false; + } + + @Override + public void setSheetHidden(int sheetIx, boolean hidden) { + + } + + @Override + public SheetVisibility getSheetVisibility(int sheetIx) { + return null; + } + + @Override + public void setSheetVisibility(int sheetIx, SheetVisibility visibility) { + + } + + @Override + public void addToolPack(UDFFinder toopack) { + + } + + @Override + public void setForceFormulaRecalculation(boolean value) { + + } + + @Override + public boolean getForceFormulaRecalculation() { + return false; + } + + @Override + public SpreadsheetVersion getSpreadsheetVersion() { + return null; + } + + @Override + public int addOlePackage(byte[] oleData, String label, String fileName, String command) { + return 0; + } + + @Override + public EvaluationWorkbook createEvaluationWorkbook() { + return null; + } + + @Override + public CellReferenceType getCellReferenceType() { + return null; + } + + @Override + public void setCellReferenceType(CellReferenceType cellReferenceType) { + + } + + @Override + public Iterator iterator() { + return null; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java new file mode 100644 index 000000000..e70228f7d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CellData.java @@ -0,0 +1,81 @@ +package com.alibaba.excel.metadata.data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.AbstractCell; +import com.alibaba.excel.util.StringUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Excel internal cell data. + * + *

+ * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CellData extends AbstractCell { + /** + * cell type + */ + private CellDataTypeEnum type; + /** + * {@link CellDataTypeEnum#NUMBER} + */ + private BigDecimal numberValue; + /** + * {@link CellDataTypeEnum#STRING} and{@link CellDataTypeEnum#ERROR} + */ + private String stringValue; + /** + * {@link CellDataTypeEnum#BOOLEAN} + */ + private Boolean booleanValue; + + /** + * The resulting converted data. + */ + private T data; + + /** + * formula + */ + private FormulaData formulaData; + + /** + * Ensure that the object does not appear null + */ + public void checkEmpty() { + if (type == null) { + type = CellDataTypeEnum.EMPTY; + } + switch (type) { + case STRING: + case DIRECT_STRING: + case ERROR: + if (StringUtils.isEmpty(stringValue)) { + type = CellDataTypeEnum.EMPTY; + } + return; + case NUMBER: + if (numberValue == null) { + type = CellDataTypeEnum.EMPTY; + } + return; + case BOOLEAN: + if (booleanValue == null) { + type = CellDataTypeEnum.EMPTY; + } + return; + default: + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ClientAnchorData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ClientAnchorData.java new file mode 100644 index 000000000..1963133df --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ClientAnchorData.java @@ -0,0 +1,123 @@ +package com.alibaba.excel.metadata.data; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.util.Internal; + +/** + * A client anchor is attached to an excel worksheet. It anchors against + * absolute coordinates, a top-left cell and fixed height and width, or + * a top-left and bottom-right cell, depending on the {@link ClientAnchorData.AnchorType}: + *

    + *
  1. {@link ClientAnchor.AnchorType#DONT_MOVE_AND_RESIZE} == absolute top-left coordinates and width/height, no + * cell references + *
  2. {@link ClientAnchor.AnchorType#MOVE_DONT_RESIZE} == fixed top-left cell reference, absolute width/height + *
  3. {@link ClientAnchor.AnchorType#MOVE_AND_RESIZE} == fixed top-left and bottom-right cell references, dynamic + * width/height + *
+ * Note this class only reports the current values for possibly calculated positions and sizes. + * If the sheet row/column sizes or positions shift, this needs updating via external calculations. + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ClientAnchorData extends CoordinateData { + /** + * top + */ + private Integer top; + + /** + * right + */ + private Integer right; + + /** + * bottom + */ + private Integer bottom; + + /** + * left + */ + private Integer left; + + /** + * anchor type + */ + private AnchorType anchorType; + + @Getter + public enum AnchorType { + /** + * Move and Resize With Anchor Cells (0) + *

+ * Specifies that the current drawing shall move and + * resize to maintain its row and column anchors (i.e. the + * object is anchored to the actual from and to row and column) + *

+ */ + MOVE_AND_RESIZE(ClientAnchor.AnchorType.MOVE_AND_RESIZE), + + /** + * Don't Move but do Resize With Anchor Cells (1) + *

+ * Specifies that the current drawing shall not move with its + * row and column, but should be resized. This option is not normally + * used, but is included for completeness. + *

+ * Note: Excel has no setting for this combination, nor does the ECMA standard. + */ + DONT_MOVE_DO_RESIZE(ClientAnchor.AnchorType.DONT_MOVE_DO_RESIZE), + + /** + * Move With Cells but Do Not Resize (2) + *

+ * Specifies that the current drawing shall move with its + * row and column (i.e. the object is anchored to the + * actual from row and column), but that the size shall remain absolute. + *

+ *

+ * If additional rows/columns are added between the from and to locations of the drawing, + * the drawing shall move its to anchors as needed to maintain this same absolute size. + *

+ */ + MOVE_DONT_RESIZE(ClientAnchor.AnchorType.MOVE_DONT_RESIZE), + + /** + * Do Not Move or Resize With Underlying Rows/Columns (3) + *

+ * Specifies that the current start and end positions shall + * be maintained with respect to the distances from the + * absolute start point of the worksheet. + *

+ *

+ * If additional rows/columns are added before the + * drawing, the drawing shall move its anchors as needed + * to maintain this same absolute position. + *

+ */ + DONT_MOVE_AND_RESIZE(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE); + + ClientAnchor.AnchorType value; + + AnchorType(ClientAnchor.AnchorType value) { + this.value = value; + } + + /** + * return the AnchorType corresponding to the code + * + * @param value the anchor type code + * @return the anchor type enum + */ + @Internal + public static ClientAnchorData.AnchorType byId(int value) { + return values()[value]; + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CommentData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CommentData.java new file mode 100644 index 000000000..6373b0a68 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CommentData.java @@ -0,0 +1,24 @@ +package com.alibaba.excel.metadata.data; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * comment + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CommentData extends ClientAnchorData { + /** + * Name of the original comment author + */ + private String author; + /** + * rich text string + */ + private RichTextStringData richTextStringData; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CoordinateData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CoordinateData.java new file mode 100644 index 000000000..0755662b8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/CoordinateData.java @@ -0,0 +1,49 @@ +package com.alibaba.excel.metadata.data; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * coordinate. + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CoordinateData { + /** + * first row index.Priority is higher than {@link #relativeFirstRowIndex}. + */ + private Integer firstRowIndex; + /** + * first column index.Priority is higher than {@link #relativeFirstColumnIndex}. + */ + private Integer firstColumnIndex; + /** + * last row index.Priority is higher than {@link #relativeLastRowIndex}. + */ + private Integer lastRowIndex; + /** + * last column index.Priority is higher than {@link #relativeLastColumnIndex}. + */ + private Integer lastColumnIndex; + + /** + * relative first row index + */ + private Integer relativeFirstRowIndex; + /** + * relative first column index + */ + private Integer relativeFirstColumnIndex; + /** + * relative last row index + */ + private Integer relativeLastRowIndex; + /** + *relative last column index + */ + private Integer relativeLastColumnIndex; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/DataFormatData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/DataFormatData.java new file mode 100644 index 000000000..4f4476326 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/DataFormatData.java @@ -0,0 +1,53 @@ +package com.alibaba.excel.metadata.data; + +import com.alibaba.excel.util.StringUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * data format + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class DataFormatData { + /** + * index + */ + private Short index; + + /** + * format + */ + private String format; + + /** + * The source is not empty merge the data to the target. + * + * @param source source + * @param target target + */ + public static void merge(DataFormatData source, DataFormatData target) { + if (source == null || target == null) { + return; + } + if (source.getIndex() != null) { + target.setIndex(source.getIndex()); + } + if (StringUtils.isNotBlank(source.getFormat())) { + target.setFormat(source.getFormat()); + } + } + + @Override + public DataFormatData clone() { + DataFormatData dataFormatData = new DataFormatData(); + dataFormatData.setIndex(getIndex()); + dataFormatData.setFormat(getFormat()); + return dataFormatData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/FormulaData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/FormulaData.java new file mode 100644 index 000000000..f71ff2eb5 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/FormulaData.java @@ -0,0 +1,27 @@ +package com.alibaba.excel.metadata.data; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * formula + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FormulaData { + /** + * formula + */ + private String formulaValue; + + @Override + public FormulaData clone() { + FormulaData formulaData = new FormulaData(); + formulaData.setFormulaValue(getFormulaValue()); + return formulaData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/HyperlinkData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/HyperlinkData.java new file mode 100644 index 000000000..03edad135 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/HyperlinkData.java @@ -0,0 +1,58 @@ +package com.alibaba.excel.metadata.data; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * hyperlink + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class HyperlinkData extends CoordinateData { + /** + * Depending on the hyperlink type it can be URL, e-mail, path to a file, etc + */ + private String address; + /** + * hyperlink type + */ + private HyperlinkType hyperlinkType; + + @Getter + public enum HyperlinkType { + /** + * Not a hyperlink + */ + NONE(org.apache.poi.common.usermodel.HyperlinkType.NONE), + + /** + * Link to an existing file or web page + */ + URL(org.apache.poi.common.usermodel.HyperlinkType.URL), + + /** + * Link to a place in this document + */ + DOCUMENT(org.apache.poi.common.usermodel.HyperlinkType.DOCUMENT), + + /** + * Link to an E-mail address + */ + EMAIL(org.apache.poi.common.usermodel.HyperlinkType.EMAIL), + + /** + * Link to a file + */ + FILE(org.apache.poi.common.usermodel.HyperlinkType.FILE); + + org.apache.poi.common.usermodel.HyperlinkType value; + + HyperlinkType(org.apache.poi.common.usermodel.HyperlinkType value) { + this.value = value; + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ImageData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ImageData.java new file mode 100644 index 000000000..2ca1cdc86 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ImageData.java @@ -0,0 +1,63 @@ +package com.alibaba.excel.metadata.data; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * image + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ImageData extends ClientAnchorData { + + /** + * image + */ + private byte[] image; + + /** + * image type + */ + private ImageType imageType; + + @Getter + public enum ImageType { + /** + * Extended windows meta file + */ + PICTURE_TYPE_EMF(2), + /** + * Windows Meta File + */ + PICTURE_TYPE_WMF(3), + /** + * Mac PICT format + */ + PICTURE_TYPE_PICT(4), + /** + * JPEG format + */ + PICTURE_TYPE_JPEG(5), + /** + * PNG format + */ + PICTURE_TYPE_PNG(6), + /** + * Device independent bitmap + */ + PICTURE_TYPE_DIB(7), + + ; + + int value; + + ImageType(int value) { + this.value = value; + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java new file mode 100644 index 000000000..1d3a2a2ff --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/ReadCellData.java @@ -0,0 +1,162 @@ +package com.alibaba.excel.metadata.data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +import com.alibaba.excel.constant.EasyExcelConstants; +import com.alibaba.excel.enums.CellDataTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * read cell data + *

+ * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class ReadCellData extends CellData { + + /** + * originalNumberValue vs numberValue + *

    + *
  1. + * NUMBER: + * originalNumberValue: Original data and the accuracy of his is 17, but in fact the excel only 15 precision to + * process the data + * numberValue: After correction of the data and the accuracy of his is 15 + * for example, originalNumberValue = `2087.0249999999996` , numberValue = `2087.03` + *
  2. + *
  3. + * DATE: + * originalNumberValue: Storage is a data type double, accurate to milliseconds + * dateValue: Based on double converted to a date format, he will revised date difference, accurate to seconds + * for example, originalNumberValue = `44729.99998836806` ,time is:`2022-06-17 23:59:58.995`, + * But in excel is displayed:` 2022-06-17 23:59:59`, dateValue = `2022-06-17 23:59:59` + *
  4. + *
+ * {@link CellDataTypeEnum#NUMBER} {@link CellDataTypeEnum#DATE} + */ + private BigDecimal originalNumberValue; + + /** + * data format. + */ + private DataFormatData dataFormatData; + + public ReadCellData(CellDataTypeEnum type) { + super(); + if (type == null) { + throw new IllegalArgumentException("Type can not be null"); + } + setType(type); + } + + public ReadCellData(T data) { + super(); + setData(data); + } + + public ReadCellData(String stringValue) { + this(CellDataTypeEnum.STRING, stringValue); + } + + public ReadCellData(CellDataTypeEnum type, String stringValue) { + super(); + if (type != CellDataTypeEnum.STRING && type != CellDataTypeEnum.ERROR) { + throw new IllegalArgumentException("Only support CellDataTypeEnum.STRING and CellDataTypeEnum.ERROR"); + } + if (stringValue == null) { + throw new IllegalArgumentException("StringValue can not be null"); + } + setType(type); + setStringValue(stringValue); + } + + public ReadCellData(BigDecimal numberValue) { + super(); + if (numberValue == null) { + throw new IllegalArgumentException("DoubleValue can not be null"); + } + setType(CellDataTypeEnum.NUMBER); + setNumberValue(numberValue); + } + + public ReadCellData(Boolean booleanValue) { + super(); + if (booleanValue == null) { + throw new IllegalArgumentException("BooleanValue can not be null"); + } + setType(CellDataTypeEnum.BOOLEAN); + setBooleanValue(booleanValue); + } + + public static ReadCellData newEmptyInstance() { + return newEmptyInstance(null, null); + } + + public static ReadCellData newEmptyInstance(Integer rowIndex, Integer columnIndex) { + ReadCellData cellData = new ReadCellData<>(CellDataTypeEnum.EMPTY); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static ReadCellData newInstance(Boolean booleanValue) { + return newInstance(booleanValue, null, null); + } + + public static ReadCellData newInstance(Boolean booleanValue, Integer rowIndex, Integer columnIndex) { + ReadCellData cellData = new ReadCellData<>(booleanValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static ReadCellData newInstance(String stringValue, Integer rowIndex, Integer columnIndex) { + ReadCellData cellData = new ReadCellData<>(stringValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static ReadCellData newInstance(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) { + ReadCellData cellData = new ReadCellData<>(numberValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + return cellData; + } + + public static ReadCellData newInstanceOriginal(BigDecimal numberValue, Integer rowIndex, Integer columnIndex) { + ReadCellData cellData = new ReadCellData<>(numberValue); + cellData.setRowIndex(rowIndex); + cellData.setColumnIndex(columnIndex); + cellData.setOriginalNumberValue(numberValue); + cellData.setNumberValue(numberValue.round(EasyExcelConstants.EXCEL_MATH_CONTEXT)); + return cellData; + } + + @Override + public ReadCellData clone() { + ReadCellData readCellData = new ReadCellData<>(); + readCellData.setType(getType()); + readCellData.setNumberValue(getNumberValue()); + readCellData.setOriginalNumberValue(getOriginalNumberValue()); + readCellData.setStringValue(getStringValue()); + readCellData.setBooleanValue(getBooleanValue()); + readCellData.setData(getData()); + if (getDataFormatData() != null) { + readCellData.setDataFormatData(getDataFormatData().clone()); + } + if (getFormulaData() != null) { + readCellData.setFormulaData(getFormulaData().clone()); + } + return readCellData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/RichTextStringData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/RichTextStringData.java new file mode 100644 index 000000000..d277614f2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/RichTextStringData.java @@ -0,0 +1,65 @@ +package com.alibaba.excel.metadata.data; + +import java.util.List; + +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.metadata.style.WriteFont; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * rich text string + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class RichTextStringData { + private String textString; + private WriteFont writeFont; + private List intervalFontList; + + public RichTextStringData(String textString) { + this.textString = textString; + } + + @Getter + @Setter + @EqualsAndHashCode + @AllArgsConstructor + public static class IntervalFont { + private Integer startIndex; + private Integer endIndex; + private WriteFont writeFont; + } + + /** + * Applies a font to the specified characters of a string. + * + * @param startIndex The start index to apply the font to (inclusive) + * @param endIndex The end index to apply to font to (exclusive) + * @param writeFont The font to use. + */ + public void applyFont(int startIndex, int endIndex, WriteFont writeFont) { + if (intervalFontList == null) { + intervalFontList = ListUtils.newArrayList(); + } + intervalFontList.add(new IntervalFont(startIndex, endIndex, writeFont)); + } + + /** + * Sets the font of the entire string. + * + * @param writeFont The font to use. + */ + public void applyFont(WriteFont writeFont) { + this.writeFont = writeFont; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/WriteCellData.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/WriteCellData.java new file mode 100644 index 000000000..d4345fa98 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/data/WriteCellData.java @@ -0,0 +1,143 @@ +package com.alibaba.excel.metadata.data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.List; + +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.poi.ss.usermodel.CellStyle; + +/** + * write cell data + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class WriteCellData extends CellData { + /** + * Support only when writing.{@link CellDataTypeEnum#DATE} + */ + private LocalDateTime dateValue; + + /** + * rich text.{@link CellDataTypeEnum#RICH_TEXT_STRING} + */ + private RichTextStringData richTextStringDataValue; + /** + * image + */ + private List imageDataList; + /** + * comment + */ + private CommentData commentData; + /** + * hyper link + */ + private HyperlinkData hyperlinkData; + + /** + * style + */ + private WriteCellStyle writeCellStyle; + + /** + * If originCellStyle is empty, one will be created. + * If both writeCellStyle and originCellStyle exist, copy from writeCellStyle to originCellStyle. + */ + private CellStyle originCellStyle; + + + public WriteCellData(String stringValue) { + this(CellDataTypeEnum.STRING, stringValue); + } + + public WriteCellData(CellDataTypeEnum type) { + super(); + setType(type); + } + + public WriteCellData(CellDataTypeEnum type, String stringValue) { + super(); + if (type != CellDataTypeEnum.STRING && type != CellDataTypeEnum.ERROR) { + throw new IllegalArgumentException("Only support CellDataTypeEnum.STRING and CellDataTypeEnum.ERROR"); + } + if (stringValue == null) { + throw new IllegalArgumentException("StringValue can not be null"); + } + setType(type); + setStringValue(stringValue); + } + + public WriteCellData(BigDecimal numberValue) { + super(); + if (numberValue == null) { + throw new IllegalArgumentException("DoubleValue can not be null"); + } + setType(CellDataTypeEnum.NUMBER); + setNumberValue(numberValue); + } + + public WriteCellData(Boolean booleanValue) { + super(); + if (booleanValue == null) { + throw new IllegalArgumentException("BooleanValue can not be null"); + } + setType(CellDataTypeEnum.BOOLEAN); + setBooleanValue(booleanValue); + } + + public WriteCellData(Date dateValue) { + super(); + if (dateValue == null) { + throw new IllegalArgumentException("DateValue can not be null"); + } + setType(CellDataTypeEnum.DATE); + this.dateValue = LocalDateTime.ofInstant(dateValue.toInstant(), ZoneId.systemDefault()); + } + + public WriteCellData(LocalDateTime dateValue) { + super(); + if (dateValue == null) { + throw new IllegalArgumentException("DateValue can not be null"); + } + setType(CellDataTypeEnum.DATE); + this.dateValue = dateValue; + } + + public WriteCellData(byte[] image) { + super(); + if (image == null) { + throw new IllegalArgumentException("Image can not be null"); + } + setType(CellDataTypeEnum.EMPTY); + this.imageDataList = ListUtils.newArrayList(); + ImageData imageData = new ImageData(); + imageData.setImage(image); + imageDataList.add(imageData); + } + + /** + * Return a style, if is empty, create a new + * + * @return not null. + */ + public WriteCellStyle getOrCreateStyle() { + if (this.writeCellStyle == null) { + this.writeCellStyle = new WriteCellStyle(); + } + return this.writeCellStyle; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java new file mode 100644 index 000000000..1bd591e31 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/DataFormatter.java @@ -0,0 +1,874 @@ +/* + * ==================================================================== Licensed to the Apache Software Foundation (ASF) + * under one or more contributor license agreements. See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * 2012 - Alfresco Software, Ltd. Alfresco Software has modified source of this file The details of changes as svn diff + * can be found in svn at location root/projects/3rd-party/src + * ==================================================================== + */ +package com.alibaba.excel.metadata.format; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DateFormatSymbols; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.ss.format.CellFormat; +import org.apache.poi.ss.format.CellFormatResult; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter; +import org.apache.poi.ss.usermodel.FractionFormat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Written with reference to {@link org.apache.poi.ss.usermodel.DataFormatter}.Made some optimizations for date + * conversion. + *

+ * This is a non-thread-safe class. + * + * @author Jiaju Zhuang + */ +public class DataFormatter { + /** + * For logging any problems we find + */ + private static final Logger LOGGER = LoggerFactory.getLogger(DataFormatter.class); + private static final String defaultFractionWholePartFormat = "#"; + private static final String defaultFractionFractionPartFormat = "#/##"; + /** + * Pattern to find a number format: "0" or "#" + */ + private static final Pattern numPattern = Pattern.compile("[0#]+"); + + /** + * Pattern to find days of week as text "ddd...." + */ + private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE); + + /** + * Pattern to find "AM/PM" marker + */ + private static final Pattern amPmPattern = + Pattern.compile("(([AP])[M/P]*)|(([上下])[午/下]*)", Pattern.CASE_INSENSITIVE); + + /** + * Pattern to find formats with condition ranges e.g. [>=100] + */ + private static final Pattern rangeConditionalPattern = + Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*"); + + /** + * A regex to find locale patterns like [$$-1009] and [$?-452]. Note that we don't currently process these into + * locales + */ + private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+])"); + + /** + * A regex to match the colour formattings rules. Allowed colours are: Black, Blue, Cyan, Green, Magenta, Red, + * White, Yellow, "Color n" (1<=n<=56) + */ + private static final Pattern colorPattern = Pattern.compile( + "(\\[BLACK])|(\\[BLUE])|(\\[CYAN])|(\\[GREEN])|" + "(\\[MAGENTA])|(\\[RED])|(\\[WHITE])|(\\[YELLOW])|" + + "(\\[COLOR\\s*\\d])|(\\[COLOR\\s*[0-5]\\d])|(\\[DBNum(1|2|3)])|(\\[\\$-\\d{0,3}])", + Pattern.CASE_INSENSITIVE); + + /** + * A regex to identify a fraction pattern. This requires that replaceAll("\\?", "#") has already been called + */ + private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*/\\s*([#\\d]+)"); + + /** + * A regex to strip junk out of fraction formats + */ + private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ ?#\\d/]+)"); + + /** + * A regex to detect if an alternate grouping character is used in a numeric format + */ + private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})"); + + private static final Pattern E_NOTATION_PATTERN = Pattern.compile("E(\\d)"); + + /** + * Cells formatted with a date or time format and which contain invalid date or time values show 255 pound signs + * ("#"). + */ + private static final String invalidDateTimeString; + + static { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 255; i++) {buf.append('#');} + invalidDateTimeString = buf.toString(); + } + + /** + * The decimal symbols of the locale used for formatting values. + */ + private DecimalFormatSymbols decimalSymbols; + + /** + * The date symbols of the locale used for formatting values. + */ + private DateFormatSymbols dateSymbols; + /** + * A default format to use when a number pattern cannot be parsed. + */ + private Format defaultNumFormat; + /** + * A map to cache formats. Map formats + */ + private final Map formats = new HashMap(); + + /** + * stores the locale valid it the last formatting call + */ + private Locale locale; + /** + * true if date uses 1904 windowing, or false if using 1900 date windowing. + * + * default is false + * + * @return + */ + private Boolean use1904windowing; + /** + * Whether to use scientific Format. + * + * default is false + */ + private Boolean useScientificFormat; + + /** + * Creates a formatter using the given locale. + */ + public DataFormatter(Boolean use1904windowing, Locale locale, Boolean useScientificFormat) { + if (use1904windowing == null) { + this.use1904windowing = Boolean.FALSE; + } else { + this.use1904windowing = use1904windowing; + } + + if (locale == null) { + this.locale = Locale.getDefault(); + } else { + this.locale = locale; + } + + if (use1904windowing == null) { + this.useScientificFormat = Boolean.FALSE; + } else { + this.useScientificFormat = useScientificFormat; + } + + this.dateSymbols = DateFormatSymbols.getInstance(this.locale); + this.decimalSymbols = DecimalFormatSymbols.getInstance(this.locale); + } + + private Format getFormat(Double data, Short dataFormat, String dataFormatString) { + + // Might be better to separate out the n p and z formats, falling back to p when n and z are not set. + // That however would require other code to be re factored. + // String[] formatBits = formatStrIn.split(";"); + // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2; + // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; + String formatStr = dataFormatString; + + // Excel supports 2+ part conditional data formats, eg positive/negative/zero, + // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds + // of different formats for different ranges, just +ve/-ve, we need to + // handle these ourselves in a special way. + // For now, if we detect 2+ parts, we call out to CellFormat to handle it + // TODO Going forward, we should really merge the logic between the two classes + if (formatStr.contains(";") && + (formatStr.indexOf(';') != formatStr.lastIndexOf(';') + || rangeConditionalPattern.matcher(formatStr).matches() + )) { + try { + // Ask CellFormat to get a formatter for it + CellFormat cfmt = CellFormat.getInstance(locale, formatStr); + // CellFormat requires callers to identify date vs not, so do so + Object cellValueO = data; + if (DateUtils.isADateFormat(dataFormat, formatStr) && + // don't try to handle Date value 0, let a 3 or 4-part format take care of it + data.doubleValue() != 0.0) { + cellValueO = DateUtils.getJavaDate(data, use1904windowing); + } + // Wrap and return (non-cachable - CellFormat does that) + return new CellFormatResultWrapper(cfmt.apply(cellValueO)); + } catch (Exception e) { + LOGGER.warn("Formatting failed for format {}, falling back", formatStr, e); + } + } + + // See if we already have it cached + Format format = formats.get(formatStr); + if (format != null) { + return format; + } + + // Is it one of the special built in types, General or @? + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + format = getDefaultFormat(); + addFormat(formatStr, format); + return format; + } + + // Build a formatter, and cache it + format = createFormat(dataFormat, formatStr); + addFormat(formatStr, format); + return format; + } + + + + private Format createFormat(Short dataFormat, String dataFormatString) { + String formatStr = dataFormatString; + + Format format = checkSpecialConverter(formatStr); + if (format != null) { + return format; + } + + // Remove colour formatting if present + Matcher colourM = colorPattern.matcher(formatStr); + while (colourM.find()) { + String colour = colourM.group(); + + // Paranoid replacement... + int at = formatStr.indexOf(colour); + if (at == -1) {break;} + String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length()); + if (nFormatStr.equals(formatStr)) {break;} + + // Try again in case there's multiple + formatStr = nFormatStr; + colourM = colorPattern.matcher(formatStr); + } + + // Strip off the locale information, we use an instance-wide locale for everything + Matcher m = localePatternGroup.matcher(formatStr); + while (m.find()) { + String match = m.group(); + String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-')); + if (symbol.indexOf('$') > -1) { + symbol = symbol.substring(0, symbol.indexOf('$')) + '\\' + symbol.substring(symbol.indexOf('$')); + } + formatStr = m.replaceAll(symbol); + m = localePatternGroup.matcher(formatStr); + } + + // Check for special cases + if (formatStr == null || formatStr.trim().length() == 0) { + return getDefaultFormat(); + } + + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + return getDefaultFormat(); + } + + if (DateUtils.isADateFormat(dataFormat, formatStr)) { + return createDateFormat(formatStr); + } + // Excel supports fractions in format strings, which Java doesn't + if (formatStr.contains("#/") || formatStr.contains("?/")) { + String[] chunks = formatStr.split(";"); + for (String chunk1 : chunks) { + String chunk = chunk1.replaceAll("\\?", "#"); + Matcher matcher = fractionStripper.matcher(chunk); + chunk = matcher.replaceAll(" "); + chunk = chunk.replaceAll(" +", " "); + Matcher fractionMatcher = fractionPattern.matcher(chunk); + // take the first match + if (fractionMatcher.find()) { + String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat; + return new FractionFormat(wholePart, fractionMatcher.group(3)); + } + } + + // Strip custom text in quotes and escaped characters for now as it can cause performance problems in + // fractions. + // String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", + // "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#"); + return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat); + } + + if (numPattern.matcher(formatStr).find()) { + return createNumberFormat(formatStr); + } + return getDefaultFormat(); + } + + private Format checkSpecialConverter(String dataFormatString) { + if ("00000\\-0000".equals(dataFormatString) || "00000-0000".equals(dataFormatString)) { + return new ZipPlusFourFormat(); + } + if ("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString) + || "[<=9999999]###-####;(###) ###-####".equals(dataFormatString) + || "###\\-####;\\(###\\)\\ ###\\-####".equals(dataFormatString) + || "###-####;(###) ###-####".equals(dataFormatString)) { + return new PhoneFormat(); + } + if ("000\\-00\\-0000".equals(dataFormatString) || "000-00-0000".equals(dataFormatString)) { + return new SSNFormat(); + } + return null; + } + + private Format createDateFormat(String pFormatStr) { + String formatStr = pFormatStr; + formatStr = formatStr.replaceAll("\\\\-", "-"); + formatStr = formatStr.replaceAll("\\\\,", ","); + formatStr = formatStr.replaceAll("\\\\\\.", "."); // . is a special regexp char + formatStr = formatStr.replaceAll("\\\\ ", " "); + formatStr = formatStr.replaceAll("\\\\/", "/"); // weird: m\\/d\\/yyyy + formatStr = formatStr.replaceAll(";@", ""); + formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy + formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting + formatStr = formatStr.replaceAll("\\\\T", "'T'"); // Quote the T is iso8601 style dates + formatStr = formatStr.replace("\"", ""); + + boolean hasAmPm = false; + Matcher amPmMatcher = amPmPattern.matcher(formatStr); + while (amPmMatcher.find()) { + formatStr = amPmMatcher.replaceAll("@"); + hasAmPm = true; + amPmMatcher = amPmPattern.matcher(formatStr); + } + formatStr = formatStr.replaceAll("@", "a"); + + Matcher dateMatcher = daysAsText.matcher(formatStr); + if (dateMatcher.find()) { + String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replaceAll("D", "E"); + formatStr = dateMatcher.replaceAll(match); + } + + // Convert excel date format to SimpleDateFormat. + // Excel uses lower and upper case 'm' for both minutes and months. + // From Excel help: + /* + The "m" or "mm" code must appear immediately after the "h" or"hh" + code or immediately before the "ss" code; otherwise, Microsoft + Excel displays the month instead of minutes." + */ + StringBuilder sb = new StringBuilder(); + char[] chars = formatStr.toCharArray(); + boolean mIsMonth = true; + List ms = new ArrayList(); + boolean isElapsed = false; + for (int j = 0; j < chars.length; j++) { + char c = chars[j]; + if (c == '\'') { + sb.append(c); + j++; + + // skip until the next quote + while (j < chars.length) { + c = chars[j]; + sb.append(c); + if (c == '\'') { + break; + } + j++; + } + } else if (c == '[' && !isElapsed) { + isElapsed = true; + mIsMonth = false; + sb.append(c); + } else if (c == ']' && isElapsed) { + isElapsed = false; + sb.append(c); + } else if (isElapsed) { + if (c == 'h' || c == 'H') { + sb.append('H'); + } else if (c == 'm' || c == 'M') { + sb.append('m'); + } else if (c == 's' || c == 'S') { + sb.append('s'); + } else { + sb.append(c); + } + } else if (c == 'h' || c == 'H') { + mIsMonth = false; + if (hasAmPm) { + sb.append('h'); + } else { + sb.append('H'); + } + } else if (c == 'm' || c == 'M') { + if (mIsMonth) { + sb.append('M'); + ms.add(Integer.valueOf(sb.length() - 1)); + } else { + sb.append('m'); + } + } else if (c == 's' || c == 'S') { + sb.append('s'); + // if 'M' precedes 's' it should be minutes ('m') + for (int index : ms) { + if (sb.charAt(index) == 'M') { + sb.replace(index, index + 1, "m"); + } + } + mIsMonth = true; + ms.clear(); + } else if (Character.isLetter(c)) { + mIsMonth = true; + ms.clear(); + if (c == 'y' || c == 'Y') { + sb.append('y'); + } else if (c == 'd' || c == 'D') { + sb.append('d'); + } else { + sb.append(c); + } + } else { + if (Character.isWhitespace(c)) { + ms.clear(); + } + sb.append(c); + } + } + formatStr = sb.toString(); + + try { + return new ExcelStyleDateFormatter(formatStr, dateSymbols); + } catch (IllegalArgumentException iae) { + LOGGER.debug("Formatting failed for format {}, falling back", formatStr, iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(); + } + + } + + private String cleanFormatForNumber(String formatStr) { + StringBuilder sb = new StringBuilder(formatStr); + // If they requested spacers, with "_", + // remove those as we don't do spacing + // If they requested full-column-width + // padding, with "*", remove those too + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + if (c == '_' || c == '*') { + if (i > 0 && sb.charAt((i - 1)) == '\\') { + // It's escaped, don't worry + continue; + } + if (i < sb.length() - 1) { + // Remove the character we're supposed + // to match the space of / pad to the + // column width with + sb.deleteCharAt(i + 1); + } + // Remove the _ too + sb.deleteCharAt(i); + i--; + } + } + + // Now, handle the other aspects like + // quoting and scientific notation + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + // remove quotes and back slashes + if (c == '\\' || c == '"') { + sb.deleteCharAt(i); + i--; + + // for scientific/engineering notation + } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') { + sb.deleteCharAt(i); + i--; + } + } + + return sb.toString(); + } + + private static class InternalDecimalFormatWithScale extends Format { + + private static final Pattern endsWithCommas = Pattern.compile("(,+)$"); + private BigDecimal divider; + private static final BigDecimal ONE_THOUSAND = new BigDecimal(1000); + private final DecimalFormat df; + + private static String trimTrailingCommas(String s) { + return s.replaceAll(",+$", ""); + } + + public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) { + df = new DecimalFormat(trimTrailingCommas(pattern), symbols); + setExcelStyleRoundingMode(df); + Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern); + if (endsWithCommasMatcher.find()) { + String commas = (endsWithCommasMatcher.group(1)); + BigDecimal temp = BigDecimal.ONE; + for (int i = 0; i < commas.length(); ++i) { + temp = temp.multiply(ONE_THOUSAND); + } + divider = temp; + } else { + divider = null; + } + } + + private Object scaleInput(Object obj) { + if (divider != null) { + if (obj instanceof BigDecimal) { + obj = ((BigDecimal)obj).divide(divider, RoundingMode.HALF_UP); + } else if (obj instanceof Double) { + obj = (Double)obj / divider.doubleValue(); + } else { + throw new UnsupportedOperationException(); + } + } + return obj; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + obj = scaleInput(obj); + return df.format(obj, toAppendTo, pos); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException(); + } + } + + private Format createNumberFormat(String formatStr) { + String format = cleanFormatForNumber(formatStr); + DecimalFormatSymbols symbols = decimalSymbols; + + // Do we need to change the grouping character? + // eg for a format like #'##0 which wants 12'345 not 12,345 + Matcher agm = alternateGrouping.matcher(format); + if (agm.find()) { + char grouping = agm.group(2).charAt(0); + // Only replace the grouping character if it is not the default + // grouping character for the US locale (',') in order to enable + // correct grouping for non-US locales. + if (grouping != ',') { + symbols = DecimalFormatSymbols.getInstance(locale); + + symbols.setGroupingSeparator(grouping); + String oldPart = agm.group(1); + String newPart = oldPart.replace(grouping, ','); + format = format.replace(oldPart, newPart); + } + } + + try { + return new InternalDecimalFormatWithScale(format, symbols); + } catch (IllegalArgumentException iae) { + LOGGER.error("Formatting failed for format {}, falling back", formatStr, iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(); + } + } + + private Format getDefaultFormat() { + // for numeric cells try user supplied default + if (defaultNumFormat != null) { + return defaultNumFormat; + // otherwise use general format + } + defaultNumFormat = new ExcelGeneralNumberFormat(locale, useScientificFormat); + return defaultNumFormat; + } + + /** + * Performs Excel-style date formatting, using the supplied Date and format + */ + private String performDateFormatting(Date d, Format dateFormat) { + Format df = dateFormat != null ? dateFormat : getDefaultFormat(); + return df.format(d); + } + + /** + * Returns the formatted value of an Excel date as a String based on the cell's DataFormat. + * i.e. "Thursday, January 02, 2003" , "01/02/2003" , "02-Jan" , etc. + *

+ * If any conditional format rules apply, the highest priority with a number format is used. If no rules contain a + * number format, or no rules apply, the cell's style format is used. If the style does not have a format, the + * default date format is applied. + * + * @param data to format + * @param dataFormat + * @param dataFormatString + * @return Formatted value + */ + private String getFormattedDateString(Double data, Short dataFormat, String dataFormatString) { + Format dateFormat = getFormat(data, dataFormat, dataFormatString); + if (dateFormat instanceof ExcelStyleDateFormatter) { + // Hint about the raw excel value + ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(data); + } + return performDateFormatting(DateUtils.getJavaDate(data, use1904windowing), dateFormat); + } + + /** + * Returns the formatted value of an Excel number as a String based on the cell's DataFormat. + * Supported formats include currency, percents, decimals, phone number, SSN, etc.: "61.54%", "$100.00", "(800) + * 555-1234". + *

+ * Format comes from either the highest priority conditional format rule with a specified format, or from the cell + * style. + * + * @param data to format + * @param dataFormat + * @param dataFormatString + * @return a formatted number string + */ + private String getFormattedNumberString(BigDecimal data, Short dataFormat, String dataFormatString) { + Format numberFormat = getFormat(data.doubleValue(), dataFormat, dataFormatString); + return E_NOTATION_PATTERN.matcher(numberFormat.format(data)).replaceFirst("E+$1"); + } + + /** + * Format data. + * + * @param data + * @param dataFormat + * @param dataFormatString + * @return + */ + public String format(BigDecimal data, Short dataFormat, String dataFormatString) { + if (DateUtils.isADateFormat(dataFormat, dataFormatString)) { + return getFormattedDateString(data.doubleValue(), dataFormat, dataFormatString); + } + return getFormattedNumberString(data, dataFormat, dataFormatString); + } + + /** + *

+ * Sets a default number format to be used when the Excel format cannot be parsed successfully. Note: This is + * a fall back for when an error occurs while parsing an Excel number format pattern. This will not affect cells + * with the General format. + *

+ *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param format A Format instance to be used as a default + * @see Format#format + */ + public void setDefaultNumberFormat(Format format) { + for (Map.Entry entry : formats.entrySet()) { + if (entry.getValue() == defaultNumFormat) { + entry.setValue(format); + } + } + defaultNumFormat = format; + } + + /** + * Adds a new format to the available formats. + *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param excelFormatStr The data format string + * @param format A Format instance + */ + public void addFormat(String excelFormatStr, Format format) { + formats.put(excelFormatStr, format); + } + + // Some custom formats + + /** + * @return a DecimalFormat with parseIntegerOnly set true + */ + private static DecimalFormat createIntegerOnlyFormat(String fmt) { + DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT); + DecimalFormat result = new DecimalFormat(fmt, dsf); + result.setParseIntegerOnly(true); + return result; + } + + /** + * Enables excel style rounding mode (round half up) on the Decimal Format given. + */ + public static void setExcelStyleRoundingMode(DecimalFormat format) { + setExcelStyleRoundingMode(format, RoundingMode.HALF_UP); + } + + /** + * Enables custom rounding mode on the given Decimal Format. + * + * @param format DecimalFormat + * @param roundingMode RoundingMode + */ + public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) { + format.setRoundingMode(roundingMode); + } + + /** + * Format class for Excel's SSN format. This class mimics Excel's built-in SSN formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class SSNFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private SSNFormat() { + // enforce singleton + } + + /** + * Format a number as an SSN + */ + public static String format(Number num) { + String result = df.format(num); + return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel Zip + 4 format. This class mimics Excel's built-in formatting for Zip + 4. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class ZipPlusFourFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private ZipPlusFourFormat() { + // enforce singleton + } + + /** + * Format a number as Zip + 4 + */ + public static String format(Number num) { + String result = df.format(num); + return result.substring(0, 5) + '-' + result.substring(5, 9); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel phone number format. This class mimics Excel's built-in phone number formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class PhoneFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("##########"); + + private PhoneFormat() { + // enforce singleton + } + + /** + * Format a number as a phone number + */ + public static String format(Number num) { + String result = df.format(num); + StringBuilder sb = new StringBuilder(); + String seg1, seg2, seg3; + int len = result.length(); + if (len <= 4) { + return result; + } + + seg3 = result.substring(len - 4, len); + seg2 = result.substring(Math.max(0, len - 7), len - 4); + seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7)); + + if (seg1.trim().length() > 0) { + sb.append('(').append(seg1).append(") "); + } + if (seg2.trim().length() > 0) { + sb.append(seg2).append('-'); + } + sb.append(seg3); + return sb.toString(); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Workaround until we merge {@link org.apache.poi.ss.usermodel.DataFormatter} with {@link CellFormat}. Constant, + * non-cachable wrapper around a + * {@link CellFormatResult} + */ + private final class CellFormatResultWrapper extends Format { + private final CellFormatResult result; + + private CellFormatResultWrapper(CellFormatResult result) { + this.result = result; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(result.text.trim()); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; // Not supported + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/ExcelGeneralNumberFormat.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/ExcelGeneralNumberFormat.java new file mode 100644 index 000000000..cdeb5e853 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/format/ExcelGeneralNumberFormat.java @@ -0,0 +1,81 @@ +package com.alibaba.excel.metadata.format; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.util.Locale; + +import org.apache.poi.ss.usermodel.DataFormatter; + +/** + * Written with reference to {@link org.apache.poi.ss.usermodel.ExcelGeneralNumberFormat }. + *

+ * Supported Do not use scientific notation. + * + * @author JiaJu Zhuang + **/ +public class ExcelGeneralNumberFormat extends Format { + + private static final long serialVersionUID = 1L; + + private static final MathContext TO_10_SF = new MathContext(10, RoundingMode.HALF_UP); + + private final DecimalFormatSymbols decimalSymbols; + private final DecimalFormat integerFormat; + private final DecimalFormat decimalFormat; + private final DecimalFormat scientificFormat; + + public ExcelGeneralNumberFormat(final Locale locale, final boolean useScientificFormat) { + decimalSymbols = DecimalFormatSymbols.getInstance(locale); + // Supported Do not use scientific notation. + if (useScientificFormat) { + scientificFormat = new DecimalFormat("0.#####E0", decimalSymbols); + } else { + scientificFormat = new DecimalFormat("#", decimalSymbols); + } + org.apache.poi.ss.usermodel.DataFormatter.setExcelStyleRoundingMode(scientificFormat); + integerFormat = new DecimalFormat("#", decimalSymbols); + org.apache.poi.ss.usermodel.DataFormatter.setExcelStyleRoundingMode(integerFormat); + decimalFormat = new DecimalFormat("#.##########", decimalSymbols); + DataFormatter.setExcelStyleRoundingMode(decimalFormat); + } + + @Override + public StringBuffer format(Object number, StringBuffer toAppendTo, FieldPosition pos) { + final double value; + if (number instanceof Number) { + value = ((Number) number).doubleValue(); + if (Double.isInfinite(value) || Double.isNaN(value)) { + return integerFormat.format(number, toAppendTo, pos); + } + } else { + // testBug54786 gets here with a date, so retain previous behaviour + return integerFormat.format(number, toAppendTo, pos); + } + + final double abs = Math.abs(value); + if (abs >= 1E11 || (abs <= 1E-10 && abs > 0)) { + return scientificFormat.format(number, toAppendTo, pos); + } else if (Math.floor(value) == value || abs >= 1E10) { + // integer, or integer portion uses all 11 allowed digits + return integerFormat.format(number, toAppendTo, pos); + } + // Non-integers of non-scientific magnitude are formatted as "up to 11 + // numeric characters, with the decimal point counting as a numeric + // character". We know there is a decimal point, so limit to 10 digits. + // https://support.microsoft.com/en-us/kb/65903 + final double rounded = new BigDecimal(value).round(TO_10_SF).doubleValue(); + return decimalFormat.format(rounded, toAppendTo, pos); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException(); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ColumnWidthProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ColumnWidthProperty.java new file mode 100644 index 000000000..f9499f823 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ColumnWidthProperty.java @@ -0,0 +1,31 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.ColumnWidth; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class ColumnWidthProperty { + private Integer width; + + public ColumnWidthProperty(Integer width) { + this.width = width; + } + + public static ColumnWidthProperty build(ColumnWidth columnWidth) { + if (columnWidth == null || columnWidth.value() < 0) { + return null; + } + return new ColumnWidthProperty(columnWidth.value()); + } + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/DateTimeFormatProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/DateTimeFormatProperty.java new file mode 100644 index 000000000..10752b74f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/DateTimeFormatProperty.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.util.BooleanUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class DateTimeFormatProperty { + private String format; + private Boolean use1904windowing; + + public DateTimeFormatProperty(String format, Boolean use1904windowing) { + this.format = format; + this.use1904windowing = use1904windowing; + } + + public static DateTimeFormatProperty build(DateTimeFormat dateTimeFormat) { + if (dateTimeFormat == null) { + return null; + } + return new DateTimeFormatProperty(dateTimeFormat.value(), + BooleanUtils.isTrue(dateTimeFormat.use1904windowing().getBooleanValue())); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelContentProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelContentProperty.java new file mode 100644 index 000000000..0b3ad1a5a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelContentProperty.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.metadata.property; + +import java.lang.reflect.Field; + +import com.alibaba.excel.converters.Converter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExcelContentProperty { + public static final ExcelContentProperty EMPTY = new ExcelContentProperty(); + + /** + * Java field + */ + private Field field; + /** + * Custom defined converters + */ + private Converter converter; + /** + * date time format + */ + private DateTimeFormatProperty dateTimeFormatProperty; + /** + * number format + */ + private NumberFormatProperty numberFormatProperty; + /** + * Content style + */ + private StyleProperty contentStyleProperty; + /** + * Content font + */ + private FontProperty contentFontProperty; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java new file mode 100644 index 000000000..ce00e2eb7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/ExcelHeadProperty.java @@ -0,0 +1,147 @@ +package com.alibaba.excel.metadata.property; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.metadata.FieldCache; +import com.alibaba.excel.metadata.FieldWrapper; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.Holder; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.FieldUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Define the header attribute of excel + * + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExcelHeadProperty { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExcelHeadProperty.class); + /** + * Custom class + */ + private Class headClazz; + /** + * The types of head + */ + private HeadKindEnum headKind; + /** + * The number of rows in the line with the most rows + */ + private int headRowNumber; + /** + * Configuration header information + */ + private Map headMap; + + public ExcelHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List> head) { + this.headClazz = headClazz; + headMap = new TreeMap<>(); + headKind = HeadKindEnum.NONE; + headRowNumber = 0; + if (head != null && !head.isEmpty()) { + int headIndex = 0; + for (int i = 0; i < head.size(); i++) { + if (configurationHolder instanceof AbstractWriteHolder) { + if (((AbstractWriteHolder)configurationHolder).ignore(null, i)) { + continue; + } + } + headMap.put(headIndex, new Head(headIndex, null, null, head.get(i), Boolean.FALSE, Boolean.TRUE)); + headIndex++; + } + headKind = HeadKindEnum.STRING; + } + // convert headClazz to head + initColumnProperties(configurationHolder); + + initHeadRowNumber(); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is {}", headKind); + } + } + + private void initHeadRowNumber() { + headRowNumber = 0; + for (Head head : headMap.values()) { + List list = head.getHeadNameList(); + if (list != null && list.size() > headRowNumber) { + headRowNumber = list.size(); + } + } + for (Head head : headMap.values()) { + List list = head.getHeadNameList(); + if (list != null && !list.isEmpty() && list.size() < headRowNumber) { + int lack = headRowNumber - list.size(); + int last = list.size() - 1; + for (int i = 0; i < lack; i++) { + list.add(list.get(last)); + } + } + } + } + + private void initColumnProperties(ConfigurationHolder configurationHolder) { + if (headClazz == null) { + return; + } + FieldCache fieldCache = ClassUtils.declaredFields(headClazz, configurationHolder); + + for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) { + initOneColumnProperty(entry.getKey(), entry.getValue(), + fieldCache.getIndexFieldMap().containsKey(entry.getKey())); + } + headKind = HeadKindEnum.CLASS; + } + + /** + * Initialization column property + * + * @param index + * @param field + * @param forceIndex + * @return Ignore current field + */ + private void initOneColumnProperty(int index, FieldWrapper field, Boolean forceIndex) { + List tmpHeadList = new ArrayList<>(); + boolean notForceName = field.getHeads() == null || field.getHeads().length == 0 + || (field.getHeads().length == 1 && StringUtils.isEmpty(field.getHeads()[0])); + if (headMap.containsKey(index)) { + tmpHeadList.addAll(headMap.get(index).getHeadNameList()); + } else { + if (notForceName) { + tmpHeadList.add(field.getFieldName()); + } else { + Collections.addAll(tmpHeadList, field.getHeads()); + } + } + Head head = new Head(index, field.getField(), field.getFieldName(), tmpHeadList, forceIndex, !notForceName); + headMap.put(index, head); + } + + public boolean hasHead() { + return headKind != HeadKindEnum.NONE; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/FontProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/FontProperty.java new file mode 100644 index 000000000..c280b921f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/FontProperty.java @@ -0,0 +1,140 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.util.StringUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FontProperty { + /** + * The name for the font (i.e. Arial) + */ + private String fontName; + /** + * Height in the familiar unit of measure - points + */ + private Short fontHeightInPoints; + /** + * Whether to use italics or not + */ + private Boolean italic; + /** + * Whether to use a strikeout horizontal line through the text or not + */ + private Boolean strikeout; + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + private Short color; + /** + * Set normal, super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + private Short typeOffset; + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + private Byte underline; + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + private Integer charset; + /** + * Bold + */ + private Boolean bold; + + public static FontProperty build(HeadFontStyle headFontStyle) { + if (headFontStyle == null) { + return null; + } + FontProperty styleProperty = new FontProperty(); + if (StringUtils.isNotBlank(headFontStyle.fontName())) { + styleProperty.setFontName(headFontStyle.fontName()); + } + if (headFontStyle.fontHeightInPoints() >= 0) { + styleProperty.setFontHeightInPoints(headFontStyle.fontHeightInPoints()); + } + styleProperty.setItalic(headFontStyle.italic().getBooleanValue()); + styleProperty.setStrikeout(headFontStyle.strikeout().getBooleanValue()); + if (headFontStyle.color() >= 0) { + styleProperty.setColor(headFontStyle.color()); + } + if (headFontStyle.typeOffset() >= 0) { + styleProperty.setTypeOffset(headFontStyle.typeOffset()); + } + if (headFontStyle.underline() >= 0) { + styleProperty.setUnderline(headFontStyle.underline()); + } + if (headFontStyle.charset() >= 0) { + styleProperty.setCharset(headFontStyle.charset()); + } + styleProperty.setBold(headFontStyle.bold().getBooleanValue()); + return styleProperty; + } + + public static FontProperty build(ContentFontStyle contentFontStyle) { + if (contentFontStyle == null) { + return null; + } + FontProperty styleProperty = new FontProperty(); + if (StringUtils.isNotBlank(contentFontStyle.fontName())) { + styleProperty.setFontName(contentFontStyle.fontName()); + } + if (contentFontStyle.fontHeightInPoints() >= 0) { + styleProperty.setFontHeightInPoints(contentFontStyle.fontHeightInPoints()); + } + styleProperty.setItalic(contentFontStyle.italic().getBooleanValue()); + styleProperty.setStrikeout(contentFontStyle.strikeout().getBooleanValue()); + if (contentFontStyle.color() >= 0) { + styleProperty.setColor(contentFontStyle.color()); + } + if (contentFontStyle.typeOffset() >= 0) { + styleProperty.setTypeOffset(contentFontStyle.typeOffset()); + } + if (contentFontStyle.underline() >= 0) { + styleProperty.setUnderline(contentFontStyle.underline()); + } + if (contentFontStyle.charset() >= 0) { + styleProperty.setCharset(contentFontStyle.charset()); + } + styleProperty.setBold(contentFontStyle.bold().getBooleanValue()); + return styleProperty; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/LoopMergeProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/LoopMergeProperty.java new file mode 100644 index 000000000..05084bf7c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/LoopMergeProperty.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class LoopMergeProperty { + /** + * Each row + */ + private int eachRow; + /** + * Extend column + */ + private int columnExtend; + + public LoopMergeProperty(int eachRow, int columnExtend) { + this.eachRow = eachRow; + this.columnExtend = columnExtend; + } + + public static LoopMergeProperty build(ContentLoopMerge contentLoopMerge) { + if (contentLoopMerge == null) { + return null; + } + return new LoopMergeProperty(contentLoopMerge.eachRow(), contentLoopMerge.columnExtend()); + } + + public int getEachRow() { + return eachRow; + } + + public void setEachRow(int eachRow) { + this.eachRow = eachRow; + } + + public int getColumnExtend() { + return columnExtend; + } + + public void setColumnExtend(int columnExtend) { + this.columnExtend = columnExtend; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/NumberFormatProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/NumberFormatProperty.java new file mode 100644 index 000000000..f8f1e4aba --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/NumberFormatProperty.java @@ -0,0 +1,43 @@ +package com.alibaba.excel.metadata.property; + +import java.math.RoundingMode; + +import com.alibaba.excel.annotation.format.NumberFormat; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class NumberFormatProperty { + private String format; + private RoundingMode roundingMode; + + public NumberFormatProperty(String format, RoundingMode roundingMode) { + this.format = format; + this.roundingMode = roundingMode; + } + + public static NumberFormatProperty build(NumberFormat numberFormat) { + if (numberFormat == null) { + return null; + } + return new NumberFormatProperty(numberFormat.value(), numberFormat.roundingMode()); + } + + public String getFormat() { + return format; + } + + public void setFormat(String format) { + this.format = format; + } + + public RoundingMode getRoundingMode() { + return roundingMode; + } + + public void setRoundingMode(RoundingMode roundingMode) { + this.roundingMode = roundingMode; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/OnceAbsoluteMergeProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/OnceAbsoluteMergeProperty.java new file mode 100644 index 000000000..c5092192c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/OnceAbsoluteMergeProperty.java @@ -0,0 +1,74 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.OnceAbsoluteMerge; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class OnceAbsoluteMergeProperty { + /** + * First row + */ + private int firstRowIndex; + /** + * Last row + */ + private int lastRowIndex; + /** + * First column + */ + private int firstColumnIndex; + /** + * Last row + */ + private int lastColumnIndex; + + public OnceAbsoluteMergeProperty(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) { + this.firstRowIndex = firstRowIndex; + this.lastRowIndex = lastRowIndex; + this.firstColumnIndex = firstColumnIndex; + this.lastColumnIndex = lastColumnIndex; + } + + public static OnceAbsoluteMergeProperty build(OnceAbsoluteMerge onceAbsoluteMerge) { + if (onceAbsoluteMerge == null) { + return null; + } + return new OnceAbsoluteMergeProperty(onceAbsoluteMerge.firstRowIndex(), onceAbsoluteMerge.lastRowIndex(), + onceAbsoluteMerge.firstColumnIndex(), onceAbsoluteMerge.lastColumnIndex()); + } + + public int getFirstRowIndex() { + return firstRowIndex; + } + + public void setFirstRowIndex(int firstRowIndex) { + this.firstRowIndex = firstRowIndex; + } + + public int getLastRowIndex() { + return lastRowIndex; + } + + public void setLastRowIndex(int lastRowIndex) { + this.lastRowIndex = lastRowIndex; + } + + public int getFirstColumnIndex() { + return firstColumnIndex; + } + + public void setFirstColumnIndex(int firstColumnIndex) { + this.firstColumnIndex = firstColumnIndex; + } + + public int getLastColumnIndex() { + return lastColumnIndex; + } + + public void setLastColumnIndex(int lastColumnIndex) { + this.lastColumnIndex = lastColumnIndex; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/RowHeightProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/RowHeightProperty.java new file mode 100644 index 000000000..1c0af1231 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/RowHeightProperty.java @@ -0,0 +1,39 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +public class RowHeightProperty { + private Short height; + + public RowHeightProperty(Short height) { + this.height = height; + } + + public static RowHeightProperty build(HeadRowHeight headRowHeight) { + if (headRowHeight == null || headRowHeight.value() < 0) { + return null; + } + return new RowHeightProperty(headRowHeight.value()); + } + + public static RowHeightProperty build(ContentRowHeight contentRowHeight) { + if (contentRowHeight == null || contentRowHeight.value() < 0) { + return null; + } + return new RowHeightProperty(contentRowHeight.value()); + } + + public Short getHeight() { + return height; + } + + public void setHeight(Short height) { + this.height = height; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/StyleProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/StyleProperty.java new file mode 100644 index 000000000..bb469cd3b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/metadata/property/StyleProperty.java @@ -0,0 +1,242 @@ +package com.alibaba.excel.metadata.property; + +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.write.metadata.style.WriteFont; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * Configuration from annotations + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class StyleProperty { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + private DataFormatData dataFormatData; + /** + * Set the font for this style + */ + private WriteFont writeFont; + /** + * Set the cell's using this style to be hidden + */ + private Boolean hidden; + + /** + * Set the cell's using this style to be locked + */ + private Boolean locked; + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + private Boolean quotePrefix; + /** + * Set the type of horizontal alignment for the cell + */ + private HorizontalAlignment horizontalAlignment; + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + */ + private Boolean wrapped; + /** + * Set the type of vertical alignment for the cell + */ + private VerticalAlignment verticalAlignment; + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + private Short rotation; + /** + * Set the number of spaces to indent the text in the cell + */ + private Short indent; + /** + * Set the type of border to use for the left border of the cell + */ + private BorderStyle borderLeft; + /** + * Set the type of border to use for the right border of the cell + */ + private BorderStyle borderRight; + /** + * Set the type of border to use for the top border of the cell + */ + private BorderStyle borderTop; + + /** + * Set the type of border to use for the bottom border of the cell + */ + private BorderStyle borderBottom; + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + private Short leftBorderColor; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + */ + private Short rightBorderColor; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + */ + private Short topBorderColor; + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + */ + private Short bottomBorderColor; + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + private FillPatternType fillPatternType; + + /** + * Set the background fill color. + * + * @see IndexedColors + */ + private Short fillBackgroundColor; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + */ + private Short fillForegroundColor; + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + private Boolean shrinkToFit; + + public static StyleProperty build(HeadStyle headStyle) { + if (headStyle == null) { + return null; + } + StyleProperty styleProperty = new StyleProperty(); + if (headStyle.dataFormat() >= 0) { + DataFormatData dataFormatData = new DataFormatData(); + dataFormatData.setIndex(headStyle.dataFormat()); + styleProperty.setDataFormatData(dataFormatData); + } + styleProperty.setHidden(headStyle.hidden().getBooleanValue()); + styleProperty.setLocked(headStyle.locked().getBooleanValue()); + styleProperty.setQuotePrefix(headStyle.quotePrefix().getBooleanValue()); + styleProperty.setHorizontalAlignment(headStyle.horizontalAlignment().getPoiHorizontalAlignment()); + styleProperty.setWrapped(headStyle.wrapped().getBooleanValue()); + styleProperty.setVerticalAlignment(headStyle.verticalAlignment().getPoiVerticalAlignmentEnum()); + if (headStyle.rotation() >= 0) { + styleProperty.setRotation(headStyle.rotation()); + } + if (headStyle.indent() >= 0) { + styleProperty.setIndent(headStyle.indent()); + } + styleProperty.setBorderLeft(headStyle.borderLeft().getPoiBorderStyle()); + styleProperty.setBorderRight(headStyle.borderRight().getPoiBorderStyle()); + styleProperty.setBorderTop(headStyle.borderTop().getPoiBorderStyle()); + styleProperty.setBorderBottom(headStyle.borderBottom().getPoiBorderStyle()); + if (headStyle.leftBorderColor() >= 0) { + styleProperty.setLeftBorderColor(headStyle.leftBorderColor()); + } + if (headStyle.rightBorderColor() >= 0) { + styleProperty.setRightBorderColor(headStyle.rightBorderColor()); + } + if (headStyle.topBorderColor() >= 0) { + styleProperty.setTopBorderColor(headStyle.topBorderColor()); + } + if (headStyle.bottomBorderColor() >= 0) { + styleProperty.setBottomBorderColor(headStyle.bottomBorderColor()); + } + styleProperty.setFillPatternType(headStyle.fillPatternType().getPoiFillPatternType()); + if (headStyle.fillBackgroundColor() >= 0) { + styleProperty.setFillBackgroundColor(headStyle.fillBackgroundColor()); + } + if (headStyle.fillForegroundColor() >= 0) { + styleProperty.setFillForegroundColor(headStyle.fillForegroundColor()); + } + styleProperty.setShrinkToFit(headStyle.shrinkToFit().getBooleanValue()); + return styleProperty; + } + + public static StyleProperty build(ContentStyle contentStyle) { + if (contentStyle == null) { + return null; + } + StyleProperty styleProperty = new StyleProperty(); + if (contentStyle.dataFormat() >= 0) { + DataFormatData dataFormatData = new DataFormatData(); + dataFormatData.setIndex(contentStyle.dataFormat()); + styleProperty.setDataFormatData(dataFormatData); + } + styleProperty.setHidden(contentStyle.hidden().getBooleanValue()); + styleProperty.setLocked(contentStyle.locked().getBooleanValue()); + styleProperty.setQuotePrefix(contentStyle.quotePrefix().getBooleanValue()); + styleProperty.setHorizontalAlignment(contentStyle.horizontalAlignment().getPoiHorizontalAlignment()); + styleProperty.setWrapped(contentStyle.wrapped().getBooleanValue()); + styleProperty.setVerticalAlignment(contentStyle.verticalAlignment().getPoiVerticalAlignmentEnum()); + if (contentStyle.rotation() >= 0) { + styleProperty.setRotation(contentStyle.rotation()); + } + if (contentStyle.indent() >= 0) { + styleProperty.setIndent(contentStyle.indent()); + } + styleProperty.setBorderLeft(contentStyle.borderLeft().getPoiBorderStyle()); + styleProperty.setBorderRight(contentStyle.borderRight().getPoiBorderStyle()); + styleProperty.setBorderTop(contentStyle.borderTop().getPoiBorderStyle()); + styleProperty.setBorderBottom(contentStyle.borderBottom().getPoiBorderStyle()); + if (contentStyle.leftBorderColor() >= 0) { + styleProperty.setLeftBorderColor(contentStyle.leftBorderColor()); + } + if (contentStyle.rightBorderColor() >= 0) { + styleProperty.setRightBorderColor(contentStyle.rightBorderColor()); + } + if (contentStyle.topBorderColor() >= 0) { + styleProperty.setTopBorderColor(contentStyle.topBorderColor()); + } + if (contentStyle.bottomBorderColor() >= 0) { + styleProperty.setBottomBorderColor(contentStyle.bottomBorderColor()); + } + styleProperty.setFillPatternType(contentStyle.fillPatternType().getPoiFillPatternType()); + if (contentStyle.fillBackgroundColor() >= 0) { + styleProperty.setFillBackgroundColor(contentStyle.fillBackgroundColor()); + } + if (contentStyle.fillForegroundColor() >= 0) { + styleProperty.setFillForegroundColor(contentStyle.fillForegroundColor()); + } + styleProperty.setShrinkToFit(contentStyle.shrinkToFit().getBooleanValue()); + return styleProperty; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java new file mode 100644 index 000000000..75f980650 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/AbstractExcelReaderParameterBuilder.java @@ -0,0 +1,59 @@ +package com.alibaba.excel.read.builder; + +import com.alibaba.excel.metadata.AbstractParameterBuilder; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.ReadBasicParameter; +import com.alibaba.excel.util.ListUtils; + +/** + * Build ExcelBuilder + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelReaderParameterBuilder extends AbstractParameterBuilder { + /** + * Count the number of added heads when read sheet. + * + *

+ * 0 - This Sheet has no head ,since the first row are the data + *

+ * 1 - This Sheet has one row head , this is the default + *

+ * 2 - This Sheet has two row head ,since the third row is the data + * + * @param headRowNumber + * @return + */ + public T headRowNumber(Integer headRowNumber) { + parameter().setHeadRowNumber(headRowNumber); + return self(); + } + + /** + * Whether to use scientific Format. + * + * default is false + * + * @param useScientificFormat + * @return + */ + public T useScientificFormat(Boolean useScientificFormat) { + parameter().setUseScientificFormat(useScientificFormat); + return self(); + } + + /** + * Custom type listener run after default + * + * @param readListener + * @return + */ + public T registerReadListener(ReadListener readListener) { + if (parameter().getCustomReadListenerList() == null) { + parameter().setCustomReadListenerList(ListUtils.newArrayList()); + } + parameter().getCustomReadListenerList().add(readListener); + return self(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java new file mode 100644 index 000000000..59fd7e1f9 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderBuilder.java @@ -0,0 +1,265 @@ +package com.alibaba.excel.read.builder; + +import java.io.File; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.HashSet; +import java.util.List; + +import javax.xml.parsers.SAXParserFactory; + +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.cache.selector.ReadCacheSelector; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.event.SyncReadListener; +import com.alibaba.excel.read.listener.ModelBuildEventListener; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * Build ExcelWriter + * + * @author Jiaju Zhuang + */ +public class ExcelReaderBuilder extends AbstractExcelReaderParameterBuilder { + /** + * Workbook + */ + private final ReadWorkbook readWorkbook; + + public ExcelReaderBuilder() { + this.readWorkbook = new ReadWorkbook(); + } + + public ExcelReaderBuilder excelType(ExcelTypeEnum excelType) { + readWorkbook.setExcelType(excelType); + return this; + } + + /** + * Read InputStream + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + public ExcelReaderBuilder file(InputStream inputStream) { + readWorkbook.setInputStream(inputStream); + return this; + } + + /** + * Read file + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + public ExcelReaderBuilder file(File file) { + readWorkbook.setFile(file); + return this; + } + + /** + * Read file + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + public ExcelReaderBuilder file(String pathName) { + return file(new File(pathName)); + } + + /** + * charset. + * Only work on the CSV file + */ + public ExcelReaderBuilder charset(Charset charset) { + readWorkbook.setCharset(charset); + return this; + } + + /** + * Mandatory use 'inputStream' .Default is false. + *

+ * if false, Will transfer 'inputStream' to temporary files to improve efficiency + */ + public ExcelReaderBuilder mandatoryUseInputStream(Boolean mandatoryUseInputStream) { + readWorkbook.setMandatoryUseInputStream(mandatoryUseInputStream); + return this; + } + + /** + * Default true + * + * @param autoCloseStream + * @return + */ + public ExcelReaderBuilder autoCloseStream(Boolean autoCloseStream) { + readWorkbook.setAutoCloseStream(autoCloseStream); + return this; + } + + /** + * Ignore empty rows.Default is true. + * + * @param ignoreEmptyRow + * @return + */ + public ExcelReaderBuilder ignoreEmptyRow(Boolean ignoreEmptyRow) { + readWorkbook.setIgnoreEmptyRow(ignoreEmptyRow); + return this; + } + + /** + * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)} + * {@link AnalysisContext#getCustom()} + * + * @param customObject + * @return + */ + public ExcelReaderBuilder customObject(Object customObject) { + readWorkbook.setCustomObject(customObject); + return this; + } + + /** + * A cache that stores temp data to save memory. + * + * @param readCache + * @return + */ + public ExcelReaderBuilder readCache(ReadCache readCache) { + readWorkbook.setReadCache(readCache); + return this; + } + + /** + * Select the cache.Default use {@link com.alibaba.excel.cache.selector.SimpleReadCacheSelector} + * + * @param readCacheSelector + * @return + */ + public ExcelReaderBuilder readCacheSelector(ReadCacheSelector readCacheSelector) { + readWorkbook.setReadCacheSelector(readCacheSelector); + return this; + } + + /** + * Whether the encryption + * + * @param password + * @return + */ + public ExcelReaderBuilder password(String password) { + readWorkbook.setPassword(password); + return this; + } + + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @param xlsxSAXParserFactoryName + * @return + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + */ + public ExcelReaderBuilder xlsxSAXParserFactoryName(String xlsxSAXParserFactoryName) { + readWorkbook.setXlsxSAXParserFactoryName(xlsxSAXParserFactoryName); + return this; + } + + /** + * Read some extra information, not by default + * + * @param extraType extra information type + * @return + */ + public ExcelReaderBuilder extraRead(CellExtraTypeEnum extraType) { + if (readWorkbook.getExtraReadSet() == null) { + readWorkbook.setExtraReadSet(new HashSet()); + } + readWorkbook.getExtraReadSet().add(extraType); + return this; + } + + /** + * Whether to use the default listener, which is used by default. + *

+ * The {@link ModelBuildEventListener} is loaded by default to convert the object. + * + * @param useDefaultListener + * @return + */ + public ExcelReaderBuilder useDefaultListener(Boolean useDefaultListener) { + readWorkbook.setUseDefaultListener(useDefaultListener); + return this; + } + + /** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`. + * + * @see ReadDefaultReturnEnum + */ + public ExcelReaderBuilder readDefaultReturn(ReadDefaultReturnEnum readDefaultReturn) { + readWorkbook.setReadDefaultReturn(readDefaultReturn); + return this; + } + + public ExcelReader build() { + return new ExcelReader(readWorkbook); + } + + public void doReadAll() { + try (ExcelReader excelReader = build()) { + excelReader.readAll(); + } + } + + /** + * Synchronous reads return results + * + * @return + */ + public List doReadAllSync() { + SyncReadListener syncReadListener = new SyncReadListener(); + registerReadListener(syncReadListener); + try (ExcelReader excelReader = build()) { + excelReader.readAll(); + excelReader.finish(); + } + return (List)syncReadListener.getList(); + } + + public ExcelReaderSheetBuilder sheet() { + return sheet(null, null); + } + + public ExcelReaderSheetBuilder sheet(Integer sheetNo) { + return sheet(sheetNo, null); + } + + public ExcelReaderSheetBuilder sheet(String sheetName) { + return sheet(null, sheetName); + } + + public ExcelReaderSheetBuilder sheet(Integer sheetNo, String sheetName) { + ExcelReaderSheetBuilder excelReaderSheetBuilder = new ExcelReaderSheetBuilder(build()); + if (sheetNo != null) { + excelReaderSheetBuilder.sheetNo(sheetNo); + } + if (sheetName != null) { + excelReaderSheetBuilder.sheetName(sheetName); + } + return excelReaderSheetBuilder; + } + + @Override + protected ReadWorkbook parameter() { + return readWorkbook; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java new file mode 100644 index 000000000..4778169bf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/builder/ExcelReaderSheetBuilder.java @@ -0,0 +1,89 @@ +package com.alibaba.excel.read.builder; + +import java.util.List; + +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.event.SyncReadListener; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.read.metadata.ReadSheet; + +/** + * Build sheet + * + * @author Jiaju Zhuang + */ +public class ExcelReaderSheetBuilder extends AbstractExcelReaderParameterBuilder { + private ExcelReader excelReader; + /** + * Sheet + */ + private final ReadSheet readSheet; + + public ExcelReaderSheetBuilder() { + this.readSheet = new ReadSheet(); + } + + public ExcelReaderSheetBuilder(ExcelReader excelReader) { + this.readSheet = new ReadSheet(); + this.excelReader = excelReader; + } + + /** + * Starting from 0 + * + * @param sheetNo + * @return + */ + public ExcelReaderSheetBuilder sheetNo(Integer sheetNo) { + readSheet.setSheetNo(sheetNo); + return this; + } + + /** + * sheet name + * + * @param sheetName + * @return + */ + public ExcelReaderSheetBuilder sheetName(String sheetName) { + readSheet.setSheetName(sheetName); + return this; + } + + public ReadSheet build() { + return readSheet; + } + + /** + * Sax read + */ + public void doRead() { + if (excelReader == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.read().sheet()' to call this method"); + } + excelReader.read(build()); + excelReader.finish(); + } + + /** + * Synchronous reads return results + * + * @return + */ + public List doReadSync() { + if (excelReader == null) { + throw new ExcelAnalysisException("Must use 'EasyExcelFactory.read().sheet()' to call this method"); + } + SyncReadListener syncReadListener = new SyncReadListener(); + registerReadListener(syncReadListener); + excelReader.read(build()); + excelReader.finish(); + return (List)syncReadListener.getList(); + } + + @Override + protected ReadSheet parameter() { + return readSheet; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/IgnoreExceptionReadListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/IgnoreExceptionReadListener.java new file mode 100644 index 000000000..d539ad562 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/IgnoreExceptionReadListener.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.read.listener; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * Interface to listen for read results + * + * @author Jiaju Zhuang + */ +public interface IgnoreExceptionReadListener extends ReadListener { + + /** + * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the + * entire read will terminate. + * + * @param exception + * @param context + * @throws Exception + */ + @Override + default void onException(Exception exception, AnalysisContext context) throws Exception {} + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java new file mode 100644 index 000000000..fab5a7c6e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ModelBuildEventListener.java @@ -0,0 +1,158 @@ +package com.alibaba.excel.read.listener; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; +import com.alibaba.excel.support.cglib.beans.BeanMap; +import com.alibaba.excel.util.BeanMapUtils; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.ConverterUtils; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.MapUtils; + + +/** + * Convert to the object the user needs + * + * @author jipengfei + */ +public class ModelBuildEventListener implements IgnoreExceptionReadListener>> { + + @Override + public void invoke(Map> cellDataMap, AnalysisContext context) { + ReadSheetHolder readSheetHolder = context.readSheetHolder(); + if (HeadKindEnum.CLASS.equals(readSheetHolder.excelReadHeadProperty().getHeadKind())) { + context.readRowHolder() + .setCurrentRowAnalysisResult(buildUserModel(cellDataMap, readSheetHolder, context)); + return; + } + context.readRowHolder().setCurrentRowAnalysisResult(buildNoModel(cellDataMap, readSheetHolder, context)); + } + + private Object buildNoModel(Map> cellDataMap, ReadSheetHolder readSheetHolder, + AnalysisContext context) { + int index = 0; + Map map = MapUtils.newLinkedHashMapWithExpectedSize(cellDataMap.size()); + for (Map.Entry> entry : cellDataMap.entrySet()) { + Integer key = entry.getKey(); + ReadCellData cellData = entry.getValue(); + while (index < key) { + map.put(index, null); + index++; + } + index++; + + ReadDefaultReturnEnum readDefaultReturn = context.readWorkbookHolder().getReadDefaultReturn(); + if (readDefaultReturn == ReadDefaultReturnEnum.STRING) { + // string + map.put(key, + (String)ConverterUtils.convertToJavaObject(cellData, null, null, readSheetHolder.converterMap(), + context, context.readRowHolder().getRowIndex(), key)); + } else { + // retrun ReadCellData + ReadCellData convertedReadCellData = convertReadCellData(cellData, + context.readWorkbookHolder().getReadDefaultReturn(), readSheetHolder, context, key); + if (readDefaultReturn == ReadDefaultReturnEnum.READ_CELL_DATA) { + map.put(key, convertedReadCellData); + } else { + map.put(key, convertedReadCellData.getData()); + } + } + } + // fix https://github.com/alibaba/easyexcel/issues/2014 + int headSize = calculateHeadSize(readSheetHolder); + while (index < headSize) { + map.put(index, null); + index++; + } + return map; + } + + private ReadCellData convertReadCellData(ReadCellData cellData, ReadDefaultReturnEnum readDefaultReturn, + ReadSheetHolder readSheetHolder, AnalysisContext context, Integer columnIndex) { + Class classGeneric; + switch (cellData.getType()) { + case STRING: + case DIRECT_STRING: + case ERROR: + case EMPTY: + classGeneric = String.class; + break; + case BOOLEAN: + classGeneric = Boolean.class; + break; + case NUMBER: + DataFormatData dataFormatData = cellData.getDataFormatData(); + if (dataFormatData != null && DateUtils.isADateFormat(dataFormatData.getIndex(), + dataFormatData.getFormat())) { + classGeneric = LocalDateTime.class; + } else { + classGeneric = BigDecimal.class; + } + break; + default: + classGeneric = ConverterUtils.defaultClassGeneric; + break; + } + + return (ReadCellData)ConverterUtils.convertToJavaObject(cellData, null, ReadCellData.class, + classGeneric, null, readSheetHolder.converterMap(), context, context.readRowHolder().getRowIndex(), + columnIndex); + } + + private int calculateHeadSize(ReadSheetHolder readSheetHolder) { + if (readSheetHolder.excelReadHeadProperty().getHeadMap().size() > 0) { + return readSheetHolder.excelReadHeadProperty().getHeadMap().size(); + } + if (readSheetHolder.getMaxNotEmptyDataHeadSize() != null) { + return readSheetHolder.getMaxNotEmptyDataHeadSize(); + } + return 0; + } + + private Object buildUserModel(Map> cellDataMap, ReadSheetHolder readSheetHolder, + AnalysisContext context) { + ExcelReadHeadProperty excelReadHeadProperty = readSheetHolder.excelReadHeadProperty(); + Object resultModel; + try { + resultModel = excelReadHeadProperty.getHeadClazz().newInstance(); + } catch (Exception e) { + throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), 0, + new ReadCellData<>(CellDataTypeEnum.EMPTY), null, + "Can not instance class: " + excelReadHeadProperty.getHeadClazz().getName(), e); + } + Map headMap = excelReadHeadProperty.getHeadMap(); + BeanMap dataMap = BeanMapUtils.create(resultModel); + for (Map.Entry entry : headMap.entrySet()) { + Integer index = entry.getKey(); + Head head = entry.getValue(); + String fieldName = head.getFieldName(); + if (!cellDataMap.containsKey(index)) { + continue; + } + ReadCellData cellData = cellDataMap.get(index); + Object value = ConverterUtils.convertToJavaObject(cellData, head.getField(), + ClassUtils.declaredExcelContentProperty(dataMap, readSheetHolder.excelReadHeadProperty().getHeadClazz(), + fieldName, readSheetHolder), readSheetHolder.converterMap(), context, + context.readRowHolder().getRowIndex(), index); + if (value != null) { + dataMap.put(fieldName, value); + } + } + return resultModel; + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) {} +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/PageReadListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/PageReadListener.java new file mode 100644 index 000000000..f2ab4351e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/PageReadListener.java @@ -0,0 +1,60 @@ +package com.alibaba.excel.read.listener; + +import java.util.List; +import java.util.function.Consumer; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.util.ListUtils; + +import org.apache.commons.collections4.CollectionUtils; + +/** + * page read listener + * + * @author Jiaju Zhuang + */ +public class PageReadListener implements ReadListener { + /** + * Default single handle the amount of data + */ + public static int BATCH_COUNT = 100; + /** + * Temporary storage of data + */ + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + /** + * consumer + */ + private final Consumer> consumer; + + /** + * Single handle the amount of data + */ + private final int batchCount; + + public PageReadListener(Consumer> consumer) { + this(consumer, BATCH_COUNT); + } + + public PageReadListener(Consumer> consumer, int batchCount) { + this.consumer = consumer; + this.batchCount = batchCount; + } + + @Override + public void invoke(T data, AnalysisContext context) { + cachedDataList.add(data); + if (cachedDataList.size() >= batchCount) { + consumer.accept(cachedDataList); + cachedDataList = ListUtils.newArrayListWithExpectedSize(batchCount); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + if (CollectionUtils.isNotEmpty(cachedDataList)) { + consumer.accept(cachedDataList); + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ReadListener.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ReadListener.java new file mode 100644 index 000000000..798991536 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/listener/ReadListener.java @@ -0,0 +1,68 @@ +package com.alibaba.excel.read.listener; + +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.Listener; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.metadata.data.ReadCellData; + +/** + * Interface to listen for read results + * + * @author Jiaju Zhuang + */ +public interface ReadListener extends Listener { + /** + * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the + * entire read will terminate. + * + * @param exception + * @param context + * @throws Exception + */ + default void onException(Exception exception, AnalysisContext context) throws Exception { + throw exception; + } + + /** + * When analysis one head row trigger invoke function. + * + * @param headMap + * @param context + */ + default void invokeHead(Map> headMap, AnalysisContext context) {} + + /** + * When analysis one row trigger invoke function. + * + * @param data one row value. It is same as {@link AnalysisContext#readRowHolder()} + * @param context analysis context + */ + void invoke(T data, AnalysisContext context); + + /** + * The current method is called when extra information is returned + * + * @param extra extra information + * @param context analysis context + */ + default void extra(CellExtra extra, AnalysisContext context) {} + + /** + * if have something to do after all analysis + * + * @param context + */ + void doAfterAllAnalysed(AnalysisContext context); + + /** + * Verify that there is another piece of data.You can stop the read by returning false + * + * @param context + * @return + */ + default boolean hasNext(AnalysisContext context) { + return true; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadBasicParameter.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadBasicParameter.java new file mode 100644 index 000000000..59149fb3d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadBasicParameter.java @@ -0,0 +1,41 @@ +package com.alibaba.excel.read.metadata; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.metadata.BasicParameter; +import com.alibaba.excel.read.listener.ReadListener; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Read basic parameter + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class ReadBasicParameter extends BasicParameter { + /** + * Count the number of added heads when read sheet. + * + *

+ * 0 - This Sheet has no head ,since the first row are the data + *

+ * 1 - This Sheet has one row head , this is the default + *

+ * 2 - This Sheet has two row head ,since the third row is the data + */ + private Integer headRowNumber; + /** + * Custom type listener run after default + */ + private List> customReadListenerList; + + public ReadBasicParameter() { + customReadListenerList = new ArrayList<>(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java new file mode 100644 index 000000000..5b6f8decd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadSheet.java @@ -0,0 +1,62 @@ +package com.alibaba.excel.read.metadata; + +/** + * Read sheet + * + * @author jipengfei + */ +public class ReadSheet extends ReadBasicParameter { + /** + * Starting from 0 + */ + private Integer sheetNo; + /** + * sheet name + */ + private String sheetName; + + public ReadSheet() {} + + public ReadSheet(Integer sheetNo) { + this.sheetNo = sheetNo; + } + + public ReadSheet(Integer sheetNo, String sheetName) { + this.sheetNo = sheetNo; + this.sheetName = sheetName; + } + + public Integer getSheetNo() { + return sheetNo; + } + + public void setSheetNo(Integer sheetNo) { + this.sheetNo = sheetNo; + } + + public String getSheetName() { + return sheetName; + } + + public void setSheetName(String sheetName) { + this.sheetName = sheetName; + } + + public void copyBasicParameter(ReadSheet other) { + if (other == null) { + return; + } + this.setHeadRowNumber(other.getHeadRowNumber()); + this.setCustomReadListenerList(other.getCustomReadListenerList()); + this.setHead(other.getHead()); + this.setClazz(other.getClazz()); + this.setCustomConverterList(other.getCustomConverterList()); + this.setAutoTrim(other.getAutoTrim()); + this.setUse1904windowing(other.getUse1904windowing()); + } + + @Override + public String toString() { + return "ReadSheet{" + "sheetNo=" + sheetNo + ", sheetName='" + sheetName + '\'' + "} " + super.toString(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java new file mode 100644 index 000000000..a3713d7c7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/ReadWorkbook.java @@ -0,0 +1,117 @@ +package com.alibaba.excel.read.metadata; + +import java.io.File; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.Set; + +import javax.xml.parsers.SAXParserFactory; + +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.cache.selector.ReadCacheSelector; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.read.listener.ModelBuildEventListener; +import com.alibaba.excel.support.ExcelTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Workbook + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class ReadWorkbook extends ReadBasicParameter { + /** + * Excel type + */ + private ExcelTypeEnum excelType; + /** + * Read InputStream + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private InputStream inputStream; + /** + * Read file + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private File file; + /** + * charset. + * Only work on the CSV file + */ + private Charset charset; + /** + * Mandatory use 'inputStream' .Default is false. + *

+ * if false, Will transfer 'inputStream' to temporary files to improve efficiency + */ + private Boolean mandatoryUseInputStream; + /** + * Default true + */ + private Boolean autoCloseStream; + /** + * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)} + * {@link AnalysisContext#getCustom()} + */ + private Object customObject; + /** + * A cache that stores temp data to save memory. + */ + private ReadCache readCache; + /** + * Ignore empty rows.Default is true. + */ + private Boolean ignoreEmptyRow; + /** + * Select the cache.Default use {@link com.alibaba.excel.cache.selector.SimpleReadCacheSelector} + */ + private ReadCacheSelector readCacheSelector; + /** + * Whether the encryption + */ + private String password; + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + */ + private String xlsxSAXParserFactoryName; + /** + * Whether to use the default listener, which is used by default. + *

+ * The {@link ModelBuildEventListener} is loaded by default to convert the object. + * defualt is true. + */ + private Boolean useDefaultListener; + + /** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`. + * + * @see ReadDefaultReturnEnum + */ + private ReadDefaultReturnEnum readDefaultReturn; + + /** + * Read some additional fields. None are read by default. + * + * @see CellExtraTypeEnum + */ + private Set extraReadSet; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java new file mode 100644 index 000000000..8163a5df4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/AbstractReadHolder.java @@ -0,0 +1,122 @@ +package com.alibaba.excel.read.metadata.holder; + +import java.util.HashMap; +import java.util.List; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.converters.DefaultConverterLoader; +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.metadata.AbstractHolder; +import com.alibaba.excel.read.listener.ModelBuildEventListener; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.ReadBasicParameter; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; +import com.alibaba.excel.util.ListUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Read Holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public abstract class AbstractReadHolder extends AbstractHolder implements ReadHolder { + /** + * Count the number of added heads when read sheet. + * + *

+ * 0 - This Sheet has no head ,since the first row are the data + *

+ * 1 - This Sheet has one row head , this is the default + *

+ * 2 - This Sheet has two row head ,since the third row is the data + */ + private Integer headRowNumber; + /** + * Excel head property + */ + private ExcelReadHeadProperty excelReadHeadProperty; + /** + * Read listener + */ + private List> readListenerList; + + public AbstractReadHolder(ReadBasicParameter readBasicParameter, AbstractReadHolder parentAbstractReadHolder) { + super(readBasicParameter, parentAbstractReadHolder); + + if (readBasicParameter.getUseScientificFormat() == null) { + if (parentAbstractReadHolder != null) { + getGlobalConfiguration().setUseScientificFormat( + parentAbstractReadHolder.getGlobalConfiguration().getUseScientificFormat()); + } + } else { + getGlobalConfiguration().setUseScientificFormat(readBasicParameter.getUseScientificFormat()); + } + + // Initialization property + this.excelReadHeadProperty = new ExcelReadHeadProperty(this, getClazz(), getHead()); + if (readBasicParameter.getHeadRowNumber() == null) { + if (parentAbstractReadHolder == null) { + if (excelReadHeadProperty.hasHead()) { + this.headRowNumber = excelReadHeadProperty.getHeadRowNumber(); + } else { + this.headRowNumber = 1; + } + } else { + this.headRowNumber = parentAbstractReadHolder.getHeadRowNumber(); + } + } else { + this.headRowNumber = readBasicParameter.getHeadRowNumber(); + } + + if (parentAbstractReadHolder == null) { + this.readListenerList = ListUtils.newArrayList(); + } else { + this.readListenerList = ListUtils.newArrayList(parentAbstractReadHolder.getReadListenerList()); + } + if (HolderEnum.WORKBOOK.equals(holderType())) { + Boolean useDefaultListener = ((ReadWorkbook)readBasicParameter).getUseDefaultListener(); + if (useDefaultListener == null || useDefaultListener) { + readListenerList.add(new ModelBuildEventListener()); + } + } + if (readBasicParameter.getCustomReadListenerList() != null + && !readBasicParameter.getCustomReadListenerList().isEmpty()) { + this.readListenerList.addAll(readBasicParameter.getCustomReadListenerList()); + } + + if (parentAbstractReadHolder == null) { + setConverterMap(DefaultConverterLoader.loadDefaultReadConverter()); + } else { + setConverterMap(new HashMap<>(parentAbstractReadHolder.getConverterMap())); + } + if (readBasicParameter.getCustomConverterList() != null + && !readBasicParameter.getCustomConverterList().isEmpty()) { + for (Converter converter : readBasicParameter.getCustomConverterList()) { + getConverterMap().put( + ConverterKeyBuild.buildKey(converter.supportJavaTypeKey(), converter.supportExcelTypeKey()), + converter); + } + } + } + + @Override + public List> readListenerList() { + return getReadListenerList(); + } + + @Override + public ExcelReadHeadProperty excelReadHeadProperty() { + return getExcelReadHeadProperty(); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadHolder.java new file mode 100644 index 000000000..32ac6ee9b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadHolder.java @@ -0,0 +1,30 @@ +package com.alibaba.excel.read.metadata.holder; + +import java.util.List; + +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; + +/** + * + * Get the corresponding Holder + * + * @author Jiaju Zhuang + **/ +public interface ReadHolder extends ConfigurationHolder { + /** + * What handler does the currently operated cell need to execute + * + * @return Current {@link ReadListener} + */ + List> readListenerList(); + + /** + * What {@link ExcelReadHeadProperty} does the currently operated cell need to execute + * + * @return Current {@link ExcelReadHeadProperty} + */ + ExcelReadHeadProperty excelReadHeadProperty(); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java new file mode 100644 index 000000000..d81df87c7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadRowHolder.java @@ -0,0 +1,90 @@ +package com.alibaba.excel.read.metadata.holder; + +import java.util.Map; + +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.Holder; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +public class ReadRowHolder implements Holder { + /** + * Returns row index of a row in the sheet that contains this cell.Start form 0. + */ + private Integer rowIndex; + /** + * Row type + */ + private RowTypeEnum rowType; + /** + * Cell map + */ + private Map cellMap; + /** + * The result of the previous listener + */ + private Object currentRowAnalysisResult; + /** + * Some global variables + */ + private GlobalConfiguration globalConfiguration; + + public ReadRowHolder(Integer rowIndex, RowTypeEnum rowType, GlobalConfiguration globalConfiguration, + Map cellMap) { + this.rowIndex = rowIndex; + this.rowType = rowType; + this.globalConfiguration = globalConfiguration; + this.cellMap = cellMap; + } + + public GlobalConfiguration getGlobalConfiguration() { + return globalConfiguration; + } + + public void setGlobalConfiguration(GlobalConfiguration globalConfiguration) { + this.globalConfiguration = globalConfiguration; + } + + public Object getCurrentRowAnalysisResult() { + return currentRowAnalysisResult; + } + + public void setCurrentRowAnalysisResult(Object currentRowAnalysisResult) { + this.currentRowAnalysisResult = currentRowAnalysisResult; + } + + public Integer getRowIndex() { + return rowIndex; + } + + public void setRowIndex(Integer rowIndex) { + this.rowIndex = rowIndex; + } + + public RowTypeEnum getRowType() { + return rowType; + } + + public void setRowType(RowTypeEnum rowType) { + this.rowType = rowType; + } + + public Map getCellMap() { + return cellMap; + } + + public void setCellMap(Map cellMap) { + this.cellMap = cellMap; + } + + @Override + public HolderEnum holderType() { + return HolderEnum.ROW; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java new file mode 100644 index 000000000..db5c4f733 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadSheetHolder.java @@ -0,0 +1,101 @@ +package com.alibaba.excel.read.metadata.holder; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.metadata.Cell; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.metadata.ReadSheet; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class ReadSheetHolder extends AbstractReadHolder { + + /** + * current param + */ + private ReadSheet readSheet; + /*** + * parent + */ + private ReadWorkbookHolder parentReadWorkbookHolder; + /*** + * sheetNo + */ + private Integer sheetNo; + /*** + * sheetName + */ + private String sheetName; + /** + * Gets the total number of rows , data may be inaccurate + */ + private Integer approximateTotalRowNumber; + /** + * Data storage of the current row. + */ + private Map cellMap; + /** + * Data storage of the current extra cell. + */ + private CellExtra cellExtra; + /** + * Index of the current row. + */ + private Integer rowIndex; + /** + * Current CellData + */ + private ReadCellData tempCellData; + /** + * Read the size of the largest head in sheet head data. + * see https://github.com/alibaba/easyexcel/issues/2014 + */ + private Integer maxNotEmptyDataHeadSize; + + /** + * Reading this sheet has ended. + */ + private Boolean ended; + + public ReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { + super(readSheet, readWorkbookHolder); + this.readSheet = readSheet; + this.parentReadWorkbookHolder = readWorkbookHolder; + this.sheetNo = readSheet.getSheetNo(); + this.sheetName = readSheet.getSheetName(); + this.cellMap = new LinkedHashMap<>(); + this.rowIndex = -1; + } + + /** + * Approximate total number of rows. + * use: getApproximateTotalRowNumber() + * + * @return + */ + @Deprecated + public Integer getTotal() { + return approximateTotalRowNumber; + } + + @Override + public HolderEnum holderType() { + return HolderEnum.SHEET; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java new file mode 100644 index 000000000..63f8c1bfd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/ReadWorkbookHolder.java @@ -0,0 +1,197 @@ +package com.alibaba.excel.read.metadata.holder; + +import java.io.File; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.alibaba.excel.cache.ReadCache; +import com.alibaba.excel.cache.selector.EternalReadCacheSelector; +import com.alibaba.excel.cache.selector.ReadCacheSelector; +import com.alibaba.excel.cache.selector.SimpleReadCacheSelector; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.support.ExcelTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class ReadWorkbookHolder extends AbstractReadHolder { + + /** + * current param + */ + private ReadWorkbook readWorkbook; + /** + * Read InputStream + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private InputStream inputStream; + /** + * Read file + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private File file; + + /** + * charset. + * Only work on the CSV file + */ + private Charset charset; + /** + * Mandatory use 'inputStream' .Default is false. + *

+ * if false, Will transfer 'inputStream' to temporary files to improve efficiency + */ + private Boolean mandatoryUseInputStream; + + /** + * Default true + */ + private Boolean autoCloseStream; + + /** + * Read not to {@code com.alibaba.excel.metadata.BasicParameter#clazz} value, the default will return type. + * Is only effective when set `useDefaultListener=true` or `useDefaultListener=null`. + * + * @see ReadDefaultReturnEnum + */ + private ReadDefaultReturnEnum readDefaultReturn; + + /** + * Excel type + */ + private ExcelTypeEnum excelType; + /** + * This object can be read in the Listener {@link AnalysisEventListener#invoke(Object, AnalysisContext)} + * {@link AnalysisContext#getCustom()} + */ + private Object customObject; + /** + * Ignore empty rows.Default is true. + */ + private Boolean ignoreEmptyRow; + /** + * A cache that stores temp data to save memory. + */ + private ReadCache readCache; + /** + * Select the cache.Default use {@link com.alibaba.excel.cache.selector.SimpleReadCacheSelector} + */ + private ReadCacheSelector readCacheSelector; + /** + * Temporary files when reading excel + */ + private File tempFile; + /** + * Whether the encryption + */ + private String password; + /** + * Read some additional fields. None are read by default. + * + * @see CellExtraTypeEnum + */ + private Set extraReadSet; + /** + * Actual sheet data + */ + private List actualSheetDataList; + /** + * Parameter sheet data + */ + private List parameterSheetDataList; + /** + * Read all + */ + private Boolean readAll; + + /** + * Prevent repeating sheet + */ + private Set hasReadSheet; + + public ReadWorkbookHolder(ReadWorkbook readWorkbook) { + super(readWorkbook, null); + this.readWorkbook = readWorkbook; + if (readWorkbook.getInputStream() != null) { + this.inputStream = readWorkbook.getInputStream(); + } + this.file = readWorkbook.getFile(); + + if (readWorkbook.getCharset() == null) { + this.charset = Charset.defaultCharset(); + } else { + this.charset = readWorkbook.getCharset(); + } + + if (readWorkbook.getMandatoryUseInputStream() == null) { + this.mandatoryUseInputStream = Boolean.FALSE; + } else { + this.mandatoryUseInputStream = readWorkbook.getMandatoryUseInputStream(); + } + if (readWorkbook.getAutoCloseStream() == null) { + this.autoCloseStream = Boolean.TRUE; + } else { + this.autoCloseStream = readWorkbook.getAutoCloseStream(); + } + + if (readWorkbook.getReadDefaultReturn() == null) { + this.readDefaultReturn = ReadDefaultReturnEnum.STRING; + } else { + this.readDefaultReturn = readWorkbook.getReadDefaultReturn(); + } + + this.customObject = readWorkbook.getCustomObject(); + if (readWorkbook.getIgnoreEmptyRow() == null) { + this.ignoreEmptyRow = Boolean.TRUE; + } else { + this.ignoreEmptyRow = readWorkbook.getIgnoreEmptyRow(); + } + if (readWorkbook.getReadCache() != null) { + if (readWorkbook.getReadCacheSelector() != null) { + throw new ExcelAnalysisException("'readCache' and 'readCacheSelector' only one choice."); + } + this.readCacheSelector = new EternalReadCacheSelector(readWorkbook.getReadCache()); + } else { + if (readWorkbook.getReadCacheSelector() == null) { + this.readCacheSelector = new SimpleReadCacheSelector(); + } else { + this.readCacheSelector = readWorkbook.getReadCacheSelector(); + } + } + if (readWorkbook.getExtraReadSet() == null) { + this.extraReadSet = new HashSet(); + } else { + this.extraReadSet = readWorkbook.getExtraReadSet(); + } + this.hasReadSheet = new HashSet(); + this.password = readWorkbook.getPassword(); + } + + @Override + public HolderEnum holderType() { + return HolderEnum.WORKBOOK; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/csv/CsvReadSheetHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/csv/CsvReadSheetHolder.java new file mode 100644 index 000000000..1b784e446 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/csv/CsvReadSheetHolder.java @@ -0,0 +1,24 @@ +package com.alibaba.excel.read.metadata.holder.csv; + +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvReadSheetHolder extends ReadSheetHolder { + + public CsvReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { + super(readSheet, readWorkbookHolder); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/csv/CsvReadWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/csv/CsvReadWorkbookHolder.java new file mode 100644 index 000000000..a65c2d79b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/csv/CsvReadWorkbookHolder.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.read.metadata.holder.csv; + +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; + +import java.io.IOException; +import java.nio.file.Files; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvReadWorkbookHolder extends ReadWorkbookHolder { + + private CSVFormat csvFormat; + private CSVParser csvParser; + + public CsvReadWorkbookHolder(ReadWorkbook readWorkbook) { + super(readWorkbook); + setExcelType(ExcelTypeEnum.CSV); + this.csvFormat = CSVFormat.DEFAULT; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadSheetHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadSheetHolder.java new file mode 100644 index 000000000..40b1d2d19 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadSheetHolder.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.read.metadata.holder.xls; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class XlsReadSheetHolder extends ReadSheetHolder { + /** + * Row type.Temporary storage, last set in ReadRowHolder. + */ + private RowTypeEnum tempRowType; + /** + * Temp object index. + */ + private Integer tempObjectIndex; + /** + * Temp object index. + */ + private Map objectCacheMap; + + public XlsReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { + super(readSheet, readWorkbookHolder); + tempRowType = RowTypeEnum.EMPTY; + objectCacheMap = new HashMap(16); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadWorkbookHolder.java new file mode 100644 index 000000000..da7d87e05 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xls/XlsReadWorkbookHolder.java @@ -0,0 +1,74 @@ +package com.alibaba.excel.read.metadata.holder.xls; + +import java.util.ArrayList; +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.poi.hssf.eventusermodel.FormatTrackingHSSFListener; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class XlsReadWorkbookHolder extends ReadWorkbookHolder { + /** + * File System + */ + private POIFSFileSystem poifsFileSystem; + /** + * Format tracking HSSFListener + */ + private FormatTrackingHSSFListener formatTrackingHSSFListener; + /** + * HSSFWorkbook + */ + private HSSFWorkbook hssfWorkbook; + /** + * Bound sheet record list. + */ + private List boundSheetRecordList; + /** + * Need read sheet. + */ + private Boolean needReadSheet; + /** + * Sheet Index + */ + private Integer readSheetIndex; + /** + * Ignore record. + */ + private Boolean ignoreRecord; + + /** + * Has the current sheet already stopped + */ + private Boolean currentSheetStopped; + + public XlsReadWorkbookHolder(ReadWorkbook readWorkbook) { + super(readWorkbook); + this.boundSheetRecordList = new ArrayList(); + this.needReadSheet = Boolean.TRUE; + setExcelType(ExcelTypeEnum.XLS); + if (getGlobalConfiguration().getUse1904windowing() == null) { + getGlobalConfiguration().setUse1904windowing(Boolean.FALSE); + } + ignoreRecord = Boolean.FALSE; + currentSheetStopped = Boolean.TRUE; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadSheetHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadSheetHolder.java new file mode 100644 index 000000000..9fa9182d3 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadSheetHolder.java @@ -0,0 +1,52 @@ +package com.alibaba.excel.read.metadata.holder.xlsx; + +import java.util.Deque; +import java.util.LinkedList; + +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class XlsxReadSheetHolder extends ReadSheetHolder { + /** + * Record the label of the current operation to prevent NPE. + */ + private Deque tagDeque; + /** + * Current Column + */ + private Integer columnIndex; + /** + * Data for current label. + */ + private StringBuilder tempData; + /** + * Formula for current label. + */ + private StringBuilder tempFormula; + /** + * excel Relationship + */ + private PackageRelationshipCollection packageRelationshipCollection; + + public XlsxReadSheetHolder(ReadSheet readSheet, ReadWorkbookHolder readWorkbookHolder) { + super(readSheet, readWorkbookHolder); + this.tagDeque = new LinkedList(); + packageRelationshipCollection + = ((XlsxReadWorkbookHolder)readWorkbookHolder).getPackageRelationshipCollectionMap().get( + readSheet.getSheetNo()); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadWorkbookHolder.java new file mode 100644 index 000000000..fbeaf0484 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/holder/xlsx/XlsxReadWorkbookHolder.java @@ -0,0 +1,84 @@ +package com.alibaba.excel.read.metadata.holder.xlsx; + +import java.util.Map; + +import javax.xml.parsers.SAXParserFactory; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.MapUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.apache.poi.xssf.model.StylesTable; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class XlsxReadWorkbookHolder extends ReadWorkbookHolder { + /** + * Package + */ + private OPCPackage opcPackage; + /** + * SAXParserFactory used when reading xlsx. + *

+ * The default will automatically find. + *

+ * Please pass in the name of a class ,like : "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" + * + * @see SAXParserFactory#newInstance() + * @see SAXParserFactory#newInstance(String, ClassLoader) + */ + private String saxParserFactoryName; + /** + * Current style information + */ + private StylesTable stylesTable; + /** + * cache data format + */ + private Map dataFormatDataCache; + + /** + * excel Relationship, key: sheetNo value: PackageRelationshipCollection + */ + private Map packageRelationshipCollectionMap; + + public XlsxReadWorkbookHolder(ReadWorkbook readWorkbook) { + super(readWorkbook); + this.saxParserFactoryName = readWorkbook.getXlsxSAXParserFactoryName(); + setExcelType(ExcelTypeEnum.XLSX); + dataFormatDataCache = MapUtils.newHashMap(); + } + + public DataFormatData dataFormatData(int dateFormatIndexInteger) { + return dataFormatDataCache.computeIfAbsent(dateFormatIndexInteger, key -> { + DataFormatData dataFormatData = new DataFormatData(); + if (stylesTable == null) { + return null; + } + XSSFCellStyle xssfCellStyle = stylesTable.getStyleAt(dateFormatIndexInteger); + if (xssfCellStyle == null) { + return null; + } + dataFormatData.setIndex(xssfCellStyle.getDataFormat()); + dataFormatData.setFormat(BuiltinFormats.getBuiltinFormat(dataFormatData.getIndex(), + xssfCellStyle.getDataFormatString(), globalConfiguration().getLocale())); + return dataFormatData; + }); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java new file mode 100644 index 000000000..0329715cf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/metadata/property/ExcelReadHeadProperty.java @@ -0,0 +1,19 @@ +package com.alibaba.excel.read.metadata.property; + +import java.util.List; + +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.metadata.Holder; +import com.alibaba.excel.metadata.property.ExcelHeadProperty; + +/** + * Define the header attribute of excel + * + * @author jipengfei + */ +public class ExcelReadHeadProperty extends ExcelHeadProperty { + + public ExcelReadHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List> head) { + super(configurationHolder, headClazz, head); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/processor/AnalysisEventProcessor.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/processor/AnalysisEventProcessor.java new file mode 100644 index 000000000..360ae97d9 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/processor/AnalysisEventProcessor.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.read.processor; + +import com.alibaba.excel.context.AnalysisContext; + +/** + * + * Event processor + * + * @author jipengfei + */ +public interface AnalysisEventProcessor { + /** + * Read extra information + * + * @param analysisContext + */ + void extra(AnalysisContext analysisContext); + + /** + * End row + * + * @param analysisContext + */ + void endRow(AnalysisContext analysisContext); + + /** + * Notify after all analysed + * + * @param analysisContext + * Analysis context + */ + void endSheet(AnalysisContext analysisContext); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/read/processor/DefaultAnalysisEventProcessor.java b/easyexcel-core/src/main/java/com/alibaba/excel/read/processor/DefaultAnalysisEventProcessor.java new file mode 100644 index 000000000..0d783a7be --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/read/processor/DefaultAnalysisEventProcessor.java @@ -0,0 +1,168 @@ +package com.alibaba.excel.read.processor; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.enums.RowTypeEnum; +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.holder.ReadRowHolder; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; +import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty; +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.util.ConverterUtils; +import com.alibaba.excel.util.StringUtils; + +import org.apache.commons.collections4.MapUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Analysis event + * + * @author jipengfei + */ +public class DefaultAnalysisEventProcessor implements AnalysisEventProcessor { + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAnalysisEventProcessor.class); + + @Override + public void extra(AnalysisContext analysisContext) { + dealExtra(analysisContext); + } + + @Override + public void endRow(AnalysisContext analysisContext) { + if (RowTypeEnum.EMPTY.equals(analysisContext.readRowHolder().getRowType())) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Empty row!"); + } + if (analysisContext.readWorkbookHolder().getIgnoreEmptyRow()) { + return; + } + } + dealData(analysisContext); + } + + @Override + public void endSheet(AnalysisContext analysisContext) { + ReadSheetHolder readSheetHolder = analysisContext.readSheetHolder(); + if (BooleanUtils.isTrue(readSheetHolder.getEnded())) { + return; + } + readSheetHolder.setEnded(Boolean.TRUE); + + for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { + readListener.doAfterAllAnalysed(analysisContext); + } + } + + private void dealExtra(AnalysisContext analysisContext) { + for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { + try { + readListener.extra(analysisContext.readSheetHolder().getCellExtra(), analysisContext); + } catch (Exception e) { + onException(analysisContext, e); + break; + } + if (!readListener.hasNext(analysisContext)) { + throw new ExcelAnalysisStopException(); + } + } + } + + private void onException(AnalysisContext analysisContext, Exception e) { + for (ReadListener readListenerException : analysisContext.currentReadHolder().readListenerList()) { + try { + readListenerException.onException(e, analysisContext); + } catch (RuntimeException re) { + throw re; + } catch (Exception e1) { + throw new ExcelAnalysisException(e1.getMessage(), e1); + } + } + } + + private void dealData(AnalysisContext analysisContext) { + ReadRowHolder readRowHolder = analysisContext.readRowHolder(); + Map> cellDataMap = (Map)readRowHolder.getCellMap(); + readRowHolder.setCurrentRowAnalysisResult(cellDataMap); + int rowIndex = readRowHolder.getRowIndex(); + int currentHeadRowNumber = analysisContext.readSheetHolder().getHeadRowNumber(); + + boolean isData = rowIndex >= currentHeadRowNumber; + + // Last head column + if (!isData && currentHeadRowNumber == rowIndex + 1) { + buildHead(analysisContext, cellDataMap); + } + // Now is data + for (ReadListener readListener : analysisContext.currentReadHolder().readListenerList()) { + try { + if (isData) { + readListener.invoke(readRowHolder.getCurrentRowAnalysisResult(), analysisContext); + } else { + readListener.invokeHead(cellDataMap, analysisContext); + } + } catch (Exception e) { + onException(analysisContext, e); + break; + } + if (!readListener.hasNext(analysisContext)) { + throw new ExcelAnalysisStopException(); + } + } + } + + private void buildHead(AnalysisContext analysisContext, Map> cellDataMap) { + // Rule out empty head, and then take the largest column + if (MapUtils.isNotEmpty(cellDataMap)) { + cellDataMap.entrySet() + .stream() + .filter(entry -> CellDataTypeEnum.EMPTY != entry.getValue().getType()) + .forEach(entry -> analysisContext.readSheetHolder().setMaxNotEmptyDataHeadSize(entry.getKey())); + } + + if (!HeadKindEnum.CLASS.equals(analysisContext.currentReadHolder().excelReadHeadProperty().getHeadKind())) { + return; + } + Map dataMap = ConverterUtils.convertToStringMap(cellDataMap, analysisContext); + ExcelReadHeadProperty excelHeadPropertyData = analysisContext.readSheetHolder().excelReadHeadProperty(); + Map headMapData = excelHeadPropertyData.getHeadMap(); + Map tmpHeadMap = new HashMap(headMapData.size() * 4 / 3 + 1); + for (Map.Entry entry : headMapData.entrySet()) { + Head headData = entry.getValue(); + if (headData.getForceIndex() || !headData.getForceName()) { + tmpHeadMap.put(entry.getKey(), headData); + continue; + } + List headNameList = headData.getHeadNameList(); + String headName = headNameList.get(headNameList.size() - 1); + for (Map.Entry stringEntry : dataMap.entrySet()) { + if (stringEntry == null) { + continue; + } + String headString = stringEntry.getValue(); + Integer stringKey = stringEntry.getKey(); + if (StringUtils.isEmpty(headString)) { + continue; + } + if (analysisContext.currentReadHolder().globalConfiguration().getAutoTrim()) { + headString = headString.trim(); + } + if (headName.equals(headString)) { + headData.setColumnIndex(stringKey); + tmpHeadMap.put(stringKey, headData); + break; + } + } + } + excelHeadPropertyData.setHeadMap(tmpHeadMap); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java b/easyexcel-core/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java new file mode 100644 index 000000000..e78bf94ce --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java @@ -0,0 +1,118 @@ +package com.alibaba.excel.support; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; + +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.read.metadata.ReadWorkbook; +import com.alibaba.excel.util.StringUtils; + +import lombok.Getter; +import org.apache.poi.util.IOUtils; + +/** + * @author jipengfei + */ +@Getter +public enum ExcelTypeEnum { + + /** + * csv + */ + CSV(".csv", new byte[] {-27, -89, -109, -27}), + + /** + * xls + */ + XLS(".xls", new byte[] {-48, -49, 17, -32, -95, -79, 26, -31}), + + /** + * xlsx + */ + XLSX(".xlsx", new byte[] {80, 75, 3, 4}); + + final String value; + final byte[] magic; + + ExcelTypeEnum(String value, byte[] magic) { + this.value = value; + this.magic = magic; + } + + final static int MAX_PATTERN_LENGTH = 8; + + public static ExcelTypeEnum valueOf(ReadWorkbook readWorkbook) { + ExcelTypeEnum excelType = readWorkbook.getExcelType(); + if (excelType != null) { + return excelType; + } + File file = readWorkbook.getFile(); + InputStream inputStream = readWorkbook.getInputStream(); + if (file == null && inputStream == null) { + throw new ExcelAnalysisException("File and inputStream must be a non-null."); + } + try { + if (file != null) { + if (!file.exists()) { + throw new ExcelAnalysisException("File " + file.getAbsolutePath() + " not exists."); + } + // If there is a password, use the FileMagic first + if (!StringUtils.isEmpty(readWorkbook.getPassword())) { + try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) { + return recognitionExcelType(bufferedInputStream); + } + } + // Use the name to determine the type + String fileName = file.getName(); + if (fileName.endsWith(XLSX.getValue())) { + return XLSX; + } else if (fileName.endsWith(XLS.getValue())) { + return XLS; + } else if (fileName.endsWith(CSV.getValue())) { + return CSV; + } + if (StringUtils.isEmpty(readWorkbook.getPassword())) { + try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file))) { + return recognitionExcelType(bufferedInputStream); + } + } + } + if (!inputStream.markSupported()) { + inputStream = new BufferedInputStream(inputStream); + readWorkbook.setInputStream(inputStream); + } + return recognitionExcelType(inputStream); + } catch (ExcelCommonException e) { + throw e; + } catch (Exception e) { + throw new ExcelCommonException( + "Convert excel format exception.You can try specifying the 'excelType' yourself", e); + } + } + + private static ExcelTypeEnum recognitionExcelType(InputStream inputStream) throws Exception { + // Grab the first bytes of this stream + byte[] data = IOUtils.peekFirstNBytes(inputStream, MAX_PATTERN_LENGTH); + if (findMagic(XLSX.magic, data)) { + return XLSX; + } else if (findMagic(XLS.magic, data)) { + return XLS; + } + // csv has no fixed prefix, if the format is not specified, it defaults to csv + return CSV; + } + + private static boolean findMagic(byte[] expected, byte[] actual) { + int i = 0; + for (byte expectedByte : expected) { + if (actual[i++] != expectedByte && expectedByte != '?') { + return false; + } + } + return true; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/BeanMapUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/BeanMapUtils.java new file mode 100644 index 000000000..29926aa09 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/BeanMapUtils.java @@ -0,0 +1,40 @@ +package com.alibaba.excel.util; + +import com.alibaba.excel.support.cglib.beans.BeanMap; +import com.alibaba.excel.support.cglib.core.DefaultNamingPolicy; + +/** + * bean utils + * + * @author Jiaju Zhuang + */ +public class BeanMapUtils { + + /** + * Helper method to create a new BeanMap. For finer + * control over the generated instance, use a new instance of + * BeanMap.Generator instead of this static method. + * + * Custom naming policy to prevent null pointer exceptions. + * see: https://github.com/alibaba/easyexcel/issues/2064 + * + * @param bean the JavaBean underlying the map + * @return a new BeanMap instance + */ + public static BeanMap create(Object bean) { + BeanMap.Generator gen = new BeanMap.Generator(); + gen.setBean(bean); + gen.setContextClass(bean.getClass()); + gen.setNamingPolicy(EasyExcelNamingPolicy.INSTANCE); + return gen.create(); + } + + public static class EasyExcelNamingPolicy extends DefaultNamingPolicy { + public static final EasyExcelNamingPolicy INSTANCE = new EasyExcelNamingPolicy(); + + @Override + protected String getTag() { + return "ByEasyExcelCGLIB"; + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/BooleanUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/BooleanUtils.java new file mode 100644 index 000000000..c01ab24bb --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/BooleanUtils.java @@ -0,0 +1,114 @@ +package com.alibaba.excel.util; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class BooleanUtils { + + private static final String TRUE_NUMBER = "1"; + + private BooleanUtils() {} + + /** + * String to boolean + * + * @param str + * @return + */ + public static Boolean valueOf(String str) { + if (TRUE_NUMBER.equals(str)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + + // boolean Boolean methods + //----------------------------------------------------------------------- + /** + *

Checks if a {@code Boolean} value is {@code true}, + * handling {@code null} by returning {@code false}.

+ * + *
+     *   BooleanUtils.isTrue(Boolean.TRUE)  = true
+     *   BooleanUtils.isTrue(Boolean.FALSE) = false
+     *   BooleanUtils.isTrue(null)          = false
+     * 
+ * + * @param bool the boolean to check, null returns {@code false} + * @return {@code true} only if the input is non-null and true + * @since 2.1 + */ + public static boolean isTrue(final Boolean bool) { + return Boolean.TRUE.equals(bool); + } + + /** + *

Checks if a {@code Boolean} value is not {@code true}, + * handling {@code null} by returning {@code true}.

+ * + *
+     *   BooleanUtils.isNotTrue(Boolean.TRUE)  = false
+     *   BooleanUtils.isNotTrue(Boolean.FALSE) = true
+     *   BooleanUtils.isNotTrue(null)          = true
+     * 
+ * + * @param bool the boolean to check, null returns {@code true} + * @return {@code true} if the input is null or false + * @since 2.3 + */ + public static boolean isNotTrue(final Boolean bool) { + return !isTrue(bool); + } + + /** + *

Checks if a {@code Boolean} value is {@code false}, + * handling {@code null} by returning {@code false}.

+ * + *
+     *   BooleanUtils.isFalse(Boolean.TRUE)  = false
+     *   BooleanUtils.isFalse(Boolean.FALSE) = true
+     *   BooleanUtils.isFalse(null)          = false
+     * 
+ * + * @param bool the boolean to check, null returns {@code false} + * @return {@code true} only if the input is non-null and false + * @since 2.1 + */ + public static boolean isFalse(final Boolean bool) { + return Boolean.FALSE.equals(bool); + } + + /** + *

Checks if a {@code Boolean} value is not {@code false}, + * handling {@code null} by returning {@code true}.

+ * + *
+     *   BooleanUtils.isNotFalse(Boolean.TRUE)  = true
+     *   BooleanUtils.isNotFalse(Boolean.FALSE) = false
+     *   BooleanUtils.isNotFalse(null)          = true
+     * 
+ * + * @param bool the boolean to check, null returns {@code true} + * @return {@code true} if the input is null or true + * @since 2.3 + */ + public static boolean isNotFalse(final Boolean bool) { + return !isFalse(bool); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java new file mode 100644 index 000000000..3f3e14a0e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/ClassUtils.java @@ -0,0 +1,583 @@ +package com.alibaba.excel.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.converters.AutoConverter; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.metadata.FieldCache; +import com.alibaba.excel.metadata.FieldWrapper; +import com.alibaba.excel.metadata.property.DateTimeFormatProperty; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.NumberFormatProperty; +import com.alibaba.excel.metadata.property.StyleProperty; +import com.alibaba.excel.support.cglib.beans.BeanMap; +import com.alibaba.excel.write.metadata.holder.WriteHolder; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class ClassUtils { + + /** + * memory cache + */ + public static final Map FIELD_CACHE = new ConcurrentHashMap<>(); + /** + * thread local cache + */ + private static final ThreadLocal> FIELD_THREAD_LOCAL = new ThreadLocal<>(); + + /** + * The cache configuration information for each of the class + */ + public static final ConcurrentHashMap, Map> CLASS_CONTENT_CACHE + = new ConcurrentHashMap<>(); + + /** + * The cache configuration information for each of the class + */ + private static final ThreadLocal, Map>> CLASS_CONTENT_THREAD_LOCAL + = new ThreadLocal<>(); + + /** + * The cache configuration information for each of the class + */ + public static final ConcurrentHashMap CONTENT_CACHE + = new ConcurrentHashMap<>(); + + /** + * The cache configuration information for each of the class + */ + private static final ThreadLocal> CONTENT_THREAD_LOCAL + = new ThreadLocal<>(); + + /** + * Calculate the configuration information for the class + * + * @param dataMap + * @param headClazz + * @param fieldName + * @return + */ + public static ExcelContentProperty declaredExcelContentProperty(Map dataMap, Class headClazz, + String fieldName, + ConfigurationHolder configurationHolder) { + Class clazz = null; + if (dataMap instanceof BeanMap) { + Object bean = ((BeanMap)dataMap).getBean(); + if (bean != null) { + clazz = bean.getClass(); + } + } + return getExcelContentProperty(clazz, headClazz, fieldName, configurationHolder); + } + + private static ExcelContentProperty getExcelContentProperty(Class clazz, Class headClass, String fieldName, + ConfigurationHolder configurationHolder) { + switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) { + case THREAD_LOCAL: + Map contentCacheMap = CONTENT_THREAD_LOCAL.get(); + if (contentCacheMap == null) { + contentCacheMap = MapUtils.newHashMap(); + CONTENT_THREAD_LOCAL.set(contentCacheMap); + } + return contentCacheMap.computeIfAbsent(buildKey(clazz, headClass, fieldName), key -> { + return doGetExcelContentProperty(clazz, headClass, fieldName, configurationHolder); + }); + case MEMORY: + return CONTENT_CACHE.computeIfAbsent(buildKey(clazz, headClass, fieldName), key -> { + return doGetExcelContentProperty(clazz, headClass, fieldName, configurationHolder); + }); + case NONE: + return doGetExcelContentProperty(clazz, headClass, fieldName, configurationHolder); + default: + throw new UnsupportedOperationException("unsupported enum"); + } + } + + private static ExcelContentProperty doGetExcelContentProperty(Class clazz, Class headClass, + String fieldName, + ConfigurationHolder configurationHolder) { + ExcelContentProperty excelContentProperty = Optional.ofNullable( + declaredFieldContentMap(clazz, configurationHolder)) + .map(map -> map.get(fieldName)) + .orElse(null); + ExcelContentProperty headExcelContentProperty = Optional.ofNullable( + declaredFieldContentMap(headClass, configurationHolder)) + .map(map -> map.get(fieldName)) + .orElse(null); + ExcelContentProperty combineExcelContentProperty = new ExcelContentProperty(); + + combineExcelContentProperty(combineExcelContentProperty, headExcelContentProperty); + if (clazz != headClass) { + combineExcelContentProperty(combineExcelContentProperty, excelContentProperty); + } + return combineExcelContentProperty; + } + + public static void combineExcelContentProperty(ExcelContentProperty combineExcelContentProperty, + ExcelContentProperty excelContentProperty) { + if (excelContentProperty == null) { + return; + } + if (excelContentProperty.getField() != null) { + combineExcelContentProperty.setField(excelContentProperty.getField()); + } + if (excelContentProperty.getConverter() != null) { + combineExcelContentProperty.setConverter(excelContentProperty.getConverter()); + } + if (excelContentProperty.getDateTimeFormatProperty() != null) { + combineExcelContentProperty.setDateTimeFormatProperty(excelContentProperty.getDateTimeFormatProperty()); + } + if (excelContentProperty.getNumberFormatProperty() != null) { + combineExcelContentProperty.setNumberFormatProperty(excelContentProperty.getNumberFormatProperty()); + } + if (excelContentProperty.getContentStyleProperty() != null) { + combineExcelContentProperty.setContentStyleProperty(excelContentProperty.getContentStyleProperty()); + } + if (excelContentProperty.getContentFontProperty() != null) { + combineExcelContentProperty.setContentFontProperty(excelContentProperty.getContentFontProperty()); + } + } + + private static ContentPropertyKey buildKey(Class clazz, Class headClass, String fieldName) { + return new ContentPropertyKey(clazz, headClass, fieldName); + } + + private static Map declaredFieldContentMap(Class clazz, + ConfigurationHolder configurationHolder) { + if (clazz == null) { + return null; + } + switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) { + case THREAD_LOCAL: + Map, Map> classContentCacheMap + = CLASS_CONTENT_THREAD_LOCAL.get(); + if (classContentCacheMap == null) { + classContentCacheMap = MapUtils.newHashMap(); + CLASS_CONTENT_THREAD_LOCAL.set(classContentCacheMap); + } + return classContentCacheMap.computeIfAbsent(clazz, key -> { + return doDeclaredFieldContentMap(clazz); + }); + case MEMORY: + return CLASS_CONTENT_CACHE.computeIfAbsent(clazz, key -> { + return doDeclaredFieldContentMap(clazz); + }); + case NONE: + return doDeclaredFieldContentMap(clazz); + default: + throw new UnsupportedOperationException("unsupported enum"); + } + + } + + private static Map doDeclaredFieldContentMap(Class clazz) { + if (clazz == null) { + return null; + } + List tempFieldList = new ArrayList<>(); + Class tempClass = clazz; + while (tempClass != null) { + Collections.addAll(tempFieldList, tempClass.getDeclaredFields()); + // Get the parent class and give it to yourself + tempClass = tempClass.getSuperclass(); + } + + ContentStyle parentContentStyle = clazz.getAnnotation(ContentStyle.class); + ContentFontStyle parentContentFontStyle = clazz.getAnnotation(ContentFontStyle.class); + Map fieldContentMap = MapUtils.newHashMapWithExpectedSize( + tempFieldList.size()); + for (Field field : tempFieldList) { + ExcelContentProperty excelContentProperty = new ExcelContentProperty(); + excelContentProperty.setField(field); + + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + if (excelProperty != null) { + Class> convertClazz = excelProperty.converter(); + if (convertClazz != AutoConverter.class) { + try { + Converter converter = convertClazz.getDeclaredConstructor().newInstance(); + excelContentProperty.setConverter(converter); + } catch (Exception e) { + throw new ExcelCommonException( + "Can not instance custom converter:" + convertClazz.getName()); + } + } + } + + ContentStyle contentStyle = field.getAnnotation(ContentStyle.class); + if (contentStyle == null) { + contentStyle = parentContentStyle; + } + excelContentProperty.setContentStyleProperty(StyleProperty.build(contentStyle)); + + ContentFontStyle contentFontStyle = field.getAnnotation(ContentFontStyle.class); + if (contentFontStyle == null) { + contentFontStyle = parentContentFontStyle; + } + excelContentProperty.setContentFontProperty(FontProperty.build(contentFontStyle)); + + excelContentProperty.setDateTimeFormatProperty( + DateTimeFormatProperty.build(field.getAnnotation(DateTimeFormat.class))); + excelContentProperty.setNumberFormatProperty( + NumberFormatProperty.build(field.getAnnotation(NumberFormat.class))); + + fieldContentMap.put(field.getName(), excelContentProperty); + } + return fieldContentMap; + } + + /** + * Parsing field in the class + * + * @param clazz Need to parse the class + * @param configurationHolder configuration + */ + public static FieldCache declaredFields(Class clazz, ConfigurationHolder configurationHolder) { + switch (configurationHolder.globalConfiguration().getFiledCacheLocation()) { + case THREAD_LOCAL: + Map fieldCacheMap = FIELD_THREAD_LOCAL.get(); + if (fieldCacheMap == null) { + fieldCacheMap = MapUtils.newHashMap(); + FIELD_THREAD_LOCAL.set(fieldCacheMap); + } + return fieldCacheMap.computeIfAbsent(new FieldCacheKey(clazz, configurationHolder), key -> { + return doDeclaredFields(clazz, configurationHolder); + }); + case MEMORY: + return FIELD_CACHE.computeIfAbsent(new FieldCacheKey(clazz, configurationHolder), key -> { + return doDeclaredFields(clazz, configurationHolder); + }); + case NONE: + return doDeclaredFields(clazz, configurationHolder); + default: + throw new UnsupportedOperationException("unsupported enum"); + } + } + + private static FieldCache doDeclaredFields(Class clazz, ConfigurationHolder configurationHolder) { + List tempFieldList = new ArrayList<>(); + Class tempClass = clazz; + // When the parent class is null, it indicates that the parent class (Object class) has reached the top + // level. + while (tempClass != null) { + Collections.addAll(tempFieldList, tempClass.getDeclaredFields()); + // Get the parent class and give it to yourself + tempClass = tempClass.getSuperclass(); + } + // Screening of field + Map> orderFieldMap = new TreeMap<>(); + Map indexFieldMap = new TreeMap<>(); + Set ignoreSet = new HashSet<>(); + + ExcelIgnoreUnannotated excelIgnoreUnannotated = clazz.getAnnotation(ExcelIgnoreUnannotated.class); + for (Field field : tempFieldList) { + declaredOneField(field, orderFieldMap, indexFieldMap, ignoreSet, excelIgnoreUnannotated); + } + Map sortedFieldMap = buildSortedAllFieldMap(orderFieldMap, indexFieldMap); + FieldCache fieldCache = new FieldCache(sortedFieldMap, indexFieldMap); + + if (!(configurationHolder instanceof WriteHolder)) { + return fieldCache; + } + + WriteHolder writeHolder = (WriteHolder)configurationHolder; + + boolean needIgnore = !CollectionUtils.isEmpty(writeHolder.excludeColumnFieldNames()) + || !CollectionUtils.isEmpty(writeHolder.excludeColumnIndexes()) + || !CollectionUtils.isEmpty(writeHolder.includeColumnFieldNames()) + || !CollectionUtils.isEmpty(writeHolder.includeColumnIndexes()); + + if (!needIgnore) { + return fieldCache; + } + // ignore filed + Map tempSortedFieldMap = MapUtils.newHashMap(); + int index = 0; + for (Map.Entry entry : sortedFieldMap.entrySet()) { + Integer key = entry.getKey(); + FieldWrapper field = entry.getValue(); + + // The current field needs to be ignored + if (writeHolder.ignore(field.getFieldName(), entry.getKey())) { + ignoreSet.add(field.getFieldName()); + indexFieldMap.remove(index); + } else { + // Mandatory sorted fields + if (indexFieldMap.containsKey(key)) { + tempSortedFieldMap.put(key, field); + } else { + // Need to reorder automatically + // Check whether the current key is already in use + while (tempSortedFieldMap.containsKey(index)) { + index++; + } + tempSortedFieldMap.put(index++, field); + } + } + } + fieldCache.setSortedFieldMap(tempSortedFieldMap); + + // resort field + resortField(writeHolder, fieldCache); + return fieldCache; + } + + /** + * it only works when {@link WriteHolder#includeColumnFieldNames()} or + * {@link WriteHolder#includeColumnIndexes()} has value + * and {@link WriteHolder#orderByIncludeColumn()} is true + **/ + private static void resortField(WriteHolder writeHolder, FieldCache fieldCache) { + if (!writeHolder.orderByIncludeColumn()) { + return; + } + Map indexFieldMap = fieldCache.getIndexFieldMap(); + + Collection includeColumnFieldNames = writeHolder.includeColumnFieldNames(); + if (!CollectionUtils.isEmpty(includeColumnFieldNames)) { + // Field sorted map + Map filedIndexMap = MapUtils.newHashMap(); + int fieldIndex = 0; + for (String includeColumnFieldName : includeColumnFieldNames) { + filedIndexMap.put(includeColumnFieldName, fieldIndex++); + } + + // rebuild sortedFieldMap + Map tempSortedFieldMap = MapUtils.newHashMap(); + fieldCache.getSortedFieldMap().forEach((index, field) -> { + Integer tempFieldIndex = filedIndexMap.get(field.getFieldName()); + if (tempFieldIndex != null) { + tempSortedFieldMap.put(tempFieldIndex, field); + + // The user has redefined the ordering and the ordering of annotations needs to be invalidated + if (!tempFieldIndex.equals(index)) { + indexFieldMap.remove(index); + } + } + }); + fieldCache.setSortedFieldMap(tempSortedFieldMap); + return; + } + + Collection includeColumnIndexes = writeHolder.includeColumnIndexes(); + if (!CollectionUtils.isEmpty(includeColumnIndexes)) { + // Index sorted map + Map filedIndexMap = MapUtils.newHashMap(); + int fieldIndex = 0; + for (Integer includeColumnIndex : includeColumnIndexes) { + filedIndexMap.put(includeColumnIndex, fieldIndex++); + } + + // rebuild sortedFieldMap + Map tempSortedFieldMap = MapUtils.newHashMap(); + fieldCache.getSortedFieldMap().forEach((index, field) -> { + Integer tempFieldIndex = filedIndexMap.get(index); + + // The user has redefined the ordering and the ordering of annotations needs to be invalidated + if (tempFieldIndex != null) { + tempSortedFieldMap.put(tempFieldIndex, field); + } + }); + fieldCache.setSortedFieldMap(tempSortedFieldMap); + } + } + + private static Map buildSortedAllFieldMap(Map> orderFieldMap, + Map indexFieldMap) { + + Map sortedAllFieldMap = new HashMap<>( + (orderFieldMap.size() + indexFieldMap.size()) * 4 / 3 + 1); + + Map tempIndexFieldMap = new HashMap<>(indexFieldMap); + int index = 0; + for (List fieldList : orderFieldMap.values()) { + for (FieldWrapper field : fieldList) { + while (tempIndexFieldMap.containsKey(index)) { + sortedAllFieldMap.put(index, tempIndexFieldMap.get(index)); + tempIndexFieldMap.remove(index); + index++; + } + sortedAllFieldMap.put(index, field); + index++; + } + } + sortedAllFieldMap.putAll(tempIndexFieldMap); + return sortedAllFieldMap; + } + + private static void declaredOneField(Field field, Map> orderFieldMap, + Map indexFieldMap, Set ignoreSet, + ExcelIgnoreUnannotated excelIgnoreUnannotated) { + String fieldName = FieldUtils.resolveCglibFieldName(field); + FieldWrapper fieldWrapper = new FieldWrapper(); + fieldWrapper.setField(field); + fieldWrapper.setFieldName(fieldName); + + ExcelIgnore excelIgnore = field.getAnnotation(ExcelIgnore.class); + + if (excelIgnore != null) { + ignoreSet.add(fieldName); + return; + } + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + boolean noExcelProperty = excelProperty == null && excelIgnoreUnannotated != null; + if (noExcelProperty) { + ignoreSet.add(fieldName); + return; + } + boolean isStaticFinalOrTransient = + (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers())) + || Modifier.isTransient(field.getModifiers()); + if (excelProperty == null && isStaticFinalOrTransient) { + ignoreSet.add(fieldName); + return; + } + // set heads + if (excelProperty != null) { + fieldWrapper.setHeads(excelProperty.value()); + } + + if (excelProperty != null && excelProperty.index() >= 0) { + if (indexFieldMap.containsKey(excelProperty.index())) { + throw new ExcelCommonException( + "The index of '" + indexFieldMap.get(excelProperty.index()).getFieldName() + + "' and '" + field.getName() + "' must be inconsistent"); + } + indexFieldMap.put(excelProperty.index(), fieldWrapper); + return; + } + + int order = Integer.MAX_VALUE; + if (excelProperty != null) { + order = excelProperty.order(); + } + List orderFieldList = orderFieldMap.computeIfAbsent(order, key -> ListUtils.newArrayList()); + orderFieldList.add(fieldWrapper); + } + + /** + *

Gets a {@code List} of all interfaces implemented by the given + * class and its superclasses.

+ * + *

The order is determined by looking through each interface in turn as + * declared in the source file and following its hierarchy up. Then each + * superclass is considered in the same way. Later duplicates are ignored, + * so the order is maintained.

+ * + * @param cls the class to look up, may be {@code null} + * @return the {@code List} of interfaces in order, + * {@code null} if null input + */ + public static List> getAllInterfaces(final Class cls) { + if (cls == null) { + return null; + } + + final LinkedHashSet> interfacesFound = new LinkedHashSet<>(); + getAllInterfaces(cls, interfacesFound); + + return new ArrayList<>(interfacesFound); + } + + /** + * Gets the interfaces for the specified class. + * + * @param cls the class to look up, may be {@code null} + * @param interfacesFound the {@code Set} of interfaces for the class + */ + private static void getAllInterfaces(Class cls, final HashSet> interfacesFound) { + while (cls != null) { + final Class[] interfaces = cls.getInterfaces(); + + for (final Class i : interfaces) { + if (interfacesFound.add(i)) { + getAllInterfaces(i, interfacesFound); + } + } + + cls = cls.getSuperclass(); + } + } + + @Getter + @Setter + @EqualsAndHashCode + @AllArgsConstructor + public static class ContentPropertyKey { + private Class clazz; + private Class headClass; + private String fieldName; + } + + @Data + public static class FieldCacheKey { + private Class clazz; + private Collection excludeColumnFieldNames; + private Collection excludeColumnIndexes; + private Collection includeColumnFieldNames; + private Collection includeColumnIndexes; + + FieldCacheKey(Class clazz, ConfigurationHolder configurationHolder) { + this.clazz = clazz; + if (configurationHolder instanceof WriteHolder) { + WriteHolder writeHolder = (WriteHolder)configurationHolder; + this.excludeColumnFieldNames = writeHolder.excludeColumnFieldNames(); + this.excludeColumnIndexes = writeHolder.excludeColumnIndexes(); + this.includeColumnFieldNames = writeHolder.includeColumnFieldNames(); + this.includeColumnIndexes = writeHolder.includeColumnIndexes(); + } + } + } + + public static void removeThreadLocalCache() { + FIELD_THREAD_LOCAL.remove(); + CLASS_CONTENT_THREAD_LOCAL.remove(); + CONTENT_THREAD_LOCAL.remove(); + } +} + diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java new file mode 100644 index 000000000..6359a99df --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/ConverterUtils.java @@ -0,0 +1,187 @@ +package com.alibaba.excel.util; + +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.converters.ConverterKeyBuild.ConverterKey; +import com.alibaba.excel.converters.NullableObjectConverter; +import com.alibaba.excel.converters.ReadConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.read.metadata.holder.ReadSheetHolder; + +/** + * Converting objects + * + * @author Jiaju Zhuang + **/ +public class ConverterUtils { + public static Class defaultClassGeneric = String.class; + + private ConverterUtils() {} + + /** + * Convert it into a String map + * + * @param cellDataMap + * @param context + * @return + */ + public static Map convertToStringMap(Map> cellDataMap, + AnalysisContext context) { + Map stringMap = MapUtils.newHashMapWithExpectedSize(cellDataMap.size()); + ReadSheetHolder readSheetHolder = context.readSheetHolder(); + int index = 0; + for (Map.Entry> entry : cellDataMap.entrySet()) { + Integer key = entry.getKey(); + ReadCellData cellData = entry.getValue(); + while (index < key) { + stringMap.put(index, null); + index++; + } + index++; + if (cellData.getType() == CellDataTypeEnum.EMPTY) { + stringMap.put(key, null); + continue; + } + Converter converter = + readSheetHolder.converterMap().get(ConverterKeyBuild.buildKey(String.class, cellData.getType())); + if (converter == null) { + throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null, + "Converter not found, convert " + cellData.getType() + " to String"); + } + try { + stringMap.put(key, + (String)(converter.convertToJavaData(new ReadConverterContext<>(cellData, null, context)))); + } catch (Exception e) { + throw new ExcelDataConvertException(context.readRowHolder().getRowIndex(), key, cellData, null, + "Convert data " + cellData + " to String error ", e); + } + } + return stringMap; + } + + /** + * Convert it into a Java object + * + * @param cellData + * @param field + * @param contentProperty + * @param converterMap + * @param context + * @param rowIndex + * @param columnIndex + * @return + */ + public static Object convertToJavaObject(ReadCellData cellData, Field field, + ExcelContentProperty contentProperty, Map> converterMap, AnalysisContext context, + Integer rowIndex, Integer columnIndex) { + return convertToJavaObject(cellData, field, null, null, contentProperty, converterMap, context, rowIndex, + columnIndex); + } + + /** + * Convert it into a Java object + * + * @param cellData + * @param field + * @param clazz + * @param contentProperty + * @param converterMap + * @param context + * @param rowIndex + * @param columnIndex + * @return + */ + public static Object convertToJavaObject(ReadCellData cellData, Field field, Class clazz, + Class classGeneric, ExcelContentProperty contentProperty, Map> converterMap, + AnalysisContext context, Integer rowIndex, Integer columnIndex) { + if (clazz == null) { + if (field == null) { + clazz = String.class; + } else { + clazz = field.getType(); + } + } + if (clazz == CellData.class || clazz == ReadCellData.class) { + ReadCellData cellDataReturn = cellData.clone(); + cellDataReturn.setData( + doConvertToJavaObject(cellData, getClassGeneric(field, classGeneric), contentProperty, + converterMap, context, rowIndex, columnIndex)); + return cellDataReturn; + } + return doConvertToJavaObject(cellData, clazz, contentProperty, converterMap, context, rowIndex, + columnIndex); + } + + private static Class getClassGeneric(Field field, Class classGeneric) { + if (classGeneric != null) { + return classGeneric; + } + if (field == null) { + return defaultClassGeneric; + } + Type type = field.getGenericType(); + if (!(type instanceof ParameterizedType)) { + return defaultClassGeneric; + } + ParameterizedType parameterizedType = (ParameterizedType)type; + Type[] types = parameterizedType.getActualTypeArguments(); + if (types == null || types.length == 0) { + return defaultClassGeneric; + } + Type actualType = types[0]; + if (!(actualType instanceof Class)) { + return defaultClassGeneric; + } + return (Class)actualType; + } + + /** + * @param cellData + * @param clazz + * @param contentProperty + * @param converterMap + * @param context + * @param rowIndex + * @param columnIndex + * @return + */ + private static Object doConvertToJavaObject(ReadCellData cellData, Class clazz, + ExcelContentProperty contentProperty, Map> converterMap, AnalysisContext context, + Integer rowIndex, Integer columnIndex) { + Converter converter = null; + if (contentProperty != null) { + converter = contentProperty.getConverter(); + } + + boolean canNotConverterEmpty = cellData.getType() == CellDataTypeEnum.EMPTY + && !(converter instanceof NullableObjectConverter); + if (canNotConverterEmpty) { + return null; + } + + if (converter == null) { + converter = converterMap.get(ConverterKeyBuild.buildKey(clazz, cellData.getType())); + } + if (converter == null) { + throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty, + "Converter not found, convert " + cellData.getType() + " to " + clazz.getName()); + } + + try { + return converter.convertToJavaData(new ReadConverterContext<>(cellData, contentProperty, context)); + } catch (Exception e) { + throw new ExcelDataConvertException(rowIndex, columnIndex, cellData, contentProperty, + "Convert data " + cellData + " to " + clazz + " error ", e); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java new file mode 100644 index 000000000..035b984c4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/DateUtils.java @@ -0,0 +1,577 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package com.alibaba.excel.util; + +import java.math.BigDecimal; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.regex.Pattern; + +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.util.LocaleUtil; + +/** + * Date utils + * + * @author Jiaju Zhuang + **/ +public class DateUtils { + /** + * Is a cache of dates + */ + private static final ThreadLocal> DATE_THREAD_LOCAL = + new ThreadLocal<>(); + /** + * Is a cache of dates + */ + private static final ThreadLocal> DATE_FORMAT_THREAD_LOCAL = + new ThreadLocal<>(); + + /** + * The following patterns are used in {@link #isADateFormat(Short, String)} + */ + private static final Pattern date_ptrn1 = Pattern.compile("^\\[\\$\\-.*?\\]"); + private static final Pattern date_ptrn2 = Pattern.compile("^\\[[a-zA-Z]+\\]"); + private static final Pattern date_ptrn3a = Pattern.compile("[yYmMdDhHsS]"); + // add "\u5e74 \u6708 \u65e5" for Chinese/Japanese date format:2017 \u5e74 2 \u6708 7 \u65e5 + private static final Pattern date_ptrn3b = + Pattern.compile("^[\\[\\]yYmMdDhHsS\\-T/\u5e74\u6708\u65e5,. :\"\\\\]+0*[ampAMP/]*$"); + // elapsed time patterns: [h],[m] and [s] + private static final Pattern date_ptrn4 = Pattern.compile("^\\[([hH]+|[mM]+|[sS]+)\\]"); + // for format which start with "[DBNum1]" or "[DBNum2]" or "[DBNum3]" could be a Chinese date + private static final Pattern date_ptrn5 = Pattern.compile("^\\[DBNum(1|2|3)\\]"); + // for format which start with "年" or "月" or "日" or "时" or "分" or "秒" could be a Chinese date + private static final Pattern date_ptrn6 = Pattern.compile("(年|月|日|时|分|秒)+"); + + public static final String DATE_FORMAT_10 = "yyyy-MM-dd"; + public static final String DATE_FORMAT_14 = "yyyyMMddHHmmss"; + public static final String DATE_FORMAT_16 = "yyyy-MM-dd HH:mm"; + public static final String DATE_FORMAT_16_FORWARD_SLASH = "yyyy/MM/dd HH:mm"; + public static final String DATE_FORMAT_17 = "yyyyMMdd HH:mm:ss"; + public static final String DATE_FORMAT_19 = "yyyy-MM-dd HH:mm:ss"; + public static final String DATE_FORMAT_19_FORWARD_SLASH = "yyyy/MM/dd HH:mm:ss"; + private static final String MINUS = "-"; + + public static String defaultDateFormat = DATE_FORMAT_19; + + public static String defaultLocalDateFormat = DATE_FORMAT_10; + + public static final int SECONDS_PER_MINUTE = 60; + public static final int MINUTES_PER_HOUR = 60; + public static final int HOURS_PER_DAY = 24; + public static final int SECONDS_PER_DAY = (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE); + + // used to specify that date is invalid + private static final int BAD_DATE = -1; + public static final long DAY_MILLISECONDS = SECONDS_PER_DAY * 1000L; + + private DateUtils() {} + + /** + * convert string to date + * + * @param dateString + * @param dateFormat + * @return + * @throws ParseException + */ + public static Date parseDate(String dateString, String dateFormat) throws ParseException { + if (StringUtils.isEmpty(dateFormat)) { + dateFormat = switchDateFormat(dateString); + } + return getCacheDateFormat(dateFormat).parse(dateString); + } + + /** + * convert string to date + * + * @param dateString + * @param dateFormat + * @param local + * @return + */ + public static LocalDateTime parseLocalDateTime(String dateString, String dateFormat, Locale local) { + if (StringUtils.isEmpty(dateFormat)) { + dateFormat = switchDateFormat(dateString); + } + if (local == null) { + return LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(dateFormat)); + } else { + return LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(dateFormat, local)); + } + } + + /** + * convert string to date + * + * @param dateString + * @param dateFormat + * @param local + * @return + */ + public static LocalDate parseLocalDate(String dateString, String dateFormat, Locale local) { + if (StringUtils.isEmpty(dateFormat)) { + dateFormat = switchDateFormat(dateString); + } + if (local == null) { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern(dateFormat)); + } else { + return LocalDate.parse(dateString, DateTimeFormatter.ofPattern(dateFormat, local)); + } + } + + /** + * convert string to date + * + * @param dateString + * @return + * @throws ParseException + */ + public static Date parseDate(String dateString) throws ParseException { + return parseDate(dateString, switchDateFormat(dateString)); + } + + /** + * switch date format + * + * @param dateString + * @return + */ + public static String switchDateFormat(String dateString) { + int length = dateString.length(); + switch (length) { + case 19: + if (dateString.contains(MINUS)) { + return DATE_FORMAT_19; + } else { + return DATE_FORMAT_19_FORWARD_SLASH; + } + case 16: + if (dateString.contains(MINUS)) { + return DATE_FORMAT_16; + } else { + return DATE_FORMAT_16_FORWARD_SLASH; + } + case 17: + return DATE_FORMAT_17; + case 14: + return DATE_FORMAT_14; + case 10: + return DATE_FORMAT_10; + default: + throw new IllegalArgumentException("can not find date format for:" + dateString); + } + } + + /** + * Format date + *

+ * yyyy-MM-dd HH:mm:ss + * + * @param date + * @return + */ + public static String format(Date date) { + return format(date, null); + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(Date date, String dateFormat) { + if (date == null) { + return null; + } + if (StringUtils.isEmpty(dateFormat)) { + dateFormat = defaultDateFormat; + } + return getCacheDateFormat(dateFormat).format(date); + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(LocalDateTime date, String dateFormat, Locale local) { + if (date == null) { + return null; + } + if (StringUtils.isEmpty(dateFormat)) { + dateFormat = defaultDateFormat; + } + if (local == null) { + return date.format(DateTimeFormatter.ofPattern(dateFormat)); + } else { + return date.format(DateTimeFormatter.ofPattern(dateFormat, local)); + } + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(LocalDate date, String dateFormat) { + return format(date, dateFormat, null); + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(LocalDate date, String dateFormat, Locale local) { + if (date == null) { + return null; + } + if (StringUtils.isEmpty(dateFormat)) { + dateFormat = defaultLocalDateFormat; + } + if (local == null) { + return date.format(DateTimeFormatter.ofPattern(dateFormat)); + } else { + return date.format(DateTimeFormatter.ofPattern(dateFormat, local)); + } + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(LocalDateTime date, String dateFormat) { + return format(date, dateFormat, null); + } + + /** + * Format date + * + * @param date + * @param dateFormat + * @return + */ + public static String format(BigDecimal date, Boolean use1904windowing, String dateFormat) { + if (date == null) { + return null; + } + LocalDateTime localDateTime = DateUtil.getLocalDateTime(date.doubleValue(), + BooleanUtils.isTrue(use1904windowing), true); + return format(localDateTime, dateFormat); + } + + private static DateFormat getCacheDateFormat(String dateFormat) { + Map dateFormatMap = DATE_FORMAT_THREAD_LOCAL.get(); + if (dateFormatMap == null) { + dateFormatMap = new HashMap(); + DATE_FORMAT_THREAD_LOCAL.set(dateFormatMap); + } else { + SimpleDateFormat dateFormatCached = dateFormatMap.get(dateFormat); + if (dateFormatCached != null) { + return dateFormatCached; + } + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); + dateFormatMap.put(dateFormat, simpleDateFormat); + return simpleDateFormat; + } + + /** + * Given an Excel date with either 1900 or 1904 date windowing, + * converts it to a java.util.Date. + * + * Excel Dates and Times are stored without any timezone + * information. If you know (through other means) that your file + * uses a different TimeZone to the system default, you can use + * this version of the getJavaDate() method to handle it. + * + * @param date The Excel date. + * @param use1904windowing true if date uses 1904 windowing, + * or false if using 1900 date windowing. + * @return Java representation of the date, or null if date is not a valid Excel date + */ + public static Date getJavaDate(double date, boolean use1904windowing) { + Calendar calendar = getJavaCalendar(date, use1904windowing, null, true); + return calendar == null ? null : calendar.getTime(); + } + + /** + * Get EXCEL date as Java Calendar with given time zone. + * @param date The Excel date. + * @param use1904windowing true if date uses 1904 windowing, + * or false if using 1900 date windowing. + * @param timeZone The TimeZone to evaluate the date in + * @param roundSeconds round to closest second + * @return Java representation of the date, or null if date is not a valid Excel date + */ + public static Calendar getJavaCalendar(double date, boolean use1904windowing, TimeZone timeZone, boolean roundSeconds) { + if (!isValidExcelDate(date)) { + return null; + } + int wholeDays = (int)Math.floor(date); + int millisecondsInDay = (int)((date - wholeDays) * DAY_MILLISECONDS + 0.5); + Calendar calendar; + if (timeZone != null) { + calendar = LocaleUtil.getLocaleCalendar(timeZone); + } else { + calendar = LocaleUtil.getLocaleCalendar(); // using default time-zone + } + setCalendar(calendar, wholeDays, millisecondsInDay, use1904windowing, roundSeconds); + return calendar; + } + + + public static void setCalendar(Calendar calendar, int wholeDays, + int millisecondsInDay, boolean use1904windowing, boolean roundSeconds) { + int startYear = 1900; + int dayAdjust = -1; // Excel thinks 2/29/1900 is a valid date, which it isn't + if (use1904windowing) { + startYear = 1904; + dayAdjust = 1; // 1904 date windowing uses 1/2/1904 as the first day + } + else if (wholeDays < 61) { + // Date is prior to 3/1/1900, so adjust because Excel thinks 2/29/1900 exists + // If Excel date == 2/29/1900, will become 3/1/1900 in Java representation + dayAdjust = 0; + } + calendar.set(startYear, Calendar.JANUARY, wholeDays + dayAdjust, 0, 0, 0); + calendar.set(Calendar.MILLISECOND, millisecondsInDay); + if (calendar.get(Calendar.MILLISECOND) == 0) { + calendar.clear(Calendar.MILLISECOND); + } + if (roundSeconds) { + // This is different from poi where you need to change 500 to 499 + calendar.add(Calendar.MILLISECOND, 499); + calendar.clear(Calendar.MILLISECOND); + } + } + + + /** + * Given a double, checks if it is a valid Excel date. + * + * @return true if valid + * @param value the double value + */ + + public static boolean isValidExcelDate(double value) + { + return (value > -Double.MIN_VALUE); + } + + + /** + * Given an Excel date with either 1900 or 1904 date windowing, + * converts it to a java.time.LocalDateTime. + * + * Excel Dates and Times are stored without any timezone + * information. If you know (through other means) that your file + * uses a different TimeZone to the system default, you can use + * this version of the getJavaDate() method to handle it. + * + * @param date The Excel date. + * @param use1904windowing true if date uses 1904 windowing, + * or false if using 1900 date windowing. + * @return Java representation of the date, or null if date is not a valid Excel date + */ + public static LocalDateTime getLocalDateTime(double date, boolean use1904windowing) { + return DateUtil.getLocalDateTime(date, use1904windowing, true); + } + + /** + * Given an Excel date with either 1900 or 1904 date windowing, + * converts it to a java.time.LocalDate. + * + * Excel Dates and Times are stored without any timezone + * information. If you know (through other means) that your file + * uses a different TimeZone to the system default, you can use + * this version of the getJavaDate() method to handle it. + * + * @param date The Excel date. + * @param use1904windowing true if date uses 1904 windowing, + * or false if using 1900 date windowing. + * @return Java representation of the date, or null if date is not a valid Excel date + */ + public static LocalDate getLocalDate(double date, boolean use1904windowing) { + LocalDateTime localDateTime = getLocalDateTime(date, use1904windowing); + return localDateTime == null ? null : localDateTime.toLocalDate(); + } + + /** + * Determine if it is a date format. + * + * @param formatIndex + * @param formatString + * @return + */ + public static boolean isADateFormat(Short formatIndex, String formatString) { + if (formatIndex == null) { + return false; + } + Map isDateCache = DATE_THREAD_LOCAL.get(); + if (isDateCache == null) { + isDateCache = MapUtils.newHashMap(); + DATE_THREAD_LOCAL.set(isDateCache); + } else { + Boolean isDatecachedDataList = isDateCache.get(formatIndex); + if (isDatecachedDataList != null) { + return isDatecachedDataList; + } + } + boolean isDate = isADateFormatUncached(formatIndex, formatString); + isDateCache.put(formatIndex, isDate); + return isDate; + } + + /** + * Determine if it is a date format. + * + * @param formatIndex + * @param formatString + * @return + */ + public static boolean isADateFormatUncached(Short formatIndex, String formatString) { + // First up, is this an internal date format? + if (isInternalDateFormat(formatIndex)) { + return true; + } + if (StringUtils.isEmpty(formatString)) { + return false; + } + String fs = formatString; + final int length = fs.length(); + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; i++) { + char c = fs.charAt(i); + if (i < length - 1) { + char nc = fs.charAt(i + 1); + if (c == '\\') { + switch (nc) { + case '-': + case ',': + case '.': + case ' ': + case '\\': + // skip current '\' and continue to the next char + continue; + } + } else if (c == ';' && nc == '@') { + i++; + // skip ";@" duplets + continue; + } + } + sb.append(c); + } + fs = sb.toString(); + + // short-circuit if it indicates elapsed time: [h], [m] or [s] + if (date_ptrn4.matcher(fs).matches()) { + return true; + } + // If it starts with [DBNum1] or [DBNum2] or [DBNum3] + // then it could be a Chinese date + fs = date_ptrn5.matcher(fs).replaceAll(""); + // If it starts with [$-...], then could be a date, but + // who knows what that starting bit is all about + fs = date_ptrn1.matcher(fs).replaceAll(""); + // If it starts with something like [Black] or [Yellow], + // then it could be a date + fs = date_ptrn2.matcher(fs).replaceAll(""); + // You're allowed something like dd/mm/yy;[red]dd/mm/yy + // which would place dates before 1900/1904 in red + // For now, only consider the first one + final int separatorIndex = fs.indexOf(';'); + if (0 < separatorIndex && separatorIndex < fs.length() - 1) { + fs = fs.substring(0, separatorIndex); + } + + // Ensure it has some date letters in it + // (Avoids false positives on the rest of pattern 3) + if (!date_ptrn3a.matcher(fs).find()) { + return false; + } + + // If we get here, check it's only made up, in any case, of: + // y m d h s - \ / , . : [ ] T + // optionally followed by AM/PM + boolean result = date_ptrn3b.matcher(fs).matches(); + if (result) { + return true; + } + result = date_ptrn6.matcher(fs).find(); + return result; + } + + /** + * Given a format ID this will check whether the format represents an internal excel date format or not. + * + * @see #isADateFormat(Short, String) + */ + public static boolean isInternalDateFormat(short format) { + switch (format) { + // Internal Date Formats as described on page 427 in + // Microsoft Excel Dev's Kit... + // 14-22 + case 0x0e: + case 0x0f: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + // 45-47 + case 0x2d: + case 0x2e: + case 0x2f: + return true; + } + return false; + } + + public static void removeThreadLocalCache() { + DATE_THREAD_LOCAL.remove(); + DATE_FORMAT_THREAD_LOCAL.remove(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/EasyExcelTempFileCreationStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/EasyExcelTempFileCreationStrategy.java new file mode 100644 index 000000000..ce4db733e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/EasyExcelTempFileCreationStrategy.java @@ -0,0 +1,136 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package com.alibaba.excel.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileAttribute; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.poi.util.DefaultTempFileCreationStrategy; +import org.apache.poi.util.TempFileCreationStrategy; + +import static org.apache.poi.util.TempFile.JAVA_IO_TMPDIR; + +/** + * In the scenario where `poifiles` are cleaned up, the {@link DefaultTempFileCreationStrategy} will throw a + * java.nio.file.NoSuchFileException. Therefore, it is necessary to verify the existence of the temporary file every + * time it is created. + * + * @author Jiaju Zhuang + */ +public class EasyExcelTempFileCreationStrategy implements TempFileCreationStrategy { + /** + * Name of POI files directory in temporary directory. + */ + public static final String POIFILES = "poifiles"; + + /** + * To use files.deleteOnExit after clean JVM exit, set the -Dpoi.delete.tmp.files.on.exit JVM property + */ + public static final String DELETE_FILES_ON_EXIT = "poi.delete.tmp.files.on.exit"; + + /** + * The directory where the temporary files will be created (null to use the default directory). + */ + private volatile File dir; + + /** + * The lock to make dir initialized only once. + */ + private final Lock dirLock = new ReentrantLock(); + + /** + * Creates the strategy so that it creates the temporary files in the default directory. + * + * @see File#createTempFile(String, String) + */ + public EasyExcelTempFileCreationStrategy() { + this(null); + } + + /** + * Creates the strategy allowing to set the + * + * @param dir The directory where the temporary files will be created (null to use the default + * directory). + * @see Files#createTempFile(Path, String, String, FileAttribute[]) + */ + public EasyExcelTempFileCreationStrategy(File dir) { + this.dir = dir; + } + + private void createPOIFilesDirectory() throws IOException { + // Create our temp dir only once by double-checked locking + // The directory is not deleted, even if it was created by this TempFileCreationStrategy + if (dir == null || !dir.exists()) { + dirLock.lock(); + try { + if (dir == null || !dir.exists()) { + String tmpDir = System.getProperty(JAVA_IO_TMPDIR); + if (tmpDir == null) { + throw new IOException("System's temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + + " jvm property!"); + } + Path dirPath = Paths.get(tmpDir, POIFILES); + dir = Files.createDirectories(dirPath).toFile(); + } + } finally { + dirLock.unlock(); + } + return; + } + } + + @Override + public File createTempFile(String prefix, String suffix) throws IOException { + // Identify and create our temp dir, if needed + createPOIFilesDirectory(); + + // Generate a unique new filename + File newFile = Files.createTempFile(dir.toPath(), prefix, suffix).toFile(); + + // Set the delete on exit flag, but only when explicitly disabled + if (System.getProperty(DELETE_FILES_ON_EXIT) != null) { + newFile.deleteOnExit(); + } + + // All done + return newFile; + } + + /* (non-JavaDoc) Created directory path is /poifiles/prefix0123456789 */ + @Override + public File createTempDirectory(String prefix) throws IOException { + // Identify and create our temp dir, if needed + createPOIFilesDirectory(); + + // Generate a unique new filename + File newDirectory = Files.createTempDirectory(dir.toPath(), prefix).toFile(); + + //this method appears to be only used in tests, so it is probably ok to use deleteOnExit + newDirectory.deleteOnExit(); + + // All done + return newDirectory; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java new file mode 100644 index 000000000..2d5647caf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/FieldUtils.java @@ -0,0 +1,168 @@ +package com.alibaba.excel.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Map; + +import com.alibaba.excel.metadata.NullObject; +import com.alibaba.excel.support.cglib.beans.BeanMap; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class FieldUtils { + + public static Class nullObjectClass = NullObject.class; + + private static final int START_RESOLVE_FIELD_LENGTH = 2; + + public static Class getFieldClass(Map dataMap, String fieldName, Object value) { + if (dataMap instanceof BeanMap) { + Class fieldClass = ((BeanMap)dataMap).getPropertyType(fieldName); + if (fieldClass != null) { + return fieldClass; + } + } + return getFieldClass(value); + } + + public static Class getFieldClass(Object value) { + if (value != null) { + return value.getClass(); + } + return nullObjectClass; + } + + /** + * Parsing the name matching cglib。 + *

+     *     null -> null
+     *     string1 -> string1
+     *     String2 -> string2
+     *     sTring3 -> STring3
+     *     STring4 -> STring4
+     *     STRING5 -> STRING5
+     *     STRing6 -> STRing6
+     * 
+ * + * @param field field + * @return field name. + */ + public static String resolveCglibFieldName(Field field) { + if (field == null) { + return null; + } + String fieldName = field.getName(); + if (StringUtils.isBlank(fieldName) || fieldName.length() < START_RESOLVE_FIELD_LENGTH) { + return fieldName; + } + char firstChar = fieldName.charAt(0); + char secondChar = fieldName.charAt(1); + if (Character.isUpperCase(firstChar) == Character.isUpperCase(secondChar)) { + return fieldName; + } + if (Character.isUpperCase(firstChar)) { + return buildFieldName(Character.toLowerCase(firstChar), fieldName); + } + return buildFieldName(Character.toUpperCase(firstChar), fieldName); + } + + private static String buildFieldName(char firstChar, String fieldName) { + return firstChar + fieldName.substring(1); + } + + /** + * Gets an accessible {@link Field} by name respecting scope. Superclasses/interfaces will be considered. + * + * @param cls the {@link Class} to reflect, must not be {@code null} + * @param fieldName the field name to obtain + * @return the Field object + * @throws IllegalArgumentException if the class is {@code null}, or the field name is blank or empty + */ + public static Field getField(final Class cls, final String fieldName) { + final Field field = getField(cls, fieldName, false); + MemberUtils.setAccessibleWorkaround(field); + return field; + } + + /** + * Gets an accessible {@link Field} by name, breaking scope if requested. Superclasses/interfaces will be + * considered. + * + * @param cls the {@link Class} to reflect, must not be {@code null} + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} method. {@code false} will + * only + * match {@code public} fields. + * @return the Field object + * @throws NullPointerException if the class is {@code null} + * @throws IllegalArgumentException if the field name is blank or empty or is matched at multiple places + * in the inheritance hierarchy + */ + public static Field getField(final Class cls, final String fieldName, final boolean forceAccess) { + Validate.isTrue(cls != null, "The class must not be null"); + Validate.isTrue(StringUtils.isNotBlank(fieldName), "The field name must not be blank/empty"); + // FIXME is this workaround still needed? lang requires Java 6 + // Sun Java 1.3 has a bugged implementation of getField hence we write the + // code ourselves + + // getField() will return the Field object with the declaring class + // set correctly to the class that declares the field. Thus requesting the + // field on a subclass will return the field from the superclass. + // + // priority order for lookup: + // searchclass private/protected/package/public + // superclass protected/package/public + // private/different package blocks access to further superclasses + // implementedinterface public + + // check up the superclass hierarchy + for (Class acls = cls; acls != null; acls = acls.getSuperclass()) { + try { + final Field field = acls.getDeclaredField(fieldName); + // getDeclaredField checks for non-public scopes as well + // and it returns accurate results + if (!Modifier.isPublic(field.getModifiers())) { + if (forceAccess) { + field.setAccessible(true); + } else { + continue; + } + } + return field; + } catch (final NoSuchFieldException ex) { // NOPMD + // ignore + } + } + // check the public interface case. This must be manually searched for + // incase there is a public supersuperclass field hidden by a private/package + // superclass field. + Field match = null; + for (final Class class1 : ClassUtils.getAllInterfaces(cls)) { + try { + final Field test = class1.getField(fieldName); + Validate.isTrue(match == null, "Reference to field %s is ambiguous relative to %s" + + "; a matching field exists on two or more implemented interfaces.", fieldName, cls); + match = test; + } catch (final NoSuchFieldException ex) { // NOPMD + // ignore + } + } + return match; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/FileTypeUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/FileTypeUtils.java new file mode 100644 index 000000000..45de5059e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/FileTypeUtils.java @@ -0,0 +1,59 @@ +package com.alibaba.excel.util; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.metadata.data.ImageData.ImageType; + +/** + * file type utils + * + * @author Jiaju Zhuang + */ +public class FileTypeUtils { + + private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', + 'f'}; + private static final int IMAGE_TYPE_MARK_LENGTH = 28; + + private static final Map FILE_TYPE_MAP; + + /** + * Default image type + */ + public static ImageType defaultImageType = ImageType.PICTURE_TYPE_PNG; + + static { + FILE_TYPE_MAP = new HashMap<>(); + FILE_TYPE_MAP.put("ffd8ff", ImageType.PICTURE_TYPE_JPEG); + FILE_TYPE_MAP.put("89504e47", ImageType.PICTURE_TYPE_PNG); + } + + public static int getImageTypeFormat(byte[] image) { + ImageType imageType = getImageType(image); + if (imageType != null) { + return imageType.getValue(); + } + return defaultImageType.getValue(); + } + + public static ImageType getImageType(byte[] image) { + if (image == null || image.length <= IMAGE_TYPE_MARK_LENGTH) { + return null; + } + byte[] typeMarkByte = new byte[IMAGE_TYPE_MARK_LENGTH]; + System.arraycopy(image, 0, typeMarkByte, 0, IMAGE_TYPE_MARK_LENGTH); + return FILE_TYPE_MAP.get(encodeHexStr(typeMarkByte)); + } + + private static String encodeHexStr(byte[] data) { + final int len = data.length; + final char[] out = new char[len << 1]; + // two characters from the hex value. + for (int i = 0, j = 0; i < len; i++) { + out[j++] = DIGITS[(0xF0 & data[i]) >>> 4]; + out[j++] = DIGITS[0x0F & data[i]]; + } + return new String(out); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/FileUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/FileUtils.java new file mode 100644 index 000000000..f514f6f7a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/FileUtils.java @@ -0,0 +1,225 @@ +package com.alibaba.excel.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.UUID; + +import com.alibaba.excel.exception.ExcelAnalysisException; +import com.alibaba.excel.exception.ExcelCommonException; + +import org.apache.poi.util.TempFile; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class FileUtils { + public static final String POI_FILES = "poifiles"; + public static final String EX_CACHE = "excache"; + /** + * If a server has multiple projects in use at the same time, a directory with the same name will be created under + * the temporary directory, but each project is run by a different user, so there is a permission problem, so each + * project creates a unique UUID as a separate Temporary Files. + */ + private static String tempFilePrefix = + System.getProperty(TempFile.JAVA_IO_TMPDIR) + File.separator + UUID.randomUUID().toString() + File.separator; + /** + * Used to store poi temporary files. + */ + private static String poiFilesPath = tempFilePrefix + POI_FILES + File.separator; + /** + * Used to store easy excel temporary files. + */ + private static String cachePath = tempFilePrefix + EX_CACHE + File.separator; + + private static final int WRITE_BUFF_SIZE = 8192; + + private FileUtils() {} + + static { + // Create a temporary directory in advance + File tempFile = new File(tempFilePrefix); + createDirectory(tempFile); + tempFile.deleteOnExit(); + // Initialize the cache directory + File cacheFile = new File(cachePath); + createDirectory(cacheFile); + } + + /** + * Reads the contents of a file into a byte array. * The file is always closed. + * + * @param file + * @return + * @throws IOException + */ + public static byte[] readFileToByteArray(final File file) throws IOException { + InputStream in = openInputStream(file); + try { + final long fileLength = file.length(); + return fileLength > 0 ? IoUtils.toByteArray(in, (int)fileLength) : IoUtils.toByteArray(in); + } finally { + in.close(); + } + } + + /** + * Opens a {@link FileInputStream} for the specified file, providing better error messages than simply calling + * new FileInputStream(file). + *

+ * At the end of the method either the stream will be successfully opened, or an exception will have been thrown. + *

+ * An exception is thrown if the file does not exist. An exception is thrown if the file object exists but is a + * directory. An exception is thrown if the file exists but cannot be read. + * + * @param file + * @return + * @throws IOException + */ + public static FileInputStream openInputStream(final File file) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + if (file.canRead() == false) { + throw new IOException("File '" + file + "' cannot be read"); + } + } else { + throw new FileNotFoundException("File '" + file + "' does not exist"); + } + return new FileInputStream(file); + } + + /** + * Write inputStream to file + * + * @param file file + * @param inputStream inputStream + */ + public static void writeToFile(File file, InputStream inputStream) { + writeToFile(file, inputStream, true); + } + + /** + * Write inputStream to file + * + * @param file file + * @param inputStream inputStream + * @param closeInputStream closeInputStream + */ + public static void writeToFile(File file, InputStream inputStream, boolean closeInputStream) { + OutputStream outputStream = null; + try { + outputStream = new FileOutputStream(file); + int bytesRead; + byte[] buffer = new byte[WRITE_BUFF_SIZE]; + while ((bytesRead = inputStream.read(buffer, 0, WRITE_BUFF_SIZE)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + } catch (Exception e) { + throw new ExcelAnalysisException("Can not create temporary file!", e); + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + throw new ExcelAnalysisException("Can not close 'outputStream'!", e); + } + } + if (inputStream != null && closeInputStream) { + try { + inputStream.close(); + } catch (IOException e) { + throw new ExcelAnalysisException("Can not close 'inputStream'", e); + } + } + } + } + + public static void createPoiFilesDirectory() { + TempFile.setTempFileCreationStrategy(new EasyExcelTempFileCreationStrategy()); + } + + public static File createCacheTmpFile() { + return createDirectory(new File(cachePath + UUID.randomUUID().toString())); + } + + public static File createTmpFile(String fileName) { + File directory = createDirectory(new File(tempFilePrefix)); + return new File(directory, fileName); + } + + /** + * @param directory + */ + public static File createDirectory(File directory) { + if (!directory.exists() && !directory.mkdirs()) { + throw new ExcelCommonException("Cannot create directory:" + directory.getAbsolutePath()); + } + return directory; + } + + /** + * delete file + * + * @param file + */ + public static void delete(File file) { + if (file.isFile()) { + file.delete(); + return; + } + if (file.isDirectory()) { + File[] childFiles = file.listFiles(); + if (childFiles == null || childFiles.length == 0) { + file.delete(); + return; + } + for (int i = 0; i < childFiles.length; i++) { + delete(childFiles[i]); + } + file.delete(); + } + } + + public static String getTempFilePrefix() { + return tempFilePrefix; + } + + public static void setTempFilePrefix(String tempFilePrefix) { + FileUtils.tempFilePrefix = tempFilePrefix; + } + + public static String getPoiFilesPath() { + return poiFilesPath; + } + + public static void setPoiFilesPath(String poiFilesPath) { + FileUtils.poiFilesPath = poiFilesPath; + } + + public static String getCachePath() { + return cachePath; + } + + public static void setCachePath(String cachePath) { + FileUtils.cachePath = cachePath; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/IntUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/IntUtils.java new file mode 100644 index 000000000..bca73f109 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/IntUtils.java @@ -0,0 +1,36 @@ +package com.alibaba.excel.util; + +/** + * Int utils + * + * @author Jiaju Zhuang + **/ +public class IntUtils { + private IntUtils() {} + + + /** + * The largest power of two that can be represented as an {@code int}. + * + * @since 10.0 + */ + public static final int MAX_POWER_OF_TWO = 1 << (Integer.SIZE - 2); + + /** + * Returns the {@code int} nearest in value to {@code value}. + * + * @param value any {@code long} value + * @return the same value cast to {@code int} if it is in the range of the {@code int} type, + * {@link Integer#MAX_VALUE} if it is too large, or {@link Integer#MIN_VALUE} if it is too + * small + */ + public static int saturatedCast(long value) { + if (value > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + if (value < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } + return (int) value; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/IoUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/IoUtils.java new file mode 100644 index 000000000..d59051179 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/IoUtils.java @@ -0,0 +1,87 @@ +package com.alibaba.excel.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * IO Utils + * + * @author Jiaju Zhuang + */ +public class IoUtils { + public static final int EOF = -1; + /** + * The default buffer size ({@value}) to use for + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + private IoUtils() {} + + /** + * Gets the contents of an InputStream as a byte[]. + * + * @param input + * @return + * @throws IOException + */ + public static byte[] toByteArray(final InputStream input) throws IOException { + final ByteArrayOutputStream output = new ByteArrayOutputStream(); + try { + copy(input, output); + return output.toByteArray(); + } finally { + output.toByteArray(); + } + } + + /** + * Gets the contents of an InputStream as a byte[]. + * + * @param input + * @param size + * @return + * @throws IOException + */ + public static byte[] toByteArray(final InputStream input, final int size) throws IOException { + if (size < 0) { + throw new IllegalArgumentException("Size must be equal or greater than zero: " + size); + } + if (size == 0) { + return new byte[0]; + } + final byte[] data = new byte[size]; + int offset = 0; + int read; + while (offset < size && (read = input.read(data, offset, size - offset)) != EOF) { + offset += read; + } + if (offset != size) { + throw new IOException("Unexpected read size. current: " + offset + ", expected: " + size); + } + return data; + } + + /** + * Copies bytes + * + * @param input + * @param output + * @return + * @throws IOException + */ + public static int copy(final InputStream input, final OutputStream output) throws IOException { + long count = 0; + int n; + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + while (EOF != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + if (count > Integer.MAX_VALUE) { + return -1; + } + return (int)count; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/ListUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/ListUtils.java new file mode 100644 index 000000000..1a3451d8f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/ListUtils.java @@ -0,0 +1,147 @@ +package com.alibaba.excel.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import lombok.NonNull; +import org.apache.commons.compress.utils.Iterators; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class ListUtils { + private ListUtils() {} + + /** + * Creates a mutable, empty {@code ArrayList} instance (for Java 6 and earlier). + * + *

Note for Java 7 and later: this method is now unnecessary and should be treated as + * deprecated. Instead, use the {@code ArrayList} {@linkplain ArrayList#ArrayList() constructor} + * directly, taking advantage of the new "diamond" syntax. + */ + public static ArrayList newArrayList() { + return new ArrayList<>(); + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given elements. + * + */ + public static ArrayList newArrayList(E... elements) { + checkNotNull(elements); + // Avoid integer overflow when a large array is passed in + int capacity = computeArrayListCapacity(elements.length); + ArrayList list = new ArrayList<>(capacity); + Collections.addAll(list, elements); + return list; + } + + + /** + * Creates a mutable {@code ArrayList} instance containing the given elements; a very thin + * shortcut for creating an empty list and then calling {@link Iterators#addAll}. + * + */ + public static ArrayList newArrayList(Iterator elements) { + ArrayList list = newArrayList(); + Iterators.addAll(list, elements); + return list; + } + + /** + * Creates a mutable {@code ArrayList} instance containing the given elements; + * + * + *

Note for Java 7 and later: if {@code elements} is a {@link Collection}, you don't + * need this method. Use the {@code ArrayList} {@linkplain ArrayList#ArrayList(Collection) + * constructor} directly, taking advantage of the new "diamond" + * syntax. + */ + public static ArrayList newArrayList(Iterable elements) { + checkNotNull(elements); // for GWT + // Let ArrayList's sizing logic work, if possible + return (elements instanceof Collection) + ? new ArrayList<>((Collection)elements) + : newArrayList(elements.iterator()); + } + + /** + * Creates an {@code ArrayList} instance backed by an array with the specified initial size; + * simply delegates to {@link ArrayList#ArrayList(int)}. + * + *

Note for Java 7 and later: this method is now unnecessary and should be treated as + * deprecated. Instead, use {@code new }{@link ArrayList#ArrayList(int) ArrayList}{@code <>(int)} + * directly, taking advantage of the new "diamond" syntax. + * (Unlike here, there is no risk of overload ambiguity, since the {@code ArrayList} constructors + * very wisely did not accept varargs.) + * + * @param initialArraySize the exact size of the initial backing array for the returned array list + * ({@code ArrayList} documentation calls this value the "capacity") + * @return a new, empty {@code ArrayList} which is guaranteed not to resize itself unless its size + * reaches {@code initialArraySize + 1} + * @throws IllegalArgumentException if {@code initialArraySize} is negative + */ + public static ArrayList newArrayListWithCapacity(int initialArraySize) { + checkNonnegative(initialArraySize, "initialArraySize"); + return new ArrayList<>(initialArraySize); + } + + /** + * Creates an {@code ArrayList} instance to hold {@code estimatedSize} elements, plus an + * unspecified amount of padding; you almost certainly mean to call {@link + * #newArrayListWithCapacity} (see that method for further advice on usage). + * + *

Note: This method will soon be deprecated. Even in the rare case that you do want + * some amount of padding, it's best if you choose your desired amount explicitly. + * + * @param estimatedSize an estimate of the eventual {@link List#size()} of the new list + * @return a new, empty {@code ArrayList}, sized appropriately to hold the estimated number of + * elements + * @throws IllegalArgumentException if {@code estimatedSize} is negative + */ + public static ArrayList newArrayListWithExpectedSize(int estimatedSize) { + return new ArrayList<>(computeArrayListCapacity(estimatedSize)); + } + + static int computeArrayListCapacity(int arraySize) { + checkNonnegative(arraySize, "arraySize"); + return IntUtils.saturatedCast(5L + arraySize + (arraySize / 10)); + } + + static int checkNonnegative(int value, String name) { + if (value < 0) { + throw new IllegalArgumentException(name + " cannot be negative but was: " + value); + } + return value; + } + + /** + * Ensures that an object reference passed as a parameter to the calling method is not null. + * + * @param reference an object reference + * @return the non-null reference that was validated + * @throws NullPointerException if {@code reference} is null + */ + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } + return reference; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/MapUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/MapUtils.java new file mode 100644 index 000000000..6039fbde4 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/MapUtils.java @@ -0,0 +1,123 @@ +package com.alibaba.excel.util; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.TreeMap; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class MapUtils { + + private MapUtils() {} + + + /** + * Creates a mutable, empty {@code HashMap} instance. + * + *

Note: if mutability is not required, use ImmutableMap.of() instead. + * + *

Note: if {@code K} is an {@code enum} type, use newEnumMap instead. + * + *

Note for Java 7 and later: this method is now unnecessary and should be treated as + * deprecated. Instead, use the {@code HashMap} constructor directly, taking advantage of the new + * "diamond" syntax. + * + * @return a new, empty {@code HashMap} + */ + public static HashMap newHashMap() { + return new HashMap<>(16); + } + + /** + * Creates a mutable, empty {@code TreeMap} instance using the natural ordering of its + * elements. + * + *

Note: if mutability is not required, use ImmutableSortedMap.of() instead. + * + *

Note for Java 7 and later: this method is now unnecessary and should be treated as + * deprecated. Instead, use the {@code TreeMap} constructor directly, taking advantage of the new + * "diamond" syntax. + * + * @return a new, empty {@code TreeMap} + */ + public static TreeMap newTreeMap() { + return new TreeMap<>(); + } + + /** + * Creates a {@code HashMap} instance, with a high enough "initial capacity" that it should + * hold {@code expectedSize} elements without growth. This behavior cannot be broadly guaranteed, + * but it is observed to be true for OpenJDK 1.7. It also can't be guaranteed that the method + * isn't inadvertently oversizing the returned map. + * + * @param expectedSize the number of entries you expect to add to the returned map + * @return a new, empty {@code HashMap} with enough capacity to hold {@code expectedSize} entries + * without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + */ + public static HashMap newHashMapWithExpectedSize(int expectedSize) { + return new HashMap<>(capacity(expectedSize)); + } + + /** + * Creates a mutable, empty, insertion-ordered {@code LinkedHashMap} instance. + * + *

Note: if mutability is not required, use ImmutableMap.of() instead. + * + *

Note for Java 7 and later: this method is now unnecessary and should be treated as + * deprecated. Instead, use the {@code LinkedHashMap} constructor directly, taking advantage of + * the new "diamond" syntax. + * + * @return a new, empty {@code LinkedHashMap} + */ + public static LinkedHashMap newLinkedHashMap() { + return new LinkedHashMap<>(); + } + + /** + * Creates a {@code LinkedHashMap} instance, with a high enough "initial capacity" that it + * should hold {@code expectedSize} elements without growth. This behavior cannot be + * broadly guaranteed, but it is observed to be true for OpenJDK 1.7. It also can't be guaranteed + * that the method isn't inadvertently oversizing the returned map. + * + * @param expectedSize the number of entries you expect to add to the returned map + * @return a new, empty {@code LinkedHashMap} with enough capacity to hold {@code expectedSize} + * entries without resizing + * @throws IllegalArgumentException if {@code expectedSize} is negative + * @since 19.0 + */ + public static LinkedHashMap newLinkedHashMapWithExpectedSize(int expectedSize) { + return new LinkedHashMap<>(capacity(expectedSize)); + } + + /** + * Returns a capacity that is sufficient to keep the map from being resized as long as it grows no + * larger than expectedSize and the load factor is ≥ its default (0.75). + */ + static int capacity(int expectedSize) { + if (expectedSize < 3) { + return expectedSize + 1; + } + if (expectedSize < IntUtils.MAX_POWER_OF_TWO) { + // This is the calculation used in JDK8 to resize when a putAll + // happens; it seems to be the most conservative calculation we + // can make. 0.75 is the default load factor. + return (int)((float)expectedSize / 0.75F + 1.0F); + } + return Integer.MAX_VALUE; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/MemberUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/MemberUtils.java new file mode 100644 index 000000000..ea639f90e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/MemberUtils.java @@ -0,0 +1,65 @@ +package com.alibaba.excel.util; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class MemberUtils { + + private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; + + + /** + * XXX Default access superclass workaround. + * + * When a {@code public} class has a default access superclass with {@code public} members, + * these members are accessible. Calling them from compiled code works fine. + * Unfortunately, on some JVMs, using reflection to invoke these members + * seems to (wrongly) prevent access even when the modifier is {@code public}. + * Calling {@code setAccessible(true)} solves the problem but will only work from + * sufficiently privileged code. Better workarounds would be gratefully + * accepted. + * @param o the AccessibleObject to set as accessible + * @return a boolean indicating whether the accessibility of the object was set to true. + */ + static boolean setAccessibleWorkaround(final AccessibleObject o) { + if (o == null || o.isAccessible()) { + return false; + } + final Member m = (Member) o; + if (!o.isAccessible() && Modifier.isPublic(m.getModifiers()) && isPackageAccess(m.getDeclaringClass().getModifiers())) { + try { + o.setAccessible(true); + return true; + } catch (final SecurityException e) { // NOPMD + // ignore in favor of subsequent IllegalAccessException + } + } + return false; + } + + /** + * Returns whether a given set of modifiers implies package access. + * @param modifiers to test + * @return {@code true} unless {@code package}/{@code protected}/{@code private} modifier detected + */ + static boolean isPackageAccess(final int modifiers) { + return (modifiers & ACCESS_TEST) == 0; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java new file mode 100644 index 000000000..83d7a23f2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/NumberDataFormatterUtils.java @@ -0,0 +1,63 @@ +package com.alibaba.excel.util; + +import java.math.BigDecimal; +import java.util.Locale; + +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.format.DataFormatter; + +/** + * Convert number data, including date. + * + * @author Jiaju Zhuang + **/ +public class NumberDataFormatterUtils { + + /** + * Cache DataFormatter. + */ + private static final ThreadLocal DATA_FORMATTER_THREAD_LOCAL = new ThreadLocal(); + + /** + * Format number data. + * + * @param data + * @param dataFormat Not null. + * @param dataFormatString + * @param globalConfiguration + * @return + */ + public static String format(BigDecimal data, Short dataFormat, String dataFormatString, + GlobalConfiguration globalConfiguration) { + if (globalConfiguration == null) { + return format(data, dataFormat, dataFormatString, null, null, null); + } + return format(data, dataFormat, dataFormatString, globalConfiguration.getUse1904windowing(), + globalConfiguration.getLocale(), globalConfiguration.getUseScientificFormat()); + } + + /** + * Format number data. + * + * @param data + * @param dataFormat Not null. + * @param dataFormatString + * @param use1904windowing + * @param locale + * @param useScientificFormat + * @return + */ + public static String format(BigDecimal data, Short dataFormat, String dataFormatString, Boolean use1904windowing, + Locale locale, Boolean useScientificFormat) { + DataFormatter dataFormatter = DATA_FORMATTER_THREAD_LOCAL.get(); + if (dataFormatter == null) { + dataFormatter = new DataFormatter(use1904windowing, locale, useScientificFormat); + DATA_FORMATTER_THREAD_LOCAL.set(dataFormatter); + } + return dataFormatter.format(data, dataFormat, dataFormatString); + } + + public static void removeThreadLocalCache() { + DATA_FORMATTER_THREAD_LOCAL.remove(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/NumberUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/NumberUtils.java new file mode 100644 index 000000000..4646c3969 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/NumberUtils.java @@ -0,0 +1,190 @@ +package com.alibaba.excel.util; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.ParseException; + +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +/** + * Number utils + * + * @author Jiaju Zhuang + */ +public class NumberUtils { + private NumberUtils() {} + + /** + * format + * + * @param num + * @param contentProperty + * @return + */ + public static String format(Number num, ExcelContentProperty contentProperty) { + if (contentProperty == null || contentProperty.getNumberFormatProperty() == null + || StringUtils.isEmpty(contentProperty.getNumberFormatProperty().getFormat())) { + if (num instanceof BigDecimal) { + return ((BigDecimal)num).toPlainString(); + } else { + return num.toString(); + } + } + String format = contentProperty.getNumberFormatProperty().getFormat(); + RoundingMode roundingMode = contentProperty.getNumberFormatProperty().getRoundingMode(); + DecimalFormat decimalFormat = new DecimalFormat(format); + decimalFormat.setRoundingMode(roundingMode); + return decimalFormat.format(num); + } + + /** + * format + * + * @param num + * @param contentProperty + * @return + */ + public static WriteCellData formatToCellDataString(Number num, ExcelContentProperty contentProperty) { + return new WriteCellData<>(format(num, contentProperty)); + } + + /** + * format + * + * @param num + * @param contentProperty + * @return + */ + public static WriteCellData formatToCellData(Number num, ExcelContentProperty contentProperty) { + WriteCellData cellData = new WriteCellData<>(new BigDecimal(num.toString())); + if (contentProperty != null && contentProperty.getNumberFormatProperty() != null + && StringUtils.isNotBlank(contentProperty.getNumberFormatProperty().getFormat())) { + WorkBookUtil.fillDataFormat(cellData, contentProperty.getNumberFormatProperty().getFormat(), null); + } + return cellData; + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + */ + public static Short parseShort(String string, ExcelContentProperty contentProperty) throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string).shortValue(); + } + return parse(string, contentProperty).shortValue(); + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + */ + public static Long parseLong(String string, ExcelContentProperty contentProperty) throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string).longValue(); + } + return parse(string, contentProperty).longValue(); + } + + /** + * parse Integer from string + * + * @param string An integer read in string format + * @param contentProperty Properties of the content read in + * @return An integer converted from a string + */ + public static Integer parseInteger(String string, ExcelContentProperty contentProperty) throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string).intValue(); + } + return parse(string, contentProperty).intValue(); + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + */ + public static Float parseFloat(String string, ExcelContentProperty contentProperty) throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string).floatValue(); + } + return parse(string, contentProperty).floatValue(); + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + */ + public static BigDecimal parseBigDecimal(String string, ExcelContentProperty contentProperty) + throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string); + } + return new BigDecimal(parse(string, contentProperty).toString()); + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + */ + public static Byte parseByte(String string, ExcelContentProperty contentProperty) throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string).byteValue(); + } + return parse(string, contentProperty).byteValue(); + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + */ + public static Double parseDouble(String string, ExcelContentProperty contentProperty) throws ParseException { + if (!hasFormat(contentProperty)) { + return new BigDecimal(string).doubleValue(); + } + return parse(string, contentProperty).doubleValue(); + } + + private static boolean hasFormat(ExcelContentProperty contentProperty) { + return contentProperty != null && contentProperty.getNumberFormatProperty() != null + && !StringUtils.isEmpty(contentProperty.getNumberFormatProperty().getFormat()); + } + + /** + * parse + * + * @param string + * @param contentProperty + * @return + * @throws ParseException + */ + private static Number parse(String string, ExcelContentProperty contentProperty) throws ParseException { + String format = contentProperty.getNumberFormatProperty().getFormat(); + RoundingMode roundingMode = contentProperty.getNumberFormatProperty().getRoundingMode(); + DecimalFormat decimalFormat = new DecimalFormat(format); + decimalFormat.setRoundingMode(roundingMode); + decimalFormat.setParseBigDecimal(true); + return decimalFormat.parse(string); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/PoiUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/PoiUtils.java new file mode 100644 index 000000000..0b70a487d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/PoiUtils.java @@ -0,0 +1,49 @@ +package com.alibaba.excel.util; + +import com.alibaba.excel.exception.ExcelRuntimeException; + +import org.apache.poi.hssf.record.RowRecord; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.util.BitField; +import org.apache.poi.util.BitFieldFactory; +import org.apache.poi.xssf.usermodel.XSSFRow; + +import java.lang.reflect.Field; + +/** + * utils + * + * @author Jiaju Zhuang + */ +public class PoiUtils { + + /** + * Whether to customize the height + */ + public static final BitField CUSTOM_HEIGHT = BitFieldFactory.getInstance(0x640); + + private static final Field ROW_RECORD_FIELD = FieldUtils.getField(HSSFRow.class, "row", true); + + /** + * Whether to customize the height + * + * @param row row + * @return + */ + public static boolean customHeight(Row row) { + if (row instanceof XSSFRow) { + XSSFRow xssfRow = (XSSFRow)row; + return xssfRow.getCTRow().getCustomHeight(); + } + if (row instanceof HSSFRow) { + HSSFRow hssfRow = (HSSFRow)row; + try { + RowRecord record = (RowRecord)ROW_RECORD_FIELD.get(hssfRow); + return CUSTOM_HEIGHT.getValue(record.getOptionFlags()) == 1; + } catch (IllegalAccessException ignore) { + } + } + return false; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/PositionUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/PositionUtils.java new file mode 100644 index 000000000..fe7e5d4ed --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/PositionUtils.java @@ -0,0 +1,64 @@ +package com.alibaba.excel.util; + +import java.util.regex.Pattern; + +/** + * @author jipengfei + */ +public class PositionUtils { + + private static final Pattern CELL_REF_PATTERN = Pattern.compile("(\\$?[A-Z]+)?" + "(\\$?[0-9]+)?", + Pattern.CASE_INSENSITIVE); + private static final char SHEET_NAME_DELIMITER = '!'; + private static final char REDUNDANT_CHARACTERS = '$'; + + private PositionUtils() {} + + public static int getRowByRowTagt(String rowTagt, Integer before) { + int row; + if (rowTagt != null) { + row = Integer.parseInt(rowTagt) - 1; + return row; + } else { + if (before == null) { + before = -1; + } + return before + 1; + } + } + + public static int getRow(String currentCellIndex) { + if (currentCellIndex == null) { + return -1; + } + int firstNumber = currentCellIndex.length() - 1; + for (; firstNumber >= 0; firstNumber--) { + char c = currentCellIndex.charAt(firstNumber); + if (c < '0' || c > '9') { + break; + } + } + return Integer.parseUnsignedInt(currentCellIndex.substring(firstNumber + 1)) - 1; + } + + public static int getCol(String currentCellIndex, Integer before) { + if (currentCellIndex == null) { + if (before == null) { + before = -1; + } + return before + 1; + } + int firstNumber = currentCellIndex.charAt(0) == REDUNDANT_CHARACTERS ? 1 : 0; + int col = 0; + for (; firstNumber < currentCellIndex.length(); firstNumber++) { + char c = currentCellIndex.charAt(firstNumber); + boolean isNotLetter = c == REDUNDANT_CHARACTERS || (c >= '0' && c <= '9'); + if (isNotLetter) { + break; + } + col = col * 26 + Character.toUpperCase(c) - 'A' + 1; + } + return col - 1; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/SheetUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/SheetUtils.java new file mode 100644 index 000000000..12886c85b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/SheetUtils.java @@ -0,0 +1,66 @@ +package com.alibaba.excel.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.ReadWorkbookHolder; + +/** + * Sheet utils + * + * @author Jiaju Zhuang + */ +public class SheetUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SheetUtils.class); + + private SheetUtils() {} + + /** + * Match the parameters to the actual sheet + * + * @param readSheet actual sheet + * @param analysisContext + * @return + */ + public static ReadSheet match(ReadSheet readSheet, AnalysisContext analysisContext) { + ReadWorkbookHolder readWorkbookHolder = analysisContext.readWorkbookHolder(); + if (readWorkbookHolder.getReadAll()) { + return readSheet; + } + for (ReadSheet parameterReadSheet : readWorkbookHolder.getParameterSheetDataList()) { + if (parameterReadSheet == null) { + continue; + } + if (parameterReadSheet.getSheetNo() == null && parameterReadSheet.getSheetName() == null) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("The first is read by default."); + } + parameterReadSheet.setSheetNo(0); + } + boolean match = (parameterReadSheet.getSheetNo() != null + && parameterReadSheet.getSheetNo().equals(readSheet.getSheetNo())); + if (!match) { + String parameterSheetName = parameterReadSheet.getSheetName(); + if (!StringUtils.isEmpty(parameterSheetName)) { + boolean autoTrim = (parameterReadSheet.getAutoTrim() != null && parameterReadSheet.getAutoTrim()) + || (parameterReadSheet.getAutoTrim() == null + && analysisContext.readWorkbookHolder().getGlobalConfiguration().getAutoTrim()); + String sheetName = readSheet.getSheetName(); + if (autoTrim) { + parameterSheetName = parameterSheetName.trim(); + sheetName = sheetName.trim(); + } + match = parameterSheetName.equals(sheetName); + } + } + if (match) { + readSheet.copyBasicParameter(parameterReadSheet); + return readSheet; + } + } + return null; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/StringUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/StringUtils.java new file mode 100644 index 000000000..b076370cd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/StringUtils.java @@ -0,0 +1,242 @@ +package com.alibaba.excel.util; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class StringUtils { + private StringUtils() {} + + /** + * A String for a space character. + */ + public static final String SPACE = " "; + + /** + * The empty String {@code ""}. + */ + public static final String EMPTY = ""; + + /** + *

Checks if a CharSequence is empty ("") or null.

+ * + *
+     * StringUtils.isEmpty(null)      = true
+     * StringUtils.isEmpty("")        = true
+     * StringUtils.isEmpty(" ")       = false
+     * StringUtils.isEmpty("bob")     = false
+     * StringUtils.isEmpty("  bob  ") = false
+     * 
+ * + *

NOTE: This method changed in Lang version 2.0. + * It no longer trims the CharSequence. + * That functionality is available in isBlank().

+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is empty or null + */ + public static boolean isEmpty(final CharSequence cs) { + return cs == null || cs.length() == 0; + } + + /** + *

Checks if a CharSequence is empty (""), null or whitespace only.

+ * + *

Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.isBlank(null)      = true
+     * StringUtils.isBlank("")        = true
+     * StringUtils.isBlank(" ")       = true
+     * StringUtils.isBlank("bob")     = false
+     * StringUtils.isBlank("  bob  ") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is null, empty or whitespace only + */ + public static boolean isBlank(final CharSequence cs) { + int strLen; + if (cs == null || (strLen = cs.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (!Character.isWhitespace(cs.charAt(i))) { + return false; + } + } + return true; + } + + /** + *

Checks if a CharSequence is not empty (""), not null and not whitespace only.

+ * + *

Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.isNotBlank(null)      = false
+     * StringUtils.isNotBlank("")        = false
+     * StringUtils.isNotBlank(" ")       = false
+     * StringUtils.isNotBlank("bob")     = true
+     * StringUtils.isNotBlank("  bob  ") = true
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is + * not empty and not null and not whitespace only + * @since 2.0 + * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) + */ + public static boolean isNotBlank(final CharSequence cs) { + return !isBlank(cs); + } + + /** + *

Compares two CharSequences, returning {@code true} if they represent + * equal sequences of characters.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.

+ * + *
+     * StringUtils.equals(null, null)   = true
+     * StringUtils.equals(null, "abc")  = false
+     * StringUtils.equals("abc", null)  = false
+     * StringUtils.equals("abc", "abc") = true
+     * StringUtils.equals("abc", "ABC") = false
+     * 
+ * + * @param cs1 the first CharSequence, may be {@code null} + * @param cs2 the second CharSequence, may be {@code null} + * @return {@code true} if the CharSequences are equal (case-sensitive), or both {@code null} + * @see Object#equals(Object) + * @since 3.0 Changed signature from equals(String, String) to equals(CharSequence, CharSequence) + */ + public static boolean equals(final CharSequence cs1, final CharSequence cs2) { + if (cs1 == cs2) { + return true; + } + if (cs1 == null || cs2 == null) { + return false; + } + if (cs1.length() != cs2.length()) { + return false; + } + if (cs1 instanceof String && cs2 instanceof String) { + return cs1.equals(cs2); + } + return regionMatches(cs1, false, 0, cs2, 0, cs1.length()); + } + + /** + * Green implementation of regionMatches. + * + * @param cs the {@code CharSequence} to be processed + * @param ignoreCase whether or not to be case insensitive + * @param thisStart the index to start on the {@code cs} CharSequence + * @param substring the {@code CharSequence} to be looked for + * @param start the index to start on the {@code substring} CharSequence + * @param length character length of the region + * @return whether the region matched + */ + public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, + final CharSequence substring, final int start, final int length) { + if (cs instanceof String && substring instanceof String) { + return ((String)cs).regionMatches(ignoreCase, thisStart, (String)substring, start, length); + } + int index1 = thisStart; + int index2 = start; + int tmpLen = length; + + // Extract these first so we detect NPEs the same as the java.lang.String version + final int srcLen = cs.length() - thisStart; + final int otherLen = substring.length() - start; + + // Check for invalid parameters + if (thisStart < 0 || start < 0 || length < 0) { + return false; + } + + // Check that the regions are long enough + if (srcLen < length || otherLen < length) { + return false; + } + + while (tmpLen-- > 0) { + final char c1 = cs.charAt(index1++); + final char c2 = substring.charAt(index2++); + + if (c1 == c2) { + continue; + } + + if (!ignoreCase) { + return false; + } + + // The same check as in String.regionMatches(): + if (Character.toUpperCase(c1) != Character.toUpperCase(c2) + && Character.toLowerCase(c1) != Character.toLowerCase(c2)) { + return false; + } + } + + return true; + } + + /** + *

Checks if the CharSequence contains only Unicode digits. + * A decimal point is not a Unicode digit and returns false.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.

+ * + *

Note that the method does not allow for a leading sign, either positive or negative. + * Also, if a String passes the numeric test, it may still generate a NumberFormatException + * when parsed by Integer.parseInt or Long.parseLong, e.g. if the value is outside the range + * for int or long respectively.

+ * + *
+     * StringUtils.isNumeric(null)   = false
+     * StringUtils.isNumeric("")     = false
+     * StringUtils.isNumeric("  ")   = false
+     * StringUtils.isNumeric("123")  = true
+     * StringUtils.isNumeric("\u0967\u0968\u0969")  = true
+     * StringUtils.isNumeric("12 3") = false
+     * StringUtils.isNumeric("ab2c") = false
+     * StringUtils.isNumeric("12-3") = false
+     * StringUtils.isNumeric("12.3") = false
+     * StringUtils.isNumeric("-123") = false
+     * StringUtils.isNumeric("+123") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains digits, and is non-null + * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isNumeric(final CharSequence cs) { + if (isEmpty(cs)) { + return false; + } + final int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (!Character.isDigit(cs.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/StyleUtil.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/StyleUtil.java new file mode 100644 index 000000000..e8a503d71 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/StyleUtil.java @@ -0,0 +1,272 @@ +package com.alibaba.excel.util; + +import java.util.Optional; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.data.HyperlinkData; +import com.alibaba.excel.metadata.data.RichTextStringData; +import com.alibaba.excel.metadata.data.RichTextStringData.IntervalFont; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.common.usermodel.HyperlinkType; +import org.apache.poi.hssf.usermodel.HSSFFont; +import org.apache.poi.hssf.usermodel.HSSFRichTextString; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.RichTextString; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.util.Units; +import org.apache.poi.xssf.usermodel.XSSFColor; +import org.apache.poi.xssf.usermodel.XSSFFont; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; + +/** + * @author jipengfei + */ +@Slf4j +public class StyleUtil { + + private StyleUtil() {} + + /** + * Build cell style + * + * @param workbook + * @param originCellStyle + * @param writeCellStyle + * @return + */ + public static CellStyle buildCellStyle(Workbook workbook, CellStyle originCellStyle, + WriteCellStyle writeCellStyle) { + CellStyle cellStyle = workbook.createCellStyle(); + if (originCellStyle != null) { + cellStyle.cloneStyleFrom(originCellStyle); + } + if (writeCellStyle == null) { + return cellStyle; + } + buildCellStyle(cellStyle, writeCellStyle); + return cellStyle; + } + + private static void buildCellStyle(CellStyle cellStyle, WriteCellStyle writeCellStyle) { + if (writeCellStyle.getHidden() != null) { + cellStyle.setHidden(writeCellStyle.getHidden()); + } + if (writeCellStyle.getLocked() != null) { + cellStyle.setLocked(writeCellStyle.getLocked()); + } + if (writeCellStyle.getQuotePrefix() != null) { + cellStyle.setQuotePrefixed(writeCellStyle.getQuotePrefix()); + } + if (writeCellStyle.getHorizontalAlignment() != null) { + cellStyle.setAlignment(writeCellStyle.getHorizontalAlignment()); + } + if (writeCellStyle.getWrapped() != null) { + cellStyle.setWrapText(writeCellStyle.getWrapped()); + } + if (writeCellStyle.getVerticalAlignment() != null) { + cellStyle.setVerticalAlignment(writeCellStyle.getVerticalAlignment()); + } + if (writeCellStyle.getRotation() != null) { + cellStyle.setRotation(writeCellStyle.getRotation()); + } + if (writeCellStyle.getIndent() != null) { + cellStyle.setIndention(writeCellStyle.getIndent()); + } + if (writeCellStyle.getBorderLeft() != null) { + cellStyle.setBorderLeft(writeCellStyle.getBorderLeft()); + } + if (writeCellStyle.getBorderRight() != null) { + cellStyle.setBorderRight(writeCellStyle.getBorderRight()); + } + if (writeCellStyle.getBorderTop() != null) { + cellStyle.setBorderTop(writeCellStyle.getBorderTop()); + } + if (writeCellStyle.getBorderBottom() != null) { + cellStyle.setBorderBottom(writeCellStyle.getBorderBottom()); + } + if (writeCellStyle.getLeftBorderColor() != null) { + cellStyle.setLeftBorderColor(writeCellStyle.getLeftBorderColor()); + } + if (writeCellStyle.getRightBorderColor() != null) { + cellStyle.setRightBorderColor(writeCellStyle.getRightBorderColor()); + } + if (writeCellStyle.getTopBorderColor() != null) { + cellStyle.setTopBorderColor(writeCellStyle.getTopBorderColor()); + } + if (writeCellStyle.getBottomBorderColor() != null) { + cellStyle.setBottomBorderColor(writeCellStyle.getBottomBorderColor()); + } + if (writeCellStyle.getFillPatternType() != null) { + cellStyle.setFillPattern(writeCellStyle.getFillPatternType()); + } + if (writeCellStyle.getFillBackgroundColor() != null) { + cellStyle.setFillBackgroundColor(writeCellStyle.getFillBackgroundColor()); + } + if (writeCellStyle.getFillForegroundColor() != null) { + cellStyle.setFillForegroundColor(writeCellStyle.getFillForegroundColor()); + } + if (writeCellStyle.getShrinkToFit() != null) { + cellStyle.setShrinkToFit(writeCellStyle.getShrinkToFit()); + } + } + + public static short buildDataFormat(Workbook workbook, DataFormatData dataFormatData) { + if (dataFormatData == null) { + return BuiltinFormats.GENERAL; + } + if (dataFormatData.getIndex() != null && dataFormatData.getIndex() >= 0) { + return dataFormatData.getIndex(); + } + if (StringUtils.isNotBlank(dataFormatData.getFormat())) { + if (log.isDebugEnabled()) { + log.info("create new data fromat:{}", dataFormatData); + } + DataFormat dataFormatCreate = workbook.createDataFormat(); + return dataFormatCreate.getFormat(dataFormatData.getFormat()); + } + return BuiltinFormats.GENERAL; + } + + public static Font buildFont(Workbook workbook, Font originFont, WriteFont writeFont) { + if (log.isDebugEnabled()) { + log.info("create new font:{},{}", writeFont, originFont); + } + if (writeFont == null && originFont == null) { + return null; + } + Font font = createFont(workbook, originFont, writeFont); + if (writeFont == null || font == null) { + return font; + } + if (writeFont.getFontName() != null) { + font.setFontName(writeFont.getFontName()); + } + if (writeFont.getFontHeightInPoints() != null) { + font.setFontHeightInPoints(writeFont.getFontHeightInPoints()); + } + if (writeFont.getItalic() != null) { + font.setItalic(writeFont.getItalic()); + } + if (writeFont.getStrikeout() != null) { + font.setStrikeout(writeFont.getStrikeout()); + } + if (writeFont.getColor() != null) { + font.setColor(writeFont.getColor()); + } + if (writeFont.getTypeOffset() != null) { + font.setTypeOffset(writeFont.getTypeOffset()); + } + if (writeFont.getUnderline() != null) { + font.setUnderline(writeFont.getUnderline()); + } + if (writeFont.getCharset() != null) { + font.setCharSet(writeFont.getCharset()); + } + if (writeFont.getBold() != null) { + font.setBold(writeFont.getBold()); + } + return font; + } + + private static Font createFont(Workbook workbook, Font originFont, WriteFont writeFont) { + Font font = workbook.createFont(); + if (originFont == null) { + return font; + } + if (originFont instanceof XSSFFont) { + XSSFFont xssfFont = (XSSFFont)font; + XSSFFont xssfOriginFont = ((XSSFFont)originFont); + xssfFont.setFontName(xssfOriginFont.getFontName()); + xssfFont.setFontHeightInPoints(xssfOriginFont.getFontHeightInPoints()); + xssfFont.setItalic(xssfOriginFont.getItalic()); + xssfFont.setStrikeout(xssfOriginFont.getStrikeout()); + // Colors cannot be overwritten + if (writeFont == null || writeFont.getColor() == null) { + xssfFont.setColor(Optional.of(xssfOriginFont) + .map(XSSFFont::getXSSFColor) + .map(XSSFColor::getRGB) + .map(rgb -> new XSSFColor(rgb, null)) + .orElse(null)); + } + xssfFont.setTypeOffset(xssfOriginFont.getTypeOffset()); + xssfFont.setUnderline(xssfOriginFont.getUnderline()); + xssfFont.setCharSet(xssfOriginFont.getCharSet()); + xssfFont.setBold(xssfOriginFont.getBold()); + return xssfFont; + } else if (originFont instanceof HSSFFont) { + HSSFFont hssfFont = (HSSFFont)font; + HSSFFont hssfOriginFont = (HSSFFont)originFont; + hssfFont.setFontName(hssfOriginFont.getFontName()); + hssfFont.setFontHeightInPoints(hssfOriginFont.getFontHeightInPoints()); + hssfFont.setItalic(hssfOriginFont.getItalic()); + hssfFont.setStrikeout(hssfOriginFont.getStrikeout()); + hssfFont.setColor(hssfOriginFont.getColor()); + hssfFont.setTypeOffset(hssfOriginFont.getTypeOffset()); + hssfFont.setUnderline(hssfOriginFont.getUnderline()); + hssfFont.setCharSet(hssfOriginFont.getCharSet()); + hssfFont.setBold(hssfOriginFont.getBold()); + return hssfFont; + } + return font; + } + + public static RichTextString buildRichTextString(WriteWorkbookHolder writeWorkbookHolder, + RichTextStringData richTextStringData) { + if (richTextStringData == null) { + return null; + } + RichTextString richTextString; + if (writeWorkbookHolder.getExcelType() == ExcelTypeEnum.XLSX) { + richTextString = new XSSFRichTextString(richTextStringData.getTextString()); + } else { + richTextString = new HSSFRichTextString(richTextStringData.getTextString()); + } + if (richTextStringData.getWriteFont() != null) { + richTextString.applyFont(writeWorkbookHolder.createFont(richTextStringData.getWriteFont(), null, true)); + } + if (CollectionUtils.isNotEmpty(richTextStringData.getIntervalFontList())) { + for (IntervalFont intervalFont : richTextStringData.getIntervalFontList()) { + richTextString.applyFont(intervalFont.getStartIndex(), intervalFont.getEndIndex(), + writeWorkbookHolder.createFont(intervalFont.getWriteFont(), null, true)); + } + } + return richTextString; + } + + public static HyperlinkType getHyperlinkType(HyperlinkData.HyperlinkType hyperlinkType) { + if (hyperlinkType == null) { + return HyperlinkType.NONE; + } + return hyperlinkType.getValue(); + } + + public static int getCoordinate(Integer coordinate) { + if (coordinate == null) { + return 0; + } + return Units.toEMU(coordinate); + } + + public static int getCellCoordinate(Integer currentCoordinate, Integer absoluteCoordinate, + Integer relativeCoordinate) { + if (absoluteCoordinate != null && absoluteCoordinate > 0) { + return absoluteCoordinate; + } + if (relativeCoordinate != null) { + return currentCoordinate + relativeCoordinate; + } + return currentCoordinate; + } + +} + + diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/Validate.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/Validate.java new file mode 100644 index 000000000..d407fc125 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/Validate.java @@ -0,0 +1,162 @@ +package com.alibaba.excel.util; + +import java.util.Objects; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Apache Software Foundation (ASF) + */ +public class Validate { + + private static final String DEFAULT_IS_TRUE_EX_MESSAGE = "The validated expression is false"; + private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null"; + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);
+ * + *

For performance reasons, the long value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression, final String message, final long value) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, Long.valueOf(value))); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);
+ * + *

For performance reasons, the double value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression, final String message, final double value) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, Double.valueOf(value))); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max);
+     * Validate.isTrue(myObject.isOk(), "The object is not okay");
+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + */ + public static void isTrue(final boolean expression, final String message, final Object... values) { + if (!expression) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.isTrue(i > 0);
+     * Validate.isTrue(myObject.isOk());
+ * + *

The message of the exception is "The validated expression is + * false".

+ * + * @param expression the boolean expression to check + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(final boolean expression) { + if (!expression) { + throw new IllegalArgumentException(DEFAULT_IS_TRUE_EX_MESSAGE); + } + } + + + /** + *

Validate that the specified argument is not {@code null}; + * otherwise throwing an exception. + * + *

Validate.notNull(myObject, "The object must not be null");
+ * + *

The message of the exception is "The validated object is + * null".

+ * + * @param the object type + * @param object the object to check + * @return the validated object (never {@code null} for method chaining) + * @throws NullPointerException if the object is {@code null} + * @see #notNull(Object, String, Object...) + */ + public static T notNull(final T object) { + return notNull(object, DEFAULT_IS_NULL_EX_MESSAGE); + } + + /** + *

Validate that the specified argument is not {@code null}; + * otherwise throwing an exception with the specified message. + * + *

Validate.notNull(myObject, "The object must not be null");
+ * + * @param the object type + * @param object the object to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message + * @return the validated object (never {@code null} for method chaining) + * @throws NullPointerException if the object is {@code null} + * @see #notNull(Object) + */ + public static T notNull(final T object, final String message, final Object... values) { + return Objects.requireNonNull(object, () -> String.format(message, values)); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/WorkBookUtil.java new file mode 100644 index 000000000..8ed49e6a0 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/WorkBookUtil.java @@ -0,0 +1,130 @@ +package com.alibaba.excel.util; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import com.alibaba.excel.metadata.csv.CsvWorkbook; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; + +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +/** + * @author jipengfei + */ +public class WorkBookUtil { + + private WorkBookUtil() {} + + public static void createWorkBook(WriteWorkbookHolder writeWorkbookHolder) throws IOException { + switch (writeWorkbookHolder.getExcelType()) { + case XLSX: + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(writeWorkbookHolder.getTempTemplateInputStream()); + writeWorkbookHolder.setCachedWorkbook(xssfWorkbook); + if (writeWorkbookHolder.getInMemory()) { + writeWorkbookHolder.setWorkbook(xssfWorkbook); + } else { + writeWorkbookHolder.setWorkbook(new SXSSFWorkbook(xssfWorkbook)); + } + return; + } + Workbook workbook; + if (writeWorkbookHolder.getInMemory()) { + workbook = new XSSFWorkbook(); + } else { + workbook = new SXSSFWorkbook(); + } + writeWorkbookHolder.setCachedWorkbook(workbook); + writeWorkbookHolder.setWorkbook(workbook); + return; + case XLS: + HSSFWorkbook hssfWorkbook; + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + hssfWorkbook = new HSSFWorkbook( + new POIFSFileSystem(writeWorkbookHolder.getTempTemplateInputStream())); + } else { + hssfWorkbook = new HSSFWorkbook(); + } + writeWorkbookHolder.setCachedWorkbook(hssfWorkbook); + writeWorkbookHolder.setWorkbook(hssfWorkbook); + if (writeWorkbookHolder.getPassword() != null) { + Biff8EncryptionKey.setCurrentUserPassword(writeWorkbookHolder.getPassword()); + hssfWorkbook.writeProtectWorkbook(writeWorkbookHolder.getPassword(), StringUtils.EMPTY); + } + return; + case CSV: + CsvWorkbook csvWorkbook = new CsvWorkbook(new PrintWriter( + new OutputStreamWriter(writeWorkbookHolder.getOutputStream(), writeWorkbookHolder.getCharset())), + writeWorkbookHolder.getGlobalConfiguration().getLocale(), + writeWorkbookHolder.getGlobalConfiguration().getUse1904windowing(), + writeWorkbookHolder.getGlobalConfiguration().getUseScientificFormat(), + writeWorkbookHolder.getCharset(), + writeWorkbookHolder.getWithBom()); + writeWorkbookHolder.setCachedWorkbook(csvWorkbook); + writeWorkbookHolder.setWorkbook(csvWorkbook); + return; + default: + throw new UnsupportedOperationException("Wrong excel type."); + } + + } + + public static Sheet createSheet(Workbook workbook, String sheetName) { + return workbook.createSheet(sheetName); + } + + public static Row createRow(Sheet sheet, int rowNum) { + return sheet.createRow(rowNum); + } + + public static Cell createCell(Row row, int colNum) { + return row.createCell(colNum); + } + + public static Cell createCell(Row row, int colNum, CellStyle cellStyle) { + Cell cell = row.createCell(colNum); + cell.setCellStyle(cellStyle); + return cell; + } + + public static Cell createCell(Row row, int colNum, CellStyle cellStyle, String cellValue) { + Cell cell = createCell(row, colNum, cellStyle); + cell.setCellValue(cellValue); + return cell; + } + + public static Cell createCell(Row row, int colNum, String cellValue) { + Cell cell = row.createCell(colNum); + cell.setCellValue(cellValue); + return cell; + } + + public static void fillDataFormat(WriteCellData cellData, String format, String defaultFormat) { + if (cellData.getWriteCellStyle() == null) { + cellData.setWriteCellStyle(new WriteCellStyle()); + } + if (cellData.getWriteCellStyle().getDataFormatData() == null) { + cellData.getWriteCellStyle().setDataFormatData(new DataFormatData()); + } + if (cellData.getWriteCellStyle().getDataFormatData().getFormat() == null) { + if (format == null) { + cellData.getWriteCellStyle().getDataFormatData().setFormat(defaultFormat); + } else { + cellData.getWriteCellStyle().getDataFormatData().setFormat(format); + } + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java b/easyexcel-core/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java new file mode 100644 index 000000000..062bfdc79 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/util/WriteHandlerUtils.java @@ -0,0 +1,182 @@ +package com.alibaba.excel.util; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.handler.chain.CellHandlerExecutionChain; +import com.alibaba.excel.write.handler.chain.RowHandlerExecutionChain; +import com.alibaba.excel.write.handler.chain.SheetHandlerExecutionChain; +import com.alibaba.excel.write.handler.chain.WorkbookHandlerExecutionChain; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.AbstractWriteHolder; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Row; + +/** + * Write handler utils + * + * @author Jiaju Zhuang + */ +@Slf4j +public class WriteHandlerUtils { + + private WriteHandlerUtils() {} + + public static WorkbookWriteHandlerContext createWorkbookWriteHandlerContext(WriteContext writeContext) { + WorkbookWriteHandlerContext context = new WorkbookWriteHandlerContext(writeContext, + writeContext.writeWorkbookHolder()); + writeContext.writeWorkbookHolder().setWorkbookWriteHandlerContext(context); + return context; + } + + public static void beforeWorkbookCreate(WorkbookWriteHandlerContext context) { + beforeWorkbookCreate(context, false); + } + + public static void beforeWorkbookCreate(WorkbookWriteHandlerContext context, boolean runOwn) { + WorkbookHandlerExecutionChain workbookHandlerExecutionChain = getWorkbookHandlerExecutionChain(context, runOwn); + if (workbookHandlerExecutionChain != null) { + workbookHandlerExecutionChain.beforeWorkbookCreate(context); + } + } + + public static void afterWorkbookCreate(WorkbookWriteHandlerContext context) { + afterWorkbookCreate(context, false); + } + + public static void afterWorkbookCreate(WorkbookWriteHandlerContext context, boolean runOwn) { + WorkbookHandlerExecutionChain workbookHandlerExecutionChain = getWorkbookHandlerExecutionChain(context, runOwn); + if (workbookHandlerExecutionChain != null) { + workbookHandlerExecutionChain.afterWorkbookCreate(context); + } + } + + private static WorkbookHandlerExecutionChain getWorkbookHandlerExecutionChain(WorkbookWriteHandlerContext context, + boolean runOwn) { + AbstractWriteHolder abstractWriteHolder = (AbstractWriteHolder)context.getWriteContext().currentWriteHolder(); + if (runOwn) { + return abstractWriteHolder.getOwnWorkbookHandlerExecutionChain(); + } else { + return abstractWriteHolder.getWorkbookHandlerExecutionChain(); + } + } + + public static void afterWorkbookDispose(WorkbookWriteHandlerContext context) { + WorkbookHandlerExecutionChain workbookHandlerExecutionChain = getWorkbookHandlerExecutionChain(context, false); + if (workbookHandlerExecutionChain != null) { + workbookHandlerExecutionChain.afterWorkbookDispose(context); + } + } + + public static SheetWriteHandlerContext createSheetWriteHandlerContext(WriteContext writeContext) { + return new SheetWriteHandlerContext(writeContext, writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder()); + } + + public static void beforeSheetCreate(SheetWriteHandlerContext context) { + beforeSheetCreate(context, false); + } + + public static void beforeSheetCreate(SheetWriteHandlerContext context, boolean runOwn) { + SheetHandlerExecutionChain sheetHandlerExecutionChain = getSheetHandlerExecutionChain(context, runOwn); + if (sheetHandlerExecutionChain != null) { + sheetHandlerExecutionChain.beforeSheetCreate(context); + } + } + + public static void afterSheetCreate(SheetWriteHandlerContext context) { + afterSheetCreate(context, false); + } + + public static void afterSheetCreate(SheetWriteHandlerContext context, boolean runOwn) { + SheetHandlerExecutionChain sheetHandlerExecutionChain = getSheetHandlerExecutionChain(context, runOwn); + if (sheetHandlerExecutionChain != null) { + sheetHandlerExecutionChain.afterSheetCreate(context); + } + } + + private static SheetHandlerExecutionChain getSheetHandlerExecutionChain(SheetWriteHandlerContext context, + boolean runOwn) { + AbstractWriteHolder abstractWriteHolder = (AbstractWriteHolder)context.getWriteContext().currentWriteHolder(); + if (runOwn) { + return abstractWriteHolder.getOwnSheetHandlerExecutionChain(); + } else { + return abstractWriteHolder.getSheetHandlerExecutionChain(); + } + } + + public static CellWriteHandlerContext createCellWriteHandlerContext(WriteContext writeContext, Row row, + Integer rowIndex, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead, + ExcelContentProperty excelContentProperty) { + return new CellWriteHandlerContext(writeContext, writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder(), writeContext.writeTableHolder(), row, rowIndex, null, columnIndex, + relativeRowIndex, head, null, null, isHead, excelContentProperty); + } + + public static void beforeCellCreate(CellWriteHandlerContext context) { + CellHandlerExecutionChain cellHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getCellHandlerExecutionChain(); + if (cellHandlerExecutionChain != null) { + cellHandlerExecutionChain.beforeCellCreate(context); + } + } + + public static void afterCellCreate(CellWriteHandlerContext context) { + CellHandlerExecutionChain cellHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getCellHandlerExecutionChain(); + if (cellHandlerExecutionChain != null) { + cellHandlerExecutionChain.afterCellCreate(context); + } + } + + public static void afterCellDataConverted(CellWriteHandlerContext context) { + CellHandlerExecutionChain cellHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getCellHandlerExecutionChain(); + if (cellHandlerExecutionChain != null) { + cellHandlerExecutionChain.afterCellDataConverted(context); + } + } + + public static void afterCellDispose(CellWriteHandlerContext context) { + CellHandlerExecutionChain cellHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getCellHandlerExecutionChain(); + if (cellHandlerExecutionChain != null) { + cellHandlerExecutionChain.afterCellDispose(context); + } + } + + public static RowWriteHandlerContext createRowWriteHandlerContext(WriteContext writeContext, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { + return new RowWriteHandlerContext(writeContext, writeContext.writeWorkbookHolder(), + writeContext.writeSheetHolder(), writeContext.writeTableHolder(), rowIndex, null, relativeRowIndex, isHead); + } + + public static void beforeRowCreate(RowWriteHandlerContext context) { + RowHandlerExecutionChain rowHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getRowHandlerExecutionChain(); + if (rowHandlerExecutionChain != null) { + rowHandlerExecutionChain.beforeRowCreate(context); + } + } + + public static void afterRowCreate(RowWriteHandlerContext context) { + RowHandlerExecutionChain rowHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getRowHandlerExecutionChain(); + if (rowHandlerExecutionChain != null) { + rowHandlerExecutionChain.afterRowCreate(context); + } + } + + public static void afterRowDispose(RowWriteHandlerContext context) { + RowHandlerExecutionChain rowHandlerExecutionChain = ((AbstractWriteHolder)context.getWriteContext() + .currentWriteHolder()).getRowHandlerExecutionChain(); + if (rowHandlerExecutionChain != null) { + rowHandlerExecutionChain.afterRowDispose(context); + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/ExcelBuilder.java new file mode 100644 index 000000000..6a323388e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/ExcelBuilder.java @@ -0,0 +1,79 @@ +package com.alibaba.excel.write; + +import java.util.Collection; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.fill.FillConfig; + +/** + * @author jipengfei + */ +public interface ExcelBuilder { + + /** + * WorkBook increase value + * + * @param data + * java basic type or java model extend BaseModel + * @param writeSheet + * Write the sheet + * @deprecated please use{@link ExcelBuilder#addContent(Collection, WriteSheet, WriteTable)} + */ + @Deprecated + void addContent(Collection data, WriteSheet writeSheet); + + /** + * WorkBook increase value + * + * @param data + * java basic type or java model extend BaseModel + * @param writeSheet + * Write the sheet + * @param writeTable + * Write the table + */ + void addContent(Collection data, WriteSheet writeSheet, WriteTable writeTable); + + /** + * WorkBook fill value + * + * @param data + * @param fillConfig + * @param writeSheet + */ + void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet); + + /** + * Creates new cell range. Indexes are zero-based. + * + * @param firstRow + * Index of first row + * @param lastRow + * Index of last row (inclusive), must be equal to or larger than {@code firstRow} + * @param firstCol + * Index of first column + * @param lastCol + * Index of last column (inclusive), must be equal to or larger than {@code firstCol} + * @deprecated please use{@link OnceAbsoluteMergeStrategy} + */ + @Deprecated + void merge(int firstRow, int lastRow, int firstCol, int lastCol); + + /** + * Gets the written data + * + * @return + */ + WriteContext writeContext(); + + /** + * Close io + * + * @param onException + */ + void finish(boolean onException); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java new file mode 100644 index 000000000..67e2b9a2b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -0,0 +1,113 @@ +package com.alibaba.excel.write; + +import java.util.Collection; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.context.WriteContextImpl; +import com.alibaba.excel.enums.WriteTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.write.executor.ExcelWriteAddExecutor; +import com.alibaba.excel.write.executor.ExcelWriteFillExecutor; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.fill.FillConfig; + +import org.apache.poi.ss.util.CellRangeAddress; + +/** + * @author jipengfei + */ +public class ExcelBuilderImpl implements ExcelBuilder { + + private final WriteContext context; + private ExcelWriteFillExecutor excelWriteFillExecutor; + private ExcelWriteAddExecutor excelWriteAddExecutor; + + static { + // Create temporary cache directory at initialization time to avoid POI concurrent write bugs + FileUtils.createPoiFilesDirectory(); + } + + public ExcelBuilderImpl(WriteWorkbook writeWorkbook) { + try { + context = new WriteContextImpl(writeWorkbook); + } catch (RuntimeException e) { + finishOnException(); + throw e; + } catch (Throwable e) { + finishOnException(); + throw new ExcelGenerateException(e); + } + } + + @Override + public void addContent(Collection data, WriteSheet writeSheet) { + addContent(data, writeSheet, null); + } + + @Override + public void addContent(Collection data, WriteSheet writeSheet, WriteTable writeTable) { + try { + context.currentSheet(writeSheet, WriteTypeEnum.ADD); + context.currentTable(writeTable); + if (excelWriteAddExecutor == null) { + excelWriteAddExecutor = new ExcelWriteAddExecutor(context); + } + excelWriteAddExecutor.add(data); + } catch (RuntimeException e) { + finishOnException(); + throw e; + } catch (Throwable e) { + finishOnException(); + throw new ExcelGenerateException(e); + } + } + + @Override + public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { + try { + if (context.writeWorkbookHolder().getTempTemplateInputStream() == null) { + throw new ExcelGenerateException("Calling the 'fill' method must use a template."); + } + if (context.writeWorkbookHolder().getExcelType() == ExcelTypeEnum.CSV) { + throw new ExcelGenerateException("csv does not support filling data."); + } + context.currentSheet(writeSheet, WriteTypeEnum.FILL); + if (excelWriteFillExecutor == null) { + excelWriteFillExecutor = new ExcelWriteFillExecutor(context); + } + excelWriteFillExecutor.fill(data, fillConfig); + } catch (RuntimeException e) { + finishOnException(); + throw e; + } catch (Throwable e) { + finishOnException(); + throw new ExcelGenerateException(e); + } + } + + private void finishOnException() { + finish(true); + } + + @Override + public void finish(boolean onException) { + if (context != null) { + context.finish(onException); + } + } + + @Override + public void merge(int firstRow, int lastRow, int firstCol, int lastCol) { + CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); + context.writeSheetHolder().getSheet().addMergedRegion(cra); + } + + @Override + public WriteContext writeContext() { + return context; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java new file mode 100644 index 000000000..014f97e29 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/AbstractExcelWriterParameterBuilder.java @@ -0,0 +1,136 @@ +package com.alibaba.excel.write.builder; + +import java.util.ArrayList; +import java.util.Collection; + +import com.alibaba.excel.metadata.AbstractParameterBuilder; +import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.metadata.WriteBasicParameter; + +/** + * Build ExcelBuilder + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelWriterParameterBuilder extends AbstractParameterBuilder { + /** + * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. + * + * @param relativeHeadRowIndex + * @return + */ + public T relativeHeadRowIndex(Integer relativeHeadRowIndex) { + parameter().setRelativeHeadRowIndex(relativeHeadRowIndex); + return self(); + } + + /** + * Need Head + */ + public T needHead(Boolean needHead) { + parameter().setNeedHead(needHead); + return self(); + } + + /** + * Custom write handler + * + * @param writeHandler + * @return + */ + public T registerWriteHandler(WriteHandler writeHandler) { + if (parameter().getCustomWriteHandlerList() == null) { + parameter().setCustomWriteHandlerList(new ArrayList()); + } + parameter().getCustomWriteHandlerList().add(writeHandler); + return self(); + } + + /** + * Use the default style.Default is true. + * + * @param useDefaultStyle + * @return + */ + public T useDefaultStyle(Boolean useDefaultStyle) { + parameter().setUseDefaultStyle(useDefaultStyle); + return self(); + } + + /** + * Whether to automatically merge headers.Default is true. + * + * @param automaticMergeHead + * @return + */ + public T automaticMergeHead(Boolean automaticMergeHead) { + parameter().setAutomaticMergeHead(automaticMergeHead); + return self(); + } + + /** + * Ignore the custom columns. + */ + public T excludeColumnIndexes(Collection excludeColumnIndexes) { + parameter().setExcludeColumnIndexes(excludeColumnIndexes); + return self(); + } + + /** + * Ignore the custom columns. + * + * @deprecated use {@link #excludeColumnFieldNames(Collection)} + */ + public T excludeColumnFiledNames(Collection excludeColumnFieldNames) { + parameter().setExcludeColumnFieldNames(excludeColumnFieldNames); + return self(); + } + + /** + * Ignore the custom columns. + */ + public T excludeColumnFieldNames(Collection excludeColumnFieldNames) { + parameter().setExcludeColumnFieldNames(excludeColumnFieldNames); + return self(); + } + + /** + * Only output the custom columns. + */ + public T includeColumnIndexes(Collection includeColumnIndexes) { + parameter().setIncludeColumnIndexes(includeColumnIndexes); + return self(); + } + + /** + * Only output the custom columns. + * + * @deprecated use {@link #includeColumnFieldNames(Collection)} spelling mistake + */ + @Deprecated + public T includeColumnFiledNames(Collection includeColumnFieldNames) { + parameter().setIncludeColumnFieldNames(includeColumnFieldNames); + return self(); + } + + /** + * Only output the custom columns. + */ + public T includeColumnFieldNames(Collection includeColumnFieldNames) { + parameter().setIncludeColumnFieldNames(includeColumnFieldNames); + return self(); + } + + /** + * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. + * + * @since 3.3.0 + **/ + public T orderByIncludeColumn(Boolean orderByIncludeColumn) { + parameter().setOrderByIncludeColumn(orderByIncludeColumn); + return self(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java new file mode 100644 index 000000000..821898f12 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterBuilder.java @@ -0,0 +1,164 @@ +package com.alibaba.excel.write.builder; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; + +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.metadata.WriteWorkbook; + +/** + * Build ExcelBuilder + * + * @author Jiaju Zhuang + */ +public class ExcelWriterBuilder extends AbstractExcelWriterParameterBuilder { + /** + * Workbook + */ + private final WriteWorkbook writeWorkbook; + + public ExcelWriterBuilder() { + this.writeWorkbook = new WriteWorkbook(); + } + + /** + * Default true + * + * @param autoCloseStream + * @return + */ + public ExcelWriterBuilder autoCloseStream(Boolean autoCloseStream) { + writeWorkbook.setAutoCloseStream(autoCloseStream); + return this; + } + + /** + * Whether the encryption. + *

+ * WARRING:Encryption is when the entire file is read into memory, so it is very memory intensive. + * + * @param password + * @return + */ + public ExcelWriterBuilder password(String password) { + writeWorkbook.setPassword(password); + return this; + } + + /** + * Write excel in memory. Default false, the cache file is created and finally written to excel. + *

+ * Comment and RichTextString are only supported in memory mode. + */ + public ExcelWriterBuilder inMemory(Boolean inMemory) { + writeWorkbook.setInMemory(inMemory); + return this; + } + + /** + * Excel is also written in the event of an exception being thrown.The default false. + */ + public ExcelWriterBuilder writeExcelOnException(Boolean writeExcelOnException) { + writeWorkbook.setWriteExcelOnException(writeExcelOnException); + return this; + } + + public ExcelWriterBuilder excelType(ExcelTypeEnum excelType) { + writeWorkbook.setExcelType(excelType); + return this; + } + + public ExcelWriterBuilder file(OutputStream outputStream) { + writeWorkbook.setOutputStream(outputStream); + return this; + } + + public ExcelWriterBuilder file(File outputFile) { + writeWorkbook.setFile(outputFile); + return this; + } + + public ExcelWriterBuilder file(String outputPathName) { + return file(new File(outputPathName)); + } + + /** + * charset. + * Only work on the CSV file + */ + public ExcelWriterBuilder charset(Charset charset) { + writeWorkbook.setCharset(charset); + return this; + } + + /** + * Set the encoding prefix in the csv file, otherwise the office may open garbled characters. + * Default true. + */ + public ExcelWriterBuilder withBom(Boolean withBom) { + writeWorkbook.setWithBom(withBom); + return this; + } + + /** + * Template file. + * This file is read into memory, excessive cases can lead to OOM. + */ + public ExcelWriterBuilder withTemplate(InputStream templateInputStream) { + writeWorkbook.setTemplateInputStream(templateInputStream); + return this; + } + + /** + * Template file. + * This file is read into memory, excessive cases can lead to OOM. + */ + public ExcelWriterBuilder withTemplate(File templateFile) { + writeWorkbook.setTemplateFile(templateFile); + return this; + } + + /** + * Template file. + * This file is read into memory, excessive cases can lead to OOM. + */ + public ExcelWriterBuilder withTemplate(String pathName) { + return withTemplate(new File(pathName)); + } + + public ExcelWriter build() { + return new ExcelWriter(writeWorkbook); + } + + public ExcelWriterSheetBuilder sheet() { + return sheet(null, null); + } + + public ExcelWriterSheetBuilder sheet(Integer sheetNo) { + return sheet(sheetNo, null); + } + + public ExcelWriterSheetBuilder sheet(String sheetName) { + return sheet(null, sheetName); + } + + public ExcelWriterSheetBuilder sheet(Integer sheetNo, String sheetName) { + ExcelWriter excelWriter = build(); + ExcelWriterSheetBuilder excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter); + if (sheetNo != null) { + excelWriterSheetBuilder.sheetNo(sheetNo); + } + if (sheetName != null) { + excelWriterSheetBuilder.sheetName(sheetName); + } + return excelWriterSheetBuilder; + } + + @Override + protected WriteWorkbook parameter() { + return writeWorkbook; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java new file mode 100644 index 000000000..b7fd6805a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterSheetBuilder.java @@ -0,0 +1,107 @@ +package com.alibaba.excel.write.builder; + +import java.util.Collection; +import java.util.function.Supplier; + +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; + +/** + * Build sheet + * + * @author Jiaju Zhuang + */ +public class ExcelWriterSheetBuilder extends AbstractExcelWriterParameterBuilder { + private ExcelWriter excelWriter; + /** + * Sheet + */ + private final WriteSheet writeSheet; + + public ExcelWriterSheetBuilder() { + this.writeSheet = new WriteSheet(); + } + + public ExcelWriterSheetBuilder(ExcelWriter excelWriter) { + this.writeSheet = new WriteSheet(); + this.excelWriter = excelWriter; + } + + /** + * Starting from 0 + * + * @param sheetNo + * @return + */ + public ExcelWriterSheetBuilder sheetNo(Integer sheetNo) { + writeSheet.setSheetNo(sheetNo); + return this; + } + + /** + * sheet name + * + * @param sheetName + * @return + */ + public ExcelWriterSheetBuilder sheetName(String sheetName) { + writeSheet.setSheetName(sheetName); + return this; + } + + public WriteSheet build() { + return writeSheet; + } + + public void doWrite(Collection data) { + if (excelWriter == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); + } + excelWriter.write(data, build()); + excelWriter.finish(); + } + + public void doFill(Object data) { + doFill(data, null); + } + + public void doFill(Object data, FillConfig fillConfig) { + if (excelWriter == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet()' to call this method"); + } + excelWriter.fill(data, fillConfig, build()); + excelWriter.finish(); + } + + public void doWrite(Supplier> supplier) { + doWrite(supplier.get()); + } + + public void doFill(Supplier supplier) { + doFill(supplier.get()); + } + + public void doFill(Supplier supplier, FillConfig fillConfig) { + doFill(supplier.get(), fillConfig); + } + + public ExcelWriterTableBuilder table() { + return table(null); + } + + public ExcelWriterTableBuilder table(Integer tableNo) { + ExcelWriterTableBuilder excelWriterTableBuilder = new ExcelWriterTableBuilder(excelWriter, build()); + if (tableNo != null) { + excelWriterTableBuilder.tableNo(tableNo); + } + return excelWriterTableBuilder; + } + + @Override + protected WriteSheet parameter() { + return writeSheet; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java new file mode 100644 index 000000000..6574db048 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/builder/ExcelWriterTableBuilder.java @@ -0,0 +1,67 @@ +package com.alibaba.excel.write.builder; + +import java.util.Collection; +import java.util.function.Supplier; + +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; + +/** + * Build sheet + * + * @author Jiaju Zhuang + */ +public class ExcelWriterTableBuilder extends AbstractExcelWriterParameterBuilder { + + private ExcelWriter excelWriter; + + private WriteSheet writeSheet; + /** + * table + */ + private final WriteTable writeTable; + + public ExcelWriterTableBuilder() { + this.writeTable = new WriteTable(); + } + + public ExcelWriterTableBuilder(ExcelWriter excelWriter, WriteSheet writeSheet) { + this.excelWriter = excelWriter; + this.writeSheet = writeSheet; + this.writeTable = new WriteTable(); + } + + /** + * Starting from 0 + * + * @param tableNo + * @return + */ + public ExcelWriterTableBuilder tableNo(Integer tableNo) { + writeTable.setTableNo(tableNo); + return this; + } + + public WriteTable build() { + return writeTable; + } + + public void doWrite(Collection data) { + if (excelWriter == null) { + throw new ExcelGenerateException("Must use 'EasyExcelFactory.write().sheet().table()' to call this method"); + } + excelWriter.write(data, writeSheet, build()); + excelWriter.finish(); + } + + public void doWrite(Supplier> supplier) { + doWrite(supplier.get()); + } + + @Override + protected WriteTable parameter() { + return writeTable; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java new file mode 100644 index 000000000..7f8fe0eae --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/AbstractExcelWriteExecutor.java @@ -0,0 +1,343 @@ +package com.alibaba.excel.write.executor; + +import java.util.List; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.converters.NullableObjectConverter; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.exception.ExcelWriteDataConvertException; +import com.alibaba.excel.metadata.data.CommentData; +import com.alibaba.excel.metadata.data.FormulaData; +import com.alibaba.excel.metadata.data.HyperlinkData; +import com.alibaba.excel.metadata.data.ImageData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.FileTypeUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.StyleUtil; +import com.alibaba.excel.util.WorkBookUtil; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Hyperlink; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public abstract class AbstractExcelWriteExecutor implements ExcelWriteExecutor { + protected WriteContext writeContext; + + public AbstractExcelWriteExecutor(WriteContext writeContext) { + this.writeContext = writeContext; + } + + /** + * Transform the data and then to set into the cell + * + * @param cellWriteHandlerContext context + */ + protected void converterAndSet(CellWriteHandlerContext cellWriteHandlerContext) { + + WriteCellData cellData = convert(cellWriteHandlerContext); + cellWriteHandlerContext.setCellDataList(ListUtils.newArrayList(cellData)); + cellWriteHandlerContext.setFirstCellData(cellData); + + WriteHandlerUtils.afterCellDataConverted(cellWriteHandlerContext); + + // Fill in picture information + fillImage(cellWriteHandlerContext, cellData.getImageDataList()); + + // Fill in comment information + fillComment(cellWriteHandlerContext, cellData.getCommentData()); + + // Fill in hyper link information + fillHyperLink(cellWriteHandlerContext, cellData.getHyperlinkData()); + + // Fill in formula information + fillFormula(cellWriteHandlerContext, cellData.getFormulaData()); + + // Fill index + cellData.setRowIndex(cellWriteHandlerContext.getRowIndex()); + cellData.setColumnIndex(cellWriteHandlerContext.getColumnIndex()); + + if (cellData.getType() == null) { + cellData.setType(CellDataTypeEnum.EMPTY); + } + Cell cell = cellWriteHandlerContext.getCell(); + switch (cellData.getType()) { + case STRING: + cell.setCellValue(cellData.getStringValue()); + return; + case BOOLEAN: + cell.setCellValue(cellData.getBooleanValue()); + return; + case NUMBER: + cell.setCellValue(cellData.getNumberValue().doubleValue()); + return; + case DATE: + cell.setCellValue(cellData.getDateValue()); + return; + case RICH_TEXT_STRING: + cell.setCellValue(StyleUtil + .buildRichTextString(writeContext.writeWorkbookHolder(), cellData.getRichTextStringDataValue())); + return; + case EMPTY: + return; + default: + throw new ExcelWriteDataConvertException(cellWriteHandlerContext, + "Not supported data:" + cellWriteHandlerContext.getOriginalValue() + " return type:" + + cellData.getType() + + "at row:" + cellWriteHandlerContext.getRowIndex()); + } + + } + + private void fillFormula(CellWriteHandlerContext cellWriteHandlerContext, FormulaData formulaData) { + if (formulaData == null) { + return; + } + Cell cell = cellWriteHandlerContext.getCell(); + if (formulaData.getFormulaValue() != null) { + cell.setCellFormula(formulaData.getFormulaValue()); + } + } + + private void fillHyperLink(CellWriteHandlerContext cellWriteHandlerContext, HyperlinkData hyperlinkData) { + if (hyperlinkData == null) { + return; + } + Integer rowIndex = cellWriteHandlerContext.getRowIndex(); + Integer columnIndex = cellWriteHandlerContext.getColumnIndex(); + Workbook workbook = cellWriteHandlerContext.getWriteWorkbookHolder().getWorkbook(); + Cell cell = cellWriteHandlerContext.getCell(); + + CreationHelper helper = workbook.getCreationHelper(); + Hyperlink hyperlink = helper.createHyperlink(StyleUtil.getHyperlinkType(hyperlinkData.getHyperlinkType())); + hyperlink.setAddress(hyperlinkData.getAddress()); + hyperlink.setFirstRow(StyleUtil.getCellCoordinate(rowIndex, hyperlinkData.getFirstRowIndex(), + hyperlinkData.getRelativeFirstRowIndex())); + hyperlink.setFirstColumn(StyleUtil.getCellCoordinate(columnIndex, hyperlinkData.getFirstColumnIndex(), + hyperlinkData.getRelativeFirstColumnIndex())); + hyperlink.setLastRow(StyleUtil.getCellCoordinate(rowIndex, hyperlinkData.getLastRowIndex(), + hyperlinkData.getRelativeLastRowIndex())); + hyperlink.setLastColumn(StyleUtil.getCellCoordinate(columnIndex, hyperlinkData.getLastColumnIndex(), + hyperlinkData.getRelativeLastColumnIndex())); + cell.setHyperlink(hyperlink); + } + + private void fillComment(CellWriteHandlerContext cellWriteHandlerContext, CommentData commentData) { + if (commentData == null) { + return; + } + ClientAnchor anchor; + Integer rowIndex = cellWriteHandlerContext.getRowIndex(); + Integer columnIndex = cellWriteHandlerContext.getColumnIndex(); + Sheet sheet = cellWriteHandlerContext.getWriteSheetHolder().getSheet(); + Cell cell = cellWriteHandlerContext.getCell(); + + if (writeContext.writeWorkbookHolder().getExcelType() == ExcelTypeEnum.XLSX) { + anchor = new XSSFClientAnchor(StyleUtil.getCoordinate(commentData.getLeft()), + StyleUtil.getCoordinate(commentData.getTop()), + StyleUtil.getCoordinate(commentData.getRight()), + StyleUtil.getCoordinate(commentData.getBottom()), + StyleUtil.getCellCoordinate(columnIndex, commentData.getFirstColumnIndex(), + commentData.getRelativeFirstColumnIndex()), + StyleUtil.getCellCoordinate(rowIndex, commentData.getFirstRowIndex(), + commentData.getRelativeFirstRowIndex()), + StyleUtil.getCellCoordinate(columnIndex, commentData.getLastColumnIndex(), + commentData.getRelativeLastColumnIndex()) + 1, + StyleUtil.getCellCoordinate(rowIndex, commentData.getLastRowIndex(), + commentData.getRelativeLastRowIndex()) + 1); + } else { + anchor = new HSSFClientAnchor(StyleUtil.getCoordinate(commentData.getLeft()), + StyleUtil.getCoordinate(commentData.getTop()), + StyleUtil.getCoordinate(commentData.getRight()), + StyleUtil.getCoordinate(commentData.getBottom()), + (short)StyleUtil.getCellCoordinate(columnIndex, commentData.getFirstColumnIndex(), + commentData.getRelativeFirstColumnIndex()), + StyleUtil.getCellCoordinate(rowIndex, commentData.getFirstRowIndex(), + commentData.getRelativeFirstRowIndex()), + (short)(StyleUtil.getCellCoordinate(columnIndex, commentData.getLastColumnIndex(), + commentData.getRelativeLastColumnIndex()) + 1), + StyleUtil.getCellCoordinate(rowIndex, commentData.getLastRowIndex(), + commentData.getRelativeLastRowIndex()) + 1); + } + + Comment comment = sheet.createDrawingPatriarch().createCellComment(anchor); + if (commentData.getRichTextStringData() != null) { + comment.setString( + StyleUtil.buildRichTextString(writeContext.writeWorkbookHolder(), commentData.getRichTextStringData())); + } + if (commentData.getAuthor() != null) { + comment.setAuthor(commentData.getAuthor()); + } + cell.setCellComment(comment); + } + + protected void fillImage(CellWriteHandlerContext cellWriteHandlerContext, List imageDataList) { + if (CollectionUtils.isEmpty(imageDataList)) { + return; + } + Integer rowIndex = cellWriteHandlerContext.getRowIndex(); + Integer columnIndex = cellWriteHandlerContext.getColumnIndex(); + Sheet sheet = cellWriteHandlerContext.getWriteSheetHolder().getSheet(); + Workbook workbook = cellWriteHandlerContext.getWriteWorkbookHolder().getWorkbook(); + + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + CreationHelper helper = sheet.getWorkbook().getCreationHelper(); + for (ImageData imageData : imageDataList) { + int index = workbook.addPicture(imageData.getImage(), + FileTypeUtils.getImageTypeFormat(imageData.getImage())); + ClientAnchor anchor = helper.createClientAnchor(); + if (imageData.getTop() != null) { + anchor.setDy1(StyleUtil.getCoordinate(imageData.getTop())); + } + if (imageData.getRight() != null) { + anchor.setDx2(-StyleUtil.getCoordinate(imageData.getRight())); + } + if (imageData.getBottom() != null) { + anchor.setDy2(-StyleUtil.getCoordinate(imageData.getBottom())); + } + if (imageData.getLeft() != null) { + anchor.setDx1(StyleUtil.getCoordinate(imageData.getLeft())); + } + anchor.setRow1(StyleUtil.getCellCoordinate(rowIndex, imageData.getFirstRowIndex(), + imageData.getRelativeFirstRowIndex())); + anchor.setCol1(StyleUtil.getCellCoordinate(columnIndex, imageData.getFirstColumnIndex(), + imageData.getRelativeFirstColumnIndex())); + anchor.setRow2(StyleUtil.getCellCoordinate(rowIndex, imageData.getLastRowIndex(), + imageData.getRelativeLastRowIndex()) + 1); + anchor.setCol2(StyleUtil.getCellCoordinate(columnIndex, imageData.getLastColumnIndex(), + imageData.getRelativeLastColumnIndex()) + 1); + if (imageData.getAnchorType() != null) { + anchor.setAnchorType(imageData.getAnchorType().getValue()); + } + drawing.createPicture(anchor, index); + } + } + + protected WriteCellData convert(CellWriteHandlerContext cellWriteHandlerContext) { + // This means that the user has defined the data. + if (cellWriteHandlerContext.getOriginalFieldClass() == WriteCellData.class) { + if (cellWriteHandlerContext.getOriginalValue() == null) { + return new WriteCellData<>(CellDataTypeEnum.EMPTY); + } + WriteCellData cellDataValue = (WriteCellData)cellWriteHandlerContext.getOriginalValue(); + if (cellDataValue.getType() != null) { + // Configuration information may not be read here + fillProperty(cellDataValue, cellWriteHandlerContext.getExcelContentProperty()); + + return cellDataValue; + } else { + if (cellDataValue.getData() == null) { + cellDataValue.setType(CellDataTypeEnum.EMPTY); + return cellDataValue; + } + } + WriteCellData cellDataReturn = doConvert(cellWriteHandlerContext); + + if (cellDataValue.getImageDataList() != null) { + cellDataReturn.setImageDataList(cellDataValue.getImageDataList()); + } + if (cellDataValue.getCommentData() != null) { + cellDataReturn.setCommentData(cellDataValue.getCommentData()); + } + if (cellDataValue.getHyperlinkData() != null) { + cellDataReturn.setHyperlinkData(cellDataValue.getHyperlinkData()); + } + // The formula information is subject to user input + if (cellDataValue.getFormulaData() != null) { + cellDataReturn.setFormulaData(cellDataValue.getFormulaData()); + } + if (cellDataValue.getWriteCellStyle() != null) { + cellDataReturn.setWriteCellStyle(cellDataValue.getWriteCellStyle()); + } + return cellDataReturn; + } + return doConvert(cellWriteHandlerContext); + } + + private void fillProperty(WriteCellData cellDataValue, ExcelContentProperty excelContentProperty) { + switch (cellDataValue.getType()) { + case DATE: + String dateFormat = null; + if (excelContentProperty != null && excelContentProperty.getDateTimeFormatProperty() != null) { + dateFormat = excelContentProperty.getDateTimeFormatProperty().getFormat(); + } + WorkBookUtil.fillDataFormat(cellDataValue, dateFormat, DateUtils.defaultDateFormat); + return; + case NUMBER: + String numberFormat = null; + if (excelContentProperty != null && excelContentProperty.getNumberFormatProperty() != null) { + numberFormat = excelContentProperty.getNumberFormatProperty().getFormat(); + } + WorkBookUtil.fillDataFormat(cellDataValue, numberFormat, null); + return; + default: + return; + } + } + + private WriteCellData doConvert(CellWriteHandlerContext cellWriteHandlerContext) { + ExcelContentProperty excelContentProperty = cellWriteHandlerContext.getExcelContentProperty(); + + Converter converter = null; + if (excelContentProperty != null) { + converter = excelContentProperty.getConverter(); + } + if (converter == null) { + // csv is converted to string by default + if (writeContext.writeWorkbookHolder().getExcelType() == ExcelTypeEnum.CSV) { + cellWriteHandlerContext.setTargetCellDataType(CellDataTypeEnum.STRING); + } + converter = writeContext.currentWriteHolder().converterMap().get( + ConverterKeyBuild.buildKey(cellWriteHandlerContext.getOriginalFieldClass(), + cellWriteHandlerContext.getTargetCellDataType())); + } + if (cellWriteHandlerContext.getOriginalValue() == null && !(converter instanceof NullableObjectConverter)) { + return new WriteCellData<>(CellDataTypeEnum.EMPTY); + } + if (converter == null) { + throw new ExcelWriteDataConvertException(cellWriteHandlerContext, + "Can not find 'Converter' support class " + cellWriteHandlerContext.getOriginalFieldClass() + .getSimpleName() + "."); + } + WriteCellData cellData; + try { + cellData = ((Converter)converter).convertToExcelData( + new WriteConverterContext<>(cellWriteHandlerContext.getOriginalValue(), excelContentProperty, + writeContext)); + } catch (Exception e) { + throw new ExcelWriteDataConvertException(cellWriteHandlerContext, + "Convert data:" + cellWriteHandlerContext.getOriginalValue() + " error, at row:" + + cellWriteHandlerContext.getRowIndex(), e); + } + if (cellData == null || cellData.getType() == null) { + throw new ExcelWriteDataConvertException(cellWriteHandlerContext, + "Convert data:" + cellWriteHandlerContext.getOriginalValue() + + " return is null or return type is null, at row:" + + cellWriteHandlerContext.getRowIndex()); + } + return cellData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java new file mode 100644 index 000000000..d1d82cc2e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteAddExecutor.java @@ -0,0 +1,215 @@ +package com.alibaba.excel.write.executor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.FieldCache; +import com.alibaba.excel.metadata.FieldWrapper; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.support.cglib.beans.BeanMap; +import com.alibaba.excel.util.BeanMapUtils; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.FieldUtils; +import com.alibaba.excel.util.WorkBookUtil; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.metadata.CollectionRowData; +import com.alibaba.excel.write.metadata.MapRowData; +import com.alibaba.excel.write.metadata.RowData; +import com.alibaba.excel.write.metadata.holder.WriteHolder; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +/** + * Add the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteAddExecutor extends AbstractExcelWriteExecutor { + + public ExcelWriteAddExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void add(Collection data) { + if (CollectionUtils.isEmpty(data)) { + data = new ArrayList<>(); + } + WriteSheetHolder writeSheetHolder = writeContext.writeSheetHolder(); + int newRowIndex = writeSheetHolder.getNewRowIndexAndStartDoWrite(); + if (writeSheetHolder.isNew() && !writeSheetHolder.getExcelWriteHeadProperty().hasHead()) { + newRowIndex += writeContext.currentWriteHolder().relativeHeadRowIndex(); + } + int relativeRowIndex = 0; + for (Object oneRowData : data) { + int lastRowIndex = relativeRowIndex + newRowIndex; + addOneRowOfDataToExcel(oneRowData, lastRowIndex, relativeRowIndex); + relativeRowIndex++; + } + } + + private void addOneRowOfDataToExcel(Object oneRowData, int rowIndex, int relativeRowIndex) { + if (oneRowData == null) { + return; + } + RowWriteHandlerContext rowWriteHandlerContext = WriteHandlerUtils.createRowWriteHandlerContext(writeContext, + rowIndex, relativeRowIndex, Boolean.FALSE); + WriteHandlerUtils.beforeRowCreate(rowWriteHandlerContext); + + Row row = WorkBookUtil.createRow(writeContext.writeSheetHolder().getSheet(), rowIndex); + rowWriteHandlerContext.setRow(row); + + WriteHandlerUtils.afterRowCreate(rowWriteHandlerContext); + + if (oneRowData instanceof Collection) { + addBasicTypeToExcel(new CollectionRowData((Collection)oneRowData), row, rowIndex, relativeRowIndex); + } else if (oneRowData instanceof Map) { + addBasicTypeToExcel(new MapRowData((Map)oneRowData), row, rowIndex, relativeRowIndex); + } else { + addJavaObjectToExcel(oneRowData, row, rowIndex, relativeRowIndex); + } + + WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext); + } + + private void addBasicTypeToExcel(RowData oneRowData, Row row, int rowIndex, int relativeRowIndex) { + if (oneRowData.isEmpty()) { + return; + } + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + int dataIndex = 0; + int maxCellIndex = -1; + for (Map.Entry entry : headMap.entrySet()) { + if (dataIndex >= oneRowData.size()) { + return; + } + int columnIndex = entry.getKey(); + Head head = entry.getValue(); + doAddBasicTypeToExcel(oneRowData, head, row, rowIndex, relativeRowIndex, dataIndex++, columnIndex); + maxCellIndex = Math.max(maxCellIndex, columnIndex); + } + // Finish + if (dataIndex >= oneRowData.size()) { + return; + } + // fix https://github.com/alibaba/easyexcel/issues/1702 + // If there is data, it is written to the next cell + maxCellIndex++; + + int size = oneRowData.size() - dataIndex; + for (int i = 0; i < size; i++) { + doAddBasicTypeToExcel(oneRowData, null, row, rowIndex, relativeRowIndex, dataIndex++, maxCellIndex++); + } + } + + private void doAddBasicTypeToExcel(RowData oneRowData, Head head, Row row, int rowIndex, int relativeRowIndex, + int dataIndex, int columnIndex) { + ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(null, + writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), + head == null ? null : head.getFieldName(), writeContext.currentWriteHolder()); + + CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext(writeContext, + row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); + WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); + + Cell cell = WorkBookUtil.createCell(row, columnIndex); + cellWriteHandlerContext.setCell(cell); + + WriteHandlerUtils.afterCellCreate(cellWriteHandlerContext); + + cellWriteHandlerContext.setOriginalValue(oneRowData.get(dataIndex)); + cellWriteHandlerContext.setOriginalFieldClass( + FieldUtils.getFieldClass(cellWriteHandlerContext.getOriginalValue())); + converterAndSet(cellWriteHandlerContext); + + WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + } + + private void addJavaObjectToExcel(Object oneRowData, Row row, int rowIndex, int relativeRowIndex) { + WriteHolder currentWriteHolder = writeContext.currentWriteHolder(); + BeanMap beanMap = BeanMapUtils.create(oneRowData); + // Bean the contains of the Map Key method with poor performance,So to create a keySet here + Set beanKeySet = new HashSet<>(beanMap.keySet()); + Set beanMapHandledSet = new HashSet<>(); + int maxCellIndex = -1; + // If it's a class it needs to be cast by type + if (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) { + Map headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap(); + for (Map.Entry entry : headMap.entrySet()) { + int columnIndex = entry.getKey(); + Head head = entry.getValue(); + String name = head.getFieldName(); + if (!beanKeySet.contains(name)) { + continue; + } + + ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap, + currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), name, currentWriteHolder); + CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( + writeContext, row, rowIndex, head, columnIndex, relativeRowIndex, Boolean.FALSE, + excelContentProperty); + WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); + + Cell cell = WorkBookUtil.createCell(row, columnIndex); + cellWriteHandlerContext.setCell(cell); + + WriteHandlerUtils.afterCellCreate(cellWriteHandlerContext); + + cellWriteHandlerContext.setOriginalValue(beanMap.get(name)); + cellWriteHandlerContext.setOriginalFieldClass(head.getField().getType()); + converterAndSet(cellWriteHandlerContext); + + WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + + beanMapHandledSet.add(name); + maxCellIndex = Math.max(maxCellIndex, columnIndex); + } + } + // Finish + if (beanMapHandledSet.size() == beanMap.size()) { + return; + } + maxCellIndex++; + + FieldCache fieldCache = ClassUtils.declaredFields(oneRowData.getClass(), writeContext.currentWriteHolder()); + for (Map.Entry entry : fieldCache.getSortedFieldMap().entrySet()) { + FieldWrapper field = entry.getValue(); + String fieldName = field.getFieldName(); + boolean uselessData = !beanKeySet.contains(fieldName) || beanMapHandledSet.contains(fieldName); + if (uselessData) { + continue; + } + Object value = beanMap.get(fieldName); + ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(beanMap, + currentWriteHolder.excelWriteHeadProperty().getHeadClazz(), fieldName, currentWriteHolder); + CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( + writeContext, row, rowIndex, null, maxCellIndex, relativeRowIndex, Boolean.FALSE, excelContentProperty); + WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); + + // fix https://github.com/alibaba/easyexcel/issues/1870 + // If there is data, it is written to the next cell + Cell cell = WorkBookUtil.createCell(row, maxCellIndex); + cellWriteHandlerContext.setCell(cell); + + WriteHandlerUtils.afterCellCreate(cellWriteHandlerContext); + + cellWriteHandlerContext.setOriginalValue(value); + cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(beanMap, fieldName, value)); + converterAndSet(cellWriteHandlerContext); + + WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + maxCellIndex++; + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java new file mode 100644 index 000000000..b7cb607c7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteExecutor.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.write.executor; + +/** + * Excel write Executor + * + * @author Jiaju Zhuang + */ +public interface ExcelWriteExecutor { +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java new file mode 100644 index 000000000..815242237 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java @@ -0,0 +1,633 @@ +package com.alibaba.excel.write.executor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.BeanMapUtils; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.FieldUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.util.WriteHandlerUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.metadata.fill.AnalysisCell; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; + +import com.alibaba.excel.util.PoiUtils; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +/** + * Fill the data into excel + * + * @author Jiaju Zhuang + */ +public class ExcelWriteFillExecutor extends AbstractExcelWriteExecutor { + + private static final String ESCAPE_FILL_PREFIX = "\\\\\\{"; + private static final String ESCAPE_FILL_SUFFIX = "\\\\\\}"; + private static final String FILL_PREFIX = "{"; + private static final String FILL_SUFFIX = "}"; + private static final char IGNORE_CHAR = '\\'; + private static final String COLLECTION_PREFIX = "."; + /** + * Fields to replace in the template + */ + private final Map> templateAnalysisCache = MapUtils.newHashMap(); + /** + * Collection fields to replace in the template + */ + private final Map> templateCollectionAnalysisCache = MapUtils.newHashMap(); + /** + * Style cache for collection fields + */ + private final Map> collectionFieldStyleCache + = MapUtils.newHashMap(); + /** + * Row height cache for collection + */ + private final Map collectionRowHeightCache = MapUtils.newHashMap(); + /** + * Last index cache for collection fields + */ + private final Map> collectionLastIndexCache = MapUtils.newHashMap(); + + private final Map relativeRowIndexMap = MapUtils.newHashMap(); + /** + * The unique data encoding for this fill + */ + private UniqueDataFlagKey currentUniqueDataFlag; + + public ExcelWriteFillExecutor(WriteContext writeContext) { + super(writeContext); + } + + public void fill(Object data, FillConfig fillConfig) { + if (data == null) { + data = new HashMap(16); + } + if (fillConfig == null) { + fillConfig = FillConfig.builder().build(); + } + fillConfig.init(); + + Object realData; + // The data prefix that is populated this time + String currentDataPrefix; + + if (data instanceof FillWrapper) { + FillWrapper fillWrapper = (FillWrapper)data; + currentDataPrefix = fillWrapper.getName(); + realData = fillWrapper.getCollectionData(); + } else { + realData = data; + currentDataPrefix = null; + } + currentUniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder(), currentDataPrefix); + + // processing data + if (realData instanceof Collection) { + List analysisCellList = readTemplateData(templateCollectionAnalysisCache); + Collection collectionData = (Collection)realData; + if (CollectionUtils.isEmpty(collectionData)) { + return; + } + Iterator iterator = collectionData.iterator(); + if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) { + shiftRows(collectionData.size(), analysisCellList); + } + while (iterator.hasNext()) { + doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex()); + } + } else { + doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null); + } + } + + private void shiftRows(int size, List analysisCellList) { + if (CollectionUtils.isEmpty(analysisCellList)) { + return; + } + int maxRowIndex = 0; + Map collectionLastIndexMap = collectionLastIndexCache.get(currentUniqueDataFlag); + for (AnalysisCell analysisCell : analysisCellList) { + if (collectionLastIndexMap != null) { + Integer lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex != null) { + if (lastRowIndex > maxRowIndex) { + maxRowIndex = lastRowIndex; + } + continue; + } + } + if (analysisCell.getRowIndex() > maxRowIndex) { + maxRowIndex = analysisCell.getRowIndex(); + } + } + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + int lastRowIndex = cachedSheet.getLastRowNum(); + if (maxRowIndex >= lastRowIndex) { + return; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + int number = size; + if (collectionLastIndexMap == null) { + number--; + } + if (number <= 0) { + return; + } + sheet.shiftRows(maxRowIndex + 1, lastRowIndex, number, true, false); + + // The current data is greater than unity rowindex increase + increaseRowIndex(templateAnalysisCache, number, maxRowIndex); + increaseRowIndex(templateCollectionAnalysisCache, number, maxRowIndex); + } + + private void increaseRowIndex(Map> templateAnalysisCache, int number, + int maxRowIndex) { + for (Map.Entry> entry : templateAnalysisCache.entrySet()) { + UniqueDataFlagKey uniqueDataFlagKey = entry.getKey(); + if (!Objects.equals(currentUniqueDataFlag.getSheetNo(), uniqueDataFlagKey.getSheetNo()) || !Objects.equals( + currentUniqueDataFlag.getSheetName(), uniqueDataFlagKey.getSheetName())) { + continue; + } + for (AnalysisCell analysisCell : entry.getValue()) { + if (analysisCell.getRowIndex() > maxRowIndex) { + analysisCell.setRowIndex(analysisCell.getRowIndex() + number); + } + } + } + } + + private void doFill(List analysisCellList, Object oneRowData, FillConfig fillConfig, + Integer relativeRowIndex) { + if (CollectionUtils.isEmpty(analysisCellList) || oneRowData == null) { + return; + } + Map dataMap; + if (oneRowData instanceof Map) { + dataMap = (Map)oneRowData; + } else { + dataMap = BeanMapUtils.create(oneRowData); + } + Set dataKeySet = new HashSet<>(dataMap.keySet()); + + RowWriteHandlerContext rowWriteHandlerContext = WriteHandlerUtils.createRowWriteHandlerContext(writeContext, + null, relativeRowIndex, Boolean.FALSE); + + for (AnalysisCell analysisCell : analysisCellList) { + CellWriteHandlerContext cellWriteHandlerContext = WriteHandlerUtils.createCellWriteHandlerContext( + writeContext, null, analysisCell.getRowIndex(), null, analysisCell.getColumnIndex(), + relativeRowIndex, Boolean.FALSE, ExcelContentProperty.EMPTY); + + if (analysisCell.getOnlyOneVariable()) { + String variable = analysisCell.getVariableList().get(0); + if (!dataKeySet.contains(variable)) { + continue; + } + Object value = dataMap.get(variable); + ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap, + writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable, + writeContext.currentWriteHolder()); + cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); + + createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); + cellWriteHandlerContext.setOriginalValue(value); + cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value)); + + converterAndSet(cellWriteHandlerContext); + WriteCellData cellData = cellWriteHandlerContext.getFirstCellData(); + + // Restyle + if (fillConfig.getAutoStyle()) { + Optional.ofNullable(collectionFieldStyleCache.get(currentUniqueDataFlag)) + .map(collectionFieldStyleMap -> collectionFieldStyleMap.get(analysisCell)) + .ifPresent(cellData::setOriginCellStyle); + } + } else { + StringBuilder cellValueBuild = new StringBuilder(); + int index = 0; + List> cellDataList = new ArrayList<>(); + + cellWriteHandlerContext.setExcelContentProperty(ExcelContentProperty.EMPTY); + cellWriteHandlerContext.setIgnoreFillStyle(Boolean.TRUE); + + createCell(analysisCell, fillConfig, cellWriteHandlerContext, rowWriteHandlerContext); + Cell cell = cellWriteHandlerContext.getCell(); + + for (String variable : analysisCell.getVariableList()) { + cellValueBuild.append(analysisCell.getPrepareDataList().get(index++)); + if (!dataKeySet.contains(variable)) { + continue; + } + Object value = dataMap.get(variable); + ExcelContentProperty excelContentProperty = ClassUtils.declaredExcelContentProperty(dataMap, + writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadClazz(), variable, + writeContext.currentWriteHolder()); + cellWriteHandlerContext.setOriginalValue(value); + cellWriteHandlerContext.setOriginalFieldClass(FieldUtils.getFieldClass(dataMap, variable, value)); + cellWriteHandlerContext.setExcelContentProperty(excelContentProperty); + cellWriteHandlerContext.setTargetCellDataType(CellDataTypeEnum.STRING); + + WriteCellData cellData = convert(cellWriteHandlerContext); + cellDataList.add(cellData); + + CellDataTypeEnum type = cellData.getType(); + if (type != null) { + switch (type) { + case STRING: + cellValueBuild.append(cellData.getStringValue()); + break; + case BOOLEAN: + cellValueBuild.append(cellData.getBooleanValue()); + break; + case NUMBER: + cellValueBuild.append(cellData.getNumberValue()); + break; + default: + break; + } + } + } + cellValueBuild.append(analysisCell.getPrepareDataList().get(index)); + cell.setCellValue(cellValueBuild.toString()); + cellWriteHandlerContext.setCellDataList(cellDataList); + if (CollectionUtils.isNotEmpty(cellDataList)) { + cellWriteHandlerContext.setFirstCellData(cellDataList.get(0)); + } + + // Restyle + if (fillConfig.getAutoStyle()) { + Optional.ofNullable(collectionFieldStyleCache.get(currentUniqueDataFlag)) + .map(collectionFieldStyleMap -> collectionFieldStyleMap.get(analysisCell)) + .ifPresent(cell::setCellStyle); + } + } + WriteHandlerUtils.afterCellDispose(cellWriteHandlerContext); + } + + // In the case of the fill line may be called many times + if (rowWriteHandlerContext.getRow() != null) { + WriteHandlerUtils.afterRowDispose(rowWriteHandlerContext); + } + } + + private Integer getRelativeRowIndex() { + Integer relativeRowIndex = relativeRowIndexMap.get(currentUniqueDataFlag); + if (relativeRowIndex == null) { + relativeRowIndex = 0; + } else { + relativeRowIndex++; + } + relativeRowIndexMap.put(currentUniqueDataFlag, relativeRowIndex); + return relativeRowIndex; + } + + private void createCell(AnalysisCell analysisCell, FillConfig fillConfig, + CellWriteHandlerContext cellWriteHandlerContext, RowWriteHandlerContext rowWriteHandlerContext) { + Sheet cachedSheet = writeContext.writeSheetHolder().getCachedSheet(); + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + Row row = cachedSheet.getRow(analysisCell.getRowIndex()); + cellWriteHandlerContext.setRow(row); + Cell cell = row.getCell(analysisCell.getColumnIndex()); + cellWriteHandlerContext.setCell(cell); + rowWriteHandlerContext.setRow(row); + rowWriteHandlerContext.setRowIndex(analysisCell.getRowIndex()); + return; + } + Sheet sheet = writeContext.writeSheetHolder().getSheet(); + + Map collectionLastIndexMap = collectionLastIndexCache + .computeIfAbsent(currentUniqueDataFlag, key -> MapUtils.newHashMap()); + + boolean isOriginalCell = false; + Integer lastRowIndex; + Integer lastColumnIndex; + switch (fillConfig.getDirection()) { + case VERTICAL: + lastRowIndex = collectionLastIndexMap.get(analysisCell); + if (lastRowIndex == null) { + lastRowIndex = analysisCell.getRowIndex(); + collectionLastIndexMap.put(analysisCell, lastRowIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastRowIndex); + } + lastColumnIndex = analysisCell.getColumnIndex(); + break; + case HORIZONTAL: + lastRowIndex = analysisCell.getRowIndex(); + lastColumnIndex = collectionLastIndexMap.get(analysisCell); + if (lastColumnIndex == null) { + lastColumnIndex = analysisCell.getColumnIndex(); + collectionLastIndexMap.put(analysisCell, lastColumnIndex); + isOriginalCell = true; + } else { + collectionLastIndexMap.put(analysisCell, ++lastColumnIndex); + } + break; + default: + throw new ExcelGenerateException("The wrong direction."); + } + + Row row = createRowIfNecessary(sheet, cachedSheet, lastRowIndex, fillConfig, analysisCell, isOriginalCell, + rowWriteHandlerContext); + cellWriteHandlerContext.setRow(row); + + cellWriteHandlerContext.setRowIndex(lastRowIndex); + cellWriteHandlerContext.setColumnIndex(lastColumnIndex); + Cell cell = createCellIfNecessary(row, lastColumnIndex, cellWriteHandlerContext); + cellWriteHandlerContext.setCell(cell); + + if (isOriginalCell) { + Map collectionFieldStyleMap = collectionFieldStyleCache.computeIfAbsent( + currentUniqueDataFlag, key -> MapUtils.newHashMap()); + collectionFieldStyleMap.put(analysisCell, cell.getCellStyle()); + } + } + + private Cell createCellIfNecessary(Row row, Integer lastColumnIndex, + CellWriteHandlerContext cellWriteHandlerContext) { + Cell cell = row.getCell(lastColumnIndex); + if (cell != null) { + return cell; + } + WriteHandlerUtils.beforeCellCreate(cellWriteHandlerContext); + cell = row.createCell(lastColumnIndex); + cellWriteHandlerContext.setCell(cell); + + WriteHandlerUtils.afterCellCreate(cellWriteHandlerContext); + return cell; + } + + private Row createRowIfNecessary(Sheet sheet, Sheet cachedSheet, Integer lastRowIndex, FillConfig fillConfig, + AnalysisCell analysisCell, boolean isOriginalCell, RowWriteHandlerContext rowWriteHandlerContext) { + rowWriteHandlerContext.setRowIndex(lastRowIndex); + Row row = sheet.getRow(lastRowIndex); + if (row != null) { + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); + rowWriteHandlerContext.setRow(row); + return row; + } + row = cachedSheet.getRow(lastRowIndex); + if (row == null) { + rowWriteHandlerContext.setRowIndex(lastRowIndex); + WriteHandlerUtils.beforeRowCreate(rowWriteHandlerContext); + + if (fillConfig.getForceNewRow()) { + row = cachedSheet.createRow(lastRowIndex); + } else { + // The last row of the middle disk inside empty rows, resulting in cachedSheet can not get inside. + // Will throw Attempting to write a row[" + rownum + "] " + "in the range [0," + this._sh + // .getLastRowNum() + "] that is already written to disk. + try { + row = sheet.createRow(lastRowIndex); + } catch (IllegalArgumentException ignore) { + row = cachedSheet.createRow(lastRowIndex); + } + } + rowWriteHandlerContext.setRow(row); + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); + + WriteHandlerUtils.afterRowCreate(rowWriteHandlerContext); + } else { + checkRowHeight(analysisCell, fillConfig, isOriginalCell, row); + rowWriteHandlerContext.setRow(row); + } + return row; + } + + private void checkRowHeight(AnalysisCell analysisCell, FillConfig fillConfig, boolean isOriginalCell, Row row) { + if (!analysisCell.getFirstRow() || !WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection())) { + return; + } + // fix https://github.com/alibaba/easyexcel/issues/1869 + if (isOriginalCell && PoiUtils.customHeight(row)) { + collectionRowHeightCache.put(currentUniqueDataFlag, row.getHeight()); + return; + } + if (fillConfig.getAutoStyle()) { + Short rowHeight = collectionRowHeightCache.get(currentUniqueDataFlag); + if (rowHeight != null) { + row.setHeight(rowHeight); + } + } + } + + private List readTemplateData(Map> analysisCache) { + List analysisCellList = analysisCache.get(currentUniqueDataFlag); + if (analysisCellList != null) { + return analysisCellList; + } + Sheet sheet = writeContext.writeSheetHolder().getCachedSheet(); + Map> firstRowCache = MapUtils.newHashMapWithExpectedSize(8); + for (int i = 0; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) { + continue; + } + for (int j = 0; j < row.getLastCellNum(); j++) { + Cell cell = row.getCell(j); + if (cell == null) { + continue; + } + String preparedData = prepareData(cell, i, j, firstRowCache); + // Prevent empty data from not being replaced + if (preparedData != null) { + cell.setCellValue(preparedData); + } + } + } + return analysisCache.get(currentUniqueDataFlag); + } + + /** + * To prepare data + * + * @param cell cell + * @param rowIndex row index + * @param columnIndex column index + * @param firstRowCache first row cache + * @return Returns the data that the cell needs to replace + */ + private String prepareData(Cell cell, int rowIndex, int columnIndex, + Map> firstRowCache) { + if (!CellType.STRING.equals(cell.getCellType())) { + return null; + } + String value = cell.getStringCellValue(); + if (StringUtils.isEmpty(value)) { + return null; + } + StringBuilder preparedData = new StringBuilder(); + AnalysisCell analysisCell = null; + + int startIndex = 0; + int length = value.length(); + int lastPrepareDataIndex = 0; + out: + while (startIndex < length) { + int prefixIndex = value.indexOf(FILL_PREFIX, startIndex); + if (prefixIndex < 0) { + break; + } + if (prefixIndex != 0) { + char prefixPrefixChar = value.charAt(prefixIndex - 1); + if (prefixPrefixChar == IGNORE_CHAR) { + startIndex = prefixIndex + 1; + continue; + } + } + int suffixIndex = -1; + while (suffixIndex == -1 && startIndex < length) { + suffixIndex = value.indexOf(FILL_SUFFIX, startIndex + 1); + if (suffixIndex < 0) { + break out; + } + startIndex = suffixIndex + 1; + char prefixSuffixChar = value.charAt(suffixIndex - 1); + if (prefixSuffixChar == IGNORE_CHAR) { + suffixIndex = -1; + } + } + if (analysisCell == null) { + analysisCell = initAnalysisCell(rowIndex, columnIndex); + } + String variable = value.substring(prefixIndex + 1, suffixIndex); + if (StringUtils.isEmpty(variable)) { + continue; + } + int collectPrefixIndex = variable.indexOf(COLLECTION_PREFIX); + if (collectPrefixIndex > -1) { + if (collectPrefixIndex != 0) { + analysisCell.setPrefix(variable.substring(0, collectPrefixIndex)); + } + variable = variable.substring(collectPrefixIndex + 1); + if (StringUtils.isEmpty(variable)) { + continue; + } + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COLLECTION); + } + analysisCell.getVariableList().add(variable); + if (lastPrepareDataIndex == prefixIndex) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + // fix https://github.com/alibaba/easyexcel/issues/2035 + if (lastPrepareDataIndex != 0) { + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + } else { + String data = convertPrepareData(value.substring(lastPrepareDataIndex, prefixIndex)); + preparedData.append(data); + analysisCell.getPrepareDataList().add(data); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + lastPrepareDataIndex = suffixIndex + 1; + } + // fix https://github.com/alibaba/easyexcel/issues/1552 + // When read template, XLSX data may be in `is` labels, and set the time set in `v` label, lead to can't set + // up successfully, so all data format to empty first. + if (analysisCell != null && CollectionUtils.isNotEmpty(analysisCell.getVariableList())) { + cell.setBlank(); + } + return dealAnalysisCell(analysisCell, value, rowIndex, lastPrepareDataIndex, length, firstRowCache, + preparedData); + } + + private String dealAnalysisCell(AnalysisCell analysisCell, String value, int rowIndex, int lastPrepareDataIndex, + int length, Map> firstRowCache, StringBuilder preparedData) { + if (analysisCell != null) { + if (lastPrepareDataIndex == length) { + analysisCell.getPrepareDataList().add(StringUtils.EMPTY); + } else { + analysisCell.getPrepareDataList().add(convertPrepareData(value.substring(lastPrepareDataIndex))); + analysisCell.setOnlyOneVariable(Boolean.FALSE); + } + UniqueDataFlagKey uniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder(), + analysisCell.getPrefix()); + if (WriteTemplateAnalysisCellTypeEnum.COMMON.equals(analysisCell.getCellType())) { + List analysisCellList = templateAnalysisCache.computeIfAbsent(uniqueDataFlag, + key -> ListUtils.newArrayList()); + analysisCellList.add(analysisCell); + } else { + Set uniqueFirstRowCache = firstRowCache.computeIfAbsent(uniqueDataFlag, + key -> new HashSet<>()); + + if (!uniqueFirstRowCache.contains(rowIndex)) { + analysisCell.setFirstRow(Boolean.TRUE); + uniqueFirstRowCache.add(rowIndex); + } + + List collectionAnalysisCellList = templateCollectionAnalysisCache.computeIfAbsent( + uniqueDataFlag, key -> ListUtils.newArrayList()); + + collectionAnalysisCellList.add(analysisCell); + } + return preparedData.toString(); + } + return null; + } + + private AnalysisCell initAnalysisCell(Integer rowIndex, Integer columnIndex) { + AnalysisCell analysisCell = new AnalysisCell(); + analysisCell.setRowIndex(rowIndex); + analysisCell.setColumnIndex(columnIndex); + analysisCell.setOnlyOneVariable(Boolean.TRUE); + List variableList = ListUtils.newArrayList(); + analysisCell.setVariableList(variableList); + List prepareDataList = ListUtils.newArrayList(); + analysisCell.setPrepareDataList(prepareDataList); + analysisCell.setCellType(WriteTemplateAnalysisCellTypeEnum.COMMON); + analysisCell.setFirstRow(Boolean.FALSE); + return analysisCell; + } + + private String convertPrepareData(String prepareData) { + prepareData = prepareData.replaceAll(ESCAPE_FILL_PREFIX, FILL_PREFIX); + prepareData = prepareData.replaceAll(ESCAPE_FILL_SUFFIX, FILL_SUFFIX); + return prepareData; + } + + private UniqueDataFlagKey uniqueDataFlag(WriteSheetHolder writeSheetHolder, String wrapperName) { + return new UniqueDataFlagKey(writeSheetHolder.getSheetNo(), writeSheetHolder.getSheetName(), wrapperName); + } + + @Getter + @Setter + @EqualsAndHashCode + @AllArgsConstructor + public static class UniqueDataFlagKey { + private Integer sheetNo; + private String sheetName; + private String wrapperName; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractCellWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractCellWriteHandler.java new file mode 100644 index 000000000..688598ac1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractCellWriteHandler.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.write.handler; + +import java.util.List; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +/** + * Abstract cell write handler + * + * @author Jiaju Zhuang + * @deprecated Please use it directly {@link CellWriteHandler} + **/ +@Deprecated +public abstract class AbstractCellWriteHandler implements CellWriteHandler { + + @Override + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + WriteCellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractRowWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractRowWriteHandler.java new file mode 100644 index 000000000..286d8757d --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractRowWriteHandler.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; + +import org.apache.poi.ss.usermodel.Row; + +/** + * Abstract row write handler + * + * @author Jiaju Zhuang + * @deprecated Please use it directly {@link RowWriteHandler} + **/ +@Deprecated +public abstract class AbstractRowWriteHandler implements RowWriteHandler { + @Override + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractSheetWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractSheetWriteHandler.java new file mode 100644 index 000000000..d24f90109 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractSheetWriteHandler.java @@ -0,0 +1,23 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * Abstract sheet write handler + * + * @author Jiaju Zhuang + * @deprecated Please use it directly {@link SheetWriteHandler} + **/ +@Deprecated +public abstract class AbstractSheetWriteHandler implements SheetWriteHandler { + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractWorkbookWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractWorkbookWriteHandler.java new file mode 100644 index 000000000..5bb2fc5f1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/AbstractWorkbookWriteHandler.java @@ -0,0 +1,28 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * Abstract workbook write handler + * + * @author Jiaju Zhuang + * @deprecated Please use it directly {@link WorkbookWriteHandler} + **/ +@Deprecated +public abstract class AbstractWorkbookWriteHandler implements WorkbookWriteHandler { + + @Override + public void beforeWorkbookCreate() { + + } + + @Override + public void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) { + + } + + @Override + public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { + + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java new file mode 100644 index 000000000..fd144e1c1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java @@ -0,0 +1,118 @@ +package com.alibaba.excel.write.handler; + +import java.util.List; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +/** + * intercepts handle cell creation + * + * @author Jiaju Zhuang + */ +public interface CellWriteHandler extends WriteHandler { + + /** + * Called before create the cell + * + * @param context + */ + default void beforeCellCreate(CellWriteHandlerContext context) { + beforeCellCreate(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getRow(), + context.getHeadData(), context.getColumnIndex(), context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called before create the cell + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param row + * @param head Nullable.It is null in the case of fill data and without head. + * @param columnIndex + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead It will always be false when fill data. + */ + default void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {} + + /** + * Called after the cell is created + * + * @param context + */ + default void afterCellCreate(CellWriteHandlerContext context) { + afterCellCreate(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getCell(), + context.getHeadData(), context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called after the cell is created + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param cell + * @param head Nullable.It is null in the case of fill data and without head. + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead It will always be false when fill data. + */ + default void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) {} + + /** + * Called after the cell data is converted + * + * @param context + */ + default void afterCellDataConverted(CellWriteHandlerContext context) { + WriteCellData writeCellData = CollectionUtils.isNotEmpty(context.getCellDataList()) ? context + .getCellDataList().get(0) : null; + afterCellDataConverted(context.getWriteSheetHolder(), context.getWriteTableHolder(), writeCellData, + context.getCell(), context.getHeadData(), context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called after the cell data is converted + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param cell + * @param head Nullable.It is null in the case of fill data and without head. + * @param cellData Nullable.It is null in the case of add header. + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead It will always be false when fill data. + */ + default void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + WriteCellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {} + + /** + * Called after all operations on the cell have been completed + * + * @param context + */ + default void afterCellDispose(CellWriteHandlerContext context) { + afterCellDispose(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getCellDataList(), + context.getCell(), context.getHeadData(), context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called after all operations on the cell have been completed + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param cell + * @param head Nullable.It is null in the case of fill data and without head. + * @param cellDataList Nullable.It is null in the case of add header.There may be several when fill the data. + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead It will always be false when fill data. + */ + default void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {} +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java new file mode 100644 index 000000000..18b96e21f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/DefaultWriteHandlerLoader.java @@ -0,0 +1,60 @@ +package com.alibaba.excel.write.handler; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.handler.impl.DefaultRowWriteHandler; +import com.alibaba.excel.write.handler.impl.DimensionWorkbookWriteHandler; +import com.alibaba.excel.write.handler.impl.FillStyleCellWriteHandler; +import com.alibaba.excel.write.style.DefaultStyle; + +/** + * Load default handler + * + * @author Jiaju Zhuang + */ +public class DefaultWriteHandlerLoader { + + public static final List DEFAULT_WRITE_HANDLER_LIST = new ArrayList<>(); + + static { + DEFAULT_WRITE_HANDLER_LIST.add(new DimensionWorkbookWriteHandler()); + DEFAULT_WRITE_HANDLER_LIST.add(new DefaultRowWriteHandler()); + DEFAULT_WRITE_HANDLER_LIST.add(new FillStyleCellWriteHandler()); + } + + /** + * Load default handler + * + * @return + */ + public static List loadDefaultHandler(Boolean useDefaultStyle, ExcelTypeEnum excelType) { + List handlerList = new ArrayList<>(); + switch (excelType) { + case XLSX: + handlerList.add(new DimensionWorkbookWriteHandler()); + handlerList.add(new DefaultRowWriteHandler()); + handlerList.add(new FillStyleCellWriteHandler()); + if (useDefaultStyle) { + handlerList.add(new DefaultStyle()); + } + break; + case XLS: + handlerList.add(new DefaultRowWriteHandler()); + handlerList.add(new FillStyleCellWriteHandler()); + if (useDefaultStyle) { + handlerList.add(new DefaultStyle()); + } + break; + case CSV: + handlerList.add(new DefaultRowWriteHandler()); + handlerList.add(new FillStyleCellWriteHandler()); + break; + default: + break; + } + return handlerList; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java new file mode 100644 index 000000000..c3dc708b7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/RowWriteHandler.java @@ -0,0 +1,83 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; + +import org.apache.poi.ss.usermodel.Row; + +/** + * intercepts handle row creation + * + * @author Jiaju Zhuang + */ +public interface RowWriteHandler extends WriteHandler { + + /** + * Called before create the row + * + * @param context + */ + default void beforeRowCreate(RowWriteHandlerContext context) { + beforeRowCreate(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getRowIndex(), + context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called before create the row + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param rowIndex + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead Nullable.It is null in the case of fill data. + */ + default void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) {} + + /** + * Called after the row is created + * + * @param context + */ + default void afterRowCreate(RowWriteHandlerContext context) { + afterRowCreate(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getRow(), + context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called after the row is created + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param row + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead Nullable.It is null in the case of fill data. + */ + default void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) {} + + /** + * Called after all operations on the row have been completed. + * In the case of the fill , may be called many times. + * + * @param context + */ + default void afterRowDispose(RowWriteHandlerContext context) { + afterRowDispose(context.getWriteSheetHolder(), context.getWriteTableHolder(), context.getRow(), + context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Called after all operations on the row have been completed. + * In the case of the fill , may be called many times. + * + * @param writeSheetHolder + * @param writeTableHolder Nullable.It is null without using table writes. + * @param row + * @param relativeRowIndex Nullable.It is null in the case of fill data. + * @param isHead Nullable.It is null in the case of fill data. + */ + default void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) {} +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java new file mode 100644 index 000000000..67672b7de --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/SheetWriteHandler.java @@ -0,0 +1,47 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * intercepts handle sheet creation + * + * @author Jiaju Zhuang + */ +public interface SheetWriteHandler extends WriteHandler { + + /** + * Called before create the sheet + * + * @param context + */ + default void beforeSheetCreate(SheetWriteHandlerContext context) { + beforeSheetCreate(context.getWriteWorkbookHolder(), context.getWriteSheetHolder()); + } + + /** + * Called before create the sheet + * + * @param writeWorkbookHolder + * @param writeSheetHolder + */ + default void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {} + + /** + * Called after the sheet is created + * + * @param context + */ + default void afterSheetCreate(SheetWriteHandlerContext context) { + afterSheetCreate(context.getWriteWorkbookHolder(), context.getWriteSheetHolder()); + } + + /** + * Called after the sheet is created + * + * @param writeWorkbookHolder + * @param writeSheetHolder + */ + default void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {} +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java new file mode 100644 index 000000000..140d226cf --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/WorkbookWriteHandler.java @@ -0,0 +1,58 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * intercepts handle Workbook creation + * + * @author Jiaju Zhuang + */ +public interface WorkbookWriteHandler extends WriteHandler { + + /** + * Called before create the workbook + * + * @param context + */ + default void beforeWorkbookCreate(WorkbookWriteHandlerContext context) { + beforeWorkbookCreate(); + } + + /** + * Called before create the workbook + */ + default void beforeWorkbookCreate() {} + + /** + * Called after the workbook is created + * + * @param context + */ + default void afterWorkbookCreate(WorkbookWriteHandlerContext context) { + afterWorkbookCreate(context.getWriteWorkbookHolder()); + } + + /** + * Called after the workbook is created + * + * @param writeWorkbookHolder + */ + default void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) {} + + /** + * Called after all operations on the workbook have been completed + * + * @param context + */ + default void afterWorkbookDispose(WorkbookWriteHandlerContext context) { + afterWorkbookDispose(context.getWriteWorkbookHolder()); + } + + /** + * Called after all operations on the workbook have been completed + * + * @param writeWorkbookHolder + */ + default void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) {} +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/WriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/WriteHandler.java new file mode 100644 index 000000000..3864ac7b0 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/WriteHandler.java @@ -0,0 +1,10 @@ +package com.alibaba.excel.write.handler; + +import com.alibaba.excel.event.Handler; + +/** + * intercepts handle excel write + * + * @author Jiaju Zhuang + */ +public interface WriteHandler extends Handler {} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/CellHandlerExecutionChain.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/CellHandlerExecutionChain.java new file mode 100644 index 000000000..35afe6fce --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/CellHandlerExecutionChain.java @@ -0,0 +1,67 @@ +package com.alibaba.excel.write.handler.chain; + +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Execute the cell handler chain + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CellHandlerExecutionChain { + /** + * next chain + */ + private CellHandlerExecutionChain next; + /** + * handler + */ + private CellWriteHandler handler; + + public CellHandlerExecutionChain(CellWriteHandler handler) { + this.handler = handler; + } + + public void beforeCellCreate(CellWriteHandlerContext context) { + this.handler.beforeCellCreate(context); + if (this.next != null) { + this.next.beforeCellCreate(context); + } + } + + public void afterCellCreate(CellWriteHandlerContext context) { + this.handler.afterCellCreate(context); + if (this.next != null) { + this.next.afterCellCreate(context); + } + } + + public void afterCellDataConverted(CellWriteHandlerContext context) { + this.handler.afterCellDataConverted(context); + if (this.next != null) { + this.next.afterCellDataConverted(context); + } + } + + public void afterCellDispose(CellWriteHandlerContext context) { + this.handler.afterCellDispose(context); + if (this.next != null) { + this.next.afterCellDispose(context); + } + } + + public void addLast(CellWriteHandler handler) { + CellHandlerExecutionChain context = this; + while (context.next != null) { + context = context.next; + } + context.next = new CellHandlerExecutionChain(handler); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/RowHandlerExecutionChain.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/RowHandlerExecutionChain.java new file mode 100644 index 000000000..4f32f3b12 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/RowHandlerExecutionChain.java @@ -0,0 +1,60 @@ +package com.alibaba.excel.write.handler.chain; + +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Execute the row handler chain + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class RowHandlerExecutionChain { + /** + * next chain + */ + private RowHandlerExecutionChain next; + /** + * handler + */ + private RowWriteHandler handler; + + public RowHandlerExecutionChain(RowWriteHandler handler) { + this.handler = handler; + } + + public void beforeRowCreate(RowWriteHandlerContext context) { + this.handler.beforeRowCreate(context); + if (this.next != null) { + this.next.beforeRowCreate(context); + } + } + + public void afterRowCreate(RowWriteHandlerContext context) { + this.handler.afterRowCreate(context); + if (this.next != null) { + this.next.afterRowCreate(context); + } + } + + public void afterRowDispose(RowWriteHandlerContext context) { + this.handler.afterRowDispose(context); + if (this.next != null) { + this.next.afterRowDispose(context); + } + } + + public void addLast(RowWriteHandler handler) { + RowHandlerExecutionChain context = this; + while (context.next != null) { + context = context.next; + } + context.next = new RowHandlerExecutionChain(handler); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/SheetHandlerExecutionChain.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/SheetHandlerExecutionChain.java new file mode 100644 index 000000000..7e079dd23 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/SheetHandlerExecutionChain.java @@ -0,0 +1,52 @@ +package com.alibaba.excel.write.handler.chain; + +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Execute the sheet handler chain + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class SheetHandlerExecutionChain { + /** + * next chain + */ + private SheetHandlerExecutionChain next; + /** + * handler + */ + private SheetWriteHandler handler; + + public SheetHandlerExecutionChain(SheetWriteHandler handler) { + this.handler = handler; + } + + public void beforeSheetCreate(SheetWriteHandlerContext context) { + this.handler.beforeSheetCreate(context); + if (this.next != null) { + this.next.beforeSheetCreate(context); + } + } + + public void afterSheetCreate(SheetWriteHandlerContext context) { + this.handler.afterSheetCreate(context); + if (this.next != null) { + this.next.afterSheetCreate(context); + } + } + public void addLast(SheetWriteHandler handler) { + SheetHandlerExecutionChain context = this; + while (context.next != null) { + context = context.next; + } + context.next = new SheetHandlerExecutionChain(handler); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/WorkbookHandlerExecutionChain.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/WorkbookHandlerExecutionChain.java new file mode 100644 index 000000000..56e746e01 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/chain/WorkbookHandlerExecutionChain.java @@ -0,0 +1,61 @@ +package com.alibaba.excel.write.handler.chain; + +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Execute the workbook handler chain + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class WorkbookHandlerExecutionChain { + /** + * next chain + */ + private WorkbookHandlerExecutionChain next; + + /** + * handler + */ + private WorkbookWriteHandler handler; + + public WorkbookHandlerExecutionChain(WorkbookWriteHandler handler) { + this.handler = handler; + } + + public void beforeWorkbookCreate(WorkbookWriteHandlerContext context) { + this.handler.beforeWorkbookCreate(context); + if (this.next != null) { + this.next.beforeWorkbookCreate(context); + } + } + + public void afterWorkbookCreate(WorkbookWriteHandlerContext context) { + this.handler.afterWorkbookCreate(context); + if (this.next != null) { + this.next.afterWorkbookCreate(context); + } + } + + public void afterWorkbookDispose(WorkbookWriteHandlerContext context) { + this.handler.afterWorkbookDispose(context); + if (this.next != null) { + this.next.afterWorkbookDispose(context); + } + } + + public void addLast(WorkbookWriteHandler handler) { + WorkbookHandlerExecutionChain context = this; + while (context.next != null) { + context = context.next; + } + context.next = new WorkbookHandlerExecutionChain(handler); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/CellWriteHandlerContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/CellWriteHandlerContext.java new file mode 100644 index 000000000..0007e7ace --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/CellWriteHandlerContext.java @@ -0,0 +1,132 @@ +package com.alibaba.excel.write.handler.context; + +import java.util.List; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.write.handler.impl.FillStyleCellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; + +/** + * cell context + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CellWriteHandlerContext { + /** + * write context + */ + private WriteContext writeContext; + /** + * workbook + */ + private WriteWorkbookHolder writeWorkbookHolder; + /** + * sheet + */ + private WriteSheetHolder writeSheetHolder; + /** + * table .Nullable.It is null without using table writes. + */ + private WriteTableHolder writeTableHolder; + /** + * row + */ + private Row row; + /** + * index + */ + private Integer rowIndex; + /** + * cell + */ + private Cell cell; + /** + * index + */ + private Integer columnIndex; + /** + * Nullable.It is null in the case of fill data. + */ + private Integer relativeRowIndex; + /** + * Nullable.It is null in the case of fill data. + */ + private Head headData; + /** + * Nullable.It is null in the case of add header.There may be several when fill the data. + */ + private List> cellDataList; + /** + * Nullable. + * It is null in the case of add header. + * In the case of write there must be not null. + * firstCellData == cellDataList.get(0) + */ + private WriteCellData firstCellData; + /** + * Nullable.It is null in the case of fill data. + */ + private Boolean head; + /** + * Field annotation configuration information. + */ + private ExcelContentProperty excelContentProperty; + + /** + * The value of the original + */ + private Object originalValue; + + /** + * The original field type + */ + private Class originalFieldClass; + + /** + * Target cell data type + */ + private CellDataTypeEnum targetCellDataType; + + /** + * Ignore the filling pattern and the {@code FillStyleCellWriteHandler} will not work. + * + * @see FillStyleCellWriteHandler + */ + private Boolean ignoreFillStyle; + + public CellWriteHandlerContext(WriteContext writeContext, + WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, Row row, Integer rowIndex, Cell cell, Integer columnIndex, + Integer relativeRowIndex, Head headData, List> cellDataList, WriteCellData firstCellData, + Boolean head, ExcelContentProperty excelContentProperty) { + this.writeContext = writeContext; + this.writeWorkbookHolder = writeWorkbookHolder; + this.writeSheetHolder = writeSheetHolder; + this.writeTableHolder = writeTableHolder; + this.row = row; + this.rowIndex = rowIndex; + this.cell = cell; + this.columnIndex = columnIndex; + this.relativeRowIndex = relativeRowIndex; + this.headData = headData; + this.cellDataList = cellDataList; + this.firstCellData = firstCellData; + this.head = head; + this.excelContentProperty = excelContentProperty; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/RowWriteHandlerContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/RowWriteHandlerContext.java new file mode 100644 index 000000000..c01c8b5b7 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/RowWriteHandlerContext.java @@ -0,0 +1,56 @@ +package com.alibaba.excel.write.handler.context; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.usermodel.Row; + +/** + * row context + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +public class RowWriteHandlerContext { + /** + * write context + */ + private WriteContext writeContext; + /** + * workbook + */ + private WriteWorkbookHolder writeWorkbookHolder; + /** + * sheet + */ + private WriteSheetHolder writeSheetHolder; + /** + * table .Nullable.It is null without using table writes. + */ + private WriteTableHolder writeTableHolder; + /** + * row index + */ + private Integer rowIndex; + /** + * row + */ + private Row row; + /** + * Nullable.It is null in the case of fill data. + */ + private Integer relativeRowIndex; + /** + * Nullable.It is null in the case of fill data. + */ + private Boolean head; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/SheetWriteHandlerContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/SheetWriteHandlerContext.java new file mode 100644 index 000000000..f3c024f48 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/SheetWriteHandlerContext.java @@ -0,0 +1,34 @@ +package com.alibaba.excel.write.handler.context; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * sheet context + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +public class SheetWriteHandlerContext { + /** + * write context + */ + private WriteContext writeContext; + /** + * workbook + */ + private WriteWorkbookHolder writeWorkbookHolder; + /** + * sheet + */ + private WriteSheetHolder writeSheetHolder; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/WorkbookWriteHandlerContext.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/WorkbookWriteHandlerContext.java new file mode 100644 index 000000000..a7ec289c1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/context/WorkbookWriteHandlerContext.java @@ -0,0 +1,29 @@ +package com.alibaba.excel.write.handler.context; + +import com.alibaba.excel.context.WriteContext; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * workbook context + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +public class WorkbookWriteHandlerContext { + /** + * write context + */ + private WriteContext writeContext; + /** + * workbook + */ + private WriteWorkbookHolder writeWorkbookHolder; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/DefaultRowWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/DefaultRowWriteHandler.java new file mode 100644 index 000000000..2d1f3f779 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/DefaultRowWriteHandler.java @@ -0,0 +1,21 @@ +package com.alibaba.excel.write.handler.impl; + +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; + +import lombok.extern.slf4j.Slf4j; + +/** + * Default row handler. + * + * @author Jiaju Zhuang + */ +@Slf4j +public class DefaultRowWriteHandler implements RowWriteHandler { + + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + context.getWriteSheetHolder().setLastRowIndex(context.getRowIndex()); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/DimensionWorkbookWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/DimensionWorkbookWriteHandler.java new file mode 100644 index 000000000..6b444a475 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/DimensionWorkbookWriteHandler.java @@ -0,0 +1,81 @@ +package com.alibaba.excel.write.handler.impl; + +import java.lang.reflect.Field; +import java.util.Map; + +import com.alibaba.excel.util.FieldUtils; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; + +/** + * Handle the problem of unable to write dimension. + * + * https://github.com/alibaba/easyexcel/issues/1282 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class DimensionWorkbookWriteHandler implements WorkbookWriteHandler { + + private static final String XSSF_SHEET_MEMBER_VARIABLE_NAME = "_sh"; + private static final Field XSSF_SHEET_FIELD = FieldUtils.getField(SXSSFSheet.class, XSSF_SHEET_MEMBER_VARIABLE_NAME, + true); + + @Override + public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { + if (writeWorkbookHolder == null || writeWorkbookHolder.getWorkbook() == null) { + return; + } + if (!(writeWorkbookHolder.getWorkbook() instanceof SXSSFWorkbook)) { + return; + } + + Map writeSheetHolderMap = writeWorkbookHolder.getHasBeenInitializedSheetIndexMap(); + if (MapUtils.isEmpty(writeSheetHolderMap)) { + return; + } + for (WriteSheetHolder writeSheetHolder : writeSheetHolderMap.values()) { + if (writeSheetHolder.getSheet() == null || !(writeSheetHolder.getSheet() instanceof SXSSFSheet)) { + continue; + } + SXSSFSheet sxssfSheet = ((SXSSFSheet)writeSheetHolder.getSheet()); + XSSFSheet xssfSheet; + try { + xssfSheet = (XSSFSheet)XSSF_SHEET_FIELD.get(sxssfSheet); + } catch (IllegalAccessException e) { + log.debug("Can not found _sh.", e); + continue; + } + if (xssfSheet == null) { + continue; + } + CTWorksheet ctWorksheet = xssfSheet.getCTWorksheet(); + if (ctWorksheet == null) { + continue; + } + int headSize = 0; + if (MapUtils.isNotEmpty(writeSheetHolder.getExcelWriteHeadProperty().getHeadMap())) { + headSize = writeSheetHolder.getExcelWriteHeadProperty().getHeadMap().size(); + if (headSize > 0) { + headSize--; + } + } + Integer lastRowIndex = writeSheetHolder.getLastRowIndex(); + if (lastRowIndex == null) { + lastRowIndex = 0; + } + + ctWorksheet.getDimension().setRef( + "A1:" + CellReference.convertNumToColString(headSize) + (lastRowIndex + 1)); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/FillStyleCellWriteHandler.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/FillStyleCellWriteHandler.java new file mode 100644 index 000000000..d3680e24b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/handler/impl/FillStyleCellWriteHandler.java @@ -0,0 +1,46 @@ +package com.alibaba.excel.write.handler.impl; + +import com.alibaba.excel.constant.OrderConstant; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.CellStyle; + +/** + * fill cell style. + * + * @author Jiaju Zhuang + */ +@Slf4j +public class FillStyleCellWriteHandler implements CellWriteHandler { + + @Override + public int order() { + return OrderConstant.FILL_STYLE; + } + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (BooleanUtils.isTrue(context.getIgnoreFillStyle())) { + return; + } + + WriteCellData cellData = context.getFirstCellData(); + if (cellData == null) { + return; + } + WriteCellStyle writeCellStyle = cellData.getWriteCellStyle(); + CellStyle originCellStyle = cellData.getOriginCellStyle(); + if (writeCellStyle == null && originCellStyle == null) { + return; + } + WriteWorkbookHolder writeWorkbookHolder = context.getWriteWorkbookHolder(); + context.getCell().setCellStyle(writeWorkbookHolder.createCellStyle(writeCellStyle, originCellStyle)); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java new file mode 100644 index 000000000..70db8a01f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.write.merge; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Sheet; + +/** + * Merge strategy + * + * @author Jiaju Zhuang + */ +public abstract class AbstractMergeStrategy implements CellWriteHandler { + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (context.getHead()) { + return; + } + merge(context.getWriteSheetHolder().getSheet(), context.getCell(), context.getHeadData(), + context.getRelativeRowIndex()); + } + + /** + * merge + * + * @param sheet + * @param cell + * @param head + * @param relativeRowIndex + */ + protected abstract void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java new file mode 100644 index 000000000..79db35b73 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/LoopMergeStrategy.java @@ -0,0 +1,67 @@ +package com.alibaba.excel.write.merge; + +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; + +import org.apache.poi.ss.util.CellRangeAddress; + +/** + * The regions of the loop merge + * + * @author Jiaju Zhuang + */ +public class LoopMergeStrategy implements RowWriteHandler { + /** + * Each row + */ + private final int eachRow; + /** + * Extend column + */ + private final int columnExtend; + /** + * The number of the current column + */ + private final int columnIndex; + + public LoopMergeStrategy(int eachRow, int columnIndex) { + this(eachRow, 1, columnIndex); + } + + public LoopMergeStrategy(int eachRow, int columnExtend, int columnIndex) { + if (eachRow < 1) { + throw new IllegalArgumentException("EachRows must be greater than 1"); + } + if (columnExtend < 1) { + throw new IllegalArgumentException("ColumnExtend must be greater than 1"); + } + if (columnExtend == 1 && eachRow == 1) { + throw new IllegalArgumentException("ColumnExtend or eachRows must be greater than 1"); + } + if (columnIndex < 0) { + throw new IllegalArgumentException("ColumnIndex must be greater than 0"); + } + this.eachRow = eachRow; + this.columnExtend = columnExtend; + this.columnIndex = columnIndex; + } + + public LoopMergeStrategy(LoopMergeProperty loopMergeProperty, Integer columnIndex) { + this(loopMergeProperty.getEachRow(), loopMergeProperty.getColumnExtend(), columnIndex); + } + + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + if (context.getHead() || context.getRelativeRowIndex() == null) { + return; + } + if (context.getRelativeRowIndex() % eachRow == 0) { + CellRangeAddress cellRangeAddress = new CellRangeAddress(context.getRowIndex(), + context.getRowIndex() + eachRow - 1, + columnIndex, columnIndex + columnExtend - 1); + context.getWriteSheetHolder().getSheet().addMergedRegionUnsafe(cellRangeAddress); + } + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java new file mode 100644 index 000000000..273d8879b --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/merge/OnceAbsoluteMergeStrategy.java @@ -0,0 +1,54 @@ +package com.alibaba.excel.write.merge; + +import org.apache.poi.ss.util.CellRangeAddress; + +import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +/** + * It only merges once when create cell(firstRowIndex,lastRowIndex) + * + * @author Jiaju Zhuang + */ +public class OnceAbsoluteMergeStrategy implements SheetWriteHandler { + /** + * First row + */ + private final int firstRowIndex; + /** + * Last row + */ + private final int lastRowIndex; + /** + * First column + */ + private final int firstColumnIndex; + /** + * Last row + */ + private final int lastColumnIndex; + + public OnceAbsoluteMergeStrategy(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) { + if (firstRowIndex < 0 || lastRowIndex < 0 || firstColumnIndex < 0 || lastColumnIndex < 0) { + throw new IllegalArgumentException("All parameters must be greater than 0"); + } + this.firstRowIndex = firstRowIndex; + this.lastRowIndex = lastRowIndex; + this.firstColumnIndex = firstColumnIndex; + this.lastColumnIndex = lastColumnIndex; + } + + public OnceAbsoluteMergeStrategy(OnceAbsoluteMergeProperty onceAbsoluteMergeProperty) { + this(onceAbsoluteMergeProperty.getFirstRowIndex(), onceAbsoluteMergeProperty.getLastRowIndex(), + onceAbsoluteMergeProperty.getFirstColumnIndex(), onceAbsoluteMergeProperty.getLastColumnIndex()); + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + CellRangeAddress cellRangeAddress = + new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex); + writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/CollectionRowData.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/CollectionRowData.java new file mode 100644 index 000000000..bf98f5c59 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/CollectionRowData.java @@ -0,0 +1,32 @@ +package com.alibaba.excel.write.metadata; + +import java.util.Collection; + +/** + * A collection row of data. + * + * @author Jiaju Zhuang + */ +public class CollectionRowData implements RowData { + + private final Object[] array; + + public CollectionRowData(Collection collection) { + this.array = collection.toArray(); + } + + @Override + public Object get(int index) { + return array[index]; + } + + @Override + public int size() { + return array.length; + } + + @Override + public boolean isEmpty() { + return array.length == 0; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/MapRowData.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/MapRowData.java new file mode 100644 index 000000000..409fc58d8 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/MapRowData.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.write.metadata; + +import java.util.Map; + +/** + * A map row of data. + * + * @author Jiaju Zhuang + */ +public class MapRowData implements RowData { + + private final Map map; + + public MapRowData(Map map) { + this.map = map; + } + + @Override + public Object get(int index) { + return map.get(index); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/RowData.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/RowData.java new file mode 100644 index 000000000..b1fe86a3e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/RowData.java @@ -0,0 +1,35 @@ +package com.alibaba.excel.write.metadata; + +/** + * A row of data. + * + * @author Jiaju Zhuang + */ +public interface RowData { + + /** + * Returns the value to which the specified key is mapped, + * or {@code null} if this map contains no mapping for the key. + * + * @param index + * @return data + */ + Object get(int index); + + /** + * Returns the number of elements in this collection. If this collection + * contains more than Integer.MAX_VALUE elements, returns + * Integer.MAX_VALUE. + * + * @return the number of elements in this collection + */ + int size(); + + /** + * Returns true if this collection contains no elements. + * + * @return true if this collection contains no elements + */ + boolean isEmpty(); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java new file mode 100644 index 000000000..a926209a1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteBasicParameter.java @@ -0,0 +1,66 @@ +package com.alibaba.excel.write.metadata; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.alibaba.excel.metadata.BasicParameter; +import com.alibaba.excel.write.handler.WriteHandler; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Write basic parameter + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class WriteBasicParameter extends BasicParameter { + /** + * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. + */ + private Integer relativeHeadRowIndex; + /** + * Need Head + */ + private Boolean needHead; + /** + * Custom type handler override the default + */ + private List customWriteHandlerList = new ArrayList(); + /** + * Use the default style.Default is true. + */ + private Boolean useDefaultStyle; + /** + * Whether to automatically merge headers.Default is true. + */ + private Boolean automaticMergeHead; + /** + * Ignore the custom columns. + */ + private Collection excludeColumnIndexes; + /** + * Ignore the custom columns. + */ + private Collection excludeColumnFieldNames; + /** + * Only output the custom columns. + */ + private Collection includeColumnIndexes; + /** + * Only output the custom columns. + */ + private Collection includeColumnFieldNames; + + /** + * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. + */ + private Boolean orderByIncludeColumn; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteSheet.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteSheet.java new file mode 100644 index 000000000..1f41376c1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteSheet.java @@ -0,0 +1,24 @@ +package com.alibaba.excel.write.metadata; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Write sheet + * + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteSheet extends WriteBasicParameter { + /** + * Starting from 0 + */ + private Integer sheetNo; + /** + * sheet name + */ + private String sheetName; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteTable.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteTable.java new file mode 100644 index 000000000..6681e1b86 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteTable.java @@ -0,0 +1,20 @@ +package com.alibaba.excel.write.metadata; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * table + * + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteTable extends WriteBasicParameter { + /** + * Starting from 0 + */ + private Integer tableNo; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java new file mode 100644 index 000000000..61bd92550 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/WriteWorkbook.java @@ -0,0 +1,89 @@ +package com.alibaba.excel.write.metadata; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; + +import com.alibaba.excel.support.ExcelTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Workbook + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class WriteWorkbook extends WriteBasicParameter { + /** + * Excel type.The default is xlsx + */ + private ExcelTypeEnum excelType; + /** + * Final output file + *

+ * If 'outputStream' and 'file' all not empty, file first + */ + private File file; + /** + * Final output stream + *

+ * If 'outputStream' and 'file' all not empty, file first + */ + private OutputStream outputStream; + /** + * charset. + * Only work on the CSV file + */ + private Charset charset; + + /** + * Set the encoding prefix in the csv file, otherwise the office may open garbled characters. + * Default true. + */ + private Boolean withBom; + + /** + * Template input stream + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private InputStream templateInputStream; + + /** + * Template file. + * This file is read into memory, excessive cases can lead to OOM. + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private File templateFile; + /** + * Default true. + */ + private Boolean autoCloseStream; + /** + * Mandatory use 'inputStream' .Default is false + */ + private Boolean mandatoryUseInputStream; + /** + * Whether the encryption + *

+ * WARRING:Encryption is when the entire file is read into memory, so it is very memory intensive. + */ + private String password; + /** + * Write excel in memory. Default false, the cache file is created and finally written to excel. + *

+ * Comment and RichTextString are only supported in memory mode. + */ + private Boolean inMemory; + /** + * Excel is also written in the event of an exception being thrown.The default false. + */ + private Boolean writeExcelOnException; +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java new file mode 100644 index 000000000..783c7d89f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/AnalysisCell.java @@ -0,0 +1,50 @@ +package com.alibaba.excel.write.metadata.fill; + +import java.util.List; + +import com.alibaba.excel.enums.WriteTemplateAnalysisCellTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Read the cells of the template while populating the data. + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class AnalysisCell { + private int columnIndex; + private int rowIndex; + private List variableList; + private List prepareDataList; + private Boolean onlyOneVariable; + private WriteTemplateAnalysisCellTypeEnum cellType; + private String prefix; + private Boolean firstRow; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnalysisCell that = (AnalysisCell)o; + if (columnIndex != that.columnIndex) { + return false; + } + return rowIndex == that.rowIndex; + } + + @Override + public int hashCode() { + int result = columnIndex; + result = 31 * result + rowIndex; + return result; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java new file mode 100644 index 000000000..ac5581eed --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/FillConfig.java @@ -0,0 +1,57 @@ +package com.alibaba.excel.write.metadata.fill; + +import com.alibaba.excel.enums.WriteDirectionEnum; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * Fill config + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class FillConfig { + private WriteDirectionEnum direction; + /** + * Create a new row each time you use the list parameter.The default create if necessary. + *

+ * Warnning:If you use forceNewRow set true, will not be able to use asynchronous write file, simply + * say the whole file will be stored in memory. + */ + private Boolean forceNewRow; + + /** + * Automatically inherit style + * + * default true. + */ + private Boolean autoStyle; + + private boolean hasInit; + + public void init() { + if (hasInit) { + return; + } + if (direction == null) { + direction = WriteDirectionEnum.VERTICAL; + } + if (forceNewRow == null) { + forceNewRow = Boolean.FALSE; + } + if (autoStyle == null) { + autoStyle = Boolean.TRUE; + } + hasInit = true; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java new file mode 100644 index 000000000..e25e4da3a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/fill/FillWrapper.java @@ -0,0 +1,44 @@ +package com.alibaba.excel.write.metadata.fill; + +import java.util.Collection; + +/** + * Multiple lists are supported when packing + * + * @author Jiaju Zhuang + **/ +public class FillWrapper { + /** + * The collection prefix that needs to be filled. + */ + private String name; + /** + * Data that needs to be filled. + */ + private Collection collectionData; + + public FillWrapper(Collection collectionData) { + this.collectionData = collectionData; + } + + public FillWrapper(String name, Collection collectionData) { + this.name = name; + this.collectionData = collectionData; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Collection getCollectionData() { + return collectionData; + } + + public void setCollectionData(Collection collectionData) { + this.collectionData = collectionData; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java new file mode 100644 index 000000000..d03ea3d8f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/AbstractWriteHolder.java @@ -0,0 +1,525 @@ +package com.alibaba.excel.write.metadata.holder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import com.alibaba.excel.constant.OrderConstant; +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ConverterKeyBuild; +import com.alibaba.excel.converters.DefaultConverterLoader; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.event.NotRepeatExecutor; +import com.alibaba.excel.metadata.AbstractHolder; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; +import com.alibaba.excel.metadata.property.RowHeightProperty; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.DefaultWriteHandlerLoader; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.WriteHandler; +import com.alibaba.excel.write.handler.chain.CellHandlerExecutionChain; +import com.alibaba.excel.write.handler.chain.RowHandlerExecutionChain; +import com.alibaba.excel.write.handler.chain.SheetHandlerExecutionChain; +import com.alibaba.excel.write.handler.chain.WorkbookHandlerExecutionChain; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.WriteBasicParameter; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.property.ExcelWriteHeadProperty; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; +import com.alibaba.excel.write.style.column.AbstractHeadColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; + +/** + * Write holder configuration + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public abstract class AbstractWriteHolder extends AbstractHolder implements WriteHolder { + /** + * Need Head + */ + private Boolean needHead; + /** + * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. + */ + private Integer relativeHeadRowIndex; + /** + * Excel head property + */ + private ExcelWriteHeadProperty excelWriteHeadProperty; + /** + * Use the default style.Default is true. + */ + private Boolean useDefaultStyle; + /** + * Whether to automatically merge headers.Default is true. + */ + private Boolean automaticMergeHead; + + /** + * Ignore the custom columns. + */ + private Collection excludeColumnIndexes; + /** + * Ignore the custom columns. + */ + private Collection excludeColumnFieldNames; + /** + * Only output the custom columns. + */ + private Collection includeColumnIndexes; + /** + * Only output the custom columns. + */ + private Collection includeColumnFieldNames; + + /** + * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. + */ + private Boolean orderByIncludeColumn; + + /** + * Write handler + */ + private List writeHandlerList; + + /** + * Execute the workbook handler chain + * Created in the sheet in the workbook interceptors will not be executed because the workbook to + * create an event long past. So when initializing sheet, supplementary workbook event. + */ + public WorkbookHandlerExecutionChain ownWorkbookHandlerExecutionChain; + /** + * Execute the sheet handler chain + * Created in the sheet in the workbook interceptors will not be executed because the workbook to + * create an event long past. So when initializing sheet, supplementary workbook event. + */ + public SheetHandlerExecutionChain ownSheetHandlerExecutionChain; + + /** + * Execute the workbook handler chain + */ + public WorkbookHandlerExecutionChain workbookHandlerExecutionChain; + /** + * Execute the sheet handler chain + */ + public SheetHandlerExecutionChain sheetHandlerExecutionChain; + + /** + * Execute the row handler chain + */ + public RowHandlerExecutionChain rowHandlerExecutionChain; + + /** + * Execute the cell handler chain + */ + public CellHandlerExecutionChain cellHandlerExecutionChain; + + public AbstractWriteHolder(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder) { + super(writeBasicParameter, parentAbstractWriteHolder); + + if (writeBasicParameter.getUseScientificFormat() != null) { + throw new UnsupportedOperationException("Currently does not support setting useScientificFormat."); + } + + if (writeBasicParameter.getNeedHead() == null) { + if (parentAbstractWriteHolder == null) { + this.needHead = Boolean.TRUE; + } else { + this.needHead = parentAbstractWriteHolder.getNeedHead(); + } + } else { + this.needHead = writeBasicParameter.getNeedHead(); + } + + if (writeBasicParameter.getRelativeHeadRowIndex() == null) { + if (parentAbstractWriteHolder == null) { + this.relativeHeadRowIndex = 0; + } else { + this.relativeHeadRowIndex = parentAbstractWriteHolder.getRelativeHeadRowIndex(); + } + } else { + this.relativeHeadRowIndex = writeBasicParameter.getRelativeHeadRowIndex(); + } + + if (writeBasicParameter.getUseDefaultStyle() == null) { + if (parentAbstractWriteHolder == null) { + this.useDefaultStyle = Boolean.TRUE; + } else { + this.useDefaultStyle = parentAbstractWriteHolder.getUseDefaultStyle(); + } + } else { + this.useDefaultStyle = writeBasicParameter.getUseDefaultStyle(); + } + + if (writeBasicParameter.getAutomaticMergeHead() == null) { + if (parentAbstractWriteHolder == null) { + this.automaticMergeHead = Boolean.TRUE; + } else { + this.automaticMergeHead = parentAbstractWriteHolder.getAutomaticMergeHead(); + } + } else { + this.automaticMergeHead = writeBasicParameter.getAutomaticMergeHead(); + } + + if (writeBasicParameter.getExcludeColumnFieldNames() == null && parentAbstractWriteHolder != null) { + this.excludeColumnFieldNames = parentAbstractWriteHolder.getExcludeColumnFieldNames(); + } else { + this.excludeColumnFieldNames = writeBasicParameter.getExcludeColumnFieldNames(); + } + if (writeBasicParameter.getExcludeColumnIndexes() == null && parentAbstractWriteHolder != null) { + this.excludeColumnIndexes = parentAbstractWriteHolder.getExcludeColumnIndexes(); + } else { + this.excludeColumnIndexes = writeBasicParameter.getExcludeColumnIndexes(); + } + if (writeBasicParameter.getIncludeColumnFieldNames() == null && parentAbstractWriteHolder != null) { + this.includeColumnFieldNames = parentAbstractWriteHolder.getIncludeColumnFieldNames(); + } else { + this.includeColumnFieldNames = writeBasicParameter.getIncludeColumnFieldNames(); + } + + if (writeBasicParameter.getOrderByIncludeColumn() == null) { + if (parentAbstractWriteHolder == null) { + this.orderByIncludeColumn = Boolean.FALSE; + } else { + this.orderByIncludeColumn = parentAbstractWriteHolder.getOrderByIncludeColumn(); + } + } else { + this.orderByIncludeColumn = writeBasicParameter.getOrderByIncludeColumn(); + } + + if (writeBasicParameter.getIncludeColumnIndexes() == null && parentAbstractWriteHolder != null) { + this.includeColumnIndexes = parentAbstractWriteHolder.getIncludeColumnIndexes(); + } else { + this.includeColumnIndexes = writeBasicParameter.getIncludeColumnIndexes(); + } + + // Initialization property + this.excelWriteHeadProperty = new ExcelWriteHeadProperty(this, getClazz(), getHead()); + + // Set converterMap + if (parentAbstractWriteHolder == null) { + setConverterMap(DefaultConverterLoader.loadDefaultWriteConverter()); + } else { + setConverterMap(new HashMap<>(parentAbstractWriteHolder.getConverterMap())); + } + if (writeBasicParameter.getCustomConverterList() != null + && !writeBasicParameter.getCustomConverterList().isEmpty()) { + for (Converter converter : writeBasicParameter.getCustomConverterList()) { + getConverterMap().put(ConverterKeyBuild.buildKey(converter.supportJavaTypeKey()), converter); + } + } + } + + protected void initHandler(WriteBasicParameter writeBasicParameter, AbstractWriteHolder parentAbstractWriteHolder) { + // Set writeHandlerMap + List handlerList = new ArrayList<>(); + + // Initialization Annotation + initAnnotationConfig(handlerList, writeBasicParameter); + + if (writeBasicParameter.getCustomWriteHandlerList() != null + && !writeBasicParameter.getCustomWriteHandlerList().isEmpty()) { + handlerList.addAll(writeBasicParameter.getCustomWriteHandlerList()); + } + sortAndClearUpHandler(handlerList, true); + + if (parentAbstractWriteHolder != null) { + if (CollectionUtils.isNotEmpty(parentAbstractWriteHolder.getWriteHandlerList())) { + handlerList.addAll(parentAbstractWriteHolder.getWriteHandlerList()); + } + } else { + if (this instanceof WriteWorkbookHolder) { + handlerList.addAll(DefaultWriteHandlerLoader.loadDefaultHandler(useDefaultStyle, + ((WriteWorkbookHolder)this).getExcelType())); + } + } + sortAndClearUpHandler(handlerList, false); + } + + protected void initAnnotationConfig(List handlerList, WriteBasicParameter writeBasicParameter) { + if (!HeadKindEnum.CLASS.equals(getExcelWriteHeadProperty().getHeadKind())) { + return; + } + if (writeBasicParameter.getClazz() == null) { + return; + } + Map headMap = getExcelWriteHeadProperty().getHeadMap(); + boolean hasColumnWidth = false; + + for (Head head : headMap.values()) { + if (head.getColumnWidthProperty() != null) { + hasColumnWidth = true; + } + dealLoopMerge(handlerList, head); + } + + if (hasColumnWidth) { + dealColumnWidth(handlerList); + } + + dealStyle(handlerList); + dealRowHigh(handlerList); + dealOnceAbsoluteMerge(handlerList); + } + + private void dealStyle(List handlerList) { + WriteHandler styleStrategy = new AbstractVerticalCellStyleStrategy() { + @Override + public int order() { + return OrderConstant.ANNOTATION_DEFINE_STYLE; + } + + @Override + protected WriteCellStyle headCellStyle(CellWriteHandlerContext context) { + Head head = context.getHeadData(); + if (head == null) { + return null; + } + return WriteCellStyle.build(head.getHeadStyleProperty(), head.getHeadFontProperty()); + } + + @Override + protected WriteCellStyle contentCellStyle(CellWriteHandlerContext context) { + ExcelContentProperty excelContentProperty = context.getExcelContentProperty(); + return WriteCellStyle.build(excelContentProperty.getContentStyleProperty(), + excelContentProperty.getContentFontProperty()); + } + }; + handlerList.add(styleStrategy); + } + + private void dealLoopMerge(List handlerList, Head head) { + LoopMergeProperty loopMergeProperty = head.getLoopMergeProperty(); + if (loopMergeProperty == null) { + return; + } + handlerList.add(new LoopMergeStrategy(loopMergeProperty, head.getColumnIndex())); + } + + private void dealOnceAbsoluteMerge(List handlerList) { + OnceAbsoluteMergeProperty onceAbsoluteMergeProperty = + getExcelWriteHeadProperty().getOnceAbsoluteMergeProperty(); + if (onceAbsoluteMergeProperty == null) { + return; + } + handlerList.add(new OnceAbsoluteMergeStrategy(onceAbsoluteMergeProperty)); + } + + private void dealRowHigh(List handlerList) { + RowHeightProperty headRowHeightProperty = getExcelWriteHeadProperty().getHeadRowHeightProperty(); + RowHeightProperty contentRowHeightProperty = getExcelWriteHeadProperty().getContentRowHeightProperty(); + if (headRowHeightProperty == null && contentRowHeightProperty == null) { + return; + } + Short headRowHeight = null; + if (headRowHeightProperty != null) { + headRowHeight = headRowHeightProperty.getHeight(); + } + Short contentRowHeight = null; + if (contentRowHeightProperty != null) { + contentRowHeight = contentRowHeightProperty.getHeight(); + } + handlerList.add(new SimpleRowHeightStyleStrategy(headRowHeight, contentRowHeight)); + } + + private void dealColumnWidth(List handlerList) { + WriteHandler columnWidthStyleStrategy = new AbstractHeadColumnWidthStyleStrategy() { + @Override + protected Integer columnWidth(Head head, Integer columnIndex) { + if (head == null) { + return null; + } + if (head.getColumnWidthProperty() != null) { + return head.getColumnWidthProperty().getWidth(); + } + return null; + } + }; + handlerList.add(columnWidthStyleStrategy); + } + + protected void sortAndClearUpHandler(List handlerList, boolean runOwn) { + // sort + Map> orderExcelWriteHandlerMap = new TreeMap<>(); + for (WriteHandler handler : handlerList) { + int order = handler.order(); + if (orderExcelWriteHandlerMap.containsKey(order)) { + orderExcelWriteHandlerMap.get(order).add(handler); + } else { + List tempHandlerList = new ArrayList<>(); + tempHandlerList.add(handler); + orderExcelWriteHandlerMap.put(order, tempHandlerList); + } + } + // clean up + Set alreadyExistedHandlerSet = new HashSet<>(); + List cleanUpHandlerList = new ArrayList<>(); + for (Map.Entry> entry : orderExcelWriteHandlerMap.entrySet()) { + for (WriteHandler handler : entry.getValue()) { + if (handler instanceof NotRepeatExecutor) { + String uniqueValue = ((NotRepeatExecutor)handler).uniqueValue(); + if (alreadyExistedHandlerSet.contains(uniqueValue)) { + continue; + } + alreadyExistedHandlerSet.add(uniqueValue); + } + cleanUpHandlerList.add(handler); + } + } + + // build chain + if (!runOwn) { + this.writeHandlerList = new ArrayList<>(); + } + for (WriteHandler writeHandler : cleanUpHandlerList) { + buildChain(writeHandler, runOwn); + } + } + + protected void buildChain(WriteHandler writeHandler, boolean runOwn) { + if (writeHandler instanceof CellWriteHandler) { + if (!runOwn) { + if (cellHandlerExecutionChain == null) { + cellHandlerExecutionChain = new CellHandlerExecutionChain((CellWriteHandler)writeHandler); + } else { + cellHandlerExecutionChain.addLast((CellWriteHandler)writeHandler); + } + } + } + if (writeHandler instanceof RowWriteHandler) { + if (!runOwn) { + if (rowHandlerExecutionChain == null) { + rowHandlerExecutionChain = new RowHandlerExecutionChain((RowWriteHandler)writeHandler); + } else { + rowHandlerExecutionChain.addLast((RowWriteHandler)writeHandler); + } + } + } + if (writeHandler instanceof SheetWriteHandler) { + if (!runOwn) { + if (sheetHandlerExecutionChain == null) { + sheetHandlerExecutionChain = new SheetHandlerExecutionChain((SheetWriteHandler)writeHandler); + } else { + sheetHandlerExecutionChain.addLast((SheetWriteHandler)writeHandler); + } + } else { + if (ownSheetHandlerExecutionChain == null) { + ownSheetHandlerExecutionChain = new SheetHandlerExecutionChain((SheetWriteHandler)writeHandler); + } else { + ownSheetHandlerExecutionChain.addLast((SheetWriteHandler)writeHandler); + } + } + } + if (writeHandler instanceof WorkbookWriteHandler) { + if (!runOwn) { + if (workbookHandlerExecutionChain == null) { + workbookHandlerExecutionChain = new WorkbookHandlerExecutionChain( + (WorkbookWriteHandler)writeHandler); + } else { + workbookHandlerExecutionChain.addLast((WorkbookWriteHandler)writeHandler); + } + } else { + if (ownWorkbookHandlerExecutionChain == null) { + ownWorkbookHandlerExecutionChain = new WorkbookHandlerExecutionChain( + (WorkbookWriteHandler)writeHandler); + } else { + ownWorkbookHandlerExecutionChain.addLast((WorkbookWriteHandler)writeHandler); + } + } + } + if (!runOwn) { + this.writeHandlerList.add(writeHandler); + } + } + + @Override + public boolean ignore(String fieldName, Integer columnIndex) { + if (fieldName != null) { + if (includeColumnFieldNames != null && !includeColumnFieldNames.contains(fieldName)) { + return true; + } + if (excludeColumnFieldNames != null && excludeColumnFieldNames.contains(fieldName)) { + return true; + } + } + if (columnIndex != null) { + if (includeColumnIndexes != null && !includeColumnIndexes.contains(columnIndex)) { + return true; + } + if (excludeColumnIndexes != null && excludeColumnIndexes.contains(columnIndex)) { + return true; + } + } + return false; + } + + @Override + public ExcelWriteHeadProperty excelWriteHeadProperty() { + return getExcelWriteHeadProperty(); + } + + @Override + public boolean needHead() { + return getNeedHead(); + } + + @Override + public int relativeHeadRowIndex() { + return getRelativeHeadRowIndex(); + } + + @Override + public boolean automaticMergeHead() { + return getAutomaticMergeHead(); + } + + @Override + public boolean orderByIncludeColumn() { + return getOrderByIncludeColumn(); + } + + @Override + public Collection includeColumnIndexes() { + return getIncludeColumnIndexes(); + } + + @Override + public Collection includeColumnFieldNames() { + return getIncludeColumnFieldNames(); + } + + @Override + public Collection excludeColumnIndexes() { + return getExcludeColumnIndexes(); + } + + @Override + public Collection excludeColumnFieldNames() { + return getExcludeColumnFieldNames(); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java new file mode 100644 index 000000000..ca0fb3e9c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteHolder.java @@ -0,0 +1,88 @@ +package com.alibaba.excel.write.metadata.holder; + +import java.util.Collection; + +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.write.property.ExcelWriteHeadProperty; + +/** + * Get the corresponding Holder + * + * @author Jiaju Zhuang + **/ +public interface WriteHolder extends ConfigurationHolder { + /** + * What 'ExcelWriteHeadProperty' does the currently operated cell need to execute + * + * @return + */ + ExcelWriteHeadProperty excelWriteHeadProperty(); + + /** + * Is to determine if a field needs to be ignored + * + * @param fieldName + * @param columnIndex + * @return + */ + boolean ignore(String fieldName, Integer columnIndex); + + /** + * Whether a header is required for the currently operated cell + * + * @return + */ + boolean needHead(); + + /** + * Whether need automatic merge headers. + * + * @return + */ + boolean automaticMergeHead(); + + /** + * Writes the head relative to the existing contents of the sheet. Indexes are zero-based. + * + * @return + */ + int relativeHeadRowIndex(); + + /** + * Data will be order by {@link #includeColumnFieldNames} or {@link #includeColumnIndexes}. + * + * default is false. + * + * @return + */ + + boolean orderByIncludeColumn(); + + /** + * Only output the custom columns. + * + * @return + */ + Collection includeColumnIndexes(); + + /** + * Only output the custom columns. + * + * @return + */ + Collection includeColumnFieldNames(); + + /** + * Ignore the custom columns. + * + * @return + */ + Collection excludeColumnIndexes(); + + /** + * Ignore the custom columns. + * + * @return + */ + Collection excludeColumnFieldNames(); +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java new file mode 100644 index 000000000..3fa493ffd --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteSheetHolder.java @@ -0,0 +1,134 @@ +package com.alibaba.excel.write.metadata.holder; + +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.enums.WriteLastRowTypeEnum; +import com.alibaba.excel.util.StringUtils; +import com.alibaba.excel.write.metadata.WriteSheet; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFSheet; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class WriteSheetHolder extends AbstractWriteHolder { + /** + * current param + */ + private WriteSheet writeSheet; + /*** + * Current poi Sheet.This is only for writing, and there may be no data in version 07 when template data needs to be + * read. + *

    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link SXSSFSheet}
  • + *
+ */ + private Sheet sheet; + /*** + * Current poi Sheet.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFSheet}
  • + *
  • 07:{@link XSSFSheet}
  • + *
+ */ + private Sheet cachedSheet; + /*** + * sheetNo + */ + private Integer sheetNo; + /*** + * sheetName + */ + private String sheetName; + /*** + * poi sheet + */ + private WriteWorkbookHolder parentWriteWorkbookHolder; + /*** + * has been initialized table + */ + private Map hasBeenInitializedTable; + + /** + * last column type + * + * @param writeSheet + * @param writeWorkbookHolder + */ + private WriteLastRowTypeEnum writeLastRowTypeEnum; + + /** + * last row index + */ + private Integer lastRowIndex; + + public WriteSheetHolder(WriteSheet writeSheet, WriteWorkbookHolder writeWorkbookHolder) { + super(writeSheet, writeWorkbookHolder); + + // init handler + initHandler(writeSheet, writeWorkbookHolder); + + this.writeSheet = writeSheet; + if (writeSheet.getSheetNo() == null && StringUtils.isEmpty(writeSheet.getSheetName())) { + this.sheetNo = 0; + } else { + this.sheetNo = writeSheet.getSheetNo(); + } + this.sheetName = writeSheet.getSheetName(); + this.parentWriteWorkbookHolder = writeWorkbookHolder; + this.hasBeenInitializedTable = new HashMap<>(); + if (writeWorkbookHolder.getTempTemplateInputStream() != null) { + writeLastRowTypeEnum = WriteLastRowTypeEnum.TEMPLATE_EMPTY; + } else { + writeLastRowTypeEnum = WriteLastRowTypeEnum.COMMON_EMPTY; + } + lastRowIndex = 0; + } + + /** + * Get the last line of index, you have to make sure that the data is written next + * + * @return + */ + public int getNewRowIndexAndStartDoWrite() { + // 'getLastRowNum' doesn't matter if it has one or zero, it's zero + int newRowIndex = 0; + switch (writeLastRowTypeEnum) { + case TEMPLATE_EMPTY: + newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum()); + if (newRowIndex != 0 || cachedSheet.getRow(0) != null) { + newRowIndex++; + } + break; + case HAS_DATA: + newRowIndex = Math.max(sheet.getLastRowNum(), cachedSheet.getLastRowNum()); + newRowIndex++; + break; + default: + break; + } + writeLastRowTypeEnum = WriteLastRowTypeEnum.HAS_DATA; + return newRowIndex; + } + + @Override + public HolderEnum holderType() { + return HolderEnum.SHEET; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteTableHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteTableHolder.java new file mode 100644 index 000000000..d72544be3 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteTableHolder.java @@ -0,0 +1,46 @@ +package com.alibaba.excel.write.metadata.holder; + +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.write.metadata.WriteTable; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * sheet holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteTableHolder extends AbstractWriteHolder { + /*** + * poi sheet + */ + private WriteSheetHolder parentWriteSheetHolder; + /*** + * tableNo + */ + private Integer tableNo; + /** + * current table param + */ + private WriteTable writeTable; + + public WriteTableHolder(WriteTable writeTable, WriteSheetHolder writeSheetHolder) { + super(writeTable, writeSheetHolder); + this.parentWriteSheetHolder = writeSheetHolder; + this.tableNo = writeTable.getTableNo(); + this.writeTable = writeTable; + + // init handler + initHandler(writeTable, writeSheetHolder); + } + + @Override + public HolderEnum holderType() { + return HolderEnum.TABLE; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java new file mode 100644 index 000000000..37d919753 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/holder/WriteWorkbookHolder.java @@ -0,0 +1,376 @@ +package com.alibaba.excel.write.metadata.holder; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.excel.enums.CacheLocationEnum; +import com.alibaba.excel.enums.HolderEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.IoUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.util.StyleUtil; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; +import com.alibaba.excel.write.metadata.WriteWorkbook; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString.Exclude; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.hssf.usermodel.HSSFCellStyle; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +/** + * Workbook holder + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@Slf4j +public class WriteWorkbookHolder extends AbstractWriteHolder { + /*** + * Current poi Workbook.This is only for writing, and there may be no data in version 07 when template data needs to + * be read. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link SXSSFWorkbook}
  • + *
+ */ + private Workbook workbook; + /*** + * Current poi Workbook.Be sure to use and this method when reading template data. + *
    + *
  • 03:{@link HSSFWorkbook}
  • + *
  • 07:{@link XSSFWorkbook}
  • + *
+ */ + private Workbook cachedWorkbook; + /** + * current param + */ + private WriteWorkbook writeWorkbook; + /** + * Final output file + *

+ * If 'outputStream' and 'file' all not empty, file first + */ + private File file; + /** + * Final output stream + */ + private OutputStream outputStream; + /** + * charset. + * Only work on the CSV file + */ + private Charset charset; + + /** + * Set the encoding prefix in the csv file, otherwise the office may open garbled characters. + * Default true. + */ + private Boolean withBom; + + /** + * Template input stream + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private InputStream templateInputStream; + /** + * Template file + *

+ * If 'inputStream' and 'file' all not empty, file first + */ + private File templateFile; + /** + * Temporary template file stream. + *

+ * A temporary file stream needs to be created in order not to modify the original template file. + */ + private InputStream tempTemplateInputStream; + /** + * Default true + */ + private Boolean autoCloseStream; + /** + * Excel type + */ + private ExcelTypeEnum excelType; + /** + * Mandatory use 'inputStream' + */ + private Boolean mandatoryUseInputStream; + /** + * prevent duplicate creation of sheet objects + */ + private Map hasBeenInitializedSheetIndexMap; + /** + * prevent duplicate creation of sheet objects + */ + private Map hasBeenInitializedSheetNameMap; + /** + * Whether the encryption + */ + private String password; + /** + * Write excel in memory. Default false, the cache file is created and finally written to excel. + *

+ * Comment and RichTextString are only supported in memory mode. + */ + private Boolean inMemory; + /** + * Excel is also written in the event of an exception being thrown.The default false. + */ + private Boolean writeExcelOnException; + + /** + * Used to cell style. + */ + private Map> cellStyleIndexMap; + /** + * Used to font. + */ + private Map fontMap; + /** + * Used to data format. + */ + private Map dataFormatMap; + + /** + * handler context + */ + @Exclude + private WorkbookWriteHandlerContext workbookWriteHandlerContext; + + public WriteWorkbookHolder(WriteWorkbook writeWorkbook) { + super(writeWorkbook, null); + this.writeWorkbook = writeWorkbook; + this.file = writeWorkbook.getFile(); + if (file != null) { + try { + this.outputStream = new FileOutputStream(file); + } catch (FileNotFoundException e) { + throw new ExcelGenerateException("Can not found file.", e); + } + } else { + this.outputStream = writeWorkbook.getOutputStream(); + } + + if (writeWorkbook.getCharset() == null) { + this.charset = Charset.defaultCharset(); + } else { + this.charset = writeWorkbook.getCharset(); + } + + if (writeWorkbook.getWithBom() == null) { + this.withBom = Boolean.TRUE; + } else { + this.withBom = writeWorkbook.getWithBom(); + } + + if (writeWorkbook.getAutoCloseStream() == null) { + this.autoCloseStream = Boolean.TRUE; + } else { + this.autoCloseStream = writeWorkbook.getAutoCloseStream(); + } + if (writeWorkbook.getExcelType() == null) { + boolean isXls = (file != null && file.getName().endsWith(ExcelTypeEnum.XLS.getValue())) + || (writeWorkbook.getTemplateFile() != null + && writeWorkbook.getTemplateFile().getName().endsWith(ExcelTypeEnum.XLS.getValue())); + if (isXls) { + this.excelType = ExcelTypeEnum.XLS; + } else { + boolean isCsv = (file != null && file.getName().endsWith(ExcelTypeEnum.CSV.getValue())) + || (writeWorkbook.getTemplateFile() != null + && writeWorkbook.getTemplateFile().getName().endsWith(ExcelTypeEnum.CSV.getValue())); + if (isCsv) { + this.excelType = ExcelTypeEnum.CSV; + } else { + this.excelType = ExcelTypeEnum.XLSX; + } + } + } else { + this.excelType = writeWorkbook.getExcelType(); + } + + // init handler + initHandler(writeWorkbook, null); + + try { + copyTemplate(); + } catch (IOException e) { + throw new ExcelGenerateException("Copy template failure.", e); + } + if (writeWorkbook.getMandatoryUseInputStream() == null) { + this.mandatoryUseInputStream = Boolean.FALSE; + } else { + this.mandatoryUseInputStream = writeWorkbook.getMandatoryUseInputStream(); + } + this.hasBeenInitializedSheetIndexMap = new HashMap<>(); + this.hasBeenInitializedSheetNameMap = new HashMap<>(); + this.password = writeWorkbook.getPassword(); + if (writeWorkbook.getInMemory() == null) { + this.inMemory = Boolean.FALSE; + } else { + this.inMemory = writeWorkbook.getInMemory(); + } + if (writeWorkbook.getWriteExcelOnException() == null) { + this.writeExcelOnException = Boolean.FALSE; + } else { + this.writeExcelOnException = writeWorkbook.getWriteExcelOnException(); + } + this.cellStyleIndexMap = MapUtils.newHashMap(); + this.fontMap = MapUtils.newHashMap(); + this.dataFormatMap = MapUtils.newHashMap(); + } + + private void copyTemplate() throws IOException { + if (writeWorkbook.getTemplateFile() == null && writeWorkbook.getTemplateInputStream() == null) { + return; + } + if (this.excelType == ExcelTypeEnum.CSV) { + throw new ExcelGenerateException("csv cannot use template."); + } + byte[] templateFileByte = null; + if (writeWorkbook.getTemplateFile() != null) { + templateFileByte = FileUtils.readFileToByteArray(writeWorkbook.getTemplateFile()); + } else if (writeWorkbook.getTemplateInputStream() != null) { + try { + templateFileByte = IoUtils.toByteArray(writeWorkbook.getTemplateInputStream()); + } finally { + if (autoCloseStream) { + writeWorkbook.getTemplateInputStream().close(); + } + } + } + this.tempTemplateInputStream = new ByteArrayInputStream(templateFileByte); + } + + @Override + public HolderEnum holderType() { + return HolderEnum.WORKBOOK; + } + + /** + * create a cell style. + * + * @param writeCellStyle + * @param originCellStyle + * @return + */ + public CellStyle createCellStyle(WriteCellStyle writeCellStyle, CellStyle originCellStyle) { + if (writeCellStyle == null) { + return originCellStyle; + } + + short styleIndex = -1; + Font originFont = null; + boolean useCache = true; + if (originCellStyle != null) { + styleIndex = originCellStyle.getIndex(); + if (originCellStyle instanceof XSSFCellStyle) { + originFont = ((XSSFCellStyle)originCellStyle).getFont(); + } else if (originCellStyle instanceof HSSFCellStyle) { + originFont = ((HSSFCellStyle)originCellStyle).getFont(workbook); + } + useCache = false; + } + + Map cellStyleMap = cellStyleIndexMap.computeIfAbsent(styleIndex, + key -> MapUtils.newHashMap()); + CellStyle cellStyle = cellStyleMap.get(writeCellStyle); + if (cellStyle != null) { + return cellStyle; + } + if (log.isDebugEnabled()) { + log.info("create new style:{},{}", writeCellStyle, originCellStyle); + } + WriteCellStyle tempWriteCellStyle = new WriteCellStyle(); + WriteCellStyle.merge(writeCellStyle, tempWriteCellStyle); + + cellStyle = StyleUtil.buildCellStyle(workbook, originCellStyle, tempWriteCellStyle); + Short dataFormat = createDataFormat(tempWriteCellStyle.getDataFormatData(), useCache); + if (dataFormat != null) { + cellStyle.setDataFormat(dataFormat); + } + Font font = createFont(tempWriteCellStyle.getWriteFont(), originFont, useCache); + if (font != null) { + cellStyle.setFont(font); + } + cellStyleMap.put(tempWriteCellStyle, cellStyle); + return cellStyle; + } + + /** + * create a font. + * + * @param writeFont + * @param originFont + * @param useCache + * @return + */ + public Font createFont(WriteFont writeFont, Font originFont, boolean useCache) { + if (!useCache) { + return StyleUtil.buildFont(workbook, originFont, writeFont); + } + WriteFont tempWriteFont = new WriteFont(); + WriteFont.merge(writeFont, tempWriteFont); + + Font font = fontMap.get(tempWriteFont); + if (font != null) { + return font; + } + font = StyleUtil.buildFont(workbook, originFont, tempWriteFont); + fontMap.put(tempWriteFont, font); + return font; + } + + /** + * create a data format. + * + * @param dataFormatData + * @param useCache + * @return + */ + public Short createDataFormat(DataFormatData dataFormatData, boolean useCache) { + if (dataFormatData == null) { + return null; + } + if (!useCache) { + return StyleUtil.buildDataFormat(workbook, dataFormatData); + } + DataFormatData tempDataFormatData = new DataFormatData(); + DataFormatData.merge(dataFormatData, tempDataFormatData); + + Short dataFormat = dataFormatMap.get(tempDataFormatData); + if (dataFormat != null) { + return dataFormat; + } + dataFormat = StyleUtil.buildDataFormat(workbook, tempDataFormatData); + dataFormatMap.put(tempDataFormatData, dataFormat); + return dataFormat; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java new file mode 100644 index 000000000..bebd210e2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/style/WriteCellStyle.java @@ -0,0 +1,360 @@ +package com.alibaba.excel.write.metadata.style; + +import com.alibaba.excel.constant.BuiltinFormats; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.StyleProperty; +import com.alibaba.excel.util.StringUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IgnoredErrorType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * Cell style when writing + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteCellStyle { + /** + * Set the data format (must be a valid format). Built in formats are defined at {@link BuiltinFormats}. + */ + private DataFormatData dataFormatData; + + /** + * Set the font for this style + */ + private WriteFont writeFont; + /** + * Set the cell's using this style to be hidden + */ + private Boolean hidden; + + /** + * Set the cell's using this style to be locked + */ + private Boolean locked; + /** + * Turn on or off "Quote Prefix" or "123 Prefix" for the style, which is used to tell Excel that the thing which + * looks like a number or a formula shouldn't be treated as on. Turning this on is somewhat (but not completely, see + * {@link IgnoredErrorType}) like prefixing the cell value with a ' in Excel + */ + private Boolean quotePrefix; + /** + * Set the type of horizontal alignment for the cell + */ + private HorizontalAlignment horizontalAlignment; + /** + * Set whether the text should be wrapped. Setting this flag to true make all content visible within a + * cell by displaying it on multiple lines + */ + private Boolean wrapped; + /** + * Set the type of vertical alignment for the cell + */ + private VerticalAlignment verticalAlignment; + /** + * Set the degree of rotation for the text in the cell. + * + * Note: HSSF uses values from -90 to 90 degrees, whereas XSSF uses values from 0 to 180 degrees. The + * implementations of this method will map between these two value-ranges accordingly, however the corresponding + * getter is returning values in the range mandated by the current type of Excel file-format that this CellStyle is + * applied to. + */ + private Short rotation; + /** + * Set the number of spaces to indent the text in the cell + */ + private Short indent; + /** + * Set the type of border to use for the left border of the cell + */ + private BorderStyle borderLeft; + /** + * Set the type of border to use for the right border of the cell + */ + private BorderStyle borderRight; + /** + * Set the type of border to use for the top border of the cell + */ + private BorderStyle borderTop; + + /** + * Set the type of border to use for the bottom border of the cell + */ + private BorderStyle borderBottom; + /** + * Set the color to use for the left border + * + * @see IndexedColors + */ + private Short leftBorderColor; + + /** + * Set the color to use for the right border + * + * @see IndexedColors + */ + private Short rightBorderColor; + + /** + * Set the color to use for the top border + * + * @see IndexedColors + */ + private Short topBorderColor; + /** + * Set the color to use for the bottom border + * + * @see IndexedColors + */ + private Short bottomBorderColor; + /** + * Setting to one fills the cell with the foreground color... No idea about other values + * + * @see FillPatternType#SOLID_FOREGROUND + */ + private FillPatternType fillPatternType; + + /** + * Set the background fill color. + * + * @see IndexedColors + */ + private Short fillBackgroundColor; + + /** + * Set the foreground fill color Note: Ensure Foreground color is set prior to background color. + * + * @see IndexedColors + */ + private Short fillForegroundColor; + /** + * Controls if the Cell should be auto-sized to shrink to fit if the text is too long + */ + private Boolean shrinkToFit; + + /** + * The source is not empty merge the data to the target. + * + * @param source source + * @param target target + */ + public static void merge(WriteCellStyle source, WriteCellStyle target) { + if (source == null || target == null) { + return; + } + if (source.getDataFormatData() != null) { + if (target.getDataFormatData() == null) { + target.setDataFormatData(source.getDataFormatData()); + } else { + DataFormatData.merge(source.getDataFormatData(), target.getDataFormatData()); + } + } + if (source.getWriteFont() != null) { + if (target.getWriteFont() == null) { + target.setWriteFont(source.getWriteFont()); + } else { + WriteFont.merge(source.getWriteFont(), target.getWriteFont()); + } + } + if (source.getHidden() != null) { + target.setHidden(source.getHidden()); + } + if (source.getLocked() != null) { + target.setLocked(source.getLocked()); + } + if (source.getQuotePrefix() != null) { + target.setQuotePrefix(source.getQuotePrefix()); + } + if (source.getHorizontalAlignment() != null) { + target.setHorizontalAlignment(source.getHorizontalAlignment()); + } + if (source.getWrapped() != null) { + target.setWrapped(source.getWrapped()); + } + if (source.getVerticalAlignment() != null) { + target.setVerticalAlignment(source.getVerticalAlignment()); + } + if (source.getRotation() != null) { + target.setRotation(source.getRotation()); + } + if (source.getIndent() != null) { + target.setIndent(source.getIndent()); + } + if (source.getBorderLeft() != null) { + target.setBorderLeft(source.getBorderLeft()); + } + if (source.getBorderRight() != null) { + target.setBorderRight(source.getBorderRight()); + } + if (source.getBorderTop() != null) { + target.setBorderTop(source.getBorderTop()); + } + if (source.getBorderBottom() != null) { + target.setBorderBottom(source.getBorderBottom()); + } + if (source.getLeftBorderColor() != null) { + target.setLeftBorderColor(source.getLeftBorderColor()); + } + if (source.getRightBorderColor() != null) { + target.setRightBorderColor(source.getRightBorderColor()); + } + if (source.getTopBorderColor() != null) { + target.setTopBorderColor(source.getTopBorderColor()); + } + if (source.getBottomBorderColor() != null) { + target.setBottomBorderColor(source.getBottomBorderColor()); + } + if (source.getFillPatternType() != null) { + target.setFillPatternType(source.getFillPatternType()); + } + if (source.getFillBackgroundColor() != null) { + target.setFillBackgroundColor(source.getFillBackgroundColor()); + } + if (source.getFillForegroundColor() != null) { + target.setFillForegroundColor(source.getFillForegroundColor()); + } + if (source.getShrinkToFit() != null) { + target.setShrinkToFit(source.getShrinkToFit()); + } + } + + /** + * The source is not empty merge the data to the target. + * + * @param styleProperty styleProperty + * @param fontProperty fontProperty + */ + public static WriteCellStyle build(StyleProperty styleProperty, FontProperty fontProperty) { + if (styleProperty == null && fontProperty == null) { + return null; + } + WriteCellStyle writeCellStyle = new WriteCellStyle(); + buildStyleProperty(styleProperty, writeCellStyle); + buildFontProperty(fontProperty, writeCellStyle); + return writeCellStyle; + } + + private static void buildFontProperty(FontProperty fontProperty, WriteCellStyle writeCellStyle) { + if (fontProperty == null) { + return; + } + if (writeCellStyle.getWriteFont() == null) { + writeCellStyle.setWriteFont(new WriteFont()); + } + WriteFont writeFont = writeCellStyle.getWriteFont(); + + if (StringUtils.isNotBlank(fontProperty.getFontName())) { + writeFont.setFontName(fontProperty.getFontName()); + } + if (fontProperty.getFontHeightInPoints() != null) { + writeFont.setFontHeightInPoints(fontProperty.getFontHeightInPoints()); + } + if (fontProperty.getItalic() != null) { + writeFont.setItalic(fontProperty.getItalic()); + } + if (fontProperty.getStrikeout() != null) { + writeFont.setStrikeout(fontProperty.getStrikeout()); + } + if (fontProperty.getColor() != null) { + writeFont.setColor(fontProperty.getColor()); + } + if (fontProperty.getTypeOffset() != null) { + writeFont.setTypeOffset(fontProperty.getTypeOffset()); + } + if (fontProperty.getUnderline() != null) { + writeFont.setUnderline(fontProperty.getUnderline()); + } + if (fontProperty.getCharset() != null) { + writeFont.setCharset(fontProperty.getCharset()); + } + if (fontProperty.getBold() != null) { + writeFont.setBold(fontProperty.getBold()); + } + } + + private static void buildStyleProperty(StyleProperty styleProperty, WriteCellStyle writeCellStyle) { + if (styleProperty == null) { + return; + } + if (styleProperty.getDataFormatData() != null) { + if (writeCellStyle.getDataFormatData() == null) { + writeCellStyle.setDataFormatData(styleProperty.getDataFormatData()); + } else { + DataFormatData.merge(styleProperty.getDataFormatData(), writeCellStyle.getDataFormatData()); + } + } + if (styleProperty.getHidden() != null) { + writeCellStyle.setHidden(styleProperty.getHidden()); + } + if (styleProperty.getLocked() != null) { + writeCellStyle.setLocked(styleProperty.getLocked()); + } + if (styleProperty.getQuotePrefix() != null) { + writeCellStyle.setQuotePrefix(styleProperty.getQuotePrefix()); + } + if (styleProperty.getHorizontalAlignment() != null) { + writeCellStyle.setHorizontalAlignment(styleProperty.getHorizontalAlignment()); + } + if (styleProperty.getWrapped() != null) { + writeCellStyle.setWrapped(styleProperty.getWrapped()); + } + if (styleProperty.getVerticalAlignment() != null) { + writeCellStyle.setVerticalAlignment(styleProperty.getVerticalAlignment()); + } + if (styleProperty.getRotation() != null) { + writeCellStyle.setRotation(styleProperty.getRotation()); + } + if (styleProperty.getIndent() != null) { + writeCellStyle.setIndent(styleProperty.getIndent()); + } + if (styleProperty.getBorderLeft() != null) { + writeCellStyle.setBorderLeft(styleProperty.getBorderLeft()); + } + if (styleProperty.getBorderRight() != null) { + writeCellStyle.setBorderRight(styleProperty.getBorderRight()); + } + if (styleProperty.getBorderTop() != null) { + writeCellStyle.setBorderTop(styleProperty.getBorderTop()); + } + if (styleProperty.getBorderBottom() != null) { + writeCellStyle.setBorderBottom(styleProperty.getBorderBottom()); + } + if (styleProperty.getLeftBorderColor() != null) { + writeCellStyle.setLeftBorderColor(styleProperty.getLeftBorderColor()); + } + if (styleProperty.getRightBorderColor() != null) { + writeCellStyle.setRightBorderColor(styleProperty.getRightBorderColor()); + } + if (styleProperty.getTopBorderColor() != null) { + writeCellStyle.setTopBorderColor(styleProperty.getTopBorderColor()); + } + if (styleProperty.getBottomBorderColor() != null) { + writeCellStyle.setBottomBorderColor(styleProperty.getBottomBorderColor()); + } + if (styleProperty.getFillPatternType() != null) { + writeCellStyle.setFillPatternType(styleProperty.getFillPatternType()); + } + if (styleProperty.getFillBackgroundColor() != null) { + writeCellStyle.setFillBackgroundColor(styleProperty.getFillBackgroundColor()); + } + if (styleProperty.getFillForegroundColor() != null) { + writeCellStyle.setFillForegroundColor(styleProperty.getFillForegroundColor()); + } + if (styleProperty.getShrinkToFit() != null) { + writeCellStyle.setShrinkToFit(styleProperty.getShrinkToFit()); + } + + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/style/WriteFont.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/style/WriteFont.java new file mode 100644 index 000000000..a42899e12 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/metadata/style/WriteFont.java @@ -0,0 +1,118 @@ +package com.alibaba.excel.write.metadata.style; + +import com.alibaba.excel.util.StringUtils; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.poi.common.usermodel.fonts.FontCharset; +import org.apache.poi.hssf.usermodel.HSSFPalette; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * Font when writing + * + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteFont { + /** + * The name for the font (i.e. Arial) + */ + private String fontName; + /** + * Height in the familiar unit of measure - points + */ + private Short fontHeightInPoints; + /** + * Whether to use italics or not + */ + private Boolean italic; + /** + * Whether to use a strikeout horizontal line through the text or not + */ + private Boolean strikeout; + /** + * The color for the font + * + * @see Font#COLOR_NORMAL + * @see Font#COLOR_RED + * @see HSSFPalette#getColor(short) + * @see IndexedColors + */ + private Short color; + /** + * Set normal, super or subscript. + * + * @see Font#SS_NONE + * @see Font#SS_SUPER + * @see Font#SS_SUB + */ + private Short typeOffset; + /** + * set type of text underlining to use + * + * @see Font#U_NONE + * @see Font#U_SINGLE + * @see Font#U_DOUBLE + * @see Font#U_SINGLE_ACCOUNTING + * @see Font#U_DOUBLE_ACCOUNTING + */ + + private Byte underline; + /** + * Set character-set to use. + * + * @see FontCharset + * @see Font#ANSI_CHARSET + * @see Font#DEFAULT_CHARSET + * @see Font#SYMBOL_CHARSET + */ + private Integer charset; + /** + * Bold + */ + private Boolean bold; + + /** + * The source is not empty merge the data to the target. + * + * @param source source + * @param target target + */ + public static void merge(WriteFont source, WriteFont target) { + if (source == null || target == null) { + return; + } + if (StringUtils.isNotBlank(source.getFontName())) { + target.setFontName(source.getFontName()); + } + if (source.getFontHeightInPoints() != null) { + target.setFontHeightInPoints(source.getFontHeightInPoints()); + } + if (source.getItalic() != null) { + target.setItalic(source.getItalic()); + } + if (source.getStrikeout() != null) { + target.setStrikeout(source.getStrikeout()); + } + if (source.getColor() != null) { + target.setColor(source.getColor()); + } + if (source.getTypeOffset() != null) { + target.setTypeOffset(source.getTypeOffset()); + } + if (source.getUnderline() != null) { + target.setUnderline(source.getUnderline()); + } + if (source.getCharset() != null) { + target.setCharset(source.getCharset()); + } + if (source.getBold() != null) { + target.setBold(source.getBold()); + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java new file mode 100644 index 000000000..7affadbe2 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/property/ExcelWriteHeadProperty.java @@ -0,0 +1,149 @@ +package com.alibaba.excel.write.property; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.annotation.write.style.OnceAbsoluteMerge; +import com.alibaba.excel.enums.HeadKindEnum; +import com.alibaba.excel.metadata.CellRange; +import com.alibaba.excel.metadata.ConfigurationHolder; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.Holder; +import com.alibaba.excel.metadata.property.ColumnWidthProperty; +import com.alibaba.excel.metadata.property.ExcelHeadProperty; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.LoopMergeProperty; +import com.alibaba.excel.metadata.property.OnceAbsoluteMergeProperty; +import com.alibaba.excel.metadata.property.RowHeightProperty; +import com.alibaba.excel.metadata.property.StyleProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * Define the header attribute of excel + * + * @author jipengfei + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExcelWriteHeadProperty extends ExcelHeadProperty { + + private RowHeightProperty headRowHeightProperty; + private RowHeightProperty contentRowHeightProperty; + private OnceAbsoluteMergeProperty onceAbsoluteMergeProperty; + + public ExcelWriteHeadProperty(ConfigurationHolder configurationHolder, Class headClazz, List> head) { + super(configurationHolder, headClazz, head); + if (getHeadKind() != HeadKindEnum.CLASS) { + return; + } + this.headRowHeightProperty = + RowHeightProperty.build(headClazz.getAnnotation(HeadRowHeight.class)); + this.contentRowHeightProperty = + RowHeightProperty.build(headClazz.getAnnotation(ContentRowHeight.class)); + this.onceAbsoluteMergeProperty = + OnceAbsoluteMergeProperty.build(headClazz.getAnnotation(OnceAbsoluteMerge.class)); + + ColumnWidth parentColumnWidth = headClazz.getAnnotation(ColumnWidth.class); + HeadStyle parentHeadStyle = headClazz.getAnnotation(HeadStyle.class); + HeadFontStyle parentHeadFontStyle = headClazz.getAnnotation(HeadFontStyle.class); + + + for (Map.Entry entry : getHeadMap().entrySet()) { + Head headData = entry.getValue(); + if (headData == null) { + throw new IllegalArgumentException( + "Passing in the class and list the head, the two must be the same size."); + } + Field field = headData.getField(); + + ColumnWidth columnWidth = field.getAnnotation(ColumnWidth.class); + if (columnWidth == null) { + columnWidth = parentColumnWidth; + } + headData.setColumnWidthProperty(ColumnWidthProperty.build(columnWidth)); + + + HeadStyle headStyle = field.getAnnotation(HeadStyle.class); + if (headStyle == null) { + headStyle = parentHeadStyle; + } + headData.setHeadStyleProperty(StyleProperty.build(headStyle)); + + HeadFontStyle headFontStyle = field.getAnnotation(HeadFontStyle.class); + if (headFontStyle == null) { + headFontStyle = parentHeadFontStyle; + } + headData.setHeadFontProperty(FontProperty.build(headFontStyle)); + + headData.setLoopMergeProperty(LoopMergeProperty.build(field.getAnnotation(ContentLoopMerge.class))); + } + } + + /** + * Calculate all cells that need to be merged + * + * @return cells that need to be merged + */ + public List headCellRangeList() { + List cellRangeList = new ArrayList(); + Set alreadyRangeSet = new HashSet(); + List headList = new ArrayList(getHeadMap().values()); + for (int i = 0; i < headList.size(); i++) { + Head head = headList.get(i); + List headNameList = head.getHeadNameList(); + for (int j = 0; j < headNameList.size(); j++) { + if (alreadyRangeSet.contains(i + "-" + j)) { + continue; + } + alreadyRangeSet.add(i + "-" + j); + String headName = headNameList.get(j); + int lastCol = i; + int lastRow = j; + for (int k = i + 1; k < headList.size(); k++) { + String key = k + "-" + j; + if (headList.get(k).getHeadNameList().get(j).equals(headName) && !alreadyRangeSet.contains(key)) { + alreadyRangeSet.add(key); + lastCol = k; + } else { + break; + } + } + Set tempAlreadyRangeSet = new HashSet<>(); + outer: + for (int k = j + 1; k < headNameList.size(); k++) { + for (int l = i; l <= lastCol; l++) { + String key = l + "-" + k; + if (headList.get(l).getHeadNameList().get(k).equals(headName) && !alreadyRangeSet.contains( + key)) { + tempAlreadyRangeSet.add(l + "-" + k); + } else { + break outer; + } + } + lastRow = k; + alreadyRangeSet.addAll(tempAlreadyRangeSet); + } + if (j == lastRow && i == lastCol) { + continue; + } + cellRangeList + .add(new CellRange(j, lastRow, head.getColumnIndex(), headList.get(lastCol).getColumnIndex())); + } + } + return cellRangeList; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java new file mode 100644 index 000000000..9a918b27c --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java @@ -0,0 +1,74 @@ +package com.alibaba.excel.write.style; + +import com.alibaba.excel.constant.OrderConstant; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import org.apache.poi.ss.usermodel.Cell; + +/** + * Cell style strategy + * + * @author Jiaju Zhuang + */ +public abstract class AbstractCellStyleStrategy implements CellWriteHandler { + + @Override + public int order() { + return OrderConstant.DEFINE_STYLE; + } + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + if (context.getHead() == null) { + return; + } + if (context.getHead()) { + setHeadCellStyle(context); + } else { + setContentCellStyle(context); + } + } + + /** + * Sets the cell style of header + * + * @param context + */ + protected void setHeadCellStyle(CellWriteHandlerContext context) { + setHeadCellStyle(context.getCell(), context.getHeadData(), context.getRelativeRowIndex()); + } + + /** + * Sets the cell style of header + * + * @param cell + * @param head + * @param relativeRowIndex + */ + protected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) { + throw new UnsupportedOperationException("Custom styles must override the setHeadCellStyle method."); + } + + /** + * Sets the cell style of content + * + * @param context + */ + protected void setContentCellStyle(CellWriteHandlerContext context) { + setContentCellStyle(context.getCell(), context.getHeadData(), context.getRelativeRowIndex()); + } + + /** + * Sets the cell style of content + * + * @param cell + * @param head + * @param relativeRowIndex + */ + protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { + throw new UnsupportedOperationException("Custom styles must override the setContentCellStyle method."); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java new file mode 100644 index 000000000..f9d76f7ff --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/AbstractVerticalCellStyleStrategy.java @@ -0,0 +1,79 @@ +package com.alibaba.excel.write.style; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; + +/** + * Use the same style for the column + * + * @author Jiaju Zhuang + */ +public abstract class AbstractVerticalCellStyleStrategy extends AbstractCellStyleStrategy { + + @Override + protected void setHeadCellStyle(CellWriteHandlerContext context) { + if (stopProcessing(context)) { + return; + } + WriteCellData cellData = context.getFirstCellData(); + WriteCellStyle.merge(headCellStyle(context), cellData.getOrCreateStyle()); + } + + @Override + protected void setContentCellStyle(CellWriteHandlerContext context) { + if (context.getFirstCellData() == null) { + return; + } + WriteCellData cellData = context.getFirstCellData(); + WriteCellStyle.merge(contentCellStyle(context), cellData.getOrCreateStyle()); + } + + /** + * Returns the column width corresponding to each column head + * + * @param context + * @return + */ + protected WriteCellStyle headCellStyle(CellWriteHandlerContext context) { + return headCellStyle(context.getHeadData()); + } + + /** + * Returns the column width corresponding to each column head + * + * @param head Nullable + * @return + */ + protected WriteCellStyle headCellStyle(Head head) { + return null; + } + + /** + * Returns the column width corresponding to each column head. + * + * @param context + * @return + */ + protected WriteCellStyle contentCellStyle(CellWriteHandlerContext context) { + return contentCellStyle(context.getHeadData()); + } + + /** + * Returns the column width corresponding to each column head + * + * @param head Nullable + * @return + */ + protected WriteCellStyle contentCellStyle(Head head) { + return null; + } + + protected boolean stopProcessing(CellWriteHandlerContext context) { + if (context.getFirstCellData() == null) { + return true; + } + return context.getHeadData() == null; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/DefaultStyle.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/DefaultStyle.java new file mode 100644 index 000000000..139998ef5 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/DefaultStyle.java @@ -0,0 +1,46 @@ +package com.alibaba.excel.write.style; + +import com.alibaba.excel.constant.OrderConstant; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.VerticalAlignment; + +/** + * The default styles + * + * @author Jiaju Zhuang + */ +public class DefaultStyle extends HorizontalCellStyleStrategy { + + @Override + public int order() { + return OrderConstant.DEFAULT_DEFINE_STYLE; + } + + public DefaultStyle() { + super(); + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + headWriteCellStyle.setWrapped(true); + headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + headWriteCellStyle.setLocked(true); + headWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + headWriteCellStyle.setBorderTop(BorderStyle.THIN); + headWriteCellStyle.setBorderBottom(BorderStyle.THIN); + headWriteCellStyle.setBorderLeft(BorderStyle.THIN); + headWriteCellStyle.setBorderRight(BorderStyle.THIN); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontName("宋体"); + headWriteFont.setFontHeightInPoints((short)14); + headWriteFont.setBold(true); + headWriteCellStyle.setWriteFont(headWriteFont); + + setHeadWriteCellStyle(headWriteCellStyle); + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java new file mode 100644 index 000000000..df04d2487 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/HorizontalCellStyleStrategy.java @@ -0,0 +1,72 @@ +package com.alibaba.excel.write.style; + +import java.util.List; + +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.collections4.CollectionUtils; + +/** + * Use the same style for the row + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class HorizontalCellStyleStrategy extends AbstractCellStyleStrategy { + + private WriteCellStyle headWriteCellStyle; + private List contentWriteCellStyleList; + + public HorizontalCellStyleStrategy() { + } + + public HorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle, + List contentWriteCellStyleList) { + this.headWriteCellStyle = headWriteCellStyle; + this.contentWriteCellStyleList = contentWriteCellStyleList; + } + + public HorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) { + this.headWriteCellStyle = headWriteCellStyle; + if (contentWriteCellStyle != null) { + this.contentWriteCellStyleList = ListUtils.newArrayList(contentWriteCellStyle); + } + } + + @Override + protected void setHeadCellStyle(CellWriteHandlerContext context) { + if (stopProcessing(context) || headWriteCellStyle == null) { + return; + } + WriteCellData cellData = context.getFirstCellData(); + WriteCellStyle.merge(headWriteCellStyle, cellData.getOrCreateStyle()); + } + + @Override + protected void setContentCellStyle(CellWriteHandlerContext context) { + if (stopProcessing(context) || CollectionUtils.isEmpty(contentWriteCellStyleList)) { + return; + } + WriteCellData cellData = context.getFirstCellData(); + if (context.getRelativeRowIndex() == null || context.getRelativeRowIndex() <= 0) { + WriteCellStyle.merge(contentWriteCellStyleList.get(0), cellData.getOrCreateStyle()); + } else { + WriteCellStyle.merge( + contentWriteCellStyleList.get(context.getRelativeRowIndex() % contentWriteCellStyleList.size()), + cellData.getOrCreateStyle()); + } + } + + protected boolean stopProcessing(CellWriteHandlerContext context) { + return context.getFirstCellData() == null; + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java new file mode 100644 index 000000000..e33f74e04 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java @@ -0,0 +1,50 @@ +package com.alibaba.excel.write.style.column; + +import java.util.List; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import org.apache.poi.ss.usermodel.Cell; + +/** + * Column width style strategy + * + * @author Jiaju Zhuang + */ +public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandler { + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + setColumnWidth(context); + } + + /** + * Sets the column width when head create + * + * @param context + */ + protected void setColumnWidth(CellWriteHandlerContext context) { + setColumnWidth(context.getWriteSheetHolder(), context.getCellDataList(), context.getCell(), + context.getHeadData(), context.getRelativeRowIndex(), context.getHead()); + } + + /** + * Sets the column width when head create + * + * @param writeSheetHolder + * @param cellDataList + * @param cell + * @param head + * @param relativeRowIndex + * @param isHead + */ + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { + throw new UnsupportedOperationException("Custom styles must override the setColumnWidth method."); + } + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java new file mode 100644 index 000000000..60a6cfc0e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java @@ -0,0 +1,46 @@ +package com.alibaba.excel.write.style.column; + +import java.util.List; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import org.apache.poi.ss.usermodel.Cell; + +/** + * Returns the column width according to each column header + * + * @author Jiaju Zhuang + */ +public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { + + @Override + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell, Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = relativeRowIndex != null && (isHead || relativeRowIndex == 0); + if (!needSetWidth) { + return; + } + Integer width = columnWidth(head, cell.getColumnIndex()); + if (width != null) { + width = width * 256; + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), width); + } + } + + /** + * Returns the column width corresponding to each column head. + * + *

+ * if return null, ignore + * + * @param head + * Nullable. + * @param columnIndex + * Not null. + * @return + */ + protected abstract Integer columnWidth(Head head, Integer columnIndex); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java new file mode 100644 index 000000000..ee47fbfb1 --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java @@ -0,0 +1,73 @@ +package com.alibaba.excel.write.style.column; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.ss.usermodel.Cell; + +/** + * Take the width of the longest column as the width. + *

+ * This is not very useful at the moment, for example if you have Numbers it will cause a newline.And the length is not + * exactly the same as the actual length. + * + * @author Jiaju Zhuang + */ +public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { + + private static final int MAX_COLUMN_WIDTH = 255; + + private final Map> cache = MapUtils.newHashMapWithExpectedSize(8); + + @Override + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell, + Head head, + Integer relativeRowIndex, Boolean isHead) { + boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList); + if (!needSetWidth) { + return; + } + Map maxColumnWidthMap = cache.computeIfAbsent(writeSheetHolder.getSheetNo(), key -> new HashMap<>(16)); + Integer columnWidth = dataLength(cellDataList, cell, isHead); + if (columnWidth < 0) { + return; + } + if (columnWidth > MAX_COLUMN_WIDTH) { + columnWidth = MAX_COLUMN_WIDTH; + } + Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex()); + if (maxColumnWidth == null || columnWidth > maxColumnWidth) { + maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth); + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256); + } + } + + private Integer dataLength(List> cellDataList, Cell cell, Boolean isHead) { + if (isHead) { + return cell.getStringCellValue().getBytes().length; + } + WriteCellData cellData = cellDataList.get(0); + CellDataTypeEnum type = cellData.getType(); + if (type == null) { + return -1; + } + switch (type) { + case STRING: + return cellData.getStringValue().getBytes().length; + case BOOLEAN: + return cellData.getBooleanValue().toString().getBytes().length; + case NUMBER: + return cellData.getNumberValue().toString().getBytes().length; + default: + return -1; + } + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java new file mode 100644 index 000000000..5db857d5e --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/column/SimpleColumnWidthStyleStrategy.java @@ -0,0 +1,25 @@ +package com.alibaba.excel.write.style.column; + +import com.alibaba.excel.metadata.Head; + +/** + * All the columns are the same width + * + * @author Jiaju Zhuang + */ +public class SimpleColumnWidthStyleStrategy extends AbstractHeadColumnWidthStyleStrategy { + private final Integer columnWidth; + + /** + * + * @param columnWidth + */ + public SimpleColumnWidthStyleStrategy(Integer columnWidth) { + this.columnWidth = columnWidth; + } + + @Override + protected Integer columnWidth(Head head, Integer columnIndex) { + return columnWidth; + } +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java new file mode 100644 index 000000000..6c908eb6a --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/row/AbstractRowHeightStyleStrategy.java @@ -0,0 +1,42 @@ +package com.alibaba.excel.write.style.row; + +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; + +import org.apache.poi.ss.usermodel.Row; + +/** + * Set the row height strategy + * + * @author Jiaju Zhuang + */ +public abstract class AbstractRowHeightStyleStrategy implements RowWriteHandler { + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + if (context.getHead() == null) { + return; + } + if (context.getHead()) { + setHeadColumnHeight(context.getRow(), context.getRelativeRowIndex()); + } else { + setContentColumnHeight(context.getRow(), context.getRelativeRowIndex()); + } + } + + /** + * Sets the height of header + * + * @param row + * @param relativeRowIndex + */ + protected abstract void setHeadColumnHeight(Row row, int relativeRowIndex); + + /** + * Sets the height of content + * + * @param row + * @param relativeRowIndex + */ + protected abstract void setContentColumnHeight(Row row, int relativeRowIndex); + +} diff --git a/easyexcel-core/src/main/java/com/alibaba/excel/write/style/row/SimpleRowHeightStyleStrategy.java b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/row/SimpleRowHeightStyleStrategy.java new file mode 100644 index 000000000..028707f3f --- /dev/null +++ b/easyexcel-core/src/main/java/com/alibaba/excel/write/style/row/SimpleRowHeightStyleStrategy.java @@ -0,0 +1,33 @@ +package com.alibaba.excel.write.style.row; + +import org.apache.poi.ss.usermodel.Row; + +/** + * Set the head column high and content column high + * + * @author Jiaju Zhuang + */ +public class SimpleRowHeightStyleStrategy extends AbstractRowHeightStyleStrategy { + private final Short headRowHeight; + private final Short contentRowHeight; + + public SimpleRowHeightStyleStrategy(Short headRowHeight, Short contentRowHeight) { + this.headRowHeight = headRowHeight; + this.contentRowHeight = contentRowHeight; + } + + @Override + protected void setHeadColumnHeight(Row row, int relativeRowIndex) { + if (headRowHeight != null) { + row.setHeightInPoints(headRowHeight); + } + } + + @Override + protected void setContentColumnHeight(Row row, int relativeRowIndex) { + if (contentRowHeight != null) { + row.setHeightInPoints(contentRowHeight); + } + } + +} diff --git a/easyexcel-support/README.md b/easyexcel-support/README.md new file mode 100644 index 000000000..580f05d0c --- /dev/null +++ b/easyexcel-support/README.md @@ -0,0 +1,3 @@ +# easyexcel-support + +外部依赖的代码,目前就一个cglib,由于cglib不支持jdk高版本,所以单独复制了一份 \ No newline at end of file diff --git a/easyexcel-support/pom.xml b/easyexcel-support/pom.xml new file mode 100644 index 000000000..f07d04468 --- /dev/null +++ b/easyexcel-support/pom.xml @@ -0,0 +1,100 @@ + + + 4.0.0 + + + com.alibaba + easyexcel-parent + ${revision} + ../pom.xml + + + https://github.com/alibaba/easyexcel + jar + easyexcel-support + easyexcel-support + + + + + org.springframework + spring-core + 5.3.37 + + + * + * + + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + true + false + true + + true + + + org.springframework:spring-core + + + + + org.springframework:spring-core + + org/springframework/asm/** + org/springframework/cglib/** + + + + + + org.springframework + com.alibaba.excel.support + + + + + + + + + org.codehaus.mojo + flatten-maven-plugin + + + flatten.process-resources + process-resources + + flatten + + + + flatten + package + + flatten + + + + + + + + diff --git a/easyexcel-support/src/main/java/com/alibaba/excel/support/Empty.java b/easyexcel-support/src/main/java/com/alibaba/excel/support/Empty.java new file mode 100644 index 000000000..82d9b080d --- /dev/null +++ b/easyexcel-support/src/main/java/com/alibaba/excel/support/Empty.java @@ -0,0 +1,9 @@ +package com.alibaba.excel.support; + +/** + * empty + * + * @author Jiaju Zhuang + */ +public class Empty { +} diff --git a/easyexcel-test/README.md b/easyexcel-test/README.md new file mode 100644 index 000000000..299588a01 --- /dev/null +++ b/easyexcel-test/README.md @@ -0,0 +1,3 @@ +# easyexcel-test + +测试案例 \ No newline at end of file diff --git a/easyexcel-test/pom.xml b/easyexcel-test/pom.xml new file mode 100644 index 000000000..8c540b22e --- /dev/null +++ b/easyexcel-test/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + + com.alibaba + easyexcel-parent + ${revision} + ../pom.xml + + + https://github.com/alibaba/easyexcel + jar + easyexcel-test + easyexcel-test + + + true + + + + + + com.alibaba + easyexcel-core + + + com.alibaba.fastjson2 + fastjson2 + 2.0.51 + + + org.springframework.boot + spring-boot-starter-web + 2.7.18 + + + org.springframework.boot + spring-boot-starter-test + 2.7.18 + + + + + org.slf4j + slf4j-simple + 1.7.36 + + + org.slf4j + jcl-over-slf4j + 1.7.36 + + + org.slf4j + log4j-over-slf4j + 1.7.36 + + + ch.qos.logback + logback-classic + 1.5.6 + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + /com/alibaba/easyexcel/test/core/**/*.java + + false + + + + + diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/StyleTestUtils.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/StyleTestUtils.java new file mode 100644 index 000000000..266f724ae --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/StyleTestUtils.java @@ -0,0 +1,43 @@ +package com.alibaba.easyexcel.test.core; + +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFCell; + +public class StyleTestUtils { + + public static byte[] getFillForegroundColor(Cell cell) { + if (cell instanceof XSSFCell) { + return ((XSSFCell)cell).getCellStyle().getFillForegroundColorColor().getRGB(); + } else { + return short2byte(((HSSFCell)cell).getCellStyle().getFillForegroundColorColor().getTriplet()); + } + } + + public static byte[] getFontColor(Cell cell, Workbook workbook) { + if (cell instanceof XSSFCell) { + return ((XSSFCell)cell).getCellStyle().getFont().getXSSFColor().getRGB(); + } else { + return short2byte(((HSSFCell)cell).getCellStyle().getFont(workbook).getHSSFColor((HSSFWorkbook)workbook) + .getTriplet()); + } + } + + public static short getFontHeightInPoints(Cell cell, Workbook workbook) { + if (cell instanceof XSSFCell) { + return ((XSSFCell)cell).getCellStyle().getFont().getFontHeightInPoints(); + } else { + return ((HSSFCell)cell).getCellStyle().getFont(workbook).getFontHeightInPoints(); + } + } + + private static byte[] short2byte(short[] shorts) { + byte[] bytes = new byte[shorts.length]; + for (int i = 0; i < shorts.length; i++) { + bytes[i] = (byte)shorts[i]; + } + return bytes; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java new file mode 100644 index 000000000..0ed28a29d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationData.java @@ -0,0 +1,39 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@ColumnWidth(50) +@HeadRowHeight(50) +@ContentRowHeight(100) +public class AnnotationData { + @ExcelProperty("日期") + @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") + private Date date; + + @ExcelProperty(value = "数字") + @NumberFormat("#.##%") + private Double number; + + @ExcelIgnore + private String ignore; + private static final String staticFinal = "test"; + private transient String transientString; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataListener.java new file mode 100644 index 000000000..a7249bfb8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataListener.java @@ -0,0 +1,41 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class AnnotationDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(AnnotationData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + AnnotationData data = list.get(0); + try { + Assertions.assertEquals(data.getDate(), DateUtils.parseDate("2020-01-01 01:01:01")); + } catch (ParseException e) { + throw new ExcelCommonException("Test Exception", e); + } + Assertions.assertEquals(data.getNumber(), 99.99, 0.00); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java new file mode 100644 index 000000000..1e17ea94c --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationDataTest.java @@ -0,0 +1,134 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.StyleTestUtils; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class AnnotationDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File fileStyle07; + private static File fileStyle03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("annotation07.xlsx"); + file03 = TestFileUtil.createNewFile("annotation03.xls"); + fileStyle07 = TestFileUtil.createNewFile("annotationStyle07.xlsx"); + fileStyle03 = TestFileUtil.createNewFile("annotationStyle03.xls"); + fileCsv = TestFileUtil.createNewFile("annotationCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv); + } + + @Test + public void t11WriteStyle07() throws Exception { + writeStyle(fileStyle07); + } + + @Test + public void t12Write03() throws Exception { + writeStyle(fileStyle03); + } + + private void writeStyle(File file) throws Exception { + EasyExcel.write().file(file).head(AnnotationStyleData.class).sheet().doWrite(dataStyle()); + + Workbook workbook = WorkbookFactory.create(file); + Sheet sheet = workbook.getSheetAt(0); + + Row row0 = sheet.getRow(0); + Cell cell00 = row0.getCell(0); + Assertions.assertArrayEquals(new byte[] {-1, 0, -1}, StyleTestUtils.getFillForegroundColor(cell00)); + Assertions.assertArrayEquals(new byte[] {-1, -52, 0}, StyleTestUtils.getFontColor(cell00, workbook)); + Assertions.assertEquals(40, StyleTestUtils.getFontHeightInPoints(cell00, workbook)); + + Cell cell01 = row0.getCell(1); + Assertions.assertArrayEquals(new byte[] {-1, 0, 0}, StyleTestUtils.getFillForegroundColor(cell01)); + Assertions.assertArrayEquals(new byte[] {0, -1, -1}, StyleTestUtils.getFontColor(cell01, workbook)); + Assertions.assertEquals(20, StyleTestUtils.getFontHeightInPoints(cell01, workbook)); + + Row row1 = sheet.getRow(1); + Cell cell10 = row1.getCell(0); + Assertions.assertArrayEquals(new byte[] {0, -52, -1}, StyleTestUtils.getFillForegroundColor(cell10)); + Assertions.assertArrayEquals(new byte[] {0, 0, -1}, StyleTestUtils.getFontColor(cell10, workbook)); + Assertions.assertEquals(50, StyleTestUtils.getFontHeightInPoints(cell10, workbook)); + Cell cell11 = row1.getCell(1); + Assertions.assertArrayEquals(new byte[] {0, -128, 0}, StyleTestUtils.getFillForegroundColor(cell11)); + Assertions.assertArrayEquals(new byte[] {-64, -64, -64}, StyleTestUtils.getFontColor(cell11, workbook)); + Assertions.assertEquals(30, StyleTestUtils.getFontHeightInPoints(cell11, workbook)); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write().file(file).head(AnnotationData.class).sheet().doWrite(dataStyle()); + + if (file == fileCsv) { + return; + } + + Workbook workbook = WorkbookFactory.create(file); + Sheet sheet = workbook.getSheetAt(0); + Assertions.assertEquals(50 * 256, sheet.getColumnWidth(0), 0); + + Row row0 = sheet.getRow(0); + Assertions.assertEquals(1000, row0.getHeight(), 0); + + Row row1 = sheet.getRow(1); + Assertions.assertEquals(2000, row1.getHeight(), 0); + } + + private List dataStyle() throws Exception { + List list = new ArrayList<>(); + AnnotationStyleData data = new AnnotationStyleData(); + data.setString("string"); + data.setString1("string1"); + list.add(data); + return list; + } + + private List data() throws Exception { + List list = new ArrayList<>(); + AnnotationData data = new AnnotationData(); + data.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); + data.setNumber(99.99); + data.setIgnore("忽略"); + data.setTransientString("忽略"); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameData.java new file mode 100644 index 000000000..fa93f2ed0 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameData.java @@ -0,0 +1,24 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class AnnotationIndexAndNameData { + @ExcelProperty(value = "第四个", index = 4) + private String index4; + @ExcelProperty(value = "第二个") + private String index2; + @ExcelProperty(index = 0) + private String index0; + @ExcelProperty(value = "第一个", index = 1) + private String index1; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameDataListener.java new file mode 100644 index 000000000..eaa73f998 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameDataListener.java @@ -0,0 +1,36 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class AnnotationIndexAndNameDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(AnnotationIndexAndNameDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(AnnotationIndexAndNameData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + AnnotationIndexAndNameData data = list.get(0); + Assertions.assertEquals(data.getIndex0(), "第0个"); + Assertions.assertEquals(data.getIndex1(), "第1个"); + Assertions.assertEquals(data.getIndex2(), "第2个"); + Assertions.assertEquals(data.getIndex4(), "第4个"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameDataTest.java new file mode 100644 index 000000000..13f093918 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationIndexAndNameDataTest.java @@ -0,0 +1,65 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * Annotation data test + * + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class AnnotationIndexAndNameDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("annotationIndexAndName07.xlsx"); + file03 = TestFileUtil.createNewFile("annotationIndexAndName03.xls"); + fileCsv = TestFileUtil.createNewFile("annotationIndexAndNameCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, AnnotationIndexAndNameData.class).sheet().doWrite(data()); + EasyExcel.read(file, AnnotationIndexAndNameData.class, new AnnotationIndexAndNameDataListener()).sheet() + .doRead(); + } + + private List data() { + List list = new ArrayList(); + AnnotationIndexAndNameData data = new AnnotationIndexAndNameData(); + data.setIndex0("第0个"); + data.setIndex1("第1个"); + data.setIndex2("第2个"); + data.setIndex4("第4个"); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationStyleData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationStyleData.java new file mode 100644 index 000000000..5b00e1ac4 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/annotation/AnnotationStyleData.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.core.annotation; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10) +@HeadFontStyle(fontHeightInPoints = 20, color = 15) +@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17) +@ContentFontStyle(fontHeightInPoints = 30, color = 22) +public class AnnotationStyleData { + @ExcelProperty("字符串") + @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14) + @HeadFontStyle(fontHeightInPoints = 40, color = 51) + @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40) + @ContentFontStyle(fontHeightInPoints = 50, color = 12) + private String string; + @ExcelProperty("字符串1") + private String string1; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/bom/BomData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/bom/BomData.java new file mode 100644 index 000000000..55b58fcd8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/bom/BomData.java @@ -0,0 +1,17 @@ +package com.alibaba.easyexcel.test.core.bom; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class BomData { + @ExcelProperty("姓名") + private String name; + @ExcelProperty("年纪") + private Long age; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/bom/BomDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/bom/BomDataTest.java new file mode 100644 index 000000000..4a591b76a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/bom/BomDataTest.java @@ -0,0 +1,126 @@ +package com.alibaba.easyexcel.test.core.bom; + +import java.io.File; +import java.io.FileOutputStream; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.ListUtils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * bom test + * + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +@Slf4j +public class BomDataTest { + + @Test + public void t01ReadCsv() { + readCsv(TestFileUtil.readFile("bom" + File.separator + "no_bom.csv")); + readCsv(TestFileUtil.readFile("bom" + File.separator + "office_bom.csv")); + } + + @Test + public void t02ReadAndWriteCsv() throws Exception { + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_default.csv"), null, null); + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_utf_8.csv"), "UTF-8", null); + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_utf_8_lower_case.csv"), "utf-8", null); + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_gbk.csv"), "GBK", null); + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_gbk_lower_case.csv"), "gbk", null); + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_utf_16be.csv"), "UTF-16BE", null); + readAndWriteCsv(TestFileUtil.createNewFile("bom" + File.separator + "bom_utf_8_not_with_bom.csv"), "UTF-8", + Boolean.FALSE); + } + + private void readAndWriteCsv(File file, String charsetName, Boolean withBom) throws Exception { + Charset charset = null; + if (charsetName != null) { + charset = Charset.forName(charsetName); + } + EasyExcel.write(new FileOutputStream(file), BomData.class) + .charset(charset) + .withBom(withBom) + .excelType(ExcelTypeEnum.CSV) + .sheet() + .doWrite(data()); + + EasyExcel.read(file, BomData.class, new ReadListener() { + + private final List dataList = Lists.newArrayList(); + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + String head = headMap.get(0).getStringValue(); + Assertions.assertEquals("姓名", head); + } + + @Override + public void invoke(BomData data, AnalysisContext context) { + dataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(dataList.size(), 10); + BomData bomData = dataList.get(0); + Assertions.assertEquals("姓名0", bomData.getName()); + Assertions.assertEquals(20, (long)bomData.getAge()); + } + }) + .charset(charset) + .sheet().doRead(); + } + + private void readCsv(File file) { + EasyExcel.read(file, BomData.class, new ReadListener() { + + private final List dataList = Lists.newArrayList(); + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + String head = headMap.get(0).getStringValue(); + Assertions.assertEquals("姓名", head); + } + + @Override + public void invoke(BomData data, AnalysisContext context) { + dataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(dataList.size(), 10); + BomData bomData = dataList.get(0); + Assertions.assertEquals("姓名0", bomData.getName()); + Assertions.assertEquals(20L, (long)bomData.getAge()); + } + }).sheet().doRead(); + } + + private List data() { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + BomData data = new BomData(); + data.setName("姓名" + i); + data.setAge(20L); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheData.java new file mode 100644 index 000000000..f64554385 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.core.cache; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CacheData { + @ExcelProperty("姓名") + private String name; + + @ExcelProperty("年龄") + private Long age; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheDataTest.java new file mode 100644 index 000000000..b1c242b37 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheDataTest.java @@ -0,0 +1,219 @@ +package com.alibaba.easyexcel.test.core.cache; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.read.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CacheLocationEnum; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.metadata.FieldCache; +import com.alibaba.excel.read.listener.PageReadListener; +import com.alibaba.excel.util.ClassUtils; +import com.alibaba.excel.util.FieldUtils; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +@Slf4j +public class CacheDataTest { + + private static File file07; + private static File fileCacheInvoke; + private static File fileCacheInvoke2; + private static File fileCacheInvokeMemory; + private static File fileCacheInvokeMemory2; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("cache/cache.xlsx"); + fileCacheInvoke = TestFileUtil.createNewFile("cache/fileCacheInvoke.xlsx"); + fileCacheInvoke2 = TestFileUtil.createNewFile("cache/fileCacheInvoke2.xlsx"); + fileCacheInvokeMemory = TestFileUtil.createNewFile("cache/fileCacheInvokeMemory.xlsx"); + fileCacheInvokeMemory2 = TestFileUtil.createNewFile("cache/fileCacheInvokeMemory2.xlsx"); + } + + @Test + public void t01ReadAndWrite() throws Exception { + Field field = FieldUtils.getField(ClassUtils.class, "FIELD_THREAD_LOCAL", true); + ThreadLocal, FieldCache>> fieldThreadLocal = (ThreadLocal, FieldCache>>)field.get( + ClassUtils.class.newInstance()); + Assertions.assertNull(fieldThreadLocal.get()); + EasyExcel.write(file07, CacheData.class).sheet().doWrite(data()); + EasyExcel.read(file07, CacheData.class, new PageReadListener(dataList -> { + Assertions.assertNotNull(fieldThreadLocal.get()); + })) + .sheet() + .doRead(); + Assertions.assertNull(fieldThreadLocal.get()); + } + + @Test + public void t02ReadAndWriteInvoke() throws Exception { + EasyExcel.write(fileCacheInvoke, CacheInvokeData.class).sheet().doWrite(dataInvoke()); + EasyExcel.read(fileCacheInvoke, CacheInvokeData.class, new AnalysisEventListener() { + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + Assertions.assertEquals(2, headMap.size()); + Assertions.assertEquals("姓名", headMap.get(0)); + Assertions.assertEquals("年龄", headMap.get(1)); + } + + @Override + public void invoke(CacheInvokeData data, AnalysisContext context) { + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + }).sheet() + .doRead(); + + Field name = FieldUtils.getField(CacheInvokeData.class, "name", true); + ExcelProperty annotation = name.getAnnotation(ExcelProperty.class); + InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation); + Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues"); + memberValues.setAccessible(true); + Map map = (Map)memberValues.get(invocationHandler); + map.put("value", new String[] {"姓名2"}); + + EasyExcel.write(fileCacheInvoke2, CacheInvokeData.class).sheet().doWrite(dataInvoke()); + EasyExcel.read(fileCacheInvoke2, CacheInvokeData.class, new AnalysisEventListener() { + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + Assertions.assertEquals(2, headMap.size()); + Assertions.assertEquals("姓名2", headMap.get(0)); + Assertions.assertEquals("年龄", headMap.get(1)); + } + + @Override + public void invoke(CacheInvokeData data, AnalysisContext context) { + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + }).sheet() + .doRead(); + + } + + @Test + public void t03ReadAndWriteInvokeMemory() throws Exception { + EasyExcel.write(fileCacheInvokeMemory, CacheInvokeMemoryData.class) + .filedCacheLocation(CacheLocationEnum.MEMORY) + .sheet() + .doWrite(dataInvokeMemory()); + EasyExcel.read(fileCacheInvokeMemory, CacheInvokeMemoryData.class, + new AnalysisEventListener() { + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + Assertions.assertEquals(2, headMap.size()); + Assertions.assertEquals("姓名", headMap.get(0)); + Assertions.assertEquals("年龄", headMap.get(1)); + } + + @Override + public void invoke(CacheInvokeMemoryData data, AnalysisContext context) { + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + }) + .filedCacheLocation(CacheLocationEnum.MEMORY) + .sheet() + .doRead(); + + Field name = FieldUtils.getField(CacheInvokeMemoryData.class, "name", true); + ExcelProperty annotation = name.getAnnotation(ExcelProperty.class); + InvocationHandler invocationHandler = Proxy.getInvocationHandler(annotation); + Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues"); + memberValues.setAccessible(true); + Map map = (Map)memberValues.get(invocationHandler); + map.put("value", new String[] {"姓名2"}); + + EasyExcel.write(fileCacheInvokeMemory2, CacheInvokeMemoryData.class) + .filedCacheLocation(CacheLocationEnum.MEMORY) + .sheet() + .doWrite(dataInvokeMemory()); + EasyExcel.read(fileCacheInvokeMemory2, CacheInvokeMemoryData.class, + new AnalysisEventListener() { + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + Assertions.assertEquals(2, headMap.size()); + Assertions.assertEquals("姓名", headMap.get(0)); + Assertions.assertEquals("年龄", headMap.get(1)); + } + + @Override + public void invoke(CacheInvokeMemoryData data, AnalysisContext context) { + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } + }) + .filedCacheLocation(CacheLocationEnum.MEMORY) + .sheet() + .doRead(); + + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + CacheData simpleData = new CacheData(); + simpleData.setName("姓名" + i); + simpleData.setAge((long)i); + list.add(simpleData); + } + return list; + } + + private List dataInvoke() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + CacheInvokeData simpleData = new CacheInvokeData(); + simpleData.setName("姓名" + i); + simpleData.setAge((long)i); + list.add(simpleData); + } + return list; + } + + private List dataInvokeMemory() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + CacheInvokeMemoryData simpleData = new CacheInvokeMemoryData(); + simpleData.setName("姓名" + i); + simpleData.setAge((long)i); + list.add(simpleData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeData.java new file mode 100644 index 000000000..6506c8af2 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.core.cache; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CacheInvokeData { + @ExcelProperty("姓名") + private String name; + + @ExcelProperty("年龄") + private Long age; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeMemoryData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeMemoryData.java new file mode 100644 index 000000000..25fa25de9 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/cache/CacheInvokeMemoryData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.core.cache; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CacheInvokeMemoryData { + @ExcelProperty("姓名") + private String name; + + @ExcelProperty("年龄") + private Long age; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java new file mode 100644 index 000000000..8f4d079d8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataListener.java @@ -0,0 +1,42 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class CellDataDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(CellDataDataListener.class); + List list = new ArrayList<>(); + + @Override + public void invoke(CellDataReadData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + CellDataReadData cellDataData = list.get(0); + + Assertions.assertEquals("2020年01月01日", cellDataData.getDate().getData()); + Assertions.assertEquals((long)cellDataData.getInteger1().getData(), 2L); + Assertions.assertEquals((long)cellDataData.getInteger2(), 2L); + if (context.readWorkbookHolder().getExcelType() != ExcelTypeEnum.CSV) { + Assertions.assertEquals(cellDataData.getFormulaValue().getFormulaData().getFormulaValue(), "B2+C2"); + } else { + Assertions.assertNull(cellDataData.getFormulaValue().getData()); + } + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java new file mode 100644 index 000000000..616afa72d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataDataTest.java @@ -0,0 +1,74 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.FormulaData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.DateUtils; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class CellDataDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("cellData07.xlsx"); + file03 = TestFileUtil.createNewFile("cellData03.xls"); + fileCsv = TestFileUtil.createNewFile("cellDataCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write(file, CellDataWriteData.class).sheet().doWrite(data()); + EasyExcel.read(file, CellDataReadData.class, new CellDataDataListener()).sheet().doRead(); + } + + private List data() throws Exception { + List list = new ArrayList<>(); + CellDataWriteData cellDataData = new CellDataWriteData(); + cellDataData.setDate(new WriteCellData<>(DateUtils.parseDate("2020-01-01 01:01:01"))); + WriteCellData integer1 = new WriteCellData<>(); + integer1.setType(CellDataTypeEnum.NUMBER); + integer1.setNumberValue(BigDecimal.valueOf(2L)); + cellDataData.setInteger1(integer1); + cellDataData.setInteger2(2); + WriteCellData formulaValue = new WriteCellData<>(); + FormulaData formulaData = new FormulaData(); + formulaValue.setFormulaData(formulaData); + formulaData.setFormulaValue("B2+C2"); + cellDataData.setFormulaValue(formulaValue); + list.add(cellDataData); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataReadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataReadData.java new file mode 100644 index 000000000..ad50e4a02 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataReadData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.metadata.data.ReadCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CellDataReadData { + @DateTimeFormat("yyyy年MM月dd日") + private ReadCellData date; + private ReadCellData integer1; + private Integer integer2; + private ReadCellData formulaValue; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataWriteData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataWriteData.java new file mode 100644 index 000000000..cf10b0d79 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/celldata/CellDataWriteData.java @@ -0,0 +1,24 @@ +package com.alibaba.easyexcel.test.core.celldata; + +import java.util.Date; + +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.metadata.data.WriteCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CellDataWriteData { + @DateTimeFormat("yyyy年MM月dd日") + private WriteCellData date; + private WriteCellData integer1; + private Integer integer2; + private WriteCellData formulaValue; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/charset/CharsetData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/charset/CharsetData.java new file mode 100644 index 000000000..234ec4847 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/charset/CharsetData.java @@ -0,0 +1,17 @@ +package com.alibaba.easyexcel.test.core.charset; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class CharsetData { + @ExcelProperty("姓名") + private String name; + @ExcelProperty("年纪") + private Integer age; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/charset/CharsetDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/charset/CharsetDataTest.java new file mode 100644 index 000000000..d65ab1842 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/charset/CharsetDataTest.java @@ -0,0 +1,110 @@ +package com.alibaba.easyexcel.test.core.charset; + +import java.io.File; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * charset + * + * @author Jiaju Zhuang + */ +@Slf4j +@TestMethodOrder(MethodOrderer.MethodName.class) +public class CharsetDataTest { + private static final Charset GBK = Charset.forName("GBK"); + private static File fileCsvGbk; + private static File fileCsvUtf8; + private static File fileCsvError; + + @BeforeAll + public static void init() { + fileCsvGbk = TestFileUtil.createNewFile("charset" + File.separator + "fileCsvGbk.csv"); + fileCsvUtf8 = TestFileUtil.createNewFile("charset" + File.separator + "fileCsvUtf8.csv"); + fileCsvError = TestFileUtil.createNewFile("charset" + File.separator + "fileCsvError.csv"); + } + + @Test + public void t01ReadAndWriteCsv() { + readAndWrite(fileCsvGbk, GBK); + readAndWrite(fileCsvUtf8, StandardCharsets.UTF_8); + } + + @Test + public void t02ReadAndWriteCsvError() { + EasyExcel.write(fileCsvError, CharsetData.class).charset(GBK).sheet().doWrite(data()); + EasyExcel.read(fileCsvError, CharsetData.class, new ReadListener() { + + private final List dataList = Lists.newArrayList(); + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + String head = headMap.get(0).getStringValue(); + Assertions.assertNotEquals("姓名", head); + } + + @Override + public void invoke(CharsetData data, AnalysisContext context) { + dataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + } + }).charset(StandardCharsets.UTF_8).sheet().doRead(); + } + + private void readAndWrite(File file, Charset charset) { + EasyExcel.write(file, CharsetData.class).charset(charset).sheet().doWrite(data()); + EasyExcel.read(file, CharsetData.class, new ReadListener() { + + private final List dataList = Lists.newArrayList(); + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + String head = headMap.get(0).getStringValue(); + Assertions.assertEquals("姓名", head); + } + + @Override + public void invoke(CharsetData data, AnalysisContext context) { + dataList.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(dataList.size(), 10); + CharsetData charsetData = dataList.get(0); + Assertions.assertEquals("姓名0", charsetData.getName()); + Assertions.assertEquals(0, (long)charsetData.getAge()); + } + }).charset(charset).sheet().doRead(); + } + + private List data() { + List list = Lists.newArrayList(); + for (int i = 0; i < 10; i++) { + CharsetData data = new CharsetData(); + data.setName("姓名" + i); + data.setAge(i); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java new file mode 100644 index 000000000..df621680e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/compatibility/CompatibilityTest.java @@ -0,0 +1,171 @@ +package com.alibaba.easyexcel.test.core.compatibility; + +import java.io.File; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.cache.Ehcache; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.util.TempFile; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * Compatible with some special files + * + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +@Slf4j +public class CompatibilityTest { + + @Test + public void t01() { + // https://github.com/alibaba/easyexcel/issues/2236 + List> list = EasyExcel.read(TestFileUtil.getPath() + "compatibility/t01.xls").sheet() + .doReadSync(); + Assertions.assertEquals(2, list.size()); + Map row1 = list.get(1); + Assertions.assertEquals("Q235(碳钢)", row1.get(0)); + } + + @Test + public void t02() { + // Exist in `sharedStrings.xml` `x:t` start tag, need to be compatible + List> list = EasyExcel.read(TestFileUtil.getPath() + "compatibility/t02.xlsx").sheet() + .headRowNumber(0).doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals(3, list.size()); + Map row2 = list.get(2); + Assertions.assertEquals("1,2-戊二醇", row2.get(2)); + } + + @Test + public void t03() { + // In the presence of the first line of a lot of null columns, ignore null columns + List> list = EasyExcel.read(TestFileUtil.getPath() + "compatibility/t03.xlsx").sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals(1, list.size()); + Map row0 = list.get(0); + Assertions.assertEquals(12, row0.size()); + } + + @Test + public void t04() { + // Exist in `sheet1.xml` `ns2:t` start tag, need to be compatible + List> list = EasyExcel.read(TestFileUtil.getPath() + "compatibility/t04.xlsx").sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals(56, list.size()); + Map row0 = list.get(0); + Assertions.assertEquals("QQSJK28F152A012242S0081", row0.get(5)); + } + + @Test + public void t05() { + // https://github.com/alibaba/easyexcel/issues/1956 + // Excel read date needs to be rounded + List> list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t05.xlsx") + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals("2023-01-01 00:00:00", list.get(0).get(0)); + Assertions.assertEquals("2023-01-01 00:00:00", list.get(1).get(0)); + Assertions.assertEquals("2023-01-01 00:00:00", list.get(2).get(0)); + Assertions.assertEquals("2023-01-01 00:00:01", list.get(3).get(0)); + Assertions.assertEquals("2023-01-01 00:00:01", list.get(4).get(0)); + } + + @Test + public void t06() { + // Keep error precision digital format + List> list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t06.xlsx") + .headRowNumber(0) + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals("2087.03", list.get(0).get(2)); + } + + @Test + public void t07() { + // https://github.com/alibaba/easyexcel/issues/2805 + // Excel read date needs to be rounded + List> list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t07.xlsx") + .readDefaultReturn(ReadDefaultReturnEnum.ACTUAL_DATA) + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals(0, new BigDecimal("24.1998124").compareTo((BigDecimal)list.get(0).get(11))); + + list = EasyExcel + .read(TestFileUtil.getPath() + "compatibility/t07.xlsx") + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals("24.20", list.get(0).get(11)); + } + + @Test + public void t08() { + // https://github.com/alibaba/easyexcel/issues/2693 + // Temporary files may be deleted if there is no operation for a long time, so they need to be recreated. + File file = TestFileUtil.createNewFile("compatibility/t08.xlsx"); + EasyExcel.write(file, SimpleData.class) + .sheet() + .doWrite(data()); + + List> list = EasyExcel.read(file) + .readCache(new Ehcache(null, 20)) + .sheet() + .doReadSync(); + Assertions.assertEquals(10L, list.size()); + + FileUtils.delete(new File(System.getProperty(TempFile.JAVA_IO_TMPDIR))); + + list = EasyExcel.read(file) + .readCache(new Ehcache(null, 20)) + .sheet() + .doReadSync(); + Assertions.assertEquals(10L, list.size()); + } + + @Test + public void t09() { + // `SH_x005f_x000D_Z002` exists in `ShardingString.xml` and needs to be replaced by: `SH_x000D_Z002` + File file = TestFileUtil.readFile("compatibility/t09.xlsx"); + List> list = EasyExcel.read(file) + .headRowNumber(0) + .sheet() + .doReadSync(); + log.info("data:{}", JSON.toJSONString(list)); + Assertions.assertEquals(1, list.size()); + + Assertions.assertEquals("SH_x000D_Z002", list.get(0).get(0)); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + SimpleData simpleData = new SimpleData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java new file mode 100644 index 000000000..adb4b1647 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataListener.java @@ -0,0 +1,49 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestUtil; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ConverterDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ConverterDataListener.class); + private final List list = new ArrayList<>(); + + @Override + public void invoke(ConverterReadData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + ConverterReadData data = list.get(0); + Assertions.assertEquals(TestUtil.TEST_DATE, data.getDate()); + Assertions.assertEquals(TestUtil.TEST_LOCAL_DATE, data.getLocalDate()); + Assertions.assertEquals(TestUtil.TEST_LOCAL_DATE_TIME, data.getLocalDateTime()); + Assertions.assertEquals(data.getBooleanData(), Boolean.TRUE); + Assertions.assertEquals(data.getBigDecimal().doubleValue(), BigDecimal.ONE.doubleValue(), 0.0); + Assertions.assertEquals(data.getBigInteger().intValue(), BigInteger.ONE.intValue(), 0.0); + Assertions.assertEquals((long)data.getLongData(), 1L); + Assertions.assertEquals((long)data.getIntegerData(), 1L); + Assertions.assertEquals((long)data.getShortData(), 1L); + Assertions.assertEquals((long)data.getByteData(), 1L); + Assertions.assertEquals(data.getDoubleData(), 1.0, 0.0); + Assertions.assertEquals(data.getFloatData(), (float)1.0, 0.0); + Assertions.assertEquals(data.getString(), "测试"); + Assertions.assertEquals(data.getCellData().getStringValue(), "自定义"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java new file mode 100644 index 000000000..0c127626d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterDataTest.java @@ -0,0 +1,132 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.io.File; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.easyexcel.test.util.TestUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.FileUtils; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ConverterDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File fileImage07; + private static File fileImage03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("converter07.xlsx"); + file03 = TestFileUtil.createNewFile("converter03.xls"); + fileCsv = TestFileUtil.createNewFile("converterCsv.csv"); + fileImage07 = TestFileUtil.createNewFile("converterImage07.xlsx"); + fileImage03 = TestFileUtil.createNewFile("converterImage03.xls"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write(file, ConverterWriteData.class).sheet().doWrite(data()); + EasyExcel.read(file, ConverterReadData.class, new ConverterDataListener()).sheet().doRead(); + } + + @Test + public void t11ReadAllConverter07() { + readAllConverter("converter" + File.separator + "converter07.xlsx"); + } + + @Test + public void t12ReadAllConverter03() { + readAllConverter("converter" + File.separator + "converter03.xls"); + } + + @Test + public void t13ReadAllConverterCsv() { + readAllConverter("converter" + File.separator + "converterCsv.csv"); + } + + @Test + public void t21WriteImage07() throws Exception { + writeImage(fileImage07); + } + + @Test + public void t22WriteImage03() throws Exception { + writeImage(fileImage03); + } + + private void writeImage(File file) throws Exception { + InputStream inputStream = null; + try { + List list = new ArrayList<>(); + ImageData imageData = new ImageData(); + list.add(imageData); + String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; + imageData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); + imageData.setFile(new File(imagePath)); + imageData.setString(imagePath); + inputStream = FileUtils.openInputStream(new File(imagePath)); + imageData.setInputStream(inputStream); + EasyExcel.write(file, ImageData.class).sheet().doWrite(list); + } finally { + if (inputStream != null) { + inputStream.close(); + } + } + } + + private void readAllConverter(String fileName) { + EasyExcel.read(TestFileUtil.readFile(fileName), ReadAllConverterData.class, new ReadAllConverterDataListener()) + .sheet().doRead(); + } + + private List data() throws Exception { + List list = new ArrayList(); + ConverterWriteData converterWriteData = new ConverterWriteData(); + converterWriteData.setDate(TestUtil.TEST_DATE); + converterWriteData.setLocalDate(TestUtil.TEST_LOCAL_DATE); + converterWriteData.setLocalDateTime(TestUtil.TEST_LOCAL_DATE_TIME); + converterWriteData.setBooleanData(Boolean.TRUE); + converterWriteData.setBigDecimal(BigDecimal.ONE); + converterWriteData.setBigInteger(BigInteger.ONE); + converterWriteData.setLongData(1L); + converterWriteData.setIntegerData(1); + converterWriteData.setShortData((short)1); + converterWriteData.setByteData((byte)1); + converterWriteData.setDoubleData(1.0); + converterWriteData.setFloatData((float)1.0); + converterWriteData.setString("测试"); + converterWriteData.setCellData(new WriteCellData<>("自定义")); + list.add(converterWriteData); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterReadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterReadData.java new file mode 100644 index 000000000..1ee3aea93 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterReadData.java @@ -0,0 +1,51 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.metadata.data.ReadCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ConverterReadData { + @ExcelProperty("日期") + private Date date; + @ExcelProperty("本地日期") + private LocalDate localDate; + @ExcelProperty("本地日期时间") + private LocalDateTime localDateTime; + @ExcelProperty("布尔") + private Boolean booleanData; + @ExcelProperty("大数") + private BigDecimal bigDecimal; + @ExcelProperty("大整数") + private BigInteger bigInteger; + @ExcelProperty("长整型") + private long longData; + @ExcelProperty("整型") + private Integer integerData; + @ExcelProperty("短整型") + private Short shortData; + @ExcelProperty("字节型") + private Byte byteData; + @ExcelProperty("双精度浮点型") + private double doubleData; + @ExcelProperty("浮点型") + private Float floatData; + @ExcelProperty("字符串") + private String string; + @ExcelProperty("自定义") + private ReadCellData cellData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterTest.java new file mode 100644 index 000000000..cf73999e6 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterTest.java @@ -0,0 +1,29 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; + +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.converters.floatconverter.FloatNumberConverter; +import com.alibaba.excel.metadata.data.WriteCellData; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ConverterTest { + + @Test + public void t01FloatNumberConverter() { + FloatNumberConverter floatNumberConverter = new FloatNumberConverter(); + WriteConverterContext context = new WriteConverterContext<>(); + context.setValue(95.62F); + WriteCellData writeCellData = floatNumberConverter.convertToExcelData(context); + Assertions.assertEquals(0, writeCellData.getNumberValue().compareTo(new BigDecimal("95.62"))); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterWriteData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterWriteData.java new file mode 100644 index 000000000..88d7b7651 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ConverterWriteData.java @@ -0,0 +1,51 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.metadata.data.WriteCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ConverterWriteData { + @ExcelProperty("日期") + private Date date; + @ExcelProperty("本地日期") + private LocalDate localDate; + @ExcelProperty("本地日期时间") + private LocalDateTime localDateTime; + @ExcelProperty("布尔") + private Boolean booleanData; + @ExcelProperty("大数") + private BigDecimal bigDecimal; + @ExcelProperty("大整数") + private BigInteger bigInteger; + @ExcelProperty("长整型") + private long longData; + @ExcelProperty("整型") + private Integer integerData; + @ExcelProperty("短整型") + private Short shortData; + @ExcelProperty("字节型") + private Byte byteData; + @ExcelProperty("双精度浮点型") + private double doubleData; + @ExcelProperty("浮点型") + private Float floatData; + @ExcelProperty("字符串") + private String string; + @ExcelProperty("自定义") + private WriteCellData cellData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java new file mode 100644 index 000000000..fb31e127c --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ImageData.java @@ -0,0 +1,29 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.io.File; +import java.io.InputStream; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.converters.string.StringImageConverter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(500) +@ColumnWidth(500 / 8) +public class ImageData { + private File file; + private InputStream inputStream; + @ExcelProperty(converter = StringImageConverter.class) + private String string; + private byte[] byteArray; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java new file mode 100644 index 000000000..dac731278 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterData.java @@ -0,0 +1,57 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ReadAllConverterData { + private BigDecimal bigDecimalBoolean; + private BigDecimal bigDecimalNumber; + private BigDecimal bigDecimalString; + private BigInteger bigIntegerBoolean; + private BigInteger bigIntegerNumber; + private BigInteger bigIntegerString; + private Boolean booleanBoolean; + private Boolean booleanNumber; + private Boolean booleanString; + private Byte byteBoolean; + private Byte byteNumber; + private Byte byteString; + private Date dateNumber; + private Date dateString; + private LocalDateTime localDateTimeNumber; + private LocalDateTime localDateTimeString; + private Double doubleBoolean; + private Double doubleNumber; + private Double doubleString; + private Float floatBoolean; + private Float floatNumber; + private Float floatString; + private Integer integerBoolean; + private Integer integerNumber; + private Integer integerString; + private Long longBoolean; + private Long longNumber; + private Long longString; + private Short shortBoolean; + private Short shortNumber; + private Short shortString; + private String stringBoolean; + private String stringNumber; + private String stringString; + private String stringError; + private String stringFormulaNumber; + private String stringFormulaString; + private String stringNumberDate; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java new file mode 100644 index 000000000..eae1fa92f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/converter/ReadAllConverterDataListener.java @@ -0,0 +1,86 @@ +package com.alibaba.easyexcel.test.core.converter; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelCommonException; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ReadAllConverterDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ReadAllConverterDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(ReadAllConverterData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + ReadAllConverterData data = list.get(0); + Assertions.assertEquals(data.getBigDecimalBoolean().doubleValue(), BigDecimal.ONE.doubleValue(), 0.0); + Assertions.assertEquals(data.getBigDecimalNumber().doubleValue(), BigDecimal.ONE.doubleValue(), 0.0); + Assertions.assertEquals(data.getBigDecimalString().doubleValue(), BigDecimal.ONE.doubleValue(), 0.0); + Assertions.assertEquals(data.getBigIntegerBoolean().intValue(), BigInteger.ONE.intValue(), 0.0); + Assertions.assertEquals(data.getBigIntegerNumber().intValue(), BigInteger.ONE.intValue(), 0.0); + Assertions.assertEquals(data.getBigIntegerString().intValue(), BigInteger.ONE.intValue(), 0.0); + Assertions.assertTrue(data.getBooleanBoolean()); + Assertions.assertTrue(data.getBooleanNumber()); + Assertions.assertTrue(data.getBooleanString()); + Assertions.assertEquals((long)data.getByteBoolean(), 1L); + Assertions.assertEquals((long)data.getByteNumber(), 1L); + Assertions.assertEquals((long)data.getByteString(), 1L); + try { + Assertions.assertEquals(data.getDateNumber(), DateUtils.parseDate("2020-01-01 01:01:01")); + Assertions.assertEquals(data.getDateString(), DateUtils.parseDate("2020-01-01 01:01:01")); + } catch (ParseException e) { + throw new ExcelCommonException("Test Exception", e); + } + Assertions.assertEquals(data.getLocalDateTimeNumber(), + DateUtils.parseLocalDateTime("2020-01-01 01:01:01", null, null)); + Assertions.assertEquals(data.getLocalDateTimeString(), + DateUtils.parseLocalDateTime("2020-01-01 01:01:01", null, null)); + Assertions.assertEquals(data.getDoubleBoolean(), 1.0, 0.0); + Assertions.assertEquals(data.getDoubleNumber(), 1.0, 0.0); + Assertions.assertEquals(data.getDoubleString(), 1.0, 0.0); + Assertions.assertEquals(data.getFloatBoolean(), (float)1.0, 0.0); + Assertions.assertEquals(data.getFloatNumber(), (float)1.0, 0.0); + Assertions.assertEquals(data.getFloatString(), (float)1.0, 0.0); + Assertions.assertEquals((long)data.getIntegerBoolean(), 1L); + Assertions.assertEquals((long)data.getIntegerNumber(), 1L); + Assertions.assertEquals((long)data.getIntegerString(), 1L); + Assertions.assertEquals((long)data.getLongBoolean(), 1L); + Assertions.assertEquals((long)data.getLongNumber(), 1L); + Assertions.assertEquals((long)data.getLongString(), 1L); + Assertions.assertEquals((long)data.getShortBoolean(), 1L); + Assertions.assertEquals((long)data.getShortNumber(), 1L); + Assertions.assertEquals((long)data.getShortString(), 1L); + Assertions.assertEquals(data.getStringBoolean().toLowerCase(), "true"); + Assertions.assertEquals(data.getStringString(), "测试"); + Assertions.assertEquals(data.getStringError(), "#VALUE!"); + if (context.readWorkbookHolder().getExcelType() != ExcelTypeEnum.CSV) { + Assertions.assertEquals("2020-1-1 1:01", data.getStringNumberDate()); + } else { + Assertions.assertEquals("2020-01-01 01:01:01", data.getStringNumberDate()); + } + double doubleStringFormulaNumber = new BigDecimal(data.getStringFormulaNumber()).doubleValue(); + Assertions.assertEquals(doubleStringFormulaNumber, 2.0, 0.0); + Assertions.assertEquals(data.getStringFormulaString(), "1测试"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatData.java new file mode 100644 index 000000000..31cdd85dc --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.core.dataformat; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class DateFormatData { + private String date; + private String dateStringCn; + private String dateStringCn2; + private String dateStringUs; + private String number; + private String numberStringCn; + private String numberStringUs; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java new file mode 100644 index 000000000..d8fd95b37 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/dataformat/DateFormatTest.java @@ -0,0 +1,104 @@ +package com.alibaba.easyexcel.test.core.dataformat; + +import java.io.File; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +@Slf4j +public class DateFormatTest { + + private static File file07V2; + private static File file07; + private static File file03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xlsx"); + file03 = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xls"); + file07V2 = TestFileUtil.readFile("dataformat" + File.separator + "dataformatv2.xlsx"); + + } + + @Test + public void t01Read07() { + readCn(file07); + readUs(file07); + } + + @Test + public void t02Read03() { + readCn(file03); + readUs(file03); + } + + @Test + public void t03Read() { + List> dataMap = EasyExcel.read(file07V2).headRowNumber(0).doReadAllSync(); + log.info("dataMap:{}", JSON.toJSONString(dataMap)); + Assertions.assertEquals("15:00", dataMap.get(0).get(0)); + Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(1).get(0)); + Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(2).get(0)); + Assertions.assertEquals("2023-1-01 00:00:01", dataMap.get(3).get(0)); + Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(4).get(0)); + Assertions.assertEquals("2023-1-01 00:00:00", dataMap.get(5).get(0)); + Assertions.assertEquals("2023-1-01 00:00:01", dataMap.get(6).get(0)); + } + + private void readCn(File file) { + List list = + EasyExcel.read(file, DateFormatData.class, null).locale(Locale.CHINA).sheet().doReadSync(); + for (DateFormatData data : list) { + if (!Objects.equals(data.getDateStringCn(), data.getDate()) && !Objects.equals(data.getDateStringCn2(), + data.getDate())) { + log.info("date:cn:{},{},{}", data.getDateStringCn(), data.getDateStringCn2(), data.getDate()); + } + if (data.getNumberStringCn() != null && !data.getNumberStringCn().equals(data.getNumber())) { + log.info("number:cn{},{}", data.getNumberStringCn(), data.getNumber()); + } + } + for (DateFormatData data : list) { + // The way dates are read in Chinese is different on Linux and Mac, so it is acceptable if it matches + // either one. + // For example, on Linux: 1-Jan -> 1-1月 + // On Mac: 1-Jan -> 1-一月 + Assertions.assertTrue( + Objects.equals(data.getDateStringCn(), data.getDate()) || Objects.equals(data.getDateStringCn2(), + data.getDate())); + Assertions.assertEquals(data.getNumberStringCn(), data.getNumber()); + } + } + + private void readUs(File file) { + List list = + EasyExcel.read(file, DateFormatData.class, null).locale(Locale.US).sheet().doReadSync(); + for (DateFormatData data : list) { + if (data.getDateStringUs() != null && !data.getDateStringUs().equals(data.getDate())) { + log.info("date:us:{},{}", data.getDateStringUs(), data.getDate()); + } + if (data.getNumberStringUs() != null && !data.getNumberStringUs().equals(data.getNumber())) { + log.info("number:us{},{}", data.getNumberStringUs(), data.getNumber()); + } + } + for (DateFormatData data : list) { + Assertions.assertEquals(data.getDateStringUs(), data.getDate()); + Assertions.assertEquals(data.getNumberStringUs(), data.getNumber()); + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java new file mode 100644 index 000000000..6cba3a71f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class EncryptData { + @ExcelProperty("姓名") + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java new file mode 100644 index 000000000..9727fefec --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataListener.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class EncryptDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(EncryptDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(EncryptData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 10); + Assertions.assertEquals(list.get(0).getName(), "姓名0"); + Assertions.assertEquals((int)(context.readSheetHolder().getSheetNo()), 0); + Assertions.assertEquals( + context.readSheetHolder().getExcelReadHeadProperty().getHeadMap().get(0).getHeadNameList().get(0), "姓名"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java new file mode 100644 index 000000000..8c2f0dca5 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/encrypt/EncryptDataTest.java @@ -0,0 +1,95 @@ +package com.alibaba.easyexcel.test.core.encrypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class EncryptDataTest { + + private static File file07; + private static File file03; + private static File file07OutputStream; + private static File file03OutputStream; + + @Test + public void testformat() { + DecimalFormat decimalFormat = new DecimalFormat("0.00"); + decimalFormat.setRoundingMode(RoundingMode.HALF_UP); + BigDecimal bigDecimal = new BigDecimal("0.105"); + + System.out.println(decimalFormat.format(bigDecimal)); + } + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("encrypt07.xlsx"); + file03 = TestFileUtil.createNewFile("encrypt03.xls"); + file07OutputStream = TestFileUtil.createNewFile("encryptOutputStream07.xlsx"); + file03OutputStream = TestFileUtil.createNewFile("encryptOutputStream03.xls"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteStream07() throws Exception { + readAndWriteStream(file07OutputStream, ExcelTypeEnum.XLSX); + } + + @Test + public void t04ReadAndWriteStream03() throws Exception { + readAndWriteStream(file03OutputStream, ExcelTypeEnum.XLS); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, EncryptData.class).password("123456").sheet().doWrite(data()); + EasyExcel.read(file, EncryptData.class, new EncryptDataListener()).password("123456").sheet().doRead(); + } + + private void readAndWriteStream(File file, ExcelTypeEnum excelType) throws Exception { + FileOutputStream fileOutputStream = new FileOutputStream(file); + EasyExcel.write(fileOutputStream, EncryptData.class).password("123456").excelType(excelType).sheet() + .doWrite(data()); + fileOutputStream.close(); + + FileInputStream fileInputStream = new FileInputStream(file); + EasyExcel.read(fileInputStream, EncryptData.class, new EncryptDataListener()).password("123456").sheet() + .doRead(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + SimpleData simpleData = new SimpleData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExcelAnalysisStopSheetExceptionDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExcelAnalysisStopSheetExceptionDataListener.java new file mode 100644 index 000000000..8a216d568 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExcelAnalysisStopSheetExceptionDataListener.java @@ -0,0 +1,52 @@ +package com.alibaba.easyexcel.test.core.exception; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.exception.ExcelAnalysisStopException; +import com.alibaba.excel.exception.ExcelAnalysisStopSheetException; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.SetUtils; +import org.assertj.core.internal.Maps; +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Slf4j +public class ExcelAnalysisStopSheetExceptionDataListener extends AnalysisEventListener { + + private Map> dataMap = MapUtils.newHashMap(); + + + @Override + public void invoke(ExceptionData data, AnalysisContext context) { + List sheetDataList = dataMap.computeIfAbsent(context.readSheetHolder().getSheetNo(), + key -> ListUtils.newArrayList()); + sheetDataList.add(data.getName()); + if (sheetDataList.size() >= 5) { + throw new ExcelAnalysisStopSheetException(); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + List sheetDataList = dataMap.get(context.readSheetHolder().getSheetNo()); + Assertions.assertNotNull(sheetDataList); + Assertions.assertEquals(5, sheetDataList.size()); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionData.java new file mode 100644 index 000000000..c989ce998 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.exception; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExceptionData { + @ExcelProperty("姓名") + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java new file mode 100644 index 000000000..95b6ddc36 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataListener.java @@ -0,0 +1,48 @@ +package com.alibaba.easyexcel.test.core.exception; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ExceptionDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionData.class); + List list = new ArrayList(); + + @Override + public void onException(Exception exception, AnalysisContext context) { + LOGGER.info("抛出异常,忽略:{}", exception.getMessage(), exception); + } + + @Override + public boolean hasNext(AnalysisContext context) { + return list.size() != 8; + } + + @Override + public void invoke(ExceptionData data, AnalysisContext context) { + list.add(data); + if (list.size() == 5) { + int i = 5 / 0; + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 8); + Assertions.assertEquals(list.get(0).getName(), "姓名0"); + Assertions.assertEquals((int)(context.readSheetHolder().getSheetNo()), 0); + Assertions.assertEquals( + context.readSheetHolder().getExcelReadHeadProperty().getHeadMap().get(0).getHeadNameList().get(0), "姓名"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataTest.java new file mode 100644 index 000000000..0d3fe12e3 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionDataTest.java @@ -0,0 +1,144 @@ +package com.alibaba.easyexcel.test.core.exception; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; + +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ExceptionDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File fileExcelAnalysisStopSheetException07; + private static File fileExcelAnalysisStopSheetException03; + private static File fileExcelAnalysisStopSheetExceptionCsv; + private static File fileException07; + private static File fileException03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("exception.xlsx"); + file03 = TestFileUtil.createNewFile("exception.xls"); + fileCsv = TestFileUtil.createNewFile("exception.csv"); + fileExcelAnalysisStopSheetException07 = TestFileUtil.createNewFile("excelAnalysisStopSheetException.xlsx"); + fileExcelAnalysisStopSheetException03 = TestFileUtil.createNewFile("excelAnalysisStopSheetException.xls"); + fileException07 = TestFileUtil.createNewFile("exceptionThrow.xlsx"); + fileException03 = TestFileUtil.createNewFile("exceptionThrow.xls"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv); + } + + @Test + public void t11ReadAndWrite07() throws Exception { + readAndWriteException(fileException07); + } + + @Test + public void t12ReadAndWrite03() throws Exception { + readAndWriteException(fileException03); + } + + @Test + public void t21ReadAndWrite07() throws Exception { + readAndWriteExcelAnalysisStopSheetException(fileExcelAnalysisStopSheetException07); + } + + @Test + public void t22ReadAndWrite03() throws Exception { + readAndWriteExcelAnalysisStopSheetException(fileExcelAnalysisStopSheetException03); + } + + + private void readAndWriteExcelAnalysisStopSheetException(File file) throws Exception { + try (ExcelWriter excelWriter = EasyExcel.write(file, ExceptionData.class).build()) { + for (int i = 0; i < 5; i++) { + String sheetName = "sheet" + i; + WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build(); + List data = data(sheetName); + excelWriter.write(data, writeSheet); + } + } + + ExcelAnalysisStopSheetExceptionDataListener excelAnalysisStopSheetExceptionDataListener + = new ExcelAnalysisStopSheetExceptionDataListener(); + EasyExcel.read(file, ExceptionData.class, excelAnalysisStopSheetExceptionDataListener).doReadAll(); + Map> dataMap = excelAnalysisStopSheetExceptionDataListener.getDataMap(); + Assertions.assertEquals(5, dataMap.size()); + for (int i = 0; i < 5; i++) { + List sheetDataList = dataMap.get(i); + Assertions.assertNotNull(sheetDataList); + Assertions.assertEquals(5, sheetDataList.size()); + String sheetName = "sheet" + i; + + for (String sheetData : sheetDataList) { + Assertions.assertTrue(sheetData.startsWith(sheetName)); + } + } + } + + private void readAndWriteException(File file) throws Exception { + EasyExcel.write(new FileOutputStream(file), ExceptionData.class).sheet().doWrite(data()); + ArithmeticException exception = Assertions.assertThrows(ArithmeticException.class, () -> EasyExcel.read( + new FileInputStream(file), ExceptionData.class, + new ExceptionThrowDataListener()).sheet().doRead()); + Assertions.assertEquals("/ by zero", exception.getMessage()); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write(new FileOutputStream(file), ExceptionData.class).sheet().doWrite(data()); + EasyExcel.read(new FileInputStream(file), ExceptionData.class, new ExceptionDataListener()).sheet().doRead(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + ExceptionData simpleData = new ExceptionData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } + + private List data(String prefix) { + List list = Lists.newArrayList(); + for (int i = 0; i < 10; i++) { + ExceptionData simpleData = new ExceptionData(); + simpleData.setName(prefix + "-姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionThrowDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionThrowDataListener.java new file mode 100644 index 000000000..01420c500 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/exception/ExceptionThrowDataListener.java @@ -0,0 +1,30 @@ +package com.alibaba.easyexcel.test.core.exception; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ExceptionThrowDataListener implements ReadListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionData.class); + List list = new ArrayList(); + + @Override + public void invoke(ExceptionData data, AnalysisContext context) { + list.add(data); + if (list.size() == 5) { + int i = 5 / 0; + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeData.java new file mode 100644 index 000000000..737bd61ba --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeData.java @@ -0,0 +1,24 @@ +package com.alibaba.easyexcel.test.core.excludeorinclude; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExcludeOrIncludeData { + @ExcelProperty(order = 1) + private String column1; + @ExcelProperty(order = 2) + private String column2; + @ExcelProperty(order = 3) + private String column3; + @ExcelProperty(order = 4) + private String column4; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java new file mode 100644 index 000000000..314b5de4a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/excludeorinclude/ExcludeOrIncludeDataTest.java @@ -0,0 +1,259 @@ +package com.alibaba.easyexcel.test.core.excludeorinclude; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import org.junit.jupiter.api.*; + +import java.io.File; +import java.util.*; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ExcludeOrIncludeDataTest { + + private static File excludeIndex07; + private static File excludeIndex03; + private static File excludeIndexCsv; + private static File excludeFieldName07; + private static File excludeFieldName03; + private static File excludeFieldNameCsv; + private static File includeIndex07; + private static File includeIndex03; + private static File includeIndexCsv; + private static File includeFieldName07; + private static File includeFieldName03; + private static File includeFieldNameCsv; + private static File includeFieldNameOrder07; + private static File includeFieldNameOrder03; + private static File includeFieldNameOrderCsv; + + private static File includeFieldNameOrderIndex07; + private static File includeFieldNameOrderIndex03; + private static File includeFieldNameOrderIndexCsv; + + @BeforeAll + public static void init() { + excludeIndex07 = TestFileUtil.createNewFile("excludeIndex.xlsx"); + excludeIndex03 = TestFileUtil.createNewFile("excludeIndex.xls"); + excludeIndexCsv = TestFileUtil.createNewFile("excludeIndex.csv"); + excludeFieldName07 = TestFileUtil.createNewFile("excludeFieldName.xlsx"); + excludeFieldName03 = TestFileUtil.createNewFile("excludeFieldName.xls"); + excludeFieldNameCsv = TestFileUtil.createNewFile("excludeFieldName.csv"); + includeIndex07 = TestFileUtil.createNewFile("includeIndex.xlsx"); + includeIndex03 = TestFileUtil.createNewFile("includeIndex.xls"); + includeIndexCsv = TestFileUtil.createNewFile("includeIndex.csv"); + includeFieldName07 = TestFileUtil.createNewFile("includeFieldName.xlsx"); + includeFieldName03 = TestFileUtil.createNewFile("includeFieldName.xls"); + includeFieldNameCsv = TestFileUtil.createNewFile("includeFieldName.csv"); + includeFieldNameOrder07 = TestFileUtil.createNewFile("includeFieldNameOrder.xlsx"); + includeFieldNameOrder03 = TestFileUtil.createNewFile("includeFieldNameOrder.xls"); + includeFieldNameOrderCsv = TestFileUtil.createNewFile("includeFieldNameOrder.csv"); + includeFieldNameOrderIndex07 = TestFileUtil.createNewFile("includeFieldNameOrderIndex.xlsx"); + includeFieldNameOrderIndex03 = TestFileUtil.createNewFile("includeFieldNameOrderIndex.xls"); + includeFieldNameOrderIndexCsv = TestFileUtil.createNewFile("includeFieldNameOrderIndex.csv"); + } + + @Test + public void t01ExcludeIndex07() { + excludeIndex(excludeIndex07); + } + + @Test + public void t02ExcludeIndex03() { + excludeIndex(excludeIndex03); + } + + @Test + public void t03ExcludeIndexCsv() { + excludeIndex(excludeIndexCsv); + } + + @Test + public void t11ExcludeFieldName07() { + excludeFieldName(excludeFieldName07); + } + + @Test + public void t12ExcludeFieldName03() { + excludeFieldName(excludeFieldName03); + } + + @Test + public void t13ExcludeFieldNameCsv() { + excludeFieldName(excludeFieldNameCsv); + } + + @Test + public void t21IncludeIndex07() { + includeIndex(includeIndex07); + } + + @Test + public void t22IncludeIndex03() { + includeIndex(includeIndex03); + } + + @Test + public void t23IncludeIndexCsv() { + includeIndex(includeIndexCsv); + } + + @Test + public void t31IncludeFieldName07() { + includeFieldName(includeFieldName07); + } + + @Test + public void t32IncludeFieldName03() { + includeFieldName(includeFieldName03); + } + + @Test + public void t33IncludeFieldNameCsv() { + includeFieldName(includeFieldNameCsv); + } + + @Test + public void t41IncludeFieldNameOrder07() { + includeFieldNameOrder(includeFieldNameOrder07); + } + + @Test + public void t42IncludeFieldNameOrder03() { + includeFieldNameOrder(includeFieldNameOrder03); + } + + @Test + public void t43IncludeFieldNameOrderCsv() { + includeFieldNameOrder(includeFieldNameOrderCsv); + } + + @Test + public void t41IncludeFieldNameOrderIndex07() { + includeFieldNameOrderIndex(includeFieldNameOrderIndex07); + } + + @Test + public void t42IncludeFieldNameOrderIndex03() { + includeFieldNameOrderIndex(includeFieldNameOrderIndex03); + } + + @Test + public void t43IncludeFieldNameOrderIndexCsv() { + includeFieldNameOrderIndex(includeFieldNameOrderIndexCsv); + } + + private void excludeIndex(File file) { + Set excludeColumnIndexes = new HashSet(); + excludeColumnIndexes.add(0); + excludeColumnIndexes.add(3); + EasyExcel.write(file, ExcludeOrIncludeData.class).excludeColumnIndexes(excludeColumnIndexes).sheet() + .doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("column2", record.get(0)); + Assertions.assertEquals("column3", record.get(1)); + + } + + private void excludeFieldName(File file) { + Set excludeColumnFieldNames = new HashSet(); + excludeColumnFieldNames.add("column1"); + excludeColumnFieldNames.add("column3"); + excludeColumnFieldNames.add("column4"); + EasyExcel.write(file, ExcludeOrIncludeData.class).excludeColumnFieldNames(excludeColumnFieldNames).sheet() + .doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(1, record.size()); + Assertions.assertEquals("column2", record.get(0)); + + } + + private void includeIndex(File file) { + Set includeColumnIndexes = new HashSet(); + includeColumnIndexes.add(1); + includeColumnIndexes.add(2); + EasyExcel.write(file, ExcludeOrIncludeData.class).includeColumnIndexes(includeColumnIndexes).sheet() + .doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("column2", record.get(0)); + Assertions.assertEquals("column3", record.get(1)); + + } + + private void includeFieldName(File file) { + Set includeColumnFieldNames = new HashSet(); + includeColumnFieldNames.add("column2"); + includeColumnFieldNames.add("column3"); + EasyExcel.write(file, ExcludeOrIncludeData.class) + .sheet() + .includeColumnFieldNames(includeColumnFieldNames) + .doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(2, record.size()); + Assertions.assertEquals("column2", record.get(0)); + Assertions.assertEquals("column3", record.get(1)); + } + + private void includeFieldNameOrderIndex(File file) { + List includeColumnIndexes = new ArrayList<>(); + includeColumnIndexes.add(3); + includeColumnIndexes.add(1); + includeColumnIndexes.add(2); + includeColumnIndexes.add(0); + EasyExcel.write(file, ExcludeOrIncludeData.class) + .includeColumnIndexes(includeColumnIndexes) + .orderByIncludeColumn(true). + sheet() + .doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(4, record.size()); + Assertions.assertEquals("column4", record.get(0)); + Assertions.assertEquals("column2", record.get(1)); + Assertions.assertEquals("column3", record.get(2)); + Assertions.assertEquals("column1", record.get(3)); + } + + private void includeFieldNameOrder(File file) { + List includeColumnFieldNames = new ArrayList<>(); + includeColumnFieldNames.add("column4"); + includeColumnFieldNames.add("column2"); + includeColumnFieldNames.add("column3"); + EasyExcel.write(file, ExcludeOrIncludeData.class) + .includeColumnFieldNames(includeColumnFieldNames) + .orderByIncludeColumn(true). + sheet() + .doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals(3, record.size()); + Assertions.assertEquals("column4", record.get(0)); + Assertions.assertEquals("column2", record.get(1)); + Assertions.assertEquals("column3", record.get(2)); + } + + private List data() { + List list = new ArrayList(); + ExcludeOrIncludeData excludeOrIncludeData = new ExcludeOrIncludeData(); + excludeOrIncludeData.setColumn1("column1"); + excludeOrIncludeData.setColumn2("column2"); + excludeOrIncludeData.setColumn3("column3"); + excludeOrIncludeData.setColumn4("column4"); + list.add(excludeOrIncludeData); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraData.java new file mode 100644 index 000000000..0424509e7 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.extra; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ExtraData { + + private String row1; + + private String row2; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataListener.java new file mode 100644 index 000000000..e11b1f167 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataListener.java @@ -0,0 +1,55 @@ +package com.alibaba.easyexcel.test.core.extra; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ExtraDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ExtraData.class); + + @Override + public void invoke(ExtraData data, AnalysisContext context) {} + + @Override + public void doAfterAllAnalysed(AnalysisContext context) {} + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + LOGGER.info("extra data:{}", JSON.toJSONString(extra)); + switch (extra.getType()) { + case COMMENT: + Assertions.assertEquals("批注的内容", extra.getText()); + Assertions.assertEquals(4, (int)extra.getRowIndex()); + Assertions.assertEquals(0, (int)extra.getColumnIndex()); + break; + case HYPERLINK: + if ("Sheet1!A1".equals(extra.getText())) { + Assertions.assertEquals(1, (int)extra.getRowIndex()); + Assertions.assertEquals(0, (int)extra.getColumnIndex()); + } else if ("Sheet2!A1".equals(extra.getText())) { + Assertions.assertEquals(2, (int)extra.getFirstRowIndex()); + Assertions.assertEquals(0, (int)extra.getFirstColumnIndex()); + Assertions.assertEquals(3, (int)extra.getLastRowIndex()); + Assertions.assertEquals(1, (int)extra.getLastColumnIndex()); + } else { + Assertions.fail("Unknown hyperlink!"); + } + break; + case MERGE: + Assertions.assertEquals(5, (int)extra.getFirstRowIndex()); + Assertions.assertEquals(0, (int)extra.getFirstColumnIndex()); + Assertions.assertEquals(6, (int)extra.getLastRowIndex()); + Assertions.assertEquals(1, (int)extra.getLastColumnIndex()); + break; + default: + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataTest.java new file mode 100644 index 000000000..9409a8df7 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/extra/ExtraDataTest.java @@ -0,0 +1,86 @@ +package com.alibaba.easyexcel.test.core.extra; + +import java.io.File; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ExtraDataTest { + private static final Logger LOGGER = LoggerFactory.getLogger(ExtraDataTest.class); + private static File file03; + private static File file07; + + private static File extraRelationships; + + @BeforeAll + public static void init() { + file03 = TestFileUtil.readFile("extra" + File.separator + "extra.xls"); + file07 = TestFileUtil.readFile("extra" + File.separator + "extra.xlsx"); + extraRelationships = TestFileUtil.readFile("extra" + File.separator + "extraRelationships.xlsx"); + } + + @Test + public void t01Read07() { + read(file07); + } + + @Test + public void t02Read03() { + read(file03); + } + + @Test + public void t03Read() { + EasyExcel.read(extraRelationships, ExtraData.class, new ReadListener() { + @Override + public void invoke(Object data, AnalysisContext context) { + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + } + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + LOGGER.info("extra data:{}", JSON.toJSONString(extra)); + switch (extra.getType()) { + case HYPERLINK: + if ("222222222".equals(extra.getText())) { + Assertions.assertEquals(1, (int)extra.getRowIndex()); + Assertions.assertEquals(0, (int)extra.getColumnIndex()); + } else if ("333333333333".equals(extra.getText())) { + Assertions.assertEquals(1, (int)extra.getRowIndex()); + Assertions.assertEquals(1, (int)extra.getColumnIndex()); + } else { + Assertions.fail("Unknown hyperlink!"); + } + break; + default: + } + } + }) + .extraRead(CellExtraTypeEnum.HYPERLINK) + .sheet() + .doRead(); + } + + private void read(File file) { + EasyExcel.read(file, ExtraData.class, new ExtraDataListener()).extraRead(CellExtraTypeEnum.COMMENT) + .extraRead(CellExtraTypeEnum.HYPERLINK).extraRead(CellExtraTypeEnum.MERGE).sheet().doRead(); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java new file mode 100644 index 000000000..9a33351ca --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/FillData.java @@ -0,0 +1,23 @@ +package com.alibaba.easyexcel.test.core.fill; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.converters.doubleconverter.DoubleStringConverter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FillData { + private String name; + @NumberFormat("#") + @ExcelProperty(converter = DoubleStringConverter.class) + private Double number; + private String empty; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java new file mode 100644 index 000000000..6d88f8486 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/FillDataTest.java @@ -0,0 +1,225 @@ +package com.alibaba.easyexcel.test.core.fill; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class FillDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File simpleTemplate07; + private static File simpleTemplate03; + private static File simpleTemplateCsv; + private static File fileComplex07; + private static File complexFillTemplate07; + private static File fileComplex03; + private static File complexFillTemplate03; + private static File fileHorizontal07; + private static File horizontalFillTemplate07; + private static File fileHorizontal03; + private static File horizontalFillTemplate03; + private static File byName07; + private static File byName03; + private static File byNameTemplate07; + private static File byNameTemplate03; + private static File fileComposite07; + private static File compositeFillTemplate07; + private static File fileComposite03; + private static File compositeFillTemplate03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("fill07.xlsx"); + file03 = TestFileUtil.createNewFile("fill03.xls"); + fileCsv = TestFileUtil.createNewFile("fill.csv"); + simpleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "simple.xlsx"); + simpleTemplate03 = TestFileUtil.readFile("fill" + File.separator + "simple.xls"); + simpleTemplateCsv = TestFileUtil.readFile("fill" + File.separator + "simple.csv"); + fileComplex07 = TestFileUtil.createNewFile("fillComplex07.xlsx"); + complexFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "complex.xlsx"); + fileComplex03 = TestFileUtil.createNewFile("fillComplex03.xls"); + complexFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "complex.xls"); + fileHorizontal07 = TestFileUtil.createNewFile("fillHorizontal07.xlsx"); + horizontalFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xlsx"); + fileHorizontal03 = TestFileUtil.createNewFile("fillHorizontal03.xls"); + horizontalFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "horizontal.xls"); + byName07 = TestFileUtil.createNewFile("byName07.xlsx"); + byNameTemplate07 = TestFileUtil.readFile("fill" + File.separator + "byName.xlsx"); + byName03 = TestFileUtil.createNewFile("byName03.xls"); + byNameTemplate03 = TestFileUtil.readFile("fill" + File.separator + "byName.xls"); + fileComposite07 = TestFileUtil.createNewFile("fileComposite07.xlsx"); + compositeFillTemplate07 = TestFileUtil.readFile("fill" + File.separator + "composite.xlsx"); + fileComposite03 = TestFileUtil.createNewFile("fileComposite03.xls"); + compositeFillTemplate03 = TestFileUtil.readFile("fill" + File.separator + "composite.xls"); + } + + @Test + public void t01Fill07() { + fill(file07, simpleTemplate07); + } + + @Test + public void t02Fill03() { + fill(file03, simpleTemplate03); + } + + @Test + public void t03FillCsv() { + ExcelGenerateException excelGenerateException = Assertions.assertThrows(ExcelGenerateException.class, + () -> fill(fileCsv, simpleTemplateCsv)); + Assertions.assertEquals("csv cannot use template.", excelGenerateException.getMessage()); + } + + @Test + public void t03ComplexFill07() { + complexFill(fileComplex07, complexFillTemplate07); + } + + @Test + public void t04ComplexFill03() { + complexFill(fileComplex03, complexFillTemplate03); + } + + @Test + public void t05HorizontalFill07() { + horizontalFill(fileHorizontal07, horizontalFillTemplate07); + } + + @Test + public void t06HorizontalFill03() { + horizontalFill(fileHorizontal03, horizontalFillTemplate03); + } + + @Test + public void t07ByNameFill07() { + byNameFill(byName07, byNameTemplate07); + } + + @Test + public void t08ByNameFill03() { + byNameFill(byName03, byNameTemplate03); + } + + @Test + public void t09CompositeFill07() { + compositeFill(fileComposite07, compositeFillTemplate07); + } + + @Test + public void t10CompositeFill03() { + compositeFill(fileComposite03, compositeFillTemplate03); + } + + private void byNameFill(File file, File template) { + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(file, FillData.class).withTemplate(template).sheet("Sheet2").doFill(fillData); + } + + private void compositeFill(File file, File template) { + try (ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + } + + List list = EasyExcel.read(file).ignoreEmptyRow(false).sheet().headRowNumber(0).doReadSync(); + Map map0 = (Map)list.get(0); + Assertions.assertEquals("张三", map0.get(21)); + Map map27 = (Map)list.get(27); + Assertions.assertEquals("张三", map27.get(0)); + Map map29 = (Map)list.get(29); + Assertions.assertEquals("张三", map29.get(3)); + } + + private void horizontalFill(File file, File template) { + try (ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } + + List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); + Assertions.assertEquals(list.size(), 5L); + Map map0 = (Map)list.get(0); + Assertions.assertEquals("张三", map0.get(2)); + } + + private void complexFill(File file, File template) { + try (ExcelWriter excelWriter = EasyExcel.write(file).withTemplate(template).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().registerWriteHandler(new LoopMergeStrategy(2, 0)).build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + } + List list = EasyExcel.read(file).sheet().headRowNumber(3).doReadSync(); + Assertions.assertEquals(list.size(), 21L); + Map map19 = (Map)list.get(19); + Assertions.assertEquals("张三", map19.get(0)); + } + + private void fill(File file, File template) { + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(file, FillData.class).withTemplate(template).sheet().doFill(fillData); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + if (i == 5) { + fillData.setName(null); + } + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationData.java new file mode 100644 index 000000000..87e2bfae1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationData.java @@ -0,0 +1,39 @@ +package com.alibaba.easyexcel.test.core.fill.annotation; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.converters.string.StringImageConverter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(100) +public class FillAnnotationData { + @ExcelProperty("日期") + @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") + private Date date; + + @ExcelProperty(value = "数字") + @NumberFormat("#.##%") + private Double number; + + @ContentLoopMerge(columnExtend = 2) + @ExcelProperty("字符串1") + private String string1; + @ExcelProperty("字符串2") + private String string2; + @ExcelProperty(value = "图片", converter = StringImageConverter.class) + private String image; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationDataTest.java new file mode 100644 index 000000000..2e8275b58 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/annotation/FillAnnotationDataTest.java @@ -0,0 +1,122 @@ +package com.alibaba.easyexcel.test.core.fill.annotation; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.DateUtils; + +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFPicture; +import org.apache.poi.hssf.usermodel.HSSFShape; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class FillAnnotationDataTest { + + private static File file07; + private static File file03; + private static File fileTemplate07; + private static File fileTemplate03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("fillAnnotation07.xlsx"); + file03 = TestFileUtil.createNewFile("fillAnnotation03.xls"); + fileTemplate07 = TestFileUtil.readFile("fill" + File.separator + "annotation.xlsx"); + fileTemplate03 = TestFileUtil.readFile("fill" + File.separator + "annotation.xls"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07, fileTemplate07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03, fileTemplate03); + } + + private void readAndWrite(File file, File fileTemplate) throws Exception { + EasyExcel.write().file(file).head(FillAnnotationData.class).withTemplate(fileTemplate).sheet().doFill(data()); + + try (Workbook workbook = WorkbookFactory.create(file)) { + Sheet sheet = workbook.getSheetAt(0); + + Row row1 = sheet.getRow(1); + Assertions.assertEquals(2000, row1.getHeight(), 0); + Cell cell10 = row1.getCell(0); + Date date = cell10.getDateCellValue(); + Assertions.assertEquals(DateUtils.parseDate("2020-01-01 01:01:01").getTime(), date.getTime()); + String dataFormatString = cell10.getCellStyle().getDataFormatString(); + Assertions.assertEquals("yyyy年MM月dd日HH时mm分ss秒", dataFormatString); + Cell cell11 = row1.getCell(1); + Assertions.assertEquals(99.99, cell11.getNumericCellValue(), 2); + boolean hasMerge = false; + for (CellRangeAddress mergedRegion : sheet.getMergedRegions()) { + if (mergedRegion.getFirstRow() == 1 && mergedRegion.getLastRow() == 1 + && mergedRegion.getFirstColumn() == 2 && mergedRegion.getLastColumn() == 3) { + hasMerge = true; + break; + } + } + Assertions.assertTrue(hasMerge); + if (sheet instanceof XSSFSheet) { + XSSFSheet xssfSheet = (XSSFSheet)sheet; + List shapeList = xssfSheet.getDrawingPatriarch().getShapes(); + XSSFShape shape0 = shapeList.get(0); + Assertions.assertTrue(shape0 instanceof XSSFPicture); + XSSFPicture picture0 = (XSSFPicture)shape0; + CTMarker ctMarker0 = picture0.getPreferredSize().getFrom(); + Assertions.assertEquals(1, ctMarker0.getRow()); + Assertions.assertEquals(4, ctMarker0.getCol()); + } else { + HSSFSheet hssfSheet = (HSSFSheet)sheet; + List shapeList = hssfSheet.getDrawingPatriarch().getChildren(); + HSSFShape shape0 = shapeList.get(0); + Assertions.assertTrue(shape0 instanceof HSSFPicture); + HSSFPicture picture0 = (HSSFPicture)shape0; + HSSFClientAnchor anchor = (HSSFClientAnchor)picture0.getAnchor(); + Assertions.assertEquals(1, anchor.getRow1()); + Assertions.assertEquals(4, anchor.getCol1()); + } + } + } + + private List data() throws Exception { + List list = new ArrayList<>(); + FillAnnotationData data = new FillAnnotationData(); + data.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); + data.setNumber(99.99); + data.setString1("string1"); + data.setString2("string2"); + data.setImage(TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"); + list.add(data); + list.add(data); + list.add(data); + list.add(data); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleAnnotatedData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleAnnotatedData.java new file mode 100644 index 000000000..c568e0275 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleAnnotatedData.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.core.fill.style; + +import java.util.Date; + +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.enums.BooleanEnum; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FillStyleAnnotatedData { + @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 13) + @ContentFontStyle(bold = BooleanEnum.TRUE, color = 19) + private String name; + @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10) + @ContentFontStyle(bold = BooleanEnum.TRUE, color = 16) + private Double number; + @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17) + @ContentFontStyle(bold = BooleanEnum.TRUE, color = 58) + private Date date; + @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 12) + @ContentFontStyle(bold = BooleanEnum.TRUE, color = 18) + private String empty; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleAnnotatedTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleAnnotatedTest.java new file mode 100644 index 000000000..406255bc3 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleAnnotatedTest.java @@ -0,0 +1,329 @@ +package com.alibaba.easyexcel.test.core.fill.style; + +import java.io.File; +import java.io.FileInputStream; +import java.util.List; + +import com.alibaba.easyexcel.test.core.fill.FillData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; + +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class FillStyleAnnotatedTest { + + private static File FillStyleAnnotated07; + private static File FillStyleAnnotated03; + private static File fileStyleTemplate07; + private static File fileStyleTemplate03; + + @BeforeAll + public static void init() { + FillStyleAnnotated07 = TestFileUtil.createNewFile("FillStyleAnnotated07.xlsx"); + FillStyleAnnotated03 = TestFileUtil.createNewFile("FillStyleAnnotated03.xls"); + fileStyleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "style.xlsx"); + fileStyleTemplate03 = TestFileUtil.readFile("fill" + File.separator + "style.xls"); + } + + @Test + public void t01Fill07() throws Exception { + fill(FillStyleAnnotated07, fileStyleTemplate07); + XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(FillStyleAnnotated07)); + XSSFSheet sheet = workbook.getSheetAt(0); + t01Fill07check(sheet.getRow(1)); + t01Fill07check(sheet.getRow(2)); + } + + private void t01Fill07check(XSSFRow row) { + XSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFFFF00", cell0.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF808000", cell0.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell0.getCellStyle().getFont().getBold()); + + XSSFCell cell1 = row.getCell(1); + Assertions.assertEquals(5.2, cell1.getNumericCellValue(), 1); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF0000", cell1.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF800000", cell1.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell1.getCellStyle().getFont().getBold()); + + XSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("FF008000", cell2.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF003300", cell2.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell2.getCellStyle().getFont().getBold()); + + XSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5.2岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF0000", cell3.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FFEEECE1", cell3.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell3.getCellStyle().getFont().getBold()); + + XSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFC00000", cell4.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF000000", cell4.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell4.getCellStyle().getFont().getBold()); + + XSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFF79646", cell5.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF8064A2", cell5.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell5.getCellStyle().getFont().getBold()); + } + + @Test + public void t02Fill03() throws Exception { + fill(FillStyleAnnotated03, fileStyleTemplate03); + HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(FillStyleAnnotated03)); + HSSFSheet sheet = workbook.getSheetAt(0); + t02Fill03check(workbook, sheet.getRow(1)); + t02Fill03check(workbook, sheet.getRow(2)); + } + + private void t02Fill03check(HSSFWorkbook workbook, HSSFRow row) { + HSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:FFFF:0", cell0.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:8080:0", cell0.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell0.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell1 = row.getCell(1); + Assertions.assertEquals(5.2, cell1.getNumericCellValue(), 1); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:0:0", cell1.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:0:0", cell1.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell1.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("0:8080:0", cell2.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("0:3333:0", cell2.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell2.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5.2岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:0:0", cell3.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("FFFF:FFFF:9999", cell3.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell3.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:3333:0", cell4.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("3333:3333:3333", cell4.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell4.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:3333:0", cell5.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("CCCC:9999:FFFF", cell5.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell5.getCellStyle().getFont(workbook).getBold()); + } + + private void fill(File file, File template) throws Exception { + EasyExcel.write(file, FillStyleAnnotatedData.class).withTemplate(template).sheet().doFill(data()); + } + + private void t11FillStyleHandler07check(XSSFRow row) { + XSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFFFF00", cell0.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF808000", cell0.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell0.getCellStyle().getFont().getBold()); + + XSSFCell cell1 = row.getCell(1); + Assertions.assertEquals("5", cell1.getStringCellValue()); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF0000", cell1.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF800000", cell1.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell1.getCellStyle().getFont().getBold()); + + XSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("FF008000", cell2.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF003300", cell2.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell2.getCellStyle().getFont().getBold()); + + XSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FF0000FF", cell3.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF000080", cell3.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell3.getCellStyle().getFont().getBold()); + + XSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFFFF00", cell4.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF808000", cell4.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell4.getCellStyle().getFont().getBold()); + + XSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("FF008080", cell5.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF003366", cell5.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell5.getCellStyle().getFont().getBold()); + } + + private void t12FillStyleHandler03check(HSSFWorkbook workbook, HSSFRow row) { + HSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:FFFF:0", cell0.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:8080:0", cell0.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell0.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell1 = row.getCell(1); + Assertions.assertEquals("5", cell1.getStringCellValue()); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:0:0", cell1.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:0:0", cell1.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell1.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("0:8080:0", cell2.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("0:3333:0", cell2.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell2.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("0:0:FFFF", cell3.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("0:0:8080", cell3.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell3.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:FFFF:0", cell4.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:8080:0", cell4.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell4.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("0:8080:8080", cell5.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("0:3333:6666", cell5.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell5.getCellStyle().getFont(workbook).getBold()); + } + + private void fillStyleHandler(File file, File template) throws Exception { + EasyExcel.write(file, FillData.class).withTemplate(template).sheet() + .registerWriteHandler(new AbstractVerticalCellStyleStrategy() { + + @Override + protected WriteCellStyle contentCellStyle(CellWriteHandlerContext context) { + WriteCellStyle writeCellStyle = new WriteCellStyle(); + WriteFont writeFont = new WriteFont(); + writeCellStyle.setWriteFont(writeFont); + writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + writeFont.setBold(true); + if (context.getColumnIndex() == 0) { + writeCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + writeFont.setColor(IndexedColors.DARK_YELLOW.getIndex()); + } + if (context.getColumnIndex() == 1) { + writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + writeFont.setColor(IndexedColors.DARK_RED.getIndex()); + } + if (context.getColumnIndex() == 2) { + writeCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + writeFont.setColor(IndexedColors.DARK_GREEN.getIndex()); + } + if (context.getColumnIndex() == 3) { + writeCellStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex()); + writeFont.setColor(IndexedColors.DARK_BLUE.getIndex()); + } + if (context.getColumnIndex() == 4) { + writeCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + writeFont.setColor(IndexedColors.DARK_YELLOW.getIndex()); + } + if (context.getColumnIndex() == 5) { + writeCellStyle.setFillForegroundColor(IndexedColors.TEAL.getIndex()); + writeFont.setColor(IndexedColors.DARK_TEAL.getIndex()); + } + return writeCellStyle; + } + + @Override + protected WriteCellStyle headCellStyle(Head head) { + return null; + } + + }) + .doFill(data()); + } + + private List data() throws Exception { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + FillStyleAnnotatedData fillData = new FillStyleAnnotatedData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + fillData.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); + if (i == 5) { + fillData.setName(null); + } + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleData.java new file mode 100644 index 000000000..20af16a39 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleData.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.core.fill.style; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FillStyleData { + private String name; + private Double number; + private Date date; + private String empty; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleDataTest.java new file mode 100644 index 000000000..3276b039c --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/fill/style/FillStyleDataTest.java @@ -0,0 +1,350 @@ +package com.alibaba.easyexcel.test.core.fill.style; + +import java.io.File; +import java.io.FileInputStream; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; + +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class FillStyleDataTest { + + private static File fileStyle07; + private static File fileStyle03; + private static File fileStyleHandler07; + private static File fileStyleHandler03; + private static File fileStyleTemplate07; + private static File fileStyleTemplate03; + + @BeforeAll + public static void init() { + fileStyle07 = TestFileUtil.createNewFile("fileStyle07.xlsx"); + fileStyle03 = TestFileUtil.createNewFile("fileStyle03.xls"); + fileStyleHandler07 = TestFileUtil.createNewFile("fileStyleHandler07.xlsx"); + fileStyleHandler03 = TestFileUtil.createNewFile("fileStyleHandler03.xls"); + fileStyleTemplate07 = TestFileUtil.readFile("fill" + File.separator + "style.xlsx"); + fileStyleTemplate03 = TestFileUtil.readFile("fill" + File.separator + "style.xls"); + } + + @Test + public void t01Fill07() throws Exception { + fill(fileStyle07, fileStyleTemplate07); + XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(fileStyle07)); + XSSFSheet sheet = workbook.getSheetAt(0); + t01Fill07check(sheet.getRow(1)); + t01Fill07check(sheet.getRow(2)); + } + + private void t01Fill07check(XSSFRow row) { + XSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FF00B050", cell0.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF7030A0", cell0.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell0.getCellStyle().getFont().getBold()); + + XSSFCell cell1 = row.getCell(1); + Assertions.assertEquals(5.2, cell1.getNumericCellValue(), 1); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FF92D050", cell1.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF4BACC6", cell1.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell1.getCellStyle().getFont().getBold()); + + XSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("FFFFC000", cell2.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FFC0504D", cell2.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell2.getCellStyle().getFont().getBold()); + + XSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5.2岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF0000", cell3.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FFEEECE1", cell3.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell3.getCellStyle().getFont().getBold()); + + XSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFC00000", cell4.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF000000", cell4.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell4.getCellStyle().getFont().getBold()); + + XSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFF79646", cell5.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF8064A2", cell5.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell5.getCellStyle().getFont().getBold()); + } + + @Test + public void t02Fill03() throws Exception { + fill(fileStyle03, fileStyleTemplate03); + HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(fileStyle03)); + HSSFSheet sheet = workbook.getSheetAt(0); + t02Fill03check(workbook, sheet.getRow(1)); + t02Fill03check(workbook, sheet.getRow(2)); + } + + private void t02Fill03check(HSSFWorkbook workbook, HSSFRow row) { + HSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("0:8080:0", cell0.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:0:8080", cell0.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell0.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell1 = row.getCell(1); + Assertions.assertEquals(5.2, cell1.getNumericCellValue(), 1); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:CCCC:0", cell1.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("0:8080:8080", cell1.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell1.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("FFFF:CCCC:0", cell2.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:0:0", cell2.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell2.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5.2岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:0:0", cell3.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("FFFF:FFFF:9999", cell3.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell3.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:3333:0", cell4.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("3333:3333:3333", cell4.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell4.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:3333:0", cell5.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("CCCC:9999:FFFF", cell5.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell5.getCellStyle().getFont(workbook).getBold()); + } + + private void fill(File file, File template) throws Exception { + EasyExcel.write(file, FillStyleData.class).withTemplate(template).sheet().doFill(data()); + } + + @Test + public void t11FillStyleHandler07() throws Exception { + fillStyleHandler(fileStyleHandler07, fileStyleTemplate07); + XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(fileStyleHandler07)); + XSSFSheet sheet = workbook.getSheetAt(0); + t11FillStyleHandler07check(sheet.getRow(1)); + t11FillStyleHandler07check(sheet.getRow(2)); + } + + private void t11FillStyleHandler07check(XSSFRow row) { + XSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFFFF00", cell0.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF808000", cell0.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell0.getCellStyle().getFont().getBold()); + + XSSFCell cell1 = row.getCell(1); + Assertions.assertEquals(5.2, cell1.getNumericCellValue(), 1); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF0000", cell1.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF800000", cell1.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell1.getCellStyle().getFont().getBold()); + + XSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("FF008000", cell2.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF003300", cell2.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell2.getCellStyle().getFont().getBold()); + + XSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5.2岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF0000", cell3.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FFEEECE1", cell3.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertTrue(cell3.getCellStyle().getFont().getBold()); + + XSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFC00000", cell4.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF000000", cell4.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell4.getCellStyle().getFont().getBold()); + + XSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFF79646", cell5.getCellStyle().getFillForegroundColorColor().getARGBHex()); + Assertions.assertEquals("FF8064A2", cell5.getCellStyle().getFont().getXSSFColor().getARGBHex()); + Assertions.assertFalse(cell5.getCellStyle().getFont().getBold()); + } + + @Test + public void t12FillStyleHandler03() throws Exception { + fillStyleHandler(fileStyleHandler03, fileStyleTemplate03); + HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(fileStyleHandler03)); + HSSFSheet sheet = workbook.getSheetAt(0); + t12FillStyleHandler03check(workbook, sheet.getRow(1)); + t12FillStyleHandler03check(workbook, sheet.getRow(2)); + } + + private void t12FillStyleHandler03check(HSSFWorkbook workbook, HSSFRow row) { + HSSFCell cell0 = row.getCell(0); + Assertions.assertEquals("张三", cell0.getStringCellValue()); + Assertions.assertEquals(49, cell0.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:FFFF:0", cell0.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:8080:0", cell0.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell0.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell1 = row.getCell(1); + Assertions.assertEquals(5.2, cell1.getNumericCellValue(), 1); + Assertions.assertEquals(0, cell1.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:0:0", cell1.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("8080:0:0", cell1.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell1.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell2 = row.getCell(2); + Assertions.assertEquals("2020-01-01 01:01:01", + DateUtils.format(cell2.getDateCellValue(), "yyyy-MM-dd HH:mm:ss")); + Assertions.assertEquals("yyyy-MM-dd HH:mm:ss", cell2.getCellStyle().getDataFormatString()); + Assertions.assertEquals("0:8080:0", cell2.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("0:3333:0", cell2.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell2.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell3 = row.getCell(3); + Assertions.assertEquals("张三今年5.2岁了", cell3.getStringCellValue()); + Assertions.assertEquals(0, cell3.getCellStyle().getDataFormat()); + Assertions.assertEquals("FFFF:0:0", cell3.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("FFFF:FFFF:9999", cell3.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertTrue(cell3.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell4 = row.getCell(4); + Assertions.assertEquals("{.name}忽略,张三", cell4.getStringCellValue()); + Assertions.assertEquals(0, cell4.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:3333:0", cell4.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("3333:3333:3333", cell4.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell4.getCellStyle().getFont(workbook).getBold()); + + HSSFCell cell5 = row.getCell(5); + Assertions.assertEquals("空", cell5.getStringCellValue()); + Assertions.assertEquals(0, cell5.getCellStyle().getDataFormat()); + Assertions.assertEquals("9999:3333:0", cell5.getCellStyle().getFillForegroundColorColor().getHexString()); + Assertions.assertEquals("CCCC:9999:FFFF", cell5.getCellStyle().getFont(workbook).getHSSFColor(workbook) + .getHexString()); + Assertions.assertFalse(cell5.getCellStyle().getFont(workbook).getBold()); + } + + private void fillStyleHandler(File file, File template) throws Exception { + EasyExcel.write(file, FillStyleData.class).withTemplate(template).sheet() + .registerWriteHandler(new AbstractVerticalCellStyleStrategy() { + + @Override + protected WriteCellStyle contentCellStyle(CellWriteHandlerContext context) { + WriteCellStyle writeCellStyle = new WriteCellStyle(); + WriteFont writeFont = new WriteFont(); + writeCellStyle.setWriteFont(writeFont); + writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + writeFont.setBold(true); + if (context.getColumnIndex() == 0) { + writeCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + writeFont.setColor(IndexedColors.DARK_YELLOW.getIndex()); + } + if (context.getColumnIndex() == 1) { + writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + writeFont.setColor(IndexedColors.DARK_RED.getIndex()); + } + if (context.getColumnIndex() == 2) { + writeCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + writeFont.setColor(IndexedColors.DARK_GREEN.getIndex()); + } + if (context.getColumnIndex() == 3) { + writeCellStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex()); + writeFont.setColor(IndexedColors.DARK_BLUE.getIndex()); + } + if (context.getColumnIndex() == 4) { + writeCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + writeFont.setColor(IndexedColors.DARK_YELLOW.getIndex()); + } + if (context.getColumnIndex() == 5) { + writeCellStyle.setFillForegroundColor(IndexedColors.TEAL.getIndex()); + writeFont.setColor(IndexedColors.DARK_TEAL.getIndex()); + } + return writeCellStyle; + } + + @Override + protected WriteCellStyle headCellStyle(Head head) { + return null; + } + + }) + .doFill(data()); + } + + private List data() throws Exception { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + FillStyleData fillData = new FillStyleData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + fillData.setDate(DateUtils.parseDate("2020-01-01 01:01:01")); + if (i == 5) { + fillData.setName(null); + } + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandler.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandler.java new file mode 100644 index 000000000..ba16e61b6 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandler.java @@ -0,0 +1,277 @@ +package com.alibaba.easyexcel.test.core.handler; + +import java.util.List; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.junit.jupiter.api.Assertions; + +/** + * @author JiaJu Zhuang + **/ +public class WriteHandler implements WorkbookWriteHandler, SheetWriteHandler, RowWriteHandler, CellWriteHandler { + + private long beforeCellCreate = 0L; + private long afterCellCreate = 0L; + private long afterCellDataConverted = 0L; + private long afterCellDispose = 0L; + private long beforeRowCreate = 0L; + private long afterRowCreate = 0L; + private long afterRowDispose = 0L; + private long beforeSheetCreate = 0L; + private long afterSheetCreate = 0L; + private long beforeWorkbookCreate = 0L; + private long afterWorkbookCreate = 0L; + private long afterWorkbookDispose = 0L; + + @Override + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + if (isHead) { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + beforeCellCreate++; + } + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, + Head head, Integer relativeRowIndex, Boolean isHead) { + if (isHead) { + Assertions.assertEquals(1L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterCellCreate++; + } + } + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + WriteCellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + Assertions.assertEquals(1L, beforeCellCreate); + Assertions.assertEquals(1L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(1, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(1L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterCellDataConverted++; + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + if (isHead) { + Assertions.assertEquals(1L, beforeCellCreate); + Assertions.assertEquals(1L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterCellDispose++; + } + } + + @Override + public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, + Integer relativeRowIndex, Boolean isHead) { + if (isHead) { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(0L, beforeRowCreate); + Assertions.assertEquals(0L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + beforeRowCreate++; + } + + } + + @Override + public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + if (isHead) { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(0L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterRowCreate++; + } + } + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + if (isHead) { + Assertions.assertEquals(1L, beforeCellCreate); + Assertions.assertEquals(1L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(1L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterRowDispose++; + } + } + + @Override + public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(0L, beforeRowCreate); + Assertions.assertEquals(0L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(0L, beforeSheetCreate); + Assertions.assertEquals(0L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + beforeSheetCreate++; + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(0L, beforeRowCreate); + Assertions.assertEquals(0L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(0L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterSheetCreate++; + } + + @Override + public void beforeWorkbookCreate() { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(0L, beforeRowCreate); + Assertions.assertEquals(0L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(0L, beforeSheetCreate); + Assertions.assertEquals(0L, afterSheetCreate); + Assertions.assertEquals(0L, beforeWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + beforeWorkbookCreate++; + } + + @Override + public void afterWorkbookCreate(WriteWorkbookHolder writeWorkbookHolder) { + Assertions.assertEquals(0L, beforeCellCreate); + Assertions.assertEquals(0L, afterCellCreate); + Assertions.assertEquals(0L, afterCellDataConverted); + Assertions.assertEquals(0L, afterCellDispose); + Assertions.assertEquals(0L, beforeRowCreate); + Assertions.assertEquals(0L, afterRowCreate); + Assertions.assertEquals(0L, afterRowDispose); + Assertions.assertEquals(0L, beforeSheetCreate); + Assertions.assertEquals(0L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterWorkbookCreate++; + } + + @Override + public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { + Assertions.assertEquals(1L, beforeCellCreate); + Assertions.assertEquals(1L, afterCellCreate); + Assertions.assertEquals(1L, afterCellDataConverted); + Assertions.assertEquals(1L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(1L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(0L, afterWorkbookDispose); + afterWorkbookDispose++; + + } + + public void afterAll() { + Assertions.assertEquals(1L, beforeCellCreate); + Assertions.assertEquals(1L, afterCellCreate); + Assertions.assertEquals(1L, afterCellDataConverted); + Assertions.assertEquals(1L, afterCellDispose); + Assertions.assertEquals(1L, beforeRowCreate); + Assertions.assertEquals(1L, afterRowCreate); + Assertions.assertEquals(1L, afterRowDispose); + Assertions.assertEquals(1L, beforeSheetCreate); + Assertions.assertEquals(1L, afterSheetCreate); + Assertions.assertEquals(1L, beforeWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookCreate); + Assertions.assertEquals(1L, afterWorkbookDispose); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandlerData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandlerData.java new file mode 100644 index 000000000..ec81e596e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandlerData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.handler; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteHandlerData { + @ExcelProperty("姓名") + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandlerTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandlerTest.java new file mode 100644 index 000000000..3988e8a07 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/handler/WriteHandlerTest.java @@ -0,0 +1,105 @@ +package com.alibaba.easyexcel.test.core.handler; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class WriteHandlerTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("writeHandler07.xlsx"); + file03 = TestFileUtil.createNewFile("writeHandler03.xls"); + fileCsv = TestFileUtil.createNewFile("writeHandlerCsv.csv"); + } + + @Test + public void t01WorkbookWrite07() throws Exception { + workbookWrite(file07); + } + + @Test + public void t02WorkbookWrite03() throws Exception { + workbookWrite(file03); + } + + @Test + public void t03WorkbookWriteCsv() throws Exception { + workbookWrite(fileCsv); + } + + @Test + public void t11SheetWrite07() throws Exception { + sheetWrite(file07); + } + + @Test + public void t12SheetWrite03() throws Exception { + sheetWrite(file03); + } + + @Test + public void t13SheetWriteCsv() throws Exception { + sheetWrite(fileCsv); + } + + @Test + public void t21TableWrite07() throws Exception { + tableWrite(file07); + } + + @Test + public void t22TableWrite03() throws Exception { + tableWrite(file03); + } + + @Test + public void t23TableWriteCsv() throws Exception { + tableWrite(fileCsv); + } + + private void workbookWrite(File file) { + WriteHandler writeHandler = new WriteHandler(); + EasyExcel.write(file).head(WriteHandlerData.class).registerWriteHandler(writeHandler).sheet().doWrite(data()); + writeHandler.afterAll(); + } + + private void sheetWrite(File file) { + WriteHandler writeHandler = new WriteHandler(); + EasyExcel.write(file).head(WriteHandlerData.class).sheet().registerWriteHandler(writeHandler).doWrite(data()); + writeHandler.afterAll(); + } + + private void tableWrite(File file) { + WriteHandler writeHandler = new WriteHandler(); + EasyExcel.write(file).head(WriteHandlerData.class).sheet().table(0).registerWriteHandler(writeHandler) + .doWrite(data()); + writeHandler.afterAll(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 1; i++) { + WriteHandlerData data = new WriteHandlerData(); + data.setName("姓名" + i); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexDataListener.java new file mode 100644 index 000000000..1b1279625 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexDataListener.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.core.head; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ComplexDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ComplexHeadData.class); + List list = new ArrayList(); + + @Override + public void invoke(ComplexHeadData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + ComplexHeadData data = list.get(0); + Assertions.assertEquals(data.getString4(), "字符串4"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadData.java new file mode 100644 index 000000000..38eb2b8a9 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadData.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.core.head; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ComplexHeadData { + @ExcelProperty({"顶格", "顶格", "两格"}) + private String string0; + @ExcelProperty({"顶格", "顶格", "两格"}) + private String string1; + @ExcelProperty({"顶格", "四联", "四联"}) + private String string2; + @ExcelProperty({"顶格", "四联", "四联"}) + private String string3; + @ExcelProperty({"顶格"}) + private String string4; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java new file mode 100644 index 000000000..a6e2ada29 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ComplexHeadDataTest.java @@ -0,0 +1,90 @@ +package com.alibaba.easyexcel.test.core.head; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ComplexHeadDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File file07AutomaticMergeHead; + private static File file03AutomaticMergeHead; + private static File fileCsvAutomaticMergeHead; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("complexHead07.xlsx"); + file03 = TestFileUtil.createNewFile("complexHead03.xls"); + fileCsv = TestFileUtil.createNewFile("complexHeadCsv.csv"); + file07AutomaticMergeHead = TestFileUtil.createNewFile("complexHeadAutomaticMergeHead07.xlsx"); + file03AutomaticMergeHead = TestFileUtil.createNewFile("complexHeadAutomaticMergeHead03.xls"); + fileCsvAutomaticMergeHead = TestFileUtil.createNewFile("complexHeadAutomaticMergeHeadCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, ComplexHeadData.class).sheet().doWrite(data()); + EasyExcel.read(file, ComplexHeadData.class, new ComplexDataListener()) + .xlsxSAXParserFactoryName("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl").sheet().doRead(); + } + + @Test + public void t11ReadAndWriteAutomaticMergeHead07() { + readAndWriteAutomaticMergeHead(file07AutomaticMergeHead); + } + + @Test + public void t12ReadAndWriteAutomaticMergeHead03() { + readAndWriteAutomaticMergeHead(file03AutomaticMergeHead); + } + + @Test + public void t13ReadAndWriteAutomaticMergeHeadCsv() { + readAndWriteAutomaticMergeHead(fileCsvAutomaticMergeHead); + } + + private void readAndWriteAutomaticMergeHead(File file) { + EasyExcel.write(file, ComplexHeadData.class).automaticMergeHead(Boolean.FALSE).sheet().doWrite(data()); + EasyExcel.read(file, ComplexHeadData.class, new ComplexDataListener()).sheet().doRead(); + } + + private List data() { + List list = new ArrayList(); + ComplexHeadData data = new ComplexHeadData(); + data.setString0("字符串0"); + data.setString1("字符串1"); + data.setString2("字符串2"); + data.setString3("字符串3"); + data.setString4("字符串4"); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java new file mode 100644 index 000000000..2cc93a2d2 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataListener.java @@ -0,0 +1,48 @@ +package com.alibaba.easyexcel.test.core.head; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ListHeadDataListener implements ReadListener> { + + private static final Logger LOGGER = LoggerFactory.getLogger(NoHeadData.class); + List> list = new ArrayList>(); + + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + Assertions.assertNotNull(context.readRowHolder().getRowIndex()); + headMap.forEach((key, value) -> { + Assertions.assertEquals(value.getRowIndex(), context.readRowHolder().getRowIndex()); + Assertions.assertEquals(value.getColumnIndex(), key); + }); + } + + @Override + public void invoke(Map data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + Map data = list.get(0); + Assertions.assertEquals("字符串0", data.get(0)); + Assertions.assertEquals("1", data.get(1)); + Assertions.assertEquals("2020-01-01 01:01:01", data.get(2)); + Assertions.assertEquals("额外数据", data.get(3)); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataTest.java new file mode 100644 index 000000000..c1dab546b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/ListHeadDataTest.java @@ -0,0 +1,78 @@ +package com.alibaba.easyexcel.test.core.head; + +import java.io.File; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.DateUtils; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ListHeadDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("listHead07.xlsx"); + file03 = TestFileUtil.createNewFile("listHead03.xls"); + fileCsv = TestFileUtil.createNewFile("listHeadCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) throws Exception { + EasyExcel.write(file).head(head()).sheet().doWrite(data()); + EasyExcel.read(file).registerReadListener(new ListHeadDataListener()).sheet().doRead(); + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("字符串"); + List head1 = new ArrayList(); + head1.add("数字"); + List head2 = new ArrayList(); + head2.add("日期"); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } + + private List> data() throws ParseException { + List> list = new ArrayList>(); + List data0 = new ArrayList(); + data0.add("字符串0"); + data0.add(1); + data0.add(DateUtils.parseDate("2020-01-01 01:01:01")); + data0.add("额外数据"); + list.add(data0); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadData.java new file mode 100644 index 000000000..a45bcf9c1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.head; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class NoHeadData { + @ExcelProperty("字符串") + private String string; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadDataListener.java new file mode 100644 index 000000000..3e5df2094 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadDataListener.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.core.head; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class NoHeadDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(NoHeadData.class); + List list = new ArrayList(); + + @Override + public void invoke(NoHeadData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + NoHeadData data = list.get(0); + Assertions.assertEquals(data.getString(), "字符串0"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadDataTest.java new file mode 100644 index 000000000..a7568602e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/head/NoHeadDataTest.java @@ -0,0 +1,59 @@ +package com.alibaba.easyexcel.test.core.head; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class NoHeadDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("noHead07.xlsx"); + file03 = TestFileUtil.createNewFile("noHead03.xls"); + fileCsv = TestFileUtil.createNewFile("noHeadCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, NoHeadData.class).needHead(Boolean.FALSE).sheet().doWrite(data()); + EasyExcel.read(file, NoHeadData.class, new NoHeadDataListener()).headRowNumber(0).sheet().doRead(); + } + + private List data() { + List list = new ArrayList(); + NoHeadData data = new NoHeadData(); + data.setString("字符串0"); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeData.java new file mode 100644 index 000000000..1bd5a7481 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeData.java @@ -0,0 +1,64 @@ +package com.alibaba.easyexcel.test.core.large; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class LargeData { + + private String str1; + + private String str2; + + private String str3; + + private String str4; + + private String str5; + + private String str6; + + private String str7; + + private String str8; + + private String str9; + + private String str10; + + private String str11; + + private String str12; + + private String str13; + + private String str14; + + private String str15; + + private String str16; + + private String str17; + + private String str18; + + private String str19; + + private String str20; + + private String str21; + + private String str22; + + private String str23; + + private String str24; + + private String str25; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataListener.java new file mode 100644 index 000000000..7736de304 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataListener.java @@ -0,0 +1,39 @@ +package com.alibaba.easyexcel.test.core.large; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class LargeDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(LargeDataListener.class); + private int count = 0; + + @Override + public void invoke(LargeData data, AnalysisContext context) { + if (count == 0) { + LOGGER.info("First row:{}", JSON.toJSONString(data)); + } + count++; + if (count % 100000 == 0) { + LOGGER.info("Already read:{},{}", count, JSON.toJSONString(data)); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.info("Large row count:{}", count); + if (context.readWorkbookHolder().getExcelType() != ExcelTypeEnum.CSV) { + Assertions.assertEquals(count, 464509); + } else { + Assertions.assertEquals(count, 499999); + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java new file mode 100644 index 000000000..2eecca2a8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/large/LargeDataTest.java @@ -0,0 +1,165 @@ +package com.alibaba.easyexcel.test.core.large; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; + +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class LargeDataTest { + private static final Logger LOGGER = LoggerFactory.getLogger(LargeDataTest.class); + private static File fileFill07; + private static File template07; + private static File fileCsv; + private static File fileWrite07; + private static File fileWriteTemp07; + private static File fileWritePoi07; + + private int i = 0; + + @BeforeAll + public static void init() { + fileFill07 = TestFileUtil.createNewFile("largefill07.xlsx"); + fileWrite07 = TestFileUtil.createNewFile("large" + File.separator + "fileWrite07.xlsx"); + fileWriteTemp07 = TestFileUtil.createNewFile("large" + File.separator + "fileWriteTemp07.xlsx"); + fileWritePoi07 = TestFileUtil.createNewFile("large" + File.separator + "fileWritePoi07.xlsx"); + template07 = TestFileUtil.readFile("large" + File.separator + "fill.xlsx"); + fileCsv = TestFileUtil.createNewFile("largefileCsv.csv"); + } + + @Test + public void t01Read() throws Exception { + long start = System.currentTimeMillis(); + EasyExcel.read(TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx", LargeData.class, + new LargeDataListener()).headRowNumber(2).sheet().doRead(); + LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); + } + + @Test + public void t02Fill() { + try (ExcelWriter excelWriter = EasyExcel.write(fileFill07).withTemplate(template07).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 5000; j++) { + excelWriter.fill(data(), writeSheet); + LOGGER.info("{} fill success.", j); + } + } + } + + @Test + public void t03ReadAndWriteCsv() { + // write + long start = System.currentTimeMillis(); + try (ExcelWriter excelWriter = EasyExcel.write(fileCsv).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 5000; j++) { + excelWriter.write(data(), writeSheet); + LOGGER.info("{} write success.", j); + } + } + LOGGER.info("CSV large data total time spent:{}", System.currentTimeMillis() - start); + + // read + start = System.currentTimeMillis(); + EasyExcel.read(fileCsv, LargeData.class, new LargeDataListener()).sheet().doRead(); + LOGGER.info("CSV large data total time spent:{}", System.currentTimeMillis() - start); + } + + @Test + public void t04Write() throws Exception { + ExcelWriter excelWriter = EasyExcel.write(fileWriteTemp07, LargeData.class).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 2; j++) { + excelWriter.write(data(), writeSheet); + } + excelWriter.finish(); + + long start = System.currentTimeMillis(); + excelWriter = EasyExcel.write(fileWrite07, LargeData.class).build(); + writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 5000; j++) { + excelWriter.write(data(), writeSheet); + LOGGER.info("{} write success.", j); + } + excelWriter.finish(); + long cost = System.currentTimeMillis() - start; + LOGGER.info("write cost:{}", cost); + start = System.currentTimeMillis(); + try (FileOutputStream fileOutputStream = new FileOutputStream(fileWritePoi07)) { + SXSSFWorkbook workbook = new SXSSFWorkbook(); + SXSSFSheet sheet = workbook.createSheet("sheet1"); + for (int i = 0; i < 100 * 5000; i++) { + SXSSFRow row = sheet.createRow(i); + for (int j = 0; j < 25; j++) { + SXSSFCell cell = row.createCell(j); + cell.setCellValue("str-" + j + "-" + i); + } + if (i % 5000 == 0) { + LOGGER.info("{} write success.", i); + } + } + workbook.write(fileOutputStream); + workbook.dispose(); + workbook.close(); + } + long costPoi = System.currentTimeMillis() - start; + LOGGER.info("poi write cost:{}", System.currentTimeMillis() - start); + LOGGER.info("{} vs {}", cost, costPoi); + Assertions.assertTrue(costPoi * 2 > cost); + } + + private List data() { + List list = new ArrayList<>(); + int size = i + 100; + for (; i < size; i++) { + LargeData largeData = new LargeData(); + list.add(largeData); + largeData.setStr1("str1-" + i); + largeData.setStr2("str2-" + i); + largeData.setStr3("str3-" + i); + largeData.setStr4("str4-" + i); + largeData.setStr5("str5-" + i); + largeData.setStr6("str6-" + i); + largeData.setStr7("str7-" + i); + largeData.setStr8("str8-" + i); + largeData.setStr9("str9-" + i); + largeData.setStr10("str10-" + i); + largeData.setStr11("str11-" + i); + largeData.setStr12("str12-" + i); + largeData.setStr13("str13-" + i); + largeData.setStr14("str14-" + i); + largeData.setStr15("str15-" + i); + largeData.setStr16("str16-" + i); + largeData.setStr17("str17-" + i); + largeData.setStr18("str18-" + i); + largeData.setStr19("str19-" + i); + largeData.setStr20("str20-" + i); + largeData.setStr21("str21-" + i); + largeData.setStr22("str22-" + i); + largeData.setStr23("str23-" + i); + largeData.setStr24("str24-" + i); + largeData.setStr25("str25-" + i); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsData.java new file mode 100644 index 000000000..22b00e192 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsData.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.core.multiplesheets; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class MultipleSheetsData { + private String title; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java new file mode 100644 index 000000000..0ac276176 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsDataTest.java @@ -0,0 +1,69 @@ +package com.alibaba.easyexcel.test.core.multiplesheets; + +import java.io.File; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.read.metadata.ReadSheet; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class MultipleSheetsDataTest { + + private static File file07; + private static File file03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.readFile("multiplesheets" + File.separator + "multiplesheets.xlsx"); + file03 = TestFileUtil.readFile("multiplesheets" + File.separator + "multiplesheets.xls"); + } + + @Test + public void t01Read07() { + read(file07); + } + + @Test + public void t02Read03() { + read(file03); + } + + @Test + public void t03Read07All() { + readAll(file07); + } + + @Test + public void t04Read03All() { + readAll(file03); + } + + private void read(File file) { + MultipleSheetsListener multipleSheetsListener = new MultipleSheetsListener(); + try (ExcelReader excelReader = EasyExcel.read(file, MultipleSheetsData.class, multipleSheetsListener).build()) { + List sheets = excelReader.excelExecutor().sheetList(); + int count = 1; + for (ReadSheet readSheet : sheets) { + excelReader.read(readSheet); + Assertions.assertEquals(multipleSheetsListener.getList().size(), count); + count++; + } + } + } + + private void readAll(File file) { + EasyExcel.read(file, MultipleSheetsData.class, new MultipleSheetsListener()).doReadAll(); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java new file mode 100644 index 000000000..b30484aae --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/multiplesheets/MultipleSheetsListener.java @@ -0,0 +1,40 @@ +package com.alibaba.easyexcel.test.core.multiplesheets; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class MultipleSheetsListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(MultipleSheetsListener.class); + List list = new ArrayList(); + + @Override + public void invoke(MultipleSheetsData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.debug("A form is read finished."); + Assertions.assertEquals(list.get(0).getTitle(), "表1数据"); + LOGGER.debug("All row:{}", JSON.toJSONString(list)); + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java new file mode 100644 index 000000000..b14e32de3 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/nomodel/NoModelDataTest.java @@ -0,0 +1,128 @@ +package com.alibaba.easyexcel.test.core.nomodel; + +import java.io.File; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.enums.ReadDefaultReturnEnum; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.util.DateUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +@Slf4j +public class NoModelDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File fileRepeat07; + private static File fileRepeat03; + private static File fileRepeatCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("noModel07.xlsx"); + file03 = TestFileUtil.createNewFile("noModel03.xls"); + fileCsv = TestFileUtil.createNewFile("noModelCsv.csv"); + fileRepeat07 = TestFileUtil.createNewFile("noModelRepeat07.xlsx"); + fileRepeat03 = TestFileUtil.createNewFile("noModelRepeat03.xls"); + fileRepeatCsv = TestFileUtil.createNewFile("noModelRepeatCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07, fileRepeat07, false); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03, fileRepeat03, false); + } + + @Test + public void t03ReadAndWriteCsv() throws Exception { + readAndWrite(fileCsv, fileRepeatCsv, true); + } + + private void readAndWrite(File file, File fileRepeat, boolean isCsv) throws Exception { + EasyExcel.write(file).sheet().doWrite(data()); + List> result = EasyExcel.read(file).headRowNumber(0).sheet().doReadSync(); + Assertions.assertEquals(10, result.size()); + Map data10 = result.get(9); + Assertions.assertEquals("string19", data10.get(0)); + Assertions.assertEquals("109", data10.get(1)); + Assertions.assertEquals("2020-01-01 01:01:01", data10.get(2)); + + List> actualDataList = EasyExcel.read(file) + .headRowNumber(0) + .readDefaultReturn(ReadDefaultReturnEnum.ACTUAL_DATA) + .sheet() + .doReadSync(); + log.info("actualDataList:{}", JSON.toJSONString(actualDataList)); + Assertions.assertEquals(10, actualDataList.size()); + Map actualData10 = actualDataList.get(9); + Assertions.assertEquals("string19", actualData10.get(0)); + if (isCsv) { + // CSV only string type + Assertions.assertEquals("109", actualData10.get(1)); + Assertions.assertEquals("2020-01-01 01:01:01", actualData10.get(2)); + } else { + Assertions.assertEquals(0, new BigDecimal("109").compareTo((BigDecimal)actualData10.get(1))); + Assertions.assertEquals(LocalDateTime.of(2020, 1, 1, 1, 1, 1), actualData10.get(2)); + } + + List>> readCellDataList = EasyExcel.read(file) + .headRowNumber(0) + .readDefaultReturn(ReadDefaultReturnEnum.READ_CELL_DATA) + .sheet() + .doReadSync(); + log.info("readCellDataList:{}", JSON.toJSONString(readCellDataList)); + Assertions.assertEquals(10, readCellDataList.size()); + Map> readCellData10 = readCellDataList.get(9); + Assertions.assertEquals("string19", readCellData10.get(0).getData()); + if (isCsv) { + // CSV only string type + Assertions.assertEquals("109", readCellData10.get(1).getData()); + Assertions.assertEquals("2020-01-01 01:01:01", readCellData10.get(2).getData()); + } else { + Assertions.assertEquals(0, new BigDecimal("109").compareTo((BigDecimal)readCellData10.get(1).getData())); + Assertions.assertEquals(LocalDateTime.of(2020, 1, 1, 1, 1, 1), readCellData10.get(2).getData()); + } + + EasyExcel.write(fileRepeat).sheet().doWrite(result); + result = EasyExcel.read(fileRepeat).headRowNumber(0).sheet().doReadSync(); + Assertions.assertEquals(10, result.size()); + data10 = result.get(9); + Assertions.assertEquals("string19", data10.get(0)); + Assertions.assertEquals("109", data10.get(1)); + Assertions.assertEquals("2020-01-01 01:01:01", data10.get(2)); + } + + private List> data() throws Exception { + List> list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + List data = new ArrayList<>(); + data.add("string1" + i); + data.add(100 + i); + data.add(DateUtils.parseDate("2020-01-01 01:01:01")); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelData.java new file mode 100644 index 000000000..286ef8fe2 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelData.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.core.noncamel; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class UnCamelData { + private String string1; + private String String2; + private String sTring3; + private String STring4; + private String STRING5; + private String STRing6; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelDataListener.java new file mode 100644 index 000000000..f9dd43e64 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelDataListener.java @@ -0,0 +1,50 @@ +package com.alibaba.easyexcel.test.core.noncamel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; + +/** + * @author Jiaju Zhuang + */ +@Slf4j +public class UnCamelDataListener extends AnalysisEventListener { + List list = new ArrayList<>(); + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + log.debug("Head is:{}", JSON.toJSONString(headMap)); + Assertions.assertEquals(headMap.get(0), "string1"); + Assertions.assertEquals(headMap.get(1), "string2"); + Assertions.assertEquals(headMap.get(2), "STring3"); + Assertions.assertEquals(headMap.get(3), "STring4"); + Assertions.assertEquals(headMap.get(4), "STRING5"); + Assertions.assertEquals(headMap.get(5), "STRing6"); + + } + + @Override + public void invoke(UnCamelData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 10); + UnCamelData unCamelData = list.get(0); + Assertions.assertEquals(unCamelData.getString1(), "string1"); + Assertions.assertEquals(unCamelData.getString2(), "string2"); + Assertions.assertEquals(unCamelData.getSTring3(), "string3"); + Assertions.assertEquals(unCamelData.getSTring4(), "string4"); + Assertions.assertEquals(unCamelData.getSTRING5(), "string5"); + Assertions.assertEquals(unCamelData.getSTRing6(), "string6"); + log.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelDataTest.java new file mode 100644 index 000000000..f60e3636b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/noncamel/UnCamelDataTest.java @@ -0,0 +1,66 @@ +package com.alibaba.easyexcel.test.core.noncamel; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class UnCamelDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("unCame07.xlsx"); + file03 = TestFileUtil.createNewFile("unCame03.xls"); + fileCsv = TestFileUtil.createNewFile("unCameCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, UnCamelData.class).sheet().doWrite(data()); + EasyExcel.read(file, UnCamelData.class, new UnCamelDataListener()).sheet().doRead(); + } + + private List data() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + UnCamelData unCamelData = new UnCamelData(); + unCamelData.setString1("string1"); + unCamelData.setString2("string2"); + unCamelData.setSTring3("string3"); + unCamelData.setSTring4("string4"); + unCamelData.setSTRING5("string5"); + unCamelData.setSTRing6("string6"); + list.add(unCamelData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterData.java new file mode 100644 index 000000000..fedabaa0f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.parameter; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class ParameterData { + @ExcelProperty("姓名") + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterDataListener.java new file mode 100644 index 000000000..7a3a9cb97 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterDataListener.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.core.parameter; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class ParameterDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(ParameterDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(ParameterData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 10); + Assertions.assertEquals(list.get(0).getName(), "姓名0"); + Assertions.assertEquals((int)(context.readSheetHolder().getSheetNo()), 0); + Assertions.assertEquals( + context.readSheetHolder().getExcelReadHeadProperty().getHeadMap().get(0).getHeadNameList().get(0), "姓名"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterDataTest.java new file mode 100644 index 000000000..ee5e294be --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/parameter/ParameterDataTest.java @@ -0,0 +1,146 @@ +package com.alibaba.easyexcel.test.core.parameter; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.cache.MapCache; +import com.alibaba.excel.converters.string.StringStringConverter; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class ParameterDataTest { + + private static File file07; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("parameter07.xlsx"); + fileCsv = TestFileUtil.createNewFile("parameterCsv.csv"); + } + + @Test + public void t01ReadAndWrite() throws Exception { + readAndWrite1(file07, ExcelTypeEnum.XLSX); + readAndWrite2(file07, ExcelTypeEnum.XLSX); + readAndWrite3(file07, ExcelTypeEnum.XLSX); + readAndWrite4(file07, ExcelTypeEnum.XLSX); + readAndWrite5(file07, ExcelTypeEnum.XLSX); + readAndWrite6(file07, ExcelTypeEnum.XLSX); + readAndWrite7(file07, ExcelTypeEnum.XLSX); + } + + @Test + public void t02ReadAndWrite() throws Exception { + readAndWrite1(fileCsv, ExcelTypeEnum.CSV); + readAndWrite2(fileCsv, ExcelTypeEnum.CSV); + readAndWrite3(fileCsv, ExcelTypeEnum.CSV); + readAndWrite4(fileCsv, ExcelTypeEnum.CSV); + readAndWrite5(fileCsv, ExcelTypeEnum.CSV); + readAndWrite6(fileCsv, ExcelTypeEnum.CSV); + readAndWrite7(fileCsv, ExcelTypeEnum.CSV); + } + + private void readAndWrite1(File file, ExcelTypeEnum type) { + EasyExcel.write(file.getPath()).head(ParameterData.class).sheet().doWrite(data()); + EasyExcel.read(file.getPath()).head(ParameterData.class).registerReadListener(new ParameterDataListener()) + .sheet().doRead(); + } + + private void readAndWrite2(File file, ExcelTypeEnum type) { + EasyExcel.write(file.getPath(), ParameterData.class).sheet().doWrite(data()); + EasyExcel.read(file.getPath(), ParameterData.class, new ParameterDataListener()).sheet().doRead(); + } + + private void readAndWrite3(File file, ExcelTypeEnum type) throws Exception { + EasyExcel.write(new FileOutputStream(file)).excelType(type).head(ParameterData.class).sheet() + .doWrite(data()); + EasyExcel.read(file.getPath()).head(ParameterData.class).registerReadListener(new ParameterDataListener()) + .sheet().doRead(); + } + + private void readAndWrite4(File file, ExcelTypeEnum type) throws Exception { + EasyExcel.write(new FileOutputStream(file), ParameterData.class).excelType(type).sheet().doWrite(data()); + EasyExcel.read(file.getPath(), new ParameterDataListener()).head(ParameterData.class).sheet().doRead(); + } + + private void readAndWrite5(File file, ExcelTypeEnum type) throws Exception { + ExcelWriter excelWriter = + EasyExcel.write(new FileOutputStream(file)).excelType(type).head(ParameterData.class).relativeHeadRowIndex( + 0).build(); + WriteSheet writeSheet = EasyExcel.writerSheet(0).relativeHeadRowIndex(0).needHead(Boolean.FALSE).build(); + WriteTable writeTable = EasyExcel.writerTable(0).relativeHeadRowIndex(0).needHead(Boolean.TRUE).build(); + excelWriter.write(data(), writeSheet, writeTable); + excelWriter.finish(); + + ExcelReader excelReader = EasyExcel.read(file.getPath(), new ParameterDataListener()).head(ParameterData.class) + .mandatoryUseInputStream(Boolean.FALSE).autoCloseStream(Boolean.TRUE).readCache(new MapCache()).build(); + ReadSheet readSheet = EasyExcel.readSheet().head(ParameterData.class).use1904windowing(Boolean.FALSE) + .headRowNumber(1).sheetNo(0).sheetName("0").build(); + excelReader.read(readSheet); + excelReader.finish(); + + excelReader = EasyExcel.read(file.getPath(), new ParameterDataListener()).head(ParameterData.class) + .mandatoryUseInputStream(Boolean.FALSE).autoCloseStream(Boolean.TRUE).readCache(new MapCache()).build(); + excelReader.read(); + excelReader.finish(); + } + + private void readAndWrite6(File file, ExcelTypeEnum type) throws Exception { + ExcelWriter excelWriter = + EasyExcel.write(new FileOutputStream(file)).excelType(type).head(ParameterData.class).relativeHeadRowIndex( + 0).build(); + WriteSheet writeSheet = EasyExcel.writerSheet(0).relativeHeadRowIndex(0).needHead(Boolean.FALSE).build(); + WriteTable writeTable = EasyExcel.writerTable(0).registerConverter(new StringStringConverter()) + .relativeHeadRowIndex(0).needHead(Boolean.TRUE).build(); + excelWriter.write(data(), writeSheet, writeTable); + excelWriter.finish(); + + ExcelReader excelReader = EasyExcel.read(file.getPath(), new ParameterDataListener()).head(ParameterData.class) + .mandatoryUseInputStream(Boolean.FALSE).autoCloseStream(Boolean.TRUE).readCache(new MapCache()).build(); + ReadSheet readSheet = EasyExcel.readSheet("0").head(ParameterData.class).use1904windowing(Boolean.FALSE) + .headRowNumber(1).sheetNo(0).build(); + excelReader.read(readSheet); + excelReader.finish(); + + excelReader = EasyExcel.read(file.getPath(), new ParameterDataListener()).head(ParameterData.class) + .mandatoryUseInputStream(Boolean.FALSE).autoCloseStream(Boolean.TRUE).readCache(new MapCache()).build(); + excelReader.read(); + excelReader.finish(); + } + + private void readAndWrite7(File file, ExcelTypeEnum type) { + EasyExcel.write(file, ParameterData.class).registerConverter(new StringStringConverter()).sheet() + .registerConverter(new StringStringConverter()).needHead(Boolean.FALSE).table(0).needHead(Boolean.TRUE) + .doWrite(data()); + EasyExcel.read(file.getPath()).head(ParameterData.class).registerReadListener(new ParameterDataListener()) + .sheet().registerConverter(new StringStringConverter()).doRead(); + } + + private List data() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + ParameterData simpleData = new ParameterData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionData.java new file mode 100644 index 000000000..e77a995ea --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.repetition; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class RepetitionData { + @ExcelProperty("字符串") + private String string; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionDataListener.java new file mode 100644 index 000000000..d4a57bcbf --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionDataListener.java @@ -0,0 +1,34 @@ +package com.alibaba.easyexcel.test.core.repetition; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class RepetitionDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(RepetitionData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 2); + Assertions.assertEquals(list.get(0).getString(), "字符串0"); + Assertions.assertEquals(list.get(1).getString(), "字符串0"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionDataTest.java new file mode 100644 index 000000000..aebf77f1e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/repetition/RepetitionDataTest.java @@ -0,0 +1,105 @@ +package com.alibaba.easyexcel.test.core.repetition; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class RepetitionDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File fileTable07; + private static File fileTable03; + private static File fileTableCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("repetition07.xlsx"); + file03 = TestFileUtil.createNewFile("repetition03.xls"); + fileCsv = TestFileUtil.createNewFile("repetitionCsv.csv"); + fileTable07 = TestFileUtil.createNewFile("repetitionTable07.xlsx"); + fileTable03 = TestFileUtil.createNewFile("repetitionTable03.xls"); + fileTableCsv = TestFileUtil.createNewFile("repetitionTableCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) { + try (ExcelWriter excelWriter = EasyExcel.write(file, RepetitionData.class).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet(0).build(); + excelWriter.write(data(), writeSheet).write(data(), writeSheet); + } + try (ExcelReader excelReader = EasyExcel.read(file, RepetitionData.class, new RepetitionDataListener()) + .build()) { + ReadSheet readSheet = EasyExcel.readSheet(0).build(); + excelReader.read(readSheet); + } + } + + @Test + public void t11ReadAndWriteTable07() { + readAndWriteTable(fileTable07); + } + + @Test + public void t12ReadAndWriteTable03() { + readAndWriteTable(fileTable03); + } + + @Test + public void t13ReadAndWriteTableCsv() { + readAndWriteTable(fileTableCsv); + } + + private void readAndWriteTable(File file) { + try (ExcelWriter excelWriter = EasyExcel.write(file, RepetitionData.class).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet(0).build(); + WriteTable writeTable = EasyExcel.writerTable(0).relativeHeadRowIndex(0).build(); + excelWriter.write(data(), writeSheet, writeTable).write(data(), writeSheet, writeTable); + } + try (ExcelReader excelReader = EasyExcel.read(file, RepetitionData.class, new RepetitionDataListener()) + .build()) { + ReadSheet readSheet = EasyExcel.readSheet(0).headRowNumber(2).build(); + excelReader.read(readSheet); + } + } + + private List data() { + List list = new ArrayList(); + RepetitionData data = new RepetitionData(); + data.setString("字符串0"); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleData.java new file mode 100644 index 000000000..0e43b3c8e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.core.simple; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class SimpleData { + @ExcelProperty("姓名") + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataListener.java new file mode 100644 index 000000000..c1213d76b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataListener.java @@ -0,0 +1,42 @@ +package com.alibaba.easyexcel.test.core.simple; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class SimpleDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDataListener.class); + List list = new ArrayList(); + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + LOGGER.debug("Head is:{}", JSON.toJSONString(headMap)); + Assertions.assertEquals(headMap.get(0), "姓名"); + } + + @Override + public void invoke(SimpleData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 10); + Assertions.assertEquals(list.get(0).getName(), "姓名0"); + Assertions.assertEquals((int)(context.readSheetHolder().getSheetNo()), 0); + Assertions.assertEquals( + context.readSheetHolder().getExcelReadHeadProperty().getHeadMap().get(0).getHeadNameList().get(0), "姓名"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataSheetNameListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataSheetNameListener.java new file mode 100644 index 000000000..09d8cbe49 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataSheetNameListener.java @@ -0,0 +1,32 @@ +package com.alibaba.easyexcel.test.core.simple; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class SimpleDataSheetNameListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDataSheetNameListener.class); + List list = new ArrayList(); + + @Override + public void invoke(SimpleData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + Assertions.assertEquals(list.get(0).getName(), "张三"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java new file mode 100644 index 000000000..6a539f75f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/simple/SimpleDataTest.java @@ -0,0 +1,130 @@ +package com.alibaba.easyexcel.test.core.simple; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.read.listener.PageReadListener; +import com.alibaba.excel.support.ExcelTypeEnum; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +@Slf4j +public class SimpleDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("simple07.xlsx"); + file03 = TestFileUtil.createNewFile("simple03.xls"); + fileCsv = TestFileUtil.createNewFile("simpleCsv.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, SimpleData.class).sheet().doWrite(data()); + EasyExcel.read(file, SimpleData.class, new SimpleDataListener()).sheet().doRead(); + } + + @Test + public void t04ReadAndWrite07() throws Exception { + readAndWriteInputStream(file07, ExcelTypeEnum.XLSX); + } + + @Test + public void t05ReadAndWrite03() throws Exception { + readAndWriteInputStream(file03, ExcelTypeEnum.XLS); + } + + @Test + public void t06ReadAndWriteCsv() throws Exception { + readAndWriteInputStream(fileCsv, ExcelTypeEnum.CSV); + } + + private void readAndWriteInputStream(File file, ExcelTypeEnum excelTypeEnum) throws Exception { + EasyExcel.write(new FileOutputStream(file), SimpleData.class).excelType(excelTypeEnum).sheet().doWrite(data()); + EasyExcel.read(new FileInputStream(file), SimpleData.class, new SimpleDataListener()).sheet().doRead(); + } + + @Test + public void t11SynchronousRead07() { + synchronousRead(file07); + } + + @Test + public void t12SynchronousRead03() { + synchronousRead(file03); + } + + @Test + public void t13SynchronousReadCsv() { + synchronousRead(fileCsv); + } + + @Test + public void t21SheetNameRead07() { + List> list = EasyExcel.read( + TestFileUtil.readFile("simple" + File.separator + "simple07.xlsx")) + .sheet("simple") + .doReadSync(); + Assertions.assertEquals(1, list.size()); + } + + @Test + public void t22PageReadListener07() { + EasyExcel.read(file07, SimpleData.class, + new PageReadListener(dataList -> { + Assertions.assertEquals(5, dataList.size()); + }, 5)) + .sheet().doRead(); + } + + private void synchronousRead(File file) { + // Synchronous read file + List list = EasyExcel.read(file).head(SimpleData.class).sheet().doReadSync(); + Assertions.assertEquals(list.size(), 10); + Assertions.assertTrue(list.get(0) instanceof SimpleData); + Assertions.assertEquals(((SimpleData)list.get(0)).getName(), "姓名0"); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + SimpleData simpleData = new SimpleData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/skip/SkipData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/skip/SkipData.java new file mode 100644 index 000000000..8e0d55ee2 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/skip/SkipData.java @@ -0,0 +1,19 @@ +package com.alibaba.easyexcel.test.core.skip; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class SkipData { + + @ExcelProperty("姓名") + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/skip/SkipDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/skip/SkipDataTest.java new file mode 100644 index 000000000..07e2415f6 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/skip/SkipDataTest.java @@ -0,0 +1,91 @@ +package com.alibaba.easyexcel.test.core.skip; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.event.SyncReadListener; +import com.alibaba.excel.exception.ExcelGenerateException; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.write.metadata.WriteSheet; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class SkipDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("skip.xlsx"); + file03 = TestFileUtil.createNewFile("skip.xls"); + fileCsv = TestFileUtil.createNewFile("skip.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + Assertions.assertThrows(ExcelGenerateException.class, () -> readAndWrite(fileCsv)); + } + + private void readAndWrite(File file) { + try (ExcelWriter excelWriter = EasyExcel.write(file, SimpleData.class).build();) { + WriteSheet writeSheet0 = EasyExcel.writerSheet(0, "第一个").build(); + WriteSheet writeSheet1 = EasyExcel.writerSheet(1, "第二个").build(); + WriteSheet writeSheet2 = EasyExcel.writerSheet(2, "第三个").build(); + WriteSheet writeSheet3 = EasyExcel.writerSheet(3, "第四个").build(); + excelWriter.write(data("name1"), writeSheet0); + excelWriter.write(data("name2"), writeSheet1); + excelWriter.write(data("name3"), writeSheet2); + excelWriter.write(data("name4"), writeSheet3); + } + + List list = EasyExcel.read(file, SkipData.class, null).sheet("第二个").doReadSync(); + Assertions.assertEquals(1, list.size()); + Assertions.assertEquals("name2", list.get(0).getName()); + + SyncReadListener syncReadListener = new SyncReadListener(); + try (ExcelReader excelReader = EasyExcel.read(file, SkipData.class, null).registerReadListener(syncReadListener) + .build()) { + ReadSheet readSheet1 = EasyExcel.readSheet("第二个").build(); + ReadSheet readSheet3 = EasyExcel.readSheet("第四个").build(); + excelReader.read(readSheet1, readSheet3); + List syncList = syncReadListener.getList(); + Assertions.assertEquals(2, syncList.size()); + Assertions.assertEquals("name2", ((SkipData)syncList.get(0)).getName()); + Assertions.assertEquals("name4", ((SkipData)syncList.get(1)).getName()); + } + } + + private List data(String name) { + List list = new ArrayList(); + SkipData data = new SkipData(); + data.setName(name); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortData.java new file mode 100644 index 000000000..668dd899e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortData.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.core.sort; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class SortData { + private String column5; + private String column6; + @ExcelProperty(order = 100) + private String column4; + @ExcelProperty(order = 99) + private String column3; + @ExcelProperty(value = "column2", index = 1) + private String column2; + @ExcelProperty(value = "column1", index = 0) + private String column1; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortDataListener.java new file mode 100644 index 000000000..dae966c1e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortDataListener.java @@ -0,0 +1,37 @@ +package com.alibaba.easyexcel.test.core.sort; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class SortDataListener extends AnalysisEventListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(SortDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(SortData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 1); + SortData sortData = list.get(0); + Assertions.assertEquals("column1", sortData.getColumn1()); + Assertions.assertEquals("column2", sortData.getColumn2()); + Assertions.assertEquals("column3", sortData.getColumn3()); + Assertions.assertEquals("column4", sortData.getColumn4()); + Assertions.assertEquals("column5", sortData.getColumn5()); + Assertions.assertEquals("column6", sortData.getColumn6()); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortDataTest.java new file mode 100644 index 000000000..74f34b9b2 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/sort/SortDataTest.java @@ -0,0 +1,123 @@ +package com.alibaba.easyexcel.test.core.sort; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class SortDataTest { + + private static File file07; + private static File file03; + private static File fileCsv; + private static File sortNoHead07; + private static File sortNoHead03; + private static File sortNoHeadCsv; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("sort.xlsx"); + file03 = TestFileUtil.createNewFile("sort.xls"); + fileCsv = TestFileUtil.createNewFile("sort.csv"); + sortNoHead07 = TestFileUtil.createNewFile("sortNoHead.xlsx"); + sortNoHead03 = TestFileUtil.createNewFile("sortNoHead.xls"); + sortNoHeadCsv = TestFileUtil.createNewFile("sortNoHead.csv"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite(file03); + } + + @Test + public void t03ReadAndWriteCsv() { + readAndWrite(fileCsv); + } + + @Test + public void t11ReadAndWriteNoHead07() { + readAndWriteNoHead(sortNoHead07); + } + + @Test + public void t12ReadAndWriteNoHead03() { + readAndWriteNoHead(sortNoHead03); + } + + @Test + public void t13ReadAndWriteNoHeadCsv() { + readAndWriteNoHead(sortNoHeadCsv); + } + + private void readAndWrite(File file) { + EasyExcel.write(file, SortData.class).sheet().doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals("column1", record.get(0)); + Assertions.assertEquals("column2", record.get(1)); + Assertions.assertEquals("column3", record.get(2)); + Assertions.assertEquals("column4", record.get(3)); + Assertions.assertEquals("column5", record.get(4)); + Assertions.assertEquals("column6", record.get(5)); + + EasyExcel.read(file, SortData.class, new SortDataListener()).sheet().doRead(); + } + + private void readAndWriteNoHead(File file) { + EasyExcel.write(file).head(head()).sheet().doWrite(data()); + List> dataMap = EasyExcel.read(file).sheet().doReadSync(); + Assertions.assertEquals(1, dataMap.size()); + Map record = dataMap.get(0); + Assertions.assertEquals("column1", record.get(0)); + Assertions.assertEquals("column2", record.get(1)); + Assertions.assertEquals("column3", record.get(2)); + Assertions.assertEquals("column4", record.get(3)); + Assertions.assertEquals("column5", record.get(4)); + Assertions.assertEquals("column6", record.get(5)); + EasyExcel.read(file, SortData.class, new SortDataListener()).sheet().doRead(); + } + + private List> head() { + List> head = new ArrayList>(); + head.add(Collections.singletonList("column1")); + head.add(Collections.singletonList("column2")); + head.add(Collections.singletonList("column3")); + head.add(Collections.singletonList("column4")); + head.add(Collections.singletonList("column5")); + head.add(Collections.singletonList("column6")); + return head; + } + + private List data() { + List list = new ArrayList(); + SortData sortData = new SortData(); + sortData.setColumn1("column1"); + sortData.setColumn2("column2"); + sortData.setColumn3("column3"); + sortData.setColumn4("column4"); + sortData.setColumn5("column5"); + sortData.setColumn6("column6"); + list.add(sortData); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleData.java new file mode 100644 index 000000000..b6b67c9d8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleData.java @@ -0,0 +1,24 @@ +package com.alibaba.easyexcel.test.core.style; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@HeadStyle +@HeadFontStyle +public class StyleData { + @ExcelProperty("字符串") + private String string; + @ExcelProperty("字符串1") + private String string1; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleDataListener.java new file mode 100644 index 000000000..64e7ff8bb --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleDataListener.java @@ -0,0 +1,34 @@ +package com.alibaba.easyexcel.test.core.style; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class StyleDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(StyleData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 2); + Assertions.assertEquals(list.get(0).getString(), "字符串0"); + Assertions.assertEquals(list.get(1).getString(), "字符串1"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleDataTest.java new file mode 100644 index 000000000..989c9c56d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/style/StyleDataTest.java @@ -0,0 +1,261 @@ +package com.alibaba.easyexcel.test.core.style; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.StyleTestUtils; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.DataFormatData; +import com.alibaba.excel.metadata.property.FontProperty; +import com.alibaba.excel.metadata.property.StyleProperty; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.AbstractVerticalCellStyleStrategy; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class StyleDataTest { + + private static File file07; + private static File file03; + private static File fileVerticalCellStyleStrategy07; + private static File fileVerticalCellStyleStrategy207; + private static File fileLoopMergeStrategy; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("style07.xlsx"); + file03 = TestFileUtil.createNewFile("style03.xls"); + fileVerticalCellStyleStrategy07 = TestFileUtil.createNewFile("verticalCellStyle.xlsx"); + fileVerticalCellStyleStrategy207 = TestFileUtil.createNewFile("verticalCellStyle2.xlsx"); + fileLoopMergeStrategy = TestFileUtil.createNewFile("loopMergeStrategy.xlsx"); + } + + @Test + public void t01ReadAndWrite07() throws Exception { + readAndWrite(file07); + } + + @Test + public void t02ReadAndWrite03() throws Exception { + readAndWrite(file03); + } + + @Test + public void t03AbstractVerticalCellStyleStrategy() { + AbstractVerticalCellStyleStrategy verticalCellStyleStrategy = new AbstractVerticalCellStyleStrategy() { + @Override + protected WriteCellStyle headCellStyle(Head head) { + WriteCellStyle writeCellStyle = new WriteCellStyle(); + writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + DataFormatData dataFormatData = new DataFormatData(); + dataFormatData.setIndex((short)0); + writeCellStyle.setDataFormatData(dataFormatData); + writeCellStyle.setHidden(false); + writeCellStyle.setLocked(true); + writeCellStyle.setQuotePrefix(true); + writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); + writeCellStyle.setWrapped(true); + writeCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); + writeCellStyle.setRotation((short)0); + writeCellStyle.setIndent((short)10); + writeCellStyle.setBorderLeft(BorderStyle.THIN); + writeCellStyle.setBorderRight(BorderStyle.THIN); + writeCellStyle.setBorderTop(BorderStyle.THIN); + writeCellStyle.setBorderBottom(BorderStyle.THIN); + writeCellStyle.setLeftBorderColor(IndexedColors.RED.getIndex()); + writeCellStyle.setRightBorderColor(IndexedColors.RED.getIndex()); + writeCellStyle.setTopBorderColor(IndexedColors.RED.getIndex()); + writeCellStyle.setBottomBorderColor(IndexedColors.RED.getIndex()); + writeCellStyle.setFillBackgroundColor(IndexedColors.RED.getIndex()); + writeCellStyle.setShrinkToFit(Boolean.TRUE); + + if (head.getColumnIndex() == 0) { + writeCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + WriteFont writeFont = new WriteFont(); + writeFont.setItalic(true); + writeFont.setStrikeout(true); + writeFont.setTypeOffset(Font.SS_NONE); + writeFont.setUnderline(Font.U_DOUBLE); + writeFont.setBold(true); + writeFont.setCharset((int)Font.DEFAULT_CHARSET); + } else { + writeCellStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex()); + } + return writeCellStyle; + } + + @Override + protected WriteCellStyle contentCellStyle(Head head) { + WriteCellStyle writeCellStyle = new WriteCellStyle(); + writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + if (head.getColumnIndex() == 0) { + writeCellStyle.setFillForegroundColor(IndexedColors.DARK_GREEN.getIndex()); + } else { + writeCellStyle.setFillForegroundColor(IndexedColors.PINK.getIndex()); + } + return writeCellStyle; + } + }; + EasyExcel.write(fileVerticalCellStyleStrategy07, StyleData.class).registerWriteHandler( + verticalCellStyleStrategy).sheet() + .doWrite(data()); + + } + + @Test + public void t04AbstractVerticalCellStyleStrategy02() { + final StyleProperty styleProperty = StyleProperty.build(StyleData.class.getAnnotation(HeadStyle.class)); + final FontProperty fontProperty = FontProperty.build(StyleData.class.getAnnotation(HeadFontStyle.class)); + AbstractVerticalCellStyleStrategy verticalCellStyleStrategy = new AbstractVerticalCellStyleStrategy() { + @Override + protected WriteCellStyle headCellStyle(Head head) { + WriteCellStyle writeCellStyle = WriteCellStyle.build(styleProperty, fontProperty); + + if (head.getColumnIndex() == 0) { + writeCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + WriteFont writeFont = new WriteFont(); + writeFont.setItalic(true); + writeFont.setStrikeout(true); + writeFont.setTypeOffset(Font.SS_NONE); + writeFont.setUnderline(Font.U_DOUBLE); + writeFont.setBold(true); + writeFont.setCharset((int)Font.DEFAULT_CHARSET); + } else { + writeCellStyle.setFillForegroundColor(IndexedColors.BLUE.getIndex()); + } + return writeCellStyle; + } + + @Override + protected WriteCellStyle contentCellStyle(Head head) { + WriteCellStyle writeCellStyle = new WriteCellStyle(); + writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + if (head.getColumnIndex() == 0) { + writeCellStyle.setFillForegroundColor(IndexedColors.DARK_GREEN.getIndex()); + } else { + writeCellStyle.setFillForegroundColor(IndexedColors.PINK.getIndex()); + } + return writeCellStyle; + } + }; + EasyExcel.write(fileVerticalCellStyleStrategy207, StyleData.class).registerWriteHandler( + verticalCellStyleStrategy).sheet() + .doWrite(data()); + } + + @Test + public void t05LoopMergeStrategy() { + EasyExcel.write(fileLoopMergeStrategy, StyleData.class).sheet().registerWriteHandler( + new LoopMergeStrategy(2, 1)) + .doWrite(data10()); + } + + private void readAndWrite(File file) throws Exception { + SimpleColumnWidthStyleStrategy simpleColumnWidthStyleStrategy = new SimpleColumnWidthStyleStrategy(50); + SimpleRowHeightStyleStrategy simpleRowHeightStyleStrategy = + new SimpleRowHeightStyleStrategy((short)40, (short)50); + + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontHeightInPoints((short)20); + headWriteFont.setColor(IndexedColors.DARK_YELLOW.getIndex()); + headWriteCellStyle.setWriteFont(headWriteFont); + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + contentWriteCellStyle.setFillForegroundColor(IndexedColors.TEAL.getIndex()); + WriteFont contentWriteFont = new WriteFont(); + contentWriteFont.setFontHeightInPoints((short)30); + contentWriteFont.setColor(IndexedColors.DARK_TEAL.getIndex()); + contentWriteCellStyle.setWriteFont(contentWriteFont); + HorizontalCellStyleStrategy horizontalCellStyleStrategy = + new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + + OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy = new OnceAbsoluteMergeStrategy(2, 2, 0, 1); + EasyExcel.write(file, StyleData.class).registerWriteHandler(simpleColumnWidthStyleStrategy) + .registerWriteHandler(simpleRowHeightStyleStrategy).registerWriteHandler(horizontalCellStyleStrategy) + .registerWriteHandler(onceAbsoluteMergeStrategy).sheet().doWrite(data()); + EasyExcel.read(file, StyleData.class, new StyleDataListener()).sheet().doRead(); + + Workbook workbook = WorkbookFactory.create(file); + Sheet sheet = workbook.getSheetAt(0); + Assertions.assertEquals(50 * 256, sheet.getColumnWidth(0), 0); + + Row row0 = sheet.getRow(0); + Assertions.assertEquals(800, row0.getHeight(), 0); + Cell cell00 = row0.getCell(0); + Assertions.assertArrayEquals(new byte[] {-1, -1, 0}, StyleTestUtils.getFillForegroundColor(cell00)); + Assertions.assertArrayEquals(new byte[] {-128, -128, 0}, StyleTestUtils.getFontColor(cell00, workbook)); + Assertions.assertEquals(20, StyleTestUtils.getFontHeightInPoints(cell00, workbook)); + + Cell cell01 = row0.getCell(1); + Assertions.assertArrayEquals(new byte[] {-1, -1, 0}, StyleTestUtils.getFillForegroundColor(cell01)); + Assertions.assertArrayEquals(new byte[] {-128, -128, 0}, StyleTestUtils.getFontColor(cell01, workbook)); + Assertions.assertEquals(20, StyleTestUtils.getFontHeightInPoints(cell01, workbook)); + + Row row1 = sheet.getRow(1); + Assertions.assertEquals(1000, row1.getHeight(), 0); + Cell cell10 = row1.getCell(0); + Assertions.assertArrayEquals(new byte[] {0, -128, -128}, StyleTestUtils.getFillForegroundColor(cell10)); + Assertions.assertArrayEquals(new byte[] {0, 51, 102}, StyleTestUtils.getFontColor(cell10, workbook)); + Assertions.assertEquals(30, StyleTestUtils.getFontHeightInPoints(cell10, workbook)); + Cell cell11 = row1.getCell(1); + Assertions.assertArrayEquals(new byte[] {0, -128, -128}, StyleTestUtils.getFillForegroundColor(cell11)); + Assertions.assertArrayEquals(new byte[] {0, 51, 102}, StyleTestUtils.getFontColor(cell11, workbook)); + Assertions.assertEquals(30, StyleTestUtils.getFontHeightInPoints(cell11, workbook)); + } + + private List data() { + List list = new ArrayList(); + StyleData data = new StyleData(); + data.setString("字符串0"); + data.setString1("字符串01"); + StyleData data1 = new StyleData(); + data1.setString("字符串1"); + data1.setString1("字符串11"); + list.add(data); + list.add(data1); + return list; + } + + private List data10() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + StyleData data = new StyleData(); + data.setString("字符串0"); + data.setString1("字符串01"); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateData.java new file mode 100644 index 000000000..02d012159 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateData.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.core.template; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class TemplateData { + @ExcelProperty("字符串0") + private String string0; + @ExcelProperty("字符串1") + private String string1; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateDataListener.java new file mode 100644 index 000000000..94f526c0b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateDataListener.java @@ -0,0 +1,34 @@ +package com.alibaba.easyexcel.test.core.template; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.simple.SimpleDataListener; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Assertions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class TemplateDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleDataListener.class); + List list = new ArrayList(); + + @Override + public void invoke(TemplateData data, AnalysisContext context) { + list.add(data); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + Assertions.assertEquals(list.size(), 2); + Assertions.assertEquals(list.get(0).getString0(), "字符串0"); + Assertions.assertEquals(list.get(1).getString0(), "字符串1"); + LOGGER.debug("First row:{}", JSON.toJSONString(list.get(0))); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateDataTest.java new file mode 100644 index 000000000..66c90d08d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/core/template/TemplateDataTest.java @@ -0,0 +1,66 @@ +package com.alibaba.easyexcel.test.core.template; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +/** + * @author Jiaju Zhuang + */ +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TemplateDataTest { + + private static File file07; + private static File file03; + + @BeforeAll + public static void init() { + file07 = TestFileUtil.createNewFile("template07.xlsx"); + file03 = TestFileUtil.createNewFile("template03.xls"); + } + + @Test + public void t01ReadAndWrite07() { + readAndWrite07(file07); + } + + @Test + public void t02ReadAndWrite03() { + readAndWrite03(file03); + } + + private void readAndWrite07(File file) { + EasyExcel.write(file, TemplateData.class) + .withTemplate(TestFileUtil.readFile("template" + File.separator + "template07.xlsx")).sheet() + .doWrite(data()); + EasyExcel.read(file, TemplateData.class, new TemplateDataListener()).headRowNumber(3).sheet().doRead(); + } + + private void readAndWrite03(File file) { + EasyExcel.write(file, TemplateData.class) + .withTemplate(TestFileUtil.readFile("template" + File.separator + "template03.xls")).sheet() + .doWrite(data()); + EasyExcel.read(file, TemplateData.class, new TemplateDataListener()).headRowNumber(3).sheet().doRead(); + } + + private List data() { + List list = new ArrayList(); + TemplateData data = new TemplateData(); + data.setString0("字符串0"); + data.setString1("字符串01"); + TemplateData data1 = new TemplateData(); + data1.setString0("字符串1"); + data1.setString1("字符串11"); + list.add(data); + list.add(data1); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java new file mode 100644 index 000000000..6eb34ada0 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillData.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.demo.fill; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FillData { + private String name; + private double number; + private Date date; + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java new file mode 100644 index 000000000..53b8b273d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java @@ -0,0 +1,231 @@ +package com.alibaba.easyexcel.test.demo.fill; + +import java.io.File; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; + +import org.junit.jupiter.api.Test; + +/** + * 写的填充写法 + * + * @author Jiaju Zhuang + * @since 2.1.1 + */ + +public class FillTest { + /** + * 最简单的填充 + * + * @since 2.1.1 + */ + @Test + public void simpleFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx"; + + // 方案1 根据对象填充 + String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); + + // 方案2 根据Map填充 + fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + Map map = MapUtils.newHashMap(); + map.put("name", "张三"); + map.put("number", 5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } + + /** + * 填充列表 + * + * @since 2.1.1 + */ + @Test + public void listFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // 填充list 的时候还要注意 模板中{.} 多了个点 表示list + // 如果填充list的对象是map,必须包涵所有list的key,哪怕数据为null,必须使用map.put(key,null) + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; + + // 方案1 一下子全部放到内存里面 并填充 + String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); + + // 方案2 分多次 填充 会使用文件缓存(省内存) + fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + } + } + + /** + * 复杂的填充 + * + * @since 2.1.1 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + // 方案1 + try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 + // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 + // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 + // 如果数据量大 list不是最后一行 参照下一个 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = MapUtils.newHashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + } + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + * + * @since 2.1.1 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + + // 方案1 + try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = ListUtils.newArrayList(); + List totalList = ListUtils.newArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } + } + + /** + * 横向的填充 + * + * @since 2.1.1 + */ + @Test + public void horizontalFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; + + String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; + // 方案1 + try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + + Map map = new HashMap<>(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + } + } + + /** + * 多列表组合填充填充 + * + * @since 2.2.0-beta1 + */ + @Test + public void compositeFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "composite.xlsx"; + + String fileName = TestFileUtil.getPath() + "compositeFill" + System.currentTimeMillis() + ".xlsx"; + + // 方案1 + try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + // 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹 + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + + Map map = new HashMap(); + //map.put("date", "2019年10月9日13:28:28"); + map.put("date", new Date()); + + excelWriter.fill(map, writeSheet); + } + } + + private List data() { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + fillData.setDate(new Date()); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/rare/WriteTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/rare/WriteTest.java new file mode 100644 index 000000000..b41ae6530 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/rare/WriteTest.java @@ -0,0 +1,140 @@ +package com.alibaba.easyexcel.test.demo.rare; + +import java.io.File; +import java.util.Date; +import java.util.List; + +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.WorkbookWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; +import com.alibaba.excel.write.handler.context.WorkbookWriteHandlerContext; +import com.alibaba.excel.write.metadata.WriteSheet; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.junit.jupiter.api.Test; + +/** + * 记录一些不太常见的案例 + * + * @author Jiaju Zhuang + */ + +@Slf4j +public class WriteTest { + + /** + * 压缩临时文件 + * 在导出Excel且格式为xlsx的时候会生成一个临时的xml文件,会比较大,再磁盘不太够的情况下,可以压缩。 + * 当然压缩式耗费性能的 + */ + @Test + public void compressedTemporaryFile() { + log.info("临时的xml存储在:{}", FileUtils.getPoiFilesPath()); + File file = TestFileUtil.createNewFile("rare/compressedTemporaryFile" + System.currentTimeMillis() + + ".xlsx"); + + // 这里 需要指定写用哪个class去写 + try (ExcelWriter excelWriter = EasyExcel.write(file, DemoData.class).registerWriteHandler( + new WorkbookWriteHandler() { + + /** + * 拦截Workbook创建完成事件 + * @param context + */ + @Override + public void afterWorkbookCreate(WorkbookWriteHandlerContext context) { + // 获取到Workbook对象 + Workbook workbook = context.getWriteWorkbookHolder().getWorkbook(); + // 只有SXSSFWorkbook模式才会生成临时文件 + if (workbook instanceof SXSSFWorkbook) { + SXSSFWorkbook sxssfWorkbook = (SXSSFWorkbook)workbook; + // 设置临时文件压缩,当然这个会浪费cpu性能 但是临时文件会变小 + sxssfWorkbook.setCompressTempFiles(true); + } + } + }).build()) { + // 这里注意 如果同一个sheet只要创建一次 + WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); + // 10万数据 确保有足够的空间 + for (int i = 0; i < 10000; i++) { + // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 + List data = data(); + excelWriter.write(data, writeSheet); + } + log.info("写入完毕,开始准备迁移压缩文件。"); + } + } + + /** + * 在指定单元格写入数据 + */ + @Test + public void specifiedCellWrite() { + File file = TestFileUtil.createNewFile("rare/specifiedCellWrite" + System.currentTimeMillis() + + ".xlsx"); + + // 需要区分是在 最后一行之前 还是之后 + // 区分的原因是:excel只能一直向前,而且内存里面只存储100条,而afterRowDispose是在每一行写入完成的时候调用,所以修改一行需要拦截这个事件 + // 如果是在最后一行之后,由于后面不会再有数据了,所以只要拦截afterWorkbookDispose,在整个excel快写完的时候调用,继续写入数据即可 + + EasyExcel.write(file, DemoData.class) + // 写入的值在最后一行之前 + .registerWriteHandler(new RowWriteHandler() { + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + if (context.getRow().getRowNum() == 2) { + Cell cell = context.getRow().getCell(2); + if (cell == null) { + cell = context.getRow().createCell(2); + } + cell.setCellValue("测试的第二行数据呀"); + } + } + }) + // 写入的值 在最后一一行之后 + .registerWriteHandler(new WorkbookWriteHandler() { + @Override + public void afterWorkbookDispose(WorkbookWriteHandlerContext context) { + Workbook workbook = context.getWriteWorkbookHolder().getWorkbook(); + Sheet sheet = workbook.getSheetAt(0); + Row row = sheet.getRow(99); + if (row == null) { + row = sheet.createRow(99); + } + Cell cell = row.getCell(2); + if (cell == null) { + cell = row.createCell(2); + } + cell.setCellValue("测试地99行数据呀"); + } + }) + .sheet("模板") + .doWrite(data()); + + log.info("写入到文件完成:{}", file); + } + + private List data() { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + data.setDate(new Date()); + data.setDoubleData(0.56); + list.add(data); + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java new file mode 100644 index 000000000..e04bc3070 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataDemoHeadDataListener.java @@ -0,0 +1,48 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 读取头 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class CellDataDemoHeadDataListener implements ReadListener { + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 100; + + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + @Override + public void invoke(CellDataReadDemoData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java new file mode 100644 index 000000000..b29c39bf1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CellDataReadDemoData.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import com.alibaba.excel.metadata.data.CellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class CellDataReadDemoData { + private CellData string; + // 这里注意 虽然是日期 但是 类型 存储的是number 因为excel 存储的就是number + private CellData date; + private CellData doubleData; + // 这里并不一定能完美的获取 有些公式是依赖性的 可能会读不到 这个问题后续会修复 + private CellData formulaValue; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterData.java new file mode 100644 index 000000000..2e87616f1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterData.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.demo.read; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class ConverterData { + /** + * 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:” + */ + @ExcelProperty(converter = CustomStringStringConverter.class) + private String string; + /** + * 这里用string 去接日期才能格式化。我想接收年月日格式 + */ + @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") + private String date; + /** + * 我想接收百分比的数字 + */ + @NumberFormat("#.##%") + private String doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterDataListener.java new file mode 100644 index 000000000..b7d144a5a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ConverterDataListener.java @@ -0,0 +1,49 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class ConverterDataListener implements ReadListener { + + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + @Override + public void invoke(ConverterData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + cachedDataList.add(data); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CustomStringStringConverter.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CustomStringStringConverter.java new file mode 100644 index 000000000..dabe57a8a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/CustomStringStringConverter.java @@ -0,0 +1,46 @@ +package com.alibaba.easyexcel.test.demo.read; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ReadConverterContext; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.WriteCellData; + +/** + * String and string converter + * + * @author Jiaju Zhuang + */ +public class CustomStringStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + /** + * 这里读的时候会调用 + * + * @param context + * @return + */ + @Override + public String convertToJavaData(ReadConverterContext context) { + return "自定义:" + context.getReadCellData().getStringValue(); + } + + /** + * 这里是写的时候会调用 不用管 + * + * @return + */ + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return new WriteCellData<>(context.getValue()); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDAO.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDAO.java new file mode 100644 index 000000000..8ca6b026a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDAO.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; + +/** + * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。 + * + * @author Jiaju Zhuang + **/ +public class DemoDAO { + + public void save(List list) { + // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入 + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoData.java new file mode 100644 index 000000000..6c3321d23 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DemoData { + private String string; + private Date date; + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java new file mode 100644 index 000000000..7815d374b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoDataListener.java @@ -0,0 +1,86 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 +@Slf4j +public class DemoDataListener implements ReadListener { + + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 100; + /** + * 缓存的数据 + */ + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + /** + * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。 + */ + private DemoDAO demoDAO; + + public DemoDataListener() { + // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数 + demoDAO = new DemoDAO(); + } + + /** + * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来 + * + * @param demoDAO + */ + public DemoDataListener(DemoDAO demoDAO) { + this.demoDAO = demoDAO; + } + + /** + * 这个每一条数据解析都会来调用 + * + * @param data one row value. It is same as {@link AnalysisContext#readRowHolder()} + * @param context + */ + @Override + public void invoke(DemoData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + cachedDataList.add(data); + // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + // 存储完成清理 list + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + /** + * 所有数据解析完成了 都会来调用 + * + * @param context + */ + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 这里也要保存数据,确保最后遗留的数据也存储到数据库 + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + demoDAO.save(cachedDataList); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java new file mode 100644 index 000000000..498725a0a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExceptionListener.java @@ -0,0 +1,81 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 读取转换异常 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class DemoExceptionListener implements ReadListener { + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + /** + * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 + * + * @param exception + * @param context + * @throws Exception + */ + @Override + public void onException(Exception exception, AnalysisContext context) { + log.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); + // 如果是某一个单元格的转换异常 能获取到具体行号 + // 如果要获取头的信息 配合invokeHeadMap使用 + if (exception instanceof ExcelDataConvertException) { + ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; + log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(), + excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData()); + } + } + + /** + * 这里会一行行的返回头 + * + * @param headMap + * @param context + */ + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + log.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); + } + + @Override + public void invoke(ExceptionDemoData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraData.java new file mode 100644 index 000000000..b0d9a03c6 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraData.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.demo.read; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class DemoExtraData { + + private String row1; + + private String row2; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java new file mode 100644 index 000000000..15c285dfa --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoExtraListener.java @@ -0,0 +1,57 @@ +package com.alibaba.easyexcel.test.demo.read; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.metadata.CellExtra; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; + +/** + * 读取单元格的批注 + * + * @author Jiaju Zhuang + **/ +@Slf4j +public class DemoExtraListener implements ReadListener { + + @Override + public void invoke(DemoExtraData data, AnalysisContext context) {} + + @Override + public void doAfterAllAnalysed(AnalysisContext context) {} + + @Override + public void extra(CellExtra extra, AnalysisContext context) { + log.info("读取到了一条额外信息:{}", JSON.toJSONString(extra)); + switch (extra.getType()) { + case COMMENT: + log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), + extra.getColumnIndex(), + extra.getText()); + break; + case HYPERLINK: + if ("Sheet1!A1".equals(extra.getText())) { + log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), + extra.getColumnIndex(), extra.getText()); + } else if ("Sheet2!A1".equals(extra.getText())) { + log.info( + "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}," + + "内容是:{}", + extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), + extra.getLastColumnIndex(), extra.getText()); + } else { + Assertions.fail("Unknown hyperlink!"); + } + break; + case MERGE: + log.info( + "额外信息是合并单元格,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}", + extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(), + extra.getLastColumnIndex()); + break; + default: + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java new file mode 100644 index 000000000..7e9cf6c67 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/DemoHeadDataListener.java @@ -0,0 +1,81 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.exception.ExcelDataConvertException; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 读取头 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class DemoHeadDataListener implements ReadListener { + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + /** + * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。 + * + * @param exception + * @param context + * @throws Exception + */ + @Override + public void onException(Exception exception, AnalysisContext context) { + log.error("解析失败,但是继续解析下一行:{}", exception.getMessage()); + if (exception instanceof ExcelDataConvertException) { + ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception; + log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(), + excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData()); + } + } + + /** + * 这里会一行行的返回头 + * + * @param headMap + * @param context + */ + @Override + public void invokeHead(Map> headMap, AnalysisContext context) { + log.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); + // 如果想转成成 Map + // 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener + // 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换 + } + + @Override + public void invoke(DemoData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java new file mode 100644 index 000000000..b202311dc --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ExceptionDemoData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class ExceptionDemoData { + /** + * 用日期去接字符串 肯定报错 + */ + private Date date; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameData.java new file mode 100644 index 000000000..a545ffa87 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameData.java @@ -0,0 +1,32 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class IndexOrNameData { + /** + * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 + */ + @ExcelProperty(index = 2) + private Double doubleData; + /** + * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据 + */ + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java new file mode 100644 index 000000000..77898d234 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/IndexOrNameDataListener.java @@ -0,0 +1,48 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class IndexOrNameDataListener extends AnalysisEventListener { + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + @Override + public void invoke(IndexOrNameData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + cachedDataList.add(data); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModelDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModelDataListener.java new file mode 100644 index 000000000..ec387ca05 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/NoModelDataListener.java @@ -0,0 +1,49 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 直接用map接收数据 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class NoModelDataListener extends AnalysisEventListener> { + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + private List> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + @Override + public void invoke(Map data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + cachedDataList.add(data); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java new file mode 100644 index 000000000..af678b98d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java @@ -0,0 +1,337 @@ +package com.alibaba.easyexcel.test.demo.read; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.converters.DefaultConverterLoader; +import com.alibaba.excel.enums.CellExtraTypeEnum; +import com.alibaba.excel.read.listener.PageReadListener; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.read.metadata.ReadSheet; +import com.alibaba.excel.read.metadata.holder.csv.CsvReadWorkbookHolder; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; + +/** + * 读的常见写法 + * + * @author Jiaju Zhuang + */ + +@Slf4j +public class ReadTest { + + /** + * 最简单的读 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + *

+ * 3. 直接读即可 + */ + @Test + public void simpleRead() { + // 写法1:JDK8+ ,不用额外写一个DemoDataListener + // since: 3.0.0-beta1 + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行 + // 具体需要返回多少行可以在`PageReadListener`的构造函数设置 + EasyExcel.read(fileName, DemoData.class, new PageReadListener(dataList -> { + for (DemoData demoData : dataList) { + log.info("读取到一条数据{}", JSON.toJSONString(demoData)); + } + })).sheet().doRead(); + + // 写法2: + // 匿名内部类 不用额外写一个DemoDataListener + fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + EasyExcel.read(fileName, DemoData.class, new ReadListener() { + /** + * 单次缓存的数据量 + */ + public static final int BATCH_COUNT = 100; + /** + *临时存储 + */ + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + + @Override + public void invoke(DemoData data, AnalysisContext context) { + cachedDataList.add(data); + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + // 存储完成清理 list + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + log.info("存储数据库成功!"); + } + }).sheet().doRead(); + + // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 + // 写法3: + fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead(); + + // 写法4 + fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 一个文件一个reader + try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) { + // 构建一个sheet 这里可以指定名字或者no + ReadSheet readSheet = EasyExcel.readSheet(0).build(); + // 读取一个sheet + excelReader.read(readSheet); + } + } + + /** + * 指定列的下标或者列名 + * + *

+ * 1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener} + *

+ * 3. 直接读即可 + */ + @Test + public void indexOrNameRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里默认读取第一个sheet + EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead(); + } + + /** + * 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + *

+ * 3. 直接读即可 + */ + @Test + public void repeatedRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 读取全部sheet + // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写 + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll(); + + // 读取部分sheet + fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + + // 写法1 + try (ExcelReader excelReader = EasyExcel.read(fileName).build()) { + // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener + ReadSheet readSheet1 = + EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); + ReadSheet readSheet2 = + EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build(); + // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能 + excelReader.read(readSheet1, readSheet2); + } + } + + /** + * 日期、数字或者自定义格式转换 + *

+ * 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()} + *

+ * 1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener} + *

+ * 3. 直接读即可 + */ + @Test + public void converterRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener()) + // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。 + // 如果就想单个字段使用请使用@ExcelProperty 指定converter + // .registerConverter(new CustomStringStringConverter()) + // 读取sheet + .sheet().doRead(); + } + + /** + * 多行头 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} + *

+ * 3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数, + * 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。 + */ + @Test + public void complexHeaderRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet() + // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行 + .headRowNumber(1).doRead(); + } + + /** + * 读取表头数据 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} + *

+ * 3. 直接读即可 + */ + @Test + public void headerRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead(); + } + + /** + * 额外信息(批注、超链接、合并单元格信息读取) + *

+ * 由于是流式读取,没法在读取到单元格数据的时候直接读取到额外信息,所以只能最后通知哪些单元格有哪些额外信息 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoExtraData} + *

+ * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExtraListener} + *

+ * 3. 直接读即可 + * + * @since 2.2.0-beat1 + */ + @Test + public void extraRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "extra.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, DemoExtraData.class, new DemoExtraListener()) + // 需要读取批注 默认不读取 + .extraRead(CellExtraTypeEnum.COMMENT) + // 需要读取超链接 默认不读取 + .extraRead(CellExtraTypeEnum.HYPERLINK) + // 需要读取合并单元格信息 默认不读取 + .extraRead(CellExtraTypeEnum.MERGE).sheet().doRead(); + } + + /** + * 读取公式和单元格类型 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link CellDataReadDemoData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener} + *

+ * 3. 直接读即可 + * + * @since 2.2.0-beat1 + */ + @Test + public void cellDataRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead(); + } + + /** + * 数据转换等异常处理 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener} + *

+ * 3. 直接读即可 + */ + @Test + public void exceptionRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet + EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead(); + } + + /** + * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面 + */ + @Test + public void synchronousRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish + List list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync(); + for (DemoData data : list) { + log.info("读取到数据:{}", JSON.toJSONString(data)); + } + + // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish + List> listMap = EasyExcel.read(fileName).sheet().doReadSync(); + for (Map data : listMap) { + // 返回每条数据的键值对 表示所在的列 和所在列的值 + log.info("读取到数据:{}", JSON.toJSONString(data)); + } + } + + /** + * 不创建对象的读 + */ + @Test + public void noModelRead() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + // 这里 只要,然后读取第一个sheet 同步读取会自动finish + EasyExcel.read(fileName, new NoModelDataListener()).sheet().doRead(); + } + + /** + * 自定义修改csv配置 + */ + @Test + public void csvFormat() { + String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.csv"; + try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) { + // 判断是 csv 文件 + if (excelReader.analysisContext().readWorkbookHolder() instanceof CsvReadWorkbookHolder) { + CsvReadWorkbookHolder csvReadWorkbookHolder = (CsvReadWorkbookHolder)excelReader.analysisContext() + .readWorkbookHolder(); + // 设置成逗号分隔 当然默认也是逗号分隔 + // 这里要注意 withDelimiter 会重新生成一个 所以要放回去 + csvReadWorkbookHolder.setCsvFormat(csvReadWorkbookHolder.getCsvFormat().withDelimiter(',')); + } + + // 拿到所有 sheet + List readSheetList = excelReader.excelExecutor().sheetList(); + // 如果只想读取第一个 咋样传入参数即可 + //ReadSheet readSheet = EasyExcel.readSheet(0).build(); + excelReader.read(readSheetList); + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/DownloadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/DownloadData.java new file mode 100644 index 000000000..501ecc785 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/DownloadData.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.demo.web; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DownloadData { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/EasyexcelApplication.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/EasyexcelApplication.java new file mode 100644 index 000000000..d12cf8534 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/EasyexcelApplication.java @@ -0,0 +1,12 @@ +package com.alibaba.easyexcel.test.demo.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class EasyexcelApplication { + + public static void main(String[] args) { + SpringApplication.run(EasyexcelApplication.class, args); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDAO.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDAO.java new file mode 100644 index 000000000..8c18484f0 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDAO.java @@ -0,0 +1,18 @@ +package com.alibaba.easyexcel.test.demo.web; + +import java.util.List; + +import org.springframework.stereotype.Repository; + +/** + * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。 + * + * @author Jiaju Zhuang + **/ +@Repository +public class UploadDAO { + + public void save(List list) { + // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入 + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadData.java new file mode 100644 index 000000000..2993969a0 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadData.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.demo.web; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class UploadData { + private String string; + private Date date; + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDataListener.java new file mode 100644 index 000000000..183bc1408 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/UploadDataListener.java @@ -0,0 +1,82 @@ +package com.alibaba.easyexcel.test.demo.web; + +import java.util.List; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去 +@Slf4j +public class UploadDataListener implements ReadListener { + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + /** + * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。 + */ + private UploadDAO uploadDAO; + + public UploadDataListener() { + // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数 + uploadDAO = new UploadDAO(); + } + + /** + * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来 + * + * @param uploadDAO + */ + public UploadDataListener(UploadDAO uploadDAO) { + this.uploadDAO = uploadDAO; + } + + /** + * 这个每一条数据解析都会来调用 + * + * @param data one row value. It is same as {@link AnalysisContext#readRowHolder()} + * @param context + */ + @Override + public void invoke(UploadData data, AnalysisContext context) { + log.info("解析到一条数据:{}", JSON.toJSONString(data)); + cachedDataList.add(data); + // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM + if (cachedDataList.size() >= BATCH_COUNT) { + saveData(); + // 存储完成清理 list + cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); + } + } + + /** + * 所有数据解析完成了 都会来调用 + * + * @param context + */ + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + // 这里也要保存数据,确保最后遗留的数据也存储到数据库 + saveData(); + log.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + log.info("{}条数据,开始存储数据库!", cachedDataList.size()); + uploadDAO.save(cachedDataList); + log.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java new file mode 100644 index 000000000..f1c8e7c20 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java @@ -0,0 +1,111 @@ +package com.alibaba.easyexcel.test.demo.web; + +import java.io.IOException; +import java.net.URLEncoder; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.util.MapUtils; +import com.alibaba.fastjson2.JSON; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +/** + * web读写案例 + * + * @author Jiaju Zhuang + **/ +@Controller +public class WebTest { + + @Autowired + private UploadDAO uploadDAO; + + /** + * 文件下载(失败了会返回一个有部分数据的Excel) + *

+ * 1. 创建excel对应的实体对象 参照{@link DownloadData} + *

+ * 2. 设置返回的 参数 + *

+ * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 + */ + @GetMapping("download") + public void download(HttpServletResponse response) throws IOException { + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 + String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + + EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); + } + + /** + * 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel) + * + * @since 2.1.1 + */ + @GetMapping("downloadFailedUsingJson") + public void downloadFailedUsingJson(HttpServletResponse response) throws IOException { + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman + try { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 + String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + // 这里需要设置不关闭流 + EasyExcel.write(response.getOutputStream(), DownloadData.class).autoCloseStream(Boolean.FALSE).sheet("模板") + .doWrite(data()); + } catch (Exception e) { + // 重置response + response.reset(); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + Map map = MapUtils.newHashMap(); + map.put("status", "failure"); + map.put("message", "下载文件失败" + e.getMessage()); + response.getWriter().println(JSON.toJSONString(map)); + } + } + + /** + * 文件上传 + *

+ * 1. 创建excel对应的实体对象 参照{@link UploadData} + *

+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener} + *

+ * 3. 直接读即可 + */ + @PostMapping("upload") + @ResponseBody + public String upload(MultipartFile file) throws IOException { + EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead(); + return "success"; + } + + private List data() { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + DownloadData data = new DownloadData(); + data.setString("字符串" + 0); + data.setDate(new Date()); + data.setDoubleData(0.56); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CommentWriteHandler.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CommentWriteHandler.java new file mode 100644 index 000000000..1be0fbddf --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CommentWriteHandler.java @@ -0,0 +1,37 @@ +package com.alibaba.easyexcel.test.demo.write; + +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.handler.context.RowWriteHandlerContext; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Comment; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; + +/** + * 自定义拦截器.新增注释,第一行头加批注 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class CommentWriteHandler implements RowWriteHandler { + + @Override + public void afterRowDispose(RowWriteHandlerContext context) { + if (BooleanUtils.isTrue(context.getHead())) { + Sheet sheet = context.getWriteSheetHolder().getSheet(); + Drawing drawingPatriarch = sheet.createDrawingPatriarch(); + // 在第一行 第二列创建一个批注 + Comment comment = + drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, (short)1, 0, (short)2, 1)); + // 输入批注信息 + comment.setString(new XSSFRichTextString("创建批注!")); + // 将批注添加到单元格对象中 + sheet.getRow(0).getCell(1).setCellComment(comment); + } + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java new file mode 100644 index 000000000..74a157d1a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ComplexHeadData.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 复杂头数据.这里最终效果是第一行就一个主标题,第二行分类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class ComplexHeadData { + @ExcelProperty({"主标题", "字符串标题"}) + private String string; + @ExcelProperty({"主标题", "日期标题"}) + private Date date; + @ExcelProperty({"主标题", "数字标题"}) + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ConverterData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ConverterData.java new file mode 100644 index 000000000..c120e47ac --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ConverterData.java @@ -0,0 +1,39 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class ConverterData { + /** + * 我想所有的 字符串起前面加上"自定义:"三个字 + */ + @ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class) + private String string; + /** + * 我想写到excel 用年月日的格式 + */ + @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") + @ExcelProperty("日期标题") + private Date date; + /** + * 我想写到excel 用百分比表示 + */ + @NumberFormat("#.##%") + @ExcelProperty(value = "数字标题") + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java new file mode 100644 index 000000000..81e12f52b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomCellWriteHandler.java @@ -0,0 +1,34 @@ +package com.alibaba.easyexcel.test.demo.write; + +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.common.usermodel.HyperlinkType; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Hyperlink; + +/** + * 自定义拦截器。对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel + * + * @author Jiaju Zhuang + */ +@Slf4j +public class CustomCellWriteHandler implements CellWriteHandler { + + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + Cell cell = context.getCell(); + // 这里可以对cell进行任何操作 + log.info("第{}行,第{}列写入完成。", cell.getRowIndex(), cell.getColumnIndex()); + if (BooleanUtils.isTrue(context.getHead()) && cell.getColumnIndex() == 0) { + CreationHelper createHelper = context.getWriteSheetHolder().getSheet().getWorkbook().getCreationHelper(); + Hyperlink hyperlink = createHelper.createHyperlink(HyperlinkType.URL); + hyperlink.setAddress("https://github.com/alibaba/easyexcel"); + cell.setHyperlink(hyperlink); + } + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomSheetWriteHandler.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomSheetWriteHandler.java new file mode 100644 index 000000000..877653581 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomSheetWriteHandler.java @@ -0,0 +1,31 @@ +package com.alibaba.easyexcel.test.demo.write; + +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.handler.context.SheetWriteHandlerContext; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.util.CellRangeAddressList; + +/** + * 自定义拦截器.对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2 + * + * @author Jiaju Zhuang + */ +@Slf4j +public class CustomSheetWriteHandler implements SheetWriteHandler { + + @Override + public void afterSheetCreate(SheetWriteHandlerContext context) { + log.info("第{}个Sheet写入成功。", context.getWriteSheetHolder().getSheetNo()); + + // 区间设置 第一列第一行和第二行的数据。由于第一行是头,所以第一、二行的数据实际上是第二三行 + CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 2, 0, 0); + DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper(); + DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"测试1", "测试2"}); + DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList); + context.getWriteSheetHolder().getSheet().addValidationData(dataValidation); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomStringStringConverter.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomStringStringConverter.java new file mode 100644 index 000000000..fde0f96a1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/CustomStringStringConverter.java @@ -0,0 +1,45 @@ +package com.alibaba.easyexcel.test.demo.write; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ReadConverterContext; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.WriteCellData; + +/** + * String and string converter + * + * @author Jiaju Zhuang + */ +public class CustomStringStringConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return String.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + /** + * 这里是读的时候会调用 不用管 + * + * @return + */ + @Override + public String convertToJavaData(ReadConverterContext context) { + return context.getReadCellData().getStringValue(); + } + + /** + * 这里是写的时候会调用 不用管 + * + * @return + */ + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + return new WriteCellData<>("自定义:" + context.getValue()); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java new file mode 100644 index 000000000..fc3079a1e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoData.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DemoData { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; + + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoMergeData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoMergeData.java new file mode 100644 index 000000000..44279db62 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoMergeData.java @@ -0,0 +1,31 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ContentLoopMerge; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 样式的数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +// 将第6-7行的2-3列合并成一个单元格 +// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2) +public class DemoMergeData { + // 这一列 每隔2行 合并单元格 + @ContentLoopMerge(eachRow = 2) + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoStyleData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoStyleData.java new file mode 100644 index 000000000..eded587b7 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/DemoStyleData.java @@ -0,0 +1,47 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadFontStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 样式的数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +// 头背景设置成红色 IndexedColors.RED.getIndex() +@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10) +// 头字体设置成20 +@HeadFontStyle(fontHeightInPoints = 20) +// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex() +@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17) +// 内容字体设置成20 +@ContentFontStyle(fontHeightInPoints = 20) +public class DemoStyleData { + // 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex() + @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14) + // 字符串的头字体设置成20 + @HeadFontStyle(fontHeightInPoints = 30) + // 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex() + @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40) + // 字符串的内容字体设置成20 + @ContentFontStyle(fontHeightInPoints = 30) + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ImageDataWithAnnotation.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ImageDataWithAnnotation.java new file mode 100644 index 000000000..ad59b68f8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ImageDataWithAnnotation.java @@ -0,0 +1,39 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.converters.string.StringImageConverter; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 图片导出类 + */ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(100) +@ColumnWidth(100 / 8) +public class ImageDataWithAnnotation { + private File file; + private InputStream inputStream; + /** + * 如果string类型 必须指定转换器,string默认转换成string + */ + @ExcelProperty(converter = StringImageConverter.class) + private String string; + private byte[] byteArray; + /** + * 根据url导出 + * + * @since 2.1.1 + */ + private URL url; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ImageDemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ImageDemoData.java new file mode 100644 index 000000000..9f1ebca37 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/ImageDemoData.java @@ -0,0 +1,49 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.converters.string.StringImageConverter; +import com.alibaba.excel.metadata.data.WriteCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 图片导出类 + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(100) +@ColumnWidth(100 / 8) +public class ImageDemoData { + private File file; + private InputStream inputStream; + /** + * 如果string类型 必须指定转换器,string默认转换成string + */ + @ExcelProperty(converter = StringImageConverter.class) + private String string; + private byte[] byteArray; + /** + * 根据url导出 + * + * @since 2.1.1 + */ + private URL url; + + /** + * 根据文件导出 并设置导出的位置。 + * + * @since 3.0.0-beta1 + */ + private WriteCellData writeCellDataFile; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/IndexData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/IndexData.java new file mode 100644 index 000000000..47c8c3b43 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/IndexData.java @@ -0,0 +1,29 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class IndexData { + @ExcelProperty(value = "字符串标题", index = 0) + private String string; + @ExcelProperty(value = "日期标题", index = 1) + private Date date; + /** + * 这里设置3 会导致第二列空的 + */ + @ExcelProperty(value = "数字标题", index = 3) + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/LongestMatchColumnWidthData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/LongestMatchColumnWidthData.java new file mode 100644 index 000000000..645be0629 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/LongestMatchColumnWidthData.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class LongestMatchColumnWidthData { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题很长日期标题很长日期标题很长很长") + private Date date; + @ExcelProperty("数字") + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WidthAndHeightData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WidthAndHeightData.java new file mode 100644 index 000000000..a3a9b7f25 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WidthAndHeightData.java @@ -0,0 +1,36 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(10) +@HeadRowHeight(20) +@ColumnWidth(25) +public class WidthAndHeightData { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + /** + * 宽度为50 + */ + @ColumnWidth(50) + @ExcelProperty("数字标题") + private Double doubleData; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteCellDemoData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteCellDemoData.java new file mode 100644 index 000000000..0cd50ddd3 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteCellDemoData.java @@ -0,0 +1,52 @@ +package com.alibaba.easyexcel.test.demo.write; + +import com.alibaba.excel.metadata.data.WriteCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 根据WriteCellData写 + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class WriteCellDemoData { + /** + * 超链接 + * + * @since 3.0.0-beta1 + */ + private WriteCellData hyperlink; + + /** + * 备注 + * + * @since 3.0.0-beta1 + */ + private WriteCellData commentData; + + /** + * 公式 + * + * @since 3.0.0-beta1 + */ + private WriteCellData formulaData; + + /** + * 指定单元格的样式。当然样式 也可以用注解等方式。 + * + * @since 3.0.0-beta1 + */ + private WriteCellData writeCellStyle; + + /** + * 指定一个单元格有多个样式 + * + * @since 3.0.0-beta1 + */ + private WriteCellData richText; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java new file mode 100644 index 000000000..0e7cf2db1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -0,0 +1,772 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.alibaba.easyexcel.test.core.head.ComplexHeadData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.format.NumberFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.CommentData; +import com.alibaba.excel.metadata.data.FormulaData; +import com.alibaba.excel.metadata.data.HyperlinkData; +import com.alibaba.excel.metadata.data.HyperlinkData.HyperlinkType; +import com.alibaba.excel.metadata.data.ImageData; +import com.alibaba.excel.metadata.data.ImageData.ImageType; +import com.alibaba.excel.metadata.data.RichTextStringData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.ListUtils; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; +import com.alibaba.excel.write.merge.LoopMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.junit.jupiter.api.Test; + +/** + * 写的常见写法 + * + * @author Jiaju Zhuang + */ + +public class WriteTest { + + /** + * 最简单的写 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 直接写即可 + */ + @Test + public void simpleWrite() { + // 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入 + + // 写法1 JDK8+ + // since: 3.0.0-beta1 + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class) + .sheet("模板") + .doWrite(() -> { + // 分页查询数据 + return data(); + }); + + // 写法2 + fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data()); + + // 写法3 + fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写 + try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { + WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); + excelWriter.write(data(), writeSheet); + } + } + + /** + * 根据参数只导出指定列 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 根据自己或者排除自己需要的列 + *

+ * 3. 直接写即可 + * + * @since 2.1.1 + */ + @Test + public void excludeOrIncludeWrite() { + String fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里需要注意 在使用ExcelProperty注解的使用,如果想不空列则需要加入order字段,而不是index,order会忽略空列,然后继续往后,而index,不会忽略空列,在第几列就是第几列。 + + // 根据用户传入字段 假设我们要忽略 date + Set excludeColumnFieldNames = new HashSet<>(); + excludeColumnFieldNames.add("date"); + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).excludeColumnFieldNames(excludeColumnFieldNames).sheet("模板") + .doWrite(data()); + + fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx"; + // 根据用户传入字段 假设我们只要导出 date + Set includeColumnFieldNames = new HashSet<>(); + includeColumnFieldNames.add("date"); + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).includeColumnFieldNames(includeColumnFieldNames).sheet("模板") + .doWrite(data()); + } + + /** + * 指定写入的列 + *

+ * 1. 创建excel对应的实体对象 参照{@link IndexData} + *

+ * 2. 使用{@link ExcelProperty}注解指定写入的列 + *

+ * 3. 直接写即可 + */ + @Test + public void indexWrite() { + String fileName = TestFileUtil.getPath() + "indexWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data()); + } + + /** + * 复杂头写入 + *

+ * 1. 创建excel对应的实体对象 参照{@link ComplexHeadData} + *

+ * 2. 使用{@link ExcelProperty}注解指定复杂的头 + *

+ * 3. 直接写即可 + */ + @Test + public void complexHeadWrite() { + String fileName = TestFileUtil.getPath() + "complexHeadWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data()); + } + + /** + * 重复多次写入 + *

+ * 1. 创建excel对应的实体对象 参照{@link ComplexHeadData} + *

+ * 2. 使用{@link ExcelProperty}注解指定复杂的头 + *

+ * 3. 直接调用二次写入即可 + */ + @Test + public void repeatedWrite() { + // 方法1: 如果写到同一个sheet + String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写 + try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { + // 这里注意 如果同一个sheet只要创建一次 + WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); + // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来 + for (int i = 0; i < 5; i++) { + // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 + List data = data(); + excelWriter.write(data, writeSheet); + } + } + + // 方法2: 如果写到不同的sheet 同一个对象 + fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 指定文件 + try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { + // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面 + for (int i = 0; i < 5; i++) { + // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样 + WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build(); + // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 + List data = data(); + excelWriter.write(data, writeSheet); + } + } + + // 方法3 如果写到不同的sheet 不同的对象 + fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 指定文件 + try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) { + // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面 + for (int i = 0; i < 5; i++) { + // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class + // 实际上可以一直变 + WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(DemoData.class).build(); + // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 + List data = data(); + excelWriter.write(data, writeSheet); + } + } + + } + + /** + * 日期、数字或者自定义格式转换 + *

+ * 1. 创建excel对应的实体对象 参照{@link ConverterData} + *

+ * 2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 + *

+ * 3. 直接写即可 + */ + @Test + public void converterWrite() { + String fileName = TestFileUtil.getPath() + "converterWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data()); + } + + /** + * 图片导出 + *

+ * 1. 创建excel对应的实体对象 参照{@link ImageDemoData} + *

+ * 2. 直接写即可 + */ + @Test + public void imageWrite() throws Exception { + String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx"; + + // 这里注意下 所有的图片都会放到内存 暂时没有很好的解法,大量图片的情况下建议 2选1: + // 1. 将图片上传到oss 或者其他存储网站: https://www.aliyun.com/product/oss ,然后直接放链接 + // 2. 使用: https://github.com/coobird/thumbnailator 或者其他工具压缩图片 + + String imagePath = TestFileUtil.getPath() + "converter" + File.separator + "img.jpg"; + try (InputStream inputStream = FileUtils.openInputStream(new File(imagePath))) { + List list = ListUtils.newArrayList(); + ImageDemoData imageDemoData = new ImageDemoData(); + list.add(imageDemoData); + // 放入五种类型的图片 实际使用只要选一种即可 + imageDemoData.setByteArray(FileUtils.readFileToByteArray(new File(imagePath))); + imageDemoData.setFile(new File(imagePath)); + imageDemoData.setString(imagePath); + imageDemoData.setInputStream(inputStream); + imageDemoData.setUrl(new URL( + "https://raw.githubusercontent.com/alibaba/easyexcel/master/src/test/resources/converter/img.jpg")); + + // 这里演示 + // 需要额外放入文字 + // 而且需要放入2个图片 + // 第一个图片靠左 + // 第二个靠右 而且要额外的占用他后面的单元格 + WriteCellData writeCellData = new WriteCellData<>(); + imageDemoData.setWriteCellDataFile(writeCellData); + // 这里可以设置为 EMPTY 则代表不需要其他数据了 + writeCellData.setType(CellDataTypeEnum.STRING); + writeCellData.setStringValue("额外的放一些文字"); + + // 可以放入多个图片 + List imageDataList = new ArrayList<>(); + ImageData imageData = new ImageData(); + imageDataList.add(imageData); + writeCellData.setImageDataList(imageDataList); + // 放入2进制图片 + imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath))); + // 图片类型 + imageData.setImageType(ImageType.PICTURE_TYPE_PNG); + // 上 右 下 左 需要留空 + // 这个类似于 css 的 margin + // 这里实测 不能设置太大 超过单元格原始大小后 打开会提示修复。暂时未找到很好的解法。 + imageData.setTop(5); + imageData.setRight(40); + imageData.setBottom(5); + imageData.setLeft(5); + + // 放入第二个图片 + imageData = new ImageData(); + imageDataList.add(imageData); + writeCellData.setImageDataList(imageDataList); + imageData.setImage(FileUtils.readFileToByteArray(new File(imagePath))); + imageData.setImageType(ImageType.PICTURE_TYPE_PNG); + imageData.setTop(5); + imageData.setRight(5); + imageData.setBottom(5); + imageData.setLeft(50); + // 设置图片的位置 假设 现在目标 是 覆盖 当前单元格 和当前单元格右边的单元格 + // 起点相对于当前单元格为0 当然可以不写 + imageData.setRelativeFirstRowIndex(0); + imageData.setRelativeFirstColumnIndex(0); + imageData.setRelativeLastRowIndex(0); + // 前面3个可以不写 下面这个需要写 也就是 结尾 需要相对当前单元格 往右移动一格 + // 也就是说 这个图片会覆盖当前单元格和 后面的那一格 + imageData.setRelativeLastColumnIndex(1); + + // 写入数据 + EasyExcel.write(fileName, ImageDemoData.class).sheet().doWrite(list); + } + } + + /** + * 超链接、备注、公式、指定单个单元格的样式、单个单元格多种样式 + *

+ * 1. 创建excel对应的实体对象 参照{@link WriteCellDemoData} + *

+ * 2. 直接写即可 + * + * @since 3.0.0-beta1 + */ + @Test + public void writeCellDataWrite() { + String fileName = TestFileUtil.getPath() + "writeCellDataWrite" + System.currentTimeMillis() + ".xlsx"; + WriteCellDemoData writeCellDemoData = new WriteCellDemoData(); + + // 设置超链接 + WriteCellData hyperlink = new WriteCellData<>("官方网站"); + writeCellDemoData.setHyperlink(hyperlink); + HyperlinkData hyperlinkData = new HyperlinkData(); + hyperlink.setHyperlinkData(hyperlinkData); + hyperlinkData.setAddress("https://github.com/alibaba/easyexcel"); + hyperlinkData.setHyperlinkType(HyperlinkType.URL); + + // 设置备注 + WriteCellData comment = new WriteCellData<>("备注的单元格信息"); + writeCellDemoData.setCommentData(comment); + CommentData commentData = new CommentData(); + comment.setCommentData(commentData); + commentData.setAuthor("Jiaju Zhuang"); + commentData.setRichTextStringData(new RichTextStringData("这是一个备注")); + // 备注的默认大小是按照单元格的大小 这里想调整到4个单元格那么大 所以向后 向下 各额外占用了一个单元格 + commentData.setRelativeLastColumnIndex(1); + commentData.setRelativeLastRowIndex(1); + + // 设置公式 + WriteCellData formula = new WriteCellData<>(); + writeCellDemoData.setFormulaData(formula); + FormulaData formulaData = new FormulaData(); + formula.setFormulaData(formulaData); + // 将 123456789 中的第一个数字替换成 2 + // 这里只是例子 如果真的涉及到公式 能内存算好尽量内存算好 公式能不用尽量不用 + formulaData.setFormulaValue("REPLACE(123456789,1,1,2)"); + + // 设置单个单元格的样式 当然样式 很多的话 也可以用注解等方式。 + WriteCellData writeCellStyle = new WriteCellData<>("单元格样式"); + writeCellStyle.setType(CellDataTypeEnum.STRING); + writeCellDemoData.setWriteCellStyle(writeCellStyle); + WriteCellStyle writeCellStyleData = new WriteCellStyle(); + writeCellStyle.setWriteCellStyle(writeCellStyleData); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色. + writeCellStyleData.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // 背景绿色 + writeCellStyleData.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + + // 设置单个单元格多种样式 + // 这里需要设置 inMomery=true 不然会导致无法展示单个单元格多种样式,所以慎用 + WriteCellData richTest = new WriteCellData<>(); + richTest.setType(CellDataTypeEnum.RICH_TEXT_STRING); + writeCellDemoData.setRichText(richTest); + RichTextStringData richTextStringData = new RichTextStringData(); + richTest.setRichTextStringDataValue(richTextStringData); + richTextStringData.setTextString("红色绿色默认"); + // 前2个字红色 + WriteFont writeFont = new WriteFont(); + writeFont.setColor(IndexedColors.RED.getIndex()); + richTextStringData.applyFont(0, 2, writeFont); + // 接下来2个字绿色 + writeFont = new WriteFont(); + writeFont.setColor(IndexedColors.GREEN.getIndex()); + richTextStringData.applyFont(2, 4, writeFont); + + List data = new ArrayList<>(); + data.add(writeCellDemoData); + EasyExcel.write(fileName, WriteCellDemoData.class).inMemory(true).sheet("模板").doWrite(data); + } + + /** + * 根据模板写入 + *

+ * 1. 创建excel对应的实体对象 参照{@link IndexData} + *

+ * 2. 使用{@link ExcelProperty}注解指定写入的列 + *

+ * 3. 使用withTemplate 写取模板 + *

+ * 4. 直接写即可 + */ + @Test + public void templateWrite() { + String templateFileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx"; + String fileName = TestFileUtil.getPath() + "templateWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 这里要注意 withTemplate 的模板文件会全量存储在内存里面,所以尽量不要用于追加文件,如果文件模板文件过大会OOM + // 如果要再文件中追加(无法在一个线程里面处理,可以在一个线程的建议参照多次写入的demo) 建议临时存储到数据库 或者 磁盘缓存(ehcache) 然后再一次性写入 + EasyExcel.write(fileName, DemoData.class).withTemplate(templateFileName).sheet().doWrite(data()); + } + + /** + * 列宽、行高 + *

+ * 1. 创建excel对应的实体对象 参照{@link WidthAndHeightData } + *

+ * 2. 使用注解{@link ColumnWidth}、{@link HeadRowHeight}、{@link ContentRowHeight}指定宽度或高度 + *

+ * 3. 直接写即可 + */ + @Test + public void widthAndHeightWrite() { + String fileName = TestFileUtil.getPath() + "widthAndHeightWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, WidthAndHeightData.class).sheet("模板").doWrite(data()); + } + + /** + * 注解形式自定义样式 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoStyleData} + *

+ * 3. 直接写即可 + * + * @since 2.2.0-beta1 + */ + @Test + public void annotationStyleWrite() { + String fileName = TestFileUtil.getPath() + "annotationStyleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoStyleData.class).sheet("模板").doWrite(data()); + } + + /** + * 拦截器形式自定义样式 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 创建一个style策略 并注册 + *

+ * 3. 直接写即可 + */ + @Test + public void handlerStyleWrite() { + // 方法1 使用已有的策略 推荐 + // HorizontalCellStyleStrategy 每一行的样式都一样 或者隔行一样 + // AbstractVerticalCellStyleStrategy 每一列的样式都一样 需要自己回调每一页 + String fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + // 头的策略 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + // 背景设置为红色 + headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontHeightInPoints((short)20); + headWriteCellStyle.setWriteFont(headWriteFont); + // 内容的策略 + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 + contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // 背景绿色 + contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + WriteFont contentWriteFont = new WriteFont(); + // 字体大小 + contentWriteFont.setFontHeightInPoints((short)20); + contentWriteCellStyle.setWriteFont(contentWriteFont); + // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + HorizontalCellStyleStrategy horizontalCellStyleStrategy = + new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class) + .registerWriteHandler(horizontalCellStyleStrategy) + .sheet("模板") + .doWrite(data()); + + // 方法2: 使用easyexcel的方式完全自己写 不太推荐 尽量使用已有策略 + // @since 3.0.0-beta2 + fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + EasyExcel.write(fileName, DemoData.class) + .registerWriteHandler(new CellWriteHandler() { + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + // 当前事件会在 数据设置到poi的cell里面才会回调 + // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true + if (BooleanUtils.isNotTrue(context.getHead())) { + // 第一个单元格 + // 只要不是头 一定会有数据 当然fill的情况 可能要context.getCellDataList() ,这个需要看模板,因为一个单元格会有多个 WriteCellData + WriteCellData cellData = context.getFirstCellData(); + // 这里需要去cellData 获取样式 + // 很重要的一个原因是 WriteCellStyle 和 dataFormatData绑定的 简单的说 比如你加了 DateTimeFormat + // ,已经将writeCellStyle里面的dataFormatData 改了 如果你自己new了一个WriteCellStyle,可能注解的样式就失效了 + // 然后 getOrCreateStyle 用于返回一个样式,如果为空,则创建一个后返回 + WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); + writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND + writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + + // 这样样式就设置好了 后面有个FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到 cell里面去 所以可以不用管了 + } + } + }).sheet("模板") + .doWrite(data()); + + // 方法3: 使用poi的样式完全自己写 不推荐 + // @since 3.0.0-beta2 + // 坑1:style里面有dataformat 用来格式化数据的 所以自己设置可能导致格式化注解不生效 + // 坑2:不要一直去创建style 记得缓存起来 最多创建6W个就挂了 + fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + EasyExcel.write(fileName, DemoData.class) + .registerWriteHandler(new CellWriteHandler() { + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + // 当前事件会在 数据设置到poi的cell里面才会回调 + // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true + if (BooleanUtils.isNotTrue(context.getHead())) { + Cell cell = context.getCell(); + // 拿到poi的workbook + Workbook workbook = context.getWriteWorkbookHolder().getWorkbook(); + // 这里千万记住 想办法能复用的地方把他缓存起来 一个表格最多创建6W个样式 + // 不同单元格尽量传同一个 cellStyle + CellStyle cellStyle = workbook.createCellStyle(); + cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND + cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + cell.setCellStyle(cellStyle); + + // 由于这里没有指定dataformat 最后展示的数据 格式可能会不太正确 + + // 这里要把 WriteCellData的样式清空, 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到 + // cell里面去 会导致自己设置的不一样 + context.getFirstCellData().setWriteCellStyle(null); + } + } + }).sheet("模板") + .doWrite(data()); + } + + /** + * 合并单元格 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} {@link DemoMergeData} + *

+ * 2. 创建一个merge策略 并注册 + *

+ * 3. 直接写即可 + * + * @since 2.2.0-beta1 + */ + @Test + public void mergeWrite() { + // 方法1 注解 + String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; + // 在DemoStyleData里面加上ContentLoopMerge注解 + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoMergeData.class).sheet("模板").doWrite(data()); + + // 方法2 自定义合并单元格策略 + fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx"; + // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写 + LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0); + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板").doWrite(data()); + } + + /** + * 使用table去写入 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 然后写入table即可 + */ + @Test + public void tableWrite() { + String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx"; + // 方法1 这里直接写多个table的案例了,如果只有一个 也可以直一行代码搞定,参照其他案 + // 这里 需要指定写用哪个class去写 + try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { + // 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了 + WriteSheet writeSheet = EasyExcel.writerSheet("模板").needHead(Boolean.FALSE).build(); + // 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要 + WriteTable writeTable0 = EasyExcel.writerTable(0).needHead(Boolean.TRUE).build(); + WriteTable writeTable1 = EasyExcel.writerTable(1).needHead(Boolean.TRUE).build(); + // 第一次写入会创建头 + excelWriter.write(data(), writeSheet, writeTable0); + // 第二次写如也会创建头,然后在第一次的后面写入数据 + excelWriter.write(data(), writeSheet, writeTable1); + } + } + + /** + * 动态头,实时生成头写入 + *

+ * 思路是这样子的,先创建List头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 然后写入table即可 + */ + @Test + public void dynamicHeadWrite() { + String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; + EasyExcel.write(fileName) + // 这里放入动态头 + .head(head()).sheet("模板") + // 当然这里数据也可以用 List> 去传入 + .doWrite(data()); + } + + /** + * 自动列宽(不太精确) + *

+ * 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照 {@link LongestMatchColumnWidthStyleStrategy} + * 重新实现. + *

+ * poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。 + * + *

+ * 1. 创建excel对应的实体对象 参照{@link LongestMatchColumnWidthData} + *

+ * 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy} + *

+ * 3. 直接写即可 + */ + @Test + public void longestMatchColumnWidthWrite() { + String fileName = + TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, LongestMatchColumnWidthData.class) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong()); + } + + /** + * 下拉,超链接等自定义拦截器(上面几点都不符合但是要对单元格进行操作的参照这个) + *

+ * demo这里实现2点。1. 对第一行第一列的头超链接到:https://github.com/alibaba/easyexcel 2. 对第一列第一行和第二行的数据新增下拉框,显示 测试1 测试2 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 注册拦截器 {@link CustomCellWriteHandler} {@link CustomSheetWriteHandler} + *

+ * 2. 直接写即可 + */ + @Test + public void customHandlerWrite() { + String fileName = TestFileUtil.getPath() + "customHandlerWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).registerWriteHandler(new CustomSheetWriteHandler()) + .registerWriteHandler(new CustomCellWriteHandler()).sheet("模板").doWrite(data()); + } + + /** + * 插入批注 + *

+ * 1. 创建excel对应的实体对象 参照{@link DemoData} + *

+ * 2. 注册拦截器 {@link CommentWriteHandler} + *

+ * 2. 直接写即可 + */ + @Test + public void commentWrite() { + String fileName = TestFileUtil.getPath() + "commentWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 这里要注意inMemory 要设置为true,才能支持批注。目前没有好的办法解决 不在内存处理批注。这个需要自己选择。 + EasyExcel.write(fileName, DemoData.class).inMemory(Boolean.TRUE).registerWriteHandler(new CommentWriteHandler()) + .sheet("模板").doWrite(data()); + } + + /** + * 可变标题处理(包括标题国际化等) + *

+ * 简单的说用List>的标题 但是还支持注解 + *

+ * 1. 创建excel对应的实体对象 参照{@link ConverterData} + *

+ * 2. 直接写即可 + */ + @Test + public void variableTitleWrite() { + // 写法1 + String fileName = TestFileUtil.getPath() + "variableTitleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, ConverterData.class).head(variableTitleHead()).sheet("模板").doWrite(data()); + } + + /** + * 不创建对象的写 + */ + @Test + public void noModelWrite() { + // 写法1 + String fileName = TestFileUtil.getPath() + "noModelWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList()); + } + + private List dataLong() { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + LongestMatchColumnWidthData data = new LongestMatchColumnWidthData(); + data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i); + data.setDate(new Date()); + data.setDoubleData(1000000000000.0); + list.add(data); + } + return list; + } + + private List> variableTitleHead() { + List> list = ListUtils.newArrayList(); + List head0 = ListUtils.newArrayList(); + head0.add("string" + System.currentTimeMillis()); + List head1 = ListUtils.newArrayList(); + head1.add("number" + System.currentTimeMillis()); + List head2 = ListUtils.newArrayList(); + head2.add("date" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } + + private List> head() { + List> list = ListUtils.newArrayList(); + List head0 = ListUtils.newArrayList(); + head0.add("字符串" + System.currentTimeMillis()); + List head1 = ListUtils.newArrayList(); + head1.add("数字" + System.currentTimeMillis()); + List head2 = ListUtils.newArrayList(); + head2.add("日期" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } + + private List> dataList() { + List> list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + List data = ListUtils.newArrayList(); + data.add("字符串" + i); + data.add(0.56); + data.add(new Date()); + list.add(data); + } + return list; + } + + private List data() { + List list = ListUtils.newArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + data.setDate(new Date()); + data.setDoubleData(0.56); + list.add(data); + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/CamlData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/CamlData.java new file mode 100644 index 000000000..86b9f4f77 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/CamlData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.temp; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * TODO + * + * @author 是仪 + */ +@Getter +@Setter +@EqualsAndHashCode +public class CamlData { + private String string1; + private String String2; + private String sTring3; + private String STring4; + private String STRING5; + private String STRing6; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java new file mode 100644 index 000000000..98693438b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData2.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.temp; + +import java.math.BigDecimal; +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DemoData2 { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; + @ExcelProperty("数字标题2") + private BigDecimal bigDecimal; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java new file mode 100644 index 000000000..9daffc57f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/DemoData3.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.temp; + +import java.time.LocalDateTime; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DemoData3 { + @ExcelProperty("日期时间标题") + private LocalDateTime localDateTime; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java new file mode 100644 index 000000000..d28fe2986 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/FillTempTest.java @@ -0,0 +1,129 @@ +package com.alibaba.easyexcel.test.temp; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.fill.FillData; +import com.alibaba.easyexcel.test.temp.fill.FillData2; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; + +import org.junit.jupiter.api.Test; + +/** + * 写的填充写法 + * + * @author Jiaju Zhuang + * @since 2.1.1 + */ + +public class FillTempTest { + + /** + * 复杂的填充 + * + * @since 2.1.1 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy = new OnceAbsoluteMergeStrategy(2, 2, 0, 1); + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).registerWriteHandler(onceAbsoluteMergeStrategy) + .withTemplate(TestFileUtil.readUserHomeFile("test/simple.xlsx")).build(); + WriteSheet writeSheet0 = EasyExcel.writerSheet(0).build(); + WriteSheet writeSheet1 = EasyExcel.writerSheet(1).build(); + + excelWriter.fill(teamp(), writeSheet0); + excelWriter.fill(teamp(), writeSheet1); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet0); + + excelWriter.finish(); + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + * + * @since 2.1.1 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = "D:\\test\\complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data2(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + excelWriter.finish(); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } + + private List data2() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData2 fillData = new FillData2(); + list.add(fillData); + fillData.setTest("ttttttt" + i); + } + return list; + } + + private List teamp() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + TempFillData fillData = new TempFillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java new file mode 100644 index 000000000..eb27bbb97 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Lock2Test.java @@ -0,0 +1,445 @@ +package com.alibaba.easyexcel.test.temp; + +import java.io.File; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.util.PositionUtils; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.poi.ss.util.CellReference; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ +@Slf4j +public class Lock2Test { + + private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class); + + @Test + public void test() throws Exception { + // File file = TestFileUtil.readUserHomeFile("test/test4.xlsx"); + // File file = TestFileUtil.readUserHomeFile("test/test6.xls"); + File file = new File("/Users/zhuangjiaju/IdeaProjects/easyexcel/src/test/resources/converter/converter07.xlsx"); + + List list = EasyExcel.read( + "/Users/zhuangjiaju/Downloads/证券投资基金估值表_外贸信托-稳盈淳享37号集合资金信托计划_2024-07-23(1).xls") + //.useDefaultListener(false) + .sheet(0) + .headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (Object data : list) { + LOGGER.info("返回数据:{}", CollectionUtils.size(data)); + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void test33() throws Exception { + File file = TestFileUtil.readUserHomeFile("test/test6.xlsx"); + + EasyExcel.read(file, LockData.class, new LockDataListener()).sheet(0).headRowNumber(0) + .doRead(); + + } + + @Test + public void write() throws Exception { + String fileName = TestFileUtil.getPath() + "styleWrite" + System.currentTimeMillis() + ".xlsx"; + // 头的策略 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + // 背景设置为红色 + headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontHeightInPoints((short)20); + headWriteCellStyle.setWriteFont(headWriteFont); + // 内容的策略 + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 + contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // 背景绿色 + contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + WriteFont contentWriteFont = new WriteFont(); + // 字体大小 + contentWriteFont.setFontHeightInPoints((short)20); + contentWriteCellStyle.setWriteFont(contentWriteFont); + // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + HorizontalCellStyleStrategy horizontalCellStyleStrategy = + new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板") + .doWrite(data()); + } + + @Test + public void simpleWrite() { + String fileName = TestFileUtil.getPath() + System.currentTimeMillis() + ".xlsx"; + System.out.println(fileName); + EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList()); + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("表头"); + + list.add(head0); + return list; + } + + private List> dataList() { + List> list = new ArrayList>(); + List data = new ArrayList(); + data.add("字符串"); + data.add(new Date()); + data.add(0.56); + list.add(data); + return list; + } + + @Test + public void testc() throws Exception { + LOGGER.info("reslut:{}", JSON.toJSONString(new CellReference("B3"))); + } + + @Test + public void simpleRead() { + // 写法1: + String fileName = "D:\\test\\珠海 (1).xlsx"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + EasyExcel.read(fileName, LockData.class, new LockDataListener()).useDefaultListener(false).sheet().doRead(); + } + + @Test + public void test2() throws Exception { + File file = new File("D:\\test\\converter03.xls"); + + List list = EasyExcel.read(file).sheet().headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + LOGGER.info("文件状态:{}", file.exists()); + file.delete(); + Thread.sleep(500 * 1000); + } + + @Test + public void test335() throws Exception { + + LOGGER.info("reslut:{}", PositionUtils.getCol("A10", null)); + LOGGER.info("reslut:{}", PositionUtils.getRow("A10")); + LOGGER.info("reslut:{}", PositionUtils.getCol("AB10", null)); + LOGGER.info("reslut:{}", PositionUtils.getRow("AB10")); + + //LOGGER.info("reslut:{}", PositionUtils2.getCol("A10",null)); + //LOGGER.info("reslut:{}", PositionUtils2.getRow("A10")); + //LOGGER.info("reslut:{}", PositionUtils2.getCol("AB10",null)); + //LOGGER.info("reslut:{}", PositionUtils2.getRow("AB10")); + } + + @Test + public void numberforamt() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44727.99998842592), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44728.99998842592), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44729.99998836806), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44727.99998842592).setScale(10, RoundingMode + // .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + // + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44728.99998842592).setScale(10, RoundingMode + // .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + + //44729.9999883681 + //44729.999988368058 + //LOGGER.info("date:{}", + // NumberDataFormatterUtils.format(BigDecimal.valueOf(44729.999988368058).setScale(10, RoundingMode + // .HALF_UP), (short)200, "yyyy-MM-dd HH:mm:ss", + // null, + // null, null)); + //LOGGER.info("date:{}",BigDecimal.valueOf(44729.999988368058).setScale(10, RoundingMode.HALF_UP).doubleValue + // ()); + + // 2022/6/17 23:59:59 + // 期望 44729.99998842592 + //LOGGER.info("data:{}", DateUtil.getJavaDate(44729.9999883681, true)); + LOGGER.info("data4:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(4, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data5:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(5, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data6:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(6, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data7:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(7, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data8:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999988368058) + .setScale(8, RoundingMode.HALF_UP).doubleValue(), false)); + + LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.999988368058, false))); + LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.9999883681, false))); + + LOGGER.info("data:{}", DateUtil.getJavaDate(Double.parseDouble("44729.999988368058"), false)); + LOGGER.info("data:{}", DateUtil.getJavaDate(Double.parseDouble("44729.9999883681"), false)); + + // 44729.999976851854 + // 44729.999988368058 + LOGGER.info("data:{}", DateUtil.getExcelDate(format.parse("2022-06-17 23:59:58"))); + // 44729.99998842592 + LOGGER.info("data:{}", DateUtil.getExcelDate(format.parse("2022-06-17 23:59:59"))); + + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999976851854) + .setScale(10, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.99998842592) + .setScale(10, RoundingMode.HALF_UP).doubleValue(), false)); + + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.999976851854) + .setScale(5, RoundingMode.HALF_UP).doubleValue(), false)); + LOGGER.info("data:{}", DateUtil.getJavaDate(BigDecimal.valueOf(44729.99998842592) + .setScale(5, RoundingMode.HALF_UP).doubleValue(), false)); + } + + @Test + public void testDate() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + log.info("TT:{}", format.format(new Date(100L))); + log.info("TT:{}", new Date().getTime()); + } + + @Test + public void testDateAll() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + SimpleDateFormat format2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + long dateTime = 0L; + while (true) { + Date date = new Date(dateTime); + double excelDate = DateUtil.getExcelDate(date); + + Assertions.assertEquals("测试基本转换错误" + dateTime, format.format(date), + format.format(DateUtil.getJavaDate(excelDate, false))); + Assertions.assertEquals("测试精度5转换错误" + dateTime, format.format(date), + format.format(DateUtil.getJavaDate(BigDecimal.valueOf(excelDate) + .setScale(10, RoundingMode.HALF_UP).doubleValue(), false))); + LOGGER.info("date:{}", format2.format(DateUtil.getJavaDate(BigDecimal.valueOf(excelDate) + .setScale(10, RoundingMode.HALF_UP).doubleValue()))); + dateTime += 1000L; + // 30天输出 + if (dateTime % (24 * 60 * 60 * 1000) == 0) { + log.info("{}成功", format.format(date)); + } + if (dateTime > 1673957544750L) { + log.info("结束啦"); + break; + } + } + log.info("结束啦"); + + } + + @Test + public void numberforamt3() throws Exception { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List> list = EasyExcel.read("/Users/zhuangjiaju/Downloads/date3.xlsx") + .useDefaultListener(false) + .sheet(0) + .headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (Map readCellDataMap : list) { + ReadCellData data = readCellDataMap.get(0); + LOGGER.info("data:{}", format.format( + DateUtil.getJavaDate(data.getNumberValue().setScale(10, RoundingMode.HALF_UP).doubleValue(), false))); + + } + // + //LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44727.999988425923, false))); + //LOGGER.info("data:{}", format.format(DateUtil.getJavaDate(44729.999988368058, false))); + + } + + @Test + public void numberforamt4() throws Exception { + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class) + .sheet("模板") + .doWrite(() -> { + // 分页查询数据 + return data2(); + }); + + } + + @Test + public void numberforamt77() throws Exception { + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData3.class) + .sheet("模板") + .doWrite(() -> { + List list = new ArrayList<>(); + DemoData3 demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 400000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 499000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 500000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 501000000)); + list.add(demoData3); + demoData3 = new DemoData3(); + demoData3.setLocalDateTime(LocalDateTime.of(2023, 1, 1, 0, 0, 0, 995000000)); + list.add(demoData3); + return list; + }); + + } + + @Test + public void numberforamt99() throws Exception { + LocalDateTime localDateTime = LocalDateTime.of(2023, 1, 1, 0, 0, 0, 995000000); + log.info("date:{}", localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"))); + + } + + @Test + public void numberforamt5() throws Exception { + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class) + .sheet("模板") + .doWrite(() -> { + // 分页查询数据 + return data3(); + }); + + } + + @Test + public void numberforamt6() throws Exception { + DecimalFormat decimalFormat = new DecimalFormat("#.#"); + BigDecimal bigDecimal = new BigDecimal(3101011021236149800L); + log.info("b:{}", bigDecimal); + log.info("b:{}", bigDecimal.setScale(-4, RoundingMode.HALF_UP)); + log.info("b:{}", decimalFormat.format(bigDecimal.setScale(-4, RoundingMode.HALF_UP))); + + } + + @Test + public void numberforamt7() throws Exception { + DecimalFormat decimalFormat = new DecimalFormat("#.#"); + BigDecimal bigDecimal = new BigDecimal(3.1010110212361498E+18).round(new MathContext(15, RoundingMode.HALF_UP)); + //bigDecimal. + + // bigDecimal + log.info("b:{}", bigDecimal); + log.info("b:{}", bigDecimal.setScale(-4, RoundingMode.HALF_UP)); + log.info("b:{}", decimalFormat.format(bigDecimal.setScale(-4, RoundingMode.HALF_UP))); + log.info("b:{}", decimalFormat.format(bigDecimal)); + + } + + private List data3() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + DemoData2 data = new DemoData2(); + data.setString("字符串" + i); + data.setDoubleData(0.56); + data.setBigDecimal(BigDecimal.valueOf(3101011021236149800L)); + list.add(data); + } + return list; + } + + private List data() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + try { + data.setDate(format.parse("2032-01-18 09:00:01.995")); + } catch (ParseException e) { + throw new RuntimeException(e); + } + data.setDoubleData(0.56); + list.add(data); + } + return list; + } + + private List data2() { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + try { + data.setDate(format.parse("2032-01-18 09:00:00.")); + } catch (ParseException e) { + throw new RuntimeException(e); + } + data.setDoubleData(0.56); + list.add(data); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockData.java new file mode 100644 index 000000000..88195cf9a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockData.java @@ -0,0 +1,29 @@ +package com.alibaba.easyexcel.test.temp; + +import com.alibaba.excel.annotation.format.NumberFormat; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 基础数据类.这里的排序和excel里面的排序一致 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class LockData { + @NumberFormat("#.##%") + private Double string0; + private String string1; + private String string2; + private String string3; + private String string4; + private String string5; + private String string6; + private String string7; + private String string8; + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockDataListener.java new file mode 100644 index 000000000..6a95ab043 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockDataListener.java @@ -0,0 +1,50 @@ +package com.alibaba.easyexcel.test.temp; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.demo.read.DemoDataListener; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +public class LockDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + @Override + public void invoke(LockData data, AnalysisContext context) { + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); + list.add(data); + if (list.size() >= BATCH_COUNT) { + saveData(); + list.clear(); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + LOGGER.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + LOGGER.info("{}条数据,开始存储数据库!", list.size()); + LOGGER.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java new file mode 100644 index 000000000..9dac75b08 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/LockTest.java @@ -0,0 +1,43 @@ +package com.alibaba.easyexcel.test.temp; + +import java.io.FileInputStream; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class LockTest { + private static final Logger LOGGER = LoggerFactory.getLogger(LockTest.class); + + @Test + public void test() throws Exception { + List list = + EasyExcel.read(new FileInputStream("/Users/zhuangjiaju/Downloads/-0304.2.xlsx")).useDefaultListener(false) + .doReadAllSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void test2() throws Exception { + List list = + EasyExcel.read(new FileInputStream("D:\\test\\开发部.xls")).sheet().headRowNumber(0).doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", ((Map)data).size()); + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java new file mode 100644 index 000000000..df898cf0a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/StyleData.java @@ -0,0 +1,23 @@ +package com.alibaba.easyexcel.test.temp; + +import java.util.List; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class StyleData { + private byte[] byteValue; + private Byte[] byteValue2; + private byte byteValue1; + private Byte byteValue4; + private byte byteValue3; + private String[] ss; + private List s1s; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java new file mode 100644 index 000000000..29ac81b46 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/StyleTest.java @@ -0,0 +1,196 @@ +package com.alibaba.easyexcel.test.temp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.Date; +import java.util.List; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; + +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.crypt.Decryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.BuiltinFormats; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class StyleTest { + private static final Logger LOGGER = LoggerFactory.getLogger(StyleTest.class); + + @Test + public void test() { + List list = EasyExcel.read("D:\\test\\styleTest.xls").sheet().headRowNumber(0).doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void poi() throws Exception { + InputStream is = new FileInputStream("D:\\test\\styleTest.xls"); + HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is); + HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(0); + HSSFRow hssfRow = hssfSheet.getRow(0); + System.out.println(hssfRow.getCell(0).getCellStyle().getDataFormatString()); + DataFormatter formatter = new DataFormatter(); + System.out.println(hssfRow.getCell(0).getNumericCellValue()); + System.out.println(hssfRow.getCell(1).getNumericCellValue()); + System.out.println(hssfRow.getCell(2).getNumericCellValue()); + System.out.println(hssfRow.getCell(0).getCellStyle().getDataFormatString()); + System.out.println(hssfRow.getCell(1).getCellStyle().getDataFormatString()); + System.out.println(hssfRow.getCell(2).getCellStyle().getDataFormatString()); + + } + + @Test + public void poi07() throws Exception { + InputStream is = new FileInputStream("D:\\test\\styleTest.xlsx"); + Workbook workbook = WorkbookFactory.create(is); // 这种方式 Excel 2003/2007/2010 都是可以处理的 + Sheet sheet = workbook.getSheetAt(0); + Row hssfRow = sheet.getRow(0); + System.out.println(hssfRow.getCell(0).getCellStyle().getDataFormatString()); + DataFormatter formatter = new DataFormatter(); + System.out.println(hssfRow.getCell(0).getNumericCellValue()); + System.out.println(hssfRow.getCell(1).getNumericCellValue()); + System.out.println(hssfRow.getCell(2).getNumericCellValue()); + System.out.println(hssfRow.getCell(0).getCellStyle().getDataFormat()); + System.out.println(hssfRow.getCell(1).getCellStyle().getDataFormat()); + System.out.println(hssfRow.getCell(2).getCellStyle().getDataFormat()); + System.out.println(hssfRow.getCell(3).getCellStyle().getDataFormat()); + System.out.println(hssfRow.getCell(0).getCellStyle().getDataFormatString()); + System.out.println(hssfRow.getCell(1).getCellStyle().getDataFormatString()); + System.out.println(hssfRow.getCell(2).getCellStyle().getDataFormatString()); + System.out.println(hssfRow.getCell(3).getCellStyle().getDataFormatString()); + isDate(hssfRow.getCell(0)); + isDate(hssfRow.getCell(1)); + isDate(hssfRow.getCell(2)); + isDate(hssfRow.getCell(3)); + + } + + @Test + public void poi0701() throws Exception { + InputStream is = new FileInputStream("D:\\test\\f1.xlsx"); + Workbook workbook = WorkbookFactory.create(is); + Sheet sheet = workbook.getSheetAt(0); + print(sheet.getRow(0).getCell(0)); + print(sheet.getRow(1).getCell(0)); + print(sheet.getRow(2).getCell(0)); + print(sheet.getRow(3).getCell(0)); + } + + @Test + public void poi0702() throws Exception { + Workbook workbook = WorkbookFactory.create(new FileInputStream("D:\\test\\t2.xlsx")); + workbook = WorkbookFactory.create(new File("D:\\test\\t2.xlsx")); + Sheet sheet = workbook.getSheetAt(0); + Row row = sheet.getRow(0); + System.out.println(row.getCell(0).getNumericCellValue()); + } + + @Test + public void poi0703() throws Exception { + try { + POIFSFileSystem poifsFileSystem = new POIFSFileSystem(new FileInputStream("D:\\test\\t2.xlsx")); + System.out.println(poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)); + } catch (Exception e) { + e.printStackTrace(); + } + + try { + POIFSFileSystem poifsFileSystem = new POIFSFileSystem(new File("D:\\test\\t2.xlsx")); + System.out.println(poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)); + } catch (Exception e) { + e.printStackTrace(); + } + try { + POIFSFileSystem poifsFileSystem = new POIFSFileSystem(new FileInputStream("D:\\test\\t222.xlsx")); + System.out.println(poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)); + } catch (Exception e) { + e.printStackTrace(); + } + try { + POIFSFileSystem poifsFileSystem = new POIFSFileSystem(new File("D:\\test\\t222.xlsx")); + System.out.println(poifsFileSystem.getRoot().hasEntry(Decryptor.DEFAULT_POIFS_ENTRY)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void print(Cell cell) { + System.out.println( + DateUtil.isADateFormat(cell.getCellStyle().getDataFormat(), cell.getCellStyle().getDataFormatString())); + System.out.println(cell.getCellStyle().getDataFormat()); + System.out.println(cell.getCellStyle().getDataFormatString()); + DataFormatter f = new DataFormatter(); + System.out.println(f.formatCellValue(cell)); + if (cell.getCellStyle().getDataFormatString() != null) { + + } + ExcelStyleDateFormatter ff = new ExcelStyleDateFormatter(cell.getCellStyle().getDataFormatString()); + + } + + @Test + public void testFormatter() throws Exception { + ExcelStyleDateFormatter ff = new ExcelStyleDateFormatter("yyyy年m月d日"); + + System.out.println(ff.format(new Date())); + } + + @Test + public void testFormatter2() throws Exception { + StyleData styleData = new StyleData(); + Field field = styleData.getClass().getDeclaredField("byteValue"); + LOGGER.info("field:{}", field.getType().getName()); + field = styleData.getClass().getDeclaredField("byteValue2"); + LOGGER.info("field:{}", field.getType().getName()); + field = styleData.getClass().getDeclaredField("byteValue4"); + LOGGER.info("field:{}", field.getType()); + field = styleData.getClass().getDeclaredField("byteValue3"); + LOGGER.info("field:{}", field.getType()); + } + + @Test + public void testFormatter3() throws Exception { + LOGGER.info("field:{}", Byte.class == Byte.class); + } + + private void isDate(Cell cell) { + System.out.println( + DateUtil.isADateFormat(cell.getCellStyle().getDataFormat(), cell.getCellStyle().getDataFormatString())); + //System.out.println(HSSFDateUtil.isCellDateFormatted(cell)); + DataFormatter f = new DataFormatter(); + System.out.println(f.formatCellValue(cell)); + + } + + @Test + public void testBuiltinFormats() throws Exception { + System.out.println(BuiltinFormats.getBuiltinFormat(48)); + System.out.println(BuiltinFormats.getBuiltinFormat(57)); + System.out.println(BuiltinFormats.getBuiltinFormat(28)); + + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java new file mode 100644 index 000000000..dbbb70584 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/TempFillData.java @@ -0,0 +1,19 @@ +package com.alibaba.easyexcel.test.temp; + +import com.alibaba.excel.annotation.write.style.ContentRowHeight; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(30) +public class TempFillData { + private String name; + private double number; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteLargeTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteLargeTest.java new file mode 100644 index 000000000..846ebdb7d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteLargeTest.java @@ -0,0 +1,199 @@ +package com.alibaba.easyexcel.test.temp; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.core.large.LargeData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.read.listener.PageReadListener; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.hssf.eventusermodel.HSSFEventFactory; +import org.apache.poi.hssf.eventusermodel.HSSFListener; +import org.apache.poi.hssf.eventusermodel.HSSFRequest; +import org.apache.poi.hssf.record.BOFRecord; +import org.apache.poi.hssf.record.BoundSheetRecord; +import org.apache.poi.hssf.record.Record; +import org.apache.poi.hssf.record.SSTRecord; +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +@Slf4j +public class WriteLargeTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(WriteLargeTest.class); + + @Test + public void test() throws Exception { + // 方法2 如果写到不同的sheet 同一个对象 + String fileName = TestFileUtil.getPath() + "large" + System.currentTimeMillis() + ".xlsx"; + // 头的策略 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + // 背景设置为红色 + headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontHeightInPoints((short)20); + headWriteCellStyle.setWriteFont(headWriteFont); + // 内容的策略 + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 + contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // 背景绿色 + contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + WriteFont contentWriteFont = new WriteFont(); + // 字体大小 + contentWriteFont.setFontHeightInPoints((short)20); + contentWriteCellStyle.setWriteFont(contentWriteFont); + // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + HorizontalCellStyleStrategy horizontalCellStyleStrategy = + new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + + ExcelWriter excelWriter = EasyExcel.write(fileName, LargeData.class).registerWriteHandler( + horizontalCellStyleStrategy).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 100; j++) { + excelWriter.write(data(), writeSheet); + LOGGER.info("{} fill success.", j); + } + excelWriter.finish(); + + } + + @Test + public void read() throws Exception { + log.info("start"); + String fileName = "/Users/zhuangjiaju/Downloads/1e9e0578a9634abbbbd9b67f338f142a.xls"; + // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 + // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行 + // 具体需要返回多少行可以在`PageReadListener`的构造函数设置 + EasyExcel.read(fileName, new PageReadListener>>(dataList -> { + log.info("SIZEL:{}", dataList.size()); + })).sheet().doRead(); + + log.info("test"); + + } + + @Test + public void read2() throws Exception { + // 使用输入的文件创建一个新的文件输入流 + //FileInputStream fin = new FileInputStream("/Users/zhuangjiaju/Downloads/1e9e0578a9634abbbbd9b67f338f142a + // .xls"); + // 创建一个新的org.apache.poi.poifs.filesystem.Filesystem + POIFSFileSystem poifs = new POIFSFileSystem( + new File("/Users/zhuangjiaju/Downloads/1e9e0578a9634abbbbd9b67f338f142a.xls")); + // 在InputStream中获取Workbook流 + InputStream din = poifs.createDocumentInputStream("Workbook"); + // 构造出HSSFRequest对象 + HSSFRequest req = new HSSFRequest(); + // 注册全部的监听器 + req.addListenerForAllRecords(new EventExample()); + // 创建事件工厂 + HSSFEventFactory factory = new HSSFEventFactory(); + // 根据文档输入流处理我们监听的事件 + factory.processEvents(req, din); + // 关闭文件输入流 + //fin.close(); + // 关闭文档输入流 + din.close(); + System.out.println("读取结束"); + } + + @Test + public void read3() throws Exception { + HSSFWorkbook hwb = new HSSFWorkbook( + new FileInputStream("/Users/zhuangjiaju/Downloads/1e9e0578a9634abbbbd9b67f338f142a.xls")); + HSSFSheet sheet = hwb.getSheetAt(0); + HSSFRow row = null; + HSSFCell cell = null; + for (int i = sheet.getFirstRowNum(); i <= sheet.getPhysicalNumberOfRows(); i++) { + row = sheet.getRow(i); + if(row!=null){ + log.info("r:{}",row.getRowNum()); + + } + } + + log.info("end"); + } + + public static class EventExample implements HSSFListener { + private SSTRecord sstrec; + + /** + * 此方法监听传入记录并根据需要处理它们 + * + * @param record读取时找到的记录 + */ + public void processRecord(Record record) { + switch (record.getSid()) { + //BOFRecord可以表示工作表或工作簿的开头 + case BOFRecord.sid: + BOFRecord bof = (BOFRecord)record; + if (bof.getType() == bof.TYPE_WORKBOOK) { + System.out.println("监听到工作表"); + } else if (bof.getType() == bof.TYPE_WORKSHEET) { + System.out.println("监听到工作簿"); + } + break; + case BoundSheetRecord.sid: + BoundSheetRecord bsr = (BoundSheetRecord)record; + System.out.println("工作簿名称: " + bsr.getSheetname()); + break; + } + } + } + + @Test + public void test2() throws Exception { + // 方法2 如果写到不同的sheet 同一个对象 + String fileName = TestFileUtil.getPath() + "large" + System.currentTimeMillis() + ".xlsx"; + + ExcelWriter excelWriter = EasyExcel.write(fileName, LargeData.class).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 100; j++) { + excelWriter.write(data(), writeSheet); + LOGGER.info("{} fill success.", j); + } + excelWriter.finish(); + + } + + private List> data() { + List> list = new ArrayList<>(); + + for (int j = 0; j < 10000; j++) { + List oneRow = new ArrayList<>(); + for (int i = 0; i < 150; i++) { + oneRow.add("这是测试字段" + i); + } + list.add(oneRow); + } + + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteV33Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteV33Test.java new file mode 100644 index 000000000..5e83405dd --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteV33Test.java @@ -0,0 +1,148 @@ +package com.alibaba.easyexcel.test.temp; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.util.BooleanUtils; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Workbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class WriteV33Test { + + private static final Logger LOGGER = LoggerFactory.getLogger(WriteV33Test.class); + + @Test + public void handlerStyleWrite() { + // 方法1 使用已有的策略 推荐 + // HorizontalCellStyleStrategy 每一行的样式都一样 或者隔行一样 + // AbstractVerticalCellStyleStrategy 每一列的样式都一样 需要自己回调每一页 + String fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + //// 头的策略 + //WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + //// 背景设置为红色 + //headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + //WriteFont headWriteFont = new WriteFont(); + //headWriteFont.setFontHeightInPoints((short)20); + //headWriteCellStyle.setWriteFont(headWriteFont); + //// 内容的策略 + //WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + //// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 + //contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + //// 背景绿色 + //contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + //WriteFont contentWriteFont = new WriteFont(); + //// 字体大小 + //contentWriteFont.setFontHeightInPoints((short)20); + //contentWriteCellStyle.setWriteFont(contentWriteFont); + //// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + //HorizontalCellStyleStrategy horizontalCellStyleStrategy = + // new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + // + //// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + //EasyExcel.write(fileName, DemoData.class) + // .registerWriteHandler(horizontalCellStyleStrategy) + // .sheet("模板") + // .doWrite(data()); + // + // 方法2: 使用easyexcel的方式完全自己写 不太推荐 尽量使用已有策略 + //fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + //EasyExcel.write(fileName, DemoData.class) + // .registerWriteHandler(new CellWriteHandler() { + // @Override + // public void afterCellDispose(CellWriteHandlerContext context) { + // // 当前事件会在 数据设置到poi的cell里面才会回调 + // // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true + // if (BooleanUtils.isNotTrue(context.getHead())) { + // // 第一个单元格 + // // 只要不是头 一定会有数据 当然fill的情况 可能要context.getCellDataList() ,这个需要看模板,因为一个单元格会有多个 WriteCellData + // WriteCellData cellData = context.getFirstCellData(); + // // 这里需要去cellData 获取样式 + // // 很重要的一个原因是 WriteCellStyle 和 dataFormatData绑定的 简单的说 比如你加了 DateTimeFormat + // // ,已经将writeCellStyle里面的dataFormatData 改了 如果你自己new了一个WriteCellStyle,可能注解的样式就失效了 + // // 然后 getOrCreateStyle 用于返回一个样式,如果为空,则创建一个后返回 + // WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); + // writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + // // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND + // writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // + // // 这样样式就设置好了 后面有个FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到 cell里面去 所以可以不用管了 + // } + // } + // }).sheet("模板") + // .doWrite(data()); + + // 方法3: 使用poi的样式完全自己写 不推荐 + // 坑1:style里面有dataformat 用来格式化数据的 所以自己设置可能导致格式化注解不生效 + // 坑2:不要一直去创建style 记得缓存起来 最多创建6W个就挂了 + fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + EasyExcel.write(fileName, DemoData.class) + .registerWriteHandler(new CellWriteHandler() { + @Override + public void afterCellDispose(CellWriteHandlerContext context) { + // 当前事件会在 数据设置到poi的cell里面才会回调 + // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true + if (BooleanUtils.isNotTrue(context.getHead())) { + Cell cell = context.getCell(); + // 拿到poi的workbook + Workbook workbook = context.getWriteWorkbookHolder().getWorkbook(); + // 这里千万记住 想办法能复用的地方把他缓存起来 一个表格最多创建6W个样式 + // 不同单元格尽量传同一个 cellStyle + CellStyle cellStyle = workbook.createCellStyle(); + cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND + cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + cell.setCellStyle(cellStyle); + + // 由于这里没有指定datafrmat 所以格式化出来的数据需要 + + // 这里要把 WriteCellData的样式清空, 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到 + // cell里面去 会导致自己设置的不一样 + context.getFirstCellData().setWriteCellStyle(null); + } + } + }).sheet("模板") + .doWrite(data()); + } + + private List data() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + i); + data.setDate(new Date()); + data.setDoubleData(0.56); + list.add(data); + } + return list; + } + + + @Test + public void test4() throws Exception{ + Path path= Files.createTempFile(new File("/Users/zhuangjiaju/test/test0422/test/xx").toPath(),System.currentTimeMillis()+"",".jpg"); + System.out.println(path); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteV34Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteV34Test.java new file mode 100644 index 000000000..68d8794be --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/WriteV34Test.java @@ -0,0 +1,88 @@ +package com.alibaba.easyexcel.test.temp; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; + +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class WriteV34Test { + + private static final Logger LOGGER = LoggerFactory.getLogger(WriteV34Test.class); + + @Test + public void test() throws Exception { + String fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx"; + // 头的策略 + WriteCellStyle headWriteCellStyle = new WriteCellStyle(); + // 背景设置为红色 + headWriteCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex()); + WriteFont headWriteFont = new WriteFont(); + headWriteFont.setFontHeightInPoints((short)20); + headWriteCellStyle.setWriteFont(headWriteFont); + // 内容的策略 + WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); + // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定 + contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND); + // 背景绿色 + contentWriteCellStyle.setFillForegroundColor(IndexedColors.GREEN.getIndex()); + WriteFont contentWriteFont = new WriteFont(); + // 字体大小 + contentWriteFont.setFontHeightInPoints((short)20); + contentWriteCellStyle.setWriteFont(contentWriteFont); + // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 + HorizontalCellStyleStrategy horizontalCellStyleStrategy = + new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle); + + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcel.write(fileName, DemoData.class).head(head()).registerWriteHandler(horizontalCellStyleStrategy).sheet( + "模板") + .doWrite(data(1)); + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("字符串" + System.currentTimeMillis()); + head0.add("再找找"); + List head1 = new ArrayList(); + head1.add("数字" + System.currentTimeMillis()); + List head2 = new ArrayList(); + head2.add("日期" + System.currentTimeMillis()); + List head3 = new ArrayList(); + head3.add("日期" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + list.add(head3); + + return list; + } + + private List data(int no) { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("字符串" + no + "---" + i); + list.add(data); + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Xls03Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Xls03Test.java new file mode 100644 index 000000000..a43525ae3 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/Xls03Test.java @@ -0,0 +1,51 @@ +package com.alibaba.easyexcel.test.temp; + +import java.util.List; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.cglib.beans.BeanMap; +import com.alibaba.excel.support.cglib.core.DebuggingClassWriter; +import com.alibaba.excel.util.BeanMapUtils; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class Xls03Test { + private static final Logger LOGGER = LoggerFactory.getLogger(Xls03Test.class); + + @Test + public void test() { + List list = EasyExcel.read("D:\\test\\8.xls").sheet().doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void test2() { + System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); + System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, + "/Users/zhuangjiaju/IdeaProjects/easyexcel/target"); + + CamlData camlData = new CamlData(); + //camlData.setTest("test2"); + //camlData.setAEst("test3"); + //camlData.setTEST("test4"); + + BeanMap beanMap = BeanMapUtils.create(camlData); + + LOGGER.info("test:{}", beanMap.get("test")); + LOGGER.info("test:{}", beanMap.get("Test")); + LOGGER.info("test:{}", beanMap.get("TEst")); + LOGGER.info("test:{}", beanMap.get("TEST")); + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/DataType.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/DataType.java new file mode 100644 index 000000000..b781f9fa8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/DataType.java @@ -0,0 +1,38 @@ +package com.alibaba.easyexcel.test.temp.bug; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author jiaosong + * @desc + * @date 2021/4/6 + */ +@Getter +@Setter +@EqualsAndHashCode +public class DataType { + /** + * 任务id + */ + @ExcelProperty("任务ID") + private Integer id; + + @ExcelProperty("多余字段1") + private String firstSurplus; + + @ExcelProperty("多余字段2") + private String secSurplus; + + @ExcelProperty("多余字段3") + private String thirdSurplus; + + @ExcelProperty(value = "备注1") + private String firstRemark; + + @ExcelProperty(value = "备注2") + private String secRemark; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/ExcelCreat.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/ExcelCreat.java new file mode 100644 index 000000000..f190ea782 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/ExcelCreat.java @@ -0,0 +1,37 @@ +package com.alibaba.easyexcel.test.temp.bug; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.util.Collections; +import java.util.List; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; + +/** + * @author jiaosong + * @desc + * @date 2020/4/6 + */ +public class ExcelCreat { + + public static void main(String[] args) throws FileNotFoundException { + List data = getData(); + ExcelWriter excelWriter = null; + excelWriter = EasyExcel.write(new FileOutputStream("all.xlsx")).build(); + WriteSheet writeSheet = EasyExcel.writerSheet(1, "test") + .head(HeadType.class) + .build(); + excelWriter.write(data, writeSheet); + excelWriter.finish(); + } + + private static List getData() { + DataType vo = new DataType(); + vo.setId(738); + vo.setFirstRemark("1222"); + vo.setSecRemark("22222"); + return Collections.singletonList(vo); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/HeadType.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/HeadType.java new file mode 100644 index 000000000..e04f74a79 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/bug/HeadType.java @@ -0,0 +1,31 @@ +package com.alibaba.easyexcel.test.temp.bug; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author jiaosong + * @desc + * @date 2021/4/6 + */ +@Getter +@Setter +@EqualsAndHashCode +public class HeadType { + + /** + * 任务id + */ + @ExcelProperty("任务ID") + private Integer id; + + @ExcelProperty(value = "备注1") + private String firstRemark; + + @ExcelProperty(value = "备注2") + private String secRemark; + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/cache/CacheTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/cache/CacheTest.java new file mode 100644 index 000000000..7ff376f64 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/cache/CacheTest.java @@ -0,0 +1,51 @@ +package com.alibaba.easyexcel.test.temp.cache; + +import java.io.File; +import java.util.HashMap; +import java.util.UUID; + +import com.alibaba.easyexcel.test.temp.poi.Poi2Test; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.fastjson2.JSON; + +import org.ehcache.Cache; +import org.ehcache.PersistentCacheManager; +import org.ehcache.config.builders.CacheConfigurationBuilder; +import org.ehcache.config.builders.CacheManagerBuilder; +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + **/ +public class CacheTest { + private static final Logger LOGGER = LoggerFactory.getLogger(Poi2Test.class); + + @Test + public void cache() throws Exception { + + File readTempFile = FileUtils.createCacheTmpFile(); + + File cacheFile = new File(readTempFile.getPath(), UUID.randomUUID().toString()); + PersistentCacheManager persistentCacheManager = + CacheManagerBuilder.newCacheManagerBuilder().with(CacheManagerBuilder.persistence(cacheFile)) + .withCache("cache", CacheConfigurationBuilder.newCacheConfigurationBuilder(Integer.class, HashMap.class, + ResourcePoolsBuilder.newResourcePoolsBuilder().disk(10, MemoryUnit.GB))) + .build(true); + Cache cache = persistentCacheManager.getCache("cache", Integer.class, HashMap.class); + + HashMap map = new HashMap(); + map.put(1, "test"); + + cache.put(1, map); + LOGGER.info("dd1:{}", JSON.toJSONString(cache.get(1))); + + cache.clear(); + + LOGGER.info("dd2:{}", JSON.toJSONString(cache.get(1))); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvData.java new file mode 100644 index 000000000..1cc91c72a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvData.java @@ -0,0 +1,32 @@ +package com.alibaba.easyexcel.test.temp.csv; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * TODO + * + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class CsvData { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvDataListeer.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvDataListeer.java new file mode 100644 index 000000000..781675631 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvDataListeer.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.temp.csv; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class CsvDataListeer extends AnalysisEventListener { + @Override + public void invoke(CsvData data, AnalysisContext context) { + log.info("data:{}", JSON.toJSONString(data)); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvReadTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvReadTest.java new file mode 100644 index 000000000..015970c39 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/csv/CsvReadTest.java @@ -0,0 +1,110 @@ +package com.alibaba.easyexcel.test.temp.csv; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import org.apache.commons.csv.CSVRecord; +import org.apache.poi.poifs.filesystem.FileMagic; +import org.junit.jupiter.api.Test; + +@Slf4j +public class CsvReadTest { + + @Test + public void write() throws Exception { + Appendable out = new PrintWriter( + new OutputStreamWriter(new FileOutputStream(TestFileUtil.createNewFile("csvWrite1.csv")))); + CSVPrinter printer = CSVFormat.DEFAULT.withHeader("userId", "userName") + .print(out); + for (int i = 0; i < 10; i++) { + printer.printRecord("userId" + i, "userName" + i); + } + printer.flush(); + printer.close(); + } + + @Test + public void read1() throws Exception { + Iterable records = CSVFormat.DEFAULT.withNullString("").parse( + new FileReader("/Users/zhuangjiaju/IdeaProjects/easyexcel/target/test-classes/t1.csv")); + for (CSVRecord record : records) { + String lastName = record.get(0); + String firstName = record.get(1); + log.info("row:{},{}", lastName, firstName); + } + + } + + @Test + public void csvWrite() throws Exception { + // 写法1 + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".csv"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, CsvData.class).sheet().doWrite(data()); + + // 读 + List list = EasyExcel.read(fileName).sheet(0).headRowNumber(0).doReadSync(); + log.info("数据:{}", list.size()); + for (Object data : list) { + log.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void writev2() throws Exception { + // 写法1 + String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".csv"; + // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, CsvData.class).sheet().doWrite(data()); + + EasyExcel.read(fileName, CsvData.class, new CsvDataListeer()).sheet().doRead(); + } + + @Test + public void writeFile() throws Exception { + FileInputStream fileInputStream = new FileInputStream(new File("/Users/zhuangjiaju/test/test1.csv")); + FileMagic fileMagic = FileMagic.valueOf(fileInputStream); + log.info("{}", fileMagic); + } + + private List data() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + CsvData data = new CsvData(); + data.setString("字符,串" + i); + //data.setDate(new Date()); + data.setDoubleData(0.56); + data.setIgnore("忽略" + i); + list.add(data); + } + return list; + } + + @Test + public void read() { + // + //Iterable records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(in); + //for (CSVRecord record : records) { + // String lastName = record.get("id"); + // String firstName = record.get("name"); + // System.out.println(lastName); + // System.out.println(firstName); + //} + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/data/DataType.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/data/DataType.java new file mode 100644 index 000000000..ef92e62c3 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/data/DataType.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.temp.data; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class DataType { + /** + * 任务id + */ + @ExcelProperty("任务ID") + private Integer id; + + @ExcelProperty("多余字段1") + private String firstSurplus; + + @ExcelProperty("多余字段2") + private String secSurplus; + + @ExcelProperty("多余字段3") + private String thirdSurplus; + + @ExcelProperty(value = "备注1") + private String firstRemark; + + @ExcelProperty(value = "备注2") + private String secRemark; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/data/HeadType.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/data/HeadType.java new file mode 100644 index 000000000..53fd0df4c --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/data/HeadType.java @@ -0,0 +1,26 @@ +package com.alibaba.easyexcel.test.temp.data; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class HeadType { + + /** + * 任务id + */ + @ExcelProperty("任务ID") + private Integer id; + + @ExcelProperty(value = "备注1") + private String firstRemark; + + @ExcelProperty(value = "备注2") + private String secRemark; + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatData.java new file mode 100644 index 000000000..bd7a7acb8 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatData.java @@ -0,0 +1,20 @@ +package com.alibaba.easyexcel.test.temp.dataformat; + +import com.alibaba.excel.metadata.data.ReadCellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * TODO + * + * @author 罗成 + **/ +@Getter +@Setter +@EqualsAndHashCode +public class DataFormatData { + private ReadCellData date; + private ReadCellData num; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatTest.java new file mode 100644 index 000000000..3e7baf74f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatTest.java @@ -0,0 +1,189 @@ +package com.alibaba.easyexcel.test.temp.dataformat; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +import com.alibaba.easyexcel.test.core.dataformat.DateFormatData; +import com.alibaba.easyexcel.test.temp.Lock2Test; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 格式测试 + * + * @author Jiaju Zhuang + **/ + +public class DataFormatTest { + private static final Logger LOGGER = LoggerFactory.getLogger(Lock2Test.class); + + @Test + public void test() throws Exception { + File file = new File("D:\\test\\dataformat.xlsx"); + + List list = + EasyExcel.read(file, DataFormatData.class, null).sheet().headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (DataFormatData data : list) { + Short dataFormat = data.getDate().getDataFormatData().getIndex(); + + String dataFormatString = data.getDate().getFormulaData().getFormulaValue(); + + if (dataFormat == null || dataFormatString == null) { + + } else { + LOGGER.info("格式化:{};{}:{}", dataFormat, dataFormatString, + DateUtil.isADateFormat(dataFormat, dataFormatString)); + } + + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void testxls() throws Exception { + File file = new File("D:\\test\\dataformat.xls"); + + List list = + EasyExcel.read(file, DataFormatData.class, null).sheet().headRowNumber(0).doReadSync(); + LOGGER.info("数据:{}", list.size()); + for (DataFormatData data : list) { + Short dataFormat = data.getDate().getDataFormatData().getIndex(); + + String dataFormatString = data.getDate().getFormulaData().getFormulaValue(); + + if (dataFormat == null || dataFormatString == null) { + + } else { + LOGGER.info("格式化:{};{}:{}", dataFormat, dataFormatString, + DateUtil.isADateFormat(dataFormat, dataFormatString)); + } + + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void test3() throws IOException { + String file = "D:\\test\\dataformat1.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + Cell cell = xssfSheet.getRow(0).getCell(0); + DataFormatter d = new DataFormatter(); + System.out.println(d.formatCellValue(cell)); + } + + @Test + public void test31() throws IOException { + System.out.println(DateUtil.isADateFormat(181, "[DBNum1][$-404]m\"\u6708\"d\"\u65e5\";@")); + } + + @Test + public void test43() throws IOException { + SimpleDateFormat s = new SimpleDateFormat("yyyy'年'm'月'd'日' h'点'mm'哈哈哈m'"); + System.out.println(s.format(new Date())); + } + + @Test + public void test463() throws IOException { + SimpleDateFormat s = new SimpleDateFormat("[$-804]yyyy年m月"); + System.out.println(s.format(new Date())); + } + + @Test + public void test1() throws Exception { + System.out.println(DateUtil.isADateFormat(181, "yyyy\"年啊\"m\"月\"d\"日\"\\ h")); + System.out.println(DateUtil.isADateFormat(180, "yyyy\"年\"m\"月\"d\"日\"\\ h\"点\"")); + } + + @Test + public void test2() throws Exception { + List list1 = new ArrayList(3000); + long start = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + list1.clear(); + } + System.out.println("end:" + (System.currentTimeMillis() - start)); + start = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + list1 = new ArrayList(3000); + } + System.out.println("end:" + (System.currentTimeMillis() - start)); + } + + @Test + public void test355() throws IOException, InvalidFormatException { + File file = TestFileUtil.readFile("dataformat" + File.separator + "dataformat.xlsx"); + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + DataFormatter d = new DataFormatter(Locale.CHINA); + + for (int i = 0; i < xssfSheet.getLastRowNum(); i++) { + Row row = xssfSheet.getRow(i); + System.out.println(d.formatCellValue(row.getCell(0))); + } + + } + + @Test + public void test3556() throws IOException, InvalidFormatException { + String file = "D://test/dataformat1.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + DataFormatter d = new DataFormatter(Locale.CHINA); + + for (int i = 0; i < xssfSheet.getLastRowNum(); i++) { + Row row = xssfSheet.getRow(i); + System.out.println(d.formatCellValue(row.getCell(0))); + } + + } + + @Test + public void tests() throws IOException, InvalidFormatException { + SimpleDateFormat s1 = new SimpleDateFormat("yyyy\"5E74\"m\"6708\"d\"65E5\""); + System.out.println(s1.format(new Date())); + s1 = new SimpleDateFormat("yyyy年m月d日"); + System.out.println(s1.format(new Date())); + } + + @Test + public void tests1() throws IOException, InvalidFormatException { + String file = "D://test/dataformat1.xlsx"; + List list = EasyExcel.read(file, DateFormatData.class, null).sheet().doReadSync(); + for (DateFormatData data : list) { + LOGGER.info("返回:{}", JSON.toJSONString(data)); + } + } + + @Test + public void tests3() throws IOException, InvalidFormatException { + SimpleDateFormat s1 = new SimpleDateFormat("ah\"时\"mm\"分\""); + System.out.println(s1.format(new Date())); + } + + private static final Pattern date_ptrn6 = Pattern.compile("^.*(年|月|日|时|分|秒)+.*$"); + + @Test + public void tests34() throws IOException, InvalidFormatException { + System.out.println(date_ptrn6.matcher("2017但是").matches()); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatter1.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatter1.java new file mode 100644 index 000000000..bb143e853 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/dataformat/DataFormatter1.java @@ -0,0 +1,1287 @@ +/* + * ==================================================================== Licensed to the Apache Software Foundation (ASF) + * under one or more contributor license agreements. See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the + * License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * 2012 - Alfresco Software, Ltd. Alfresco Software has modified source of this file The details of changes as svn diff + * can be found in svn at location root/projects/3rd-party/src + * ==================================================================== + */ +package com.alibaba.easyexcel.test.temp.dataformat; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Observable; +import java.util.Observer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.alibaba.excel.analysis.ExcelAnalyserImpl; +import org.apache.poi.ss.format.CellFormat; +import org.apache.poi.ss.format.CellFormatResult; +import org.apache.poi.ss.formula.ConditionalFormattingEvaluator; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.ExcelGeneralNumberFormat; +import org.apache.poi.ss.usermodel.ExcelNumberFormat; +import org.apache.poi.ss.usermodel.ExcelStyleDateFormatter; +import org.apache.poi.ss.usermodel.FormulaError; +import org.apache.poi.ss.usermodel.FormulaEvaluator; +import org.apache.poi.ss.usermodel.FractionFormat; +import org.apache.poi.ss.util.DateFormatConverter; +import org.apache.poi.ss.util.NumberToTextConverter; +import org.apache.poi.util.LocaleUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * DataFormatter contains methods for formatting the value stored in an Cell. This can be useful for reports and GUI + * presentations when you need to display data exactly as it appears in Excel. Supported formats include currency, SSN, + * percentages, decimals, dates, phone numbers, zip codes, etc. + *

+ * Internally, formats will be implemented using subclasses of {@link Format} such as {@link DecimalFormat} and + * {@link java.text.SimpleDateFormat}. Therefore the formats used by this class must obey the same pattern rules as + * these Format subclasses. This means that only legal number pattern characters ("0", "#", ".", "," etc.) may appear in + * number formats. Other characters can be inserted before or after the number pattern to form a + * prefix or suffix. + *

+ *

+ * For example the Excel pattern "$#,##0.00 "USD"_);($#,##0.00 "USD")" + * will be correctly formatted as "$1,000.00 USD" or "($1,000.00 USD)". However the pattern + * "00-00-00" is incorrectly formatted by DecimalFormat as "000000--". For Excel formats that are not + * compatible with DecimalFormat, you can provide your own custom {@link Format} implementation via + * DataFormatter.addFormat(String,Format). The following custom formats are already provided by this class: + *

+ * + *
+ * 
  • SSN "000-00-0000"
  • + *
  • Phone Number "(###) ###-####"
  • + *
  • Zip plus 4 "00000-0000"
  • + *
+ *
+ *

+ * If the Excel format pattern cannot be parsed successfully, then a default format will be used. The default number + * format will mimic the Excel General format: "#" for whole numbers and "#.##########" for decimal numbers. You can + * override the default format pattern with + * DataFormatter.setDefaultNumberFormat(Format). Note: the default format will only be used when a Format + * cannot be created from the cell's data format string. + * + *

+ * Note that by default formatted numeric values are trimmed. Excel formats can contain spacers and padding and the + * default behavior is to strip them off. + *

+ *

+ * Example: + *

+ *

+ * Consider a numeric cell with a value 12.343 and format "##.##_ ". The trailing underscore + * and space ("_ ") in the format adds a space to the end and Excel formats this cell as "12.34 ", but + * DataFormatter trims the formatted value and returns "12.34". + *

+ * You can enable spaces by passing the emulateCSV=true flag in the DateFormatter cosntructor. + * If set to true, then the output tries to conform to what you get when you take an xls or xlsx in Excel and Save As + * CSV file: + *
    + *
  • returned values are not trimmed
  • + *
  • Invalid dates are formatted as 255 pound signs ("#")
  • + *
  • simulate Excel's handling of a format string of all # when the value is 0. Excel will output "", + * DataFormatter will output "0". + *
+ *

+ * Some formats are automatically "localized" by Excel, eg show as mm/dd/yyyy when loaded in Excel in some Locales but + * as dd/mm/yyyy in others. These are always returned in the "default" (US) format, as stored in the file. Some format + * strings request an alternate locale, eg [$-809]d/m/yy h:mm AM/PM which explicitly requests UK locale. + * These locale directives are (currently) ignored. You can use {@link DateFormatConverter} to do some of this + * localisation if you need it. + */ +public class DataFormatter1 implements Observer { + private static final String defaultFractionWholePartFormat = "#"; + private static final String defaultFractionFractionPartFormat = "#/##"; + /** + * Pattern to find a number format: "0" or "#" + */ + private static final Pattern numPattern = Pattern.compile("[0#]+"); + + /** + * Pattern to find days of week as text "ddd...." + */ + private static final Pattern daysAsText = Pattern.compile("([d]{3,})", Pattern.CASE_INSENSITIVE); + + /** + * Pattern to find "AM/PM" marker + */ + private static final Pattern amPmPattern = Pattern.compile("((A|P)[M/P]*)", Pattern.CASE_INSENSITIVE); + + /** + * Pattern to find formats with condition ranges e.g. [>=100] + */ + private static final Pattern rangeConditionalPattern = + Pattern.compile(".*\\[\\s*(>|>=|<|<=|=)\\s*[0-9]*\\.*[0-9].*"); + + /** + * A regex to find locale patterns like [$$-1009] and [$?-452]. Note that we don't currently process these into + * locales + */ + private static final Pattern localePatternGroup = Pattern.compile("(\\[\\$[^-\\]]*-[0-9A-Z]+\\])"); + + /** + * A regex to match the colour formattings rules. Allowed colours are: Black, Blue, Cyan, Green, Magenta, Red, + * White, Yellow, "Color n" (1<=n<=56) + */ + private static final Pattern colorPattern = Pattern.compile("(\\[BLACK\\])|(\\[BLUE\\])|(\\[CYAN\\])|(\\[GREEN\\])|" + + "(\\[MAGENTA\\])|(\\[RED\\])|(\\[WHITE\\])|(\\[YELLOW\\])|" + + "(\\[COLOR\\s*\\d\\])|(\\[COLOR\\s*[0-5]\\d\\])", Pattern.CASE_INSENSITIVE); + + /** + * A regex to identify a fraction pattern. This requires that replaceAll("\\?", "#") has already been called + */ + private static final Pattern fractionPattern = Pattern.compile("(?:([#\\d]+)\\s+)?(#+)\\s*\\/\\s*([#\\d]+)"); + + /** + * A regex to strip junk out of fraction formats + */ + private static final Pattern fractionStripper = Pattern.compile("(\"[^\"]*\")|([^ \\?#\\d\\/]+)"); + + /** + * A regex to detect if an alternate grouping character is used in a numeric format + */ + private static final Pattern alternateGrouping = Pattern.compile("([#0]([^.#0])[#0]{3})"); + + /** + * Cells formatted with a date or time format and which contain invalid date or time values show 255 pound signs + * ("#"). + */ + private static final String invalidDateTimeString; + + static { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < 255; i++) {buf.append('#');} + invalidDateTimeString = buf.toString(); + } + + /** + * The decimal symbols of the locale used for formatting values. + */ + private DecimalFormatSymbols decimalSymbols; + + /** + * The date symbols of the locale used for formatting values. + */ + private DateFormatSymbols dateSymbols; + + /** + * A default date format, if no date format was given + */ + private DateFormat defaultDateformat; + + /** + * General format for numbers. + */ + private Format generalNumberFormat; + + /** + * A default format to use when a number pattern cannot be parsed. + */ + private Format defaultNumFormat; + + /** + * A map to cache formats. Map formats + */ + private final Map formats = new HashMap(); + + private final boolean emulateCSV; + + /** + * stores the locale valid it the last formatting call + */ + private Locale locale; + + /** + * stores if the locale should change according to {@link LocaleUtil#getUserLocale()} + */ + private boolean localeIsAdapting; + + private class LocaleChangeObservable extends Observable { + void checkForLocaleChange() { + checkForLocaleChange(LocaleUtil.getUserLocale()); + } + + void checkForLocaleChange(Locale newLocale) { + if (!localeIsAdapting) {return;} + if (newLocale.equals(locale)) {return;} + super.setChanged(); + notifyObservers(newLocale); + } + } + + /** + * the Observable to notify, when the locale has been changed + */ + private final LocaleChangeObservable localeChangedObservable = new LocaleChangeObservable(); + + /** + * For logging any problems we find + */ + private static final Logger logger = LoggerFactory.getLogger(ExcelAnalyserImpl.class); + + /** + * Creates a formatter using the {@link Locale#getDefault() default locale}. + */ + public DataFormatter1() { + this(false); + } + + /** + * Creates a formatter using the {@link Locale#getDefault() default locale}. + * + * @param emulateCSV whether to emulate CSV output. + */ + public DataFormatter1(boolean emulateCSV) { + this(LocaleUtil.getUserLocale(), true, emulateCSV); + } + + /** + * Creates a formatter using the given locale. + */ + public DataFormatter1(Locale locale) { + this(locale, false); + } + + /** + * Creates a formatter using the given locale. + * + * @param emulateCSV whether to emulate CSV output. + */ + public DataFormatter1(Locale locale, boolean emulateCSV) { + this(locale, false, emulateCSV); + } + + /** + * Creates a formatter using the given locale. + * + * @param localeIsAdapting (true only if locale is not user-specified) + * @param emulateCSV whether to emulate CSV output. + */ + private DataFormatter1(Locale locale, boolean localeIsAdapting, boolean emulateCSV) { + this.localeIsAdapting = true; + localeChangedObservable.addObserver(this); + // localeIsAdapting must be true prior to this first checkForLocaleChange call. + localeChangedObservable.checkForLocaleChange(locale); + // set localeIsAdapting so subsequent checks perform correctly + // (whether a specific locale was provided to this DataFormatter or DataFormatter should + // adapt to the current user locale as the locale changes) + this.localeIsAdapting = localeIsAdapting; + this.emulateCSV = emulateCSV; + } + + /** + * Return a Format for the given cell if one exists, otherwise try to create one. This method will return + * null if the any of the following is true: + *

    + *
  • the cell's style is null
  • + *
  • the style's data format string is null or empty
  • + *
  • the format string cannot be recognized as either a number or date
  • + *
+ * + * @param cell The cell to retrieve a Format for + * @return A Format for the format String + */ + private Format getFormat(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { + if (cell == null) {return null;} + + ExcelNumberFormat numFmt = ExcelNumberFormat.from(cell, cfEvaluator); + + if (numFmt == null) { + return null; + } + + int formatIndex = numFmt.getIdx(); + String formatStr = numFmt.getFormat(); + if (formatStr == null || formatStr.trim().length() == 0) { + return null; + } + return getFormat(cell.getNumericCellValue(), formatIndex, formatStr); + } + + private Format getFormat(double cellValue, int formatIndex, String formatStrIn) { + localeChangedObservable.checkForLocaleChange(); + + // Might be better to separate out the n p and z formats, falling back to p when n and z are not set. + // That however would require other code to be re factored. + // String[] formatBits = formatStrIn.split(";"); + // int i = cellValue > 0.0 ? 0 : cellValue < 0.0 ? 1 : 2; + // String formatStr = (i < formatBits.length) ? formatBits[i] : formatBits[0]; + + String formatStr = formatStrIn; + + // Excel supports 2+ part conditional data formats, eg positive/negative/zero, + // or (>1000),(>0),(0),(negative). As Java doesn't handle these kinds + // of different formats for different ranges, just +ve/-ve, we need to + // handle these ourselves in a special way. + // For now, if we detect 2+ parts, we call out to CellFormat to handle it + // TODO Going forward, we should really merge the logic between the two classes + if (formatStr.contains(";") && (formatStr.indexOf(';') != formatStr.lastIndexOf(';') + || rangeConditionalPattern.matcher(formatStr).matches())) { + try { + // Ask CellFormat to get a formatter for it + CellFormat cfmt = CellFormat.getInstance(locale, formatStr); + // CellFormat requires callers to identify date vs not, so do so + Object cellValueO = Double.valueOf(cellValue); + if (DateUtil.isADateFormat(formatIndex, formatStr) && + // don't try to handle Date value 0, let a 3 or 4-part format take care of it + ((Double)cellValueO).doubleValue() != 0.0) { + cellValueO = DateUtil.getJavaDate(cellValue); + } + // Wrap and return (non-cachable - CellFormat does that) + return new CellFormatResultWrapper(cfmt.apply(cellValueO)); + } catch (Exception e) { + logger.warn("Formatting failed for format {}, falling back", formatStr, e); + } + } + + // Excel's # with value 0 will output empty where Java will output 0. This hack removes the # from the format. + if (emulateCSV && cellValue == 0.0 && formatStr.contains("#") && !formatStr.contains("0")) { + formatStr = formatStr.replaceAll("#", ""); + } + + // See if we already have it cached + Format format = formats.get(formatStr); + if (format != null) { + return format; + } + + // Is it one of the special built in types, General or @? + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + return generalNumberFormat; + } + + // Build a formatter, and cache it + format = createFormat(cellValue, formatIndex, formatStr); + formats.put(formatStr, format); + return format; + } + + /** + * Create and return a Format based on the format string from a cell's style. If the pattern cannot be parsed, + * return a default pattern. + * + * @param cell The Excel cell + * @return A Format representing the excel format. May return null. + */ + public Format createFormat(Cell cell) { + + int formatIndex = cell.getCellStyle().getDataFormat(); + String formatStr = cell.getCellStyle().getDataFormatString(); + return createFormat(cell.getNumericCellValue(), formatIndex, formatStr); + } + + private Format createFormat(double cellValue, int formatIndex, String sFormat) { + localeChangedObservable.checkForLocaleChange(); + + String formatStr = sFormat; + + // Remove colour formatting if present + Matcher colourM = colorPattern.matcher(formatStr); + while (colourM.find()) { + String colour = colourM.group(); + + // Paranoid replacement... + int at = formatStr.indexOf(colour); + if (at == -1) {break;} + String nFormatStr = formatStr.substring(0, at) + formatStr.substring(at + colour.length()); + if (nFormatStr.equals(formatStr)) {break;} + + // Try again in case there's multiple + formatStr = nFormatStr; + colourM = colorPattern.matcher(formatStr); + } + + // Strip off the locale information, we use an instance-wide locale for everything + Matcher m = localePatternGroup.matcher(formatStr); + while (m.find()) { + String match = m.group(); + String symbol = match.substring(match.indexOf('$') + 1, match.indexOf('-')); + if (symbol.indexOf('$') > -1) { + symbol = symbol.substring(0, symbol.indexOf('$')) + '\\' + + symbol.substring(symbol.indexOf('$'), symbol.length()); + } + formatStr = m.replaceAll(symbol); + m = localePatternGroup.matcher(formatStr); + } + + // Check for special cases + if (formatStr == null || formatStr.trim().length() == 0) { + return getDefaultFormat(cellValue); + } + + if ("General".equalsIgnoreCase(formatStr) || "@".equals(formatStr)) { + return generalNumberFormat; + } + + if ("".equals("") || (DateUtil.isADateFormat(formatIndex, formatStr) && DateUtil.isValidExcelDate(cellValue))) { + return createDateFormat(formatStr, cellValue); + } + // Excel supports fractions in format strings, which Java doesn't + if (formatStr.contains("#/") || formatStr.contains("?/")) { + String[] chunks = formatStr.split(";"); + for (String chunk1 : chunks) { + String chunk = chunk1.replaceAll("\\?", "#"); + Matcher matcher = fractionStripper.matcher(chunk); + chunk = matcher.replaceAll(" "); + chunk = chunk.replaceAll(" +", " "); + Matcher fractionMatcher = fractionPattern.matcher(chunk); + // take the first match + if (fractionMatcher.find()) { + String wholePart = (fractionMatcher.group(1) == null) ? "" : defaultFractionWholePartFormat; + return new FractionFormat(wholePart, fractionMatcher.group(3)); + } + } + + // Strip custom text in quotes and escaped characters for now as it can cause performance problems in + // fractions. + // String strippedFormatStr = formatStr.replaceAll("\\\\ ", " ").replaceAll("\\\\.", + // "").replaceAll("\"[^\"]*\"", " ").replaceAll("\\?", "#"); + // System.out.println("formatStr: "+strippedFormatStr); + return new FractionFormat(defaultFractionWholePartFormat, defaultFractionFractionPartFormat); + } + + if (numPattern.matcher(formatStr).find()) { + return createNumberFormat(formatStr, cellValue); + } + + if (emulateCSV) { + return new ConstantStringFormat(cleanFormatForNumber(formatStr)); + } + // TODO - when does this occur? + return null; + } + + private Format createDateFormat(String pFormatStr, double cellValue) { + String formatStr = pFormatStr; + formatStr = formatStr.replaceAll("\\\\-", "-"); + formatStr = formatStr.replaceAll("\\\\,", ","); + formatStr = formatStr.replaceAll("\\\\\\.", "."); // . is a special regexp char + formatStr = formatStr.replaceAll("\\\\ ", " "); + formatStr = formatStr.replaceAll("\\\\/", "/"); // weird: m\\/d\\/yyyy + formatStr = formatStr.replaceAll(";@", ""); + formatStr = formatStr.replaceAll("\"/\"", "/"); // "/" is escaped for no reason in: mm"/"dd"/"yyyy + formatStr = formatStr.replace("\"\"", "'"); // replace Excel quoting with Java style quoting + formatStr = formatStr.replaceAll("\\\\T", "'T'"); // Quote the T is iso8601 style dates + + boolean hasAmPm = false; + Matcher amPmMatcher = amPmPattern.matcher(formatStr); + while (amPmMatcher.find()) { + formatStr = amPmMatcher.replaceAll("@"); + hasAmPm = true; + amPmMatcher = amPmPattern.matcher(formatStr); + } + formatStr = formatStr.replaceAll("@", "a"); + + Matcher dateMatcher = daysAsText.matcher(formatStr); + if (dateMatcher.find()) { + String match = dateMatcher.group(0).toUpperCase(Locale.ROOT).replaceAll("D", "E"); + formatStr = dateMatcher.replaceAll(match); + } + + // Convert excel date format to SimpleDateFormat. + // Excel uses lower and upper case 'm' for both minutes and months. + // From Excel help: + /* + The "m" or "mm" code must appear immediately after the "h" or"hh" + code or immediately before the "ss" code; otherwise, Microsoft + Excel displays the month instead of minutes." + */ + + StringBuilder sb = new StringBuilder(); + char[] chars = formatStr.toCharArray(); + boolean mIsMonth = true; + List ms = new ArrayList(); + boolean isElapsed = false; + for (int j = 0; j < chars.length; j++) { + char c = chars[j]; + if (c == '\'') { + sb.append(c); + j++; + + // skip until the next quote + while (j < chars.length) { + c = chars[j]; + sb.append(c); + if (c == '\'') { + break; + } + j++; + } + } else if (c == '[' && !isElapsed) { + isElapsed = true; + mIsMonth = false; + sb.append(c); + } else if (c == ']' && isElapsed) { + isElapsed = false; + sb.append(c); + } else if (isElapsed) { + if (c == 'h' || c == 'H') { + sb.append('H'); + } else if (c == 'm' || c == 'M') { + sb.append('m'); + } else if (c == 's' || c == 'S') { + sb.append('s'); + } else { + sb.append(c); + } + } else if (c == 'h' || c == 'H') { + mIsMonth = false; + if (hasAmPm) { + sb.append('h'); + } else { + sb.append('H'); + } + } else if (c == 'm' || c == 'M') { + if (mIsMonth) { + sb.append('M'); + ms.add(Integer.valueOf(sb.length() - 1)); + } else { + sb.append('m'); + } + } else if (c == 's' || c == 'S') { + sb.append('s'); + // if 'M' precedes 's' it should be minutes ('m') + for (int index : ms) { + if (sb.charAt(index) == 'M') { + sb.replace(index, index + 1, "m"); + } + } + mIsMonth = true; + ms.clear(); + } else if (Character.isLetter(c)) { + mIsMonth = true; + ms.clear(); + if (c == 'y' || c == 'Y') { + sb.append('y'); + } else if (c == 'd' || c == 'D') { + sb.append('d'); + } else { + sb.append(c); + } + } else { + if (Character.isWhitespace(c)) { + ms.clear(); + } + sb.append(c); + } + } + formatStr = sb.toString(); + + try { + return new ExcelStyleDateFormatter(formatStr, dateSymbols); + } catch (IllegalArgumentException iae) { + logger.debug("Formatting failed for format {}, falling back", formatStr, iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(cellValue); + } + + } + + private String cleanFormatForNumber(String formatStr) { + StringBuilder sb = new StringBuilder(formatStr); + + if (emulateCSV) { + // Requested spacers with "_" are replaced by a single space. + // Full-column-width padding "*" are removed. + // Not processing fractions at this time. Replace ? with space. + // This matches CSV output. + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + if (c == '_' || c == '*' || c == '?') { + if (i > 0 && sb.charAt((i - 1)) == '\\') { + // It's escaped, don't worry + continue; + } + if (c == '?') { + sb.setCharAt(i, ' '); + } else if (i < sb.length() - 1) { + // Remove the character we're supposed + // to match the space of / pad to the + // column width with + if (c == '_') { + sb.setCharAt(i + 1, ' '); + } else { + sb.deleteCharAt(i + 1); + } + // Remove the character too + sb.deleteCharAt(i); + i--; + } + } + } + } else { + // If they requested spacers, with "_", + // remove those as we don't do spacing + // If they requested full-column-width + // padding, with "*", remove those too + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + if (c == '_' || c == '*') { + if (i > 0 && sb.charAt((i - 1)) == '\\') { + // It's escaped, don't worry + continue; + } + if (i < sb.length() - 1) { + // Remove the character we're supposed + // to match the space of / pad to the + // column width with + sb.deleteCharAt(i + 1); + } + // Remove the _ too + sb.deleteCharAt(i); + i--; + } + } + } + + // Now, handle the other aspects like + // quoting and scientific notation + for (int i = 0; i < sb.length(); i++) { + char c = sb.charAt(i); + // remove quotes and back slashes + if (c == '\\' || c == '"') { + sb.deleteCharAt(i); + i--; + + // for scientific/engineering notation + } else if (c == '+' && i > 0 && sb.charAt(i - 1) == 'E') { + sb.deleteCharAt(i); + i--; + } + } + + return sb.toString(); + } + + private static class InternalDecimalFormatWithScale extends Format { + + private static final Pattern endsWithCommas = Pattern.compile("(,+)$"); + private BigDecimal divider; + private static final BigDecimal ONE_THOUSAND = new BigDecimal(1000); + private final DecimalFormat df; + + private static final String trimTrailingCommas(String s) { + return s.replaceAll(",+$", ""); + } + + public InternalDecimalFormatWithScale(String pattern, DecimalFormatSymbols symbols) { + df = new DecimalFormat(trimTrailingCommas(pattern), symbols); + setExcelStyleRoundingMode(df); + Matcher endsWithCommasMatcher = endsWithCommas.matcher(pattern); + if (endsWithCommasMatcher.find()) { + String commas = (endsWithCommasMatcher.group(1)); + BigDecimal temp = BigDecimal.ONE; + for (int i = 0; i < commas.length(); ++i) { + temp = temp.multiply(ONE_THOUSAND); + } + divider = temp; + } else { + divider = null; + } + } + + private Object scaleInput(Object obj) { + if (divider != null) { + if (obj instanceof BigDecimal) { + obj = ((BigDecimal)obj).divide(divider, RoundingMode.HALF_UP); + } else if (obj instanceof Double) { + obj = (Double)obj / divider.doubleValue(); + } else { + throw new UnsupportedOperationException(); + } + } + return obj; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + obj = scaleInput(obj); + return df.format(obj, toAppendTo, pos); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException(); + } + } + + private Format createNumberFormat(String formatStr, double cellValue) { + String format = cleanFormatForNumber(formatStr); + DecimalFormatSymbols symbols = decimalSymbols; + + // Do we need to change the grouping character? + // eg for a format like #'##0 which wants 12'345 not 12,345 + Matcher agm = alternateGrouping.matcher(format); + if (agm.find()) { + char grouping = agm.group(2).charAt(0); + // Only replace the grouping character if it is not the default + // grouping character for the US locale (',') in order to enable + // correct grouping for non-US locales. + if (grouping != ',') { + symbols = DecimalFormatSymbols.getInstance(locale); + + symbols.setGroupingSeparator(grouping); + String oldPart = agm.group(1); + String newPart = oldPart.replace(grouping, ','); + format = format.replace(oldPart, newPart); + } + } + + try { + return new InternalDecimalFormatWithScale(format, symbols); + } catch (IllegalArgumentException iae) { + logger.debug("Formatting failed for format {}, falling back", formatStr, iae); + // the pattern could not be parsed correctly, + // so fall back to the default number format + return getDefaultFormat(cellValue); + } + } + + /** + * Returns a default format for a cell. + * + * @param cell The cell + * @return a default format + */ + public Format getDefaultFormat(Cell cell) { + return getDefaultFormat(cell.getNumericCellValue()); + } + + private Format getDefaultFormat(double cellValue) { + localeChangedObservable.checkForLocaleChange(); + + // for numeric cells try user supplied default + if (defaultNumFormat != null) { + return defaultNumFormat; + + // otherwise use general format + } + return generalNumberFormat; + } + + /** + * Performs Excel-style date formatting, using the supplied Date and format + */ + private String performDateFormatting(Date d, Format dateFormat) { + return (dateFormat != null ? dateFormat : defaultDateformat).format(d); + } + + /** + * Returns the formatted value of an Excel date as a String based on the cell's DataFormat. + * i.e. "Thursday, January 02, 2003" , "01/02/2003" , "02-Jan" , etc. + *

+ * If any conditional format rules apply, the highest priority with a number format is used. If no rules contain a + * number format, or no rules apply, the cell's style format is used. If the style does not have a format, the + * default date format is applied. + * + * @param cell to format + * @param cfEvaluator ConditionalFormattingEvaluator (if available) + * @return Formatted value + */ + private String getFormattedDateString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { + Format dateFormat = getFormat(cell, cfEvaluator); + if (dateFormat instanceof ExcelStyleDateFormatter) { + // Hint about the raw excel value + ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(cell.getNumericCellValue()); + } + Date d = cell.getDateCellValue(); + return performDateFormatting(d, dateFormat); + } + + /** + * Returns the formatted value of an Excel number as a String based on the cell's DataFormat. + * Supported formats include currency, percents, decimals, phone number, SSN, etc.: "61.54%", "$100.00", "(800) + * 555-1234". + *

+ * Format comes from either the highest priority conditional format rule with a specified format, or from the cell + * style. + * + * @param cell The cell + * @param cfEvaluator if available, or null + * @return a formatted number string + */ + private String getFormattedNumberString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) { + + Format numberFormat = getFormat(cell, cfEvaluator); + double d = cell.getNumericCellValue(); + if (numberFormat == null) { + return String.valueOf(d); + } + String formatted = numberFormat.format(new Double(d)); + return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation + } + + /** + * Formats the given raw cell value, based on the supplied format index and string, according to excel style rules. + * + * @see #formatCellValue(Cell) + */ + public String formatRawCellContents(double value, int formatIndex, String formatString) { + return formatRawCellContents(value, formatIndex, formatString, false); + } + + /** + * Formats the given raw cell value, based on the supplied format index and string, according to excel style rules. + * + * @see #formatCellValue(Cell) + */ + public String formatRawCellContents(double value, int formatIndex, String formatString, boolean use1904Windowing) { + localeChangedObservable.checkForLocaleChange(); + + // Is it a date? + if (DateUtil.isADateFormat(formatIndex, formatString)) { + if (DateUtil.isValidExcelDate(value)) { + Format dateFormat = getFormat(value, formatIndex, formatString); + if (dateFormat instanceof ExcelStyleDateFormatter) { + // Hint about the raw excel value + ((ExcelStyleDateFormatter)dateFormat).setDateToBeFormatted(value); + } + Date d = DateUtil.getJavaDate(value, use1904Windowing); + return performDateFormatting(d, dateFormat); + } + // RK: Invalid dates are 255 #s. + if (emulateCSV) { + return invalidDateTimeString; + } + } + + // else Number + Format numberFormat = getFormat(value, formatIndex, formatString); + if (numberFormat == null) { + return String.valueOf(value); + } + + // When formatting 'value', double to text to BigDecimal produces more + // accurate results than double to Double in JDK8 (as compared to + // previous versions). However, if the value contains E notation, this + // would expand the values, which we do not want, so revert to + // original method. + String result; + final String textValue = NumberToTextConverter.toText(value); + if (textValue.indexOf('E') > -1) { + result = numberFormat.format(new Double(value)); + } else { + result = numberFormat.format(new BigDecimal(textValue)); + } + // Complete scientific notation by adding the missing +. + if (result.indexOf('E') > -1 && !result.contains("E-")) { + result = result.replaceFirst("E", "E+"); + } + return result; + } + + /** + *

+ * Returns the formatted value of a cell as a String regardless of the cell type. If the Excel format + * pattern cannot be parsed then the cell value will be formatted using a default format. + *

+ *

+ * When passed a null or blank cell, this method will return an empty String (""). Formulas in formula type cells + * will not be evaluated. + *

+ * + * @param cell The cell + * @return the formatted cell value as a String + */ + public String formatCellValue(Cell cell) { + return formatCellValue(cell, null); + } + + /** + *

+ * Returns the formatted value of a cell as a String regardless of the cell type. If the Excel number + * format pattern cannot be parsed then the cell value will be formatted using a default format. + *

+ *

+ * When passed a null or blank cell, this method will return an empty String (""). Formula cells will be evaluated + * using the given {@link FormulaEvaluator} if the evaluator is non-null. If the evaluator is null, then the formula + * String will be returned. The caller is responsible for setting the currentRow on the evaluator + *

+ * + * @param cell The cell (can be null) + * @param evaluator The FormulaEvaluator (can be null) + * @return a string value of the cell + */ + public String formatCellValue(Cell cell, FormulaEvaluator evaluator) { + return formatCellValue(cell, evaluator, null); + } + + /** + *

+ * Returns the formatted value of a cell as a String regardless of the cell type. If the Excel number + * format pattern cannot be parsed then the cell value will be formatted using a default format. + *

+ *

+ * When passed a null or blank cell, this method will return an empty String (""). Formula cells will be evaluated + * using the given {@link FormulaEvaluator} if the evaluator is non-null. If the evaluator is null, then the formula + * String will be returned. The caller is responsible for setting the currentRow on the evaluator + *

+ *

+ * When a ConditionalFormattingEvaluator is present, it is checked first to see if there is a number format to + * apply. If multiple rules apply, the last one is used. If no ConditionalFormattingEvaluator is present, no rules + * apply, or the applied rules do not define a format, the cell's style format is used. + *

+ *

+ * The two evaluators should be from the same context, to avoid inconsistencies in cached values. + *

+ * + * @param cell The cell (can be null) + * @param evaluator The FormulaEvaluator (can be null) + * @param cfEvaluator ConditionalFormattingEvaluator (can be null) + * @return a string value of the cell + */ + public String formatCellValue(Cell cell, FormulaEvaluator evaluator, ConditionalFormattingEvaluator cfEvaluator) { + localeChangedObservable.checkForLocaleChange(); + + if (cell == null) { + return ""; + } + + CellType cellType = cell.getCellType(); + if (cellType == CellType.FORMULA) { + if (evaluator == null) { + return cell.getCellFormula(); + } + cellType = evaluator.evaluateFormulaCell(cell); + } + switch (cellType) { + case NUMERIC: + + // if (DateUtil.isCellDateFormatted(cell, cfEvaluator)) { + return getFormattedDateString(cell, cfEvaluator); + // } + // return getFormattedNumberString(cell, cfEvaluator); + + case STRING: + return cell.getRichStringCellValue().getString(); + + case BOOLEAN: + return cell.getBooleanCellValue() ? "TRUE" : "FALSE"; + case BLANK: + return ""; + case ERROR: + return FormulaError.forInt(cell.getErrorCellValue()).getString(); + default: + throw new RuntimeException("Unexpected celltype (" + cellType + ")"); + } + } + + /** + *

+ * Sets a default number format to be used when the Excel format cannot be parsed successfully. Note: This is + * a fall back for when an error occurs while parsing an Excel number format pattern. This will not affect cells + * with the General format. + *

+ *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param format A Format instance to be used as a default + * @see java.text.Format#format + */ + public void setDefaultNumberFormat(Format format) { + for (Map.Entry entry : formats.entrySet()) { + if (entry.getValue() == generalNumberFormat) { + entry.setValue(format); + } + } + defaultNumFormat = format; + } + + /** + * Adds a new format to the available formats. + *

+ * The value that will be passed to the Format's format method (specified by java.text.Format#format) + * will be a double value from a numeric cell. Therefore the code in the format method should expect a + * Number value. + *

+ * + * @param excelFormatStr The data format string + * @param format A Format instance + */ + public void addFormat(String excelFormatStr, Format format) { + formats.put(excelFormatStr, format); + } + + // Some custom formats + + /** + * @return a DecimalFormat with parseIntegerOnly set true + */ + private static DecimalFormat createIntegerOnlyFormat(String fmt) { + DecimalFormatSymbols dsf = DecimalFormatSymbols.getInstance(Locale.ROOT); + DecimalFormat result = new DecimalFormat(fmt, dsf); + result.setParseIntegerOnly(true); + return result; + } + + /** + * Enables excel style rounding mode (round half up) on the Decimal Format given. + */ + public static void setExcelStyleRoundingMode(DecimalFormat format) { + setExcelStyleRoundingMode(format, RoundingMode.HALF_UP); + } + + /** + * Enables custom rounding mode on the given Decimal Format. + * + * @param format DecimalFormat + * @param roundingMode RoundingMode + */ + public static void setExcelStyleRoundingMode(DecimalFormat format, RoundingMode roundingMode) { + format.setRoundingMode(roundingMode); + } + + /** + * If the Locale has been changed via {@link LocaleUtil#setUserLocale(Locale)} the stored formats need to be + * refreshed. All formats which aren't originated from DataFormatter itself, i.e. all Formats added via + * {@link DataFormatter#addFormat(String, Format)} and {@link DataFormatter#setDefaultNumberFormat(Format)}, need to + * be added again. To notify callers, the returned {@link Observable} should be used. The Object in + * {@link Observer#update(Observable, Object)} is the new Locale. + * + * @return the listener object, where callers can register themselves + */ + public Observable getLocaleChangedObservable() { + return localeChangedObservable; + } + + /** + * Update formats when locale has been changed + * + * @param observable usually this is our own Observable instance + * @param localeObj only reacts on Locale objects + */ + public void update(Observable observable, Object localeObj) { + if (!(localeObj instanceof Locale)) {return;} + Locale newLocale = (Locale)localeObj; + if (!localeIsAdapting || newLocale.equals(locale)) {return;} + + locale = newLocale; + + dateSymbols = DateFormatSymbols.getInstance(locale); + decimalSymbols = DecimalFormatSymbols.getInstance(locale); + generalNumberFormat = new ExcelGeneralNumberFormat(locale); + + // taken from Date.toString() + defaultDateformat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", dateSymbols); + defaultDateformat.setTimeZone(LocaleUtil.getUserTimeZone()); + + // init built-in formats + + formats.clear(); + Format zipFormat = ZipPlusFourFormat.instance; + addFormat("00000\\-0000", zipFormat); + addFormat("00000-0000", zipFormat); + + Format phoneFormat = PhoneFormat.instance; + // allow for format string variations + addFormat("[<=9999999]###\\-####;\\(###\\)\\ ###\\-####", phoneFormat); + addFormat("[<=9999999]###-####;(###) ###-####", phoneFormat); + addFormat("###\\-####;\\(###\\)\\ ###\\-####", phoneFormat); + addFormat("###-####;(###) ###-####", phoneFormat); + + Format ssnFormat = SSNFormat.instance; + addFormat("000\\-00\\-0000", ssnFormat); + addFormat("000-00-0000", ssnFormat); + } + + /** + * Format class for Excel's SSN format. This class mimics Excel's built-in SSN formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class SSNFormat extends Format { + public static final Format instance = new SSNFormat(); + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private SSNFormat() { + // enforce singleton + } + + /** + * Format a number as an SSN + */ + public static String format(Number num) { + String result = df.format(num); + return result.substring(0, 3) + '-' + result.substring(3, 5) + '-' + result.substring(5, 9); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel Zip + 4 format. This class mimics Excel's built-in formatting for Zip + 4. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class ZipPlusFourFormat extends Format { + public static final Format instance = new ZipPlusFourFormat(); + private static final DecimalFormat df = createIntegerOnlyFormat("000000000"); + + private ZipPlusFourFormat() { + // enforce singleton + } + + /** + * Format a number as Zip + 4 + */ + public static String format(Number num) { + String result = df.format(num); + return result.substring(0, 5) + '-' + result.substring(5, 9); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class for Excel phone number format. This class mimics Excel's built-in phone number formatting. + * + * @author James May + */ + @SuppressWarnings("serial") + private static final class PhoneFormat extends Format { + public static final Format instance = new PhoneFormat(); + private static final DecimalFormat df = createIntegerOnlyFormat("##########"); + + private PhoneFormat() { + // enforce singleton + } + + /** + * Format a number as a phone number + */ + public static String format(Number num) { + String result = df.format(num); + StringBuilder sb = new StringBuilder(); + String seg1, seg2, seg3; + int len = result.length(); + if (len <= 4) { + return result; + } + + seg3 = result.substring(len - 4, len); + seg2 = result.substring(Math.max(0, len - 7), len - 4); + seg1 = result.substring(Math.max(0, len - 10), Math.max(0, len - 7)); + + if (seg1.trim().length() > 0) { + sb.append('(').append(seg1).append(") "); + } + if (seg2.trim().length() > 0) { + sb.append(seg2).append('-'); + } + sb.append(seg3); + return sb.toString(); + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(format((Number)obj)); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Format class that does nothing and always returns a constant string. + * + * This format is used to simulate Excel's handling of a format string of all # when the value is 0. Excel will + * output "", Java will output "0". + */ + @SuppressWarnings("serial") + private static final class ConstantStringFormat extends Format { + private static final DecimalFormat df = createIntegerOnlyFormat("##########"); + private final String str; + + public ConstantStringFormat(String s) { + str = s; + } + + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(str); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return df.parseObject(source, pos); + } + } + + /** + * Workaround until we merge {@link DataFormatter} with {@link CellFormat}. Constant, non-cachable wrapper around a + * {@link CellFormatResult} + */ + @SuppressWarnings("serial") + private final class CellFormatResultWrapper extends Format { + private final CellFormatResult result; + + private CellFormatResultWrapper(CellFormatResult result) { + this.result = result; + } + + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + if (emulateCSV) { + return toAppendTo.append(result.text); + } else { + return toAppendTo.append(result.text.trim()); + } + } + + public Object parseObject(String source, ParsePosition pos) { + return null; // Not supported + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/fill/FillData2.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/fill/FillData2.java new file mode 100644 index 000000000..0ddda379c --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/fill/FillData2.java @@ -0,0 +1,15 @@ +package com.alibaba.easyexcel.test.temp.fill; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +public class FillData2 { + private String test; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/fill/FillTempTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/fill/FillTempTest.java new file mode 100644 index 000000000..352e4600b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/fill/FillTempTest.java @@ -0,0 +1,227 @@ +package com.alibaba.easyexcel.test.temp.fill; + +import java.io.File; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.fill.FillData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; + +import org.junit.jupiter.api.Test; + +/** + * 写的填充写法 + * + * @author Jiaju Zhuang + * @since 2.1.1 + */ + +public class FillTempTest { + /** + * 最简单的填充 + * + * @since 2.1.1 + */ + @Test + public void simpleFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + String templateFileName = "/Users/zhuangjiaju/Downloads/simple.xlsx"; + + // 方案1 根据对象填充 + String fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + FillData fillData = new FillData(); + fillData.setName("张三"); + fillData.setNumber(5.2); + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData); + + //// 方案2 根据Map填充 + //fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"; + //// 这里 会填充到第一个sheet, 然后文件流会自动关闭 + //Map map = new HashMap(); + //map.put("name", "张三"); + //map.put("number", 5.2); + //EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map); + } + + /** + * 填充列表 + * + * @since 2.1.1 + */ + @Test + public void listFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // 填充list 的时候还要注意 模板中{.} 多了个点 表示list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "list.xlsx"; + + // 方案1 一下子全部放到内存里面 并填充 + String fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + // 这里 会填充到第一个sheet, 然后文件流会自动关闭 + EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(data()); + + // 方案2 分多次 填充 会使用文件缓存(省内存) + fileName = TestFileUtil.getPath() + "listFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + // 千万别忘记关闭流 + excelWriter.finish(); + } + + /** + * 复杂的填充 + * + * @since 2.1.1 + */ + @Test + public void complexFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complex.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。 + // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用 + // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存 + // 如果数据量大 list不是最后一行 参照下一个 + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + map.put("total", 1000); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } + + /** + * 数据量大的复杂填充 + *

+ * 这里的解决方案是 确保模板list为最后一行,然后再拼接table.还有03版没救,只能刚正面加内存。 + * + * @since 2.1.1 + */ + @Test + public void complexFillWithTable() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + // 这里模板 删除了list以后的数据,也就是统计的这一行 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "complexFillWithTable.xlsx"; + + String fileName = TestFileUtil.getPath() + "complexFillWithTable" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + // 直接写入数据 + excelWriter.fill(data(), writeSheet); + excelWriter.fill(data(), writeSheet); + + // 写入list之前的数据 + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // list 后面还有个统计 想办法手动写入 + // 这里偷懒直接用list 也可以用对象 + List> totalListList = new ArrayList>(); + List totalList = new ArrayList(); + totalListList.add(totalList); + totalList.add(null); + totalList.add(null); + totalList.add(null); + // 第四列 + totalList.add("统计:1000"); + // 这里是write 别和fill 搞错了 + excelWriter.write(totalListList, writeSheet); + excelWriter.finish(); + // 总体上写法比较复杂 但是也没有想到好的版本 异步的去写入excel 不支持行的删除和移动,也不支持备注这种的写入,所以也排除了可以 + // 新建一个 然后一点点复制过来的方案,最后导致list需要新增行的时候,后面的列的数据没法后移,后续会继续想想解决方案 + } + + /** + * 横向的填充 + * + * @since 2.1.1 + */ + @Test + public void horizontalFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "horizontal.xlsx"; + + String fileName = TestFileUtil.getPath() + "horizontalFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + excelWriter.fill(data(), fillConfig, writeSheet); + excelWriter.fill(data(), fillConfig, writeSheet); + + Map map = new HashMap(); + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } + + /** + * 多列表组合填充填充 + * + * @since 2.2.0-beta1 + */ + @Test + public void compositeFill() { + // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 + // {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list + String templateFileName = + TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "composite.xlsx"; + + String fileName = TestFileUtil.getPath() + "compositeFill" + System.currentTimeMillis() + ".xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + // 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹 + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data2", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + excelWriter.fill(new FillWrapper("data3", data()), writeSheet); + + Map map = new HashMap(); + //map.put("date", "2019年10月9日13:28:28"); + map.put("date", new Date()); + + excelWriter.fill(map, writeSheet); + + // 别忘记关闭流 + excelWriter.finish(); + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1662/Data1662.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1662/Data1662.java new file mode 100644 index 000000000..818aa524a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1662/Data1662.java @@ -0,0 +1,25 @@ +package com.alibaba.easyexcel.test.temp.issue1662; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +@AllArgsConstructor +@NoArgsConstructor +public class Data1662 { + @ExcelProperty(index = 0) + private String str; + @ExcelProperty(index = 1) + private Date date; + @ExcelProperty(index = 2) + private double r; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1662/Issue1662Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1662/Issue1662Test.java new file mode 100644 index 000000000..466227fe2 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1662/Issue1662Test.java @@ -0,0 +1,41 @@ +package com.alibaba.easyexcel.test.temp.issue1662; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.junit.jupiter.api.Test; + +public class Issue1662Test { + @Test + public void test1662() { + String fileName = TestFileUtil.getPath() + "Test1939" + ".xlsx"; + System.out.println(fileName); + EasyExcel.write(fileName).head(head()).sheet("模板").doWrite(dataList()); + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + List head1 = new ArrayList(); + head0.add("xx"); + head0.add("日期"); + list.add(head0); + head1.add("日期"); + list.add(head1); + return list; + } + + private List> dataList() { + List> list = new ArrayList>(); + List data = new ArrayList(); + data.add("字符串"); + data.add(new Date()); + data.add(0.56); + list.add(data); + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1663/FillData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1663/FillData.java new file mode 100644 index 000000000..0dfd27f7b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1663/FillData.java @@ -0,0 +1,13 @@ +package com.alibaba.easyexcel.test.temp.issue1663; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class FillData { + private String name; + private double number; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1663/FillTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1663/FillTest.java new file mode 100644 index 000000000..f9c2a2b40 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue1663/FillTest.java @@ -0,0 +1,50 @@ +package com.alibaba.easyexcel.test.temp.issue1663; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.fill.FillData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.enums.WriteDirectionEnum; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.fill.FillConfig; +import com.alibaba.excel.write.metadata.fill.FillWrapper; + +import org.junit.jupiter.api.Test; + +public class FillTest { + @Test + public void TestFillNullPoint() { + String templateFileName = + TestFileUtil.getPath() + "temp/issue1663" + File.separator + "template.xlsx"; + + String fileName = TestFileUtil.getPath() + "temp/issue1663" + File.separator + "issue1663.xlsx"; + ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.VERTICAL).build(); + excelWriter.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); + + Map map = new HashMap(); + // Variable {date} does not exist in the template.xlsx, which should be ignored instead of reporting an error. + map.put("date", "2019年10月9日13:28:28"); + excelWriter.fill(map, writeSheet); + excelWriter.finish(); + } + + private List data() { + List list + = new ArrayList(); + for (int i = 0; i < 10; i++) { + com.alibaba.easyexcel.test.demo.fill.FillData fillData = new FillData(); + list.add(fillData); + fillData.setName("张三"); + fillData.setNumber(5.2); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue2443/Issue2443.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue2443/Issue2443.java new file mode 100644 index 000000000..e5a87c5fb --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue2443/Issue2443.java @@ -0,0 +1,13 @@ +package com.alibaba.easyexcel.test.temp.issue2443; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class Issue2443 { + private int a; + private int b; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue2443/Issue2443Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue2443/Issue2443Test.java new file mode 100644 index 000000000..d23f9fb86 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/issue2443/Issue2443Test.java @@ -0,0 +1,58 @@ +package com.alibaba.easyexcel.test.temp.issue2443; + +import java.io.File; +import java.text.ParseException; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.read.listener.PageReadListener; +import com.alibaba.excel.util.NumberUtils; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + + +@Slf4j +public class Issue2443Test { + //CS304 (manually written) Issue link: https://github.com/alibaba/easyexcel/issues/2443 + @Test + public void IssueTest1() { + String fileName = TestFileUtil.getPath() + "temp/issue2443" + File.separator + "date1.xlsx"; + EasyExcel.read(fileName, Issue2443.class, new PageReadListener(dataList -> { + for (Issue2443 issueData : dataList) { + log.info("读取到一条数据{}", JSON.toJSONString(issueData)); + } + })).sheet().doRead(); + } + + //CS304 (manually written) Issue link: https://github.com/alibaba/easyexcel/issues/2443 + @Test + public void IssueTest2() { + String fileName = TestFileUtil.getPath() + "temp/issue2443" + File.separator + "date2.xlsx"; + EasyExcel.read(fileName, Issue2443.class, new PageReadListener(dataList -> { + for (Issue2443 issueData : dataList) { + log.info("读取到一条数据{}", JSON.toJSONString(issueData)); + } + })).sheet().doRead(); + } + + @Test + public void parseIntegerTest1() throws ParseException { + String string = "1.00"; + ExcelContentProperty contentProperty = null; + int Int = NumberUtils.parseInteger(string, contentProperty); + Assertions.assertEquals(1, Int); + } + + @Test + public void parseIntegerTest2() throws ParseException { + String string = "2.00"; + ExcelContentProperty contentProperty = null; + int Int = NumberUtils.parseInteger(string, contentProperty); + Assertions.assertEquals(2, Int); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java new file mode 100644 index 000000000..7885a8d2e --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeData.java @@ -0,0 +1,66 @@ +package com.alibaba.easyexcel.test.temp.large; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author Jiaju Zhuang + */ +@Getter +@Setter +@EqualsAndHashCode +@NoArgsConstructor +public class LargeData { + + private String str1; + + private String str2; + + private String str3; + + private String str4; + + private String str5; + + private String str6; + + private String str7; + + private String str8; + + private String str9; + + private String str10; + + private String str11; + + private String str12; + + private String str13; + + private String str14; + + private String str15; + + private String str16; + + private String str17; + + private String str18; + + private String str19; + + private String str20; + + private String str21; + + private String str22; + + private String str23; + + private String str24; + + private String str25; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeDataListener.java new file mode 100644 index 000000000..b62ee1f64 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/LargeDataListener.java @@ -0,0 +1,32 @@ +package com.alibaba.easyexcel.test.temp.large; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class LargeDataListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(LargeDataListener.class); + private int count = 0; + + @Override + public void invoke(LargeData data, AnalysisContext context) { + if (count == 0) { + LOGGER.info("First row:{}", JSON.toJSONString(data)); + } + count++; + if (count % 100000 == 0) { + LOGGER.info("Already read:{}", count); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.info("Large row count:{}", count); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/NoModelLargeDataListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/NoModelLargeDataListener.java new file mode 100644 index 000000000..5e180624a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/NoModelLargeDataListener.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.temp.large; + +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ +public class NoModelLargeDataListener extends AnalysisEventListener> { + + private static final Logger LOGGER = LoggerFactory.getLogger(NoModelLargeDataListener.class); + private int count = 0; + + @Override + public void invoke(Map data, AnalysisContext context) { + if (count == 0) { + LOGGER.info("First row:{}", JSON.toJSONString(data)); + } + count++; + if (count % 100000 == 0) { + LOGGER.info("Already read:{}", count); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.info("Large row count:{}", count); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java new file mode 100644 index 000000000..246656c1b --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/large/TempLargeDataTest.java @@ -0,0 +1,222 @@ +package com.alibaba.easyexcel.test.temp.large; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.write.metadata.WriteSheet; + +import org.apache.poi.openxml4j.util.ZipSecureFile; +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * @author Jiaju Zhuang + */ + +public class TempLargeDataTest { + + private static final Logger LOGGER = LoggerFactory.getLogger(TempLargeDataTest.class); + private int i = 0; + + private static File fileFill07; + private static File template07; + private static File fileCsv; + private static File fileWrite07; + private static File fileWriteTemp07; + private static File fileWritePoi07; + + @BeforeAll + public static void init() { + fileFill07 = TestFileUtil.createNewFile("largefill07.xlsx"); + fileWrite07 = TestFileUtil.createNewFile("large" + File.separator + "fileWrite07.xlsx"); + fileWriteTemp07 = TestFileUtil.createNewFile("large" + File.separator + "fileWriteTemp07.xlsx"); + fileWritePoi07 = TestFileUtil.createNewFile("large" + File.separator + "fileWritePoi07.xlsx"); + template07 = TestFileUtil.readFile("large" + File.separator + "fill.xlsx"); + fileCsv = TestFileUtil.createNewFile("largefileCsv.csv"); + } + + @Test + public void read() throws Exception { + long start = System.currentTimeMillis(); + EasyExcel.read(new FileInputStream("D:\\test\\MRP生产视图(1).xlsx"), LargeData.class, new LargeDataListener()) + .headRowNumber(2).sheet().doRead(); + LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); + } + + @Test + public void noModelRead() throws Exception { + ZipSecureFile.setMaxEntrySize(Long.MAX_VALUE); + long start = System.currentTimeMillis(); + EasyExcel.read(TestFileUtil.readUserHomeFile("test/ld.xlsx"), new NoModelLargeDataListener()) + .sheet().doRead(); + LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); + } + + @Test + public void noModelRead2() throws Exception { + Field field = ZipSecureFile.class.getDeclaredField("MAX_ENTRY_SIZE"); + field.setAccessible(true); + field.set(null, Long.MAX_VALUE); + + long start = System.currentTimeMillis(); + EasyExcel.read( + new File("/Users/zhuangjiaju/IdeaProjects/easyexcel/target/test-classes/large1617887262709.xlsx"), + new NoModelLargeDataListener()) + .sheet().doRead(); + LOGGER.info("Large data total time spent:{}", System.currentTimeMillis() - start); + } + + @Test + public void t04Write() throws Exception { + ExcelWriter excelWriter = EasyExcel.write(fileWriteTemp07, + com.alibaba.easyexcel.test.core.large.LargeData.class).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 2; j++) { + excelWriter.write(data(), writeSheet); + } + excelWriter.finish(); + + long start = System.currentTimeMillis(); + excelWriter = EasyExcel.write(fileWrite07, com.alibaba.easyexcel.test.core.large.LargeData.class).build(); + writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 5000; j++) { + excelWriter.write(data(), writeSheet); + LOGGER.info("{} write success.", j); + } + excelWriter.finish(); + long cost = System.currentTimeMillis() - start; + LOGGER.info("write cost:{}", cost); + start = System.currentTimeMillis(); + try (FileOutputStream fileOutputStream = new FileOutputStream(fileWritePoi07)) { + SXSSFWorkbook workbook = new SXSSFWorkbook(); + SXSSFSheet sheet = workbook.createSheet("sheet1"); + for (int i = 0; i < 100 * 5000; i++) { + SXSSFRow row = sheet.createRow(i); + for (int j = 0; j < 25; j++) { + SXSSFCell cell = row.createCell(j); + cell.setCellValue("str-" + j + "-" + i); + } + if (i % 5000 == 0) { + LOGGER.info("{} write success.", i); + } + } + workbook.write(fileOutputStream); + workbook.dispose(); + workbook.close(); + } + long costPoi = System.currentTimeMillis() - start; + LOGGER.info("poi write cost:{}", System.currentTimeMillis() - start); + LOGGER.info("{} vs {}", cost, costPoi); + Assertions.assertTrue(costPoi * 2 > cost); + } + + @Test + public void t04WriteExcel() throws Exception { + IntStream.rangeClosed(0, 100).forEach(index -> { + ExcelWriter excelWriter = EasyExcel.write(fileWriteTemp07, + com.alibaba.easyexcel.test.core.large.LargeData.class).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 5000; j++) { + excelWriter.write(data(), writeSheet); + } + excelWriter.finish(); + LOGGER.info("{} 完成", index); + }); + } + + @Test + public void t04WriteExcelNo() throws Exception { + IntStream.rangeClosed(0, 10000).forEach(index -> { + ExcelWriter excelWriter = EasyExcel.write(fileWriteTemp07, + com.alibaba.easyexcel.test.core.large.LargeData.class).build(); + WriteSheet writeSheet = EasyExcel.writerSheet().build(); + for (int j = 0; j < 50; j++) { + excelWriter.write(data(), writeSheet); + } + excelWriter.finish(); + LOGGER.info("{} 完成", index); + }); + } + + @Test + public void t04WriteExcelPoi() throws Exception { + IntStream.rangeClosed(0, 10000).forEach(index -> { + try (FileOutputStream fileOutputStream = new FileOutputStream(fileWritePoi07)) { + SXSSFWorkbook workbook = new SXSSFWorkbook(500); + //workbook.setCompressTempFiles(true); + SXSSFSheet sheet = workbook.createSheet("sheet1"); + for (int i = 0; i < 100 * 50; i++) { + SXSSFRow row = sheet.createRow(i); + for (int j = 0; j < 25; j++) { + String str = "str-" + j + "-" + i; + //if (i + 10000 == j) { + SXSSFCell cell = row.createCell(j); + cell.setCellValue(str); + //System.out.println(str); + //} + } + if (i % 5000 == 0) { + LOGGER.info("{} write success.", i); + } + } + workbook.write(fileOutputStream); + workbook.dispose(); + workbook.close(); + } catch (Exception e) { + e.printStackTrace(); + } + LOGGER.info("{} 完成", index); + }); + } + + private List data() { + List list = new ArrayList<>(); + + int size = i + 100; + for (; i < size; i++) { + LargeData largeData = new LargeData(); + list.add(largeData); + largeData.setStr1("str1-" + i); + largeData.setStr2("str2-" + i); + largeData.setStr3("str3-" + i); + largeData.setStr4("str4-" + i); + largeData.setStr5("str5-" + i); + largeData.setStr6("str6-" + i); + largeData.setStr7("str7-" + i); + largeData.setStr8("str8-" + i); + largeData.setStr9("str9-" + i); + largeData.setStr10("str10-" + i); + largeData.setStr11("str11-" + i); + largeData.setStr12("str12-" + i); + largeData.setStr13("str13-" + i); + largeData.setStr14("str14-" + i); + largeData.setStr15("str15-" + i); + largeData.setStr16("str16-" + i); + largeData.setStr17("str17-" + i); + largeData.setStr18("str18-" + i); + largeData.setStr19("str19-" + i); + largeData.setStr20("str20-" + i); + largeData.setStr21("str21-" + i); + largeData.setStr22("str22-" + i); + largeData.setStr23("str23-" + i); + largeData.setStr24("str24-" + i); + largeData.setStr25("str25-" + i); + } + return list; + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi2Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi2Test.java new file mode 100644 index 000000000..a2567e114 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi2Test.java @@ -0,0 +1,46 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.IOException; + +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class Poi2Test { + private static final Logger LOGGER = LoggerFactory.getLogger(Poi2Test.class); + + @Test + public void test() throws IOException { + String file = "D:\\test\\珠海.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + SXSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + SXSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + } + + @Test + public void lastRowNumXSSF() throws IOException { + String file = "D:\\test\\珠海.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + xssfSheet.createRow(20); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java new file mode 100644 index 000000000..b9cac73fc --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/Poi3Test.java @@ -0,0 +1,59 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; + +import com.alibaba.easyexcel.test.util.TestFileUtil; + +import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageAccess; +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class Poi3Test { + private static final Logger LOGGER = LoggerFactory.getLogger(Poi3Test.class); + + @Test + public void Encryption() throws Exception { + String file = TestFileUtil.getPath() + "large" + File.separator + "large07.xlsx"; + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); + Encryptor enc = info.getEncryptor(); + enc.confirmPassword("foobaa"); + OPCPackage opc = OPCPackage.open(new File(file), PackageAccess.READ_WRITE); + OutputStream os = enc.getDataStream(fs); + opc.save(os); + opc.close(); + + // Write out the encrypted version + FileOutputStream fos = new FileOutputStream("D:\\test\\99999999999.xlsx"); + fs.writeFilesystem(fos); + fos.close(); + fs.close(); + + } + + @Test + public void Encryption2() throws Exception { + Biff8EncryptionKey.setCurrentUserPassword("123456"); + POIFSFileSystem fs = new POIFSFileSystem(new File("d:/test/simple03.xls"), true); + HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true); + Biff8EncryptionKey.setCurrentUserPassword(null); + System.out.println(hwb.getSheetAt(0).getSheetName()); + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiDateFormatTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiDateFormatTest.java new file mode 100644 index 000000000..dbef4a08a --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiDateFormatTest.java @@ -0,0 +1,40 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.IOException; + +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class PoiDateFormatTest { + private static final Logger LOGGER = LoggerFactory.getLogger(PoiDateFormatTest.class); + + @Test + public void read() throws IOException { + String file + = "/Users/zhuangjiaju/IdeaProjects/easyexcel/easyexcel-test/src/test/resources/dataformat/dataformat.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook( file); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(7); + XSSFCell cell = row.getCell(0); + LOGGER.info("dd{}", cell.getDateCellValue()); + LOGGER.info("dd{}", cell.getNumericCellValue()); + + LOGGER.info("dd{}", DateUtil.isCellDateFormatted(cell)); + + + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiEncryptTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiEncryptTest.java new file mode 100644 index 000000000..5484a6142 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiEncryptTest.java @@ -0,0 +1,75 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.core.encrypt.EncryptData; +import com.alibaba.easyexcel.test.core.simple.SimpleData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; + +import org.apache.poi.poifs.crypt.EncryptionInfo; +import org.apache.poi.poifs.crypt.EncryptionMode; +import org.apache.poi.poifs.crypt.Encryptor; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; + +/** + * TODO + * + * @author Jiaju Zhuang + */ + +public class PoiEncryptTest { + @Test + public void encrypt() throws Exception { + + XSSFWorkbook workbook = new XSSFWorkbook(); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(workbook); + + Sheet sheet = sxssfWorkbook.createSheet("sheet1"); + sheet.createRow(0).createCell(0).setCellValue("T2"); + + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile); + + Encryptor enc = info.getEncryptor(); + enc.confirmPassword("123456"); + + // write the workbook into the encrypted OutputStream + OutputStream encos = enc.getDataStream(fs); + sxssfWorkbook.write(encos); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + encos.close(); // this is necessary before writing out the FileSystem + + OutputStream os = new FileOutputStream( + TestFileUtil.createNewFile("encrypt" + System.currentTimeMillis() + ".xlsx")); + fs.writeFilesystem(os); + os.close(); + fs.close(); + } + + @Test + public void encryptExcel() throws Exception { + EasyExcel.write(TestFileUtil.createNewFile("encryptv2" + System.currentTimeMillis() + ".xlsx"), + EncryptData.class).password("123456") + .sheet().doWrite(data()); + } + + private List data() { + List list = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + SimpleData simpleData = new SimpleData(); + simpleData.setName("姓名" + i); + list.add(simpleData); + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiFormatTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiFormatTest.java new file mode 100644 index 000000000..4dfef5b16 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiFormatTest.java @@ -0,0 +1,52 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.IOException; +import java.util.Locale; + +import org.apache.poi.ss.usermodel.DataFormatter; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class PoiFormatTest { + private static final Logger LOGGER = LoggerFactory.getLogger(PoiFormatTest.class); + + @Test + public void lastRowNum() throws IOException { + String file = "D:\\test\\原文件.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + SXSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + SXSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + xssfSheet.createRow(20); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + } + + @Test + public void lastRowNumXSSF() throws IOException { + String file = "/Users/zhuangjiaju/Downloads/测试格式.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(1); + XSSFCell xssfCell = row.getCell(0); + DataFormatter d = new DataFormatter(Locale.CHINA); + LOGGER.info("fo:{}", d.formatCellValue(xssfCell)); + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java new file mode 100644 index 000000000..6c3f229a9 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiTest.java @@ -0,0 +1,316 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Date; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.util.FileUtils; + +import org.apache.poi.hssf.usermodel.HSSFCellStyle; +import org.apache.poi.hssf.usermodel.HSSFFont; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFColor; +import org.apache.poi.xssf.usermodel.XSSFFont; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class PoiTest { + private static final Logger LOGGER = LoggerFactory.getLogger(PoiTest.class); + + @Test + public void lastRowNum() throws IOException { + String file = "/Users/zhuangjiaju/test/test3.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + SXSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + SXSSFRow row = xssfSheet.getRow(0); + LOGGER.info("dd{}", row.getCell(0).getColumnIndex()); + Date date = row.getCell(1).getDateCellValue(); + + } + + @Test + public void lastRowNumXSSF() throws IOException { + + String file = "/Users/zhuangjiaju/test/test3 copy 10.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new FileInputStream(file)); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(1); + LOGGER.info("dd{}", row.getCell(0).getRow().getRowNum()); + LOGGER.info("dd{}", xssfSheet.getLastRowNum()); + + XSSFCellStyle cellStyle = row.getCell(0).getCellStyle(); + LOGGER.info("size1:{}", cellStyle.getFontIndexAsInt()); + + XSSFCellStyle cellStyle1 = xssfWorkbook.createCellStyle(); + LOGGER.info("size2:{}", cellStyle1.getFontIndexAsInt()); + + cellStyle1.cloneStyleFrom(cellStyle); + LOGGER.info("size3:{}", cellStyle1.getFontIndexAsInt()); + + LOGGER.info("bbb:{}", cellStyle1.getFont().getXSSFColor().getIndex()); + LOGGER.info("bbb:{}", cellStyle1.getFont().getXSSFColor().getIndexed()); + XSSFColor myColor = new XSSFColor(cellStyle1.getFont().getXSSFColor().getRGB(), null); + LOGGER.info("bbb:{}", cellStyle1.getFont().getXSSFColor().getRGB()); + LOGGER.info("bbb:{}", cellStyle1.getFont().getXSSFColor().getARGBHex()); + + LOGGER.info("bbb:{}", cellStyle1.getFont().getBold()); + LOGGER.info("bbb:{}", cellStyle1.getFont().getFontName()); + + XSSFFont xssfFont = xssfWorkbook.createFont(); + + xssfFont.setColor(myColor); + + xssfFont.setFontHeightInPoints((short)50); + xssfFont.setBold(Boolean.TRUE); + cellStyle1.setFont(xssfFont); + cellStyle1.setFillForegroundColor(IndexedColors.PINK.getIndex()); + + LOGGER.info("aaa:{}", cellStyle1.getFont().getColor()); + + row.getCell(1).setCellStyle(cellStyle1); + row.getCell(1).setCellValue(3334l); + + XSSFCellStyle cellStyle2 = xssfWorkbook.createCellStyle(); + cellStyle2.cloneStyleFrom(cellStyle); + cellStyle2.setFillForegroundColor(IndexedColors.BLUE.getIndex()); + //cellStyle2.setFont(cellStyle1.getFont()); + row.getCell(2).setCellStyle(cellStyle2); + row.getCell(2).setCellValue(3334l); + //LOGGER.info("date1:{}", row.getCell(0).getStringCellValue()); + //LOGGER.info("date2:{}", ((XSSFColor) cellStyle.getFillForegroundColorColor()).getIndex()); + //LOGGER.info("date2:{}", ((XSSFColor) cellStyle.getFillForegroundColorColor()).isRGB()); + //LOGGER.info("date4:{}", ((XSSFColor) cellStyle.getFillForegroundColorColor()).isIndexed()); + //LOGGER.info("date3:{}", cellStyle.getFont().getXSSFColor().getRGB()); + //LOGGER.info("date4:{}", cellStyle.getFont().getCTFont().getColorArray(0).getRgb()); + FileOutputStream fileOutputStream = new FileOutputStream(file); + xssfWorkbook.write(fileOutputStream); + fileOutputStream.flush(); + xssfWorkbook.close(); + } + + @Test + public void lastRowNumXSSFv22() throws IOException { + + String file = "/Users/zhuangjiaju/test/test3 copy 2.xls"; + HSSFWorkbook xssfWorkbook = new HSSFWorkbook(new FileInputStream(file)); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + HSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + HSSFRow row = xssfSheet.getRow(1); + LOGGER.info("dd{}", row.getCell(0).getRow().getRowNum()); + LOGGER.info("dd{}", xssfSheet.getLastRowNum()); + + HSSFCellStyle cellStyle = row.getCell(0).getCellStyle(); + LOGGER.info("单元格1的字体:{}", cellStyle.getFontIndexAsInt()); + + HSSFCellStyle cellStyle1 = xssfWorkbook.createCellStyle(); + LOGGER.info("size2:{}", cellStyle1.getFontIndexAsInt()); + + cellStyle1.cloneStyleFrom(cellStyle); + LOGGER.info("单元格2的字体:{}", cellStyle1.getFontIndexAsInt()); + + LOGGER.info("bbb:{}", cellStyle1.getFont(xssfWorkbook).getColor()); + + HSSFFont xssfFont = xssfWorkbook.createFont(); + + xssfFont.setColor(cellStyle1.getFont(xssfWorkbook).getColor()); + xssfFont.setFontHeightInPoints((short)50); + xssfFont.setBold(Boolean.TRUE); + cellStyle1.setFont(xssfFont); + cellStyle1.setFillForegroundColor(IndexedColors.PINK.getIndex()); + + LOGGER.info("aaa:{}", cellStyle1.getFont(xssfWorkbook).getColor()); + + row.getCell(1).setCellStyle(cellStyle1); + row.getCell(1).setCellValue(3334l); + + HSSFCellStyle cellStyle2 = xssfWorkbook.createCellStyle(); + cellStyle2.cloneStyleFrom(cellStyle); + cellStyle2.setFillForegroundColor(IndexedColors.BLUE.getIndex()); + //cellStyle2.setFont(cellStyle1.getFont()); + row.getCell(2).setCellStyle(cellStyle2); + row.getCell(2).setCellValue(3334l); + //LOGGER.info("date1:{}", row.getCell(0).getStringCellValue()); + //LOGGER.info("date2:{}", ((XSSFColor) cellStyle.getFillForegroundColorColor()).getIndex()); + //LOGGER.info("date2:{}", ((XSSFColor) cellStyle.getFillForegroundColorColor()).isRGB()); + //LOGGER.info("date4:{}", ((XSSFColor) cellStyle.getFillForegroundColorColor()).isIndexed()); + //LOGGER.info("date3:{}", cellStyle.getFont().getXSSFColor().getRGB()); + //LOGGER.info("date4:{}", cellStyle.getFont().getCTFont().getColorArray(0).getRgb()); + FileOutputStream fileOutputStream = new FileOutputStream(file); + xssfWorkbook.write(fileOutputStream); + fileOutputStream.flush(); + xssfWorkbook.close(); + } + + @Test + public void lastRowNum233() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + Workbook xx = new XSSFWorkbook(file); + System.out.println(new File(file).exists()); + + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + xssfWorkbook.close(); + } + + @Test + public void lastRowNum255() throws IOException, InvalidFormatException { + String file = "D:\\test\\complex.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + xssfSheet.shiftRows(1, 4, 10, true, true); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void cp() throws IOException, InvalidFormatException { + String file = "d://test/tt.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + SXSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + SXSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + xssfSheet.createRow(20); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + } + + @Test + public void lastRowNum233443() throws IOException, InvalidFormatException { + String file = "d://test/em0.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + System.out.println(xssfSheet.getLastRowNum()); + System.out.println(xssfSheet.getRow(0)); + + } + + @Test + public void lastRowNum2333() throws IOException, InvalidFormatException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(new File(file)); + SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(xssfWorkbook); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + sxssfWorkbook.write(fileout); + sxssfWorkbook.dispose(); + sxssfWorkbook.close(); + + xssfWorkbook.close(); + } + + @Test + public void testread() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + // + // Cell cell = xssfSheet.getRow(0).createCell(9); + + String file1 = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + + SXSSFWorkbook xssfWorkbook1 = new SXSSFWorkbook(new XSSFWorkbook(file1)); + Sheet xssfSheet1 = xssfWorkbook1.getXSSFWorkbook().getSheetAt(0); + + // Cell cell1 = xssfSheet1.getRow(0).createCell(9); + + xssfWorkbook.close(); + xssfWorkbook1.close(); + } + + @Test + public void testreadRead() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + FileUtils.readFileToByteArray(new File(file)); + } + + @Test + public void lastRowNum2332222() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + + Cell cell = xssfSheet.getRow(0).createCell(9); + cell.setCellValue("testssdf是士大夫否t"); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + } + + @Test + public void lastRowNum23443() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getSheetAt(0); + + FileOutputStream fileout = new FileOutputStream("d://test/r2" + System.currentTimeMillis() + ".xlsx"); + xssfWorkbook.write(fileout); + xssfWorkbook.close(); + } + + @Test + public void lastRowNum2() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + SXSSFWorkbook xssfWorkbook = new SXSSFWorkbook(new XSSFWorkbook(file)); + Sheet xssfSheet = xssfWorkbook.getXSSFWorkbook().getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getPhysicalNumberOfRows()); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + LOGGER.info("一共行数:{}", xssfSheet.getFirstRowNum()); + + } + + @Test + public void lastRowNumXSSF2() throws IOException { + String file = TestFileUtil.getPath() + "fill" + File.separator + "simple.xlsx"; + XSSFWorkbook xssfWorkbook = new XSSFWorkbook(file); + LOGGER.info("一共:{}个sheet", xssfWorkbook.getNumberOfSheets()); + XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(0); + LOGGER.info("一共行数:{}", xssfSheet.getLastRowNum()); + XSSFRow row = xssfSheet.getRow(0); + LOGGER.info("第一行数据:{}", row); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java new file mode 100644 index 000000000..98e7509a0 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/PoiWriteTest.java @@ -0,0 +1,120 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.io.BufferedInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.net.URL; +import java.util.regex.Pattern; + +import com.alibaba.fastjson2.JSON; + +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class PoiWriteTest { + private static final Logger LOGGER = LoggerFactory.getLogger(PoiWriteTest.class); + + @Test + public void write0() throws IOException { + FileOutputStream fileOutputStream = + new FileOutputStream("D://test//tt132" + System.currentTimeMillis() + ".xlsx"); + SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook(); + SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); + SXSSFRow row = sheet.createRow(0); + SXSSFCell cell1 = row.createCell(0); + cell1.setCellValue(999999999999999L); + SXSSFCell cell2 = row.createCell(1); + cell2.setCellValue(1000000000000001L); + SXSSFCell cell32 = row.createCell(2); + cell32.setCellValue(300.35f); + sxxsFWorkbook.write(fileOutputStream); + } + + @Test + public void write01() throws IOException { + float ff = 300.35f; + BigDecimal bd = new BigDecimal(Float.toString(ff)); + System.out.println(bd.doubleValue()); + System.out.println(bd.floatValue()); + + } + + @Test + public void write() throws IOException { + FileOutputStream fileOutputStream = + new FileOutputStream("D://test//tt132" + System.currentTimeMillis() + ".xlsx"); + SXSSFWorkbook sxxsFWorkbook = new SXSSFWorkbook(); + SXSSFSheet sheet = sxxsFWorkbook.createSheet("t1"); + SXSSFRow row = sheet.createRow(0); + SXSSFCell cell1 = row.createCell(0); + cell1.setCellValue(Long.toString(999999999999999L)); + SXSSFCell cell2 = row.createCell(1); + cell2.setCellValue(Long.toString(1000000000000001L)); + sxxsFWorkbook.write(fileOutputStream); + } + + @Test + public void write1() throws IOException { + System.out.println(JSON.toJSONString(long2Bytes(-999999999999999L))); + System.out.println(JSON.toJSONString(long2Bytes(-9999999999999999L))); + } + + public static byte[] long2Bytes(long num) { + byte[] byteNum = new byte[8]; + for (int ix = 0; ix < 8; ++ix) { + int offset = 64 - (ix + 1) * 8; + byteNum[ix] = (byte)((num >> offset) & 0xff); + } + return byteNum; + } + + private static final Pattern FILL_PATTERN = Pattern.compile("^.*?\\$\\{[^}]+}.*?$"); + + @Test + public void part() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${number}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${name}今年").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number}岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("今年${number岁了").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("${}").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("胜多负少").matches()); + } + + private static final Pattern FILL_PATTERN2 = Pattern.compile("测试"); + + @Test + public void part2() throws IOException { + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试呀").find()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("测试u").matches()); + LOGGER.info("test:{}", FILL_PATTERN.matcher("我是测试").matches()); + + } + + @Test + public void part4() throws IOException { + //URL url=new URL("http://120.55.161.4/group1/M00/00/00/i8QJ8WFfwMiAXKYrAAACqC1MFiY641.png"); + URL url = new URL( + "https://img-blog.csdn.net/20160729002743309?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T" + + "/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center"); + + InputStream in = new BufferedInputStream(url.openStream()); + + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java new file mode 100644 index 000000000..99540f2a0 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/poi/TestCell.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.temp.poi; + +import java.util.List; + +import com.alibaba.excel.metadata.data.CellData; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * TODO + * + * @author 罗成 + **/ +@Getter +@Setter +@EqualsAndHashCode +public class TestCell { + private CellData c1; + private CellData> c2; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java new file mode 100644 index 000000000..b05fd5401 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/CommentTest.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.temp.read; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.data.CellData; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class CommentTest { + private static final Logger LOGGER = LoggerFactory.getLogger(CommentTest.class); + + @Test + public void comment() throws Exception { + File file = new File("D:\\test\\d1.xlsx"); + List> datas = EasyExcel.read(file).doReadAllSync(); + for (Map data : datas) { + LOGGER.info("数据:{}", JSON.toJSONString(data)); + } + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java new file mode 100644 index 000000000..094c58f57 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HDListener.java @@ -0,0 +1,42 @@ +package com.alibaba.easyexcel.test.temp.read; + +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +public class HDListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(HDListener.class); + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + LOGGER.info("HEAD:{}", JSON.toJSONString(headMap)); + LOGGER.info("total:{}", context.readSheetHolder().getTotal()); + + } + + @Override + public void invoke(HeadReadData data, AnalysisContext context) { + LOGGER.info("index:{}", context.readRowHolder().getRowIndex()); + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.info("所有数据解析完成!"); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadListener.java new file mode 100644 index 000000000..4d1eb8bae --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadListener.java @@ -0,0 +1,42 @@ +package com.alibaba.easyexcel.test.temp.read; + +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +public class HeadListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(HeadListener.class); + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + + @Override + public void invokeHeadMap(Map headMap, AnalysisContext context) { + LOGGER.info("HEAD:{}", JSON.toJSONString(headMap)); + LOGGER.info("total:{}", context.readSheetHolder().getTotal()); + + } + + @Override + public void invoke(HeadReadData data, AnalysisContext context) { + LOGGER.info("index:{}", context.readRowHolder().getRowIndex()); + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.info("所有数据解析完成!"); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java new file mode 100644 index 000000000..a942763f7 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.temp.read; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class HeadReadData { + @ExcelProperty({"主标题", "数据1"}) + private String h1; + @ExcelProperty({"主标题", "数据2"}) + private String h2; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java new file mode 100644 index 000000000..ab0042583 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/HeadReadTest.java @@ -0,0 +1,40 @@ +package com.alibaba.easyexcel.test.temp.read; + +import java.io.File; + +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.cache.Ehcache; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 临时测试 + * + * @author Jiaju Zhuang + **/ + +public class HeadReadTest { + private static final Logger LOGGER = LoggerFactory.getLogger(HeadReadTest.class); + + @Test + public void test() throws Exception { + File file = TestFileUtil.readUserHomeFile("test/t2.xlsx"); + EasyExcel.read(file, HeadReadData.class, new HeadListener()).ignoreEmptyRow(false).sheet(0).doRead(); + + } + + @Test + public void testCache() throws Exception { + File file = new File("D:\\test\\headt1.xls"); + EasyExcel.read(file, HeadReadData.class, new HDListener()).readCache(new Ehcache(20)).sheet(0).doRead(); + + LOGGER.info("------------------"); + EasyExcel.read(file, HeadReadData.class, new HDListener()).readCache(new Ehcache(20)).sheet(0).doRead(); + LOGGER.info("------------------"); + EasyExcel.read(file, HeadReadData.class, new HDListener()).readCache(new Ehcache(20)).sheet(0).doRead(); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/TestListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/TestListener.java new file mode 100644 index 000000000..189271691 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/read/TestListener.java @@ -0,0 +1,27 @@ +package com.alibaba.easyexcel.test.temp.read; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; + +/** + * TODO + * + * @author JiaJu Zhuang + * @date 2020/4/9 16:33 + **/ +@Slf4j +public class TestListener extends AnalysisEventListener { + + @Override + public void invoke(Object o, AnalysisContext analysisContext) { + log.info("解析一条:{}", JSON.toJSONString(o)); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/DemoData1.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/DemoData1.java new file mode 100644 index 000000000..97c26ed98 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/DemoData1.java @@ -0,0 +1,30 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class DemoData1 { + @ExcelProperty("字符串标题") + @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42) + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/DemoData2.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/DemoData2.java new file mode 100644 index 000000000..ddc0e3dbe --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/DemoData2.java @@ -0,0 +1,30 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.enums.poi.FillPatternTypeEnum; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@EqualsAndHashCode +public class DemoData2 { + @ExcelProperty("字符串标题") + @HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 42) + private String string; + @ExcelProperty("日期标题") + private Date date; + @ExcelProperty("数字标题") + private Double doubleData; + /** + * 忽略这个字段 + */ + @ExcelIgnore + private String ignore; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgListener.java new file mode 100644 index 000000000..161ccda54 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgListener.java @@ -0,0 +1,35 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.util.Map; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +public class HgListener extends AnalysisEventListener> { + private static final Logger LOGGER = LoggerFactory.getLogger(HgListener.class); + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + + @Override + public void invoke(Map data, AnalysisContext context) { + LOGGER.info("index:{}", context.readRowHolder().getRowIndex()); + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + LOGGER.info("所有数据解析完成!"); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java new file mode 100644 index 000000000..4e1f40c5c --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/HgTest.java @@ -0,0 +1,57 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.List; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.fastjson2.JSON; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class HgTest { + private static final Logger LOGGER = LoggerFactory.getLogger(HgTest.class); + + @Test + public void hh() throws IOException { + List list = + EasyExcel.read(new FileInputStream("D:\\test\\201909301017rule.xlsx")).headRowNumber(2).sheet() + .doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void hh5() throws IOException { + URL url = new URL( + "http://hotelcontractfil.oss-cn-beijing.aliyuncs" + + ".com/2019/%E5%98%89%E6%83%A0-%E4%B8%AD%E4%BA%A4%E5%BB%BA_2019-09-01_2019-09-30_1569055677522" + + ".xlsx?Expires=1884415681&OSSAccessKeyId=LTAIGZDkqZfPArBr&Signature=Rf0gbO8vl3l%2Brj1KdyzHHMsUhCE" + + "%3D"); + InputStream is = url.openStream(); + List list = + EasyExcel.read(is).headRowNumber(0).sheet().doReadSync(); + for (Object data : list) { + LOGGER.info("返回数据:{}", JSON.toJSONString(data)); + } + } + + @Test + public void hh2() throws IOException { + EasyExcel.read(new FileInputStream("D:\\test\\商户不匹配工单信息收集表格.xlsx")).registerReadListener( + new HgListener()) + .headRowNumber(0).sheet().doRead(); + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/JsonData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/JsonData.java new file mode 100644 index 000000000..34a60bc8f --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/JsonData.java @@ -0,0 +1,19 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * TODO + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class JsonData { + private String SS1; + private String sS2; + private String ss3; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/RepeatListener.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/RepeatListener.java new file mode 100644 index 000000000..6755c94de --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/RepeatListener.java @@ -0,0 +1,51 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.easyexcel.test.demo.read.DemoDataListener; +import com.alibaba.easyexcel.test.temp.LockData; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.alibaba.fastjson2.JSON; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 模板的读取类 + * + * @author Jiaju Zhuang + */ +public class RepeatListener extends AnalysisEventListener { + private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class); + /** + * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收 + */ + private static final int BATCH_COUNT = 5; + List list = new ArrayList(); + + @Override + public void invoke(LockData data, AnalysisContext context) { + LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data)); + list.add(data); + if (list.size() >= BATCH_COUNT) { + saveData(); + list.clear(); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + LOGGER.info("所有数据解析完成!"); + } + + /** + * 加上存储数据库 + */ + private void saveData() { + LOGGER.info("{}条数据,开始存储数据库!", list.size()); + LOGGER.info("存储数据库成功!"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/RepeatTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/RepeatTest.java new file mode 100644 index 000000000..e1e669e48 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/RepeatTest.java @@ -0,0 +1,55 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.io.FileInputStream; +import java.io.IOException; + +import com.alibaba.easyexcel.test.temp.LockData; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelReader; +import com.alibaba.excel.read.metadata.ReadSheet; + +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +public class RepeatTest { + private static final Logger LOGGER = LoggerFactory.getLogger(RepeatTest.class); + + @Test + public void hh() throws IOException { + ExcelReader reader = + EasyExcel.read(new FileInputStream("D:\\test\\hg2.xls"), LockData.class, new RepeatListener()) + .headRowNumber(0).build(); + ReadSheet r1 = EasyExcel.readSheet(0).build(); + ReadSheet r2 = EasyExcel.readSheet(2).build(); + reader.read(r1); + reader.read(r2); + reader.finish(); + } + + @Test + public void hh2() throws IOException { + ExcelReader reader = + EasyExcel.read(new FileInputStream("D:\\test\\sheet.xls"), LockData.class, new RepeatListener()) + .headRowNumber(0).build(); + ReadSheet r2 = EasyExcel.readSheet(1).build(); + reader.read(r2); + reader.finish(); + } + + @Test + public void hh1() throws IOException { + ExcelReader reader = + EasyExcel.read(new FileInputStream("D:\\test\\hg2.xls"), LockData.class, new RepeatListener()) + .headRowNumber(0).build(); + ReadSheet r2 = EasyExcel.readSheet(0).build(); + reader.read(r2); + reader.finish(); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/Write.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/Write.java new file mode 100644 index 000000000..760a638e6 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/Write.java @@ -0,0 +1,148 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import com.alibaba.easyexcel.test.core.large.LargeData; +import com.alibaba.easyexcel.test.demo.write.DemoData; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.util.BeanMapUtils; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.WriteTable; +import com.alibaba.fastjson2.JSON; + +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 测试poi + * + * @author Jiaju Zhuang + **/ + +@Slf4j +public class Write { + private static final Logger LOGGER = LoggerFactory.getLogger(Write.class); + + @Test + public void simpleWrite1() { + LargeData ss = new LargeData(); + ss.setStr23("ttt"); + Map map = BeanMapUtils.create(ss); + System.out.println(map.containsKey("str23")); + System.out.println(map.containsKey("str22")); + System.out.println(map.get("str23")); + System.out.println(map.get("str22")); + } + + @Test + public void simpleWrite() { + log.info("t5"); + // 写法1 + String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, DemoData.class).relativeHeadRowIndex(10).sheet("模板").doWrite(data()); + } + + @Test + public void simpleWrite2() { + // 写法1 + String fileName = TestFileUtil.getPath() + "t22" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName, WriteData.class).sheet("模板").registerWriteHandler(new WriteHandler()).doWrite( + data1()); + } + + @Test + public void simpleWrite3() { + // 写法1 + String fileName = TestFileUtil.getPath() + "t33" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + // 如果这里想使用03 则 传入excelType参数即可 + EasyExcel.write(fileName).head(head()).inMemory(true).sheet("模板").registerWriteHandler(new WriteCellHandler()) + .doWrite( + data1()); + } + + @Test + public void json() { + JsonData jsonData = new JsonData(); + jsonData.setSS1("11"); + jsonData.setSS2("22"); + jsonData.setSs3("33"); + System.out.println(JSON.toJSONString(jsonData)); + + } + + @Test + public void json3() { + String json = "{\"SS1\":\"11\",\"sS2\":\"22\",\"ss3\":\"33\"}"; + + JsonData jsonData = JSON.parseObject(json, JsonData.class); + System.out.println(JSON.toJSONString(jsonData)); + + } + + @Test + public void tableWrite() { + String fileName = TestFileUtil.getPath() + "tableWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里直接写多个table的案例了,如果只有一个 也可以直一行代码搞定,参照其他案例 + // 这里 需要指定写用哪个class去写 + ExcelWriter excelWriter = EasyExcel.write(fileName).build(); + // 把sheet设置为不需要头 不然会输出sheet的头 这样看起来第一个table 就有2个头了 + WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); + // 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要 + WriteTable writeTable0 = EasyExcel.writerTable(0).head(DemoData1.class).build(); + // 第一次写入会创建头 + excelWriter.write(data(), writeSheet, writeTable0); + // 第二次写如也会创建头,然后在第一次的后面写入数据 + /// 千万别忘记close 会帮忙关闭流 + excelWriter.finish(); + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("字符串" + System.currentTimeMillis()); + List head1 = new ArrayList(); + head1.add("数字" + System.currentTimeMillis()); + List head2 = new ArrayList(); + head2.add("日期" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } + + private List data() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + DemoData data = new DemoData(); + data.setString("640121807369666560" + i); + data.setDate(new Date()); + data.setDoubleData(null); + list.add(data); + } + return list; + } + + private List data1() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + WriteData data = new WriteData(); + data.setDd(new Date()); + data.setF1(33f); + list.add(data); + } + return list; + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteCellHandler.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteCellHandler.java new file mode 100644 index 000000000..b92005c7d --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteCellHandler.java @@ -0,0 +1,39 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.IndexedColors; + +/** + * @author Jiaju Zhuang + */ +@Slf4j +public class WriteCellHandler implements CellWriteHandler { + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + WriteCellData cellData, Cell cell, Head head, Integer integer, Boolean isHead) { + + if (!isHead) { + CreationHelper createHelper = writeSheetHolder.getSheet().getWorkbook().getCreationHelper(); + CellStyle cellStyle = writeSheetHolder.getSheet().getWorkbook().createCellStyle(); + if (cellStyle != null) { + DataFormat dataFormat = createHelper.createDataFormat(); + cellStyle.setWrapText(true); + cellStyle.setFillBackgroundColor(IndexedColors.RED.getIndex()); + cellStyle.setBottomBorderColor(IndexedColors.RED.getIndex()); + cellStyle.setDataFormat(dataFormat.getFormat("yyyy-MM-dd")); + cell.setCellStyle(cellStyle); + } + } + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteData.java new file mode 100644 index 000000000..f4ced8cbe --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import java.util.Date; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; + +/** + * write data + * + * @author Jiaju Zhuang + **/ +@Getter +@Setter +@EqualsAndHashCode +public class WriteData { + // @ContentStyle(locked = true) + private Date dd; + // @ContentStyle(locked = false) + private float f1; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteHandler.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteHandler.java new file mode 100644 index 000000000..1650c21ab --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/simple/WriteHandler.java @@ -0,0 +1,21 @@ +package com.alibaba.easyexcel.test.temp.simple; + +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; + +import lombok.extern.slf4j.Slf4j; + +/** + * @author Jiaju Zhuang + */ +@Slf4j +public class WriteHandler implements SheetWriteHandler { + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, + WriteSheetHolder writeSheetHolder) { + log.info("锁住"); + writeSheetHolder.getSheet().protectSheet("edit"); + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/write/TempWriteData.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/write/TempWriteData.java new file mode 100644 index 000000000..2ef4b56ae --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/write/TempWriteData.java @@ -0,0 +1,19 @@ +package com.alibaba.easyexcel.test.temp.write; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ContentStyle; +import com.alibaba.excel.annotation.write.style.HeadStyle; +import com.alibaba.excel.enums.BooleanEnum; + +import lombok.Data; + +@Data +//@Accessors(chain = true) +public class TempWriteData { + private String name1; + + @ExcelProperty(" 换行\r\n \\ \r\n的名字") + @HeadStyle(wrapped = BooleanEnum.TRUE) + @ContentStyle(wrapped = BooleanEnum.TRUE) + private String name; +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/write/TempWriteTest.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/write/TempWriteTest.java new file mode 100644 index 000000000..9f66a5076 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/temp/write/TempWriteTest.java @@ -0,0 +1,162 @@ +package com.alibaba.easyexcel.test.temp.write; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.Map; + +import com.alibaba.easyexcel.test.demo.read.CustomStringStringConverter; +import com.alibaba.easyexcel.test.util.TestFileUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.cglib.beans.BeanMap; +import com.alibaba.excel.util.BeanMapUtils; +import com.alibaba.excel.util.FileUtils; +import com.alibaba.excel.util.ListUtils; + +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.CreationHelper; +import org.apache.poi.ss.usermodel.Picture; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFCell; +import org.apache.poi.xssf.streaming.SXSSFDrawing; +import org.apache.poi.xssf.streaming.SXSSFRow; +import org.apache.poi.xssf.streaming.SXSSFSheet; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.jupiter.api.Test; + +@Slf4j +public class TempWriteTest { + + @Test + public void write() { + TempWriteData tempWriteData = new TempWriteData(); + tempWriteData.setName("zs\r\n \\ \r\n t4"); + EasyExcel.write(TestFileUtil.getPath() + "TempWriteTest" + System.currentTimeMillis() + ".xlsx", + TempWriteData.class) + .sheet() + .registerConverter(new CustomStringStringConverter()) + .doWrite(ListUtils.newArrayList(tempWriteData)); + + EasyExcel.write(TestFileUtil.getPath() + "TempWriteTest" + System.currentTimeMillis() + ".xlsx", + TempWriteData.class) + .sheet() + .doWrite(ListUtils.newArrayList(tempWriteData)); + + } + + @Test + public void cglib() { + TempWriteData tempWriteData = new TempWriteData(); + tempWriteData.setName("1"); + tempWriteData.setName1("2"); + BeanMap beanMap = BeanMapUtils.create(tempWriteData); + + log.info("d1{}", beanMap.get("name")); + log.info("d2{}", beanMap.get("name1")); + + TempWriteData tempWriteData2 = new TempWriteData(); + + Map map = new HashMap<>(); + map.put("name", "zs"); + BeanMap beanMap2 = BeanMapUtils.create(tempWriteData2); + beanMap2.putAll(map); + log.info("3{}", tempWriteData2.getName()); + + } + + @Test + public void imageWrite() throws Exception { + //String fileName = TestFileUtil.getPath() + "imageWrite" + System.currentTimeMillis() + ".xlsx"; + // + //// 这里 需要指定写用哪个class去写 + //try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) { + // // 这里注意 如果同一个sheet只要创建一次 + // WriteSheet writeSheet = EasyExcel.writerSheet("模板").build(); + // // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来 + // for (int i = 0; i < 5; i++) { + // // 分页去数据库查询数据 这里可以去数据库查询每一页的数据 + // List data = data(); + // excelWriter.write(data, writeSheet); + // } + //} + } + + @Test + public void imageWritePoi() throws Exception { + String file = "/Users/zhuangjiaju/test/imagetest" + System.currentTimeMillis() + ".xlsx"; + SXSSFWorkbook workbook = new SXSSFWorkbook(); + SXSSFSheet sheet = workbook.createSheet("测试"); + CreationHelper helper = workbook.getCreationHelper(); + SXSSFDrawing sxssfDrawin = sheet.createDrawingPatriarch(); + + byte[] imagebyte = FileUtils.readFileToByteArray(new File("/Users/zhuangjiaju/Documents/demo.jpg")); + + for (int i = 0; i < 1 * 10000; i++) { + SXSSFRow row = sheet.createRow(i); + SXSSFCell cell = row.createCell(0); + cell.setCellValue(i); + int pictureIdx = workbook.addPicture(imagebyte, Workbook.PICTURE_TYPE_JPEG); + ClientAnchor anchor = helper.createClientAnchor(); + anchor.setCol1(0); + anchor.setRow1(i); + // 插入图片 + Picture pict = sxssfDrawin.createPicture(anchor, pictureIdx); + pict.resize(); + log.info("新增行:{}", i); + } + FileOutputStream fileOutputStream = new FileOutputStream(file); + workbook.write(fileOutputStream); + fileOutputStream.flush(); + workbook.close(); + } + + @Test + public void tep() throws Exception { + String file = "/Users/zhuangjiaju/test/imagetest" + System.currentTimeMillis() + ".xlsx"; + SXSSFWorkbook workbook = new SXSSFWorkbook(); + SXSSFSheet sheet = workbook.createSheet("测试"); + CreationHelper helper = workbook.getCreationHelper(); + SXSSFDrawing sxssfDrawin = sheet.createDrawingPatriarch(); + + byte[] imagebyte = FileUtils.readFileToByteArray(new File("/Users/zhuangjiaju/Documents/demo.jpg")); + + for (int i = 0; i < 1 * 10000; i++) { + SXSSFRow row = sheet.createRow(i); + SXSSFCell cell = row.createCell(0); + cell.setCellValue(i); + int pictureIdx = workbook.addPicture(imagebyte, Workbook.PICTURE_TYPE_JPEG); + ClientAnchor anchor = helper.createClientAnchor(); + anchor.setCol1(0); + anchor.setRow1(i); + // 插入图片 + Picture pict = sxssfDrawin.createPicture(anchor, pictureIdx); + pict.resize(); + log.info("新增行:{}", i); + } + FileOutputStream fileOutputStream = new FileOutputStream(file); + workbook.write(fileOutputStream); + fileOutputStream.flush(); + workbook.close(); + } + + @Test + public void large() throws Exception { + String file = "/Users/zhuangjiaju/test/imagetest" + System.currentTimeMillis() + ".xlsx"; + SXSSFWorkbook workbook = new SXSSFWorkbook(new XSSFWorkbook( + new File( + "/Users/zhuangjiaju/IdeaProjects/easyexcel/easyexcel-test/src/test/resources/large/large07.xlsx"))); + SXSSFSheet sheet = workbook.createSheet("测试"); + + SXSSFRow row = sheet.createRow(500000); + SXSSFCell cell = row.createCell(0); + cell.setCellValue("test"); + + FileOutputStream fileOutputStream = new FileOutputStream(file); + workbook.write(fileOutputStream); + fileOutputStream.flush(); + workbook.close(); + + } +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java new file mode 100644 index 000000000..72c983979 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java @@ -0,0 +1,76 @@ +package com.alibaba.easyexcel.test.util; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.collections4.CollectionUtils; + +public class TestFileUtil { + + public static InputStream getResourcesFileInputStream(String fileName) { + return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName); + } + + public static String getPath() { + return TestFileUtil.class.getResource("/").getPath(); + } + + public static TestPathBuild pathBuild() { + return new TestPathBuild(); + } + + public static File createNewFile(String pathName) { + File file = new File(getPath() + pathName); + if (file.exists()) { + file.delete(); + } else { + if (!file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + } + return file; + } + + public static File readFile(String pathName) { + return new File(getPath() + pathName); + } + + public static File readUserHomeFile(String pathName) { + return new File(System.getProperty("user.home") + File.separator + pathName); + } + + /** + * build to test file path + **/ + public static class TestPathBuild { + private TestPathBuild() { + subPath = new ArrayList<>(); + } + + private final List subPath; + + public TestPathBuild sub(String dirOrFile) { + subPath.add(dirOrFile); + return this; + } + + public String getPath() { + if (CollectionUtils.isEmpty(subPath)) { + return TestFileUtil.class.getResource("/").getPath(); + } + if (subPath.size() == 1) { + return TestFileUtil.class.getResource("/").getPath() + subPath.get(0); + } + StringBuilder path = new StringBuilder(TestFileUtil.class.getResource("/").getPath()); + path.append(subPath.get(0)); + for (int i = 1; i < subPath.size(); i++) { + path.append(File.separator).append(subPath.get(i)); + } + return path.toString(); + } + + } + +} diff --git a/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestUtil.java b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestUtil.java new file mode 100644 index 000000000..02077f3a1 --- /dev/null +++ b/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestUtil.java @@ -0,0 +1,33 @@ +package com.alibaba.easyexcel.test.util; + +import java.text.ParseException; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; + +import com.alibaba.excel.util.DateUtils; + +import lombok.extern.slf4j.Slf4j; + +/** + * test util + * + * @author Jiaju Zhuang + */ +@Slf4j +public class TestUtil { + + public static final Date TEST_DATE; + public static final LocalDate TEST_LOCAL_DATE = LocalDate.of(2020, 1, 1); + public static final LocalDateTime TEST_LOCAL_DATE_TIME = LocalDateTime.of(2020, 1, 1, 1, 1, 1); + + static { + try { + TEST_DATE = DateUtils.parseDate("2020-01-01 01:01:01"); + } catch (ParseException e) { + log.error("init TestUtil error.", e); + throw new RuntimeException(e); + } + } + +} diff --git a/easyexcel-test/src/test/resources/bom/no_bom.csv b/easyexcel-test/src/test/resources/bom/no_bom.csv new file mode 100644 index 000000000..461c1eb00 --- /dev/null +++ b/easyexcel-test/src/test/resources/bom/no_bom.csv @@ -0,0 +1,11 @@ +姓名,年纪 +姓名0,20 +姓名1,20 +姓名2,20 +姓名3,20 +姓名4,20 +姓名5,20 +姓名6,20 +姓名7,20 +姓名8,20 +姓名9,20 \ No newline at end of file diff --git a/easyexcel-test/src/test/resources/bom/office_bom.csv b/easyexcel-test/src/test/resources/bom/office_bom.csv new file mode 100644 index 000000000..b6a80992d --- /dev/null +++ b/easyexcel-test/src/test/resources/bom/office_bom.csv @@ -0,0 +1,11 @@ +姓名,年纪 +姓名0,20 +姓名1,20 +姓名2,20 +姓名3,20 +姓名4,20 +姓名5,20 +姓名6,20 +姓名7,20 +姓名8,20 +姓名9,20 \ No newline at end of file diff --git a/easyexcel-test/src/test/resources/compatibility/t01.xls b/easyexcel-test/src/test/resources/compatibility/t01.xls new file mode 100644 index 000000000..eb0782f56 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t01.xls differ diff --git a/easyexcel-test/src/test/resources/compatibility/t02.xlsx b/easyexcel-test/src/test/resources/compatibility/t02.xlsx new file mode 100644 index 000000000..b8d755de8 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t02.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t03.xlsx b/easyexcel-test/src/test/resources/compatibility/t03.xlsx new file mode 100644 index 000000000..3a31ef78e Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t03.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t04.xlsx b/easyexcel-test/src/test/resources/compatibility/t04.xlsx new file mode 100644 index 000000000..7c95d425c Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t04.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t05.xlsx b/easyexcel-test/src/test/resources/compatibility/t05.xlsx new file mode 100644 index 000000000..248ec7d17 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t05.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t06.xlsx b/easyexcel-test/src/test/resources/compatibility/t06.xlsx new file mode 100644 index 000000000..b27be0279 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t06.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t07.xlsx b/easyexcel-test/src/test/resources/compatibility/t07.xlsx new file mode 100644 index 000000000..a7b0eac74 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t07.xlsx differ diff --git a/easyexcel-test/src/test/resources/compatibility/t09.xlsx b/easyexcel-test/src/test/resources/compatibility/t09.xlsx new file mode 100644 index 000000000..0b29141d2 Binary files /dev/null and b/easyexcel-test/src/test/resources/compatibility/t09.xlsx differ diff --git a/easyexcel-test/src/test/resources/converter/converter03.xls b/easyexcel-test/src/test/resources/converter/converter03.xls new file mode 100644 index 000000000..89c2ab6ef Binary files /dev/null and b/easyexcel-test/src/test/resources/converter/converter03.xls differ diff --git a/easyexcel-test/src/test/resources/converter/converter07.xlsx b/easyexcel-test/src/test/resources/converter/converter07.xlsx new file mode 100644 index 000000000..99ace690b Binary files /dev/null and b/easyexcel-test/src/test/resources/converter/converter07.xlsx differ diff --git a/easyexcel-test/src/test/resources/converter/converterCsv.csv b/easyexcel-test/src/test/resources/converter/converterCsv.csv new file mode 100644 index 000000000..01bcf2efc --- /dev/null +++ b/easyexcel-test/src/test/resources/converter/converterCsv.csv @@ -0,0 +1,2 @@ +大数的布尔(不支持),大数的数字,大数的字符串,大整数的布尔(不支持),大整数的数字,大整数的字符串,布尔的布尔,布尔的数字(不支持),布尔的字符串,字节的布尔(不支持),字节的数字,字节的字符串,日期的数字,日期的字符串,本地日期的数字,本地日期的字符串,双精度浮点的布尔(不支持),双精度浮点的数字,双精度浮点的字符串,浮点的布尔(不支持),浮点的数字,浮点的字符串,整型的布尔(不支持),整型的数字,整型的字符串,长整型的布尔(不支持),长整型的数字,长整型的字符串,短整型的布尔(不支持),短整型的数字,短整型的字符串,字符串的布尔,字符串的数字,字符串的字符串,字符串的错误,字符串的数字公式,字符串的字符串公式,字符串的数字日期 +1,1,1,1,1,1,TRUE,TRUE,TRUE,1,1,1,2020-01-01 01:01:01,2020-01-01 01:01:01,2020-01-01 01:01:01,2020-01-01 01:01:01,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,TRUE,1,测试,#VALUE!,2,1测试,2020-01-01 01:01:01 \ No newline at end of file diff --git a/easyexcel-test/src/test/resources/converter/img.jpg b/easyexcel-test/src/test/resources/converter/img.jpg new file mode 100644 index 000000000..953f39f34 Binary files /dev/null and b/easyexcel-test/src/test/resources/converter/img.jpg differ diff --git a/easyexcel-test/src/test/resources/dataformat/dataformat.xls b/easyexcel-test/src/test/resources/dataformat/dataformat.xls new file mode 100644 index 000000000..95c306ce0 Binary files /dev/null and b/easyexcel-test/src/test/resources/dataformat/dataformat.xls differ diff --git a/easyexcel-test/src/test/resources/dataformat/dataformat.xlsx b/easyexcel-test/src/test/resources/dataformat/dataformat.xlsx new file mode 100644 index 000000000..34a05cc92 Binary files /dev/null and b/easyexcel-test/src/test/resources/dataformat/dataformat.xlsx differ diff --git a/easyexcel-test/src/test/resources/dataformat/dataformatv2.xlsx b/easyexcel-test/src/test/resources/dataformat/dataformatv2.xlsx new file mode 100644 index 000000000..c49aa0440 Binary files /dev/null and b/easyexcel-test/src/test/resources/dataformat/dataformatv2.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/cellDataDemo.xlsx b/easyexcel-test/src/test/resources/demo/cellDataDemo.xlsx new file mode 100644 index 000000000..947229c70 Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/cellDataDemo.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/demo.csv b/easyexcel-test/src/test/resources/demo/demo.csv new file mode 100644 index 000000000..97f2a428d --- /dev/null +++ b/easyexcel-test/src/test/resources/demo/demo.csv @@ -0,0 +1,11 @@ +字符串标题,日期标题,数字标题 +字符串0,2020-01-01 01:01:00,1 +字符串1,2020-01-02 01:01:00,2 +字符串2,2020-01-03 01:01:00,3 +字符串3,2020-01-04 01:01:00,4 +字符串4,2020-01-05 01:01:00,5 +字符串5,2020-01-06 01:01:00,6 +字符串6,2020-01-07 01:01:00,7 +字符串7,2020-01-08 01:01:00,8 +字符串8,2020-01-09 01:01:00,9 +字符串9,2020-01-10 01:01:00,10 \ No newline at end of file diff --git a/easyexcel-test/src/test/resources/demo/demo.xlsx b/easyexcel-test/src/test/resources/demo/demo.xlsx new file mode 100644 index 000000000..662bb9717 Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/demo.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/extra.xlsx b/easyexcel-test/src/test/resources/demo/extra.xlsx new file mode 100644 index 000000000..4936b05a0 Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/extra.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/fill/complex.xlsx b/easyexcel-test/src/test/resources/demo/fill/complex.xlsx new file mode 100644 index 000000000..537671369 Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/fill/complex.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/fill/complexFillWithTable.xlsx b/easyexcel-test/src/test/resources/demo/fill/complexFillWithTable.xlsx new file mode 100644 index 000000000..4de1a1e79 Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/fill/complexFillWithTable.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/fill/composite.xlsx b/easyexcel-test/src/test/resources/demo/fill/composite.xlsx new file mode 100644 index 000000000..c76a2b12b Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/fill/composite.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/fill/horizontal.xlsx b/easyexcel-test/src/test/resources/demo/fill/horizontal.xlsx new file mode 100644 index 000000000..c8b4564ee Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/fill/horizontal.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/fill/list.xlsx b/easyexcel-test/src/test/resources/demo/fill/list.xlsx new file mode 100644 index 000000000..d29e05e76 Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/fill/list.xlsx differ diff --git a/easyexcel-test/src/test/resources/demo/fill/simple.xlsx b/easyexcel-test/src/test/resources/demo/fill/simple.xlsx new file mode 100644 index 000000000..7514d1dab Binary files /dev/null and b/easyexcel-test/src/test/resources/demo/fill/simple.xlsx differ diff --git a/easyexcel-test/src/test/resources/extra/extra.xls b/easyexcel-test/src/test/resources/extra/extra.xls new file mode 100644 index 000000000..89f389bed Binary files /dev/null and b/easyexcel-test/src/test/resources/extra/extra.xls differ diff --git a/easyexcel-test/src/test/resources/extra/extra.xlsx b/easyexcel-test/src/test/resources/extra/extra.xlsx new file mode 100644 index 000000000..4936b05a0 Binary files /dev/null and b/easyexcel-test/src/test/resources/extra/extra.xlsx differ diff --git a/easyexcel-test/src/test/resources/extra/extraRelationships.xlsx b/easyexcel-test/src/test/resources/extra/extraRelationships.xlsx new file mode 100644 index 000000000..5784cd8b6 Binary files /dev/null and b/easyexcel-test/src/test/resources/extra/extraRelationships.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/annotation.xls b/easyexcel-test/src/test/resources/fill/annotation.xls new file mode 100644 index 000000000..de09678c7 Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/annotation.xls differ diff --git a/easyexcel-test/src/test/resources/fill/annotation.xlsx b/easyexcel-test/src/test/resources/fill/annotation.xlsx new file mode 100644 index 000000000..2a4a92b60 Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/annotation.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/byName.xls b/easyexcel-test/src/test/resources/fill/byName.xls new file mode 100644 index 000000000..e07fd503e Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/byName.xls differ diff --git a/easyexcel-test/src/test/resources/fill/byName.xlsx b/easyexcel-test/src/test/resources/fill/byName.xlsx new file mode 100644 index 000000000..327e0557a Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/byName.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/complex.xls b/easyexcel-test/src/test/resources/fill/complex.xls new file mode 100644 index 000000000..d5758951c Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/complex.xls differ diff --git a/easyexcel-test/src/test/resources/fill/complex.xlsx b/easyexcel-test/src/test/resources/fill/complex.xlsx new file mode 100644 index 000000000..537671369 Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/complex.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/composite.xls b/easyexcel-test/src/test/resources/fill/composite.xls new file mode 100644 index 000000000..e48aa0c14 Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/composite.xls differ diff --git a/easyexcel-test/src/test/resources/fill/composite.xlsx b/easyexcel-test/src/test/resources/fill/composite.xlsx new file mode 100644 index 000000000..c76a2b12b Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/composite.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/horizontal.xls b/easyexcel-test/src/test/resources/fill/horizontal.xls new file mode 100644 index 000000000..570f9012d Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/horizontal.xls differ diff --git a/easyexcel-test/src/test/resources/fill/horizontal.xlsx b/easyexcel-test/src/test/resources/fill/horizontal.xlsx new file mode 100644 index 000000000..c8b4564ee Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/horizontal.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/simple.csv b/easyexcel-test/src/test/resources/fill/simple.csv new file mode 100644 index 000000000..a51534815 --- /dev/null +++ b/easyexcel-test/src/test/resources/fill/simple.csv @@ -0,0 +1,2 @@ +姓名,数字,复杂,忽略,空 +{name},{number},{name}今年{number}岁了,\{name\}忽略,{name},空{.empty} \ No newline at end of file diff --git a/easyexcel-test/src/test/resources/fill/simple.xls b/easyexcel-test/src/test/resources/fill/simple.xls new file mode 100644 index 000000000..317ef6deb Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/simple.xls differ diff --git a/easyexcel-test/src/test/resources/fill/simple.xlsx b/easyexcel-test/src/test/resources/fill/simple.xlsx new file mode 100644 index 000000000..a441eba8c Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/simple.xlsx differ diff --git a/easyexcel-test/src/test/resources/fill/style.xls b/easyexcel-test/src/test/resources/fill/style.xls new file mode 100644 index 000000000..3127743c0 Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/style.xls differ diff --git a/easyexcel-test/src/test/resources/fill/style.xlsx b/easyexcel-test/src/test/resources/fill/style.xlsx new file mode 100644 index 000000000..062540d0d Binary files /dev/null and b/easyexcel-test/src/test/resources/fill/style.xlsx differ diff --git a/easyexcel-test/src/test/resources/large/fill.xlsx b/easyexcel-test/src/test/resources/large/fill.xlsx new file mode 100644 index 000000000..c3c376d31 Binary files /dev/null and b/easyexcel-test/src/test/resources/large/fill.xlsx differ diff --git a/easyexcel-test/src/test/resources/large/large07.xlsx b/easyexcel-test/src/test/resources/large/large07.xlsx new file mode 100644 index 000000000..a317e71f9 Binary files /dev/null and b/easyexcel-test/src/test/resources/large/large07.xlsx differ diff --git a/easyexcel-test/src/test/resources/logback.xml b/easyexcel-test/src/test/resources/logback.xml new file mode 100644 index 000000000..74ebe7539 --- /dev/null +++ b/easyexcel-test/src/test/resources/logback.xml @@ -0,0 +1,15 @@ + + + + + + + ${CONSOLE_LOG_PATTERN} + utf8 + + + + + + + diff --git a/easyexcel-test/src/test/resources/multiplesheets/multiplesheets.xls b/easyexcel-test/src/test/resources/multiplesheets/multiplesheets.xls new file mode 100644 index 000000000..a5601288c Binary files /dev/null and b/easyexcel-test/src/test/resources/multiplesheets/multiplesheets.xls differ diff --git a/easyexcel-test/src/test/resources/multiplesheets/multiplesheets.xlsx b/easyexcel-test/src/test/resources/multiplesheets/multiplesheets.xlsx new file mode 100644 index 000000000..f90680a14 Binary files /dev/null and b/easyexcel-test/src/test/resources/multiplesheets/multiplesheets.xlsx differ diff --git a/easyexcel-test/src/test/resources/simple/simple07.xlsx b/easyexcel-test/src/test/resources/simple/simple07.xlsx new file mode 100644 index 000000000..3d25fcd8c Binary files /dev/null and b/easyexcel-test/src/test/resources/simple/simple07.xlsx differ diff --git a/easyexcel-test/src/test/resources/temp/issue1663/template.xlsx b/easyexcel-test/src/test/resources/temp/issue1663/template.xlsx new file mode 100644 index 000000000..a968ff415 Binary files /dev/null and b/easyexcel-test/src/test/resources/temp/issue1663/template.xlsx differ diff --git a/easyexcel-test/src/test/resources/temp/issue2443/date1.xlsx b/easyexcel-test/src/test/resources/temp/issue2443/date1.xlsx new file mode 100644 index 000000000..92ef811d9 Binary files /dev/null and b/easyexcel-test/src/test/resources/temp/issue2443/date1.xlsx differ diff --git a/easyexcel-test/src/test/resources/temp/issue2443/date2.xlsx b/easyexcel-test/src/test/resources/temp/issue2443/date2.xlsx new file mode 100644 index 000000000..c6feb3251 Binary files /dev/null and b/easyexcel-test/src/test/resources/temp/issue2443/date2.xlsx differ diff --git a/easyexcel-test/src/test/resources/template/template03.xls b/easyexcel-test/src/test/resources/template/template03.xls new file mode 100644 index 000000000..7c17eee9e Binary files /dev/null and b/easyexcel-test/src/test/resources/template/template03.xls differ diff --git a/easyexcel-test/src/test/resources/template/template07.xlsx b/easyexcel-test/src/test/resources/template/template07.xlsx new file mode 100644 index 000000000..a046fbcba Binary files /dev/null and b/easyexcel-test/src/test/resources/template/template07.xlsx differ diff --git a/easyexcel/pom.xml b/easyexcel/pom.xml new file mode 100644 index 000000000..2df8f71a7 --- /dev/null +++ b/easyexcel/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + + com.alibaba + easyexcel-parent + ${revision} + ../pom.xml + + + https://github.com/alibaba/easyexcel + jar + easyexcel + easyexcel + + + + + com.alibaba + easyexcel-core + + + + diff --git a/easyexcel/src/main/java/com/alibaba/excel/Empty.java b/easyexcel/src/main/java/com/alibaba/excel/Empty.java new file mode 100644 index 000000000..7d936df08 --- /dev/null +++ b/easyexcel/src/main/java/com/alibaba/excel/Empty.java @@ -0,0 +1,9 @@ +package com.alibaba.excel; + +/** + * empty + * + * @author Jiaju Zhuang + */ +public class Empty { +} diff --git a/img/WechatIMG8.png b/img/WechatIMG8.png deleted file mode 100644 index a87e52ed6..000000000 Binary files a/img/WechatIMG8.png and /dev/null differ diff --git a/img/readme/large.png b/img/readme/large.png new file mode 100644 index 000000000..0188af9e9 Binary files /dev/null and b/img/readme/large.png differ diff --git a/img/style/eclipse/step.jpg b/img/style/eclipse/step.jpg new file mode 100644 index 000000000..fcbb85728 Binary files /dev/null and b/img/style/eclipse/step.jpg differ diff --git a/img/style/idea/step1.png b/img/style/idea/step1.png new file mode 100644 index 000000000..d4a39f551 Binary files /dev/null and b/img/style/idea/step1.png differ diff --git a/img/style/idea/step2.png b/img/style/idea/step2.png new file mode 100644 index 000000000..2dd04148a Binary files /dev/null and b/img/style/idea/step2.png differ diff --git a/img/style/idea/step3.png b/img/style/idea/step3.png new file mode 100644 index 000000000..2fbde4a0a Binary files /dev/null and b/img/style/idea/step3.png differ diff --git a/lombok.config b/lombok.config new file mode 100644 index 000000000..50dfe6165 --- /dev/null +++ b/lombok.config @@ -0,0 +1,2 @@ +lombok.toString.callSuper = CALL +lombok.equalsAndHashCode.callSuper= CALL \ No newline at end of file diff --git a/mvnw b/mvnw new file mode 100755 index 000000000..5643201c7 --- /dev/null +++ b/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 000000000..8a15b7f31 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index f0270b67b..ab227234b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,23 +1,31 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 com.alibaba - easyexcel - 1.1.2-beta5 - jar - easyexcel - + easyexcel-parent + pom + ${revision} + easyexcel-parent easyexcel is a excel handle tools written in Java - https://github.com/alibaba/easyexcel 2018 + + easyexcel-core + easyexcel-support + easyexcel-test + easyexcel + + - + 4.0.3 UTF-8 - 1.7 + 1.8 + true + true + true @@ -25,13 +33,6 @@ scm:git:https://git@github.com/alibaba/easyexcel.git - - - - - - - Alibaba Group @@ -44,6 +45,11 @@ jipengfei 1558143046@qq.com + + zhuangjiaju + Jiaju Zhuang + zhuangjiaju@qq.com + @@ -55,30 +61,7 @@ - - - org.apache.poi - poi - 3.17 - - - org.apache.poi - poi-ooxml - 3.17 - - - cglib - cglib - 3.1 - - - junit - junit - 4.12 - test - - ossrh @@ -90,19 +73,200 @@ + + + + com.alibaba + easyexcel + ${revision} + + + + com.alibaba + easyexcel-core + ${revision} + + + + com.alibaba + easyexcel-support + 3.3.4 + + + + org.apache.commons + commons-csv + 1.11.0 + + + + org.apache.poi + poi + 5.2.5 + + + org.apache.poi + poi-ooxml + 5.2.5 + + + + org.ehcache + ehcache + 3.9.11 + + + commons-io + commons-io + 2.16.1 + + + org.slf4j + slf4j-api + 1.7.36 + + + + + org.projectlombok + lombok + 1.18.32 + + + + + + + + org.slf4j + slf4j-api + + + org.projectlombok + lombok + provided + + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.8 + + + com.alibaba.p3c + p3c-pmd + 1.3.6 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.0 + + + org.projectlombok + lombok-maven-plugin + 1.18.20.0 + + + org.apache.maven.plugins + maven-surefire-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + org.codehaus.mojo + flatten-maven-plugin + 1.2.7 + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.13 + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + + true + true + + rulesets/java/ali-comment.xml + rulesets/java/ali-concurrent.xml + rulesets/java/ali-constant.xml + rulesets/java/ali-exception.xml + rulesets/java/ali-flowcontrol.xml + rulesets/java/ali-naming.xml + rulesets/java/ali-oop.xml + rulesets/java/ali-orm.xml + rulesets/java/ali-other.xml + rulesets/java/ali-set.xml + + + com/alibaba/excel/event/AnalysisEventListener.java + com/alibaba/excel/metadata/DataFormatter.java + com/alibaba/excel/util/DateUtils.java + com/alibaba/excel/util/MapUtils.java + com/alibaba/excel/util/EasyExcelTempFileCreationStrategy.java + com/alibaba/excel/metadata/format/DataFormatter.java + com/alibaba/excel/metadata/format/ExcelGeneralNumberFormat.java + com/alibaba/excel/metadata/csv/CsvDataFormat.java + com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java + com/alibaba/excel/analysis/v07/handlers/sax/SharedStringsTableHandler.java + com/alibaba/excel/write/executor/ExcelWriteFillExecutor.java + + + + + pmd-check-verify + validate + + check + + + + org.apache.maven.plugins maven-compiler-plugin - 1.6 - 1.6 + 1.8 + 1.8 + org.apache.maven.plugins maven-source-plugin - 2.1 true @@ -115,11 +279,9 @@ - org.apache.maven.plugins maven-gpg-plugin - 1.6 verify @@ -128,11 +290,17 @@ + + + + --pinentry-mode + loopback + + org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 attach-javadocs @@ -142,6 +310,62 @@ + + org.projectlombok + lombok-maven-plugin + + + generate-sources + + delombok + + + + + + + org.codehaus.mojo + flatten-maven-plugin + + true + oss + + + + flatten.process-resources + process-resources + + flatten + + + + flatten + package + + flatten + + + + flatten.clean + clean + + clean + + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + true + + ossrh + https://oss.sonatype.org/ + true + + - \ No newline at end of file + diff --git a/problem.md b/problem.md deleted file mode 100644 index 9244148ee..000000000 --- a/problem.md +++ /dev/null @@ -1,116 +0,0 @@ -# 常见问题汇总 - -## 1、系统环境变量缺失或JDK版本不支持 - -``` -java.lang.NullPointerException - at sun.awt.FontConfiguration.getVersion(FontConfiguration.java:1264) - at sun.awt.FontConfiguration.readFontConfigFile(FontConfiguration.java:219) - at sun.awt.FontConfiguration.init(FontConfiguration.java:107) - at sun.awt.X11FontManager.createFontConfiguration(X11FontManager.java:774) - at sun.font.SunFontManager$2.run(SunFontManager.java:431) - at java.security.AccessController.doPrivileged(Native Method) - at sun.font.SunFontManager.(SunFontManager.java:376) - at sun.awt.FcFontManager.(FcFontManager.java:35) - at sun.awt.X11FontManager.(X11FontManager.java:57) - at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) - at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) - at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) - at java.lang.reflect.Constructor.newInstance(Constructor.java:423) - at java.lang.Class.newInstance(Class.java:442) - at sun.font.FontManagerFactory$1.run(FontManagerFactory.java:83) - at java.security.AccessController.doPrivileged(Native Method) - at sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74) - at java.awt.Font.getFont2D(Font.java:495) - at java.awt.Font.canDisplayUpTo(Font.java:2080) - at java.awt.font.TextLayout.singleFont(TextLayout.java:470) - at java.awt.font.TextLayout.(TextLayout.java:531) - at org.apache.poi.ss.util.SheetUtil.getDefaultCharWidth(SheetUtil.java:275) - at org.apache.poi.xssf.streaming.AutoSizeColumnTracker.(AutoSizeColumnTracker.java:117) - at org.apache.poi.xssf.streaming.SXSSFSheet.(SXSSFSheet.java:79) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createAndRegisterSXSSFSheet(SXSSFWorkbook.java:656) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:677) - at org.apache.poi.xssf.streaming.SXSSFWorkbook.createSheet(SXSSFWorkbook.java:83) - at com.alibaba.excel.write.context.GenerateContextImpl.buildCurrentSheet(GenerateContextImpl.java:93) - at com.alibaba.excel.write.ExcelBuilderImpl.addContent(ExcelBuilderImpl.java:53) - at com.alibaba.excel.ExcelWriter.write(ExcelWriter.java:44) -``` - -### 解决方法 - -该异常由于自己的环境变量缺少swing需要的字体配置,检查自己应用是否有配置-Djava.awt.headless=true,如果没有请加上该系统参数,可以解决问题。如果仍旧不行,在dockerfile中增加字体安装命令: -![粘贴图片.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/a857edfbc8199db7bb35b9e99f1f57d5.png) -参考: -https://lark.alipay.com/aone355606/gfqllg/ulptif -https://stackoverflow.com/questions/30626136/cannot-load-font-in-jre-8 http://www.jianshu.com/p/c05b5fc71bd0 -## 2、xls格式错用xlsx方式解析 - -``` -org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF) - at org.apache.poi.openxml4j.opc.internal.ZipHelper.verifyZipHeader(ZipHelper.java:172) - at org.apache.poi.openxml4j.opc.internal.ZipHelper.openZipStream(ZipHelper.java:229) - at org.apache.poi.openxml4j.opc.ZipPackage.(ZipPackage.java:97) - at org.apache.poi.openxml4j.opc.OPCPackage.open(OPCPackage.java:342) - at com.alibaba.excel.read.v07.XlsxSaxAnalyser.(XlsxSaxAnalyser.java:46) - at com.alibaba.excel.read.ExcelAnalyserImpl.getSaxAnalyser(ExcelAnalyserImpl.java:30) - at com.alibaba.excel.read.ExcelAnalyserImpl.analysis(ExcelAnalyserImpl.java:51) - at com.alibaba.excel.ExcelReader.read(ExcelReader.java:55) - at read.v07.Read2007Xlsx.noModel(Read2007Xlsx.java:42) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) - at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) - at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) - at mockit.integration.junit4.internal.BlockJUnit4ClassRunnerDecorator.executeTest(BlockJUnit4ClassRunnerDecorator.java:126) - at mockit.integration.junit4.internal.BlockJUnit4ClassRunnerDecorator.invokeExplosively(BlockJUnit4ClassRunnerDecorator.java:104) - at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java) - at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) - at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) - at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) - at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) - at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) - at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) - at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) - at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) - at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) - at org.junit.runners.ParentRunner.run(ParentRunner.java:363) - at org.junit.runner.JUnitCore.run(JUnitCore.java:137) - at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) - at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51) - at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237) - at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) -``` - -### 解决方法 - -该异常时由于03版的xls,文件用07版的方式做解析的报错,请检查excelType是否设置错误。或者是不是手动去修改了excel文件名后缀的xls为xlsx。 - -## 3、xlsx错用xls解析 - -``` -org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF) - at org.apache.poi.poifs.storage.HeaderBlock.(HeaderBlock.java:152) - at org.apache.poi.poifs.storage.HeaderBlock.(HeaderBlock.java:140) - at org.apache.poi.poifs.filesystem.NPOIFSFileSystem.(NPOIFSFileSystem.java:302) - at org.apache.poi.poifs.filesystem.POIFSFileSystem.(POIFSFileSystem.java:87) - at com.alibaba.excel.read.v03.XlsSaxAnalyser.(XlsSaxAnalyser.java:55) - at com.alibaba.excel.read.ExcelAnalyserImpl.getSaxAnalyser(ExcelAnalyserImpl.java:27) - at com.alibaba.excel.read.ExcelAnalyserImpl.analysis(ExcelAnalyserImpl.java:51) - at com.alibaba.excel.ExcelReader.read(ExcelReader.java:55) - at read.v03.XLS2003FunctionTest.testExcel2003NoModel(XLS2003FunctionTest.java:31) - at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) - at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) - at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) - at java.lang.reflect.Method.invoke(Method.java:498) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.runTest(JUnitTestCaseDecorator.java:142) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.originalRunBare(JUnitTestCaseDecorator.java:102) - at mockit.integration.junit3.internal.JUnitTestCaseDecorator.runBare(JUnitTestCaseDecorator.java:87) -``` -原理和原因大致同上 \ No newline at end of file diff --git a/quickstart.md b/quickstart.md index afba79518..dcf12add7 100644 --- a/quickstart.md +++ b/quickstart.md @@ -1,368 +1,2 @@ -# easyexcel核心功能 - -## *读任意大小的03、07版Excel不会OO]
-## *读Excel自动通过注解,把结果映射为java模型
-## *读Excel支持多sheet
-## *读Excel时候是否对Excel内容做trim()增加容错
-## *写小量数据的03版Excel(不要超过2000行)
-## *写任意大07版Excel不会OOM
-## *写Excel通过注解将表头自动写入Excel
-## *写Excel可以自定义Excel样式 如:字体,加粗,表头颜色,数据内容颜色
-## *写Excel到多个不同sheet
-## *写Excel时一个sheet可以写多个Table
-## *写Excel时候自定义是否需要写表头
- -## 二方包依赖 - -使用前最好咨询下最新版,或者到mvn仓库搜索先easyexcel的最新版 - -``` - - com.alibaba - easyexcel - 1.0.0-RELEASE - -``` -## 读Excel - -使用easyexcel解析03、07版本的Excel只是ExcelTypeEnum不同,其他使用完全相同,使用者无需知道底层解析的差异。 - -### 无java模型直接把excel解析的每行结果以List<String>返回 在ExcelListener获取解析结果 - -读excel代码示例如下: -``` - @Test - public void testExcel2003NoModel() { - InputStream inputStream = getInputStream("loan1.xls"); - try { - // 解析每行结果在listener中处理 - ExcelListener listener = new ExcelListener(); - - ExcelReader excelReader = new ExcelReader(inputStream, ExcelTypeEnum.XLS, null, listener); - excelReader.read(); - } catch (Exception e) { - - } finally { - try { - inputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } -``` -ExcelListener示例代码如下: -``` - /* 解析监听器, - * 每解析一行会回调invoke()方法。 - * 整个excel解析结束会执行doAfterAllAnalysed()方法 - * - * 下面只是我写的一个样例而已,可以根据自己的逻辑修改该类。 - * @author jipengfei - * @date 2017/03/14 - */ -public class ExcelListener extends AnalysisEventListener { - - //自定义用于暂时存储data。 - //可以通过实例获取该值 - private List datas = new ArrayList(); - public void invoke(Object object, AnalysisContext context) { - System.out.println("当前行:"+context.getCurrentRowNum()); - System.out.println(object); - datas.add(object);//数据存储到list,供批量处理,或后续自己业务逻辑处理。 - doSomething(object);//根据自己业务做处理 - } - private void doSomething(Object object) { - //1、入库调用接口 - } - public void doAfterAllAnalysed(AnalysisContext context) { - // datas.clear();//解析结束销毁不用的资源 - } - public List getDatas() { - return datas; - } - public void setDatas(List datas) { - this.datas = datas; - } -} -``` -### 有java模型映射 -java模型写法如下: -``` -public class LoanInfo extends BaseRowModel { - @ExcelProperty(index = 0) - private String bankLoanId; - - @ExcelProperty(index = 1) - private Long customerId; - - @ExcelProperty(index = 2,format = "yyyy/MM/dd") - private Date loanDate; - - @ExcelProperty(index = 3) - private BigDecimal quota; - - @ExcelProperty(index = 4) - private String bankInterestRate; - - @ExcelProperty(index = 5) - private Integer loanTerm; - - @ExcelProperty(index = 6,format = "yyyy/MM/dd") - private Date loanEndDate; - - @ExcelProperty(index = 7) - private BigDecimal interestPerMonth; - - @ExcelProperty(value = {"一级表头","二级表头"}) - private BigDecimal sax; -} -``` -@ExcelProperty(index = 3)数字代表该字段与excel对应列号做映射,也可以采用 @ExcelProperty(value = {"一级表头","二级表头"})用于解决不确切知道excel第几列和该字段映射,位置不固定,但表头的内容知道的情况。 -``` - @Test - public void testExcel2003WithReflectModel() { - InputStream inputStream = getInputStream("loan1.xls"); - try { - // 解析每行结果在listener中处理 - AnalysisEventListener listener = new ExcelListener(); - - ExcelReader excelReader = new ExcelReader(inputStream, ExcelTypeEnum.XLS, null, listener); - - excelReader.read(new Sheet(1, 2, LoanInfo.class)); - } catch (Exception e) { - - } finally { - try { - inputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - } -``` -带模型解析与不带模型解析主要在构造new Sheet(1, 2, LoanInfo.class)时候包含class。Class需要继承BaseRowModel暂时BaseRowModel没有任何内容,后面升级可能会增加一些默认的数据。 - -## 写Excel - -### 每行数据是List<String>无表头 - -``` - OutputStream out = new FileOutputStream("/Users/jipengfei/77.xlsx"); - try { - ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX,false); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 0); - sheet1.setSheetName("第一个sheet"); - writer.write(getListString(), sheet1); - writer.finish(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } -``` - -### 每行数据是一个java模型有表头----表头层级为一 - -生成Excel格式如下图 -![屏幕快照 2017-06-02 上午9.49.39.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/dfcb44d05380e2e26bce93f850d9fc99.png) - -模型写法如下: -``` -public class ExcelPropertyIndexModel extends BaseRowModel { - - @ExcelProperty(value = "姓名" ,index = 0) - private String name; - - @ExcelProperty(value = "年龄",index = 1) - private String age; - - @ExcelProperty(value = "邮箱",index = 2) - private String email; - - @ExcelProperty(value = "地址",index = 3) - private String address; - - @ExcelProperty(value = "性别",index = 4) - private String sax; - - @ExcelProperty(value = "高度",index = 5) - private String heigh; - - @ExcelProperty(value = "备注",index = 6) - private String last; -} -``` - @ExcelProperty(value = "姓名",index = 0) value是表头数据,默认会写在excel的表头位置,index代表第几列。 -``` - @Test - public void test1() throws FileNotFoundException { - OutputStream out = new FileOutputStream("/Users/jipengfei/78.xlsx"); - try { - ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 0,ExcelPropertyIndexModel.class); - writer.write(getData(), sheet1); - writer.finish(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } -``` - -### 每行数据是一个java模型有表头----表头层级为多层级 - -生成Excel格式如下图: -![屏幕快照 2017-06-02 上午9.53.07.png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/0cdb1673665e7940cd670871afb4b3d7.png) -java模型写法如下: -``` -public class MultiLineHeadExcelModel extends BaseRowModel { - - @ExcelProperty(value = {"表头1","表头1","表头31"},index = 0) - private String p1; - - @ExcelProperty(value = {"表头1","表头1","表头32"},index = 1) - private String p2; - - @ExcelProperty(value = {"表头3","表头3","表头3"},index = 2) - private int p3; - - @ExcelProperty(value = {"表头4","表头4","表头4"},index = 3) - private long p4; - - @ExcelProperty(value = {"表头5","表头51","表头52"},index = 4) - private String p5; - - @ExcelProperty(value = {"表头6","表头61","表头611"},index = 5) - private String p6; - - @ExcelProperty(value = {"表头6","表头61","表头612"},index = 6) - private String p7; - - @ExcelProperty(value = {"表头6","表头62","表头621"},index = 7) - private String p8; - - @ExcelProperty(value = {"表头6","表头62","表头622"},index = 8) - private String p9; -} -``` -写Excel写法同上,只需将ExcelPropertyIndexModel.class改为MultiLineHeadExcelModel.class - - -### 一个Excel多个sheet写法 - -``` - @Test - public void test1() throws FileNotFoundException { - - OutputStream out = new FileOutputStream("/Users/jipengfei/77.xlsx"); - try { - ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX,false); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 0); - sheet1.setSheetName("第一个sheet"); - writer.write(getListString(), sheet1); - - //写第二个sheet sheet2 模型上打有表头的注解,合并单元格 - Sheet sheet2 = new Sheet(2, 3, MultiLineHeadExcelModel.class, "第二个sheet", null); - sheet2.setTableStyle(getTableStyle1()); - writer.write(getModeldatas(), sheet2); - - //写sheet3 模型上没有注解,表头数据动态传入 - List> head = new ArrayList>(); - List headCoulumn1 = new ArrayList(); - List headCoulumn2 = new ArrayList(); - List headCoulumn3 = new ArrayList(); - headCoulumn1.add("第一列"); - headCoulumn2.add("第二列"); - headCoulumn3.add("第三列"); - head.add(headCoulumn1); - head.add(headCoulumn2); - head.add(headCoulumn3); - Sheet sheet3 = new Sheet(3, 1, NoAnnModel.class, "第三个sheet", head); - writer.write(getNoAnnModels(), sheet3); - writer.finish(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } -``` - -### 一个sheet中有多个表格 - -``` -@Test - public void test2() throws FileNotFoundException { - OutputStream out = new FileOutputStream("/Users/jipengfei/77.xlsx"); - try { - ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX,false); - - //写sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 0); - sheet1.setSheetName("第一个sheet"); - Table table1 = new Table(1); - writer.write(getListString(), sheet1, table1); - writer.write(getListString(), sheet1, table1); - - //写sheet2 模型上打有表头的注解 - Table table2 = new Table(2); - table2.setTableStyle(getTableStyle1()); - table2.setClazz(MultiLineHeadExcelModel.class); - writer.write(getModeldatas(), sheet1, table2); - - //写sheet3 模型上没有注解,表头数据动态传入,此情况下模型field顺序与excel现实顺序一致 - List> head = new ArrayList>(); - List headCoulumn1 = new ArrayList(); - List headCoulumn2 = new ArrayList(); - List headCoulumn3 = new ArrayList(); - headCoulumn1.add("第一列"); - headCoulumn2.add("第二列"); - headCoulumn3.add("第三列"); - head.add(headCoulumn1); - head.add(headCoulumn2); - head.add(headCoulumn3); - Table table3 = new Table(3); - table3.setHead(head); - table3.setClazz(NoAnnModel.class); - table3.setTableStyle(getTableStyle2()); - writer.write(getNoAnnModels(), sheet1, table3); - writer.write(getNoAnnModels(), sheet1, table3); - - writer.finish(); - } catch (Exception e) { - e.printStackTrace(); - } finally { - try { - out.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } -``` -## 测试数据分析 - -![POI usermodel PK easyexcel(Excel 2003).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/02c4bfbbab99a649788523d04f84a42f.png) -![POI usermodel PK easyexcel(Excel 2007).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/f6a8a19ec959f0eb564e652de523fc9e.png) -![POI usermodel PK easyexcel(Excel 2003) (1).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/26888f7ea1cb8dc56db494926544edf7.png) -![POI usermodel PK easyexcel(Excel 2007) (1).png](http://ata2-img.cn-hangzhou.img-pub.aliyun-inc.com/4de1ac95bdfaa4b1870b224af4f4cb75.png) -从上面的性能测试可以看出easyexcel在解析耗时上比poiuserModel模式弱了一些。主要原因是我内部采用了反射做模型字段映射,中间我也加了cache,但感觉这点差距可以接受的。但在内存消耗上差别就比较明显了,easyexcel在后面文件再增大,内存消耗几乎不会增加了。但poi userModel就不一样了,简直就要爆掉了。想想一个excel解析200M,同时有20个人再用估计一台机器就挂了。 - +# 请登录官网查看 +官网地址:[https://alibaba-easyexcel.github.io](https://alibaba-easyexcel.github.io) \ No newline at end of file diff --git a/src/main/java/com/alibaba/excel/EasyExcelFactory.java b/src/main/java/com/alibaba/excel/EasyExcelFactory.java deleted file mode 100644 index 5327dc93a..000000000 --- a/src/main/java/com/alibaba/excel/EasyExcelFactory.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.alibaba.excel; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.event.WriteHandler; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.support.ExcelTypeEnum; - -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * Reader and writer factory class - * - * @author jipengfei - */ -public class EasyExcelFactory { - - /** - * Quickly read small files,no more than 10,000 lines. - * - * @param in the POI filesystem that contains the Workbook stream. - * @param sheet read sheet. - * @return analysis result. - */ - public static List read(InputStream in, Sheet sheet) { - final List rows = new ArrayList(); - new ExcelReader(in, null, new AnalysisEventListener() { - @Override - public void invoke(Object object, AnalysisContext context) { - rows.add(object); - } - - @Override - public void doAfterAllAnalysed(AnalysisContext context) { - } - }, false).read(sheet); - return rows; - } - - /** - * Parsing large file - * - * @param in the POI filesystem that contains the Workbook stream. - * @param sheet read sheet. - * @param listener Callback method after each row is parsed. - */ - public static void readBySax(InputStream in, Sheet sheet, AnalysisEventListener listener) { - new ExcelReader(in, null, listener).read(sheet); - } - - /** - * Get ExcelReader. - * - * @param in the POI filesystem that contains the Workbook stream. - * @param listener Callback method after each row is parsed. - * @return ExcelReader. - */ - public static ExcelReader getReader(InputStream in, AnalysisEventListener listener) { - return new ExcelReader(in, null, listener); - } - - /** - * Get ExcelWriter - * - * @param outputStream the java OutputStream you wish to write the data to. - * @return new ExcelWriter. - */ - public static ExcelWriter getWriter(OutputStream outputStream) { - return new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true); - } - - /** - * Get ExcelWriter - * - * @param outputStream the java OutputStream you wish to write the data to. - * @param typeEnum 03 or 07 - * @param needHead Do you need to write the header to the file? - * @return new ExcelWriter - */ - public static ExcelWriter getWriter(OutputStream outputStream, ExcelTypeEnum typeEnum, boolean needHead) { - return new ExcelWriter(outputStream, typeEnum, needHead); - } - - /** - * Get ExcelWriter with a template file - * - * @param temp Append data after a POI file , Can be null(the template POI filesystem that contains the - * Workbook stream) - * @param outputStream the java OutputStream you wish to write the data to - * @param typeEnum 03 or 07 - * @return new ExcelWriter - */ - public static ExcelWriter getWriterWithTemp(InputStream temp, OutputStream outputStream, ExcelTypeEnum typeEnum, - boolean needHead) { - return new ExcelWriter(temp, outputStream, typeEnum, needHead); - } - - /** - * Get ExcelWriter with a template file - * - * @param temp Append data after a POI file , Can be null(the template POI filesystem that contains the - * Workbook stream) - * @param outputStream the java OutputStream you wish to write the data to - * @param typeEnum 03 or 07 - * @param needHead - * @param handler User-defined callback - * @return new ExcelWriter - */ - public static ExcelWriter getWriterWithTempAndHandler(InputStream temp, - OutputStream outputStream, - ExcelTypeEnum typeEnum, - boolean needHead, - WriteHandler handler) { - return new ExcelWriter(temp, outputStream, typeEnum, needHead, handler); - } - -} diff --git a/src/main/java/com/alibaba/excel/ExcelReader.java b/src/main/java/com/alibaba/excel/ExcelReader.java deleted file mode 100644 index 9ed589276..000000000 --- a/src/main/java/com/alibaba/excel/ExcelReader.java +++ /dev/null @@ -1,149 +0,0 @@ -package com.alibaba.excel; - -import com.alibaba.excel.analysis.ExcelAnalyser; -import com.alibaba.excel.analysis.ExcelAnalyserImpl; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.parameter.AnalysisParam; -import com.alibaba.excel.support.ExcelTypeEnum; - -import java.io.InputStream; -import java.util.List; - -/** - * Excel readers are all read in event mode. - * - * @author jipengfei - */ -public class ExcelReader { - - /** - * Analyser - */ - private ExcelAnalyser analyser ; - - /** - * Create new reader - * - * @param in the POI filesystem that contains the Workbook stream - * @param excelTypeEnum 03 or 07 - * @param customContent {@link AnalysisEventListener#invoke(Object, AnalysisContext) }AnalysisContext - * @param eventListener Callback method after each row is parsed. - */ - @Deprecated - public ExcelReader(InputStream in, ExcelTypeEnum excelTypeEnum, Object customContent, - AnalysisEventListener eventListener) { - this(in, excelTypeEnum, customContent, eventListener, true); - } - - /** - * Create new reader - * - * @param in the POI filesystem that contains the Workbook stream - * @param customContent {@link AnalysisEventListener#invoke(Object, AnalysisContext) }AnalysisContext - * @param eventListener Callback method after each row is parsed - */ - public ExcelReader(InputStream in, Object customContent, - AnalysisEventListener eventListener) { - this(in, customContent, eventListener, true); - } - - /** - * Create new reader - * - * @param param old param Deprecated - * @param eventListener Callback method after each row is parsed. - */ - @Deprecated - public ExcelReader(AnalysisParam param, AnalysisEventListener eventListener) { - this(param.getIn(), param.getExcelTypeEnum(), param.getCustomContent(), eventListener, true); - } - - /** - * Create new reader - * - * @param in the POI filesystem that contains the Workbook stream - * @param excelTypeEnum 03 or 07 - * @param customContent {@link AnalysisEventListener#invoke(Object, AnalysisContext) }AnalysisContext - * @param eventListener Callback method after each row is parsed. - * @param trim The content of the form is empty and needs to be empty. The purpose is to be fault-tolerant, - * because there are often table contents with spaces that can not be converted into custom - * types. For example: '1234 ' contain a space cannot be converted to int. - */ - @Deprecated - public ExcelReader(InputStream in, ExcelTypeEnum excelTypeEnum, Object customContent, - AnalysisEventListener eventListener, boolean trim) { - validateParam(in, eventListener); - analyser = new ExcelAnalyserImpl(in, excelTypeEnum, customContent, eventListener, trim); - } - - /** - * Create new reader - * - * @param in - * @param customContent {@link AnalysisEventListener#invoke(Object, AnalysisContext) }AnalysisContext - * @param eventListener - * @param trim The content of the form is empty and needs to be empty. The purpose is to be fault-tolerant, - * because there are often table contents with spaces that can not be converted into custom - * types. For example: '1234 ' contain a space cannot be converted to int. - */ - public ExcelReader(InputStream in, Object customContent, - AnalysisEventListener eventListener, boolean trim) { - ExcelTypeEnum excelTypeEnum = ExcelTypeEnum.valueOf(in); - validateParam(in, eventListener); - analyser =new ExcelAnalyserImpl(in, excelTypeEnum, customContent, eventListener, trim); - } - - /** - * Parse all sheet content by default - */ - public void read() { - analyser.analysis(); - } - - /** - * Parse the specified sheet,SheetNo start from 1 - * - * @param sheet Read sheet - */ - public void read(Sheet sheet) { - analyser.analysis(sheet); - } - - /** - * Parse the specified sheet - * - * @param sheet Read sheet - * @param clazz object parsed into each row of data - */ - @Deprecated - public void read(Sheet sheet, Class clazz) { - sheet.setClazz(clazz); - analyser.analysis(sheet); - } - - /** - * Parse the workBook get all sheets - * - * @return workBook all sheets - */ - public List getSheets() { - return analyser.getSheets(); - } - - /** - * validate param - * - * @param in - * @param eventListener - */ - private void validateParam(InputStream in, AnalysisEventListener eventListener) { - if (eventListener == null) { - throw new IllegalArgumentException("AnalysisEventListener can not null"); - } else if (in == null) { - throw new IllegalArgumentException("InputStream can not null"); - } - } -} diff --git a/src/main/java/com/alibaba/excel/ExcelWriter.java b/src/main/java/com/alibaba/excel/ExcelWriter.java deleted file mode 100644 index e3c5e0ed7..000000000 --- a/src/main/java/com/alibaba/excel/ExcelWriter.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.alibaba.excel; - -import com.alibaba.excel.event.WriteHandler; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.metadata.Table; -import com.alibaba.excel.parameter.GenerateParam; -import com.alibaba.excel.support.ExcelTypeEnum; -import com.alibaba.excel.write.ExcelBuilder; -import com.alibaba.excel.write.ExcelBuilderImpl; - -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; - -/** - * Excel Writer This tool is used to write data out to Excel via POI. - * This object can perform the following two functions. - *
- *    1. Create a new empty Excel workbook, write the data to the stream after the data is filled.
- *    2. Edit existing Excel, write the original Excel file, or write it to other places.}
- * 
- * @author jipengfei - */ -public class ExcelWriter { - - private ExcelBuilder excelBuilder; - - /** - * Create new writer - * @param outputStream the java OutputStream you wish to write the data to - * @param typeEnum 03 or 07 - */ - public ExcelWriter(OutputStream outputStream, ExcelTypeEnum typeEnum) { - this(outputStream, typeEnum, true); - } - - @Deprecated - private Class objectClass; - - /** - * @param generateParam - */ - @Deprecated - public ExcelWriter(GenerateParam generateParam) { - this(generateParam.getOutputStream(), generateParam.getType(), true); - this.objectClass = generateParam.getClazz(); - } - - /** - * - * Create new writer - * @param outputStream the java OutputStream you wish to write the data to - * @param typeEnum 03 or 07 - * @param needHead Do you need to write the header to the file? - */ - public ExcelWriter(OutputStream outputStream, ExcelTypeEnum typeEnum, boolean needHead) { - excelBuilder = new ExcelBuilderImpl(null, outputStream, typeEnum, needHead, null); - } - - /** - * Create new writer - * @param templateInputStream Append data after a POI file ,Can be null(the template POI filesystem that contains the Workbook stream) - * @param outputStream the java OutputStream you wish to write the data to - * @param typeEnum 03 or 07 - */ - public ExcelWriter(InputStream templateInputStream, OutputStream outputStream, ExcelTypeEnum typeEnum,Boolean needHead) { - excelBuilder = new ExcelBuilderImpl(templateInputStream,outputStream, typeEnum, needHead, null); - } - - - /** - * Create new writer - * @param templateInputStream Append data after a POI file ,Can be null(the template POI filesystem that contains the Workbook stream) - * @param outputStream the java OutputStream you wish to write the data to - * @param typeEnum 03 or 07 - * @param writeHandler User-defined callback - */ - public ExcelWriter(InputStream templateInputStream, OutputStream outputStream, ExcelTypeEnum typeEnum, Boolean needHead, - WriteHandler writeHandler) { - excelBuilder = new ExcelBuilderImpl(templateInputStream,outputStream, typeEnum, needHead,writeHandler); - } - - /** - * Write data to a sheet - * @param data Data to be written - * @param sheet Write to this sheet - * @return this current writer - */ - public ExcelWriter write(List data, Sheet sheet) { - excelBuilder.addContent(data, sheet); - return this; - } - - - /** - * Write data to a sheet - * @param data Data to be written - * @return this current writer - */ - @Deprecated - public ExcelWriter write(List data) { - if (objectClass != null) { - return this.write(data,new Sheet(1,0,objectClass)); - }else { - return this.write0(data,new Sheet(1,0,objectClass)); - - } - } - - /** - * - * Write data to a sheet - * @param data Data to be written - * @param sheet Write to this sheet - * @return this - */ - public ExcelWriter write1(List> data, Sheet sheet) { - excelBuilder.addContent(data, sheet); - return this; - } - - /** - * Write data to a sheet - * @param data Data to be written - * @param sheet Write to this sheet - * @return this - */ - public ExcelWriter write0(List> data, Sheet sheet) { - excelBuilder.addContent(data, sheet); - return this; - } - - /** - * Write data to a sheet - * @param data Data to be written - * @param sheet Write to this sheet - * @param table Write to this table - * @return this - */ - public ExcelWriter write(List data, Sheet sheet, Table table) { - excelBuilder.addContent(data, sheet, table); - return this; - } - - /** - * Write data to a sheet - * @param data Data to be written - * @param sheet Write to this sheet - * @param table Write to this table - * @return this - */ - public ExcelWriter write0(List> data, Sheet sheet, Table table) { - excelBuilder.addContent(data, sheet, table); - return this; - } - - /** - * Merge Cells,Indexes are zero-based. - * - * @param firstRow Index of first row - * @param lastRow Index of last row (inclusive), must be equal to or larger than {@code firstRow} - * @param firstCol Index of first column - * @param lastCol Index of last column (inclusive), must be equal to or larger than {@code firstCol} - */ - public ExcelWriter merge(int firstRow, int lastRow, int firstCol, int lastCol){ - excelBuilder.merge(firstRow,lastRow,firstCol,lastCol); - return this; - } - - /** - * Write data to a sheet - * @param data Data to be written - * @param sheet Write to this sheet - * @param table Write to this table - * @return - */ - public ExcelWriter write1(List> data, Sheet sheet, Table table) { - excelBuilder.addContent(data, sheet, table); - return this; - } - - /** - * Close IO - */ - public void finish() { - excelBuilder.finish(); - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/BaseSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/BaseSaxAnalyser.java deleted file mode 100644 index 45c0d9bc5..000000000 --- a/src/main/java/com/alibaba/excel/analysis/BaseSaxAnalyser.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.alibaba.excel.analysis; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.event.AnalysisEventRegisterCenter; -import com.alibaba.excel.event.OneRowAnalysisFinishEvent; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.util.TypeUtil; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * @author jipengfei - */ -public abstract class BaseSaxAnalyser implements AnalysisEventRegisterCenter, ExcelAnalyser { - - protected AnalysisContext analysisContext; - - private LinkedHashMap listeners = new LinkedHashMap(); - - /** - * execute method - */ - protected abstract void execute(); - - - @Override - public void appendLister(String name, AnalysisEventListener listener) { - if (!listeners.containsKey(name)) { - listeners.put(name, listener); - } - } - - @Override - public void analysis(Sheet sheetParam) { - execute(); - } - - @Override - public void analysis() { - execute(); - } - - /** - */ - @Override - public void cleanAllListeners() { - listeners = new LinkedHashMap(); - } - - @Override - public void notifyListeners(OneRowAnalysisFinishEvent event) { - analysisContext.setCurrentRowAnalysisResult(event.getData()); - /** Parsing header content **/ - if (analysisContext.getCurrentRowNum() < analysisContext.getCurrentSheet().getHeadLineMun()) { - if (analysisContext.getCurrentRowNum() <= analysisContext.getCurrentSheet().getHeadLineMun() - 1) { - analysisContext.buildExcelHeadProperty(null, - (List)analysisContext.getCurrentRowAnalysisResult()); - } - } else { - List content = converter((List)event.getData()); - /** Parsing Analyze the body content **/ - analysisContext.setCurrentRowAnalysisResult(content); - if (listeners.size() == 1) { - analysisContext.setCurrentRowAnalysisResult(content); - } - /** notify all event listeners **/ - for (Map.Entry entry : listeners.entrySet()) { - entry.getValue().invoke(analysisContext.getCurrentRowAnalysisResult(), analysisContext); - } - } - } - - private List converter(List data) { - List list = new ArrayList(); - if (data != null) { - for (String str : data) { - list.add(TypeUtil.formatFloat(str)); - } - } - return list; - } - -} diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java deleted file mode 100644 index b5aef4d08..000000000 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyser.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibaba.excel.analysis; - -import com.alibaba.excel.metadata.Sheet; - -import java.util.List; - -/** - * Excel file analyser - * - * @author jipengfei - */ -public interface ExcelAnalyser { - - /** - * parse one sheet - * - * @param sheetParam - */ - void analysis(Sheet sheetParam); - - /** - * parse all sheets - */ - void analysis(); - - /** - * get all sheet of workbook - * - * @return all sheets - */ - List getSheets(); - -} diff --git a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java b/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java deleted file mode 100644 index b8dd37647..000000000 --- a/src/main/java/com/alibaba/excel/analysis/ExcelAnalyserImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.alibaba.excel.analysis; - -import com.alibaba.excel.analysis.v03.XlsSaxAnalyser; -import com.alibaba.excel.analysis.v07.XlsxSaxAnalyser; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.context.AnalysisContextImpl; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.modelbuild.ModelBuildEventListener; -import com.alibaba.excel.support.ExcelTypeEnum; - -import java.io.InputStream; -import java.util.List; - -/** - * @author jipengfei - */ -public class ExcelAnalyserImpl implements ExcelAnalyser { - - private AnalysisContext analysisContext; - - private BaseSaxAnalyser saxAnalyser; - - public ExcelAnalyserImpl(InputStream inputStream, ExcelTypeEnum excelTypeEnum, Object custom, - AnalysisEventListener eventListener, boolean trim) { - analysisContext = new AnalysisContextImpl(inputStream, excelTypeEnum, custom, - eventListener, trim); - } - - private BaseSaxAnalyser getSaxAnalyser() { - if (saxAnalyser != null) { - return this.saxAnalyser; - } - try { - if (analysisContext.getExcelType() != null) { - switch (analysisContext.getExcelType()) { - case XLS: - this.saxAnalyser = new XlsSaxAnalyser(analysisContext); - break; - case XLSX: - this.saxAnalyser = new XlsxSaxAnalyser(analysisContext); - break; - } - } else { - try { - this.saxAnalyser = new XlsxSaxAnalyser(analysisContext); - } catch (Exception e) { - if (!analysisContext.getInputStream().markSupported()) { - throw new ExcelAnalysisException( - "Xls must be available markSupported,you can do like this new " - + "BufferedInputStream(new FileInputStream(\"/xxxx\")) "); - } - this.saxAnalyser = new XlsSaxAnalyser(analysisContext); - } - } - } catch (Exception e) { - throw new ExcelAnalysisException("File type error,io must be available markSupported,you can do like " - + "this new BufferedInputStream(new FileInputStream(\\\"/xxxx\\\")) \"", e); - } - return this.saxAnalyser; - } - - @Override - public void analysis(Sheet sheetParam) { - analysisContext.setCurrentSheet(sheetParam); - analysis(); - } - - @Override - public void analysis() { - BaseSaxAnalyser saxAnalyser = getSaxAnalyser(); - appendListeners(saxAnalyser); - saxAnalyser.execute(); - analysisContext.getEventListener().doAfterAllAnalysed(analysisContext); - } - - @Override - public List getSheets() { - BaseSaxAnalyser saxAnalyser = getSaxAnalyser(); - saxAnalyser.cleanAllListeners(); - return saxAnalyser.getSheets(); - } - - private void appendListeners(BaseSaxAnalyser saxAnalyser) { - saxAnalyser.cleanAllListeners(); - if (analysisContext.getCurrentSheet() != null && analysisContext.getCurrentSheet().getClazz() != null) { - saxAnalyser.appendLister("model_build_listener", new ModelBuildEventListener()); - } - if (analysisContext.getEventListener() != null) { - saxAnalyser.appendLister("user_define_listener", analysisContext.getEventListener()); - } - } - -} diff --git a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java deleted file mode 100644 index 015462546..000000000 --- a/src/main/java/com/alibaba/excel/analysis/v03/XlsSaxAnalyser.java +++ /dev/null @@ -1,318 +0,0 @@ -package com.alibaba.excel.analysis.v03; - -import com.alibaba.excel.analysis.BaseSaxAnalyser; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.OneRowAnalysisFinishEvent; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.metadata.Sheet; -import org.apache.poi.hssf.eventusermodel.*; -import org.apache.poi.hssf.eventusermodel.dummyrecord.LastCellOfRowDummyRecord; -import org.apache.poi.hssf.eventusermodel.dummyrecord.MissingCellDummyRecord; -import org.apache.poi.hssf.model.HSSFFormulaParser; -import org.apache.poi.hssf.record.*; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * /** * A text extractor for Excel files. *

* Returns the textual content of the file, suitable for * indexing by - * something like Lucene, but not really * intended for display to the user. *

*

* To turn an excel file into - * a CSV or similar, then see * the XLS2CSVmra example *

* * @see - * XLS2CSVmra - * - * @author jipengfei - */ -public class XlsSaxAnalyser extends BaseSaxAnalyser implements HSSFListener { - - private boolean analyAllSheet = false; - - public XlsSaxAnalyser(AnalysisContext context) throws IOException { - this.analysisContext = context; - this.records = new ArrayList(); - if (analysisContext.getCurrentSheet() == null) { - this.analyAllSheet = true; - } - context.setCurrentRowNum(0); - this.fs = new POIFSFileSystem(analysisContext.getInputStream()); - - } - - @Override - public List getSheets() { - execute(); - return sheets; - } - - @Override - public void execute() { - init(); - MissingRecordAwareHSSFListener listener = new MissingRecordAwareHSSFListener(this); - formatListener = new FormatTrackingHSSFListener(listener); - - HSSFEventFactory factory = new HSSFEventFactory(); - HSSFRequest request = new HSSFRequest(); - - if (outputFormulaValues) { - request.addListenerForAllRecords(formatListener); - } else { - workbookBuildingListener = new EventWorkbookBuilder.SheetRecordCollectingListener(formatListener); - request.addListenerForAllRecords(workbookBuildingListener); - } - - try { - factory.processWorkbookEvents(request, fs); - } catch (IOException e) { - throw new ExcelAnalysisException(e); - } - } - - private void init() { - lastRowNumber = 0; - lastColumnNumber = 0; - nextRow = 0; - - nextColumn = 0; - - sheetIndex = 0; - - records = new ArrayList(); - - notAllEmpty = false; - - orderedBSRs = null; - - boundSheetRecords = new ArrayList(); - - sheets = new ArrayList(); - if (analysisContext.getCurrentSheet() == null) { - this.analyAllSheet = true; - } else { - this.analyAllSheet = false; - } - } - - private POIFSFileSystem fs; - - private int lastRowNumber; - private int lastColumnNumber; - - /** - * Should we output the formula, or the value it has? - */ - private boolean outputFormulaValues = true; - - /** - * For parsing Formulas - */ - private EventWorkbookBuilder.SheetRecordCollectingListener workbookBuildingListener; - private HSSFWorkbook stubWorkbook; - private SSTRecord sstRecord; - private FormatTrackingHSSFListener formatListener; - - /** - * So we known which sheet we're on - */ - - private int nextRow; - private int nextColumn; - private boolean outputNextStringRecord; - - /** - * Main HSSFListener method, processes events, and outputs the CSV as the file is processed. - */ - - private int sheetIndex; - - private List records; - - private boolean notAllEmpty = false; - - private BoundSheetRecord[] orderedBSRs; - - private List boundSheetRecords = new ArrayList(); - - private List sheets = new ArrayList(); - - public void processRecord(Record record) { - int thisRow = -1; - int thisColumn = -1; - String thisStr = null; - - switch (record.getSid()) { - case BoundSheetRecord.sid: - boundSheetRecords.add((BoundSheetRecord)record); - break; - case BOFRecord.sid: - BOFRecord br = (BOFRecord)record; - if (br.getType() == BOFRecord.TYPE_WORKSHEET) { - // Create sub workbook if required - if (workbookBuildingListener != null && stubWorkbook == null) { - stubWorkbook = workbookBuildingListener.getStubHSSFWorkbook(); - } - - if (orderedBSRs == null) { - orderedBSRs = BoundSheetRecord.orderByBofPosition(boundSheetRecords); - } - sheetIndex++; - - Sheet sheet = new Sheet(sheetIndex, 0); - sheet.setSheetName(orderedBSRs[sheetIndex - 1].getSheetname()); - sheets.add(sheet); - if (this.analyAllSheet) { - analysisContext.setCurrentSheet(sheet); - } - } - break; - - case SSTRecord.sid: - sstRecord = (SSTRecord)record; - break; - - case BlankRecord.sid: - BlankRecord brec = (BlankRecord)record; - - thisRow = brec.getRow(); - thisColumn = brec.getColumn(); - thisStr = ""; - break; - case BoolErrRecord.sid: - BoolErrRecord berec = (BoolErrRecord)record; - - thisRow = berec.getRow(); - thisColumn = berec.getColumn(); - thisStr = ""; - break; - - case FormulaRecord.sid: - FormulaRecord frec = (FormulaRecord)record; - - thisRow = frec.getRow(); - thisColumn = frec.getColumn(); - - if (outputFormulaValues) { - if (Double.isNaN(frec.getValue())) { - // Formula result is a string - // This is stored in the next record - outputNextStringRecord = true; - nextRow = frec.getRow(); - nextColumn = frec.getColumn(); - } else { - thisStr = formatListener.formatNumberDateCell(frec); - } - } else { - thisStr = HSSFFormulaParser.toFormulaString(stubWorkbook, frec.getParsedExpression()); - } - break; - case StringRecord.sid: - if (outputNextStringRecord) { - // String for formula - StringRecord srec = (StringRecord)record; - thisStr = srec.getString(); - thisRow = nextRow; - thisColumn = nextColumn; - outputNextStringRecord = false; - } - break; - - case LabelRecord.sid: - LabelRecord lrec = (LabelRecord)record; - - thisRow = lrec.getRow(); - thisColumn = lrec.getColumn(); - thisStr = lrec.getValue(); - break; - case LabelSSTRecord.sid: - LabelSSTRecord lsrec = (LabelSSTRecord)record; - - thisRow = lsrec.getRow(); - thisColumn = lsrec.getColumn(); - if (sstRecord == null) { - thisStr = ""; - } else { - thisStr = sstRecord.getString(lsrec.getSSTIndex()).toString(); - } - break; - case NoteRecord.sid: - NoteRecord nrec = (NoteRecord)record; - - thisRow = nrec.getRow(); - thisColumn = nrec.getColumn(); - // TODO: Find object to match nrec.getShapeId() - thisStr = "(TODO)"; - break; - case NumberRecord.sid: - NumberRecord numrec = (NumberRecord)record; - - thisRow = numrec.getRow(); - thisColumn = numrec.getColumn(); - - // Format - thisStr = formatListener.formatNumberDateCell(numrec); - break; - case RKRecord.sid: - RKRecord rkrec = (RKRecord)record; - - thisRow = rkrec.getRow(); - thisColumn = rkrec.getColumn(); - thisStr = ""; - break; - default: - break; - } - - // Handle new row - if (thisRow != -1 && thisRow != lastRowNumber) { - lastColumnNumber = -1; - } - - // Handle missing column - if (record instanceof MissingCellDummyRecord) { - MissingCellDummyRecord mc = (MissingCellDummyRecord)record; - thisRow = mc.getRow(); - thisColumn = mc.getColumn(); - thisStr = ""; - } - - // If we got something to print out, do so - if (thisStr != null) { - - if (analysisContext.trim()) { - thisStr = thisStr.trim(); - } - if (!"".equals(thisStr)) { - notAllEmpty = true; - } - // } - records.add(thisStr); - } - - // Update column and row count - if (thisRow > -1) { - lastRowNumber = thisRow; - } - if (thisColumn > -1) { - lastColumnNumber = thisColumn; - } - - // Handle end of row - if (record instanceof LastCellOfRowDummyRecord) { - thisRow = ((LastCellOfRowDummyRecord)record).getRow(); - - if (lastColumnNumber == -1) { - lastColumnNumber = 0; - } - analysisContext.setCurrentRowNum(thisRow); - Sheet sheet = analysisContext.getCurrentSheet(); - - if ((sheet == null || sheet.getSheetNo() == sheetIndex) && notAllEmpty) { - notifyListeners(new OneRowAnalysisFinishEvent(records)); - } - records.clear(); - lastColumnNumber = -1; - notAllEmpty = false; - } - } -} diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowHandler.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowHandler.java deleted file mode 100644 index 2431e9afd..000000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxRowHandler.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.alibaba.excel.analysis.v07; - -import com.alibaba.excel.annotation.FieldType; -import com.alibaba.excel.constant.ExcelXmlConstants; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventRegisterCenter; -import com.alibaba.excel.event.OneRowAnalysisFinishEvent; -import com.alibaba.excel.util.PositionUtils; -import org.apache.poi.xssf.model.SharedStringsTable; -import org.apache.poi.xssf.usermodel.XSSFRichTextString; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import java.util.Arrays; - -import static com.alibaba.excel.constant.ExcelXmlConstants.*; - -/** - * - * @author jipengfei - */ -public class XlsxRowHandler extends DefaultHandler { - - private String currentCellIndex; - - private FieldType currentCellType; - - private int curRow; - - private int curCol; - - private String[] curRowContent = new String[20]; - - private String currentCellValue; - - private SharedStringsTable sst; - - private AnalysisContext analysisContext; - - private AnalysisEventRegisterCenter registerCenter; - - public XlsxRowHandler(AnalysisEventRegisterCenter registerCenter, SharedStringsTable sst, - AnalysisContext analysisContext) { - this.registerCenter = registerCenter; - this.analysisContext = analysisContext; - this.sst = sst; - - } - - @Override - public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { - - setTotalRowCount(name, attributes); - - startCell(name, attributes); - - startCellValue(name); - - } - - private void startCellValue(String name) { - if (name.equals(CELL_VALUE_TAG) || name.equals(CELL_VALUE_TAG_1)) { - // initialize current cell value - currentCellValue = ""; - } - } - - private void startCell(String name, Attributes attributes) { - if (ExcelXmlConstants.CELL_TAG.equals(name)) { - currentCellIndex = attributes.getValue(ExcelXmlConstants.POSITION); - int nextRow = PositionUtils.getRow(currentCellIndex); - if (nextRow > curRow) { - curRow = nextRow; - // endRow(ROW_TAG); - } - analysisContext.setCurrentRowNum(curRow); - curCol = PositionUtils.getCol(currentCellIndex); - - String cellType = attributes.getValue("t"); - currentCellType = FieldType.EMPTY; - if (cellType != null && cellType.equals("s")) { - currentCellType = FieldType.STRING; - } - } - } - - private void endCellValue(String name) throws SAXException { - // ensure size - if (curCol >= curRowContent.length) { - curRowContent = Arrays.copyOf(curRowContent, (int)(curCol * 1.5)); - } - if (CELL_VALUE_TAG.equals(name)) { - - switch (currentCellType) { - case STRING: - int idx = Integer.parseInt(currentCellValue); - currentCellValue = new XSSFRichTextString(sst.getEntryAt(idx)).toString(); - currentCellType = FieldType.EMPTY; - break; - } - curRowContent[curCol] = currentCellValue; - } else if (CELL_VALUE_TAG_1.equals(name)) { - curRowContent[curCol] = currentCellValue; - } - } - - @Override - public void endElement(String uri, String localName, String name) throws SAXException { - endRow(name); - endCellValue(name); - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - currentCellValue += new String(ch, start, length); - } - - - private void setTotalRowCount(String name, Attributes attributes) { - if (DIMENSION.equals(name)) { - String d = attributes.getValue(DIMENSION_REF); - String totalStr = d.substring(d.indexOf(":") + 1, d.length()); - String c = totalStr.toUpperCase().replaceAll("[A-Z]", ""); - analysisContext.setTotalCount(Integer.parseInt(c)); - } - - } - - private void endRow(String name) { - if (name.equals(ROW_TAG)) { - registerCenter.notifyListeners(new OneRowAnalysisFinishEvent(curRowContent,curCol)); - curRowContent = new String[20]; - } - } - -} - diff --git a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java b/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java deleted file mode 100644 index 34f2480bd..000000000 --- a/src/main/java/com/alibaba/excel/analysis/v07/XlsxSaxAnalyser.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.alibaba.excel.analysis.v07; - -import com.alibaba.excel.analysis.BaseSaxAnalyser; -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.metadata.Sheet; -import org.apache.poi.openxml4j.exceptions.OpenXML4JException; -import org.apache.poi.openxml4j.opc.OPCPackage; -import org.apache.poi.xssf.eventusermodel.XSSFReader; -import org.apache.poi.xssf.model.SharedStringsTable; -import org.apache.xmlbeans.XmlException; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbookPr; -import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorkbookDocument; -import org.xml.sax.ContentHandler; -import org.xml.sax.InputSource; -import org.xml.sax.XMLReader; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author jipengfei - */ -public class XlsxSaxAnalyser extends BaseSaxAnalyser { - - private XSSFReader xssfReader; - - private SharedStringsTable sharedStringsTable; - - private List sheetSourceList = new ArrayList(); - - private boolean use1904WindowDate = false; - - public XlsxSaxAnalyser(AnalysisContext analysisContext) throws IOException, OpenXML4JException, XmlException { - this.analysisContext = analysisContext; - - analysisContext.setCurrentRowNum(0); - this.xssfReader = new XSSFReader(OPCPackage.open(analysisContext.getInputStream())); - this.sharedStringsTable = this.xssfReader.getSharedStringsTable(); - - InputStream workbookXml = xssfReader.getWorkbookData(); - WorkbookDocument ctWorkbook = WorkbookDocument.Factory.parse(workbookXml); - CTWorkbook wb = ctWorkbook.getWorkbook(); - CTWorkbookPr prefix = wb.getWorkbookPr(); - if (prefix != null) { - this.use1904WindowDate = prefix.getDate1904(); - } - this.analysisContext.setUse1904WindowDate(use1904WindowDate); - - - XSSFReader.SheetIterator ite; - sheetSourceList = new ArrayList(); - ite = (XSSFReader.SheetIterator)xssfReader.getSheetsData(); - while (ite.hasNext()) { - InputStream inputStream = ite.next(); - String sheetName = ite.getSheetName(); - SheetSource sheetSource = new SheetSource(sheetName, inputStream); - sheetSourceList.add(sheetSource); - } - - } - - @Override - protected void execute() { - Sheet sheetParam = analysisContext.getCurrentSheet(); - if (sheetParam != null && sheetParam.getSheetNo() > 0 && sheetSourceList.size() >= sheetParam.getSheetNo()) { - InputStream sheetInputStream = sheetSourceList.get(sheetParam.getSheetNo() - 1).getInputStream(); - parseXmlSource(sheetInputStream); - - } else { - int i = 0; - for (SheetSource sheetSource : sheetSourceList) { - i++; - analysisContext.setCurrentSheet(new Sheet(i)); - parseXmlSource(sheetSource.getInputStream()); - } - } - } - - private void parseXmlSource(InputStream inputStream) { - InputSource sheetSource = new InputSource(inputStream); - try { - SAXParserFactory saxFactory = SAXParserFactory.newInstance(); - saxFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - saxFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); - saxFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - SAXParser saxParser = saxFactory.newSAXParser(); - XMLReader xmlReader = saxParser.getXMLReader(); - ContentHandler handler = new XlsxRowHandler(this, sharedStringsTable, analysisContext); - xmlReader.setContentHandler(handler); - xmlReader.parse(sheetSource); - inputStream.close(); - } catch (Exception e) { - e.printStackTrace(); - throw new ExcelAnalysisException(e); - } - } - - @Override - public List getSheets() { - List sheets = new ArrayList(); - int i = 1; - for (SheetSource sheetSource : sheetSourceList) { - Sheet sheet = new Sheet(i, 0); - sheet.setSheetName(sheetSource.getSheetName()); - i++; - sheets.add(sheet); - } - - return sheets; - } - - class SheetSource { - - private String sheetName; - - private InputStream inputStream; - - public SheetSource(String sheetName, InputStream inputStream) { - this.sheetName = sheetName; - this.inputStream = inputStream; - } - - public String getSheetName() { - return sheetName; - } - - public void setSheetName(String sheetName) { - this.sheetName = sheetName; - } - - public InputStream getInputStream() { - return inputStream; - } - - public void setInputStream(InputStream inputStream) { - this.inputStream = inputStream; - } - } -} diff --git a/src/main/java/com/alibaba/excel/annotation/ExcelColumnNum.java b/src/main/java/com/alibaba/excel/annotation/ExcelColumnNum.java deleted file mode 100644 index eb56fe38f..000000000 --- a/src/main/java/com/alibaba/excel/annotation/ExcelColumnNum.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alibaba.excel.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Created by jipengfei on 17/3/19. - * Field column num at excel head - * - * @author jipengfei - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface ExcelColumnNum { - - /** - * col num - * @return - */ - int value(); - - /** - * - * Default @see com.alibaba.excel.util.TypeUtil - * if default is not meet you can set format - * - * @return - */ - String format() default ""; -} diff --git a/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java b/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java deleted file mode 100644 index a063a3277..000000000 --- a/src/main/java/com/alibaba/excel/annotation/ExcelProperty.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.alibaba.excel.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @author jipengfei - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface ExcelProperty { - - /** - * @return - */ - String[] value() default {""}; - - - /** - * @return - */ - int index() default 99999; - - /** - * - * default @see com.alibaba.excel.util.TypeUtil - * if default is not meet you can set format - * - * @return - */ - String format() default ""; -} diff --git a/src/main/java/com/alibaba/excel/annotation/FieldType.java b/src/main/java/com/alibaba/excel/annotation/FieldType.java deleted file mode 100644 index 73729959c..000000000 --- a/src/main/java/com/alibaba/excel/annotation/FieldType.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alibaba.excel.annotation; - -/** - * - * @author jipengfei - */ -public enum FieldType { - - STRING, INT, LONG, DATE, BOOLEAN, DOUBLE,EMPTY; - -} diff --git a/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java b/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java deleted file mode 100644 index dcd2d5a43..000000000 --- a/src/main/java/com/alibaba/excel/constant/ExcelXmlConstants.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.alibaba.excel.constant; - -/** - * @author jipengfei - */ -public class ExcelXmlConstants { - public static final String DIMENSION = "dimension"; - public static final String DIMENSION_REF = "ref"; - public static final String POSITION = "r"; - - public static final String ROW_TAG = "row"; - public static final String CELL_TAG = "c"; - public static final String CELL_VALUE_TAG = "v"; - - public static final String CELL_VALUE_TAG_1 = "t"; - -} diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContext.java b/src/main/java/com/alibaba/excel/context/AnalysisContext.java deleted file mode 100644 index 23c846f96..000000000 --- a/src/main/java/com/alibaba/excel/context/AnalysisContext.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.alibaba.excel.context; - -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.ExcelHeadProperty; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.support.ExcelTypeEnum; - -import java.io.InputStream; -import java.util.List; - -/** - * - * A context is the main anchorage point of a excel reader. - * @author jipengfei - */ -public interface AnalysisContext { - - /** - * Custom attribute - */ - Object getCustom(); - - /** - * get current sheet - * - * @return current analysis sheet - */ - Sheet getCurrentSheet(); - - /** - * set current sheet - * @param sheet - */ - void setCurrentSheet(Sheet sheet); - - /** - * - * get excel type - * @return excel type - */ - ExcelTypeEnum getExcelType(); - - /** - * get in io - * @return file io - */ - InputStream getInputStream(); - - /** - * - * custom listener - * @return listener - */ - AnalysisEventListener getEventListener(); - - /** - * get current row - * @return - */ - Integer getCurrentRowNum(); - - /** - * set current row num - * @param row - */ - void setCurrentRowNum(Integer row); - - /** - * get total row ,Data may be inaccurate - * @return - */ - @Deprecated - Integer getTotalCount(); - - /** - * get total row ,Data may be inaccurate - * - * @param totalCount - */ - void setTotalCount(Integer totalCount); - - /** - * get excel head - * @return - */ - ExcelHeadProperty getExcelHeadProperty(); - - /** - * - * @param clazz - * @param headOneRow - */ - void buildExcelHeadProperty(Class clazz, List headOneRow); - - /** - * - *if need to short match the content - * @return - */ - boolean trim(); - - /** - * set current result - * @param result - */ - void setCurrentRowAnalysisResult(Object result); - - - /** - * get current result - * @return get current result - */ - Object getCurrentRowAnalysisResult(); - - /** - * Interrupt execution - */ - void interrupt(); - - /** - * date use1904WindowDate - * @return - */ - boolean use1904WindowDate(); - - /** - * date use1904WindowDate - * @param use1904WindowDate - */ - void setUse1904WindowDate(boolean use1904WindowDate); -} diff --git a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java b/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java deleted file mode 100644 index fff50552f..000000000 --- a/src/main/java/com/alibaba/excel/context/AnalysisContextImpl.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.alibaba.excel.context; - -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.exception.ExcelAnalysisException; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.ExcelHeadProperty; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.support.ExcelTypeEnum; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author jipengfei - */ -public class AnalysisContextImpl implements AnalysisContext { - - private Object custom; - - private Sheet currentSheet; - - private ExcelTypeEnum excelType; - - private InputStream inputStream; - - private AnalysisEventListener eventListener; - - private Integer currentRowNum; - - private Integer totalCount; - - private ExcelHeadProperty excelHeadProperty; - - private boolean trim; - - private boolean use1904WindowDate = false; - - @Override - public void setUse1904WindowDate(boolean use1904WindowDate) { - this.use1904WindowDate = use1904WindowDate; - } - - @Override - public Object getCurrentRowAnalysisResult() { - return currentRowAnalysisResult; - } - - @Override - public void interrupt() { - throw new ExcelAnalysisException("interrupt error"); - } - - @Override - public boolean use1904WindowDate() { - return use1904WindowDate; - } - - @Override - public void setCurrentRowAnalysisResult(Object currentRowAnalysisResult) { - this.currentRowAnalysisResult = currentRowAnalysisResult; - } - - private Object currentRowAnalysisResult; - - public AnalysisContextImpl(InputStream inputStream, ExcelTypeEnum excelTypeEnum, Object custom, - AnalysisEventListener listener, boolean trim) { - this.custom = custom; - this.eventListener = listener; - this.inputStream = inputStream; - this.excelType = excelTypeEnum; - this.trim = trim; - } - - @Override - public void setCurrentSheet(Sheet currentSheet) { - cleanCurrentSheet(); - this.currentSheet = currentSheet; - if (currentSheet.getClazz() != null) { - buildExcelHeadProperty(currentSheet.getClazz(), null); - } - } - - private void cleanCurrentSheet() { - this.currentSheet = null; - this.excelHeadProperty = null; - this.totalCount = 0; - this.currentRowAnalysisResult = null; - this.currentRowNum =0; - } - - @Override - public ExcelTypeEnum getExcelType() { - return excelType; - } - - public void setExcelType(ExcelTypeEnum excelType) { - this.excelType = excelType; - } - - public Object getCustom() { - return custom; - } - - public void setCustom(Object custom) { - this.custom = custom; - } - - @Override - public Sheet getCurrentSheet() { - return currentSheet; - } - - @Override - public InputStream getInputStream() { - return inputStream; - } - - public void setInputStream(InputStream inputStream) { - this.inputStream = inputStream; - } - - @Override - public AnalysisEventListener getEventListener() { - return eventListener; - } - - public void setEventListener(AnalysisEventListener eventListener) { - this.eventListener = eventListener; - } - - @Override - public Integer getCurrentRowNum() { - return this.currentRowNum; - } - - @Override - public void setCurrentRowNum(Integer row) { - this.currentRowNum = row; - } - - @Override - public Integer getTotalCount() { - return totalCount; - } - - @Override - public void setTotalCount(Integer totalCount) { - this.totalCount = totalCount; - } - - @Override - public ExcelHeadProperty getExcelHeadProperty() { - return this.excelHeadProperty; - } - - @Override - public void buildExcelHeadProperty(Class clazz, List headOneRow) { - if (this.excelHeadProperty == null && (clazz != null || headOneRow != null)) { - this.excelHeadProperty = new ExcelHeadProperty(clazz, new ArrayList>()); - } - if (this.excelHeadProperty.getHead() == null && headOneRow != null) { - this.excelHeadProperty.appendOneRow(headOneRow); - } - } - - @Override - public boolean trim() { - return this.trim; - } -} diff --git a/src/main/java/com/alibaba/excel/context/WriteContext.java b/src/main/java/com/alibaba/excel/context/WriteContext.java deleted file mode 100644 index b1aee5924..000000000 --- a/src/main/java/com/alibaba/excel/context/WriteContext.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.alibaba.excel.context; - -import com.alibaba.excel.event.WriteHandler; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.ExcelHeadProperty; -import com.alibaba.excel.metadata.Table; -import com.alibaba.excel.support.ExcelTypeEnum; -import com.alibaba.excel.util.CollectionUtils; -import com.alibaba.excel.util.StyleUtil; -import com.alibaba.excel.util.WorkBookUtil; -import org.apache.poi.ss.usermodel.*; -import org.apache.poi.ss.util.CellRangeAddress; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static com.alibaba.excel.util.StyleUtil.buildSheetStyle; - -/** - * A context is the main anchorage point of a excel writer. - * - * @author jipengfei - */ -public class WriteContext { - - /*** - * The sheet currently written - */ - private Sheet currentSheet; - - /** - * current param - */ - private com.alibaba.excel.metadata.Sheet currentSheetParam; - - /** - * The sheet currently written's name - */ - private String currentSheetName; - - /** - * - */ - private Table currentTable; - - /** - * Excel type - */ - private ExcelTypeEnum excelType; - - /** - * POI Workbook - */ - private Workbook workbook; - - /** - * Final output stream - */ - private OutputStream outputStream; - - /** - * Written form collection - */ - private Map tableMap = new ConcurrentHashMap(); - - /** - * Cell default style - */ - private CellStyle defaultCellStyle; - - /** - * Current table head style - */ - private CellStyle currentHeadCellStyle; - - /** - * Current table content style - */ - private CellStyle currentContentCellStyle; - - /** - * the header attribute of excel - */ - private ExcelHeadProperty excelHeadProperty; - - private boolean needHead = Boolean.TRUE; - - private WriteHandler afterWriteHandler; - - public WriteHandler getAfterWriteHandler() { - return afterWriteHandler; - } - - public WriteContext(InputStream templateInputStream, OutputStream out, ExcelTypeEnum excelType, - boolean needHead, WriteHandler afterWriteHandler) throws IOException { - this.needHead = needHead; - this.outputStream = out; - this.afterWriteHandler = afterWriteHandler; - this.workbook = WorkBookUtil.createWorkBook(templateInputStream, excelType); - this.defaultCellStyle = StyleUtil.buildDefaultCellStyle(this.workbook); - - } - - /** - * @param sheet - */ - public void currentSheet(com.alibaba.excel.metadata.Sheet sheet) { - if (null == currentSheetParam || currentSheetParam.getSheetNo() != sheet.getSheetNo()) { - cleanCurrentSheet(); - currentSheetParam = sheet; - try { - this.currentSheet = workbook.getSheetAt(sheet.getSheetNo() - 1); - } catch (Exception e) { - this.currentSheet = WorkBookUtil.createSheet(workbook, sheet); - if (null != afterWriteHandler) { - this.afterWriteHandler.sheet(sheet.getSheetNo(), currentSheet); - } - } - buildSheetStyle(currentSheet, sheet.getColumnWidthMap()); - /** **/ - this.initCurrentSheet(sheet); - } - - } - - private void initCurrentSheet(com.alibaba.excel.metadata.Sheet sheet) { - - /** **/ - initExcelHeadProperty(sheet.getHead(), sheet.getClazz()); - - initTableStyle(sheet.getTableStyle()); - - initTableHead(); - - } - - private void cleanCurrentSheet() { - this.currentSheet = null; - this.currentSheetParam = null; - this.excelHeadProperty = null; - this.currentHeadCellStyle = null; - this.currentContentCellStyle = null; - this.currentTable = null; - - } - - /** - * init excel header - * - * @param head - * @param clazz - */ - private void initExcelHeadProperty(List> head, Class clazz) { - if (head != null || clazz != null) { this.excelHeadProperty = new ExcelHeadProperty(clazz, head); } - } - - public void initTableHead() { - if (needHead && null != excelHeadProperty && !CollectionUtils.isEmpty(excelHeadProperty.getHead())) { - int startRow = currentSheet.getLastRowNum(); - if (startRow > 0) { - startRow += 4; - } else { - startRow = currentSheetParam.getStartRow(); - } - addMergedRegionToCurrentSheet(startRow); - int i = startRow; - for (; i < this.excelHeadProperty.getRowNum() + startRow; i++) { - Row row = WorkBookUtil.createRow(currentSheet, i); - if (null != afterWriteHandler) { - this.afterWriteHandler.row(i, row); - } - addOneRowOfHeadDataToExcel(row, this.excelHeadProperty.getHeadByRowNum(i - startRow)); - } - } - } - - private void addMergedRegionToCurrentSheet(int startRow) { - for (com.alibaba.excel.metadata.CellRange cellRangeModel : excelHeadProperty.getCellRangeModels()) { - currentSheet.addMergedRegion(new CellRangeAddress(cellRangeModel.getFirstRow() + startRow, - cellRangeModel.getLastRow() + startRow, - cellRangeModel.getFirstCol(), cellRangeModel.getLastCol())); - } - } - - private void addOneRowOfHeadDataToExcel(Row row, List headByRowNum) { - if (headByRowNum != null && headByRowNum.size() > 0) { - for (int i = 0; i < headByRowNum.size(); i++) { - Cell cell = WorkBookUtil.createCell(row, i, getCurrentHeadCellStyle(), headByRowNum.get(i)); - if (null != afterWriteHandler) { - this.afterWriteHandler.cell(i, cell); - } - } - } - } - - private void initTableStyle(com.alibaba.excel.metadata.TableStyle tableStyle) { - if (tableStyle != null) { - this.currentHeadCellStyle = StyleUtil.buildCellStyle(this.workbook, tableStyle.getTableHeadFont(), - tableStyle.getTableHeadBackGroundColor()); - this.currentContentCellStyle = StyleUtil.buildCellStyle(this.workbook, tableStyle.getTableContentFont(), - tableStyle.getTableContentBackGroundColor()); - } - } - - private void cleanCurrentTable() { - this.excelHeadProperty = null; - this.currentHeadCellStyle = null; - this.currentContentCellStyle = null; - this.currentTable = null; - - } - - public void currentTable(Table table) { - if (null == currentTable || currentTable.getTableNo() != table.getTableNo()) { - cleanCurrentTable(); - this.currentTable = table; - this.initExcelHeadProperty(table.getHead(), table.getClazz()); - this.initTableStyle(table.getTableStyle()); - this.initTableHead(); - } - - } - - public ExcelHeadProperty getExcelHeadProperty() { - return this.excelHeadProperty; - } - - public boolean needHead() { - return this.needHead; - } - - public Sheet getCurrentSheet() { - return currentSheet; - } - - public void setCurrentSheet(Sheet currentSheet) { - this.currentSheet = currentSheet; - } - - public String getCurrentSheetName() { - return currentSheetName; - } - - public void setCurrentSheetName(String currentSheetName) { - this.currentSheetName = currentSheetName; - } - - public ExcelTypeEnum getExcelType() { - return excelType; - } - - public void setExcelType(ExcelTypeEnum excelType) { - this.excelType = excelType; - } - - public OutputStream getOutputStream() { - return outputStream; - } - - public CellStyle getCurrentHeadCellStyle() { - return this.currentHeadCellStyle == null ? defaultCellStyle : this.currentHeadCellStyle; - } - - public CellStyle getCurrentContentStyle() { - return this.currentContentCellStyle; - } - - public Workbook getWorkbook() { - return workbook; - } - - public com.alibaba.excel.metadata.Sheet getCurrentSheetParam() { - return currentSheetParam; - } - - public void setCurrentSheetParam(com.alibaba.excel.metadata.Sheet currentSheetParam) { - this.currentSheetParam = currentSheetParam; - } - - public Table getCurrentTable() { - return currentTable; - } - - public void setCurrentTable(Table currentTable) { - this.currentTable = currentTable; - } -} - - diff --git a/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java b/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java deleted file mode 100644 index 2a30da412..000000000 --- a/src/main/java/com/alibaba/excel/event/AnalysisEventListener.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alibaba.excel.event; - -import com.alibaba.excel.context.AnalysisContext; - -/** - * - * - * @author jipengfei - */ -public abstract class AnalysisEventListener { - - /** - * when analysis one row trigger invoke function - * - * @param object one row data - * @param context analysis context - */ - public abstract void invoke(T object, AnalysisContext context); - - /** - * if have something to do after all analysis - * - * @param context - */ - public abstract void doAfterAllAnalysed(AnalysisContext context); -} diff --git a/src/main/java/com/alibaba/excel/event/AnalysisEventRegisterCenter.java b/src/main/java/com/alibaba/excel/event/AnalysisEventRegisterCenter.java deleted file mode 100644 index 7969f77f3..000000000 --- a/src/main/java/com/alibaba/excel/event/AnalysisEventRegisterCenter.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.alibaba.excel.event; - -/** - * Event center. - * - * @author jipengfei - */ -public interface AnalysisEventRegisterCenter { - - /** - * Append listener - * - * @param name listener name. - * @param listener Callback method after each row is parsed. - */ - void appendLister(String name, AnalysisEventListener listener); - - /** - * Parse one row to notify all event listeners - * - * @param event parse event - */ - void notifyListeners(OneRowAnalysisFinishEvent event); - - /** - * Clean all listeners. - */ - void cleanAllListeners(); -} diff --git a/src/main/java/com/alibaba/excel/event/OneRowAnalysisFinishEvent.java b/src/main/java/com/alibaba/excel/event/OneRowAnalysisFinishEvent.java deleted file mode 100644 index 048e9d66e..000000000 --- a/src/main/java/com/alibaba/excel/event/OneRowAnalysisFinishEvent.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alibaba.excel.event; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author jipengfei - */ -public class OneRowAnalysisFinishEvent { - - public OneRowAnalysisFinishEvent(Object content) { - this.data = content; - } - - public OneRowAnalysisFinishEvent(String[] content, int length) { - if (content != null) { - List ls = new ArrayList(length); - for (int i = 0; i <= length; i++) { - ls.add(content[i]); - } - data = ls; - } - } - - private Object data; - - public Object getData() { - return data; - } - - public void setData(Object data) { - this.data = data; - } -} diff --git a/src/main/java/com/alibaba/excel/event/WriteHandler.java b/src/main/java/com/alibaba/excel/event/WriteHandler.java deleted file mode 100644 index 408799f62..000000000 --- a/src/main/java/com/alibaba/excel/event/WriteHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.alibaba.excel.event; - -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; - -/** - * - * @author jipengfei - */ -public interface WriteHandler { - - /** - * Custom operation after creating each sheet - * - * @param sheetNo - * @param sheet - */ - void sheet(int sheetNo, Sheet sheet); - - /** - * Custom operation after creating each row - * - * @param rowNum - * @param row - */ - void row(int rowNum, Row row); - - /** - * Custom operation after creating each cell - * @param cellNum - * @param cell - */ - void cell(int cellNum, Cell cell); -} diff --git a/src/main/java/com/alibaba/excel/metadata/BaseRowModel.java b/src/main/java/com/alibaba/excel/metadata/BaseRowModel.java deleted file mode 100644 index 6f4a80596..000000000 --- a/src/main/java/com/alibaba/excel/metadata/BaseRowModel.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.alibaba.excel.metadata; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.poi.ss.usermodel.CellStyle; - -/** - * Excel基础模型 - * @author jipengfei - */ -public class BaseRowModel { - - /** - * 每列样式 - */ - private Map cellStyleMap = new HashMap(); - - public void addStyle(Integer row, CellStyle cellStyle){ - cellStyleMap.put(row,cellStyle); - } - - public CellStyle getStyle(Integer row){ - return cellStyleMap.get(row); - } - - public Map getCellStyleMap() { - return cellStyleMap; - } - - public void setCellStyleMap(Map cellStyleMap) { - this.cellStyleMap = cellStyleMap; - } -} diff --git a/src/main/java/com/alibaba/excel/metadata/CellRange.java b/src/main/java/com/alibaba/excel/metadata/CellRange.java deleted file mode 100644 index 6805526e6..000000000 --- a/src/main/java/com/alibaba/excel/metadata/CellRange.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.alibaba.excel.metadata; - -/** - * @author jipengfei - */ -public class CellRange { - - private int firstRow; - private int lastRow; - private int firstCol; - private int lastCol; - - public CellRange(int firstRow, int lastRow, int firstCol, int lastCol) { - this.firstRow = firstRow; - this.lastRow = lastRow; - this.firstCol = firstCol; - this.lastCol = lastCol; - } - - public int getFirstRow() { - return firstRow; - } - - public void setFirstRow(int firstRow) { - this.firstRow = firstRow; - } - - public int getLastRow() { - return lastRow; - } - - public void setLastRow(int lastRow) { - this.lastRow = lastRow; - } - - public int getFirstCol() { - return firstCol; - } - - public void setFirstCol(int firstCol) { - this.firstCol = firstCol; - } - - public int getLastCol() { - return lastCol; - } - - public void setLastCol(int lastCol) { - this.lastCol = lastCol; - } -} diff --git a/src/main/java/com/alibaba/excel/metadata/ExcelColumnProperty.java b/src/main/java/com/alibaba/excel/metadata/ExcelColumnProperty.java deleted file mode 100644 index cc6442f1e..000000000 --- a/src/main/java/com/alibaba/excel/metadata/ExcelColumnProperty.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.alibaba.excel.metadata; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - -/** - * @author jipengfei - */ -public class ExcelColumnProperty implements Comparable { - - /** - */ - private Field field; - - /** - */ - private int index = 99999; - - /** - */ - private List head = new ArrayList(); - - /** - */ - private String format; - - public String getFormat() { - return format; - } - - public void setFormat(String format) { - this.format = format; - } - - public Field getField() { - return field; - } - - public void setField(Field field) { - this.field = field; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public List getHead() { - return head; - } - - public void setHead(List head) { - this.head = head; - } - - public int compareTo(ExcelColumnProperty o) { - int x = this.index; - int y = o.getIndex(); - return (x < y) ? -1 : ((x == y) ? 0 : 1); - } -} \ No newline at end of file diff --git a/src/main/java/com/alibaba/excel/metadata/ExcelHeadProperty.java b/src/main/java/com/alibaba/excel/metadata/ExcelHeadProperty.java deleted file mode 100644 index 95ff9fda5..000000000 --- a/src/main/java/com/alibaba/excel/metadata/ExcelHeadProperty.java +++ /dev/null @@ -1,228 +0,0 @@ -package com.alibaba.excel.metadata; - -import com.alibaba.excel.annotation.ExcelColumnNum; -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.util.StringUtils; - -import java.lang.reflect.Field; -import java.util.*; - -/** - * Define the header attribute of excel - * - * @author jipengfei - */ -public class ExcelHeadProperty { - - /** - * Custom class - */ - private Class headClazz; - - /** - * A two-dimensional array describing the header - */ - private List> head = new ArrayList>(); - - /** - * Attributes described by the header - */ - private List columnPropertyList = new ArrayList(); - - /** - * Attributes described by the header - */ - private Map excelColumnPropertyMap1 = new HashMap(); - - public ExcelHeadProperty(Class headClazz, List> head) { - this.headClazz = headClazz; - this.head = head; - initColumnProperties(); - } - - /** - */ - private void initColumnProperties() { - if (this.headClazz != null) { - List fieldList = new ArrayList(); - Class tempClass = this.headClazz; - //When the parent class is null, it indicates that the parent class (Object class) has reached the top - // level. - while (tempClass != null) { - fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields())); - //Get the parent class and give it to yourself - tempClass = tempClass.getSuperclass(); - } - List> headList = new ArrayList>(); - for (Field f : fieldList) { - initOneColumnProperty(f); - } - //对列排序 - Collections.sort(columnPropertyList); - if (head == null || head.size() == 0) { - for (ExcelColumnProperty excelColumnProperty : columnPropertyList) { - headList.add(excelColumnProperty.getHead()); - } - this.head = headList; - } - } - } - - /** - * @param f - */ - private void initOneColumnProperty(Field f) { - ExcelProperty p = f.getAnnotation(ExcelProperty.class); - ExcelColumnProperty excelHeadProperty = null; - if (p != null) { - excelHeadProperty = new ExcelColumnProperty(); - excelHeadProperty.setField(f); - excelHeadProperty.setHead(Arrays.asList(p.value())); - excelHeadProperty.setIndex(p.index()); - excelHeadProperty.setFormat(p.format()); - excelColumnPropertyMap1.put(p.index(), excelHeadProperty); - } else { - ExcelColumnNum columnNum = f.getAnnotation(ExcelColumnNum.class); - if (columnNum != null) { - excelHeadProperty = new ExcelColumnProperty(); - excelHeadProperty.setField(f); - excelHeadProperty.setIndex(columnNum.value()); - excelHeadProperty.setFormat(columnNum.format()); - excelColumnPropertyMap1.put(columnNum.value(), excelHeadProperty); - } - } - if (excelHeadProperty != null) { - this.columnPropertyList.add(excelHeadProperty); - } - - } - - /** - * - */ - public void appendOneRow(List row) { - - for (int i = 0; i < row.size(); i++) { - List list; - if (head.size() <= i) { - list = new ArrayList(); - head.add(list); - } else { - list = head.get(0); - } - list.add(row.get(i)); - } - - } - - /** - * @param columnNum - * @return - */ - public ExcelColumnProperty getExcelColumnProperty(int columnNum) { - return excelColumnPropertyMap1.get(columnNum); - } - - public Class getHeadClazz() { - return headClazz; - } - - public void setHeadClazz(Class headClazz) { - this.headClazz = headClazz; - } - - public List> getHead() { - return this.head; - } - - public void setHead(List> head) { - this.head = head; - } - - public List getColumnPropertyList() { - return columnPropertyList; - } - - public void setColumnPropertyList(List columnPropertyList) { - this.columnPropertyList = columnPropertyList; - } - - /** - * Calculate all cells that need to be merged - * - * @return cells that need to be merged - */ - public List getCellRangeModels() { - List cellRanges = new ArrayList(); - for (int i = 0; i < head.size(); i++) { - List columnValues = head.get(i); - for (int j = 0; j < columnValues.size(); j++) { - int lastRow = getLastRangNum(j, columnValues.get(j), columnValues); - int lastColumn = getLastRangNum(i, columnValues.get(j), getHeadByRowNum(j)); - if ((lastRow > j || lastColumn > i) && lastRow >= 0 && lastColumn >= 0) { - cellRanges.add(new CellRange(j, lastRow, i, lastColumn)); - } - } - } - return cellRanges; - } - - public List getHeadByRowNum(int rowNum) { - List l = new ArrayList(head.size()); - for (List list : head) { - if (list.size() > rowNum) { - l.add(list.get(rowNum)); - } else { - l.add(list.get(list.size() - 1)); - } - } - return l; - } - - /** - * Get the last consecutive string position - * - * @param j current value position - * @param value value content - * @param values values - * @return the last consecutive string position - */ - private int getLastRangNum(int j, String value, List values) { - if (value == null) { - return -1; - } - if (j > 0) { - String preValue = values.get(j - 1); - if (value.equals(preValue)) { - return -1; - } - } - int last = j; - for (int i = last + 1; i < values.size(); i++) { - String current = values.get(i); - if (value.equals(current)) { - last = i; - } else { - // if i>j && !value.equals(current) Indicates that the continuous range is exceeded - if (i > j) { - break; - } - } - } - return last; - - } - - public int getRowNum() { - int headRowNum = 0; - for (List list : head) { - if (list != null && list.size() > 0) { - if (list.size() > headRowNum) { - headRowNum = list.size(); - } - } - } - return headRowNum; - } - -} diff --git a/src/main/java/com/alibaba/excel/metadata/IndexValue.java b/src/main/java/com/alibaba/excel/metadata/IndexValue.java deleted file mode 100644 index 2be042759..000000000 --- a/src/main/java/com/alibaba/excel/metadata/IndexValue.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.alibaba.excel.metadata; - -/** - * @author jipengfei - */ -public class IndexValue { - - private String v_index; - private String v_value; - - public IndexValue(String v_index, String v_value) { - super(); - this.v_index = v_index; - this.v_value = v_value; - } - - public String getV_index() { - return v_index; - } - - public void setV_index(String v_index) { - this.v_index = v_index; - } - - public String getV_value() { - return v_value; - } - - public void setV_value(String v_value) { - this.v_value = v_value; - } - - @Override - public String toString() { - return "IndexValue [v_index=" + v_index + ", v_value=" + v_value + "]"; - } - - -} diff --git a/src/main/java/com/alibaba/excel/metadata/Sheet.java b/src/main/java/com/alibaba/excel/metadata/Sheet.java deleted file mode 100644 index ef98b9cc7..000000000 --- a/src/main/java/com/alibaba/excel/metadata/Sheet.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.alibaba.excel.metadata; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * - * @author jipengfei - */ -public class Sheet { - - /** - */ - private int headLineMun; - - /** - * Starting from 1 - */ - private int sheetNo; - - /** - */ - private String sheetName; - - /** - */ - private Class clazz; - - /** - */ - private List> head; - - /** - * - */ - private TableStyle tableStyle; - - /** - * column with - */ - private Map columnWidthMap = new HashMap(); - - /** - * - */ - private Boolean autoWidth = Boolean.FALSE; - - /** - * - */ - private int startRow = 0; - - - public Sheet(int sheetNo) { - this.sheetNo = sheetNo; - } - - public Sheet(int sheetNo, int headLineMun) { - this.sheetNo = sheetNo; - this.headLineMun = headLineMun; - } - - public Sheet(int sheetNo, int headLineMun, Class clazz) { - this.sheetNo = sheetNo; - this.headLineMun = headLineMun; - this.clazz = clazz; - } - - public Sheet(int sheetNo, int headLineMun, Class clazz, String sheetName, - List> head) { - this.sheetNo = sheetNo; - this.clazz = clazz; - this.headLineMun = headLineMun; - this.sheetName = sheetName; - this.head = head; - } - - public List> getHead() { - return head; - } - - public void setHead(List> head) { - this.head = head; - } - - public Class getClazz() { - return clazz; - } - - public void setClazz(Class clazz) { - this.clazz = clazz; - if (headLineMun == 0) { - this.headLineMun = 1; - } - } - - public int getHeadLineMun() { - return headLineMun; - } - - public void setHeadLineMun(int headLineMun) { - this.headLineMun = headLineMun; - } - - public int getSheetNo() { - return sheetNo; - } - - public void setSheetNo(int sheetNo) { - this.sheetNo = sheetNo; - } - - public String getSheetName() { - return sheetName; - } - - public void setSheetName(String sheetName) { - this.sheetName = sheetName; - } - - public TableStyle getTableStyle() { - return tableStyle; - } - - public void setTableStyle(TableStyle tableStyle) { - this.tableStyle = tableStyle; - } - - - - public Map getColumnWidthMap() { - return columnWidthMap; - } - - public void setColumnWidthMap(Map columnWidthMap) { - this.columnWidthMap = columnWidthMap; - } - - @Override - public String toString() { - return "Sheet{" + - "headLineMun=" + headLineMun + - ", sheetNo=" + sheetNo + - ", sheetName='" + sheetName + '\'' + - ", clazz=" + clazz + - ", head=" + head + - ", tableStyle=" + tableStyle + - ", columnWidthMap=" + columnWidthMap + - '}'; - } - - public Boolean getAutoWidth() { - return autoWidth; - } - - public void setAutoWidth(Boolean autoWidth) { - this.autoWidth = autoWidth; - } - - public int getStartRow() { - return startRow; - } - - public void setStartRow(int startRow) { - this.startRow = startRow; - } -} diff --git a/src/main/java/com/alibaba/excel/metadata/Table.java b/src/main/java/com/alibaba/excel/metadata/Table.java deleted file mode 100644 index 6643c4639..000000000 --- a/src/main/java/com/alibaba/excel/metadata/Table.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.alibaba.excel.metadata; - -import java.util.List; - -/** - * @author jipengfei - */ -public class Table { - /** - */ - private Class clazz; - - /** - */ - private List> head; - - /** - */ - private int tableNo; - - /** - */ - private TableStyle tableStyle; - - public TableStyle getTableStyle() { - return tableStyle; - } - - public void setTableStyle(TableStyle tableStyle) { - this.tableStyle = tableStyle; - } - - public Table(Integer tableNo) { - this.tableNo = tableNo; - } - - public Class getClazz() { - return clazz; - } - - public void setClazz(Class clazz) { - this.clazz = clazz; - } - - public List> getHead() { - return head; - } - - public void setHead(List> head) { - this.head = head; - } - - public int getTableNo() { - return tableNo; - } - - public void setTableNo(int tableNo) { - this.tableNo = tableNo; - } -} diff --git a/src/main/java/com/alibaba/excel/metadata/TableStyle.java b/src/main/java/com/alibaba/excel/metadata/TableStyle.java deleted file mode 100644 index 228019c3a..000000000 --- a/src/main/java/com/alibaba/excel/metadata/TableStyle.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.alibaba.excel.metadata; - -import org.apache.poi.ss.usermodel.IndexedColors; - -/** - * @author jipengfei - */ -public class TableStyle { - - /** - * 表头背景颜色 - */ - private IndexedColors tableHeadBackGroundColor; - - /** - * 表头字体样式 - */ - private Font tableHeadFont; - - /** - * 表格内容字体样式 - */ - private Font tableContentFont; - - /** - * 表格内容背景颜色 - */ - private IndexedColors tableContentBackGroundColor; - - public IndexedColors getTableHeadBackGroundColor() { - return tableHeadBackGroundColor; - } - - public void setTableHeadBackGroundColor(IndexedColors tableHeadBackGroundColor) { - this.tableHeadBackGroundColor = tableHeadBackGroundColor; - } - - public Font getTableHeadFont() { - return tableHeadFont; - } - - public void setTableHeadFont(Font tableHeadFont) { - this.tableHeadFont = tableHeadFont; - } - - public Font getTableContentFont() { - return tableContentFont; - } - - public void setTableContentFont(Font tableContentFont) { - this.tableContentFont = tableContentFont; - } - - public IndexedColors getTableContentBackGroundColor() { - return tableContentBackGroundColor; - } - - public void setTableContentBackGroundColor(IndexedColors tableContentBackGroundColor) { - this.tableContentBackGroundColor = tableContentBackGroundColor; - } -} diff --git a/src/main/java/com/alibaba/excel/modelbuild/ModelBuildEventListener.java b/src/main/java/com/alibaba/excel/modelbuild/ModelBuildEventListener.java deleted file mode 100644 index bea35c7fb..000000000 --- a/src/main/java/com/alibaba/excel/modelbuild/ModelBuildEventListener.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.alibaba.excel.modelbuild; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.metadata.ExcelHeadProperty; -import com.alibaba.excel.util.TypeUtil; -import net.sf.cglib.beans.BeanMap; - -import java.util.List; - -/** - * @author jipengfei - */ -public class ModelBuildEventListener extends AnalysisEventListener { - - @Override - public void invoke(Object object, AnalysisContext context) { - if (context.getExcelHeadProperty() != null && context.getExcelHeadProperty().getHeadClazz() != null) { - try { - Object resultModel = buildUserModel(context, (List)object); - context.setCurrentRowAnalysisResult(resultModel); - } catch (Exception e) { - throw new ExcelGenerateException(e); - } - } - } - - private Object buildUserModel(AnalysisContext context, List stringList) throws Exception { - ExcelHeadProperty excelHeadProperty = context.getExcelHeadProperty(); - Object resultModel = excelHeadProperty.getHeadClazz().newInstance(); - if (excelHeadProperty == null) { - return resultModel; - } - BeanMap.create(resultModel).putAll( - TypeUtil.getFieldValues(stringList, excelHeadProperty, context.use1904WindowDate())); - return resultModel; - } - - @Override - public void doAfterAllAnalysed(AnalysisContext context) { - - } -} diff --git a/src/main/java/com/alibaba/excel/parameter/AnalysisParam.java b/src/main/java/com/alibaba/excel/parameter/AnalysisParam.java deleted file mode 100644 index f8679ac01..000000000 --- a/src/main/java/com/alibaba/excel/parameter/AnalysisParam.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.alibaba.excel.parameter; - -import com.alibaba.excel.support.ExcelTypeEnum; - -import java.io.InputStream; - -/** - * - * @author jipengfei - */ -@Deprecated -public class AnalysisParam { - - /** - * @see ExcelTypeEnum - */ - private ExcelTypeEnum excelTypeEnum; - - /** - * the POI filesystem that contains the Workbook stream - */ - private InputStream in; - - /** - * user defined param to listener use - */ - private Object customContent; - - public AnalysisParam(InputStream in, ExcelTypeEnum excelTypeEnum, Object customContent) { - this.in = in; - this.excelTypeEnum = excelTypeEnum; - this.customContent = customContent; - } - - public ExcelTypeEnum getExcelTypeEnum() { - return excelTypeEnum; - } - - public void setExcelTypeEnum(ExcelTypeEnum excelTypeEnum) { - this.excelTypeEnum = excelTypeEnum; - } - - public Object getCustomContent() { - return customContent; - } - - public void setCustomContent(Object customContent) { - this.customContent = customContent; - } - - public InputStream getIn() { - return in; - } - - public void setIn(InputStream in) { - this.in = in; - } -} diff --git a/src/main/java/com/alibaba/excel/parameter/ExcelWriteParam.java b/src/main/java/com/alibaba/excel/parameter/ExcelWriteParam.java deleted file mode 100644 index 9104e83d5..000000000 --- a/src/main/java/com/alibaba/excel/parameter/ExcelWriteParam.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.alibaba.excel.parameter; - -import java.io.OutputStream; - -import com.alibaba.excel.support.ExcelTypeEnum; - -/** - * {@link com.alibaba.excel.ExcelWriter} - * - * @author jipengfei - */ -@Deprecated -public class ExcelWriteParam { - - /** - */ - private OutputStream outputStream; - - /** - */ - private ExcelTypeEnum type; - - public ExcelWriteParam(OutputStream outputStream, ExcelTypeEnum type) { - this.outputStream = outputStream; - this.type = type; - - } - - public OutputStream getOutputStream() { - return outputStream; - } - - public void setOutputStream(OutputStream outputStream) { - this.outputStream = outputStream; - } - - public ExcelTypeEnum getType() { - return type; - } - - public void setType(ExcelTypeEnum type) { - this.type = type; - } -} diff --git a/src/main/java/com/alibaba/excel/parameter/GenerateParam.java b/src/main/java/com/alibaba/excel/parameter/GenerateParam.java deleted file mode 100644 index 9dfccc5f3..000000000 --- a/src/main/java/com/alibaba/excel/parameter/GenerateParam.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.alibaba.excel.parameter; - -import java.io.OutputStream; - -import com.alibaba.excel.support.ExcelTypeEnum; - -/** - * Created by jipengfei on 17/2/19. - */ -public class GenerateParam { - - private OutputStream outputStream; - - private String sheetName; - - private Class clazz; - - private ExcelTypeEnum type; - - public GenerateParam(String sheetName, Class clazz, OutputStream outputStream) { - this.outputStream = outputStream; - this.sheetName = sheetName; - this.clazz = clazz; - } - - public OutputStream getOutputStream() { - return outputStream; - } - - public void setOutputStream(OutputStream outputStream) { - this.outputStream = outputStream; - } - - - public String getSheetName() { - return sheetName; - } - - public void setSheetName(String sheetName) { - this.sheetName = sheetName; - } - - public Class getClazz() { - return clazz; - } - - public void setClazz(Class clazz) { - this.clazz = clazz; - } - - public ExcelTypeEnum getType() { - return type; - } - - public void setType(ExcelTypeEnum type) { - this.type = type; - } -} diff --git a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java b/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java deleted file mode 100644 index 57882b902..000000000 --- a/src/main/java/com/alibaba/excel/support/ExcelTypeEnum.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.alibaba.excel.support; - -import org.apache.poi.poifs.filesystem.FileMagic; - -import java.io.IOException; -import java.io.InputStream; - -/** - * @author jipengfei - */ -public enum ExcelTypeEnum { - - XLS(".xls"), XLSX(".xlsx"); - - private String value; - - ExcelTypeEnum(String value) { - this.setValue(value); - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - public static ExcelTypeEnum valueOf(InputStream inputStream){ - try { - if (!inputStream.markSupported()) { - return null; - } - FileMagic fileMagic = FileMagic.valueOf(inputStream); - if(FileMagic.OLE2.equals(fileMagic)){ - return XLS; - } - if(FileMagic.OOXML.equals(fileMagic)){ - return XLSX; - } - return null; - } catch (IOException e) { - throw new RuntimeException(e); - } - } -} diff --git a/src/main/java/com/alibaba/excel/util/CollectionUtils.java b/src/main/java/com/alibaba/excel/util/CollectionUtils.java deleted file mode 100644 index 3b9025fd8..000000000 --- a/src/main/java/com/alibaba/excel/util/CollectionUtils.java +++ /dev/null @@ -1,340 +0,0 @@ -package com.alibaba.excel.util; - -import java.util.*; - -/** - * Miscellaneous collection utility methods. - * Mainly for internal use within the framework. - * - * @author Juergen Hoeller - * @author Rob Harrop - * @author Arjen Poutsma - * @since 1.1.3 - */ -public abstract class CollectionUtils { - - /** - * Return {@code true} if the supplied Collection is {@code null} or empty. - * Otherwise, return {@code false}. - * @param collection the Collection to check - * @return whether the given Collection is empty - */ - public static boolean isEmpty(Collection collection) { - return (collection == null || collection.isEmpty()); - } - - /** - * Return {@code true} if the supplied Map is {@code null} or empty. - * Otherwise, return {@code false}. - * @param map the Map to check - * @return whether the given Map is empty - */ - public static boolean isEmpty(Map map) { - return (map == null || map.isEmpty()); - } - - /** - * Convert the supplied array into a List. A primitive array gets converted - * into a List of the appropriate wrapper type. - *

NOTE: Generally prefer the standard {@link Arrays#asList} method. - * This {@code arrayToList} method is just meant to deal with an incoming Object - * value that might be an {@code Object[]} or a primitive array at runtime. - *

A {@code null} source value will be converted to an empty List. - * @param source the (potentially primitive) array - * @return the converted List result - * @see ObjectUtils#toObjectArray(Object) - * @see Arrays#asList(Object[]) - */ - @SuppressWarnings("rawtypes") - public static List arrayToList(Object source) { - return Arrays.asList(ObjectUtils.toObjectArray(source)); - } - - /** - * Merge the given array into the given Collection. - * @param array the array to merge (may be {@code null}) - * @param collection the target Collection to merge the array into - */ - @SuppressWarnings("unchecked") - public static void mergeArrayIntoCollection(Object array, Collection collection) { - if (collection == null) { - throw new IllegalArgumentException("Collection must not be null"); - } - Object[] arr = ObjectUtils.toObjectArray(array); - for (Object elem : arr) { - collection.add((E) elem); - } - } - - /** - * Merge the given Properties instance into the given Map, - * copying all properties (key-value pairs) over. - *

Uses {@code Properties.propertyNames()} to even catch - * default properties linked into the original Properties instance. - * @param props the Properties instance to merge (may be {@code null}) - * @param map the target Map to merge the properties into - */ - @SuppressWarnings("unchecked") - public static void mergePropertiesIntoMap(Properties props, Map map) { - if (map == null) { - throw new IllegalArgumentException("Map must not be null"); - } - if (props != null) { - for (Enumeration en = props.propertyNames(); en.hasMoreElements();) { - String key = (String) en.nextElement(); - Object value = props.get(key); - if (value == null) { - // Allow for defaults fallback or potentially overridden accessor... - value = props.getProperty(key); - } - map.put((K) key, (V) value); - } - } - } - - - /** - * Check whether the given Iterator contains the given element. - * @param iterator the Iterator to check - * @param element the element to look for - * @return {@code true} if found, {@code false} else - */ - public static boolean contains(Iterator iterator, Object element) { - if (iterator != null) { - while (iterator.hasNext()) { - Object candidate = iterator.next(); - if (ObjectUtils.nullSafeEquals(candidate, element)) { - return true; - } - } - } - return false; - } - - /** - * Check whether the given Enumeration contains the given element. - * @param enumeration the Enumeration to check - * @param element the element to look for - * @return {@code true} if found, {@code false} else - */ - public static boolean contains(Enumeration enumeration, Object element) { - if (enumeration != null) { - while (enumeration.hasMoreElements()) { - Object candidate = enumeration.nextElement(); - if (ObjectUtils.nullSafeEquals(candidate, element)) { - return true; - } - } - } - return false; - } - - /** - * Check whether the given Collection contains the given element instance. - *

Enforces the given instance to be present, rather than returning - * {@code true} for an equal element as well. - * @param collection the Collection to check - * @param element the element to look for - * @return {@code true} if found, {@code false} else - */ - public static boolean containsInstance(Collection collection, Object element) { - if (collection != null) { - for (Object candidate : collection) { - if (candidate == element) { - return true; - } - } - } - return false; - } - - /** - * Return {@code true} if any element in '{@code candidates}' is - * contained in '{@code source}'; otherwise returns {@code false}. - * @param source the source Collection - * @param candidates the candidates to search for - * @return whether any of the candidates has been found - */ - public static boolean containsAny(Collection source, Collection candidates) { - if (isEmpty(source) || isEmpty(candidates)) { - return false; - } - for (Object candidate : candidates) { - if (source.contains(candidate)) { - return true; - } - } - return false; - } - - /** - * Return the first element in '{@code candidates}' that is contained in - * '{@code source}'. If no element in '{@code candidates}' is present in - * '{@code source}' returns {@code null}. Iteration order is - * {@link Collection} implementation specific. - * @param source the source Collection - * @param candidates the candidates to search for - * @return the first present object, or {@code null} if not found - */ - @SuppressWarnings("unchecked") - public static E findFirstMatch(Collection source, Collection candidates) { - if (isEmpty(source) || isEmpty(candidates)) { - return null; - } - for (Object candidate : candidates) { - if (source.contains(candidate)) { - return (E) candidate; - } - } - return null; - } - - /** - * Find a single value of the given type in the given Collection. - * @param collection the Collection to search - * @param type the type to look for - * @return a value of the given type found if there is a clear match, - * or {@code null} if none or more than one such value found - */ - @SuppressWarnings("unchecked") - public static T findValueOfType(Collection collection, Class type) { - if (isEmpty(collection)) { - return null; - } - T value = null; - for (Object element : collection) { - if (type == null || type.isInstance(element)) { - if (value != null) { - // More than one value found... no clear single value. - return null; - } - value = (T) element; - } - } - return value; - } - - /** - * Find a single value of one of the given types in the given Collection: - * searching the Collection for a value of the first type, then - * searching for a value of the second type, etc. - * @param collection the collection to search - * @param types the types to look for, in prioritized order - * @return a value of one of the given types found if there is a clear match, - * or {@code null} if none or more than one such value found - */ - public static Object findValueOfType(Collection collection, Class[] types) { - if (isEmpty(collection) || ObjectUtils.isEmpty(types)) { - return null; - } - for (Class type : types) { - Object value = findValueOfType(collection, type); - if (value != null) { - return value; - } - } - return null; - } - - /** - * Determine whether the given Collection only contains a single unique object. - * @param collection the Collection to check - * @return {@code true} if the collection contains a single reference or - * multiple references to the same instance, {@code false} else - */ - public static boolean hasUniqueObject(Collection collection) { - if (isEmpty(collection)) { - return false; - } - boolean hasCandidate = false; - Object candidate = null; - for (Object elem : collection) { - if (!hasCandidate) { - hasCandidate = true; - candidate = elem; - } - else if (candidate != elem) { - return false; - } - } - return true; - } - - /** - * Find the common element type of the given Collection, if any. - * @param collection the Collection to check - * @return the common element type, or {@code null} if no clear - * common type has been found (or the collection was empty) - */ - public static Class findCommonElementType(Collection collection) { - if (isEmpty(collection)) { - return null; - } - Class candidate = null; - for (Object val : collection) { - if (val != null) { - if (candidate == null) { - candidate = val.getClass(); - } - else if (candidate != val.getClass()) { - return null; - } - } - } - return candidate; - } - - /** - * Marshal the elements from the given enumeration into an array of the given type. - * Enumeration elements must be assignable to the type of the given array. The array - * returned will be a different instance than the array given. - */ - public static A[] toArray(Enumeration enumeration, A[] array) { - ArrayList elements = new ArrayList(); - while (enumeration.hasMoreElements()) { - elements.add(enumeration.nextElement()); - } - return elements.toArray(array); - } - - /** - * Adapt an enumeration to an iterator. - * @param enumeration the enumeration - * @return the iterator - */ - public static Iterator toIterator(Enumeration enumeration) { - return new EnumerationIterator(enumeration); - } - - - - /** - * Iterator wrapping an Enumeration. - */ - private static class EnumerationIterator implements Iterator { - - private final Enumeration enumeration; - - public EnumerationIterator(Enumeration enumeration) { - this.enumeration = enumeration; - } - - @Override - public boolean hasNext() { - return this.enumeration.hasMoreElements(); - } - - @Override - public E next() { - return this.enumeration.nextElement(); - } - - @Override - public void remove() throws UnsupportedOperationException { - throw new UnsupportedOperationException("Not supported"); - } - } - - -} - diff --git a/src/main/java/com/alibaba/excel/util/IndexValueConverter.java b/src/main/java/com/alibaba/excel/util/IndexValueConverter.java deleted file mode 100644 index a0c1529cb..000000000 --- a/src/main/java/com/alibaba/excel/util/IndexValueConverter.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.alibaba.excel.util; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -import com.alibaba.excel.metadata.IndexValue; - -/** - * 去除空Cell - * @author jipengfei - */ -public class IndexValueConverter { - public static List converter(List i_list) { - - List tem = new ArrayList(); - - char[] start = {'@'}; - int j = 0; - for (; j < i_list.size(); j++) { - IndexValue currentIndexValue = i_list.get(j); - char[] currentIndex = currentIndexValue.getV_index().replaceAll("[0-9]", "").toCharArray(); - if (j > 0) { - start = i_list.get(j - 1).getV_index().replaceAll("[0-9]", "").toCharArray(); - } - int deep = subtraction26(currentIndex, start); - int k = 0; - for (; k < deep - 1; k++) { - tem.add(null); - } - tem.add(currentIndexValue.getV_value()); - } - return tem; - } - - private static int subtraction26(char[] currentIndex, char[] beforeIndex) { - int result = 0; - - Stack currentStack = new Stack(); - Stack berforStack = new Stack(); - - for (int i = 0; i < currentIndex.length; i++) { - currentStack.push(currentIndex[i]); - } - for (int i = 0; i < beforeIndex.length; i++) { - berforStack.push(beforeIndex[i]); - } - int i = 0; - char beforechar = '@'; - while (!currentStack.isEmpty()) { - char currentChar = currentStack.pop(); - if (!berforStack.isEmpty()) { - beforechar = berforStack.pop(); - } - int n = currentChar - beforechar; - if(n<0){ - n = n+26; - if(!currentStack.isEmpty()){ - char borrow = currentStack.pop(); - char newBorrow =(char)(borrow -1); - currentStack.push(newBorrow); - } - } - - - result += n * Math.pow(26, i); - i++; - beforechar='@'; - } - - return result; - } -} diff --git a/src/main/java/com/alibaba/excel/util/ObjectUtils.java b/src/main/java/com/alibaba/excel/util/ObjectUtils.java deleted file mode 100644 index 8b8556c2a..000000000 --- a/src/main/java/com/alibaba/excel/util/ObjectUtils.java +++ /dev/null @@ -1,945 +0,0 @@ -package com.alibaba.excel.util; - -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import java.lang.reflect.Array; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; - -/** - * Miscellaneous object utility methods. - * - *

Mainly for internal use within the framework. - * - *

Thanks to Alex Ruiz for contributing several enhancements to this class! - * - * @author Juergen Hoeller - * @author Keith Donald - * @author Rod Johnson - * @author Rob Harrop - * @author Chris Beams - * @author Sam Brannen - * @since 19.03.2004 - * @see CollectionUtils - */ -public abstract class ObjectUtils { - - private static final int INITIAL_HASH = 7; - private static final int MULTIPLIER = 31; - - private static final String EMPTY_STRING = ""; - private static final String NULL_STRING = "null"; - private static final String ARRAY_START = "{"; - private static final String ARRAY_END = "}"; - private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END; - private static final String ARRAY_ELEMENT_SEPARATOR = ", "; - - - /** - * Return whether the given throwable is a checked exception: - * that is, neither a RuntimeException nor an Error. - * @param ex the throwable to check - * @return whether the throwable is a checked exception - * @see Exception - * @see RuntimeException - * @see Error - */ - public static boolean isCheckedException(Throwable ex) { - return !(ex instanceof RuntimeException || ex instanceof Error); - } - - /** - * Check whether the given exception is compatible with the specified - * exception types, as declared in a throws clause. - * @param ex the exception to check - * @param declaredExceptions the exception types declared in the throws clause - * @return whether the given exception is compatible - */ - public static boolean isCompatibleWithThrowsClause(Throwable ex, Class... declaredExceptions) { - if (!isCheckedException(ex)) { - return true; - } - if (declaredExceptions != null) { - for (Class declaredException : declaredExceptions) { - if (declaredException.isInstance(ex)) { - return true; - } - } - } - return false; - } - - /** - * Determine whether the given object is an array: - * either an Object array or a primitive array. - * @param obj the object to check - */ - public static boolean isArray(Object obj) { - return (obj != null && obj.getClass().isArray()); - } - - /** - * Determine whether the given array is empty: - * i.e. {@code null} or of zero length. - * @param array the array to check - * @see #isEmpty(Object) - */ - public static boolean isEmpty(Object[] array) { - return (array == null || array.length == 0); - } - - /** - * Determine whether the given object is empty. - *

This method supports the following object types. - *

- *

If the given object is non-null and not one of the aforementioned - * supported types, this method returns {@code false}. - * @param obj the object to check - * @return {@code true} if the object is {@code null} or empty - * @since 4.2 - * @see ObjectUtils#isEmpty(Object[]) - * @see StringUtils#hasLength(CharSequence) - * @see StringUtils#isEmpty(Object) - * @see CollectionUtils#isEmpty(Collection) - * @see CollectionUtils#isEmpty(Map) - */ - @SuppressWarnings("rawtypes") - public static boolean isEmpty(Object obj) { - if (obj == null) { - return true; - } - - if (obj instanceof CharSequence) { - return ((CharSequence) obj).length() == 0; - } - if (obj.getClass().isArray()) { - return Array.getLength(obj) == 0; - } - if (obj instanceof Collection) { - return ((Collection) obj).isEmpty(); - } - if (obj instanceof Map) { - return ((Map) obj).isEmpty(); - } - - // else - return false; - } - - /** - * Check whether the given array contains the given element. - * @param array the array to check (may be {@code null}, - * in which case the return value will always be {@code false}) - * @param element the element to check for - * @return whether the element has been found in the given array - */ - public static boolean containsElement(Object[] array, Object element) { - if (array == null) { - return false; - } - for (Object arrayEle : array) { - if (nullSafeEquals(arrayEle, element)) { - return true; - } - } - return false; - } - - /** - * Check whether the given array of enum constants contains a constant with the given name, - * ignoring case when determining a match. - * @param enumValues the enum values to check, typically the product of a call to MyEnum.values() - * @param constant the constant name to find (must not be null or empty string) - * @return whether the constant has been found in the given array - */ - public static boolean containsConstant(Enum[] enumValues, String constant) { - return containsConstant(enumValues, constant, false); - } - - /** - * Check whether the given array of enum constants contains a constant with the given name. - * @param enumValues the enum values to check, typically the product of a call to MyEnum.values() - * @param constant the constant name to find (must not be null or empty string) - * @param caseSensitive whether case is significant in determining a match - * @return whether the constant has been found in the given array - */ - public static boolean containsConstant(Enum[] enumValues, String constant, boolean caseSensitive) { - for (Enum candidate : enumValues) { - if (caseSensitive ? - candidate.toString().equals(constant) : - candidate.toString().equalsIgnoreCase(constant)) { - return true; - } - } - return false; - } - - /** - * Case insensitive alternative to {@link Enum#valueOf(Class, String)}. - * @param the concrete Enum type - * @param enumValues the array of all Enum constants in question, usually per Enum.values() - * @param constant the constant to get the enum value of - * @throws IllegalArgumentException if the given constant is not found in the given array - * of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception. - */ - public static > E caseInsensitiveValueOf(E[] enumValues, String constant) { - for (E candidate : enumValues) { - if (candidate.toString().equalsIgnoreCase(constant)) { - return candidate; - } - } - throw new IllegalArgumentException( - String.format("constant [%s] does not exist in enum type %s", - constant, enumValues.getClass().getComponentType().getName())); - } - - /** - * Append the given object to the given array, returning a new array - * consisting of the input array contents plus the given object. - * @param array the array to append to (can be {@code null}) - * @param obj the object to append - * @return the new array (of the same component type; never {@code null}) - */ - public static A[] addObjectToArray(A[] array, O obj) { - Class compType = Object.class; - if (array != null) { - compType = array.getClass().getComponentType(); - } - else if (obj != null) { - compType = obj.getClass(); - } - int newArrLength = (array != null ? array.length + 1 : 1); - @SuppressWarnings("unchecked") - A[] newArr = (A[]) Array.newInstance(compType, newArrLength); - if (array != null) { - System.arraycopy(array, 0, newArr, 0, array.length); - } - newArr[newArr.length - 1] = obj; - return newArr; - } - - /** - * Convert the given array (which may be a primitive array) to an - * object array (if necessary of primitive wrapper objects). - *

A {@code null} source value will be converted to an - * empty Object array. - * @param source the (potentially primitive) array - * @return the corresponding object array (never {@code null}) - * @throws IllegalArgumentException if the parameter is not an array - */ - public static Object[] toObjectArray(Object source) { - if (source instanceof Object[]) { - return (Object[]) source; - } - if (source == null) { - return new Object[0]; - } - if (!source.getClass().isArray()) { - throw new IllegalArgumentException("Source is not an array: " + source); - } - int length = Array.getLength(source); - if (length == 0) { - return new Object[0]; - } - Class wrapperType = Array.get(source, 0).getClass(); - Object[] newArray = (Object[]) Array.newInstance(wrapperType, length); - for (int i = 0; i < length; i++) { - newArray[i] = Array.get(source, i); - } - return newArray; - } - - - //--------------------------------------------------------------------- - // Convenience methods for content-based equality/hash-code handling - //--------------------------------------------------------------------- - - /** - * Determine if the given objects are equal, returning {@code true} if - * both are {@code null} or {@code false} if only one is {@code null}. - *

Compares arrays with {@code Arrays.equals}, performing an equality - * check based on the array elements rather than the array reference. - * @param o1 first Object to compare - * @param o2 second Object to compare - * @return whether the given objects are equal - * @see Object#equals(Object) - * @see Arrays#equals - */ - public static boolean nullSafeEquals(Object o1, Object o2) { - if (o1 == o2) { - return true; - } - if (o1 == null || o2 == null) { - return false; - } - if (o1.equals(o2)) { - return true; - } - if (o1.getClass().isArray() && o2.getClass().isArray()) { - return arrayEquals(o1, o2); - } - return false; - } - - /** - * Compare the given arrays with {@code Arrays.equals}, performing an equality - * check based on the array elements rather than the array reference. - * @param o1 first array to compare - * @param o2 second array to compare - * @return whether the given objects are equal - * @see #nullSafeEquals(Object, Object) - * @see Arrays#equals - */ - private static boolean arrayEquals(Object o1, Object o2) { - if (o1 instanceof Object[] && o2 instanceof Object[]) { - return Arrays.equals((Object[]) o1, (Object[]) o2); - } - if (o1 instanceof boolean[] && o2 instanceof boolean[]) { - return Arrays.equals((boolean[]) o1, (boolean[]) o2); - } - if (o1 instanceof byte[] && o2 instanceof byte[]) { - return Arrays.equals((byte[]) o1, (byte[]) o2); - } - if (o1 instanceof char[] && o2 instanceof char[]) { - return Arrays.equals((char[]) o1, (char[]) o2); - } - if (o1 instanceof double[] && o2 instanceof double[]) { - return Arrays.equals((double[]) o1, (double[]) o2); - } - if (o1 instanceof float[] && o2 instanceof float[]) { - return Arrays.equals((float[]) o1, (float[]) o2); - } - if (o1 instanceof int[] && o2 instanceof int[]) { - return Arrays.equals((int[]) o1, (int[]) o2); - } - if (o1 instanceof long[] && o2 instanceof long[]) { - return Arrays.equals((long[]) o1, (long[]) o2); - } - if (o1 instanceof short[] && o2 instanceof short[]) { - return Arrays.equals((short[]) o1, (short[]) o2); - } - return false; - } - - /** - * Return as hash code for the given object; typically the value of - * {@code Object#hashCode()}}. If the object is an array, - * this method will delegate to any of the {@code nullSafeHashCode} - * methods for arrays in this class. If the object is {@code null}, - * this method returns 0. - * @see Object#hashCode() - * @see #nullSafeHashCode(Object[]) - * @see #nullSafeHashCode(boolean[]) - * @see #nullSafeHashCode(byte[]) - * @see #nullSafeHashCode(char[]) - * @see #nullSafeHashCode(double[]) - * @see #nullSafeHashCode(float[]) - * @see #nullSafeHashCode(int[]) - * @see #nullSafeHashCode(long[]) - * @see #nullSafeHashCode(short[]) - */ - public static int nullSafeHashCode(Object obj) { - if (obj == null) { - return 0; - } - if (obj.getClass().isArray()) { - if (obj instanceof Object[]) { - return nullSafeHashCode((Object[]) obj); - } - if (obj instanceof boolean[]) { - return nullSafeHashCode((boolean[]) obj); - } - if (obj instanceof byte[]) { - return nullSafeHashCode((byte[]) obj); - } - if (obj instanceof char[]) { - return nullSafeHashCode((char[]) obj); - } - if (obj instanceof double[]) { - return nullSafeHashCode((double[]) obj); - } - if (obj instanceof float[]) { - return nullSafeHashCode((float[]) obj); - } - if (obj instanceof int[]) { - return nullSafeHashCode((int[]) obj); - } - if (obj instanceof long[]) { - return nullSafeHashCode((long[]) obj); - } - if (obj instanceof short[]) { - return nullSafeHashCode((short[]) obj); - } - } - return obj.hashCode(); - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(Object[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (Object element : array) { - hash = MULTIPLIER * hash + nullSafeHashCode(element); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(boolean[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (boolean element : array) { - hash = MULTIPLIER * hash + hashCode(element); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(byte[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (byte element : array) { - hash = MULTIPLIER * hash + element; - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(char[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (char element : array) { - hash = MULTIPLIER * hash + element; - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(double[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (double element : array) { - hash = MULTIPLIER * hash + hashCode(element); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(float[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (float element : array) { - hash = MULTIPLIER * hash + hashCode(element); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(int[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (int element : array) { - hash = MULTIPLIER * hash + element; - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(long[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (long element : array) { - hash = MULTIPLIER * hash + hashCode(element); - } - return hash; - } - - /** - * Return a hash code based on the contents of the specified array. - * If {@code array} is {@code null}, this method returns 0. - */ - public static int nullSafeHashCode(short[] array) { - if (array == null) { - return 0; - } - int hash = INITIAL_HASH; - for (short element : array) { - hash = MULTIPLIER * hash + element; - } - return hash; - } - - /** - * Return the same value as {@link Boolean#hashCode()}}. - * @see Boolean#hashCode() - */ - public static int hashCode(boolean bool) { - return (bool ? 1231 : 1237); - } - - /** - * Return the same value as {@link Double#hashCode()}}. - * @see Double#hashCode() - */ - public static int hashCode(double dbl) { - return hashCode(Double.doubleToLongBits(dbl)); - } - - /** - * Return the same value as {@link Float#hashCode()}}. - * @see Float#hashCode() - */ - public static int hashCode(float flt) { - return Float.floatToIntBits(flt); - } - - /** - * Return the same value as {@link Long#hashCode()}}. - * @see Long#hashCode() - */ - public static int hashCode(long lng) { - return (int) (lng ^ (lng >>> 32)); - } - - - //--------------------------------------------------------------------- - // Convenience methods for toString output - //--------------------------------------------------------------------- - - /** - * Return a String representation of an object's overall identity. - * @param obj the object (may be {@code null}) - * @return the object's identity as String representation, - * or an empty String if the object was {@code null} - */ - public static String identityToString(Object obj) { - if (obj == null) { - return EMPTY_STRING; - } - return obj.getClass().getName() + "@" + getIdentityHexString(obj); - } - - /** - * Return a hex String form of an object's identity hash code. - * @param obj the object - * @return the object's identity code in hex notation - */ - public static String getIdentityHexString(Object obj) { - return Integer.toHexString(System.identityHashCode(obj)); - } - - /** - * Return a content-based String representation if {@code obj} is - * not {@code null}; otherwise returns an empty String. - *

Differs from {@link #nullSafeToString(Object)} in that it returns - * an empty String rather than "null" for a {@code null} value. - * @param obj the object to build a display String for - * @return a display String representation of {@code obj} - * @see #nullSafeToString(Object) - */ - public static String getDisplayString(Object obj) { - if (obj == null) { - return EMPTY_STRING; - } - return nullSafeToString(obj); - } - - /** - * Determine the class name for the given object. - *

Returns {@code "null"} if {@code obj} is {@code null}. - * @param obj the object to introspect (may be {@code null}) - * @return the corresponding class name - */ - public static String nullSafeClassName(Object obj) { - return (obj != null ? obj.getClass().getName() : NULL_STRING); - } - - /** - * Return a String representation of the specified Object. - *

Builds a String representation of the contents in case of an array. - * Returns {@code "null"} if {@code obj} is {@code null}. - * @param obj the object to build a String representation for - * @return a String representation of {@code obj} - */ - public static String nullSafeToString(Object obj) { - if (obj == null) { - return NULL_STRING; - } - if (obj instanceof String) { - return (String) obj; - } - if (obj instanceof Object[]) { - return nullSafeToString((Object[]) obj); - } - if (obj instanceof boolean[]) { - return nullSafeToString((boolean[]) obj); - } - if (obj instanceof byte[]) { - return nullSafeToString((byte[]) obj); - } - if (obj instanceof char[]) { - return nullSafeToString((char[]) obj); - } - if (obj instanceof double[]) { - return nullSafeToString((double[]) obj); - } - if (obj instanceof float[]) { - return nullSafeToString((float[]) obj); - } - if (obj instanceof int[]) { - return nullSafeToString((int[]) obj); - } - if (obj instanceof long[]) { - return nullSafeToString((long[]) obj); - } - if (obj instanceof short[]) { - return nullSafeToString((short[]) obj); - } - String str = obj.toString(); - return (str != null ? str : EMPTY_STRING); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(Object[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - sb.append(String.valueOf(array[i])); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(boolean[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(byte[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(char[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - sb.append("'").append(array[i]).append("'"); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(double[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(float[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(int[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(long[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - - /** - * Return a String representation of the contents of the specified array. - *

The String representation consists of a list of the array's elements, - * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated - * by the characters {@code ", "} (a comma followed by a space). Returns - * {@code "null"} if {@code array} is {@code null}. - * @param array the array to build a String representation for - * @return a String representation of {@code array} - */ - public static String nullSafeToString(short[] array) { - if (array == null) { - return NULL_STRING; - } - int length = array.length; - if (length == 0) { - return EMPTY_ARRAY; - } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - if (i == 0) { - sb.append(ARRAY_START); - } - else { - sb.append(ARRAY_ELEMENT_SEPARATOR); - } - sb.append(array[i]); - } - sb.append(ARRAY_END); - return sb.toString(); - } - -} diff --git a/src/main/java/com/alibaba/excel/util/POITempFile.java b/src/main/java/com/alibaba/excel/util/POITempFile.java deleted file mode 100644 index 6382255f8..000000000 --- a/src/main/java/com/alibaba/excel/util/POITempFile.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.alibaba.excel.util; - -import java.io.File; - -/** - * - * @author jipengfei - */ -public class POITempFile { - - private static final String JAVA_IO_TMPDIR = "java.io.tmpdir"; - - private static final String POIFILES = "poifiles"; - - /** - */ - public static void createPOIFilesDirectory() { - - String tmpDir = System.getProperty(JAVA_IO_TMPDIR); - if (tmpDir == null) { - throw new RuntimeException( - "Systems temporary directory not defined - set the -D" + JAVA_IO_TMPDIR + " jvm property!"); - } - File directory = new File(tmpDir, POIFILES); - if (!directory.exists()) { - syncCreatePOIFilesDirectory(directory); - } - - } - - /** - * - * @param directory - */ - private static synchronized void syncCreatePOIFilesDirectory(File directory) { - if (!directory.exists()) { - directory.mkdirs(); - } - } -} diff --git a/src/main/java/com/alibaba/excel/util/PositionUtils.java b/src/main/java/com/alibaba/excel/util/PositionUtils.java deleted file mode 100644 index 37a87dd59..000000000 --- a/src/main/java/com/alibaba/excel/util/PositionUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alibaba.excel.util; - -/** - * @author jipengfei - */ -public class PositionUtils { - - public static int getRow(String currentCellIndex) { - int row = 0; - if (currentCellIndex != null) { - String rowStr = currentCellIndex.replaceAll("[A-Z]", "").replaceAll("[a-z]", ""); - row = Integer.parseInt(rowStr)-1; - } - return row; - } - - public static int getCol(String currentCellIndex) { - int col = 0; - if (currentCellIndex != null) { - - char[] currentIndex = currentCellIndex.replaceAll("[0-9]", "").toCharArray(); - for (int i = 0; i < currentIndex.length; i++) { - col += (currentIndex[i] - '@') * Math.pow(26, (currentIndex.length - i - 1)); - } - } - return col-1; - } -} diff --git a/src/main/java/com/alibaba/excel/util/RowUtil.java b/src/main/java/com/alibaba/excel/util/RowUtil.java deleted file mode 100644 index dc5ea013a..000000000 --- a/src/main/java/com/alibaba/excel/util/RowUtil.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alibaba.excel.util; - -import org.apache.poi.ss.usermodel.Workbook; - -public class RowUtil { - - //public static int computeNextRow(Workbook workbook,int startRow){ - // - //} -} diff --git a/src/main/java/com/alibaba/excel/util/StringUtils.java b/src/main/java/com/alibaba/excel/util/StringUtils.java deleted file mode 100644 index 83ddd18e8..000000000 --- a/src/main/java/com/alibaba/excel/util/StringUtils.java +++ /dev/null @@ -1,1221 +0,0 @@ -package com.alibaba.excel.util; - -/* - * Copyright 2002-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Properties; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.TimeZone; - -/** - * Miscellaneous {@link String} utility methods. - * - *

Mainly for internal use within the framework; consider - * Apache's Commons Lang - * for a more comprehensive suite of {@code String} utilities. - * - *

This class delivers some simple functionality that should really be - * provided by the core Java {@link String} and {@link StringBuilder} - * classes. It also provides easy-to-use methods to convert between - * delimited strings, such as CSV strings, and collections and arrays. - * - * @author Rod Johnson - * @author Juergen Hoeller - * @author Keith Donald - * @author Rob Harrop - * @author Rick Evans - * @author Arjen Poutsma - * @author Sam Brannen - * @author Brian Clozel - * @since 16 April 2001 - */ -public abstract class StringUtils { - - private static final String FOLDER_SEPARATOR = "/"; - - private static final String WINDOWS_FOLDER_SEPARATOR = "\\"; - - private static final String TOP_PATH = ".."; - - private static final String CURRENT_PATH = "."; - - private static final char EXTENSION_SEPARATOR = '.'; - - - //--------------------------------------------------------------------- - // General convenience methods for working with Strings - //--------------------------------------------------------------------- - - /** - * Check whether the given {@code String} is empty. - *

This method accepts any Object as an argument, comparing it to - * {@code null} and the empty String. As a consequence, this method - * will never return {@code true} for a non-null non-String object. - *

The Object signature is useful for general attribute handling code - * that commonly deals with Strings but generally has to iterate over - * Objects since attributes may e.g. be primitive value objects as well. - * @param str the candidate String - * @since 3.2.1 - */ - public static boolean isEmpty(Object str) { - return (str == null || "".equals(str)); - } - - /** - * Check that the given {@code CharSequence} is neither {@code null} nor - * of length 0. - *

Note: this method returns {@code true} for a {@code CharSequence} - * that purely consists of whitespace. - *

-     * StringUtils.hasLength(null) = false
-     * StringUtils.hasLength("") = false
-     * StringUtils.hasLength(" ") = true
-     * StringUtils.hasLength("Hello") = true
-     * 
- * @param str the {@code CharSequence} to check (may be {@code null}) - * @return {@code true} if the {@code CharSequence} is not {@code null} and has length - * @see #hasText(String) - */ - public static boolean hasLength(CharSequence str) { - return (str != null && str.length() > 0); - } - - /** - * Check that the given {@code String} is neither {@code null} nor of length 0. - *

Note: this method returns {@code true} for a {@code String} that - * purely consists of whitespace. - * @param str the {@code String} to check (may be {@code null}) - * @return {@code true} if the {@code String} is not {@code null} and has length - * @see #hasLength(CharSequence) - * @see #hasText(String) - */ - public static boolean hasLength(String str) { - return (str != null && !str.isEmpty()); - } - - /** - * Check whether the given {@code CharSequence} contains actual text. - *

More specifically, this method returns {@code true} if the - * {@code CharSequence} is not {@code null}, its length is greater than - * 0, and it contains at least one non-whitespace character. - *

-     * StringUtils.hasText(null) = false
-     * StringUtils.hasText("") = false
-     * StringUtils.hasText(" ") = false
-     * StringUtils.hasText("12345") = true
-     * StringUtils.hasText(" 12345 ") = true
-     * 
- * @param str the {@code CharSequence} to check (may be {@code null}) - * @return {@code true} if the {@code CharSequence} is not {@code null}, - * its length is greater than 0, and it does not contain whitespace only - * @see Character#isWhitespace - */ - public static boolean hasText(CharSequence str) { - return (hasLength(str) && containsText(str)); - } - - /** - * Check whether the given {@code String} contains actual text. - *

More specifically, this method returns {@code true} if the - * {@code String} is not {@code null}, its length is greater than 0, - * and it contains at least one non-whitespace character. - * @param str the {@code String} to check (may be {@code null}) - * @return {@code true} if the {@code String} is not {@code null}, its - * length is greater than 0, and it does not contain whitespace only - * @see #hasText(CharSequence) - */ - public static boolean hasText(String str) { - return (hasLength(str) && containsText(str)); - } - - private static boolean containsText(CharSequence str) { - int strLen = str.length(); - for (int i = 0; i < strLen; i++) { - if (!Character.isWhitespace(str.charAt(i))) { - return true; - } - } - return false; - } - - /** - * Check whether the given {@code CharSequence} contains any whitespace characters. - * @param str the {@code CharSequence} to check (may be {@code null}) - * @return {@code true} if the {@code CharSequence} is not empty and - * contains at least 1 whitespace character - * @see Character#isWhitespace - */ - public static boolean containsWhitespace(CharSequence str) { - if (!hasLength(str)) { - return false; - } - - int strLen = str.length(); - for (int i = 0; i < strLen; i++) { - if (Character.isWhitespace(str.charAt(i))) { - return true; - } - } - return false; - } - - /** - * Check whether the given {@code String} contains any whitespace characters. - * @param str the {@code String} to check (may be {@code null}) - * @return {@code true} if the {@code String} is not empty and - * contains at least 1 whitespace character - * @see #containsWhitespace(CharSequence) - */ - public static boolean containsWhitespace(String str) { - return containsWhitespace((CharSequence) str); - } - - /** - * Trim leading and trailing whitespace from the given {@code String}. - * @param str the {@code String} to check - * @return the trimmed {@code String} - * @see Character#isWhitespace - */ - public static String trimWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - - StringBuilder sb = new StringBuilder(str); - while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { - sb.deleteCharAt(0); - } - while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { - sb.deleteCharAt(sb.length() - 1); - } - return sb.toString(); - } - - /** - * Trim all whitespace from the given {@code String}: - * leading, trailing, and in between characters. - * @param str the {@code String} to check - * @return the trimmed {@code String} - * @see Character#isWhitespace - */ - public static String trimAllWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - - int len = str.length(); - StringBuilder sb = new StringBuilder(str.length()); - for (int i = 0; i < len; i++) { - char c = str.charAt(i); - if (!Character.isWhitespace(c)) { - sb.append(c); - } - } - return sb.toString(); - } - - /** - * Trim leading whitespace from the given {@code String}. - * @param str the {@code String} to check - * @return the trimmed {@code String} - * @see Character#isWhitespace - */ - public static String trimLeadingWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - - StringBuilder sb = new StringBuilder(str); - while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { - sb.deleteCharAt(0); - } - return sb.toString(); - } - - /** - * Trim trailing whitespace from the given {@code String}. - * @param str the {@code String} to check - * @return the trimmed {@code String} - * @see Character#isWhitespace - */ - public static String trimTrailingWhitespace(String str) { - if (!hasLength(str)) { - return str; - } - - StringBuilder sb = new StringBuilder(str); - while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { - sb.deleteCharAt(sb.length() - 1); - } - return sb.toString(); - } - - /** - * Trim all occurrences of the supplied leading character from the given {@code String}. - * @param str the {@code String} to check - * @param leadingCharacter the leading character to be trimmed - * @return the trimmed {@code String} - */ - public static String trimLeadingCharacter(String str, char leadingCharacter) { - if (!hasLength(str)) { - return str; - } - - StringBuilder sb = new StringBuilder(str); - while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) { - sb.deleteCharAt(0); - } - return sb.toString(); - } - - /** - * Trim all occurrences of the supplied trailing character from the given {@code String}. - * @param str the {@code String} to check - * @param trailingCharacter the trailing character to be trimmed - * @return the trimmed {@code String} - */ - public static String trimTrailingCharacter(String str, char trailingCharacter) { - if (!hasLength(str)) { - return str; - } - - StringBuilder sb = new StringBuilder(str); - while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) { - sb.deleteCharAt(sb.length() - 1); - } - return sb.toString(); - } - - /** - * Test if the given {@code String} starts with the specified prefix, - * ignoring upper/lower case. - * @param str the {@code String} to check - * @param prefix the prefix to look for - * @see String#startsWith - */ - public static boolean startsWithIgnoreCase(String str, String prefix) { - return (str != null && prefix != null && str.length() >= prefix.length() && - str.regionMatches(true, 0, prefix, 0, prefix.length())); - } - - /** - * Test if the given {@code String} ends with the specified suffix, - * ignoring upper/lower case. - * @param str the {@code String} to check - * @param suffix the suffix to look for - * @see String#endsWith - */ - public static boolean endsWithIgnoreCase(String str, String suffix) { - return (str != null && suffix != null && str.length() >= suffix.length() && - str.regionMatches(true, str.length() - suffix.length(), suffix, 0, suffix.length())); - } - - /** - * Test whether the given string matches the given substring - * at the given index. - * @param str the original string (or StringBuilder) - * @param index the index in the original string to start matching against - * @param substring the substring to match at the given index - */ - public static boolean substringMatch(CharSequence str, int index, CharSequence substring) { - if (index + substring.length() > str.length()) { - return false; - } - for (int i = 0; i < substring.length(); i++) { - if (str.charAt(index + i) != substring.charAt(i)) { - return false; - } - } - return true; - } - - /** - * Count the occurrences of the substring {@code sub} in string {@code str}. - * @param str string to search in - * @param sub string to search for - */ - public static int countOccurrencesOf(String str, String sub) { - if (!hasLength(str) || !hasLength(sub)) { - return 0; - } - - int count = 0; - int pos = 0; - int idx; - while ((idx = str.indexOf(sub, pos)) != -1) { - ++count; - pos = idx + sub.length(); - } - return count; - } - - /** - * Replace all occurrences of a substring within a string with another string. - * @param inString {@code String} to examine - * @param oldPattern {@code String} to replace - * @param newPattern {@code String} to insert - * @return a {@code String} with the replacements - */ - public static String replace(String inString, String oldPattern, String newPattern) { - if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { - return inString; - } - int index = inString.indexOf(oldPattern); - if (index == -1) { - // no occurrence -> can return input as-is - return inString; - } - - int capacity = inString.length(); - if (newPattern.length() > oldPattern.length()) { - capacity += 16; - } - StringBuilder sb = new StringBuilder(capacity); - - int pos = 0; // our position in the old string - int patLen = oldPattern.length(); - while (index >= 0) { - sb.append(inString.substring(pos, index)); - sb.append(newPattern); - pos = index + patLen; - index = inString.indexOf(oldPattern, pos); - } - - // append any characters to the right of a match - sb.append(inString.substring(pos)); - return sb.toString(); - } - - /** - * Delete all occurrences of the given substring. - * @param inString the original {@code String} - * @param pattern the pattern to delete all occurrences of - * @return the resulting {@code String} - */ - public static String delete(String inString, String pattern) { - return replace(inString, pattern, ""); - } - - /** - * Delete any character in a given {@code String}. - * @param inString the original {@code String} - * @param charsToDelete a set of characters to delete. - * E.g. "az\n" will delete 'a's, 'z's and new lines. - * @return the resulting {@code String} - */ - public static String deleteAny(String inString, String charsToDelete) { - if (!hasLength(inString) || !hasLength(charsToDelete)) { - return inString; - } - - StringBuilder sb = new StringBuilder(inString.length()); - for (int i = 0; i < inString.length(); i++) { - char c = inString.charAt(i); - if (charsToDelete.indexOf(c) == -1) { - sb.append(c); - } - } - return sb.toString(); - } - - - //--------------------------------------------------------------------- - // Convenience methods for working with formatted Strings - //--------------------------------------------------------------------- - - /** - * Quote the given {@code String} with single quotes. - * @param str the input {@code String} (e.g. "myString") - * @return the quoted {@code String} (e.g. "'myString'"), - * or {@code null} if the input was {@code null} - */ - public static String quote(String str) { - return (str != null ? "'" + str + "'" : null); - } - - /** - * Turn the given Object into a {@code String} with single quotes - * if it is a {@code String}; keeping the Object as-is else. - * @param obj the input Object (e.g. "myString") - * @return the quoted {@code String} (e.g. "'myString'"), - * or the input object as-is if not a {@code String} - */ - public static Object quoteIfString(Object obj) { - return (obj instanceof String ? quote((String) obj) : obj); - } - - /** - * Unqualify a string qualified by a '.' dot character. For example, - * "this.name.is.qualified", returns "qualified". - * @param qualifiedName the qualified name - */ - public static String unqualify(String qualifiedName) { - return unqualify(qualifiedName, '.'); - } - - /** - * Unqualify a string qualified by a separator character. For example, - * "this:name:is:qualified" returns "qualified" if using a ':' separator. - * @param qualifiedName the qualified name - * @param separator the separator - */ - public static String unqualify(String qualifiedName, char separator) { - return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1); - } - - /** - * Capitalize a {@code String}, changing the first letter to - * upper case as per {@link Character#toUpperCase(char)}. - * No other letters are changed. - * @param str the {@code String} to capitalize - * @return the capitalized {@code String} - */ - public static String capitalize(String str) { - return changeFirstCharacterCase(str, true); - } - - /** - * Uncapitalize a {@code String}, changing the first letter to - * lower case as per {@link Character#toLowerCase(char)}. - * No other letters are changed. - * @param str the {@code String} to uncapitalize - * @return the uncapitalized {@code String} - */ - public static String uncapitalize(String str) { - return changeFirstCharacterCase(str, false); - } - - private static String changeFirstCharacterCase(String str, boolean capitalize) { - if (!hasLength(str)) { - return str; - } - - char baseChar = str.charAt(0); - char updatedChar; - if (capitalize) { - updatedChar = Character.toUpperCase(baseChar); - } - else { - updatedChar = Character.toLowerCase(baseChar); - } - if (baseChar == updatedChar) { - return str; - } - - char[] chars = str.toCharArray(); - chars[0] = updatedChar; - return new String(chars, 0, chars.length); - } - - /** - * Extract the filename from the given Java resource path, - * e.g. {@code "mypath/myfile.txt" -> "myfile.txt"}. - * @param path the file path (may be {@code null}) - * @return the extracted filename, or {@code null} if none - */ - public static String getFilename(String path) { - if (path == null) { - return null; - } - - int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); - return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path); - } - - /** - * Extract the filename extension from the given Java resource path, - * - * @param path the file path (may be {@code null}) - * @return the extracted filename extension, or {@code null} if none - */ - public static String getFilenameExtension(String path) { - if (path == null) { - return null; - } - - int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); - if (extIndex == -1) { - return null; - } - - int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); - if (folderIndex > extIndex) { - return null; - } - - return path.substring(extIndex + 1); - } - - /** - * Strip the filename extension from the given Java resource path, - * - * @param path the file path - * @return the path with stripped filename extension - */ - public static String stripFilenameExtension(String path) { - if (path == null) { - return null; - } - - int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); - if (extIndex == -1) { - return path; - } - - int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); - if (folderIndex > extIndex) { - return path; - } - - return path.substring(0, extIndex); - } - - /** - * Apply the given relative path to the given Java resource path, - * assuming standard Java folder separation (i.e. "/" separators). - * @param path the path to start from (usually a full file path) - * @param relativePath the relative path to apply - * (relative to the full file path above) - * @return the full file path that results from applying the relative path - */ - public static String applyRelativePath(String path, String relativePath) { - int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); - if (separatorIndex != -1) { - String newPath = path.substring(0, separatorIndex); - if (!relativePath.startsWith(FOLDER_SEPARATOR)) { - newPath += FOLDER_SEPARATOR; - } - return newPath + relativePath; - } - else { - return relativePath; - } - } - - /** - * Normalize the path by suppressing sequences like "path/.." and - * inner simple dots. - *

The result is convenient for path comparison. For other uses, - * notice that Windows separators ("\") are replaced by simple slashes. - * @param path the original path - * @return the normalized path - */ - public static String cleanPath(String path) { - if (path == null) { - return null; - } - String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); - - // Strip prefix from path to analyze, to not treat it as part of the - // first path element. This is necessary to correctly parse paths like - // "file:core/../core/io/Resource.class", where the ".." should just - // strip the first "core" directory while keeping the "file:" prefix. - int prefixIndex = pathToUse.indexOf(":"); - String prefix = ""; - if (prefixIndex != -1) { - prefix = pathToUse.substring(0, prefixIndex + 1); - if (prefix.contains("/")) { - prefix = ""; - } - else { - pathToUse = pathToUse.substring(prefixIndex + 1); - } - } - if (pathToUse.startsWith(FOLDER_SEPARATOR)) { - prefix = prefix + FOLDER_SEPARATOR; - pathToUse = pathToUse.substring(1); - } - - String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); - List pathElements = new LinkedList(); - int tops = 0; - - for (int i = pathArray.length - 1; i >= 0; i--) { - String element = pathArray[i]; - if (CURRENT_PATH.equals(element)) { - // Points to current directory - drop it. - } - else if (TOP_PATH.equals(element)) { - // Registering top path found. - tops++; - } - else { - if (tops > 0) { - // Merging path element with element corresponding to top path. - tops--; - } - else { - // Normal path element found. - pathElements.add(0, element); - } - } - } - - // Remaining top paths need to be retained. - for (int i = 0; i < tops; i++) { - pathElements.add(0, TOP_PATH); - } - - return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); - } - - /** - * Compare two paths after normalization of them. - * @param path1 first path for comparison - * @param path2 second path for comparison - * @return whether the two paths are equivalent after normalization - */ - public static boolean pathEquals(String path1, String path2) { - return cleanPath(path1).equals(cleanPath(path2)); - } - - /** - * Parse the given {@code localeString} value into a {@link Locale}. - *

This is the inverse operation of {@link Locale#toString Locale's toString}. - * @param localeString the locale {@code String}, following {@code Locale's} - * {@code toString()} format ("en", "en_UK", etc); - * also accepts spaces as separators, as an alternative to underscores - * @return a corresponding {@code Locale} instance, or {@code null} if none - * @throws IllegalArgumentException in case of an invalid locale specification - */ - public static Locale parseLocaleString(String localeString) { - String[] parts = tokenizeToStringArray(localeString, "_ ", false, false); - String language = (parts.length > 0 ? parts[0] : ""); - String country = (parts.length > 1 ? parts[1] : ""); - - validateLocalePart(language); - validateLocalePart(country); - - String variant = ""; - if (parts.length > 2) { - // There is definitely a variant, and it is everything after the country - // code sans the separator between the country code and the variant. - int endIndexOfCountryCode = localeString.indexOf(country, language.length()) + country.length(); - // Strip off any leading '_' and whitespace, what's left is the variant. - variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode)); - if (variant.startsWith("_")) { - variant = trimLeadingCharacter(variant, '_'); - } - } - return (language.length() > 0 ? new Locale(language, country, variant) : null); - } - - private static void validateLocalePart(String localePart) { - for (int i = 0; i < localePart.length(); i++) { - char ch = localePart.charAt(i); - if (ch != ' ' && ch != '_' && ch != '#' && !Character.isLetterOrDigit(ch)) { - throw new IllegalArgumentException( - "Locale part \"" + localePart + "\" contains invalid characters"); - } - } - } - - /** - * Determine the RFC 3066 compliant language tag, - * as used for the HTTP "Accept-Language" header. - * @param locale the Locale to transform to a language tag - * @return the RFC 3066 compliant language tag as {@code String} - */ - public static String toLanguageTag(Locale locale) { - return locale.getLanguage() + (hasText(locale.getCountry()) ? "-" + locale.getCountry() : ""); - } - - /** - * Parse the given {@code timeZoneString} value into a {@link TimeZone}. - * @param timeZoneString the time zone {@code String}, following {@link TimeZone#getTimeZone(String)} - * but throwing {@link IllegalArgumentException} in case of an invalid time zone specification - * @return a corresponding {@link TimeZone} instance - * @throws IllegalArgumentException in case of an invalid time zone specification - */ - public static TimeZone parseTimeZoneString(String timeZoneString) { - TimeZone timeZone = TimeZone.getTimeZone(timeZoneString); - if ("GMT".equals(timeZone.getID()) && !timeZoneString.startsWith("GMT")) { - // We don't want that GMT fallback... - throw new IllegalArgumentException("Invalid time zone specification '" + timeZoneString + "'"); - } - return timeZone; - } - - - //--------------------------------------------------------------------- - // Convenience methods for working with String arrays - //--------------------------------------------------------------------- - - /** - * Append the given {@code String} to the given {@code String} array, - * returning a new array consisting of the input array contents plus - * the given {@code String}. - * @param array the array to append to (can be {@code null}) - * @param str the {@code String} to append - * @return the new array (never {@code null}) - */ - public static String[] addStringToArray(String[] array, String str) { - if (ObjectUtils.isEmpty(array)) { - return new String[] {str}; - } - - String[] newArr = new String[array.length + 1]; - System.arraycopy(array, 0, newArr, 0, array.length); - newArr[array.length] = str; - return newArr; - } - - /** - * Concatenate the given {@code String} arrays into one, - * with overlapping array elements included twice. - *

The order of elements in the original arrays is preserved. - * @param array1 the first array (can be {@code null}) - * @param array2 the second array (can be {@code null}) - * @return the new array ({@code null} if both given arrays were {@code null}) - */ - public static String[] concatenateStringArrays(String[] array1, String[] array2) { - if (ObjectUtils.isEmpty(array1)) { - return array2; - } - if (ObjectUtils.isEmpty(array2)) { - return array1; - } - - String[] newArr = new String[array1.length + array2.length]; - System.arraycopy(array1, 0, newArr, 0, array1.length); - System.arraycopy(array2, 0, newArr, array1.length, array2.length); - return newArr; - } - - /** - * Merge the given {@code String} arrays into one, with overlapping - * array elements only included once. - *

The order of elements in the original arrays is preserved - * (with the exception of overlapping elements, which are only - * included on their first occurrence). - * @param array1 the first array (can be {@code null}) - * @param array2 the second array (can be {@code null}) - * @return the new array ({@code null} if both given arrays were {@code null}) - */ - public static String[] mergeStringArrays(String[] array1, String[] array2) { - if (ObjectUtils.isEmpty(array1)) { - return array2; - } - if (ObjectUtils.isEmpty(array2)) { - return array1; - } - - List result = new ArrayList(); - result.addAll(Arrays.asList(array1)); - for (String str : array2) { - if (!result.contains(str)) { - result.add(str); - } - } - return toStringArray(result); - } - - /** - * Turn given source {@code String} array into sorted array. - * @param array the source array - * @return the sorted array (never {@code null}) - */ - public static String[] sortStringArray(String[] array) { - if (ObjectUtils.isEmpty(array)) { - return new String[0]; - } - - Arrays.sort(array); - return array; - } - - /** - * Copy the given {@code Collection} into a {@code String} array. - *

The {@code Collection} must contain {@code String} elements only. - * @param collection the {@code Collection} to copy - * @return the {@code String} array - */ - public static String[] toStringArray(Collection collection) { - if (collection == null) { - return null; - } - - return collection.toArray(new String[collection.size()]); - } - - /** - * Copy the given Enumeration into a {@code String} array. - * The Enumeration must contain {@code String} elements only. - * @param enumeration the Enumeration to copy - * @return the {@code String} array - */ - public static String[] toStringArray(Enumeration enumeration) { - if (enumeration == null) { - return null; - } - - List list = Collections.list(enumeration); - return list.toArray(new String[list.size()]); - } - - /** - * Trim the elements of the given {@code String} array, - * calling {@code String.trim()} on each of them. - * @param array the original {@code String} array - * @return the resulting array (of the same size) with trimmed elements - */ - public static String[] trimArrayElements(String[] array) { - if (ObjectUtils.isEmpty(array)) { - return new String[0]; - } - - String[] result = new String[array.length]; - for (int i = 0; i < array.length; i++) { - String element = array[i]; - result[i] = (element != null ? element.trim() : null); - } - return result; - } - - /** - * Remove duplicate strings from the given array. - *

As of 4.2, it preserves the original order, as it uses a {@link LinkedHashSet}. - * @param array the {@code String} array - * @return an array without duplicates, in natural sort order - */ - public static String[] removeDuplicateStrings(String[] array) { - if (ObjectUtils.isEmpty(array)) { - return array; - } - - Set set = new LinkedHashSet(); - for (String element : array) { - set.add(element); - } - return toStringArray(set); - } - - /** - * Split a {@code String} at the first occurrence of the delimiter. - * Does not include the delimiter in the result. - * @param toSplit the string to split - * @param delimiter to split the string up with - * @return a two element array with index 0 being before the delimiter, and - * index 1 being after the delimiter (neither element includes the delimiter); - * or {@code null} if the delimiter wasn't found in the given input {@code String} - */ - public static String[] split(String toSplit, String delimiter) { - if (!hasLength(toSplit) || !hasLength(delimiter)) { - return null; - } - int offset = toSplit.indexOf(delimiter); - if (offset < 0) { - return null; - } - - String beforeDelimiter = toSplit.substring(0, offset); - String afterDelimiter = toSplit.substring(offset + delimiter.length()); - return new String[] {beforeDelimiter, afterDelimiter}; - } - - /** - * Take an array of strings and split each element based on the given delimiter. - * A {@code Properties} instance is then generated, with the left of the - * delimiter providing the key, and the right of the delimiter providing the value. - *

Will trim both the key and value before adding them to the - * {@code Properties} instance. - * @param array the array to process - * @param delimiter to split each element using (typically the equals symbol) - * @return a {@code Properties} instance representing the array contents, - * or {@code null} if the array to process was {@code null} or empty - */ - public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) { - return splitArrayElementsIntoProperties(array, delimiter, null); - } - - /** - * Take an array of strings and split each element based on the given delimiter. - * A {@code Properties} instance is then generated, with the left of the - * delimiter providing the key, and the right of the delimiter providing the value. - *

Will trim both the key and value before adding them to the - * {@code Properties} instance. - * @param array the array to process - * @param delimiter to split each element using (typically the equals symbol) - * @param charsToDelete one or more characters to remove from each element - * prior to attempting the split operation (typically the quotation mark - * symbol), or {@code null} if no removal should occur - * @return a {@code Properties} instance representing the array contents, - * or {@code null} if the array to process was {@code null} or empty - */ - public static Properties splitArrayElementsIntoProperties( - String[] array, String delimiter, String charsToDelete) { - - if (ObjectUtils.isEmpty(array)) { - return null; - } - - Properties result = new Properties(); - for (String element : array) { - if (charsToDelete != null) { - element = deleteAny(element, charsToDelete); - } - String[] splittedElement = split(element, delimiter); - if (splittedElement == null) { - continue; - } - result.setProperty(splittedElement[0].trim(), splittedElement[1].trim()); - } - return result; - } - - /** - * Tokenize the given {@code String} into a {@code String} array via a - * {@link StringTokenizer}. - *

Trims tokens and omits empty tokens. - *

The given {@code delimiters} string can consist of any number of - * delimiter characters. Each of those characters can be used to separate - * tokens. A delimiter is always a single character; for multi-character - * delimiters, consider using {@link #delimitedListToStringArray}. - * @param str the {@code String} to tokenize - * @param delimiters the delimiter characters, assembled as a {@code String} - * (each of the characters is individually considered as a delimiter) - * @return an array of the tokens - * @see StringTokenizer - * @see String#trim() - * @see #delimitedListToStringArray - */ - public static String[] tokenizeToStringArray(String str, String delimiters) { - return tokenizeToStringArray(str, delimiters, true, true); - } - - /** - * Tokenize the given {@code String} into a {@code String} array via a - * {@link StringTokenizer}. - *

The given {@code delimiters} string can consist of any number of - * delimiter characters. Each of those characters can be used to separate - * tokens. A delimiter is always a single character; for multi-character - * delimiters, consider using {@link #delimitedListToStringArray}. - * @param str the {@code String} to tokenize - * @param delimiters the delimiter characters, assembled as a {@code String} - * (each of the characters is individually considered as a delimiter) - * @param trimTokens trim the tokens via {@link String#trim()} - * @param ignoreEmptyTokens omit empty tokens from the result array - * (only applies to tokens that are empty after trimming; StringTokenizer - * will not consider subsequent delimiters as token in the first place). - * @return an array of the tokens - * @see StringTokenizer - * @see String#trim() - * @see #delimitedListToStringArray - */ - public static String[] tokenizeToStringArray( - String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { - - if (str == null) { - return null; - } - - StringTokenizer st = new StringTokenizer(str, delimiters); - List tokens = new ArrayList(); - while (st.hasMoreTokens()) { - String token = st.nextToken(); - if (trimTokens) { - token = token.trim(); - } - if (!ignoreEmptyTokens || token.length() > 0) { - tokens.add(token); - } - } - return toStringArray(tokens); - } - - /** - * Take a {@code String} that is a delimited list and convert it into a - * {@code String} array. - *

A single {@code delimiter} may consist of more than one character, - * but it will still be considered as a single delimiter string, rather - * than as bunch of potential delimiter characters, in contrast to - * {@link #tokenizeToStringArray}. - * @param str the input {@code String} - * @param delimiter the delimiter between elements (this is a single delimiter, - * rather than a bunch individual delimiter characters) - * @return an array of the tokens in the list - * @see #tokenizeToStringArray - */ - public static String[] delimitedListToStringArray(String str, String delimiter) { - return delimitedListToStringArray(str, delimiter, null); - } - - /** - * Take a {@code String} that is a delimited list and convert it into - * a {@code String} array. - *

A single {@code delimiter} may consist of more than one character, - * but it will still be considered as a single delimiter string, rather - * than as bunch of potential delimiter characters, in contrast to - * {@link #tokenizeToStringArray}. - * @param str the input {@code String} - * @param delimiter the delimiter between elements (this is a single delimiter, - * rather than a bunch individual delimiter characters) - * @param charsToDelete a set of characters to delete; useful for deleting unwanted - * line breaks: e.g. "\r\n\f" will delete all new lines and line feeds in a {@code String} - * @return an array of the tokens in the list - * @see #tokenizeToStringArray - */ - public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { - if (str == null) { - return new String[0]; - } - if (delimiter == null) { - return new String[] {str}; - } - - List result = new ArrayList(); - if ("".equals(delimiter)) { - for (int i = 0; i < str.length(); i++) { - result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); - } - } - else { - int pos = 0; - int delPos; - while ((delPos = str.indexOf(delimiter, pos)) != -1) { - result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); - pos = delPos + delimiter.length(); - } - if (str.length() > 0 && pos <= str.length()) { - // Add rest of String, but not in case of empty input. - result.add(deleteAny(str.substring(pos), charsToDelete)); - } - } - return toStringArray(result); - } - - /** - * Convert a comma delimited list (e.g., a row from a CSV file) into an - * array of strings. - * @param str the input {@code String} - * @return an array of strings, or the empty array in case of empty input - */ - public static String[] commaDelimitedListToStringArray(String str) { - return delimitedListToStringArray(str, ","); - } - - /** - * Convert a comma delimited list (e.g., a row from a CSV file) into a set. - *

Note that this will suppress duplicates, and as of 4.2, the elements in - * the returned set will preserve the original order in a {@link LinkedHashSet}. - * @param str the input {@code String} - * @return a set of {@code String} entries in the list - * @see #removeDuplicateStrings(String[]) - */ - public static Set commaDelimitedListToSet(String str) { - Set set = new LinkedHashSet(); - String[] tokens = commaDelimitedListToStringArray(str); - for (String token : tokens) { - set.add(token); - } - return set; - } - - /** - * Convert a {@link Collection} to a delimited {@code String} (e.g. CSV). - *

Useful for {@code toString()} implementations. - * @param coll the {@code Collection} to convert - * @param delim the delimiter to use (typically a ",") - * @param prefix the {@code String} to start each element with - * @param suffix the {@code String} to end each element with - * @return the delimited {@code String} - */ - public static String collectionToDelimitedString(Collection coll, String delim, String prefix, String suffix) { - if (CollectionUtils.isEmpty(coll)) { - return ""; - } - - StringBuilder sb = new StringBuilder(); - Iterator it = coll.iterator(); - while (it.hasNext()) { - sb.append(prefix).append(it.next()).append(suffix); - if (it.hasNext()) { - sb.append(delim); - } - } - return sb.toString(); - } - - /** - * Convert a {@code Collection} into a delimited {@code String} (e.g. CSV). - *

Useful for {@code toString()} implementations. - * @param coll the {@code Collection} to convert - * @param delim the delimiter to use (typically a ",") - * @return the delimited {@code String} - */ - public static String collectionToDelimitedString(Collection coll, String delim) { - return collectionToDelimitedString(coll, delim, "", ""); - } - - /** - * Convert a {@code Collection} into a delimited {@code String} (e.g., CSV). - *

Useful for {@code toString()} implementations. - * @param coll the {@code Collection} to convert - * @return the delimited {@code String} - */ - public static String collectionToCommaDelimitedString(Collection coll) { - return collectionToDelimitedString(coll, ","); - } - - /** - * Convert a {@code String} array into a delimited {@code String} (e.g. CSV). - *

Useful for {@code toString()} implementations. - * @param arr the array to display - * @param delim the delimiter to use (typically a ",") - * @return the delimited {@code String} - */ - public static String arrayToDelimitedString(Object[] arr, String delim) { - if (ObjectUtils.isEmpty(arr)) { - return ""; - } - if (arr.length == 1) { - return ObjectUtils.nullSafeToString(arr[0]); - } - - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < arr.length; i++) { - if (i > 0) { - sb.append(delim); - } - sb.append(arr[i]); - } - return sb.toString(); - } - - /** - * Convert a {@code String} array into a comma delimited {@code String} - * (i.e., CSV). - *

Useful for {@code toString()} implementations. - * @param arr the array to display - * @return the delimited {@code String} - */ - public static String arrayToCommaDelimitedString(Object[] arr) { - return arrayToDelimitedString(arr, ","); - } - -} diff --git a/src/main/java/com/alibaba/excel/util/StyleUtil.java b/src/main/java/com/alibaba/excel/util/StyleUtil.java deleted file mode 100644 index cfb9c12b7..000000000 --- a/src/main/java/com/alibaba/excel/util/StyleUtil.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.alibaba.excel.util; - -import org.apache.poi.ss.usermodel.*; - -import java.util.Map; - -/** - * @author jipengfei - */ -public class StyleUtil { - - /** - * - * @param workbook - * @return - */ - public static CellStyle buildDefaultCellStyle(Workbook workbook) { - CellStyle newCellStyle = workbook.createCellStyle(); - Font font = workbook.createFont(); - font.setFontName("宋体"); - font.setFontHeightInPoints((short)14); - font.setBold(true); - newCellStyle.setFont(font); - newCellStyle.setWrapText(true); - newCellStyle.setVerticalAlignment(VerticalAlignment.CENTER); - newCellStyle.setAlignment(HorizontalAlignment.CENTER); - newCellStyle.setLocked(true); - newCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); - newCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); - newCellStyle.setBorderBottom(BorderStyle.THIN); - newCellStyle.setBorderLeft(BorderStyle.THIN); - return newCellStyle; - } - - /** - * - * @param workbook - * @param f - * @param indexedColors - * @return - */ - public static CellStyle buildCellStyle(Workbook workbook, com.alibaba.excel.metadata.Font f, - IndexedColors indexedColors) { - CellStyle cellStyle = buildDefaultCellStyle(workbook); - if (f != null) { - Font font = workbook.createFont(); - font.setFontName(f.getFontName()); - font.setFontHeightInPoints(f.getFontHeightInPoints()); - font.setBold(f.isBold()); - cellStyle.setFont(font); - } - if (indexedColors != null) { - cellStyle.setFillForegroundColor(indexedColors.getIndex()); - } - return cellStyle; - } - - public static Sheet buildSheetStyle(Sheet currentSheet, Map sheetWidthMap){ - currentSheet.setDefaultColumnWidth(20); - for (Map.Entry entry : sheetWidthMap.entrySet()) { - currentSheet.setColumnWidth(entry.getKey(), entry.getValue()); - } - return currentSheet; - } - - -} diff --git a/src/main/java/com/alibaba/excel/util/TypeUtil.java b/src/main/java/com/alibaba/excel/util/TypeUtil.java deleted file mode 100644 index b79d623da..000000000 --- a/src/main/java/com/alibaba/excel/util/TypeUtil.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.alibaba.excel.util; - -import com.alibaba.excel.metadata.ExcelColumnProperty; -import com.alibaba.excel.metadata.ExcelHeadProperty; -import net.sf.cglib.beans.BeanMap; -import org.apache.poi.hssf.usermodel.HSSFDateUtil; - -import java.lang.reflect.Field; -import java.math.BigDecimal; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * @author jipengfei - */ -public class TypeUtil { - - private static List DATE_FORMAT_LIST = new ArrayList(4); - - static { - DATE_FORMAT_LIST.add("yyyy/MM/dd HH:mm:ss"); - DATE_FORMAT_LIST.add("yyyy-MM-dd HH:mm:ss"); - DATE_FORMAT_LIST.add("yyyyMMdd HH:mm:ss"); - } - - private static int getCountOfChar(String value, char c) { - int n = 0; - if (value == null) { - return 0; - } - char[] chars = value.toCharArray(); - for (char cc : chars) { - if (cc == c) { - n++; - } - } - return n; - } - - public static Object convert(String value, Field field, String format, boolean us) { - if (!StringUtils.isEmpty(value)) { - if (Float.class.equals(field.getType())) { - return Float.parseFloat(value); - } - if (Integer.class.equals(field.getType()) || int.class.equals(field.getType())) { - return Integer.parseInt(value); - } - if (Double.class.equals(field.getType()) || double.class.equals(field.getType())) { - if (null != format && !"".equals(format)) { - int n = getCountOfChar(value, '0'); - return Double.parseDouble(TypeUtil.formatFloat0(value, n)); - } else { - return Double.parseDouble(TypeUtil.formatFloat(value)); - } - } - if (Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())) { - String valueLower = value.toLowerCase(); - if (valueLower.equals("true") || valueLower.equals("false")) { - return Boolean.parseBoolean(value.toLowerCase()); - } - Integer integer = Integer.parseInt(value); - if (integer == 0) { - return false; - } else { - return true; - } - } - if (Long.class.equals(field.getType()) || long.class.equals(field.getType())) { - return Long.parseLong(value); - } - if (Date.class.equals(field.getType())) { - if (value.contains("-") || value.contains("/") || value.contains(":")) { - return getSimpleDateFormatDate(value, format); - } else { - Double d = Double.parseDouble(value); - return HSSFDateUtil.getJavaDate(d, us); - } - } - if (BigDecimal.class.equals(field.getType())) { - return new BigDecimal(value); - } - if(String.class.equals(field.getType())){ - return formatFloat(value); - } - - } - return null; - } - - public static Boolean isNum(Field field) { - if (field == null) { - return false; - } - if (Integer.class.equals(field.getType()) || int.class.equals(field.getType())) { - return true; - } - if (Double.class.equals(field.getType()) || double.class.equals(field.getType())) { - return true; - } - - if (Long.class.equals(field.getType()) || long.class.equals(field.getType())) { - return true; - } - - if (BigDecimal.class.equals(field.getType())) { - return true; - } - return false; - } - - public static Boolean isNum(Object cellValue) { - if (cellValue instanceof Integer - || cellValue instanceof Double - || cellValue instanceof Short - || cellValue instanceof Long - || cellValue instanceof Float - || cellValue instanceof BigDecimal) { - return true; - } - return false; - } - - public static String getDefaultDateString(Date date) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - return simpleDateFormat.format(date); - - } - - public static Date getSimpleDateFormatDate(String value, String format) { - if (!StringUtils.isEmpty(value)) { - Date date = null; - if (!StringUtils.isEmpty(format)) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); - try { - date = simpleDateFormat.parse(value); - return date; - } catch (ParseException e) { - } - } - for (String dateFormat : DATE_FORMAT_LIST) { - try { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); - date = simpleDateFormat.parse(value); - } catch (ParseException e) { - } - if (date != null) { - break; - } - } - - return date; - - } - return null; - - } - - - public static String formatFloat(String value) { - if (null != value && value.contains(".")) { - if (isNumeric(value)) { - try { - BigDecimal decimal = new BigDecimal(value); - BigDecimal setScale = decimal.setScale(10, BigDecimal.ROUND_HALF_DOWN).stripTrailingZeros(); - return setScale.toPlainString(); - } catch (Exception e) { - } - } - } - return value; - } - - public static String formatFloat0(String value, int n) { - if (null != value && value.contains(".")) { - if (isNumeric(value)) { - try { - BigDecimal decimal = new BigDecimal(value); - BigDecimal setScale = decimal.setScale(n, BigDecimal.ROUND_HALF_DOWN); - return setScale.toPlainString(); - } catch (Exception e) { - } - } - } - return value; - } - - public static final Pattern pattern = Pattern.compile("[\\+\\-]?[\\d]+([\\.][\\d]*)?([Ee][+-]?[\\d]+)?$"); - - private static boolean isNumeric(String str) { - Matcher isNum = pattern.matcher(str); - if (!isNum.matches()) { - return false; - } - return true; - } - - public static String formatDate(Date cellValue, String format) { - SimpleDateFormat simpleDateFormat; - if(!StringUtils.isEmpty(format)) { - simpleDateFormat = new SimpleDateFormat(format); - }else { - simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - } - return simpleDateFormat.format(cellValue); - } - - public static String getFieldStringValue(BeanMap beanMap, String fieldName, String format) { - String cellValue = null; - Object value = beanMap.get(fieldName); - if (value != null) { - if (value instanceof Date) { - cellValue = TypeUtil.formatDate((Date)value, format); - } else { - cellValue = value.toString(); - } - } - return cellValue; - } - - public static Map getFieldValues(List stringList, ExcelHeadProperty excelHeadProperty, Boolean use1904WindowDate) { - Map map = new HashMap(); - for (int i = 0; i < stringList.size(); i++) { - ExcelColumnProperty columnProperty = excelHeadProperty.getExcelColumnProperty(i); - if (columnProperty != null) { - Object value = TypeUtil.convert(stringList.get(i), columnProperty.getField(), - columnProperty.getFormat(), use1904WindowDate); - if (value != null) { - map.put(columnProperty.getField().getName(),value); - } - } - } - return map; - } -} diff --git a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java b/src/main/java/com/alibaba/excel/util/WorkBookUtil.java deleted file mode 100644 index 55369292d..000000000 --- a/src/main/java/com/alibaba/excel/util/WorkBookUtil.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.alibaba.excel.util; - -import com.alibaba.excel.support.ExcelTypeEnum; -import org.apache.poi.hssf.usermodel.HSSFWorkbook; -import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.apache.poi.ss.usermodel.*; -import org.apache.poi.xssf.streaming.SXSSFWorkbook; -import org.apache.poi.xssf.usermodel.XSSFWorkbook; - -import java.io.IOException; -import java.io.InputStream; - -import static com.alibaba.excel.util.StyleUtil.buildSheetStyle; - -/** - * - * @author jipengfei - */ -public class WorkBookUtil { - - public static Workbook createWorkBook(InputStream templateInputStream, ExcelTypeEnum excelType) throws IOException { - Workbook workbook; - if (ExcelTypeEnum.XLS.equals(excelType)) { - workbook = (templateInputStream == null) ? new HSSFWorkbook() : new HSSFWorkbook( - new POIFSFileSystem(templateInputStream)); - } else { - workbook = (templateInputStream == null) ? new SXSSFWorkbook(500) : new SXSSFWorkbook( - new XSSFWorkbook(templateInputStream)); - } - return workbook; - } - - public static Sheet createOrGetSheet(Workbook workbook, com.alibaba.excel.metadata.Sheet sheet) { - Sheet sheet1 = null; - try { - try { - sheet1 = workbook.getSheetAt(sheet.getSheetNo()-1); - } catch (Exception e) { - } - if (null == sheet1) { - sheet1 = createSheet(workbook, sheet); - buildSheetStyle(sheet1,sheet.getColumnWidthMap()); - } - } catch (Exception e) { - throw new RuntimeException("constructCurrentSheet error", e); - } - return sheet1; - } - - public static Sheet createSheet(Workbook workbook, com.alibaba.excel.metadata.Sheet sheet) { - return workbook.createSheet(sheet.getSheetName() != null ? sheet.getSheetName() : sheet.getSheetNo() + ""); - } - - public static Row createRow(Sheet sheet, int rowNum) { - return sheet.createRow(rowNum); - } - - public static Cell createCell(Row row, int colNum, CellStyle cellStyle, String cellValue) { - return createCell(row, colNum, cellStyle, cellValue, false); - } - - public static Cell createCell(Row row, int colNum, CellStyle cellStyle, Object cellValue, Boolean isNum) { - Cell cell = row.createCell(colNum); - cell.setCellStyle(cellStyle); - if (null != cellValue) { - if (isNum) { - cell.setCellValue(Double.parseDouble(cellValue.toString())); - } else { - cell.setCellValue(cellValue.toString()); - } - } - return cell; - } - -} diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java b/src/main/java/com/alibaba/excel/write/ExcelBuilder.java deleted file mode 100644 index c0160181b..000000000 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilder.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.alibaba.excel.write; - -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.metadata.Table; - -import java.util.List; - -/** - * @author jipengfei - */ -public interface ExcelBuilder { - - /** - * workBook increase data - * - * @param data java basic type or java model extend BaseModel - * @param startRow Start row number - */ - void addContent(List data, int startRow); - - /** - * WorkBook increase data - * - * @param data java basic type or java model extend BaseModel - * @param sheetParam Write the sheet - */ - void addContent(List data, Sheet sheetParam); - - /** - * WorkBook increase data - * - * @param data java basic type or java model extend BaseModel - * @param sheetParam Write the sheet - * @param table Write the table - */ - void addContent(List data, Sheet sheetParam, Table table); - - /** - * Creates new cell range. Indexes are zero-based. - * - * @param firstRow Index of first row - * @param lastRow Index of last row (inclusive), must be equal to or larger than {@code firstRow} - * @param firstCol Index of first column - * @param lastCol Index of last column (inclusive), must be equal to or larger than {@code firstCol} - */ - void merge(int firstRow, int lastRow, int firstCol, int lastCol); - - /** - * Close io - */ - void finish(); -} diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java deleted file mode 100644 index 02b37cb83..000000000 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.alibaba.excel.write; - -import com.alibaba.excel.context.WriteContext; -import com.alibaba.excel.event.WriteHandler; -import com.alibaba.excel.exception.ExcelGenerateException; -import com.alibaba.excel.metadata.BaseRowModel; -import com.alibaba.excel.metadata.ExcelColumnProperty; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.metadata.Table; -import com.alibaba.excel.support.ExcelTypeEnum; -import com.alibaba.excel.util.CollectionUtils; -import com.alibaba.excel.util.POITempFile; -import com.alibaba.excel.util.TypeUtil; -import com.alibaba.excel.util.WorkBookUtil; -import net.sf.cglib.beans.BeanMap; -import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.util.CellRangeAddress; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.List; - -/** - * @author jipengfei - */ -public class ExcelBuilderImpl implements ExcelBuilder { - - private WriteContext context; - - public ExcelBuilderImpl(InputStream templateInputStream, - OutputStream out, - ExcelTypeEnum excelType, - boolean needHead, WriteHandler writeHandler) { - try { - //初始化时候创建临时缓存目录,用于规避POI在并发写bug - POITempFile.createPOIFilesDirectory(); - context = new WriteContext(templateInputStream, out, excelType, needHead, writeHandler); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void addContent(List data, int startRow) { - if (CollectionUtils.isEmpty(data)) { - return; - } - int rowNum = context.getCurrentSheet().getLastRowNum(); - if (rowNum == 0) { - Row row = context.getCurrentSheet().getRow(0); - if (row == null) { - if (context.getExcelHeadProperty() == null || !context.needHead()) { - rowNum = -1; - } - } - } - if (rowNum < startRow) { - rowNum = startRow; - } - for (int i = 0; i < data.size(); i++) { - int n = i + rowNum + 1; - addOneRowOfDataToExcel(data.get(i), n); - } - } - - @Override - public void addContent(List data, Sheet sheetParam) { - context.currentSheet(sheetParam); - addContent(data, sheetParam.getStartRow()); - } - - @Override - public void addContent(List data, Sheet sheetParam, Table table) { - context.currentSheet(sheetParam); - context.currentTable(table); - addContent(data, sheetParam.getStartRow()); - } - - @Override - public void merge(int firstRow, int lastRow, int firstCol, int lastCol) { - CellRangeAddress cra = new CellRangeAddress(firstRow, lastRow, firstCol, lastCol); - context.getCurrentSheet().addMergedRegion(cra); - } - - @Override - public void finish() { - try { - context.getWorkbook().write(context.getOutputStream()); - context.getWorkbook().close(); - } catch (IOException e) { - throw new ExcelGenerateException("IO error", e); - } - } - - private void addBasicTypeToExcel(List oneRowData, Row row) { - if (CollectionUtils.isEmpty(oneRowData)) { - return; - } - for (int i = 0; i < oneRowData.size(); i++) { - Object cellValue = oneRowData.get(i); - Cell cell = WorkBookUtil.createCell(row, i, context.getCurrentContentStyle(), cellValue, - TypeUtil.isNum(cellValue)); - if (null != context.getAfterWriteHandler()) { - context.getAfterWriteHandler().cell(i, cell); - } - } - } - - private void addJavaObjectToExcel(Object oneRowData, Row row) { - int i = 0; - BeanMap beanMap = BeanMap.create(oneRowData); - for (ExcelColumnProperty excelHeadProperty : context.getExcelHeadProperty().getColumnPropertyList()) { - BaseRowModel baseRowModel = (BaseRowModel)oneRowData; - String cellValue = TypeUtil.getFieldStringValue(beanMap, excelHeadProperty.getField().getName(), - excelHeadProperty.getFormat()); - CellStyle cellStyle = baseRowModel.getStyle(i) != null ? baseRowModel.getStyle(i) - : context.getCurrentContentStyle(); - Cell cell = WorkBookUtil.createCell(row, i, cellStyle, cellValue, - TypeUtil.isNum(excelHeadProperty.getField())); - if (null != context.getAfterWriteHandler()) { - context.getAfterWriteHandler().cell(i, cell); - } - i++; - } - - } - - private void addOneRowOfDataToExcel(Object oneRowData, int n) { - Row row = WorkBookUtil.createRow(context.getCurrentSheet(), n); - if (null != context.getAfterWriteHandler()) { - context.getAfterWriteHandler().row(n, row); - } - if (oneRowData instanceof List) { - addBasicTypeToExcel((List)oneRowData, row); - } else { - addJavaObjectToExcel(oneRowData, row); - } - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/ReadTest.java b/src/test/java/com/alibaba/easyexcel/test/ReadTest.java deleted file mode 100644 index f92fedeb0..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/ReadTest.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.alibaba.easyexcel.test; - -import com.alibaba.easyexcel.test.listen.ExcelListener; -import com.alibaba.easyexcel.test.model.ReadModel; -import com.alibaba.easyexcel.test.model.ReadModel2; -import com.alibaba.easyexcel.test.util.FileUtil; -import com.alibaba.excel.EasyExcelFactory; -import com.alibaba.excel.ExcelReader; -import com.alibaba.excel.metadata.Sheet; -import org.junit.Test; - -import java.io.IOException; -import java.io.InputStream; -import java.util.List; - -public class ReadTest { - - - /** - * 07版本excel读数据量少于1千行数据,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void simpleReadListStringV2007() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2007.xlsx"); - List data = EasyExcelFactory.read(inputStream, new Sheet(1, 0)); - inputStream.close(); - print(data); - } - - - /** - * 07版本excel读数据量少于1千行数据自动转成javamodel,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void simpleReadJavaModelV2007() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2007.xlsx"); - List data = EasyExcelFactory.read(inputStream, new Sheet(2, 1, ReadModel.class)); - inputStream.close(); - print(data); - } - - /** - * 07版本excel读数据量大于1千行,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void saxReadListStringV2007() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2007.xlsx"); - ExcelListener excelListener = new ExcelListener(); - EasyExcelFactory.readBySax(inputStream, new Sheet(1, 1), excelListener); - inputStream.close(); - - } - /** - * 07版本excel读数据量大于1千行,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void saxReadJavaModelV2007() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2007.xlsx"); - ExcelListener excelListener = new ExcelListener(); - EasyExcelFactory.readBySax(inputStream, new Sheet(2, 1, ReadModel.class), excelListener); - inputStream.close(); - } - - /** - * 07版本excel读取sheet - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void saxReadSheetsV2007() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2007.xlsx"); - ExcelListener excelListener = new ExcelListener(); - ExcelReader excelReader = EasyExcelFactory.getReader(inputStream,excelListener); - List sheets = excelReader.getSheets(); - System.out.println("llll****"+sheets); - System.out.println(); - for (Sheet sheet:sheets) { - if(sheet.getSheetNo() ==1) { - excelReader.read(sheet); - }else if(sheet.getSheetNo() ==2){ - sheet.setHeadLineMun(1); - sheet.setClazz(ReadModel.class); - excelReader.read(sheet); - }else if(sheet.getSheetNo() ==3){ - sheet.setHeadLineMun(1); - sheet.setClazz(ReadModel2.class); - excelReader.read(sheet); - } - } - inputStream.close(); - } - - - - /** - * 03版本excel读数据量少于1千行数据,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void simpleReadListStringV2003() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2003.xls"); - List data = EasyExcelFactory.read(inputStream, new Sheet(1, 0)); - inputStream.close(); - print(data); - } - - /** - * 03版本excel读数据量少于1千行数据转成javamodel,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void simpleReadJavaModelV2003() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2003.xls"); - List data = EasyExcelFactory.read(inputStream, new Sheet(2, 1, ReadModel.class)); - inputStream.close(); - print(data); - } - - /** - * 03版本excel读数据量大于1千行数据,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void saxReadListStringV2003() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2003.xls"); - ExcelListener excelListener = new ExcelListener(); - EasyExcelFactory.readBySax(inputStream, new Sheet(2, 1), excelListener); - inputStream.close(); - } - - /** - * 03版本excel读数据量大于1千行数据转成javamodel,内部采用回调方法. - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void saxReadJavaModelV2003() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2003.xls"); - ExcelListener excelListener = new ExcelListener(); - EasyExcelFactory.readBySax(inputStream, new Sheet(2, 1, ReadModel.class), excelListener); - inputStream.close(); - } - - /** - * 00版本excel读取sheet - * - * @throws IOException 简单抛出异常,真实环境需要catch异常,同时在finally中关闭流 - */ - @Test - public void saxReadSheetsV2003() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("2003.xls"); - ExcelListener excelListener = new ExcelListener(); - ExcelReader excelReader = EasyExcelFactory.getReader(inputStream,excelListener); - List sheets = excelReader.getSheets(); - System.out.println(); - for (Sheet sheet:sheets) { - if(sheet.getSheetNo() == 1) { - excelReader.read(sheet); - }else { - sheet.setHeadLineMun(2); - sheet.setClazz(ReadModel.class); - excelReader.read(sheet); - } - } - inputStream.close(); - } - - - public void print(List datas){ - int i=0; - for (Object ob:datas) { - System.out.println(i++); - System.out.println(ob); - } - } - -} diff --git a/src/test/java/com/alibaba/easyexcel/test/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/WriteTest.java deleted file mode 100644 index 6b481b630..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/WriteTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.alibaba.easyexcel.test; - -import com.alibaba.easyexcel.test.listen.AfterWriteHandlerImpl; -import com.alibaba.easyexcel.test.model.WriteModel; -import com.alibaba.easyexcel.test.util.FileUtil; -import com.alibaba.excel.EasyExcelFactory; -import com.alibaba.excel.ExcelWriter; -import com.alibaba.excel.metadata.Sheet; -import com.alibaba.excel.metadata.Table; -import com.alibaba.excel.support.ExcelTypeEnum; -import org.junit.Test; - -import java.io.*; -import java.util.HashMap; -import java.util.Map; - -import static com.alibaba.easyexcel.test.util.DataUtil.*; - -public class WriteTest { - - @Test - public void writeV2007() throws IOException { - OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); - ExcelWriter writer = EasyExcelFactory.getWriter(out); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 3); - sheet1.setSheetName("第一个sheet"); - - //设置列宽 设置每列的宽度 - Map columnWidth = new HashMap(); - columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); - sheet1.setColumnWidthMap(columnWidth); - sheet1.setHead(createTestListStringHead()); - //or 设置自适应宽度 - //sheet1.setAutoWidth(Boolean.TRUE); - writer.write1(createTestListObject(), sheet1); - - //写第二个sheet sheet2 模型上打有表头的注解,合并单元格 - Sheet sheet2 = new Sheet(2, 3, WriteModel.class, "第二个sheet", null); - sheet2.setTableStyle(createTableStyle()); - //writer.write1(null, sheet2); - writer.write(createTestListJavaMode(), sheet2); - //需要合并单元格 - writer.merge(5,20,1,1); - - //写第三个sheet包含多个table情况 - Sheet sheet3 = new Sheet(3, 0); - sheet3.setSheetName("第三个sheet"); - Table table1 = new Table(1); - table1.setHead(createTestListStringHead()); - writer.write1(createTestListObject(), sheet3, table1); - - //写sheet2 模型上打有表头的注解 - Table table2 = new Table(2); - table2.setTableStyle(createTableStyle()); - table2.setClazz(WriteModel.class); - writer.write(createTestListJavaMode(), sheet3, table2); - - writer.finish(); - out.close(); - - } - - - @Test - public void writeV2007WithTemplate() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("temp.xlsx"); - OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); - ExcelWriter writer = EasyExcelFactory.getWriterWithTemp(inputStream,out,ExcelTypeEnum.XLSX,true); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 3); - sheet1.setSheetName("第一个sheet"); - sheet1.setStartRow(20); - - //设置列宽 设置每列的宽度 - Map columnWidth = new HashMap(); - columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); - sheet1.setColumnWidthMap(columnWidth); - sheet1.setHead(createTestListStringHead()); - //or 设置自适应宽度 - //sheet1.setAutoWidth(Boolean.TRUE); - writer.write1(createTestListObject(), sheet1); - - //写第二个sheet sheet2 模型上打有表头的注解,合并单元格 - Sheet sheet2 = new Sheet(2, 3, WriteModel.class, "第二个sheet", null); - sheet2.setTableStyle(createTableStyle()); - sheet2.setStartRow(20); - writer.write(createTestListJavaMode(), sheet2); - - //写第三个sheet包含多个table情况 - Sheet sheet3 = new Sheet(3, 0); - sheet3.setSheetName("第三个sheet"); - sheet3.setStartRow(30); - Table table1 = new Table(1); - table1.setHead(createTestListStringHead()); - writer.write1(createTestListObject(), sheet3, table1); - - //写sheet2 模型上打有表头的注解 - Table table2 = new Table(2); - table2.setTableStyle(createTableStyle()); - table2.setClazz(WriteModel.class); - writer.write(createTestListJavaMode(), sheet3, table2); - - writer.finish(); - out.close(); - - } - - @Test - public void writeV2007WithTemplateAndHandler() throws IOException { - InputStream inputStream = FileUtil.getResourcesFileInputStream("temp.xlsx"); - OutputStream out = new FileOutputStream("/Users/jipengfei/2007.xlsx"); - ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(inputStream,out,ExcelTypeEnum.XLSX,true, - new AfterWriteHandlerImpl()); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 3); - sheet1.setSheetName("第一个sheet"); - sheet1.setStartRow(20); - - //设置列宽 设置每列的宽度 - Map columnWidth = new HashMap(); - columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); - sheet1.setColumnWidthMap(columnWidth); - sheet1.setHead(createTestListStringHead()); - //or 设置自适应宽度 - //sheet1.setAutoWidth(Boolean.TRUE); - writer.write1(createTestListObject(), sheet1); - - //写第二个sheet sheet2 模型上打有表头的注解,合并单元格 - Sheet sheet2 = new Sheet(2, 3, WriteModel.class, "第二个sheet", null); - sheet2.setTableStyle(createTableStyle()); - sheet2.setStartRow(20); - writer.write(createTestListJavaMode(), sheet2); - - //写第三个sheet包含多个table情况 - Sheet sheet3 = new Sheet(3, 0); - sheet3.setSheetName("第三个sheet"); - sheet3.setStartRow(30); - Table table1 = new Table(1); - table1.setHead(createTestListStringHead()); - writer.write1(createTestListObject(), sheet3, table1); - - //写sheet2 模型上打有表头的注解 - Table table2 = new Table(2); - table2.setTableStyle(createTableStyle()); - table2.setClazz(WriteModel.class); - writer.write(createTestListJavaMode(), sheet3, table2); - - writer.finish(); - out.close(); - - } - - - - @Test - public void writeV2003() throws IOException { - OutputStream out = new FileOutputStream("/Users/jipengfei/2003.xls"); - ExcelWriter writer = EasyExcelFactory.getWriter(out, ExcelTypeEnum.XLS,true); - //写第一个sheet, sheet1 数据全是List 无模型映射关系 - Sheet sheet1 = new Sheet(1, 3); - sheet1.setSheetName("第一个sheet"); - - //设置列宽 设置每列的宽度 - Map columnWidth = new HashMap(); - columnWidth.put(0,10000);columnWidth.put(1,40000);columnWidth.put(2,10000);columnWidth.put(3,10000); - sheet1.setColumnWidthMap(columnWidth); - sheet1.setHead(createTestListStringHead()); - //or 设置自适应宽度 - //sheet1.setAutoWidth(Boolean.TRUE); - writer.write1(createTestListObject(), sheet1); - - //写第二个sheet sheet2 模型上打有表头的注解,合并单元格 - Sheet sheet2 = new Sheet(2, 3, WriteModel.class, "第二个sheet", null); - sheet2.setTableStyle(createTableStyle()); - writer.write(createTestListJavaMode(), sheet2); - - //写第三个sheet包含多个table情况 - Sheet sheet3 = new Sheet(3, 0); - sheet3.setSheetName("第三个sheet"); - Table table1 = new Table(1); - table1.setHead(createTestListStringHead()); - writer.write1(createTestListObject(), sheet3, table1); - - //写sheet2 模型上打有表头的注解 - Table table2 = new Table(2); - table2.setTableStyle(createTableStyle()); - table2.setClazz(WriteModel.class); - writer.write(createTestListJavaMode(), sheet3, table2); - - writer.finish(); - out.close(); - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java new file mode 100644 index 000000000..b356c5863 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java @@ -0,0 +1,3 @@ +/** + * Documents have been migrated to https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/fill/FillTest.java + */ diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java new file mode 100644 index 000000000..7a9213ecc --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java @@ -0,0 +1,3 @@ +/** + * Documents have been migrated to https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java + */ diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java new file mode 100644 index 000000000..86ceefb9d --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java @@ -0,0 +1,3 @@ +/** + * Documents have been migrated to https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java + */ diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java new file mode 100644 index 000000000..61bd164e0 --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -0,0 +1,3 @@ +/** + * Documents have been migrated to https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java + */ diff --git a/src/test/java/com/alibaba/easyexcel/test/listen/AfterWriteHandlerImpl.java b/src/test/java/com/alibaba/easyexcel/test/listen/AfterWriteHandlerImpl.java deleted file mode 100644 index fd7c38fbb..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/listen/AfterWriteHandlerImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.alibaba.easyexcel.test.listen; - -import com.alibaba.excel.event.WriteHandler; -import org.apache.poi.ss.usermodel.*; -import org.apache.poi.xssf.usermodel.XSSFClientAnchor; -import org.apache.poi.xssf.usermodel.XSSFRichTextString; - -public class AfterWriteHandlerImpl implements WriteHandler { - - // - CellStyle cellStyle; - - @Override - public void sheet(int sheetNo, Sheet sheet) { - Workbook workbook = sheet.getWorkbook(); - //要锁定单元格需先为此表单设置保护密码,设置之后此表单默认为所有单元格锁定,可使用setLocked(false)为指定单元格设置不锁定。 - //设置表单保护密码 - sheet.protectSheet("your password"); - //创建样式 - cellStyle = workbook.createCellStyle(); - //设置是否锁 - cellStyle.setLocked(false); - - } - - @Override - public void row(int rowNum, Row row) { - Workbook workbook = row.getSheet().getWorkbook(); - //设置行高 - row.setHeight((short)20); - } - - @Override - public void cell(int cellNum, Cell cell) { - Workbook workbook = cell.getSheet().getWorkbook(); - Sheet currentSheet = cell.getSheet(); - if (cellNum == 4 && cell.getRowIndex() == 30) { - //设置样式 - //注意:样式最好采用公用样式,样式在创建sheet后创建,如果有多个样式也需要在创建sheet时候创建后面直接使用,不要每个Cell Create 一个样式,不然会导致报错 The maximum number - // of Cell Styles was exceeded. - cell.setCellStyle(cellStyle); - - - - //设置备注 - Drawing draw = currentSheet.createDrawingPatriarch(); - Comment comment = draw.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, 4, 25, 9, 30)); - XSSFRichTextString rtf = new XSSFRichTextString("添加批注内容收到货死的死哦多胡搜idsad是否会杜甫的范德萨发!1111"); - Font commentFormatter = workbook.createFont(); - commentFormatter.setFontName("宋体"); - //设置字体大小 - commentFormatter.setFontHeightInPoints((short)9); - rtf.applyFont(commentFormatter); - comment.setString(rtf); - comment.setAuthor("ceshi"); - cell.setCellComment(comment); - } - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/listen/ExcelListener.java b/src/test/java/com/alibaba/easyexcel/test/listen/ExcelListener.java deleted file mode 100644 index 0f17a391c..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/listen/ExcelListener.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibaba.easyexcel.test.listen; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; - -import java.util.ArrayList; -import java.util.List; - -public class ExcelListener extends AnalysisEventListener { - - - private List data = new ArrayList(); - - @Override - public void invoke(Object object, AnalysisContext context) { - System.out.println(context.getCurrentSheet()); - data.add(object); - if(data.size()>=100){ - doSomething(); - data = new ArrayList(); - } - } - - @Override - public void doAfterAllAnalysed(AnalysisContext context) { - doSomething(); - } - public void doSomething(){ - for (Object o:data) { - System.out.println(o); - } - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/model/BaseReadModel.java b/src/test/java/com/alibaba/easyexcel/test/model/BaseReadModel.java deleted file mode 100644 index be65aa765..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/model/BaseReadModel.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alibaba.easyexcel.test.model; - -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.metadata.BaseRowModel; - -public class BaseReadModel extends BaseRowModel { - @ExcelProperty(index = 0) - protected String str; - - @ExcelProperty(index = 1) - protected Float ff; - public String getStr() { - return str; - } - - - public void setStr(String str) { - this.str = str; - } - - public Float getFf() { - return ff; - } - - public void setFf(Float ff) { - this.ff = ff; - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/model/BaseWriteModel.java b/src/test/java/com/alibaba/easyexcel/test/model/BaseWriteModel.java deleted file mode 100644 index 8688216cd..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/model/BaseWriteModel.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.alibaba.easyexcel.test.model; - -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.metadata.BaseRowModel; - -public class BaseWriteModel extends BaseRowModel { - @ExcelProperty(value = {"表头1","表头1","表头31"},index = 0) - protected String p1; - - @ExcelProperty(value = {"表头1","表头1","表头32"},index = 1) - protected String p2; - - public String getP1() { - return p1; - } - - public void setP1(String p1) { - this.p1 = p1; - } - - public String getP2() { - return p2; - } - - public void setP2(String p2) { - this.p2 = p2; - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/model/ReadModel.java b/src/test/java/com/alibaba/easyexcel/test/model/ReadModel.java deleted file mode 100644 index fe901719a..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/model/ReadModel.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.alibaba.easyexcel.test.model; - -import com.alibaba.excel.annotation.ExcelProperty; - -import java.math.BigDecimal; -import java.util.Date; - -public class ReadModel extends BaseReadModel { - - @ExcelProperty(index = 2) - private Integer mm; - - @ExcelProperty(index = 3) - private BigDecimal money; - - @ExcelProperty(index = 4) - private Long times; - - @ExcelProperty(index = 5) - private Double activityCode; - - @ExcelProperty(index = 6,format = "yyyy-MM-dd") - private Date date; - - @ExcelProperty(index = 7) - private String lx; - - @ExcelProperty(index = 8) - private String name; - - @ExcelProperty(index = 18) - private String kk; - - - public Integer getMm() { - return mm; - } - - public void setMm(Integer mm) { - this.mm = mm; - } - - public BigDecimal getMoney() { - return money; - } - - public void setMoney(BigDecimal money) { - this.money = money; - } - - public Long getTimes() { - return times; - } - - public void setTimes(Long times) { - this.times = times; - } - - public Double getActivityCode() { - return activityCode; - } - - public void setActivityCode(Double activityCode) { - this.activityCode = activityCode; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getLx() { - return lx; - } - - public void setLx(String lx) { - this.lx = lx; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getKk() { - return kk; - } - - public void setKk(String kk) { - this.kk = kk; - } - - @Override - public String toString() { - return "JavaModel{" + - "str='" + str + '\'' + - ", ff=" + ff + - ", mm=" + mm + - ", money=" + money + - ", times=" + times + - ", activityCode=" + activityCode + - ", date=" + date + - ", lx='" + lx + '\'' + - ", name='" + name + '\'' + - ", kk='" + kk + '\'' + - '}'; - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/model/ReadModel2.java b/src/test/java/com/alibaba/easyexcel/test/model/ReadModel2.java deleted file mode 100644 index a64f70117..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/model/ReadModel2.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.alibaba.easyexcel.test.model; - -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.metadata.BaseRowModel; - -import java.math.BigDecimal; -import java.util.Date; - -public class ReadModel2 extends BaseRowModel { - @ExcelProperty(index = 0) - private String str; - - @ExcelProperty(index = 1) - private Float ff; - - @ExcelProperty(index = 2) - private Integer mm; - - @ExcelProperty(index = 3) - private BigDecimal money; - - @ExcelProperty(index = 4) - private Long times; - - @ExcelProperty(index = 5) - private Double activityCode; - - @ExcelProperty(index = 6,format = "yyyy-MM-dd") - private Date date; - - @ExcelProperty(index = 7) - private String lx; - - @ExcelProperty(index = 8) - private String name; - - @ExcelProperty(index = 18) - private String kk; - - public String getStr() { - return str; - } - - - public void setStr(String str) { - this.str = str; - } - - public Float getFf() { - return ff; - } - - public void setFf(Float ff) { - this.ff = ff; - } - - public Integer getMm() { - return mm; - } - - public void setMm(Integer mm) { - this.mm = mm; - } - - public BigDecimal getMoney() { - return money; - } - - public void setMoney(BigDecimal money) { - this.money = money; - } - - public Long getTimes() { - return times; - } - - public void setTimes(Long times) { - this.times = times; - } - - public Double getActivityCode() { - return activityCode; - } - - public void setActivityCode(Double activityCode) { - this.activityCode = activityCode; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getLx() { - return lx; - } - - public void setLx(String lx) { - this.lx = lx; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getKk() { - return kk; - } - - public void setKk(String kk) { - this.kk = kk; - } - - @Override - public String toString() { - return "JavaModel2{" + - "str='" + str + '\'' + - ", ff=" + ff + - ", mm=" + mm + - ", money=" + money + - ", times=" + times + - ", activityCode=" + activityCode + - ", date=" + date + - ", lx='" + lx + '\'' + - ", name='" + name + '\'' + - ", kk='" + kk + '\'' + - '}'; - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/model/WriteModel.java b/src/test/java/com/alibaba/easyexcel/test/model/WriteModel.java deleted file mode 100644 index acb17c1e2..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/model/WriteModel.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.alibaba.easyexcel.test.model; - -import com.alibaba.excel.annotation.ExcelProperty; -import com.alibaba.excel.metadata.BaseRowModel; - -import java.math.BigDecimal; -import java.util.Date; - -public class WriteModel extends BaseWriteModel { - - - - @ExcelProperty(value = {"表头3","表头3","表头3"},index = 2) - private int p3; - - @ExcelProperty(value = {"表头1","表头4","表头4"},index = 3) - private long p4; - - @ExcelProperty(value = {"表头5","表头51","表头52"},index = 4) - private String p5; - - @ExcelProperty(value = {"表头6","表头61","表头611"},index = 5) - private float p6; - - @ExcelProperty(value = {"表头6","表头61","表头612"},index = 6) - private BigDecimal p7; - - @ExcelProperty(value = {"表头6","表头62","表头621"},index = 7) - private Date p8; - - @ExcelProperty(value = {"表头6","表头62","表头622"},index = 8) - private String p9; - - @ExcelProperty(value = {"表头6","表头62","表头622"},index = 9) - private double p10; - - public String getP1() { - return p1; - } - - public void setP1(String p1) { - this.p1 = p1; - } - - public String getP2() { - return p2; - } - - public void setP2(String p2) { - this.p2 = p2; - } - - public int getP3() { - return p3; - } - - public void setP3(int p3) { - this.p3 = p3; - } - - public long getP4() { - return p4; - } - - public void setP4(long p4) { - this.p4 = p4; - } - - public String getP5() { - return p5; - } - - public void setP5(String p5) { - this.p5 = p5; - } - - public float getP6() { - return p6; - } - - public void setP6(float p6) { - this.p6 = p6; - } - - public BigDecimal getP7() { - return p7; - } - - public void setP7(BigDecimal p7) { - this.p7 = p7; - } - - public Date getP8() { - return p8; - } - - public void setP8(Date p8) { - this.p8 = p8; - } - - public String getP9() { - return p9; - } - - public void setP9(String p9) { - this.p9 = p9; - } - - public double getP10() { - return p10; - } - - public void setP10(double p10) { - this.p10 = p10; - } - - @Override - public String toString() { - return "JavaModel1{" + - "p1='" + p1 + '\'' + - ", p2='" + p2 + '\'' + - ", p3=" + p3 + - ", p4=" + p4 + - ", p5='" + p5 + '\'' + - ", p6=" + p6 + - ", p7=" + p7 + - ", p8=" + p8 + - ", p9='" + p9 + '\'' + - ", p10=" + p10 + - '}'; - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/util/DataUtil.java b/src/test/java/com/alibaba/easyexcel/test/util/DataUtil.java deleted file mode 100644 index 505dab559..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/util/DataUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.alibaba.easyexcel.test.util; - -import com.alibaba.easyexcel.test.model.WriteModel; -import com.alibaba.excel.metadata.Font; -import com.alibaba.excel.metadata.TableStyle; -import org.apache.poi.ss.usermodel.IndexedColors; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class DataUtil { - - - public static List> createTestListObject() { - List> object = new ArrayList>(); - for (int i = 0; i < 1000; i++) { - List da = new ArrayList(); - da.add("字符串"+i); - da.add(Long.valueOf(187837834l+i)); - da.add(Integer.valueOf(2233+i)); - da.add(Double.valueOf(2233.00+i)); - da.add(Float.valueOf(2233.0f+i)); - da.add(new Date()); - da.add(new BigDecimal("3434343433554545"+i)); - da.add(Short.valueOf((short)i)); - object.add(da); - } - return object; - } - - public static List> createTestListStringHead(){ - //写sheet3 模型上没有注解,表头数据动态传入 - List> head = new ArrayList>(); - List headCoulumn1 = new ArrayList(); - List headCoulumn2 = new ArrayList(); - List headCoulumn3 = new ArrayList(); - List headCoulumn4 = new ArrayList(); - List headCoulumn5 = new ArrayList(); - - headCoulumn1.add("第一列");headCoulumn1.add("第一列");headCoulumn1.add("第一列"); - headCoulumn2.add("第一列");headCoulumn2.add("第一列");headCoulumn2.add("第一列"); - - headCoulumn3.add("第二列");headCoulumn3.add("第二列");headCoulumn3.add("第二列"); - headCoulumn4.add("第三列");headCoulumn4.add("第三列2");headCoulumn4.add("第三列2"); - headCoulumn5.add("第一列");headCoulumn5.add("第3列");headCoulumn5.add("第4列"); - - head.add(headCoulumn1); - head.add(headCoulumn2); - head.add(headCoulumn3); - head.add(headCoulumn4); - head.add(headCoulumn5); - return head; - } - - public static List createTestListJavaMode(){ - List model1s = new ArrayList(); - for (int i = 0; i <10000 ; i++) { - WriteModel model1 = new WriteModel(); - model1.setP1("第一列,第行"); - model1.setP2("121212jjj"); - model1.setP3(33+i); - model1.setP4(44); - model1.setP5("555"); - model1.setP6(666.2f); - model1.setP7(new BigDecimal("454545656343434"+i)); - model1.setP8(new Date()); - model1.setP9("llll9999>&&&&&6666^^^^"); - model1.setP10(1111.77+i); - model1s.add(model1); - } - return model1s; - } - - public static TableStyle createTableStyle() { - TableStyle tableStyle = new TableStyle(); - Font headFont = new Font(); - headFont.setBold(true); - headFont.setFontHeightInPoints((short)22); - headFont.setFontName("楷体"); - tableStyle.setTableHeadFont(headFont); - tableStyle.setTableHeadBackGroundColor(IndexedColors.BLUE); - - Font contentFont = new Font(); - contentFont.setBold(true); - contentFont.setFontHeightInPoints((short)22); - contentFont.setFontName("黑体"); - tableStyle.setTableContentFont(contentFont); - tableStyle.setTableContentBackGroundColor(IndexedColors.GREEN); - return tableStyle; - } -} diff --git a/src/test/java/com/alibaba/easyexcel/test/util/FileUtil.java b/src/test/java/com/alibaba/easyexcel/test/util/FileUtil.java deleted file mode 100644 index 60cea7ab9..000000000 --- a/src/test/java/com/alibaba/easyexcel/test/util/FileUtil.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alibaba.easyexcel.test.util; - -import java.io.InputStream; - -public class FileUtil { - - public static InputStream getResourcesFileInputStream(String fileName) { - return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName); - } -} diff --git a/src/test/resources/2003.xls b/src/test/resources/2003.xls deleted file mode 100644 index 3c2fa34d1..000000000 Binary files a/src/test/resources/2003.xls and /dev/null differ diff --git a/src/test/resources/2007.xlsx b/src/test/resources/2007.xlsx deleted file mode 100644 index 1c17101aa..000000000 Binary files a/src/test/resources/2007.xlsx and /dev/null differ diff --git a/src/test/resources/temp.xlsx b/src/test/resources/temp.xlsx deleted file mode 100644 index 2149a2375..000000000 Binary files a/src/test/resources/temp.xlsx and /dev/null differ diff --git a/style/STYLE.md b/style/STYLE.md new file mode 100644 index 000000000..cc75b44d5 --- /dev/null +++ b/style/STYLE.md @@ -0,0 +1,29 @@ +## 统一代码风格 +如果要编辑本项目,一定要统一代码风格 +### 统一方案 +本工程代码遵守阿里巴巴[p3c](https://github.com/alibaba/p3c)规范,在代码开发前建议: +* 安装阿里巴巴规约插件,用于提早发现不规范代码,具体安装方法参照:[p3c](https://github.com/alibaba/p3c),里面有eclipse,idea的安装方法 +* 安装codeStyle插件,用于格式化代码的时候符合代码规范,安装方法见:[安装codeStyle插件](#codeStyleInstall) +* checkStyle校验,这个不用安装,`mvn`在编译的时候自己会调用,写在`pom.xml`里,用`maven-pmd-plugin`调用`pmd-p3c`规范校验 + +如果工作中遇到代码格式化问题,经常导致git冲突,也可以采用上面的方案 +### codeStyle插件安装 +这里注意下,不管eclipse或者idea,都需要导入eclipse文件夹下面的配置 +#### eclipse +* 依次点击:`Window->Preferences->Java->Code Style->Formatter->Import` +* 选择`style/eclipse/codestyle.xml`文件 确定 +* 默认在`Active profile`中选择新导入的`P3C-CodeStyle`,如未选择,请手动选择 +* 点击`Apply`完成配置 +![step](../img/style/eclipse/step.jpg) +#### idea +* 依次点击进入插件界面:`File->Settings->Plugins`,搜索 eclipse code formatter,如已有插件则不需安装,如没有,点击Search in repositories自动搜索线上插件。 +![step1](../img/style/idea/step1.png) +* 导入`style/eclipse/codestyle.xml` 这里记住用的也是eclipse里面的 点击OK +![step1](../img/style/idea/step2.png) +* 依次点击进入插件界面:`File->Settings->Editor->Code Style->Java->Import Scheme->Intellij IDEA code style XML`,导入`style/idea/codestyle.xml` 这里用的是idea的配置文件 +![step1](../img/style/idea/step3.png) +* 完成 + +### 附 +#### windows系统,代码规范校验提示乱码 +这个是因为cmd默认gbk,cmd输入:`CHCP 65001` 在运行mvn 提示就是不会乱码了 \ No newline at end of file diff --git a/style/codestyle/eclipse/codestyle.xml b/style/codestyle/eclipse/codestyle.xml new file mode 100644 index 000000000..42b305523 --- /dev/null +++ b/style/codestyle/eclipse/codestyle.xml @@ -0,0 +1,603 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/style/codestyle/idea/codestyle.xml b/style/codestyle/idea/codestyle.xml new file mode 100644 index 000000000..5ee5b77b4 --- /dev/null +++ b/style/codestyle/idea/codestyle.xml @@ -0,0 +1,91 @@ + + + \ No newline at end of file diff --git a/update.md b/update.md index 141495739..c31e8bdf5 100644 --- a/update.md +++ b/update.md @@ -1,39 +1,455 @@ -# 1.0.1 +# 4.0.3 -完善测试用例,防止歧义,模型字段映射不上时候有抛异常,改为提醒。 +* 兼容部分日期格式读取异常的问题 -# 1.0.2 +# 4.0.2 -修复拿到一行数据后,存到list中,但最后处理时候变为空的bug。 +* 兼容某些特殊的xls: 修改了内置的样式导致判断样式错误 +* 重新加回 `commons-io` -# 1.0.3 +# 4.0.1 -修复无@ExcelProperty标注的多余字段时候报错。 +* `commons-io` 修改为依赖 `poi`的版本 +* 修复临时目录被清理可能提示`NoSuchFileException`的异常 -# 1.0.4 +# 4.0.0 -修复日期类型转换时候数字问题。基础模型支持字段类型int,long,double,boolean,date,string +* `poi`由`4.1.2`升级到`5.2.5` +* `commons-csv`由`1.1.0`升级到`1.10.0` +* `slf4j-api`由`1.7.32`升级到`1.7.36` +* `ehcache`由`3.9.9`升级到`3.9.11` +* 支持`jdk21` -# 1.0.5 +# 3.3.4 -优化类型转换的性能。 +* 支持停止单个`sheet`以后继续读取其他`sheet`,使用`ExcelAnalysisStopSheetException` -# 1.0.6 +# 3.3.3 -增加@ExcelColumnNum,修复字符串前后空白,增加过滤功能。 -# 1.0.8 +* 兼容某些特殊的xls: 缺少每个sheet的终止符 -如果整行excel数据全部为空,则不解析返回。完善多sheet的解析。 +# 3.3.2 -# 1.0.9 +* 修复`includeColumnIndexes`和`includeColumnFieldNames`在sheet后面失效的问题 -修复excel超过16列被覆盖的问题,修复数据只有一行时候无法透传的bug。 +# 3.3.1 +* 修改版本发布问题 -# 1.2.1 +# 3.3.0 -修复POI在大并发情况下创建临时目录失败的bug +* 读csv会忽略BOM数据 [Issue #3137](https://github.com/alibaba/easyexcel/issues/3137) +* 解决csv用office打开乱码的问题,写csv默认带上BOM数据 +* xlsx存在隐藏字符时需要忽略,确保和展示看到的一样 +* 新增`commons-io` 2.11.0 包 +* 在`easyexcel-parent` 包中移除测试包的`dependencyManagement` +* 删除`org.apache.poi.hssf.usermodel.PoiUtils.java`, + 使用反射获取 [Issue #2804](https://github.com/alibaba/easyexcel/issues/2804) +* 默认对象反射缓存改成`ThreadLocal` + ,并支持设置反射缓存类型 [Issue #2792](https://github.com/alibaba/easyexcel/issues/2792) +* 支持根据`includeColumnIndexes`和`includeColumnFieldNames` + 排序 [Issue #2697](https://github.com/alibaba/easyexcel/issues/2697) +* 根据文件流解析,由抛出异常改为,默认识别为csv + +# 3.2.1 + +* 兼容`LocalDate` [Issue #2908](https://github.com/alibaba/easyexcel/issues/2908) +* 优化大文件内存存储,减少内存占用 [Issue #2657](https://github.com/alibaba/easyexcel/issues/2657) +* 在临时文件被删除的情况下能正常的读取 [Issue #2693](https://github.com/alibaba/easyexcel/issues/2693) +* 分页读取监听器支持自定义分页条数 [Issue #2383](https://github.com/alibaba/easyexcel/issues/2383) + +# 3.2.0 + +* 修复部分xlsx读取日期可能相差1秒的bug [Issue #1956](https://github.com/alibaba/easyexcel/issues/1956) +* 修复部分数据精度和excel不匹配的bug [Issue #2805](https://github.com/alibaba/easyexcel/issues/2805) +* 不创建对象的读支持读取原始的数据类型 + +# 3.1.5 + +* 提高xlsx读取兼容性:兼用ns2开头的标签 + +# 3.1.4 + +* 提高xlsx读取兼容性:在存在第一行很多空 +* 列的情况下,忽略空列 + +# 3.1.3 + +* 提高xlsx兼容性:兼容`sharedStrings.xml` 存在 `x:t`标签的情况 + +# 3.1.2 + +* 修复长时间运行会产生大对象的bug [Issue #2725](https://github.com/alibaba/easyexcel/issues/2725) + +# 3.1.1 + +* 修复部分xlsx无法读取超链接的bug + +# 3.1.0 + +* 支持jdk17,去除cglib&asm依赖,改成重新拷贝一份 [Issue #2240](https://github.com/alibaba/easyexcel/issues/2240) +* 升级ehcache 到 3.9.9 ,为了兼容jdk17 +* 在有样式没有数据的情况下也算空行 [Issue #2294](https://github.com/alibaba/easyexcel/issues/2294) +* 修复无法根据文件流判断csv的bug [Issue #2297](https://github.com/alibaba/easyexcel/issues/2297) +* 修复CSV不关闭流的bug [Issue #2309](https://github.com/alibaba/easyexcel/issues/2309) +* 修复`filed`拼接错误 [Issue #2390](https://github.com/alibaba/easyexcel/issues/2390) +* 修复`BigIntegerStringConverter`无效的bug [Issue #2325](https://github.com/alibaba/easyexcel/issues/2325) +* 修复03版本无法读取到公式格式的bug [Issue #2444](https://github.com/alibaba/easyexcel/issues/2444) +* 提高类型转换兼容性 [Issue #2443](https://github.com/alibaba/easyexcel/issues/2443) +* 捕获`setFeature`异常不影响主流程 [Issue #2054](https://github.com/alibaba/easyexcel/issues/2054) +* 增加部分`xls`容错 [Issue #2236](https://github.com/alibaba/easyexcel/issues/2236) +* 修复读取csv文件时`autoTrim`配置失效 [Issue #2227](https://github.com/alibaba/easyexcel/issues/2227) +* csv支持设置读写编码 [Issue #2404](https://github.com/alibaba/easyexcel/issues/2404) +* 读写支持`try-with-resources`语法糖 + +# 3.0.5 + +* 修复`ReadListener` 转换异常不抛出的问题 + +# 3.0.4 + +* 调整读写默认大小,防止大批量写的时候可能会full gc +* `fill`的情况新增 `afterRowDispose`事件 + +# 3.0.3 + +* 修复`HeadStyle`无效的bug + +# 3.0.2 + +* 大幅提升读写性能 +* 修复列宽注解没用的bug [Issue #2151](https://github.com/alibaba/easyexcel/issues/2151) +* 修复`CellData`接收失败的的bug [Issue #2147](https://github.com/alibaba/easyexcel/issues/2147) + +# 3.0.1 + +* 升级到正式版 +* 修复填充样式可能丢失的问题 [Issue #2124](https://github.com/alibaba/easyexcel/issues/2124) +* 修复填充数据为空 可能NPE的bug +* 修复填充样式可能不生效bug +* 修复样式可能超过最大限制的bug +* 修复写入过慢的bug + +# 3.0.0-beta3 + +* 修复导出浮点型数据可能精度异常的bug + +# 3.0.0-beta2 + +* 优化写入样式 + +# 3.0.0-beta1 + +* 升级jdk8 不再支持jdk6 jdk7 +* 升级poi 到 4.1.2 +* 升级cglib 到 3.3.0 +* 升级ehcache 到 3.8.1 +* 支持非驼峰的字段读写 +* 支持csv的读&写 +* 修复`CellData`可能不返回行列号 [Issue #1832](https://github.com/alibaba/easyexcel/issues/1832) +* 优化读取性能 +* 修复部分情况下不抛出异常 +* 07版在导出的时候会导出 行数 [Issue #1282](https://github.com/alibaba/easyexcel/issues/1282) +* 修复没有样式的情况下空指针异常 [Issue #1738](https://github.com/alibaba/easyexcel/issues/1738) +* 修改异常抛出逻辑 [Issue #1618](https://github.com/alibaba/easyexcel/issues/1618) +* 兼容一些非官方excel的情况 [Issue #1527](https://github.com/alibaba/easyexcel/issues/1527) +* 修改读的关闭流无效 [Issue #1840](https://github.com/alibaba/easyexcel/issues/1840) +* 写入支持Collection [Issue #1834](https://github.com/alibaba/easyexcel/issues/1834) +* `Converter`支持null转换 [Issue #1776](https://github.com/alibaba/easyexcel/issues/1776) +* cglib 新增命名策略,防止和`spring`的冲突 [Issue #2064](https://github.com/alibaba/easyexcel/issues/2064) +* 修改填充可能填充错误的bug [Issue #2035](https://github.com/alibaba/easyexcel/issues/2035) +* 修复无对象读 返回map的size可能会头的size不一致 [Issue #2014](https://github.com/alibaba/easyexcel/issues/2014) +* 修复合并头可能异常的bug [Issue #1662](https://github.com/alibaba/easyexcel/issues/1662) +* 修复填充调用横向样式策略报错 [Issue #1651](https://github.com/alibaba/easyexcel/issues/1651) +* 修复不自动行高的问题 [Issue #1869](https://github.com/alibaba/easyexcel/issues/1869) +* 新增头的非空校验 [Issue #1765](https://github.com/alibaba/easyexcel/issues/1765) +* 修复某些特殊的excel读取失败的问题 [Issue #1595](https://github.com/alibaba/easyexcel/issues/1595) +* 修复不创建对象写入数据异常 [Issue #1702](https://github.com/alibaba/easyexcel/issues/1702) +* 修复头和数据对象不一致会覆盖的问题 [Issue #1870](https://github.com/alibaba/easyexcel/issues/1870) +* 修复忽略字段后可能排序不一致的问题 +* 修改填充时,无法使用生成的模板 [Issue #1552](https://github.com/alibaba/easyexcel/issues/1552) +* 修改填充可以不自动继承样式 [Issue #1710](https://github.com/alibaba/easyexcel/issues/1710) +* 修复填充数据不能为空的问题 [Issue #1703](https://github.com/alibaba/easyexcel/issues/1703) +* 新增部分jdk8特性 + +# 2.2.11 + +* 修复有些xlsx解析失败的bug [Issue #1595](https://github.com/alibaba/easyexcel/issues/1595) + +# 2.2.10 + +* 修复读取的时候用string接收数字 可能四舍五入不一致的bug + +# 2.2.9 + +* 修复读取的时候用string接收数字 可能四舍五入不一致的bug + +# 2.2.8 + +* 兼容07在特殊的excel的情况下,读取数据异常 + +# 2.2.7 + +* 修改07在特殊情况下用`String`接收数字会丢小数位的bug + +# 2.2.6 + +* 修改跳着读取03版本空指针bug + +# 2.2.5 + +* `ExcelProperty`新增`order` 用于排序 +* 修复导出指定`index`会导致空行的bug + +# 2.2.4 + +* 撤销删除`AbstractMergeStrategy` +* 修改默认用String读取数字不使用科学计数法 通过`useScientificFormat`修改 +* 修复07版仅有样式的空行 默认不忽略的bug +* 写入`sheet`不设置`index`和`name`默认不为0的问题 +* 修复多个`sheet`不按照顺序写入 会乱序的bug [Issue #1332](https://github.com/alibaba/easyexcel/issues/1332) +* 修改head是List时,内容单元格的样式不生效 [Issue #1339](https://github.com/alibaba/easyexcel/issues/1339) +* 修复xls仅公式行 不读取的bug [Issue #1324](https://github.com/alibaba/easyexcel/issues/1324) +* 修复xls直接读取第2页 `NPE` 的bug [Issue #1280](https://github.com/alibaba/easyexcel/issues/1280) +* 修复填充的时候,最后一行中间有空行会创建失败的bug +* 修复`includeColumnIndexes`不包含第列 + 会无法导出数据的bug [Issue #1346](https://github.com/alibaba/easyexcel/issues/1346) +* 修复`@NumberFormat`注解转换double时可能会丢失精度 [Issue #1306](https://github.com/alibaba/easyexcel/issues/1306) + +# 2.2.3 + +* 修改填充数据空数据的bug [Issue #1274](https://github.com/alibaba/easyexcel/issues/1274) +* 回退自定义转换器入参为空 + +# 2.2.2 + +* 修改`sheet`事件未调用的bug +* 修复复杂表头不是`index=0`开始 合并异常的bug [Issue #1322](https://github.com/alibaba/easyexcel/issues/1322) + +# 2.2.1 + +* 发布正式版 +* 修复第一行为空不会调用`invokeHeadMap`的bug [Issue #993](https://github.com/alibaba/easyexcel/issues/993) +* + +当类的属性没有按照ExcelProperty的属性index顺序排序的时候,写数据出现错乱 [Issue #1046](https://github.com/alibaba/easyexcel/issues/1046) + +* 新增支持自定义转换器 入参可以为空 实现`NullableObjectConverter` + 即可 [Issue #1084](https://github.com/alibaba/easyexcel/issues/1084) +* 修复xls丢失结束标记的情况下 会漏读最后一行 +* 修复填充的时候 多次`forceNewRow` 空指针的bug [Issue #1201](https://github.com/alibaba/easyexcel/issues/1201) +* 修复`table`、`sheet`中创建的拦截器不执行`workbook` + 事件的bug [Issue #1202](https://github.com/alibaba/easyexcel/issues/1202) + +# 2.2.0-beta2 + +* 修复最长匹配策略不同表格会有影响的bug [Issue #1010](https://github.com/alibaba/easyexcel/issues/1010) +* `LinkedList`写入的性能问题 #1121 +* 修复在某些情况下可能出现不必要的`warn`日志 + +# 2.2.0-beta1 + +* 重写主流程,代码更加优雅 +* 修复用String接收日期、数字和excel显示不一致的bug(不是完美修复,但是大部分情况已经兼容) +* 降低Ehcache版本 3.7.1(jkd7) -> 3.4.0(jdk6) +* 修复xls 用Map接收时多次接收会是同一个对象的bug +* 修复浮点型数据导入到excel 会丢失精度的bug +* 新增支持读取批注、超链接、合并单元格 +* 如果是`RuntimeException`则不再封装对象 +* 新增`CellData`可以获取行列号 +* 新增样式注解 +* 新增合并单元格注解 +* 提升合并策略效率 +* 兼容部分比较特殊的excel +* 同时传入了`List>`和`class`的head,会通过index去匹配注解 +* 修复读取转换器的并发问题 +* 填充支持多个List对象 + +# 2.1.7 + +* 修复使用1+版本的写法,第1条开始读修改为第0条开始读 + +# 2.1.6 + +* 修复写入只有`sheetName`会抛异常 + +# 2.1.5 + +* 修复部分xlsx没有行号读取异常 +* 填充时候支持根据`sheetName`定位`sheet` + +# 2.1.4 + +* 新增参数`useDefaultListener` 可以排除默认对象转换 + +# 2.1.3 + +* 每个java进程单独创建一个缓存目录 [Issue #813](https://github.com/alibaba/easyexcel/issues/813) +* 统一修改合并为unsafe,提高大量数据导出的合并的效率 +* 修改merge返回参数`relativeRowIndex`为`Integer` +* 新增参数`automaticMergeHead` 可以设置不自动合并头 [Issue #822](https://github.com/alibaba/easyexcel/issues/822) +* 新增参数`xlsxSAXParserFactoryName` 可以指定`SAXParserFactory` +* 修复合并策略 空指针的问题 +* `SimpleColumnWidthStyleStrategy` 新增 参数`columnIndex` [Issue #806](https://github.com/alibaba/easyexcel/issues/806) + +# 2.1.2 + +* 修复强制创建新行填充,只有一行数据会未填充的bug + +# 2.1.1 + +* 发布正式版 +* 修改map返回为LinkedHashMap +* 修改同步读取返回对象支持泛型 +* 修复03版不能直接读取第二个sheet的bug [Issue #772](https://github.com/alibaba/easyexcel/issues/772) +* 新增支持图片导出用URL [Issue #774](https://github.com/alibaba/easyexcel/issues/774) +* 加入多次关闭判断,防止多次关闭异常 +* 加入根据模板自动识别导出的excel类型 +* 修改默认失败后,不再往文件流写入数据。通过参数`writeExcelOnException` 参数设置异常了也要写入前面的数据。 +* 循环合并策略支持一次性合并多列 +* `ExcelDataConvertException`返回新增具体报错的数据 +* 加入解析class缓存 +* 修复填充的时候行高不复制的Bug [Issue #780](https://github.com/alibaba/easyexcel/issues/780) +* 修复03版无法获取大概总行数的bug + +# 2.1.0-beta4 + +* 修改最长匹配策略会空指针的bug [Issue #747](https://github.com/alibaba/easyexcel/issues/747) +* 修改afterRowDispose错误 [Issue #751](https://github.com/alibaba/easyexcel/issues/751) +* 修复多个头的情况下会读取数据为空 + +# 2.1.0-beta3 + +* 支持强行指定在内存处理,以支持备注、RichTextString等的写入 +* 修复关闭流失败,可能会不删除临时文件的问题 +* 支持根据参数自定义导出列 +* 修改最长匹配策略的最大长度 [Issue #734](https://github.com/alibaba/easyexcel/issues/734) +* 修复策略头未生效的bug [Issue #735](https://github.com/alibaba/easyexcel/issues/735) +* 修复填充的时候有数字会异常 + +# 2.1.0-beta2 + +* 修改模板通过流创建报错的bug +* 修复空数据未替换掉的bug +* 修复空模板会空一行的bug + +# 2.1.0-beta1 + +* 新增支持导入、导出支持公式 +* 新增支持读取单元格类型、写入指定单元格类型 +* 支持通过模板填充数据 +* 新增写支持 禁用头样式 `useDefaultStyle` +* 用map读取数据 空的单元格也会有个 null的数据 +* 转换报错 能获取到对应的行号和列号 +* 优化读取全部sheet方案 +* 新增注解`ExcelIgnoreUnannotated` 支持忽略未加`ExcelProperty`注解的字段 +* 支持导出加密 [Issue #361](https://github.com/alibaba/easyexcel/issues/361) +* 支持导入加密 [Issue #295](https://github.com/alibaba/easyexcel/issues/295) + +# 2.0.5 + +* 优化07版超大文件读取方案 +* 支持自己设置超大文件读取参数 +* 读取xlsx会改变修改时间的bug [Issue #574](https://github.com/alibaba/easyexcel/issues/574) +* 默认读取忽略空行 根据参数ignoreEmptyRow参数设置 + +# 2.0.4 + +* 修复07版整个excel仅存在数字时会出现的NPE +* 修复03版 用String接收电话会出现科学计数法的问题 + +# 2.0.3 + +* 修复重大bug 在07版读取文件的时候 小概率导致数字部分丢失 + +# 2.0.2 + +* 修复xls无法获取sheetList的bug [Issue #621](https://github.com/alibaba/easyexcel/issues/621) +* 修复监听器转换异常会重复提示的bug + +# 2.0.1 + +* 降级poi为3.17 兼容jdk6 + +# 2.0.0 + +* 修复当cell为空可能会抛出空指针的bug +* 修复电话等长数字可能出现科学计数法的问题 [Issue #583](https://github.com/alibaba/easyexcel/issues/583) +* 升级为正式版 + +# 2.0.0-beta6 + +* 修复空行读取空指针异常 +* 修复写入指定头为List>,但是数据用List导致的空指针 + +# 2.0.0-beta5 + +* 修复在读取值的时候读取了额外值导致数据转换异常 + +# 2.0.0-beta4 + +* 修改在传入List>判断行数错误 [Issue #526](https://github.com/alibaba/easyexcel/issues/526) +* 修复在mac 2016 2017导出的excel 可能存在多余字段的问题 +* 修复03版 读取无法指定sheet的问题 [Issue #533](https://github.com/alibaba/easyexcel/issues/533) + +# 2.0.0-beta3 + +* 导出完成移除临时目录 [Issue #386](https://github.com/alibaba/easyexcel/issues/386) +* 新增读取返回头数据 + +# 2.0.0-beta2 + +* 加速gc回收 [Issue #511](https://github.com/alibaba/easyexcel/issues/511) +* 修改空字符串读取可能读取上个字段的数据的bug +* 修改换行数据无法读取的bug [Issue #521](https://github.com/alibaba/easyexcel/issues/521) +* 修复在空字符串的时候 格式转换异常 [Issue #520](https://github.com/alibaba/easyexcel/issues/520) + +# 2.0.0-beta1 + +* 优化读写逻辑 +* 优化读写对外接口 +* 加入转换器,方便格式转换 +* 极大优化读大文件的内存和效率 +* sheetNo 改成0开始 +* 读支持指定列名 +* 升级poi 到4.0.1 # 1.2.4 修复read()方法存在的bug + +# 1.2.1 + +修复POI在大并发情况下创建临时目录失败的bug + +# 1.0.9 + +修复excel超过16列被覆盖的问题,修复数据只有一行时候无法透传的bug。 + +# 1.0.8 + +如果整行excel数据全部为空,则不解析返回。完善多sheet的解析。 + +# 1.0.6 + +增加@ExcelColumnNum,修复字符串前后空白,增加过滤功能。 + +# 1.0.5 + +优化类型转换的性能。 + +# 1.0.4 + +修复日期类型转换时候数字问题。基础模型支持字段类型int,long,double,boolean,date,string + +# 1.0.3 + +修复无@ExcelProperty标注的多余字段时候报错。 + +# 1.0.2 + +修复拿到一行数据后,存到list中,但最后处理时候变为空的bug。 + +# 1.0.1 + +完善测试用例,防止歧义,模型字段映射不上时候有抛异常,改为提醒。 \ No newline at end of file