├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ └── ci.yaml ├── .gitignore ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── LICENSE ├── README-EN.md ├── README.md ├── docs ├── ApiDesignSampleStudy.pptx ├── How-to-Design-a-Good-API-and-Why-it-Matters-by-Joshua-Bloch.pdf ├── The-Little-Manual-of-API-Design.pdf ├── generic_io_uml.asta ├── input-output.jpg ├── interface-dependency.jpg ├── invocation-sequence.jpg └── java-api-design-exercise.md ├── in.txt ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main └── java │ └── com │ └── oldratlee │ └── io │ ├── core │ ├── Input.java │ ├── Output.java │ ├── Receiver.java │ ├── Sender.java │ └── filter │ │ ├── Filters.java │ │ ├── Function.java │ │ └── Specification.java │ └── utils │ ├── Inputs.java │ └── Outputs.java └── test └── java └── com └── oldratlee └── io ├── IoApiTest.java └── demo ├── Demo_FileTransport.java ├── Demo_Intercept_CountLine.java ├── Demo_Intercept_FilterLine.java └── Demo_PersonToFileTransport.java /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | tab_width = 2 8 | indent_size = 2 9 | ij_continuation_indent_size = 4 10 | indent_style = space 11 | trim_trailing_whitespace = true 12 | 13 | ij_markdown_wrap_text_if_long = false 14 | 15 | 16 | [*.{java,kt}] 17 | indent_size = 4 18 | ij_continuation_indent_size = 8 19 | 20 | [*.xml] 21 | indent_style = tab 22 | 23 | [*.{md,mkd,markdown}] 24 | indent_size = 4 25 | ij_continuation_indent_size = 8 26 | trim_trailing_whitespace = false 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "maven" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "github-actions" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # Quickstart for GitHub Actions 2 | # https://docs.github.com/en/actions/quickstart 3 | 4 | name: CI 5 | on: [ push, pull_request, workflow_dispatch ] 6 | jobs: 7 | test: 8 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners 9 | runs-on: ${{ matrix.os }} 10 | timeout-minutes: 5 11 | name: CI on Java ${{ matrix.java }} OS ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ ubuntu-latest, windows-latest ] 15 | java: [ 8, 11, 17, 21 ] 16 | fail-fast: false 17 | max-parallel: 64 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-java@v4 22 | with: 23 | distribution: zulu 24 | java-version: ${{ matrix.java }} 25 | cache: maven 26 | - run: ./mvnw -V --no-transfer-progress clean package 27 | # https://github.com/marketplace/actions/codecov 28 | - uses: codecov/codecov-action@v5 29 | env: 30 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tmp 2 | 3 | # maven build directory 4 | target/ 5 | 6 | # Eclipse project files 7 | .settings/ 8 | .project 9 | .classpath 10 | 11 | # IntelliJ IDEA project files 12 | .idea/ 13 | *.ipr 14 | *.iml 15 | *.iws 16 | 17 | # temporary files 18 | *.log 19 | *.log.* 20 | 21 | *.cache 22 | *.diff 23 | *.patch 24 | 25 | *.bak 26 | *.bkp 27 | *.swp 28 | ~$* 29 | 30 | # os temporary files 31 | .DS_Store 32 | Thumbs.db 33 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | https://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright [yyyy] [name of copyright owner] 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | https://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /README-EN.md: -------------------------------------------------------------------------------- 1 | # 📐 IO API Design 2 | 3 | 4 |

5 | Github Workflow Build Status 6 | Codecov 7 | Java support 8 | License 9 | GitHub Stars 10 | GitHub Forks 11 | GitHub Issues 12 | GitHub repo size 13 | gitpod: Ready to Code 14 |

15 | 16 | :book: English Documentation | [:book: 中文文档](README.md) 17 | 18 | 19 | 20 | ------------------------------ 21 | 22 | 23 | 24 | 25 | 26 | - [Function of Package](#function-of-package) 27 | - [More Info](#more-info) 28 | 29 | 30 | 31 | ------------------------------ 32 | 33 | The demo implementation of the generic io api supplied in blog [A generic input/output API in Java](https://web.archive.org/web/20130216100207/http://www.jroller.com/rickard/entry/a_generic_input_output_api) (PS: [Original blog link]((http://www.jroller.com/rickard/entry/a_generic_input_output_api) is broken...) 34 | 35 | Function of Package 36 | ------------------- 37 | 38 | - package `com.oldratlee.io.core` 39 | core interfaces 40 | - package `com.oldratlee.io.core.filter` 41 | implementation class of `Filter` 42 | - package `com.oldratlee.io.utils` 43 | utils classes 44 | - package `com.oldratlee.io.demo` 45 | `Main` classes of Demo sample 46 | 47 | More Info 48 | ------------------- 49 | 50 | - Question or Issue: https://github.com/oldratlee/io-api/issues 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 📐 `Java`的通用`IO API`设计 2 | 3 |

4 | Github Workflow Build Status 5 | Codecov 6 | Java support 7 | License 8 | GitHub Stars 9 | GitHub Forks 10 | GitHub Issues 11 | GitHub repo size 12 | gitpod: Ready to Code 13 |

14 | 15 | [:book: English Documentation](README-EN.md) | :book: 中文文档 16 | 17 | 18 | 19 | ------------------------------ 20 | 21 | 22 | 23 | 24 | 25 | - [包的功能](#%E5%8C%85%E7%9A%84%E5%8A%9F%E8%83%BD) 26 | - [更多信息](#%E6%9B%B4%E5%A4%9A%E4%BF%A1%E6%81%AF) 27 | - [API设计的进一步学习资料](#api%E8%AE%BE%E8%AE%A1%E7%9A%84%E8%BF%9B%E4%B8%80%E6%AD%A5%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99) 28 | - [简单资料](#%E7%AE%80%E5%8D%95%E8%B5%84%E6%96%99) 29 | - [系统书籍](#%E7%B3%BB%E7%BB%9F%E4%B9%A6%E7%B1%8D) 30 | 31 | 32 | 33 | ------------------------------ 34 | 35 | [Java的通用I/O API](https://github.com/oldratlee/translations/blob/master/generic-io-api-in-java-and-api-design/README.md)(by _Rickard Öberg_)中给出了一个通用`Java` `IO API`设计,并且有`API`的`Demo`代码。 36 | 37 | 更重要的是给出了这个`API`设计本身的步骤和过程,这让`API`设计有些条理。文中示范了从 普通简单实现 整理成 正交分解、可复用、可扩展、高性能和错误保真的`API`设计 的过程,这个过程是很值得理解和学习! 38 | 39 | 设计偏向是艺术,一个赏心悦目的设计,尤其是`API`设计,旁人看来多是妙手偶得的感觉,如果能有些章可循真是一件美事。 40 | 41 | 在艺术工作中,真的艺术性工作量也只是一部分,而给出 _**方法**_ 以 _**减少艺术工作之中艺术性工作量**_ 的人是 **大师**。 ❤️ 42 | 43 | 原文中只给出设计的 44 | 45 | - 发展思路 46 | - 关键接口 47 | - 典型的使用方式 48 | 49 | 没有给出可运行的实现及其连接的细节,看起来可能比较费力,因为设计细致分解后抽象度高而不容易理解。 50 | 51 | 为了大家和自己更深入有效地学习,需要: 52 | 53 | 1. 给出这个通用`IO API`的可运行的`Demo`实现。 54 | 这个工程即是本人的可运行的`Demo`实现。 55 | 当然个人力荐你先自己实现练习一下,这样比直接看我的实现,在学习上会有效得多! 56 | 1. 写了一篇分析总结。 57 | 本人的分析总结:[用Java I/O API设计练习的分析和总结](docs/java-api-design-exercise.md)。这个你可以直接看,以更高效方便地理解这个`API`的设计。 58 | 59 | > PS: 60 | > 61 | > 上面2件事其实是份自学的家庭作业哦~ :laughing: 62 | > 在阿里中间件团队的时候(2011年),[@_ShawnQianx_ 大大](http://weibo.com/shawnqianx)看到这篇文章时,给组员布置的家庭作业~ :bowtie: 63 | > 64 | > @_ShawnQianx_ 对这篇文章及作者的评论: 65 | > 66 | > 设计时,一要分解好系统,二是多个组件拼回来还是系统预期的样子,二步都做好是难度所在。这个人分析和把控的功力很好! 67 | 68 | ## 包的功能 69 | 70 | - 包`com.oldratlee.io.core` 71 | 核心接口 72 | - 包`com.oldratlee.io.core.filter` 73 | 实现的`Filter`功能的类 74 | - 包`com.oldratlee.io.utils` 75 | 工具类 76 | - 包`com.oldratlee.io.demo` 77 | Demo示例的`Main`类 78 | 79 | ## 更多信息 80 | 81 | - 个人在组内分享时的PPT:[API设计实例分析](docs/ApiDesignSampleStudy.pptx) 82 | - 本人对这篇博文的译文:[【译】Java的通用I/O API](https://github.com/oldratlee/translations/tree/master/generic-io-api-in-java-and-api-design/README.md) 83 | - 问题交流: https://github.com/oldratlee/io-api/issues 84 | 85 | ## API设计的进一步学习资料 86 | 87 | ### 简单资料 88 | 89 | - How to Design a Good API and Why it Matters(by Joshua Bloch) 【[本地下载](docs/How-to-Design-a-Good-API-and-Why-it-Matters-by-Joshua-Bloch.pdf)】 90 | 91 | - Google Search 92 | 93 | 94 | ### 系统书籍 95 | 96 | - The Little Manual of API Design 【[本地下载](docs/The-Little-Manual-of-API-Design.pdf)】 97 | 98 | - [《软件框架设计的艺术》](http://book.douban.com/subject/6003832/) | 英文原版[Practical API Design: Confessions of a Java Framework Architect](http://www.amazon.com/Practical-API-Design-Confessions-Framework/dp/1430243171) 99 | - [Contributing to Eclipse中文版](https://book.douban.com/subject/1219945/) | 英文原版[Contributing to Eclipse : Principles, Patterns, and Plug-Ins](https://book.douban.com/subject/1610318/) 100 | - [.NET设计规范 : NET约定、惯用法与模式](http://book.douban.com/subject/4805165/) | 英文原版[Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition)](http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613) 101 | -------------------------------------------------------------------------------- /docs/ApiDesignSampleStudy.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/ApiDesignSampleStudy.pptx -------------------------------------------------------------------------------- /docs/How-to-Design-a-Good-API-and-Why-it-Matters-by-Joshua-Bloch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/How-to-Design-a-Good-API-and-Why-it-Matters-by-Joshua-Bloch.pdf -------------------------------------------------------------------------------- /docs/The-Little-Manual-of-API-Design.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/The-Little-Manual-of-API-Design.pdf -------------------------------------------------------------------------------- /docs/generic_io_uml.asta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/generic_io_uml.asta -------------------------------------------------------------------------------- /docs/input-output.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/input-output.jpg -------------------------------------------------------------------------------- /docs/interface-dependency.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/interface-dependency.jpg -------------------------------------------------------------------------------- /docs/invocation-sequence.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oldratlee/io-api/286b7769867b91b547fe8975c18b2aea9c2f23bc/docs/invocation-sequence.jpg -------------------------------------------------------------------------------- /docs/java-api-design-exercise.md: -------------------------------------------------------------------------------- 1 | _转自 个人博客[通用Java I/O API设计练习总结](http://oldratlee.com/493/tech/java/java-api-design-exercise.html) 和 自己在公司Blog[通用Java I/O API设计练习总结](http://code.alibabatech.com/blog/architecture_1489/java-api-design-exercise.html)。_ 2 | 3 | 通用Java I/O API设计练习总结 4 | =============================== 5 | 6 | [Java的通用I/O API](https://github.com/oldratlee/translations/tree/master/generic-io-api-in-java-and-api-design)(by _Rickard Öberg_)中给出了一个通用Java IO API设计,并且有API的Demo代码。 7 | 8 | 博文中只给出设计的发展思路、关键接口、典型的使用方式。没有实现细节,看起来可能比较费力。 9 | \# 细致分解后的设计往往比较抽象,不容易快速理解。 10 | 11 | 做为练习,实现这个API。本工程即是实现示例 :) 12 | 13 | 这里给出我的实现和说明,节省你一些理解的时间。 14 | \# 我在组内分享时的PPT:[API设计实例分析](ApiDesignSampleStudy.pptx) 15 | 16 | 一、实现展示 17 | --------------------- 18 | 19 | ### 关键接口的依赖关系图 20 | 21 | ![依赖关系](https://user-images.githubusercontent.com/1063891/234199458-9e976bc1-10b6-464a-9c87-50ac1488de7f.jpg) 22 | 23 | ### 接口的调用序列图 24 | 25 | ![调用序列](https://user-images.githubusercontent.com/1063891/234199658-64b2189c-ea78-43d6-8692-37a25d2f1153.jpg) 26 | 27 | 图中一些约定: 28 | 29 | * 红色的方框:创建对象、清理资源 操作 30 | * 蓝色的方法:核心接口上的方法 31 | * 红色的方法:IO的操作所在的方法 32 | 33 | 注: [UML图源文件](generic_io_uml.asta),使用[Astah](http://astah.net/download#community)绘制。 34 | 35 | ### 包的功能 36 | 37 | - package com.oldratlee.io.core 38 | 核心接口 39 | - package com.oldratlee.io.core.filter 40 | 实现的Filter功能的类 41 | - package com.oldratlee.io.utils 42 | 工具类 43 | - package com.oldratlee.io.demo 44 | Demo示例的Main类 45 | 46 | 二、实现分析 47 | ---------------------- 48 | 49 | 下面列一下序列图中的一些关键信息: 50 | 51 | 1. 客户负责创建Input、Output、调用input.transferTo(output)方法。 52 | 2. Input、Output负责初始化、清理资源; 53 | 新建Sender、Receiver对象; 54 | 调用Sender、Receiver方法,驱动实例的IO操作。 55 | 56 | 再回顾一下原作者的分析: 57 | 58 | 1. 客户代码,初始化了传输,要知道输入和输出的源。 59 | 1. 从输入中读的代码。 60 | 1. 辅助代码,用于跟踪整个过程。这些代码我希望能够重用,而不管是何种传输的类型。 61 | 1. 最后这个部分是接收数据,写数据。这个代码,我要批量读写,可以在第2第4部分修改,改成一次处理多行。 62 | 63 | 三、总结 64 | ---------------------- 65 | 66 | ### API的设计过程 67 | 68 | 1. 分析整理已有的典型程序代码 69 | 注意这里不要涉及修饰性的功能,先使用最简单典型的例子。 70 | 1. 把代码按功能职责分类,描述出功能职责(以后把功能职责 简称化 功能) 71 | 1. 提取出客户的操作,这些操作不会受设计的影响,并就是API使用的入口功能。 72 | 入口上是大粒度的描述。 73 | 1. 从入口功能开始,按照功能的依赖关系,逐个整理各个功能的 74 | - 接口方法 75 | - 要依赖功能,作为接口方法上的参数,关联2个功能。 76 | - 用户看到的部分粒度变细,分解内部功能。 77 | 1. 与最终操作连接后结束API整理 78 | 这个例子中,即实际的IO操作。 79 | 80 | ### 这样设计结果 81 | 82 | 1. 分离的功能: 83 | - 资源维护 和 操作分离(IO资源维护和IO操作分离) 84 | - 核心功能 和 修饰性功能 85 | 2. 分离功能之间功能方法参数关联起来,即外围功能持有依赖功能,组合方式(非继承)。 86 | 3. 各个功能有明确的接口,且是组合的,所以可以方便Wrap以拦截加入修饰性功能。 87 | 4. 同时整理出了API(外围接口)和SPI(内部接口) 88 | 89 | 虽然总结了一下,查要照着操练,难免显得过于很有难度的!随意给出指正,讨论 :) 90 | -------------------------------------------------------------------------------- /in.txt: -------------------------------------------------------------------------------- 1 | Hello World! 2 | 3 | Hello Hell! 4 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.3.2 23 | # 24 | # Optional ENV vars 25 | # ----------------- 26 | # JAVA_HOME - location of a JDK home dir, required when download maven via java source 27 | # MVNW_REPOURL - repo url base for downloading maven distribution 28 | # MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 29 | # MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output 30 | # ---------------------------------------------------------------------------- 31 | 32 | set -euf 33 | [ "${MVNW_VERBOSE-}" != debug ] || set -x 34 | 35 | # OS specific support. 36 | native_path() { printf %s\\n "$1"; } 37 | case "$(uname)" in 38 | CYGWIN* | MINGW*) 39 | [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" 40 | native_path() { cygpath --path --windows "$1"; } 41 | ;; 42 | esac 43 | 44 | # set JAVACMD and JAVACCMD 45 | set_java_home() { 46 | # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched 47 | if [ -n "${JAVA_HOME-}" ]; then 48 | if [ -x "$JAVA_HOME/jre/sh/java" ]; then 49 | # IBM's JDK on AIX uses strange locations for the executables 50 | JAVACMD="$JAVA_HOME/jre/sh/java" 51 | JAVACCMD="$JAVA_HOME/jre/sh/javac" 52 | else 53 | JAVACMD="$JAVA_HOME/bin/java" 54 | JAVACCMD="$JAVA_HOME/bin/javac" 55 | 56 | if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then 57 | echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 58 | echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 59 | return 1 60 | fi 61 | fi 62 | else 63 | JAVACMD="$( 64 | 'set' +e 65 | 'unset' -f command 2>/dev/null 66 | 'command' -v java 67 | )" || : 68 | JAVACCMD="$( 69 | 'set' +e 70 | 'unset' -f command 2>/dev/null 71 | 'command' -v javac 72 | )" || : 73 | 74 | if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then 75 | echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 76 | return 1 77 | fi 78 | fi 79 | } 80 | 81 | # hash string like Java String::hashCode 82 | hash_string() { 83 | str="${1:-}" h=0 84 | while [ -n "$str" ]; do 85 | char="${str%"${str#?}"}" 86 | h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) 87 | str="${str#?}" 88 | done 89 | printf %x\\n $h 90 | } 91 | 92 | verbose() { :; } 93 | [ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } 94 | 95 | die() { 96 | printf %s\\n "$1" >&2 97 | exit 1 98 | } 99 | 100 | trim() { 101 | # MWRAPPER-139: 102 | # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. 103 | # Needed for removing poorly interpreted newline sequences when running in more 104 | # exotic environments such as mingw bash on Windows. 105 | printf "%s" "${1}" | tr -d '[:space:]' 106 | } 107 | 108 | # parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties 109 | while IFS="=" read -r key value; do 110 | case "${key-}" in 111 | distributionUrl) distributionUrl=$(trim "${value-}") ;; 112 | distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; 113 | esac 114 | done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" 115 | [ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" 116 | 117 | case "${distributionUrl##*/}" in 118 | maven-mvnd-*bin.*) 119 | MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ 120 | case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in 121 | *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; 122 | :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; 123 | :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; 124 | :Linux*x86_64*) distributionPlatform=linux-amd64 ;; 125 | *) 126 | echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 127 | distributionPlatform=linux-amd64 128 | ;; 129 | esac 130 | distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" 131 | ;; 132 | maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; 133 | *) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; 134 | esac 135 | 136 | # apply MVNW_REPOURL and calculate MAVEN_HOME 137 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 138 | [ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" 139 | distributionUrlName="${distributionUrl##*/}" 140 | distributionUrlNameMain="${distributionUrlName%.*}" 141 | distributionUrlNameMain="${distributionUrlNameMain%-bin}" 142 | MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" 143 | MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" 144 | 145 | exec_maven() { 146 | unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : 147 | exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" 148 | } 149 | 150 | if [ -d "$MAVEN_HOME" ]; then 151 | verbose "found existing MAVEN_HOME at $MAVEN_HOME" 152 | exec_maven "$@" 153 | fi 154 | 155 | case "${distributionUrl-}" in 156 | *?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; 157 | *) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; 158 | esac 159 | 160 | # prepare tmp dir 161 | if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then 162 | clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } 163 | trap clean HUP INT TERM EXIT 164 | else 165 | die "cannot create temp dir" 166 | fi 167 | 168 | mkdir -p -- "${MAVEN_HOME%/*}" 169 | 170 | # Download and Install Apache Maven 171 | verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 172 | verbose "Downloading from: $distributionUrl" 173 | verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 174 | 175 | # select .zip or .tar.gz 176 | if ! command -v unzip >/dev/null; then 177 | distributionUrl="${distributionUrl%.zip}.tar.gz" 178 | distributionUrlName="${distributionUrl##*/}" 179 | fi 180 | 181 | # verbose opt 182 | __MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' 183 | [ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v 184 | 185 | # normalize http auth 186 | case "${MVNW_PASSWORD:+has-password}" in 187 | '') MVNW_USERNAME='' MVNW_PASSWORD='' ;; 188 | has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; 189 | esac 190 | 191 | if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then 192 | verbose "Found wget ... using wget" 193 | wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" 194 | elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then 195 | verbose "Found curl ... using curl" 196 | curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" 197 | elif set_java_home; then 198 | verbose "Falling back to use Java to download" 199 | javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" 200 | targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" 201 | cat >"$javaSource" <<-END 202 | public class Downloader extends java.net.Authenticator 203 | { 204 | protected java.net.PasswordAuthentication getPasswordAuthentication() 205 | { 206 | return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); 207 | } 208 | public static void main( String[] args ) throws Exception 209 | { 210 | setDefault( new Downloader() ); 211 | java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); 212 | } 213 | } 214 | END 215 | # For Cygwin/MinGW, switch paths to Windows format before running javac and java 216 | verbose " - Compiling Downloader.java ..." 217 | "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" 218 | verbose " - Running Downloader.java ..." 219 | "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" 220 | fi 221 | 222 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 223 | if [ -n "${distributionSha256Sum-}" ]; then 224 | distributionSha256Result=false 225 | if [ "$MVN_CMD" = mvnd.sh ]; then 226 | echo "Checksum validation is not supported for maven-mvnd." >&2 227 | echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 228 | exit 1 229 | elif command -v sha256sum >/dev/null; then 230 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then 231 | distributionSha256Result=true 232 | fi 233 | elif command -v shasum >/dev/null; then 234 | if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then 235 | distributionSha256Result=true 236 | fi 237 | else 238 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 239 | echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 240 | exit 1 241 | fi 242 | if [ $distributionSha256Result = false ]; then 243 | echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 244 | echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 245 | exit 1 246 | fi 247 | fi 248 | 249 | # unzip and move 250 | if command -v unzip >/dev/null; then 251 | unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" 252 | else 253 | tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" 254 | fi 255 | printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" 256 | mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" 257 | 258 | clean || : 259 | exec_maven "$@" 260 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.oldratlee 6 | io-api-demo 7 | 0.0.1-SNAPSHOT 8 | ${project.artifactId} 9 | 📐 generic API design demo 10 | https://github.com/oldratlee/io-api 11 | 2012 12 | 13 | 14 | 15 | Apache 2 16 | https://www.apache.org/licenses/LICENSE-2.0.txt 17 | repo 18 | A business-friendly OSS license 19 | 20 | 21 | 22 | scm:git:git@github.com:oldratlee/io-api.git 23 | scm:git:git@github.com:oldratlee/io-api.git 24 | https://github.com/oldratlee/io-api 25 | 26 | 27 | https://github.com/oldratlee/io-api/issues 28 | GitHub Issues 29 | 30 | 31 | 32 | Jerry Lee 33 | oldratlee 34 | oldratlee(AT)gmail(DOT)com 35 | 36 | Developer 37 | 38 | +8 39 | https://github.com/oldratlee 40 | 41 | 42 | 43 | 44 | 1.8 45 | ${maven.compiler.source} 46 | UTF-8 47 | 48 | 4.9.3 49 | 3.0.2 50 | 26.0.2 51 | 52 | 53 | 5.12.2 54 | 55 | 56 | 57 | 58 | 59 | com.github.spotbugs 60 | spotbugs-annotations 61 | true 62 | 63 | 64 | com.google.code.findbugs 65 | jsr305 66 | true 67 | 68 | 69 | org.jetbrains 70 | annotations 71 | true 72 | 73 | 74 | 75 | org.junit.jupiter 76 | junit-jupiter 77 | test 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.junit 85 | junit-bom 86 | ${junit5.version} 87 | pom 88 | import 89 | 90 | 94 | 95 | 101 | com.github.spotbugs 102 | spotbugs-annotations 103 | ${spotbugs.annotations.version} 104 | true 105 | 106 | 107 | com.google.code.findbugs 108 | jsr305 109 | ${jsr305.version} 110 | true 111 | 112 | 113 | 114 | org.jetbrains 115 | annotations 116 | ${jetbrains.annotations.version} 117 | true 118 | 119 | 120 | 121 | 122 | 123 | 124 | 129 | 130 | org.apache.maven.plugins 131 | maven-enforcer-plugin 132 | 133 | 134 | enforce-maven 135 | 136 | enforce 137 | 138 | 139 | 140 | 141 | ${maven.compiler.source} 142 | 143 | 144 | 3.3.9 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-clean-plugin 157 | 3.4.1 158 | 159 | 160 | org.apache.maven.plugins 161 | maven-resources-plugin 162 | 3.3.1 163 | 164 | 165 | org.apache.maven.plugins 166 | maven-compiler-plugin 167 | 3.14.0 168 | 169 | 170 | 171 | -Xlint:unchecked 172 | -J-Duser.language=en_us 173 | 174 | 175 | 176 | 177 | org.apache.maven.plugins 178 | maven-surefire-plugin 179 | 3.5.3 180 | 181 | 182 | org.apache.maven.plugins 183 | maven-jar-plugin 184 | 3.4.2 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-source-plugin 189 | 3.3.1 190 | 191 | 192 | org.apache.maven.plugins 193 | maven-javadoc-plugin 194 | 3.11.2 195 | 196 | 197 | org.apache.maven.plugins 198 | maven-gpg-plugin 199 | 3.2.7 200 | 201 | 202 | org.apache.maven.plugins 203 | maven-site-plugin 204 | 3.21.0 205 | 206 | 207 | org.apache.maven.plugins 208 | maven-install-plugin 209 | 3.1.4 210 | 211 | 212 | org.apache.maven.plugins 213 | maven-deploy-plugin 214 | 3.1.4 215 | 216 | 217 | org.apache.maven.plugins 218 | maven-enforcer-plugin 219 | 3.5.0 220 | 221 | 222 | com.github.spotbugs 223 | spotbugs-maven-plugin 224 | 4.9.3.0 225 | 226 | 227 | org.jacoco 228 | jacoco-maven-plugin 229 | 0.8.13 230 | 231 | 232 | 233 | 234 | 235 | 236 | gen-src 237 | 238 | 239 | performRelease 240 | true 241 | 242 | 243 | 244 | 245 | 246 | org.apache.maven.plugins 247 | maven-source-plugin 248 | 249 | 250 | 251 | 252 | 253 | gen-api-doc 254 | 255 | 256 | performRelease 257 | true 258 | 259 | 260 | 261 | 262 | *.internal:*.internal.*:*.internal.*.*:*.internal.*.*.*:*.internal.*.*.*.* 263 | 264 | 265 | 266 | 267 | 268 | 272 | org.apache.maven.plugins 273 | maven-javadoc-plugin 274 | 275 | 8 276 | protected 277 | UTF-8 278 | UTF-8 279 | UTF-8 280 | true 281 | 285 | all,-missing 286 | 287 | ${javadoc.default.exclude.packages} 288 | 289 | -J-Duser.language=en 290 | -J-Duser.country=US 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | gen-sign 299 | 300 | 301 | performRelease 302 | true 303 | 304 | 305 | 306 | 307 | 308 | org.apache.maven.plugins 309 | maven-gpg-plugin 310 | 311 | 312 | sign-artifacts 313 | verify 314 | 315 | sign 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | lint 325 | 326 | 327 | performRelease 328 | true 329 | 330 | 331 | 332 | 333 | 334 | 345 | com.github.spotbugs 346 | spotbugs-maven-plugin 347 | 348 | 349 | verify 350 | 351 | check 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | gen-git-properties 361 | 362 | 363 | performRelease 364 | true 365 | 366 | 367 | 368 | 369 | 374 | 375 | io.github.git-commit-id 376 | git-commit-id-maven-plugin 377 | 9.0.2 378 | 379 | 380 | get-the-git-infos 381 | 382 | revision 383 | 384 | 385 | 386 | validate-the-git-infos 387 | 388 | validateRevision 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | validating git dirty 397 | ${git.dirty} 398 | false 399 | 400 | 401 | true 402 | 403 | ${project.build.outputDirectory}/META-INF/scm/${project.groupId}/${project.artifactId}/git.properties 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | gen-code-cov 412 | 413 | 414 | 415 | env.CI 416 | true 417 | 418 | 419 | 420 | 421 | 422 | 427 | org.jacoco 428 | jacoco-maven-plugin 429 | 430 | 431 | 432 | prepare-agent 433 | 434 | 435 | 436 | true 437 | 438 | 439 | 440 | report 441 | test 442 | 443 | report 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/Input.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core; 2 | 3 | /** 4 | * Input. 5 | * 6 | * @param data type 7 | * @param Exception when send data 8 | */ 9 | @FunctionalInterface 10 | public interface Input { 11 | 12 | void transferTo(Output output) throws SenderThrowableType, ReceiverThrowableType; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/Output.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core; 2 | 3 | /** 4 | * Output. 5 | * 6 | * @param data type 7 | * @param Exception when receive data 8 | */ 9 | @FunctionalInterface 10 | public interface Output { 11 | 12 | void receiveFrom(Sender sender) throws ReceiverThrowableType, SenderThrowableType; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/Receiver.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core; 2 | 3 | /** 4 | * @param data type 5 | * @param Exception when receive data 6 | */ 7 | public interface Receiver { 8 | void receive(T item) throws ReceiverThrowableType; 9 | 10 | void finished() throws ReceiverThrowableType; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/Sender.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core; 2 | 3 | /** 4 | * @param data type 5 | * @param Exception when send data 6 | */ 7 | @FunctionalInterface 8 | public interface Sender { 9 | /** 10 | * @param Exception when receive data 11 | */ 12 | 13 | void sendTo(Receiver receiver) throws ReceiverThrowableType, SenderThrowableType; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/filter/Filters.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core.filter; 2 | 3 | import com.oldratlee.io.core.Output; 4 | import com.oldratlee.io.core.Receiver; 5 | import com.oldratlee.io.core.Sender; 6 | 7 | /** 8 | * @author oldratlee 9 | */ 10 | public class Filters { 11 | static class SpecificationOutputWrapper 12 | implements Output { 13 | 14 | final Output output; 15 | final Specification specification; 16 | 17 | public SpecificationOutputWrapper(Output output, 18 | Specification specification) { 19 | this.output = output; 20 | this.specification = specification; 21 | } 22 | 23 | @Override 24 | public 25 | void receiveFrom(Sender sender) throws ReceiverThrowableType, SenderThrowableType { 26 | output.receiveFrom(new SpecificationSenderWrapper<>(sender, specification)); 27 | } 28 | } 29 | 30 | static class SpecificationSenderWrapper 31 | implements Sender { 32 | 33 | final Sender sender; 34 | final Specification specification; 35 | 36 | public SpecificationSenderWrapper(Sender sender, 37 | Specification specification) { 38 | this.sender = sender; 39 | this.specification = specification; 40 | } 41 | 42 | @Override 43 | public 44 | void sendTo(Receiver receiver) 45 | throws ReceiverThrowableType, SenderThrowableType { 46 | sender.sendTo(new SpecificationReceiverWrapper(receiver, specification)); 47 | } 48 | } 49 | 50 | static class SpecificationReceiverWrapper 51 | implements Receiver { 52 | 53 | final Receiver receiver; 54 | final Specification specification; 55 | 56 | public SpecificationReceiverWrapper(Receiver receiver, 57 | Specification specification) { 58 | this.receiver = receiver; 59 | this.specification = specification; 60 | } 61 | 62 | @Override 63 | public void receive(T item) throws ReceiverThrowableType { 64 | if (specification.test(item)) { 65 | receiver.receive(item); 66 | } 67 | } 68 | 69 | @Override 70 | public void finished() throws ReceiverThrowableType { 71 | receiver.finished(); 72 | } 73 | } 74 | 75 | public static 76 | Output filter(Specification specification, 77 | Output output) { 78 | return new SpecificationOutputWrapper<>(output, specification); 79 | } 80 | 81 | 82 | static class FunctionOutputWrapper 83 | implements Output { 84 | 85 | final Output output; 86 | final Function function; 87 | 88 | public FunctionOutputWrapper(Output output, 89 | Function function) { 90 | this.output = output; 91 | this.function = function; 92 | } 93 | 94 | @Override 95 | public void receiveFrom(Sender sender) 96 | throws ReceiverThrowableType, SenderThrowableType { 97 | output.receiveFrom(new FunctionSenderWrapper<>(sender, function)); 98 | } 99 | } 100 | 101 | static class FunctionSenderWrapper 102 | implements Sender { 103 | final Sender sender; 104 | final Function function; 105 | 106 | public FunctionSenderWrapper(Sender sender, 107 | Function function) { 108 | this.sender = sender; 109 | this.function = function; 110 | } 111 | 112 | @Override 113 | public 114 | void sendTo(Receiver receiver) 115 | throws ReceiverThrowableType, SenderThrowableType { 116 | sender.sendTo(new FunctionReceiverWrapper(receiver, function)); 117 | } 118 | } 119 | 120 | static class FunctionReceiverWrapper 121 | implements Receiver { 122 | 123 | final Receiver receiver; 124 | final Function function; 125 | 126 | public FunctionReceiverWrapper(Receiver receiver, 127 | Function function) { 128 | this.receiver = receiver; 129 | this.function = function; 130 | } 131 | 132 | @Override 133 | public void receive(From item) throws ReceiverThrowableType { 134 | receiver.receive(function.map(item)); 135 | } 136 | 137 | @Override 138 | public void finished() throws ReceiverThrowableType { 139 | receiver.finished(); 140 | } 141 | } 142 | 143 | public static 144 | Output filter(Function function, 145 | final Output output) { 146 | return new FunctionOutputWrapper<>(output, function); 147 | } 148 | 149 | private Filters() { 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/filter/Function.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core.filter; 2 | 3 | /** 4 | * transform from one type to another. 5 | * 6 | * @param Input data type 7 | * @param Output data type 8 | * @author oldratlee 9 | */ 10 | @FunctionalInterface 11 | public interface Function { 12 | /** 13 | * @return return the transformed data. {@code null} to indicate ignore the input data. 14 | */ 15 | To map(From from); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/core/filter/Specification.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.core.filter; 2 | 3 | /** 4 | * Check data. 5 | * 6 | * @param data type 7 | * @author oldratlee 8 | */ 9 | @FunctionalInterface 10 | public interface Specification { 11 | boolean test(T item); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/utils/Inputs.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.utils; 2 | 3 | import com.oldratlee.io.core.Input; 4 | import com.oldratlee.io.core.Output; 5 | import com.oldratlee.io.core.Receiver; 6 | import com.oldratlee.io.core.Sender; 7 | 8 | import java.io.*; 9 | 10 | /** 11 | * Utils of {@link Input}. 12 | * 13 | * @author oldratlee 14 | */ 15 | public class Inputs { 16 | 17 | static class TextInput implements Input { 18 | private final Reader reader; 19 | 20 | public TextInput(File source) throws IOException { 21 | reader = new FileReader(source); 22 | } 23 | 24 | @Override 25 | public 26 | void transferTo(Output output) throws IOException, ReceiverThrowableType { 27 | final TextSender sender = new TextSender(reader); 28 | output.receiveFrom(sender); 29 | 30 | try { 31 | reader.close(); 32 | } catch (Exception e) { 33 | // ignore close exception :) 34 | } 35 | } 36 | 37 | } 38 | 39 | static class TextSender implements Sender { 40 | private final BufferedReader bufferReader; 41 | 42 | public TextSender(Reader reader) { 43 | this.bufferReader = new BufferedReader(reader); 44 | } 45 | 46 | @Override 47 | public 48 | void sendTo(Receiver receiver) 49 | throws ReceiverThrowableType, IOException { 50 | String readLine; 51 | while ((readLine = bufferReader.readLine()) != null) { 52 | receiver.receive(readLine + "\n"); 53 | } 54 | } 55 | } 56 | 57 | public static Input text(File source) throws IOException { 58 | return new TextInput(source); 59 | } 60 | 61 | private Inputs() { 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/oldratlee/io/utils/Outputs.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.utils; 2 | 3 | import com.oldratlee.io.core.Output; 4 | import com.oldratlee.io.core.Receiver; 5 | import com.oldratlee.io.core.Sender; 6 | 7 | import java.io.File; 8 | import java.io.FileWriter; 9 | import java.io.IOException; 10 | import java.io.Writer; 11 | 12 | /** 13 | * Utils of {@link Output}. 14 | * 15 | * @author oldratlee 16 | */ 17 | public class Outputs { 18 | static class TextOutput implements Output { 19 | private final Writer writer; 20 | 21 | public TextOutput(File destination) throws IOException { 22 | writer = new FileWriter(destination); 23 | } 24 | 25 | @Override 26 | public void receiveFrom(Sender sender) 27 | throws IOException, SenderThrowableType { 28 | final TextFileReceiver receiver = new TextFileReceiver(writer); 29 | sender.sendTo(receiver); 30 | receiver.finished(); 31 | 32 | try { 33 | writer.close(); 34 | } catch (Exception e) { 35 | // ignore close exception 36 | } 37 | } 38 | } 39 | 40 | static class TextFileReceiver implements 41 | Receiver { 42 | final Writer writer; 43 | 44 | public TextFileReceiver(Writer writer) { 45 | this.writer = writer; 46 | } 47 | 48 | @Override 49 | public void receive(String item) throws IOException { 50 | writer.write(item); 51 | } 52 | 53 | @Override 54 | public void finished() { 55 | } 56 | } 57 | 58 | public static Output text(File destination) throws IOException { 59 | return new TextOutput(destination); 60 | } 61 | 62 | private Outputs() { 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/com/oldratlee/io/IoApiTest.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io; 2 | 3 | import com.oldratlee.io.demo.Demo_FileTransport; 4 | import com.oldratlee.io.demo.Demo_Intercept_CountLine; 5 | import com.oldratlee.io.demo.Demo_Intercept_FilterLine; 6 | import com.oldratlee.io.demo.Demo_PersonToFileTransport; 7 | import org.junit.jupiter.api.Test; 8 | 9 | class IoApiTest { 10 | @Test 11 | void helloWorld() throws Exception { 12 | Demo_FileTransport.main(new String[0]); 13 | Demo_PersonToFileTransport.main(new String[0]); 14 | 15 | Demo_Intercept_CountLine.main(new String[0]); 16 | Demo_Intercept_FilterLine.main(new String[0]); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/com/oldratlee/io/demo/Demo_FileTransport.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.demo; 2 | 3 | import com.oldratlee.io.utils.Inputs; 4 | import com.oldratlee.io.utils.Outputs; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | 10 | /** 11 | * @author oldratlee 12 | */ 13 | public class Demo_FileTransport { 14 | public static void main(String[] args) throws IOException { 15 | File source = new File("in.txt"); 16 | File destination = new File("out.tmp"); 17 | 18 | Inputs.text(source).transferTo(Outputs.text(destination)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/com/oldratlee/io/demo/Demo_Intercept_CountLine.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.demo; 2 | 3 | import com.oldratlee.io.core.Input; 4 | import com.oldratlee.io.core.Output; 5 | import com.oldratlee.io.core.filter.Filters; 6 | import com.oldratlee.io.core.filter.Function; 7 | import com.oldratlee.io.utils.Inputs; 8 | import com.oldratlee.io.utils.Outputs; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | 14 | /** 15 | * @author oldratlee 16 | */ 17 | public class Demo_Intercept_CountLine { 18 | public static void main(String[] args) throws IOException { 19 | File source = new File("in.txt"); 20 | File destination = new File("out.tmp"); 21 | final AtomicInteger count = new AtomicInteger(); 22 | 23 | Input input = Inputs.text(source); 24 | 25 | Output output = Outputs.text(destination); 26 | 27 | Function function = from -> { 28 | count.incrementAndGet(); 29 | return from; 30 | }; 31 | 32 | input.transferTo(Filters.filter(function, output)); 33 | 34 | System.out.println("Counter: " + count.get()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/com/oldratlee/io/demo/Demo_Intercept_FilterLine.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.demo; 2 | 3 | import com.oldratlee.io.core.Input; 4 | import com.oldratlee.io.core.Output; 5 | import com.oldratlee.io.core.filter.Filters; 6 | import com.oldratlee.io.core.filter.Specification; 7 | import com.oldratlee.io.utils.Inputs; 8 | import com.oldratlee.io.utils.Outputs; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | 13 | /** 14 | * @author oldratlee 15 | */ 16 | public class Demo_Intercept_FilterLine { 17 | public static void main(String[] args) throws IOException { 18 | File source = new File("in.txt"); 19 | File destination = new File("out.tmp"); 20 | 21 | Input input = Inputs.text(source); 22 | 23 | Output output = Outputs.text(destination); 24 | 25 | // 过滤空行 26 | Specification specification = item -> !item.trim().isEmpty(); 27 | 28 | input.transferTo(Filters.filter(specification, output)); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/oldratlee/io/demo/Demo_PersonToFileTransport.java: -------------------------------------------------------------------------------- 1 | package com.oldratlee.io.demo; 2 | 3 | import com.oldratlee.io.core.Input; 4 | import com.oldratlee.io.core.Output; 5 | import com.oldratlee.io.core.Receiver; 6 | import com.oldratlee.io.core.Sender; 7 | import com.oldratlee.io.utils.Outputs; 8 | 9 | import java.io.File; 10 | import java.io.IOException; 11 | 12 | /** 13 | * @author ding.lid 14 | */ 15 | public class Demo_PersonToFileTransport { 16 | static class Person { 17 | private final String name; 18 | private final int age; 19 | 20 | Person(String name, int age) { 21 | this.name = name; 22 | this.age = age; 23 | } 24 | } 25 | 26 | static class PersonTextInput implements Input { 27 | private final Person person; 28 | 29 | PersonTextInput(Person person) { 30 | this.person = person; 31 | } 32 | 33 | @Override 34 | public void transferTo(Output output) 35 | throws IOException, ReceiverThrowableType { 36 | final PersonTextSender sender = new PersonTextSender(person); 37 | output.receiveFrom(sender); 38 | } 39 | 40 | } 41 | 42 | static class PersonTextSender implements Sender { 43 | private static final String LINE_SEPARATOR = System.getProperty("line.separator"); 44 | 45 | private final Person person; 46 | 47 | PersonTextSender(Person person) { 48 | this.person = person; 49 | } 50 | 51 | @Override 52 | public void sendTo(Receiver receiver) 53 | throws ReceiverThrowableType { 54 | receiver.receive("name: " + person.name + LINE_SEPARATOR); 55 | receiver.receive("age: " + person.age + LINE_SEPARATOR); 56 | } 57 | } 58 | 59 | public static void main(String[] args) throws Exception { 60 | Person person = new Person("Jerry", 42); 61 | File destination = new File("out.tmp"); 62 | 63 | new PersonTextInput(person).transferTo(Outputs.text(destination)); 64 | } 65 | } 66 | --------------------------------------------------------------------------------