├── .gitignore ├── BUILD.md ├── CHANGES.md ├── LICENSE ├── README.md ├── SECURITY.md ├── VERSION_PREFIX ├── bin ├── classpath └── roundtrip ├── build.clj ├── build ├── doc ├── package ├── release └── revision ├── deps.edn ├── pom.xml └── src ├── main └── java │ └── com │ └── cognitect │ └── transit │ ├── ArrayReadHandler.java │ ├── ArrayReader.java │ ├── DefaultReadHandler.java │ ├── Keyword.java │ ├── Link.java │ ├── MapReadHandler.java │ ├── MapReader.java │ ├── Named.java │ ├── Ratio.java │ ├── ReadHandler.java │ ├── Reader.java │ ├── SPI │ └── ReaderSPI.java │ ├── Symbol.java │ ├── TaggedValue.java │ ├── TransitFactory.java │ ├── URI.java │ ├── WriteHandler.java │ ├── Writer.java │ └── impl │ ├── AbstractEmitter.java │ ├── AbstractParser.java │ ├── AbstractWriteHandler.java │ ├── Cache.java │ ├── Constants.java │ ├── Emitter.java │ ├── JsonEmitter.java │ ├── JsonParser.java │ ├── JsonVerboseEmitter.java │ ├── KeywordImpl.java │ ├── LinkImpl.java │ ├── ListBuilderImpl.java │ ├── MapBuilderImpl.java │ ├── MsgpackEmitter.java │ ├── MsgpackParser.java │ ├── Parser.java │ ├── Quote.java │ ├── RatioImpl.java │ ├── ReadCache.java │ ├── ReadHandlerMap.java │ ├── ReadHandlers.java │ ├── ReaderFactory.java │ ├── SymbolImpl.java │ ├── Tag.java │ ├── TagFinder.java │ ├── TagProvider.java │ ├── TagProviderAware.java │ ├── TaggedValueImpl.java │ ├── URIImpl.java │ ├── Util.java │ ├── WriteCache.java │ ├── WriteHandlerMap.java │ ├── WriteHandlers.java │ └── WriterFactory.java └── test └── java └── com └── cognitect └── transit ├── TestRoundtrip.java ├── TransitJSONMachineModeTest.java ├── TransitMPTest.java └── TransitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Package Files # 4 | *.jar 5 | *.war 6 | *.ear 7 | 8 | *.iml 9 | .idea/ 10 | target/ 11 | design.org 12 | 13 | *~ 14 | 15 | .cpcache 16 | .java-version 17 | -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | ## Build (internal) 2 | 3 | ### Version 4 | 5 | The build version is automatically incremented. To determine the 6 | current build version: 7 | 8 | build/version 9 | 10 | ### Package 11 | 12 | Packaging builds the maven artifacts in `target/package` and installs 13 | it into the local maven repository. To package: 14 | 15 | build/package 16 | 17 | ### Deploy 18 | 19 | Deployment requires that the AWS CLI tools be installed (see 20 | https://aws.amazon.com/cli/). 21 | 22 | The deploy script runs the package script, and then deploys the 23 | artifacts to the S3 bucket "mrel". To deploy: 24 | 25 | build/release 26 | 27 | ### Docs 28 | 29 | To build and deploy api documentation: 30 | 31 | build/doc 32 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # transit-java 1.0 2 | 3 | ### 1.0.371 / 2023-03-31 4 | 5 | * Update jackson-core to 2.14.2 6 | * Update jaxb-api to 2.4.0-b180830.0359 7 | 8 | ### 1.0.366 / 2023-03-31 9 | 10 | * Fix for https://github.com/cognitect/transit-clj/issues/37 - error reading cmap with null key 11 | 12 | ### 1.0.362 / 2022-02-04 13 | 14 | * Fix for https://github.com/cognitect/transit-clj/issues/47 - interpose a transformer on tag resolution when present. This handles the case where a transformer may convert an object into a form that the marhsaling phase may not be able to handle. This bifurcation between the classifying tag and the marshaled form was specifically causing issues in transit-clj where symbol map keys with metadata did not marshal into a stringable form but were classified as stringable earlier in the write phase. 15 | * Remove dependency on org.apache.commons.codec, use java.util.Base64 16 | * Other minor dep version updates 17 | 18 | ### 1.0.343 / 2020-02-18 19 | 20 | * No changes, just version 21 | 22 | ### 0.8.337 / 2018-08-17 23 | 24 | * Add explicit jaxb-api dependency for Java 9+ (issue #28) 25 | 26 | ### 0.8.332 / 2018-03-30 27 | 28 | * Add support for transform function in writer 29 | * Compile to Java 1.8 target (previously 1.6) 30 | 31 | ### 0.8.327 / 2017-03-31 32 | 33 | * Add support for defaultWriteHandler in TransitFactory.writer() 34 | 35 | ### 0.8.324 / 2017-03-13 36 | 37 | * Update jackson-core dependency from 2.3.2 to 2.8.7 38 | 39 | ### 0.8.319 / 2016-11-18 40 | 41 | * Fix double check locking of non-thread-safe handler caches 42 | 43 | ### 0.8.316 / 2016-09-30 44 | 45 | * WriteHandler thread safety improvements 46 | 47 | ### 0.8.313 / 2016-07-27 48 | 49 | * Updated msgpack dependency to 0.6.12 50 | 51 | ### 0.8.311 / 2015-10-22 52 | 53 | * Simplified commons-codec dependency to be more direct 54 | * Simplified jackson-core dependency to reduce transitive dependencies (#11) 55 | 56 | ### 0.8.307 / 2015-10-09 57 | 58 | * Fixed #15: Wrap SimpleDateFormat in ThreadLocal 59 | 60 | ### 0.8.304 / 2015-08-07 61 | 62 | * ReadHandlerMap and WriteHandlerMap 63 | 64 | ### 0.8.285 / 2015-03-13 65 | 66 | * cache read and write handlers 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # transit-java 2 | 3 | Transit is a data format and a set of libraries for conveying values between applications written in different languages. This library provides support for marshalling Transit data to/from Java. 4 | 5 | * [Rationale](https://blog.cognitect.com/blog/2014/7/22/transit) 6 | * [API docs](https://cognitect.github.io/transit-java/) 7 | * [Specification](https://github.com/cognitect/transit-format) 8 | 9 | This implementation's major.minor version number corresponds to the version of the Transit specification it supports. 10 | 11 | _NOTE: Transit is intended primarily as a wire protocol for transferring data between applications. If storing Transit data durably, readers and writers are expected to use the same version of Transit and you are responsible for migrating/transforming/re-storing that data when and if the transit format changes._ 12 | 13 | ## Releases and Dependency Information 14 | 15 | * Latest release: 1.0.371 16 | * [All Released Versions](https://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.cognitect%22%20AND%20a%3A%22transit-java%22) 17 | 18 | [Maven](https://maven.apache.org/) dependency information: 19 | 20 | ```xml 21 | 22 | com.cognitect 23 | transit-java 24 | 1.0.371 25 | 26 | ``` 27 | 28 | ## Usage 29 | 30 | ```java 31 | import java.io.ByteArrayOutputStream; 32 | import java.io.ByteArrayInputStream; 33 | import com.cognitect.transit.TransitFactory; 34 | import com.cognitect.transit.Reader; 35 | import com.cognitect.transit.Writer; 36 | 37 | // Write the data to a stream 38 | OutputStream out = new ByteArrayOutputStream(); 39 | Writer writer = TransitFactory.writer(TransitFactory.Format.MSGPACK, out); 40 | writer.write(data); 41 | 42 | // Read the data from a stream 43 | InputStream in = new ByteArrayInputStream(out.toByteArray()); 44 | Reader reader = TransitFactory.reader(TransitFactory.Format.MSGPACK, in); 45 | Object data = reader.read(); 46 | ``` 47 | 48 | ### Custom write handler 49 | 50 | ```java 51 | public class Point { 52 | public final int x; 53 | public final int y; 54 | public Point(int x,int y) {this.x = x; this.y = y;} 55 | public String toString() { return "Point at " + x + ", " + y; } 56 | public boolean equals(Object other) { return other instanceof Point && 57 | ((Point)other).x == x && 58 | ((Point)other).y == y; } 59 | public int hashCode() { return x * y; } 60 | } 61 | 62 | Map> customHandlers = new HashMap>(){{ 63 | put(Point.class, new WriteHandler() { 64 | @Override 65 | public String tag(Object o) { return "point"; } 66 | @Override 67 | public Object rep(Object o) { return Arrays.asList(((Point)o).x, ((Point)o).y); } 68 | @Override 69 | public String stringRep(Object o) { return rep(o).toString(); } 70 | @Override 71 | public WriteHandler getVerboseHandler() { return this; } 72 | }); 73 | }}; 74 | OutputStream out = new ByteArrayOutputStream(); 75 | Writer w = TransitFactory.writer(TransitFactory.Format.JSON, out, TransitFactory.writeHandlerMap(customHandlers)); 76 | w.write(new Point(37, 42)); 77 | System.out.print(out.toString()); 78 | ;; => ["~#point",[37,42]] 79 | ``` 80 | 81 | ### Custom read handler 82 | 83 | ```java 84 | Map> customHandlers = new HashMap>() {{ 85 | put("point", new ReadHandler() { 86 | @Override 87 | public Object fromRep(Object o) { 88 | List coords = (List) o; 89 | int x = ((Long) coords.get(0)).intValue(); 90 | int y = ((Long) coords.get(1)).intValue(); 91 | return new Point(x,y); 92 | } 93 | }); 94 | }}; 95 | InputStream in = new ByteArrayInputStream("[\"~#point\",[37,42]]".getBytes()); 96 | Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, in, customHandlers); 97 | System.out.print(reader.read()); 98 | // => Point at 37, 42 99 | ``` 100 | 101 | ### Custom default write handler 102 | 103 | ```java 104 | WriteHandler customDefaultWriteHandler = new WriteHandler() { 105 | @Override 106 | public String tag(Object o) { return "unknown"; } 107 | @Override 108 | public Object rep(Object o) { return o.toString(); } 109 | @Override 110 | public String stringRep(Object o) { return o.toString(); } 111 | @Override 112 | public WriteHandler getVerboseHandler() { return this; } 113 | }; 114 | OutputStream out = new ByteArrayOutputStream(); 115 | Writer w = TransitFactory.writer(TransitFactory.Format.JSON, out, customDefaultWriteHandler); 116 | w.write(new Point(37,42)); 117 | System.out.print(out.toString()); 118 | // => "[\"~#unknown\",\"Point at 37, 42\"]" 119 | ``` 120 | 121 | ### Custom default read handler 122 | 123 | ```java 124 | DefaultReadHandler readHandler = new DefaultReadHandler() { 125 | @Override 126 | public Object fromRep(String tag, Object rep) { 127 | return tag + ": " + rep.toString(); 128 | } 129 | }; 130 | InputStream in = new ByteArrayInputStream("[\"~#unknown\",[37,42]]".getBytes()); 131 | Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, in, readHandler); 132 | System.out.print(reader.read()); 133 | // => unknown: [37, 42] 134 | ``` 135 | 136 | ## Default Type Mapping 137 | 138 | |Transit type|Write accepts|Read returns| 139 | |------------|-------------|------------| 140 | |null|null|null| 141 | |string|java.lang.String|java.lang.String| 142 | |boolean|java.lang.Boolean|java.lang.Boolean| 143 | |integer|java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long|java.lang.Long| 144 | |decimal|java.lang.Float, java.lang.Double|java.lang.Double| 145 | |keyword|cognitect.transit.Keyword|cognitect.transit.Keyword| 146 | |symbol|cognitect.transit.Symbol|cognitect.transit.Symbol| 147 | |big decimal|java.math.BigDecimal|java.math.BigDecimal| 148 | |big integer|java.math.BigInteger|java.math.BigInteger| 149 | |time|java.util.Date|long| 150 | |uri|java.net.URI, cognitect.transit.URI|cognitect.transit.URI| 151 | |uuid|java.util.UUID|java.util.UUID| 152 | |char|java.lang.Character|java.lang.Character| 153 | |array|Object[],primitive arrays|java.util.ArrayList| 154 | |list|java.util.List|java.util.LinkedList| 155 | |set|java.util.Set|java.util.HashSet| 156 | |map|java.util.Map|java.util.HashMap| 157 | |link|cognitect.transit.Link|cognitect.transit.Link| 158 | |ratio +|cognitect.transit.Ratio|cognitect.transit.Ratio| 159 | 160 | \+ Extension type 161 | 162 | ## Layered Implementations 163 | 164 | This library is specifically designed to support layering Transit 165 | implementations for other JVM-based languages on top of it. There are 166 | three steps to implementing a library for a new language on top of 167 | this: 168 | 169 | - Implement WriteHandlers and ReadHandlers specific for the target 170 | language. Typically, WriteHandlers will be used _in addition to_ the 171 | ones provided by the Java library (see 172 | TransitFactory.defaultWriteHandlers). ReadHandlers will be used _in 173 | place of_ some of the ones provided by the Java Libary (see 174 | TransitFactory.defaultReadHandlers). 175 | 176 | - Implement a factory API to create Readers and Writers. In general, 177 | Readers and Writers encapsulate the stream they work with. The APIs 178 | should enable an application to provide custom WriteHandlers and 179 | ReadHandlers, which get merged with the ones defined by the new 180 | library as well as the defaults provided by the Java library. The 181 | Reader API should also provide a way to specify a default behavior 182 | if no ReadHandler is available for a specific Transit value (see 183 | com.cognitect.transit.DefaultReadHandler). The factory API should 184 | delegate to TransitFactory to create Readers and Writers with the 185 | correct options. 186 | 187 | - Implement a MapReader and an ArrayReader for unmarshaling these 188 | Transit ground types into objects appropriate for the target 189 | language. In the factory API for creating Readers, use each new Reader's 190 | com.cognitect.transit.SPI.ReaderSPI interface to attach instances 191 | of the new library's custom MapReader and ArrayReader 192 | implementations to a Reader before returning it. This must be done 193 | before the Reader instance is used to read data. 194 | 195 | N.B. The ReaderSPI interface is in an impl package because it is only 196 | intended to be used by layered Transit libraries, not by 197 | applications using Transit. 198 | 199 | The [Clojure Transit library](https://github.com/cognitect/transit-clj) 200 | is implemented using this layering approach and can be used as an 201 | example of how to implement support for additional JVM languages 202 | without having to implement all of Transit from scratch. 203 | 204 | ## Contributing 205 | 206 | This library is open source, developed internally by Cognitect. We welcome discussions of potential problems and enhancement suggestions on the [transit-format mailing list](https://groups.google.com/forum/#!forum/transit-format). Issues can be filed using GitHub [issues](https://github.com/cognitect/transit-java/issues) for this project. Because transit is incorporated into products and client projects, we prefer to do development internally and are not accepting pull requests or patches. 207 | 208 | ## Copyright and License 209 | 210 | Copyright © 2014 Cognitect 211 | 212 | Licensed under the Apache License, Version 2.0 (the "License"); 213 | you may not use this file except in compliance with the License. 214 | You may obtain a copy of the License at 215 | 216 | https://www.apache.org/licenses/LICENSE-2.0 217 | 218 | Unless required by applicable law or agreed to in writing, software 219 | distributed under the License is distributed on an "AS IS" BASIS, 220 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 221 | See the License for the specific language governing permissions and 222 | limitations under the License. 223 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy for Nubank Open Source Projects 2 | 3 | ## Supported Versions 4 | 5 | Nubank supports the latest version of each of our open-source projects. Once a new version is released, we stop providing patches for security issues in older versions. 6 | 7 | ## Reporting Security Issues 8 | 9 | Your efforts to responsibly disclose your findings are sincerely appreciated and will be taken into account to acknowledge your contributions. 10 | If you discover a vulnerability, please do the following: 11 | 12 | 1. E-mail your findings to [security@nubank.com.br](mailto:security@nubank.com.br). If the issue is sensitive, please use [our PGP key](https://nubank.com.br/.well-known/security.txt) to encrypt your communications with us. 13 | 2. Do not take advantage of the vulnerability or problem you have discovered. 14 | 3. Do not reveal the problem to others until it has been resolved. 15 | 4. Provide sufficient information to reproduce the problem so we can resolve it as quickly as possible. 16 | 5. You will receive a response from us acknowledging receipt of your vulnerability report. 17 | 6. You'll receive regular updates about our progress. 18 | 7. Once the issue is resolved, we would like to mention your name in any dispatches about the issue so that we can credit you for your discovery. Please engage in responsible privacy practices when disclosing bugs to us, and we'll handle each report with utmost urgency. 19 | 20 | ## Preferred Languages 21 | 22 | We prefer all communications to be in English. 23 | 24 | ## Policy Adherence 25 | 26 | We greatly value your assistance in discovering and reporting vulnerabilities, and look forward to working with users who wish to help ensure Nubank's open-source projects' safety and security. Thank you for supporting Nubank's mission and helping ensure the highest levels of security for our community! 27 | -------------------------------------------------------------------------------- /VERSION_PREFIX: -------------------------------------------------------------------------------- 1 | 1.0 2 | -------------------------------------------------------------------------------- /bin/classpath: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | cd `dirname $0`/.. 6 | 7 | mvn dependency:build-classpath | awk '/\[INFO\] Dependencies classpath:/{flag=1;next}/\[INFO\] --/{flag=0}flag{print}' 8 | -------------------------------------------------------------------------------- /bin/roundtrip: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0`/.. 4 | 5 | exec java -Xmx1g -server -cp `bin/classpath`:target/classes:target/test-classes com.cognitect.transit.TestRoundtrip "$@" 6 | -------------------------------------------------------------------------------- /build.clj: -------------------------------------------------------------------------------- 1 | (ns build 2 | (:refer-clojure :exclude [compile]) 3 | (:require [clojure.tools.build.api :as b])) 4 | 5 | (def basis (b/create-basis nil)) 6 | (def lib 'com.cognitect/transit-java) 7 | (def version (str (slurp "VERSION_PREFIX") "." (b/git-count-revs nil))) 8 | (def class-dir "target/classes") 9 | 10 | (defn compile 11 | [_] 12 | (b/delete {:path "target"}) 13 | (b/javac {:src-dirs ["src/main/java"] :class-dir class-dir :basis basis})) 14 | -------------------------------------------------------------------------------- /build/doc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo -e "Building docs\n" 6 | mvn javadoc:javadoc 7 | echo -e "Built docs at target/site/apidocs\n" 8 | 9 | echo -e "Checking out gh-pages branch\n" 10 | rm -rf gh-pages 11 | git clone --quiet --branch=gh-pages git@github.com:cognitect/transit-java.git gh-pages > /dev/null 12 | cd gh-pages 13 | 14 | echo -e "Replacing old gh-pages content with new docs\n" 15 | git rm -rf ./* 16 | cp -Rf ../target/site/apidocs/* ./ 17 | git add -f . 18 | git commit -m "Updating gh-pages with new api docs" 19 | git push -fq origin gh-pages > /dev/null 20 | cd .. 21 | 22 | echo -e "Cleaning up\n" 23 | rm -rf gh-pages 24 | 25 | -------------------------------------------------------------------------------- /build/package: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "Cleaning..." 6 | rm -rf ./target 7 | 8 | echo "Calculating version..." 9 | prefix=`cat VERSION_PREFIX` 10 | suffix=`build/revision` 11 | version=$prefix.$suffix 12 | echo $version 13 | 14 | target_name=transit-java-${version} 15 | 16 | echo "Packaging..." 17 | mvn versions:set -DnewVersion=${version} 18 | mvn install -DcreateChecksum=true 19 | mvn install:install-file -Dfile=./target/${target_name}.jar -DpomFile=pom.xml -DcreateChecksum=true -DlocalRepositoryPath=./target/package 20 | mvn versions:revert 21 | 22 | echo "Package done!" 23 | -------------------------------------------------------------------------------- /build/release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | echo "Cleaning..." 6 | rm -rf ./target 7 | 8 | echo "Calculating version..." 9 | prefix=`cat VERSION_PREFIX` 10 | suffix=`build/revision` 11 | version=$prefix.$suffix 12 | echo $version 13 | 14 | target_name=transit-java-${version} 15 | 16 | echo "Releasing..." 17 | mvn versions:set -DnewVersion=${version} 18 | mvn clean deploy 19 | mvn versions:revert 20 | 21 | echo "Tagging..." 22 | git tag -a v${version} -m "Release ${version}" 23 | git push origin v${version} 24 | 25 | echo "Updating README.md versions" 26 | sed -i '' "s/[[:digit:]]\{1,2\}\.[[:digit:]]\{1,2\}\.[[:digit:]]\{2,4\}/${version}/g" README.md 27 | git commit -m "Update README.md with ${version}" README.md 28 | git push 29 | 30 | echo "Updating gh-pages with new api docs" 31 | build/doc 32 | 33 | echo "Release done!" 34 | -------------------------------------------------------------------------------- /build/revision: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Returns the revision number used for deployment. 4 | 5 | set -e 6 | 7 | REVISION=`git --no-replace-objects describe --tags --match v0.0` 8 | 9 | # Extract the version number from the string. Do this in two steps so 10 | # it is a little easier to understand. 11 | REVISION=${REVISION:5} # drop the first 5 characters 12 | REVISION=${REVISION:0:${#REVISION}-9} # drop the last 9 characters 13 | 14 | echo $REVISION 15 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["target/classes"] 2 | :deps 3 | {com.fasterxml.jackson.core/jackson-core {:mvn/version "2.8.7"} 4 | org.msgpack/msgpack {:mvn/version "0.6.12"} 5 | javax.xml.bind/jaxb-api {:mvn/version "2.3.0"}} 6 | 7 | :deps/prep-lib 8 | {:ensure "target/classes" 9 | :alias :build 10 | :fn compile} 11 | 12 | :aliases 13 | {:test 14 | {:extra-deps {junit/junit {:mvn/version "4.13.2"}}} 15 | 16 | :build 17 | {:deps {io.github.clojure/tools.build {:git/tag "v0.7.2" :git/sha "0361dde"}} 18 | :ns-default build}}} 19 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.cognitect 5 | transit-java 6 | jar 7 | dev 8 | transit-java 9 | Transit is a data format and a set of libraries for conveying values between applications written in different languages. This library provides support for marshalling Transit data to/from Java. 10 | http://github.com/cognitect/transit-java 11 | 12 | 13 | The Apache Software License, Version 2.0 14 | http://www.apache.org/licenses/LICENSE-2.0.txt 15 | 16 | 17 | 18 | 19 | Tim Ewald 20 | tim@cognitect.com 21 | Cognitect 22 | http://cognitect.com 23 | 24 | 25 | 26 | scm:git:git@github.com:cognitect/transit-java.git 27 | scm:git:git@github.com:cognitect/transit-java.git 28 | git@github.com:cognitect/transit-java.git 29 | 30 | 31 | 32 | junit 33 | junit 34 | 4.13.2 35 | test 36 | 37 | 38 | com.fasterxml.jackson.core 39 | jackson-core 40 | 2.14.2 41 | 42 | 43 | org.msgpack 44 | msgpack 45 | 0.6.12 46 | 47 | 48 | javax.xml.bind 49 | jaxb-api 50 | 2.4.0-b180830.0359 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-compiler-plugin 58 | 3.8.1 59 | 60 | 1.8 61 | 1.8 62 | -Xlint:unchecked,rawtypes,removal 63 | 64 | 65 | 66 | org.codehaus.mojo 67 | versions-maven-plugin 68 | 2.8.1 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-javadoc-plugin 73 | 3.3.1 74 | 75 | 76 | attach-javadocs 77 | 78 | jar 79 | 80 | 81 | 82 | 83 | com.cognitect.transit.impl 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-source-plugin 89 | 3.2.1 90 | 91 | 92 | attach-sources 93 | 94 | jar-no-fork 95 | 96 | 97 | 98 | 99 | 100 | org.sonatype.plugins 101 | nexus-staging-maven-plugin 102 | 1.6.8 103 | true 104 | 105 | ossrh 106 | https://oss.sonatype.org/ 107 | true 108 | 109 | 110 | 111 | org.apache.maven.plugins 112 | maven-gpg-plugin 113 | 3.0.1 114 | 115 | 116 | sign-artifacts 117 | verify 118 | 119 | sign 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/ArrayReadHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Provides an ArrayReader to Transit to use in incrementally parsing 19 | * an array representation of a value. 20 | */ 21 | public interface ArrayReadHandler extends ReadHandler { 22 | 23 | /** 24 | * Provides an ArrayReader that a parser can use to convert 25 | * an array representation to an instance of a type incrementally 26 | * @return an ArrayReader 27 | */ 28 | ArrayReader arrayReader(); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/ArrayReader.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | public interface ArrayReader { 18 | /** 19 | * Initializes a new gestational result 20 | * @return a new gestational result 21 | */ 22 | G init(); 23 | 24 | /** 25 | * Initializes a new gestational result of specified size 26 | * @param size initial size of the new result 27 | * @return a new gestational result 28 | */ 29 | G init(int size); 30 | 31 | /** 32 | * Adds an item to the result, returning 33 | * a new result; new result must be used for 34 | * any further invocations 35 | * @param a gestational result 36 | * @param item an item 37 | * @return a new result 38 | */ 39 | G add(G a, T item); 40 | 41 | /** 42 | * Completes building of a result from a gestational result 43 | * @param ar the gestational result 44 | * @return the completed object 45 | */ 46 | A complete(G ar); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/DefaultReadHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Processes a non-decodable transit value 19 | */ 20 | public interface DefaultReadHandler { 21 | /** 22 | * Reads a transit representation that cannot otherwise be read 23 | * @param tag the transit value's tag 24 | * @param rep the transit value's representation 25 | * @return the resulting generic object 26 | */ 27 | T fromRep(String tag, Object rep); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Keyword.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Represents a keyword 19 | * N.B. the strings returned by the Named interface for Keywords must be interned 20 | */ 21 | public interface Keyword extends Comparable , Named{ 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Link.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Represents a hypermedia link, as per http://amundsen.com/media-types/collection/format/#arrays-links 19 | */ 20 | public interface Link { 21 | /** 22 | * Get the link's href 23 | * @return href 24 | */ 25 | public URI getHref(); 26 | 27 | /** 28 | * Get the link's rel 29 | * @return rel 30 | */ 31 | public String getRel(); 32 | 33 | /** 34 | * Get the link's name 35 | * @return name 36 | */ 37 | public String getName(); 38 | 39 | /** 40 | * Get the link's prompt 41 | * @return prompt 42 | */ 43 | public String getPrompt(); 44 | 45 | /** 46 | * Get the link's render semantic 47 | * @return render 48 | */ 49 | public String getRender(); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/MapReadHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Provides a MapReader to Transit to use in incrementally parsing 19 | * a map representation of a value. 20 | */ 21 | public interface MapReadHandler extends ReadHandler { 22 | /** 23 | * Provides a MapReader that a parser can use to convert 24 | * a map representation to an instance of a type incrementally 25 | * @return a MapReader 26 | */ 27 | MapReader mapReader(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/MapReader.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | public interface MapReader { 18 | /** 19 | * Initializes a new (gestational) map 20 | * @return a new map 21 | */ 22 | G init(); 23 | 24 | /** 25 | * Initializes a new (gestational) map of specified size 26 | * @param size initial size of the new map 27 | * @return a new map 28 | */ 29 | G init(int size); 30 | 31 | /** 32 | * Adds a key and value to the map, returning 33 | * a new map; new map must be used for 34 | * any further invocations 35 | * @param m a (gestational) map 36 | * @param key a key 37 | * @param val a value 38 | * @return a new gestational map 39 | */ 40 | G add(G m, K key, V val); 41 | 42 | /** 43 | * Completes building of a map 44 | * @param m a map 45 | * @return the completed map 46 | */ 47 | M complete(G m); 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Named.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Rich Hickey. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Represents a namespace-scoped named object 19 | */ 20 | public interface Named{ 21 | /** 22 | * Gets the namespace 23 | * @return namespace 24 | */ 25 | String getNamespace(); 26 | 27 | /** 28 | * Gets the name 29 | * @return name 30 | */ 31 | String getName(); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Ratio.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | import java.math.BigInteger; 18 | 19 | /** 20 | * Represents a ratio using BigIntegers 21 | */ 22 | public interface Ratio extends Comparable { 23 | /** 24 | * The value of the ratio as double 25 | * @return a double 26 | */ 27 | public Double getValue(); 28 | 29 | /** 30 | * Gets the numerator 31 | * @return numerator 32 | */ 33 | public BigInteger getNumerator(); 34 | 35 | /** 36 | * Gets the denominator 37 | * @return denominator 38 | */ 39 | public BigInteger getDenominator(); 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/ReadHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** Converts a transit representation to an instance of a type; if type 18 | * implements ArrayReadHandler or MapReadHandler to support incremental 19 | * parsing of representation, that interface will be used instead 20 | */ 21 | public interface ReadHandler { 22 | /** 23 | * Converts a transit value to an instance of a type 24 | * @param rep the transit value 25 | * @return the converted object 26 | */ 27 | T fromRep(Rep rep); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Reader.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Interface for reading values in transit format 19 | */ 20 | public interface Reader { 21 | /** 22 | * Reads a single value from an input source 23 | * @return the value 24 | */ 25 | T read(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/SPI/ReaderSPI.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.SPI; 5 | 6 | import com.cognitect.transit.ArrayReader; 7 | import com.cognitect.transit.MapReader; 8 | import com.cognitect.transit.Reader; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * Interface for providing custom MapReader and ArrayReader implementations for a Reader to use 15 | * when parsing native JSON or msgpack composite structures. This entry point exists to 16 | * enable Transit libraries for other JVM languages to layer on top of the Java Transit library, 17 | * but still get language-appropriate maps and arrays returned from a parse, while ensuring that 18 | * parsing and decoding work correctly. This interface should never be used by applications 19 | * that using this library. 20 | */ 21 | public interface ReaderSPI { 22 | /** 23 | * Specifies a custom MapReader and ArrayReader to use when parsing native maps and arrays 24 | * in JSON or msgpack. Implementations must accept any type of input and must return a maps 25 | * or lists of any type of content. This function must be called before Reader.read is called. 26 | * @param mapBuilder a custom MapReader that produces a Map of objects to objects 27 | * @param listBuilder a custom ArrayReader that yields a List of objects 28 | * @return 29 | */ 30 | public Reader setBuilders(MapReader, Object, Object> mapBuilder, 31 | ArrayReader, Object> listBuilder); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Symbol.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Represents a symbol 19 | */ 20 | public interface Symbol extends Comparable { 21 | /** 22 | * Gets the value of the symbol, a string 23 | * @return symbol 24 | */ 25 | public String getValue(); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/TaggedValue.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Represents a transit tag and value. Returned by default when a reader encounters a tag for 19 | * which there is no registered ReadHandler. Can also be used in a custom WriteHandler implementation 20 | * to force representation to use a transit ground type using a rep for which there is no 21 | * registered handler (e.g., an iterable for the representation of an array). 22 | */ 23 | public interface TaggedValue { 24 | /** 25 | * Gets the tag 26 | * @return tag 27 | */ 28 | public String getTag(); 29 | 30 | /** 31 | * Gets the representation of the value 32 | * @return rep 33 | */ 34 | public Rep getRep(); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/TransitFactory.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | 18 | import com.cognitect.transit.impl.*; 19 | 20 | import java.io.InputStream; 21 | import java.io.OutputStream; 22 | import java.util.Map; 23 | import java.util.function.Function; 24 | 25 | /** 26 | * Main entry point for using transit-java library. Provides methods to construct 27 | * readers and writers, as well as helpers to make various other values. 28 | */ 29 | public class TransitFactory { 30 | /** 31 | * Transit formats 32 | */ 33 | public static enum Format { JSON, MSGPACK, JSON_VERBOSE } 34 | 35 | /** 36 | * Creates a writer instance. 37 | * @param type format to write in 38 | * @param out output stream to write to 39 | * @return a Writer 40 | */ 41 | public static Writer writer(Format type, OutputStream out) { 42 | return writer(type, out, defaultDefaultWriteHandler()); 43 | } 44 | 45 | /** 46 | * Creates a writer instance. 47 | * @param type format to write in 48 | * @param out output stream to write to 49 | * @param customHandlers additional WriteHandlers to use in addition 50 | * to or in place of the default WriteHandlers 51 | * @return a writer 52 | */ 53 | public static Writer writer(Format type, OutputStream out, Map> customHandlers) { 54 | return writer(type, out, customHandlers, null); 55 | } 56 | 57 | /** 58 | * Creates a writer instance. 59 | * @param type format to write in 60 | * @param out output stream to write to 61 | * @param defaultWriteHandler WriteHandler to use by default 62 | * @return a writer 63 | */ 64 | public static Writer writer(Format type, OutputStream out, WriteHandler defaultWriteHandler) { 65 | return writer(type, out, null, defaultWriteHandler); 66 | } 67 | 68 | /** 69 | * Creates a writer instance. 70 | * @param type format to write in 71 | * @param out output stream to write to 72 | * @param customHandlers additional WriteHandlers to use in addition 73 | * to or in place of the default WriteHandlers 74 | * @param defaultWriteHandler WriteHandler to use by default 75 | * @return a writer 76 | */ 77 | public static Writer writer(Format type, OutputStream out, Map> customHandlers, WriteHandler defaultWriteHandler) { 78 | return writer(type, out, customHandlers, defaultWriteHandler, null); 79 | } 80 | 81 | /** 82 | * Creates a writer instance. 83 | * @param type format to write in 84 | * @param out output stream to write to 85 | * @param customHandlers additional WriteHandlers to use in addition 86 | * to or in place of the default WriteHandlers 87 | * @param defaultWriteHandler WriteHandler to use by default 88 | * @param transform a transform function to apply to values before writing 89 | * @return a writer 90 | */ 91 | public static Writer writer(Format type, OutputStream out, Map> customHandlers, WriteHandler defaultWriteHandler, Function transform) { 92 | try { 93 | switch (type) { 94 | case MSGPACK: 95 | return WriterFactory.getMsgpackInstance(out, customHandlers, defaultWriteHandler, transform); 96 | case JSON: 97 | return WriterFactory.getJsonInstance(out, customHandlers, defaultWriteHandler, false, transform); 98 | case JSON_VERBOSE: 99 | return WriterFactory.getJsonInstance(out, customHandlers, defaultWriteHandler, true, transform); 100 | default: 101 | throw new IllegalArgumentException("Unknown Writer type: " + type.toString()); 102 | } 103 | } catch (Throwable e) { 104 | throw new RuntimeException(e); 105 | } 106 | } 107 | 108 | /** 109 | * Creates a reader instance. 110 | * @param type the format to read in 111 | * @param in the input stream to read from 112 | * @return a reader 113 | */ 114 | public static Reader reader(Format type, InputStream in) { 115 | return reader(type, in, defaultDefaultReadHandler()); 116 | } 117 | 118 | /** 119 | * Creates a reader instance. 120 | * @param type the format to read in 121 | * @param in the input stream to read from 122 | * @param customHandlers a map of custom ReadHandlers to use in addition 123 | * or in place of the default ReadHandlers 124 | * @return a reader 125 | */ 126 | public static Reader reader(Format type, InputStream in, Map> customHandlers) { 127 | return reader(type, in, customHandlers, null); 128 | } 129 | 130 | 131 | 132 | /** 133 | * Creates a reader instance. 134 | * @param type the format to read in 135 | * @param in the input stream to read from 136 | * @param customDefaultHandler a DefaultReadHandler to use for processing 137 | * encoded values for which there is no read handler 138 | * @return a reader 139 | */ 140 | public static Reader reader(Format type, InputStream in, DefaultReadHandler customDefaultHandler) { 141 | return reader(type, in, null, customDefaultHandler); 142 | } 143 | 144 | /** 145 | * Creates a reader instance. 146 | * @param type the format to read in 147 | * @param in the input stream to read from 148 | * @param customHandlers a map of custom ReadHandlers to use in addition 149 | * or in place of the default ReadHandlers 150 | * @param customDefaultHandler a DefaultReadHandler to use for processing 151 | * encoded values for which there is no read handler 152 | * @return a reader 153 | */ 154 | public static Reader reader(Format type, final InputStream in, 155 | final Map> customHandlers, 156 | final DefaultReadHandler customDefaultHandler) { 157 | try { 158 | switch (type) { 159 | case JSON: 160 | case JSON_VERBOSE: 161 | return ReaderFactory.getJsonInstance(in, customHandlers, customDefaultHandler); 162 | case MSGPACK: 163 | return ReaderFactory.getMsgpackInstance(in, customHandlers, customDefaultHandler); 164 | default: 165 | throw new IllegalArgumentException("Unknown Reader type: " + type.toString()); 166 | } 167 | } catch (Throwable e) { 168 | throw new RuntimeException(e); 169 | } 170 | } 171 | 172 | /** 173 | * Converts a string or keyword to a keyword 174 | * @param o A string or a keyword 175 | * @return a keyword 176 | */ 177 | public static Keyword keyword(Object o) { 178 | if (o instanceof Keyword) 179 | return (Keyword) o; 180 | else if (o instanceof String) { 181 | String s = (String) o; 182 | if (s.charAt(0) == ':') 183 | return new KeywordImpl(s.substring(1)); 184 | else 185 | return new KeywordImpl(s); 186 | } 187 | else throw new IllegalArgumentException("Cannot make keyword from " + o.getClass().getSimpleName()); 188 | } 189 | 190 | /** 191 | * Converts a string or a symbol to a symbol 192 | * @param o a string or a symbol 193 | * @return a symbol 194 | */ 195 | public static Symbol symbol(Object o) { 196 | if (o instanceof Symbol) 197 | return (Symbol) o; 198 | else if (o instanceof String) { 199 | String s = (String) o; 200 | if (s.charAt(0) == ':') 201 | return new SymbolImpl(s.substring(1)); 202 | else 203 | return new SymbolImpl(s); 204 | } 205 | else throw new IllegalArgumentException("Cannot make symbol from " + o.getClass().getSimpleName()); 206 | } 207 | 208 | /** 209 | * Creates a TaggedValue 210 | * @param tag tag string 211 | * @param rep value representation 212 | * @return a tagged value 213 | */ 214 | public static TaggedValue taggedValue(String tag, T rep) { 215 | return new TaggedValueImpl(tag, rep); 216 | } 217 | 218 | /** 219 | * Creates a Link 220 | * @param href an href value 221 | * @param rel a rel value 222 | * @return a link instance 223 | */ 224 | public static Link link(String href, String rel) { 225 | return link(href, rel, null, null, null); 226 | } 227 | 228 | /** 229 | * Creates a Link 230 | * @param href an href value 231 | * @param rel a rel value 232 | * @return a link instance 233 | */ 234 | public static Link link(URI href, String rel) { 235 | return link(href, rel, null, null, null); 236 | } 237 | 238 | /** 239 | * Creates a Link 240 | * @param href an href value 241 | * @param rel a rel value 242 | * @param name an optional name value 243 | * @param prompt an optional prompt value 244 | * @param render an optional render value 245 | * @return a link instance 246 | */ 247 | public static Link link(String href, String rel, String name, String prompt, String render) { 248 | return link(new URIImpl(href), rel, name, prompt, render); 249 | } 250 | 251 | /** 252 | * Creates a Link 253 | * @param href an href value 254 | * @param rel a rel value 255 | * @param name an optional name value 256 | * @param prompt an optional prompt value 257 | * @param render an optional render value 258 | * @return a link instance 259 | */ 260 | public static Link link(URI href, String rel, String name, String prompt, String render) { 261 | return new LinkImpl(href, rel, name, prompt, render); 262 | } 263 | 264 | /** 265 | * Creates a URI 266 | * @param uri a uri string 267 | * @return the URI 268 | */ 269 | public static URI uri(String uri) { 270 | return new URIImpl(uri); 271 | } 272 | 273 | /** 274 | * Returns the DefaultReadHandler that is used by default 275 | * @return DefaultReadHandler instance 276 | */ 277 | public static DefaultReadHandler> defaultDefaultReadHandler() { return ReaderFactory.defaultDefaultHandler(); } 278 | 279 | public static WriteHandler defaultDefaultWriteHandler() { return WriterFactory.defaultDefaultHandler(); } 280 | 281 | /** 282 | * Returns a map of tags to ReadHandlers that is used by default 283 | * @return tag to ReadHandler map 284 | */ 285 | public static Map> defaultReadHandlers() { return ReaderFactory.defaultHandlers(); } 286 | 287 | /** 288 | * Creates a read-only Map of String to ReadHandler containing 289 | * default ReadHandlers with customHandlers merged in. Use this to 290 | * build the collection of read handlers once, and pass it to 291 | * repeated calls to TransitFactory.reader. This can be more 292 | * efficient than repeatedly passing a map of custom handlers to 293 | * TransitFactory.reader, which then merges them with the default 294 | * handlers and/or looks them up in a cache each invocation. 295 | * @param customHandlers a map of custom ReadHandlers to use in addition 296 | * or in place of the default ReadHandlers 297 | * @return a ReadHandlerMap 298 | */ 299 | public static Map> readHandlerMap(Map> customHandlers) { 300 | return new ReadHandlerMap(customHandlers); 301 | } 302 | 303 | /** 304 | * Returns a map of classes to Handlers that is used by default 305 | * @return class to Handler map 306 | */ 307 | public static Map> defaultWriteHandlers() { 308 | return new WriteHandlerMap(); 309 | } 310 | 311 | /** 312 | * Creates a read-only Map of Class to WriteHandler containing 313 | * default WriteHandlers with customHandlers merged in. Use this 314 | * to build the collection of write handlers once, and pass it to 315 | * repeated calls to TransitFactory.reader. This can be more 316 | * efficient than repeatedly passing a map of custom handlers to 317 | * TransitFactory.writer, which then merges them with the default 318 | * handlers and/or looks them up in a cache each invocation. 319 | * @param customHandlers a map of custom WriteHandler to use in addition 320 | * or in place of the default WriteHandler 321 | * @return a WriteHandlerMap 322 | */ 323 | public static Map> writeHandlerMap(Map> customHandlers) { 324 | return new WriteHandlerMap(customHandlers); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/URI.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Represents a URI as a String. Transit-java uses this instead of java.net.URI because String creation 19 | * is more efficient and the common case only needs Strings. Use a custom URIWriter and URIReader if your 20 | * system needs a java.net.URI instead. 21 | */ 22 | public interface URI extends Comparable { 23 | /** 24 | * Gets the String value of the URI 25 | * @return String 26 | */ 27 | public String getValue(); 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/WriteHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Converts an instance of an type to a transit representation 19 | */ 20 | public interface WriteHandler { 21 | 22 | /** 23 | * Gets the tag to use for the object 24 | * @param o the object 25 | * @return tag 26 | */ 27 | String tag(T o); 28 | 29 | /** 30 | * Gets the representation to use for the object, either an instance of transit ground type, 31 | * or object for which there is a Handler (including an instance of TaggedValue). 32 | * @param o the object 33 | * @return the representation 34 | */ 35 | Rep rep(T o); 36 | 37 | /** 38 | * Gets the string representation to use for the object; can return null 39 | * @param o the object 40 | * @return the string representation 41 | */ 42 | String stringRep(T o); 43 | 44 | /** 45 | * Gets an alternative handler which provides more readable representations for use in 46 | * verbose mode; can return null 47 | * @return a handler 48 | */ 49 | WriteHandler getVerboseHandler(); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/Writer.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | /** 18 | * Interface for writing values in transit format. 19 | */ 20 | public interface Writer { 21 | /** 22 | * Writes a single value to an output stream 23 | * @param o the value to write 24 | */ 25 | void write(T o); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/AbstractEmitter.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.WriteHandler; 7 | 8 | import java.util.*; 9 | import java.util.function.Function; 10 | 11 | public abstract class AbstractEmitter implements Emitter 12 | { 13 | private WriteHandlerMap writeHandlerMap; 14 | private WriteHandler defaultWriteHandler; 15 | private Function transform; 16 | 17 | @Deprecated 18 | protected AbstractEmitter(WriteHandlerMap writeHandlerMap) { 19 | this(writeHandlerMap, null); 20 | } 21 | 22 | protected AbstractEmitter(WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler) { 23 | this.writeHandlerMap = writeHandlerMap; 24 | this.defaultWriteHandler = defaultWriteHandler; 25 | } 26 | 27 | protected AbstractEmitter(WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler, Function transform) { 28 | this.writeHandlerMap = writeHandlerMap; 29 | this.writeHandlerMap.transform = transform; 30 | this.defaultWriteHandler = defaultWriteHandler; 31 | this.transform = transform; 32 | } 33 | 34 | protected String escape(String s) { 35 | 36 | int l = s.length(); 37 | if(l > 0) { 38 | char c = s.charAt(0); 39 | if(c == Constants.ESC || c == Constants.SUB || c == Constants.RESERVED) { 40 | return Constants.ESC + s; 41 | } 42 | } 43 | return s; 44 | } 45 | 46 | protected void emitTagged(String t, Object o, boolean ignored, WriteCache cache) throws Exception { 47 | 48 | emitArrayStart(2L); 49 | emitString(Constants.ESC_TAG, t, "", false, cache); 50 | marshal(o, false, cache); 51 | emitArrayEnd(); 52 | } 53 | 54 | protected void emitEncoded(String t, WriteHandler h, Object o, boolean asMapKey, WriteCache cache) throws Exception { 55 | 56 | if(t.length() == 1) { 57 | Object r = h.rep(o); 58 | if(r instanceof String) { 59 | emitString(Constants.ESC_STR, t, (String)r, asMapKey, cache); 60 | } 61 | else if(prefersStrings() || asMapKey) { 62 | String sr = h.stringRep(o); 63 | if(sr != null) 64 | emitString(Constants.ESC_STR, t, sr, asMapKey, cache); 65 | else 66 | throw new Exception("Cannot be encoded as a string " + o); 67 | } 68 | else { 69 | emitTagged(t, r, asMapKey, cache); 70 | } 71 | } 72 | else if(asMapKey) 73 | throw new Exception("Cannot be used as a map key " + o); 74 | else 75 | emitTagged(t, h.rep(o), asMapKey, cache); 76 | } 77 | 78 | protected void emitMap(Object m, boolean ignored, WriteCache cache) throws Exception 79 | { 80 | Iterable> entries; 81 | if (m instanceof Map) 82 | entries = ((Map) m).entrySet(); 83 | else { 84 | entries = (Iterable>) m; 85 | } 86 | emitMap(entries, ignored, cache); 87 | } 88 | 89 | abstract protected void emitMap(Iterable> i, boolean ignored, WriteCache cache) throws Exception; 90 | 91 | protected void emitArray(Object o, boolean ignored, WriteCache cache) throws Exception { 92 | 93 | emitArrayStart(Util.arraySize(o)); 94 | 95 | if(o instanceof RandomAccess){ 96 | List xs = (List)o; 97 | for(int i=0;i h = writeHandlerMap.getHandler(o); 162 | if (h == null) h = defaultWriteHandler; 163 | 164 | boolean supported = false; 165 | if(h != null) { // TODO: maybe remove getWriteHandler call and this check and just call tag 166 | String t = h.tag(o); 167 | if(t != null) { 168 | supported = true; 169 | if(t.length() == 1) { 170 | switch(t.charAt(0)) { 171 | case '_': emitNil(asMapKey, cache); break; 172 | case 's': emitString(null, null, escape((String)h.rep(o)), asMapKey, cache); break; 173 | case '?': emitBoolean((Boolean)h.rep(o), asMapKey, cache); break; 174 | case 'i': emitInteger(h.rep(o), asMapKey, cache); break; 175 | case 'd': emitDouble(h.rep(o), asMapKey, cache); break; 176 | case 'b': emitBinary(h.rep(o), asMapKey, cache); break; 177 | case '\'': emitTagged(t, h.rep(o), false, cache); break; 178 | default: emitEncoded(t, h, o, asMapKey, cache); break; 179 | } 180 | } 181 | else { 182 | if(t.equals("array")) 183 | emitArray(h.rep(o), asMapKey, cache); 184 | else if(t.equals("map")) { 185 | emitMap(h.rep(o), asMapKey, cache); 186 | } 187 | else 188 | emitEncoded(t, h, o, asMapKey, cache); 189 | } 190 | flushWriter(); 191 | } 192 | } 193 | 194 | if(!supported) 195 | throw new Exception("Not supported: " + o.getClass()); 196 | } 197 | 198 | protected void marshalTop(Object o, WriteCache cache) throws Exception { 199 | 200 | WriteHandler h = writeHandlerMap.getHandler(o); 201 | if (h == null) { 202 | h = defaultWriteHandler; 203 | } 204 | String tag = h.tag(o); 205 | if (tag == null) { 206 | throw new Exception("Not supported: " + o); 207 | } 208 | 209 | if (tag.length() == 1) 210 | o = new Quote(o); 211 | 212 | marshal(o, false, cache); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/AbstractParser.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.ArrayReader; 7 | import com.cognitect.transit.DefaultReadHandler; 8 | import com.cognitect.transit.MapReader; 9 | import com.cognitect.transit.ReadHandler; 10 | 11 | import java.text.SimpleDateFormat; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public abstract class AbstractParser implements Parser { 16 | 17 | private static final ThreadLocal dateTimeFormat = 18 | new ThreadLocal() { 19 | @Override protected SimpleDateFormat initialValue() { 20 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 21 | sdf.setTimeZone(java.util.TimeZone.getTimeZone("UTC")); 22 | return sdf; 23 | } 24 | }; 25 | 26 | public static SimpleDateFormat getDateTimeFormat() { 27 | return dateTimeFormat.get(); 28 | } 29 | 30 | protected final Map> handlers; 31 | private final DefaultReadHandler defaultHandler; 32 | protected MapReader, Object, Object> mapBuilder; 33 | protected ArrayReader, Object> listBuilder; 34 | 35 | @SuppressWarnings("unchecked") 36 | protected AbstractParser(Map> handlers, 37 | DefaultReadHandler defaultHandler, 38 | MapReader, Object, Object> mapBuilder, 39 | ArrayReader, Object> listBuilder) { 40 | this.handlers = handlers; 41 | this.defaultHandler = defaultHandler; 42 | this.mapBuilder = (MapReader, Object, Object>) mapBuilder; 43 | this.listBuilder = (ArrayReader, Object>) listBuilder; 44 | } 45 | 46 | @SuppressWarnings("unchecked") 47 | protected ReadHandler getHandler(String tag) { 48 | return (ReadHandler) handlers.get(tag); 49 | } 50 | 51 | protected Object decode(String tag, Object rep) { 52 | 53 | ReadHandler d = getHandler(tag); 54 | if(d != null) { 55 | return d.fromRep(rep); 56 | } else if(defaultHandler != null) { 57 | return defaultHandler.fromRep(tag, rep); 58 | } else { 59 | throw new RuntimeException("Cannot fromRep " + tag + ": " + rep.toString()); 60 | } 61 | } 62 | 63 | protected Object parseString(Object o) { 64 | if (o instanceof String) { 65 | String s = (String) o; 66 | if (s.length() > 1) { 67 | switch(s.charAt(0)) { 68 | case Constants.ESC: { 69 | switch (s.charAt(1)) { 70 | case Constants.ESC: 71 | case Constants.SUB: 72 | case Constants.RESERVED: 73 | return s.substring(1); 74 | case Constants.TAG: 75 | return new Tag(s.substring(2)); 76 | default: 77 | return decode(s.substring(1, 2), s.substring(2)); 78 | } 79 | } 80 | case Constants.SUB: { 81 | if (s.charAt(1) == ' ') { 82 | return Constants.MAP_AS_ARRAY; 83 | } 84 | } 85 | } 86 | } 87 | } 88 | return o; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/AbstractWriteHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.WriteHandler; 7 | 8 | public abstract class AbstractWriteHandler implements WriteHandler { 9 | 10 | @Override 11 | public String stringRep(T o) { 12 | return null; 13 | } 14 | 15 | @Override 16 | public WriteHandler getVerboseHandler() { 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Cache.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | 9 | public class Cache extends LinkedHashMap { 10 | 11 | public Cache() { 12 | super(10); 13 | } 14 | 15 | @Override 16 | protected boolean removeEldestEntry(Map.Entry eldest) { 17 | return size() > 10; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Constants.java: -------------------------------------------------------------------------------- 1 | package com.cognitect.transit.impl; 2 | 3 | public final class Constants { 4 | public static final char ESC = '~'; 5 | public static final String ESC_STR = String.valueOf(ESC); 6 | public static final char TAG = '#'; 7 | public static final String TAG_STR = String.valueOf(TAG); 8 | public static final char SUB = '^'; 9 | public static final String SUB_STR = String.valueOf(SUB); 10 | public static final char RESERVED = '`'; 11 | public static final String ESC_TAG = String.valueOf(ESC) + TAG; 12 | public static final String QUOTE_TAG = ESC_TAG + "'"; 13 | public static final String MAP_AS_ARRAY = "^ "; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Emitter.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import java.io.IOException; 7 | 8 | public interface Emitter { 9 | 10 | void emit(Object o, boolean asMapKey, WriteCache cache) throws Exception; 11 | void emitNil(boolean asMapKey, WriteCache cache) throws Exception; 12 | void emitString(String prefix, String tag, String s, boolean asMapKey, WriteCache cache) throws Exception; 13 | void emitBoolean(Boolean b, boolean asMapKey, WriteCache cache) throws Exception; 14 | void emitBoolean(boolean b, boolean asMapKey, WriteCache cache) throws Exception; 15 | void emitInteger(Object i, boolean asMapKey, WriteCache cache) throws Exception; 16 | void emitInteger(long i, boolean asMapKey, WriteCache cache) throws Exception; 17 | void emitDouble(Object d, boolean asMapKey, WriteCache cache) throws Exception; 18 | void emitDouble(float d, boolean asMapKey, WriteCache cache) throws Exception; 19 | void emitDouble(double d, boolean asMapKey, WriteCache cache) throws Exception; 20 | void emitBinary(Object b, boolean asMapKey, WriteCache cache) throws Exception; 21 | void emitArrayStart(Long size) throws Exception; 22 | void emitArrayEnd() throws Exception; 23 | void emitMapStart(Long size) throws Exception; 24 | void emitMapEnd() throws Exception; 25 | boolean prefersStrings(); 26 | void flushWriter() throws IOException; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/JsonEmitter.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.WriteHandler; 7 | import com.fasterxml.jackson.core.JsonGenerator; 8 | 9 | import java.io.IOException; 10 | import java.math.BigInteger; 11 | import java.util.Map; 12 | import java.util.function.Function; 13 | import java.util.Base64; 14 | 15 | public class JsonEmitter extends AbstractEmitter { 16 | 17 | private final static BigInteger JSON_INT_MAX = new BigInteger(String.valueOf((long) Math.pow(2, 53) - 1)); 18 | private final static BigInteger JSON_INT_MIN = new BigInteger("-" + JSON_INT_MAX.toString()); 19 | 20 | protected final JsonGenerator gen; 21 | 22 | @Deprecated 23 | public JsonEmitter(JsonGenerator gen, WriteHandlerMap writeHandlerMap) { 24 | super(writeHandlerMap, null); 25 | this.gen = gen; 26 | } 27 | 28 | public JsonEmitter(JsonGenerator gen, WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler) { 29 | super(writeHandlerMap, defaultWriteHandler); 30 | this.gen = gen; 31 | } 32 | 33 | public JsonEmitter(JsonGenerator gen, WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler, Function transform) { 34 | super(writeHandlerMap, defaultWriteHandler, transform); 35 | this.gen = gen; 36 | } 37 | 38 | @Override 39 | public void emit(Object o, boolean asMapKey, WriteCache cache) throws Exception { 40 | 41 | marshalTop(o, cache); 42 | } 43 | 44 | @Override 45 | public void emitNil(boolean asMapKey, WriteCache cache) throws Exception { 46 | 47 | if(asMapKey) 48 | emitString(Constants.ESC_STR, "_", "", asMapKey, cache); 49 | else 50 | gen.writeNull(); 51 | } 52 | 53 | @Override 54 | public void emitString(String prefix, String tag, String s, boolean asMapKey, WriteCache cache) throws Exception { 55 | String outString = cache.cacheWrite(Util.maybePrefix(prefix, tag, s), asMapKey); 56 | gen.writeString(outString); 57 | } 58 | 59 | @Override 60 | public void emitBoolean(Boolean b, boolean asMapKey, WriteCache cache) throws Exception { 61 | if(asMapKey) { 62 | emitString(Constants.ESC_STR,"?", b ? "t" : "f", asMapKey, cache); 63 | } 64 | else { 65 | gen.writeBoolean(b); 66 | } 67 | } 68 | 69 | @Override 70 | public void emitBoolean(boolean b, boolean asMapKey, WriteCache cache) throws Exception { 71 | if(asMapKey) { 72 | emitString(Constants.ESC_STR,"?", b ? "t" : "f", asMapKey, cache); 73 | } 74 | else { 75 | gen.writeBoolean(b); 76 | } 77 | } 78 | 79 | @Override 80 | public void emitInteger(Object o, boolean asMapKey, WriteCache cache) throws Exception { 81 | long i = Util.numberToPrimitiveLong(o); 82 | if(asMapKey || i > JSON_INT_MAX.longValue() || i < JSON_INT_MIN.longValue()) 83 | emitString(Constants.ESC_STR, "i", String.valueOf(i), asMapKey, cache); 84 | else 85 | gen.writeNumber(i); 86 | } 87 | 88 | @Override 89 | public void emitInteger(long i, boolean asMapKey, WriteCache cache) throws Exception { 90 | if(asMapKey || i > JSON_INT_MAX.longValue() || i < JSON_INT_MIN.longValue()) 91 | emitString(Constants.ESC_STR, "i", String.valueOf(i), asMapKey, cache); 92 | else 93 | gen.writeNumber(i); 94 | } 95 | 96 | @Override 97 | public void emitDouble(Object d, boolean asMapKey, WriteCache cache) throws Exception { 98 | if(asMapKey) 99 | emitString(Constants.ESC_STR, "d", d.toString(), asMapKey, cache); 100 | else if(d instanceof Double) 101 | gen.writeNumber((Double)d); 102 | else if(d instanceof Float) 103 | gen.writeNumber((Float)d); 104 | } 105 | 106 | @Override 107 | public void emitDouble(float d, boolean asMapKey, WriteCache cache) throws Exception { 108 | if(asMapKey) 109 | emitString(Constants.ESC_STR, "d", String.valueOf(d), asMapKey, cache); 110 | else 111 | gen.writeNumber(d); 112 | } 113 | 114 | @Override 115 | public void emitDouble(double d, boolean asMapKey, WriteCache cache) throws Exception { 116 | if(asMapKey) 117 | emitString(Constants.ESC_STR, "d", String.valueOf(d), asMapKey, cache); 118 | else 119 | gen.writeNumber(d); 120 | } 121 | 122 | @Override 123 | public void emitBinary(Object b, boolean asMapKey, WriteCache cache) throws Exception { 124 | 125 | byte[] encodedBytes = Base64.getEncoder().encode((byte[])b); 126 | emitString(Constants.ESC_STR, "b", new String(encodedBytes), asMapKey, cache); 127 | } 128 | 129 | @Override 130 | public void emitArrayStart(Long size) throws Exception { 131 | gen.writeStartArray(); 132 | } 133 | 134 | @Override 135 | public void emitArrayEnd() throws Exception { 136 | gen.writeEndArray(); 137 | } 138 | 139 | @Override 140 | public void emitMapStart(Long ignored) throws Exception { 141 | gen.writeStartObject(); 142 | } 143 | 144 | @Override 145 | public void emitMapEnd() throws Exception { 146 | gen.writeEndObject(); 147 | } 148 | 149 | @Override 150 | public void flushWriter() throws IOException { 151 | 152 | gen.flush(); 153 | } 154 | 155 | @Override 156 | public boolean prefersStrings() { 157 | 158 | return true; 159 | } 160 | 161 | @Override 162 | protected void emitMap(Iterable> i, boolean ignored, WriteCache cache) throws Exception { 163 | 164 | long sz = Util.mapSize(i); 165 | 166 | emitArrayStart(sz); 167 | emitString(null, null, Constants.MAP_AS_ARRAY, false, cache); 168 | 169 | for (Map.Entry e : i) { 170 | marshal(e.getKey(), true, cache); 171 | marshal(e.getValue(), false, cache); 172 | } 173 | emitArrayEnd(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/JsonParser.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.*; 7 | import com.fasterxml.jackson.core.JsonToken; 8 | 9 | import java.io.EOFException; 10 | import java.io.IOException; 11 | import java.math.BigInteger; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class JsonParser extends AbstractParser { 16 | 17 | private final com.fasterxml.jackson.core.JsonParser jp; 18 | 19 | public JsonParser(com.fasterxml.jackson.core.JsonParser jp, 20 | Map> handlers, 21 | DefaultReadHandler defaultHandler, 22 | MapReader, Object, Object> mapBuilder, 23 | ArrayReader, Object> listBuilder) { 24 | 25 | super(handlers, defaultHandler, mapBuilder, listBuilder); 26 | this.jp = jp; 27 | } 28 | 29 | private Object parseLong() throws IOException { 30 | 31 | Object val; 32 | try { 33 | val = jp.getLongValue(); 34 | }catch(IOException e) { 35 | val = new BigInteger(jp.getText()); 36 | } 37 | 38 | return val; 39 | } 40 | 41 | @Override 42 | public Object parse(ReadCache cache) throws IOException { 43 | if(jp.nextToken() == null) 44 | throw new EOFException(); 45 | else 46 | return parseVal(false, cache); 47 | } 48 | 49 | @Override 50 | public Object parseVal(boolean asMapKey, ReadCache cache) throws IOException { 51 | 52 | switch(jp.getCurrentToken()) { 53 | case START_OBJECT: 54 | return parseMap(asMapKey, cache, null); 55 | case START_ARRAY: 56 | return parseArray(asMapKey, cache, null); 57 | case FIELD_NAME: 58 | return cache.cacheRead(jp.getText(), asMapKey, this); 59 | case VALUE_STRING: 60 | return cache.cacheRead(jp.getText(), asMapKey, this); 61 | case VALUE_NUMBER_INT: 62 | return parseLong(); 63 | case VALUE_NUMBER_FLOAT: 64 | return jp.getDoubleValue(); 65 | case VALUE_TRUE: 66 | return true; 67 | case VALUE_FALSE: 68 | return false; 69 | case VALUE_NULL: 70 | return null; 71 | default: return null; 72 | } 73 | } 74 | 75 | @Override 76 | public Object parseMap(boolean ignored, ReadCache cache, MapReadHandler handler) throws IOException { 77 | return parseMap(ignored, cache, handler, JsonToken.END_OBJECT); 78 | } 79 | 80 | public Object parseMap(boolean ignored, ReadCache cache, MapReadHandler handler, JsonToken endToken) throws IOException { 81 | 82 | MapReader mr = (handler != null) ? handler.mapReader() : mapBuilder; 83 | 84 | Object mb = mr.init(); 85 | 86 | while(jp.nextToken() != endToken) { 87 | Object key = parseVal(true, cache); 88 | if (key instanceof Tag) { 89 | String tag = ((Tag) key).getValue(); 90 | ReadHandler val_handler = getHandler(tag); 91 | Object val; 92 | jp.nextToken(); // advance to read value 93 | if (val_handler != null) { 94 | if (this.jp.getCurrentToken() == JsonToken.START_OBJECT && val_handler instanceof MapReadHandler) { 95 | // use map reader to decode value 96 | val = parseMap(false, cache, (MapReadHandler) val_handler); 97 | } else if (this.jp.getCurrentToken() == JsonToken.START_ARRAY && val_handler instanceof ArrayReadHandler) { 98 | // use array reader to decode value 99 | val = parseArray(false, cache, (ArrayReadHandler) val_handler); 100 | } else { 101 | // read value and decode normally 102 | val = val_handler.fromRep(parseVal(false, cache)); 103 | } 104 | } else { 105 | // default decode 106 | val = this.decode(tag, parseVal(false, cache)); 107 | } 108 | jp.nextToken(); // advance to read end of object or array 109 | return val; 110 | } else { 111 | jp.nextToken(); // advance to read value 112 | mb = mr.add(mb, key, parseVal(false, cache)); 113 | } 114 | } 115 | 116 | return mr.complete(mb); 117 | } 118 | 119 | @Override 120 | public Object parseArray(boolean ignored, ReadCache cache, ArrayReadHandler handler) throws IOException { 121 | 122 | // if nextToken == JsonToken.END_ARRAY 123 | if (jp.nextToken() != JsonToken.END_ARRAY) { 124 | Object firstVal = parseVal(false, cache); 125 | if (firstVal != null) { 126 | if (firstVal == Constants.MAP_AS_ARRAY) { 127 | // if the same, build a map w/ rest of array contents 128 | return parseMap(false, cache, null, JsonToken.END_ARRAY); 129 | } else if (firstVal instanceof Tag) { 130 | String tag = ((Tag) firstVal).getValue(); 131 | ReadHandler val_handler = getHandler(tag); 132 | jp.nextToken(); // advance to value 133 | Object val; 134 | if (val_handler != null) { 135 | if (this.jp.getCurrentToken() == JsonToken.START_OBJECT && val_handler instanceof MapReadHandler) { 136 | // use map reader to decode value 137 | val = parseMap(false, cache, (MapReadHandler) val_handler); 138 | } else if (this.jp.getCurrentToken() == JsonToken.START_ARRAY && val_handler instanceof ArrayReadHandler) { 139 | // use array reader to decode value 140 | val = parseArray(false, cache, (ArrayReadHandler) val_handler); 141 | } else { 142 | // read value and decode normally 143 | val = val_handler.fromRep(parseVal(false, cache)); 144 | } 145 | } else { 146 | // default decode 147 | val = this.decode(tag, parseVal(false, cache)); 148 | } 149 | jp.nextToken(); // advance past end of object or array 150 | return val; 151 | } 152 | } 153 | 154 | // process array w/o special decoding or interpretation 155 | ArrayReader ar = (handler != null) ? handler.arrayReader() : listBuilder; 156 | Object ab = ar.init(); 157 | ab = ar.add(ab, firstVal); 158 | while (jp.nextToken() != JsonToken.END_ARRAY) { 159 | ab = ar.add(ab, parseVal(false, cache)); 160 | } 161 | return ar.complete(ab); 162 | } 163 | 164 | // make an empty collection, honoring handler's arrayReader, if present 165 | ArrayReader ar = (handler != null) ? handler.arrayReader() : listBuilder; 166 | return ar.complete(ar.init(0)); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/JsonVerboseEmitter.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.WriteHandler; 7 | import com.fasterxml.jackson.core.JsonGenerator; 8 | 9 | import java.util.Map; 10 | import java.util.function.Function; 11 | 12 | public class JsonVerboseEmitter extends JsonEmitter { 13 | 14 | @Deprecated 15 | public JsonVerboseEmitter(JsonGenerator gen, WriteHandlerMap writeHandlerMap) { 16 | super(gen, writeHandlerMap, null); 17 | } 18 | 19 | public JsonVerboseEmitter(JsonGenerator gen, WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler) { 20 | super(gen, writeHandlerMap, defaultWriteHandler); 21 | } 22 | 23 | public JsonVerboseEmitter(JsonGenerator gen, WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler, Function transform) { 24 | super(gen, writeHandlerMap, defaultWriteHandler, transform); 25 | } 26 | 27 | @Override 28 | public void emitString(String prefix, String tag, String s, boolean asMapKey, WriteCache cache) throws Exception { 29 | String outString = cache.cacheWrite(Util.maybePrefix(prefix, tag, s), asMapKey); 30 | if(asMapKey) 31 | gen.writeFieldName(outString); 32 | else 33 | gen.writeString(outString); 34 | } 35 | 36 | @Override 37 | protected void emitTagged(String t, Object o, boolean ignored, WriteCache cache) throws Exception { 38 | emitMapStart(1L); 39 | emitString(Constants.ESC_TAG, t, "", true, cache); 40 | marshal(o, false, cache); 41 | emitMapEnd(); 42 | } 43 | 44 | @Override 45 | protected void emitMap(Iterable> i, boolean ignored, WriteCache cache) throws Exception { 46 | 47 | long sz = Util.mapSize(i); 48 | 49 | emitMapStart(sz); 50 | for (Map.Entry e : i) { 51 | marshal(e.getKey(), true, cache); 52 | marshal(e.getValue(), false, cache); 53 | } 54 | emitMapEnd(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/KeywordImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.Keyword; 7 | 8 | public class KeywordImpl implements Comparable, Keyword { 9 | 10 | final String ns; 11 | final String name; 12 | transient String _str; 13 | transient int _hash; 14 | 15 | public KeywordImpl(String nsname) { 16 | int i = nsname.indexOf('/'); 17 | if(i == -1) { 18 | ns = null; 19 | name = nsname.intern(); 20 | } else { 21 | ns = nsname.substring(0, i).intern(); 22 | name = nsname.substring(i + 1).intern(); 23 | } 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | if(_str == null){ 29 | if(ns != null) 30 | _str = ":" + ns + "/" + name; 31 | else 32 | _str = ":" + name; 33 | } 34 | return _str; 35 | } 36 | 37 | @Override 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | @Override 43 | public String getNamespace() { 44 | return ns; 45 | } 46 | 47 | @Override 48 | public boolean equals(Object o) { 49 | 50 | if(o == this) 51 | return true; 52 | 53 | //N.B. relies on interned strings 54 | return o instanceof Keyword && 55 | ((Keyword)o).getNamespace() == ns && 56 | ((Keyword)o).getName() == name; 57 | 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | if(_hash == 0) 63 | _hash = 17 * toString().hashCode(); 64 | return _hash; 65 | } 66 | 67 | @Override 68 | public int compareTo(Keyword keyword) { return toString().compareTo(keyword.toString()); } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/LinkImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.Link; 7 | import com.cognitect.transit.URI; 8 | 9 | import java.util.Collections; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public class LinkImpl implements Link { 14 | 15 | private static final String LINK = "link"; 16 | private static final String IMAGE = "image"; 17 | 18 | private static final String HREF = "href"; 19 | private static final String REL = "rel"; 20 | private static final String PROMPT = "prompt"; 21 | private static final String NAME = "name"; 22 | private static final String RENDER = "render"; 23 | 24 | private Map m; 25 | 26 | public LinkImpl(URI href, String rel, String name, String render, String prompt) { 27 | Map m = new HashMap(5); 28 | if (href == null) throw new IllegalArgumentException("Value of href cannot be null"); 29 | m.put(HREF, href); 30 | if (rel == null) throw new IllegalArgumentException("Value of rel cannot be null"); 31 | m.put(REL, rel); 32 | if (name != null) m.put(NAME, name); 33 | if (prompt != null) m.put(PROMPT, prompt); 34 | if (render != null) { 35 | if ((render == LINK) || (render == IMAGE)) { 36 | m.put(RENDER, render); 37 | } else { 38 | throw new IllegalArgumentException("Value of render must be \"link\" or \"image\""); 39 | } 40 | } 41 | this.m = Collections.unmodifiableMap(m); 42 | } 43 | 44 | @SuppressWarnings("unchecked") 45 | public LinkImpl(Map m) { 46 | this.m = (Map) Collections.unmodifiableMap(m); 47 | } 48 | 49 | public Map toMap() { return m; } 50 | 51 | @Override 52 | public URI getHref() { return (URI) m.get(HREF); } 53 | 54 | @Override 55 | public String getRel() { 56 | return (String) m.get(REL); 57 | } 58 | 59 | @Override 60 | public String getName() { 61 | return (String) m.get(NAME); 62 | } 63 | 64 | @Override 65 | public String getPrompt() { 66 | return (String) m.get(PROMPT); 67 | } 68 | 69 | @Override 70 | public String getRender() { 71 | return (String) m.get(RENDER); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/ListBuilderImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.ArrayReader; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ListBuilderImpl implements ArrayReader, List, Object> { 12 | @Override 13 | public List init() { 14 | return init(16); 15 | } 16 | 17 | @Override 18 | public List init(int size) { 19 | return new ArrayList(size); 20 | } 21 | 22 | @Override 23 | public List add(List a, Object item) { 24 | a.add(item); 25 | return a; 26 | } 27 | 28 | @Override 29 | public List complete(List a) { 30 | return a; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/MapBuilderImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.MapReader; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class MapBuilderImpl implements MapReader, Map, Object, Object> { 12 | @Override 13 | public Map init() { 14 | return init(16); 15 | } 16 | 17 | @Override 18 | public Map init(int size) { 19 | return new HashMap(size); 20 | } 21 | 22 | @Override 23 | public Map add(Map m, Object key, Object value) { 24 | m.put(key, value); 25 | return m; 26 | } 27 | 28 | @Override 29 | public Map complete(Map m) { 30 | return m; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/MsgpackEmitter.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.WriteHandler; 7 | import org.msgpack.packer.Packer; 8 | 9 | import java.io.IOException; 10 | import java.util.Map; 11 | import java.util.function.Function; 12 | import java.util.Base64; 13 | 14 | public class MsgpackEmitter extends AbstractEmitter { 15 | 16 | private final Packer gen; 17 | 18 | @Deprecated 19 | public MsgpackEmitter(Packer gen, WriteHandlerMap writeHandlerMap) { 20 | super(writeHandlerMap, null); 21 | this.gen = gen; 22 | } 23 | 24 | public MsgpackEmitter(Packer gen, WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler) { 25 | super(writeHandlerMap, defaultWriteHandler); 26 | this.gen = gen; 27 | } 28 | 29 | public MsgpackEmitter(Packer gen, WriteHandlerMap writeHandlerMap, WriteHandler defaultWriteHandler, Function transform) { 30 | super(writeHandlerMap, defaultWriteHandler, transform); 31 | this.gen = gen; 32 | } 33 | 34 | @Override 35 | public void emit(Object o, boolean asMapKey, WriteCache cache) throws Exception { 36 | marshalTop(o, cache); 37 | } 38 | 39 | @Override 40 | public void emitNil(boolean asMapKey, WriteCache cache) throws Exception { 41 | this.gen.writeNil(); 42 | } 43 | 44 | @Override 45 | public void emitString(String prefix, String tag, String s, boolean asMapKey, WriteCache cache) throws Exception { 46 | String outString = cache.cacheWrite(Util.maybePrefix(prefix, tag, s), asMapKey); 47 | this.gen.write(outString); 48 | } 49 | 50 | @Override 51 | public void emitBoolean(Boolean b, boolean asMapKey, WriteCache cache) throws Exception { 52 | this.gen.write(b); 53 | } 54 | 55 | @Override 56 | public void emitBoolean(boolean b, boolean asMapKey, WriteCache cache) throws Exception { 57 | this.gen.write(b); 58 | } 59 | 60 | @Override 61 | public void emitInteger(Object o, boolean asMapKey, WriteCache cache) throws Exception { 62 | long i = Util.numberToPrimitiveLong(o); 63 | if ((i > Long.MAX_VALUE) || (i < Long.MIN_VALUE)) 64 | this.emitString(Constants.ESC_STR, "i", o.toString(), asMapKey, cache); 65 | this.gen.write(i); 66 | } 67 | 68 | 69 | @Override 70 | public void emitInteger(long i, boolean asMapKey, WriteCache cache) throws Exception { 71 | if ((i > Long.MAX_VALUE) || (i < Long.MIN_VALUE)) 72 | this.emitString(Constants.ESC_STR, "i", String.valueOf(i), asMapKey, cache); 73 | this.gen.write(i); 74 | } 75 | 76 | 77 | @Override 78 | public void emitDouble(Object d, boolean asMapKey, WriteCache cache) throws Exception { 79 | if (d instanceof Double) 80 | this.gen.write((Double) d); 81 | else if (d instanceof Float) 82 | this.gen.write((Float) d); 83 | else 84 | throw new Exception("Unknown floating point type: " + d.getClass()); 85 | } 86 | 87 | @Override 88 | public void emitDouble(float d, boolean asMapKey, WriteCache cache) throws Exception { 89 | this.gen.write(d); 90 | } 91 | 92 | @Override 93 | public void emitDouble(double d, boolean asMapKey, WriteCache cache) throws Exception { 94 | this.gen.write(d); 95 | } 96 | 97 | @Override 98 | public void emitBinary(Object b, boolean asMapKey, WriteCache cache) throws Exception { 99 | byte[] encodedBytes = Base64.getEncoder().encode((byte[])b); 100 | emitString(Constants.ESC_STR, "b", new String(encodedBytes), asMapKey, cache); 101 | } 102 | 103 | @Override 104 | public void emitArrayStart(Long size) throws Exception { 105 | this.gen.writeArrayBegin(size.intValue()); 106 | } 107 | 108 | @Override 109 | public void emitArrayEnd() throws Exception { 110 | this.gen.writeArrayEnd(); 111 | } 112 | 113 | @Override 114 | public void emitMapStart(Long size) throws Exception { 115 | this.gen.writeMapBegin(size.intValue()); 116 | } 117 | 118 | @Override 119 | public void emitMapEnd() throws Exception { 120 | this.gen.writeMapEnd(); 121 | } 122 | 123 | @Override 124 | public void flushWriter() throws IOException { 125 | this.gen.flush(); 126 | } 127 | 128 | @Override 129 | public boolean prefersStrings() { 130 | return false; 131 | } 132 | 133 | @Override 134 | protected void emitMap(Iterable> i, boolean ignored, WriteCache cache) throws Exception { 135 | emitMapStart(Util.mapSize(i)); 136 | for (Map.Entry e : i) { 137 | marshal(e.getKey(), true, cache); 138 | marshal(e.getValue(), false, cache); 139 | } 140 | emitMapEnd(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/MsgpackParser.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.*; 7 | import org.msgpack.type.Value; 8 | import org.msgpack.type.ValueType; 9 | import org.msgpack.unpacker.Unpacker; 10 | 11 | import java.io.IOException; 12 | import java.math.BigInteger; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | 17 | public class MsgpackParser extends AbstractParser { 18 | private final Unpacker mp; 19 | 20 | public MsgpackParser(Unpacker mp, 21 | Map> handlers, 22 | DefaultReadHandler defaultHandler, 23 | MapReader, Object, Object> mapBuilder, 24 | ArrayReader, Object> listBuilder) { 25 | super(handlers, defaultHandler, mapBuilder, listBuilder); 26 | this.mp = mp; 27 | } 28 | 29 | private Object parseLong() throws IOException { 30 | Value val = mp.readValue(); 31 | 32 | try { 33 | return val.asIntegerValue().getLong(); 34 | } 35 | catch (Exception ex) { 36 | BigInteger bi = new BigInteger(val.asRawValue().getString()); 37 | } 38 | 39 | return val; 40 | } 41 | 42 | @Override 43 | public Object parse(ReadCache cache) throws IOException { 44 | return parseVal(false, cache); 45 | } 46 | 47 | @Override 48 | public Object parseVal(boolean asMapKey, ReadCache cache) throws IOException { 49 | switch (mp.getNextType()) { 50 | case MAP: 51 | return parseMap(asMapKey, cache, null); 52 | case ARRAY: 53 | return parseArray(asMapKey, cache, null); 54 | case RAW: 55 | return cache.cacheRead(mp.readValue().asRawValue().getString(), asMapKey, this); 56 | case INTEGER: 57 | return parseLong(); 58 | case FLOAT: 59 | return mp.readValue().asFloatValue().getDouble(); 60 | case BOOLEAN: 61 | return mp.readValue().asBooleanValue().getBoolean(); 62 | case NIL: 63 | mp.readNil(); 64 | } 65 | 66 | return null; 67 | } 68 | 69 | @Override 70 | public Object parseMap(boolean ignored, ReadCache cache, MapReadHandler handler) throws IOException { 71 | 72 | int sz = this.mp.readMapBegin(); 73 | 74 | MapReader mr = (handler != null) ? handler.mapReader() : mapBuilder; 75 | 76 | Object mb = mr.init(sz); 77 | 78 | for (int remainder = sz; remainder > 0; remainder--) { 79 | Object key = parseVal(true, cache); 80 | if (key instanceof Tag) { 81 | String tag = ((Tag)key).getValue(); 82 | ReadHandler val_handler = getHandler(tag); 83 | Object val; 84 | if (val_handler != null) { 85 | if (this.mp.getNextType() == ValueType.MAP && val_handler instanceof MapReadHandler) { 86 | // use map reader to decode value 87 | val = parseMap(false, cache, (MapReadHandler) val_handler); 88 | } else if (this.mp.getNextType() == ValueType.ARRAY && val_handler instanceof ArrayReadHandler) { 89 | // use array reader to decode value 90 | val = parseArray(false, cache, (ArrayReadHandler) val_handler); 91 | } else { 92 | // read value and decode normally 93 | val = val_handler.fromRep(parseVal(false, cache)); 94 | } 95 | } else { 96 | // default decode 97 | val = this.decode(tag, parseVal(false, cache)); 98 | } 99 | 100 | this.mp.readMapEnd(true); 101 | return val; 102 | } else { 103 | mb = mr.add(mb, key, parseVal(false, cache)); 104 | } 105 | } 106 | 107 | this.mp.readMapEnd(true); 108 | return mr.complete(mb); 109 | } 110 | 111 | @Override 112 | public Object parseArray(boolean ignored, ReadCache cache, ArrayReadHandler handler) throws IOException { 113 | 114 | int sz = this.mp.readArrayBegin(); 115 | 116 | ArrayReader ar = (handler != null) ? handler.arrayReader() : listBuilder; 117 | 118 | Object ab = ar.init(sz); 119 | 120 | for (int remainder = sz; remainder > 0; remainder--) { 121 | Object val = parseVal(false, cache); 122 | if ((val != null) && (val instanceof Tag)) { 123 | // it's a tagged value 124 | String tag = ((Tag) val).getValue(); 125 | ReadHandler val_handler = getHandler(tag); 126 | if (val_handler != null) { 127 | if (this.mp.getNextType() == ValueType.MAP && val_handler instanceof MapReadHandler) { 128 | // use map reader to decode value 129 | val = parseMap(false, cache, (MapReadHandler) val_handler); 130 | } else if (this.mp.getNextType() == ValueType.ARRAY && val_handler instanceof ArrayReadHandler) { 131 | // use array reader to decode value 132 | val = parseArray(false, cache, (ArrayReadHandler) val_handler); 133 | } else { 134 | // read value and decode normally 135 | val = val_handler.fromRep(parseVal(false, cache)); 136 | } 137 | } else { 138 | // default decode 139 | val = this.decode(tag, parseVal(false, cache)); 140 | } 141 | this.mp.readArrayEnd(); 142 | return val; 143 | } else { 144 | // fall through to regular parse 145 | ab = ar.add(ab, val); 146 | } 147 | } 148 | 149 | this.mp.readArrayEnd(); 150 | return ar.complete(ab); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Parser.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.ArrayReadHandler; 7 | import com.cognitect.transit.MapReadHandler; 8 | 9 | import java.io.IOException; 10 | 11 | public interface Parser { 12 | Object parse(ReadCache cache) throws IOException; 13 | Object parseVal(boolean asMapKey, ReadCache cache) throws IOException; 14 | Object parseMap(boolean asMapKey, ReadCache cache, MapReadHandler handler) throws IOException; 15 | Object parseArray(boolean asMapKey, ReadCache cache, ArrayReadHandler handler) throws IOException; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Quote.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | public class Quote { 7 | 8 | public final Object o; 9 | 10 | public Quote(Object o) { 11 | this.o = o; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/RatioImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.Ratio; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.BigInteger; 10 | 11 | public class RatioImpl implements Ratio { 12 | 13 | private final BigInteger numerator; 14 | private final BigInteger denominator; 15 | 16 | public RatioImpl(BigInteger numerator, BigInteger denominator) { 17 | this.numerator = numerator; 18 | this.denominator = denominator; 19 | } 20 | 21 | @Override 22 | public boolean equals(Object o) { 23 | 24 | if(o instanceof Ratio && ((Ratio)o).getNumerator() == numerator && ((Ratio)o).getDenominator() == denominator) 25 | return true; 26 | else 27 | 28 | return false; 29 | } 30 | 31 | @Override 32 | public Double getValue() { 33 | BigDecimal n = new BigDecimal(numerator); 34 | BigDecimal d = new BigDecimal(denominator); 35 | return n.divide(d).doubleValue(); 36 | } 37 | 38 | @Override 39 | public BigInteger getNumerator() { 40 | return numerator; 41 | } 42 | 43 | @Override 44 | public BigInteger getDenominator() { 45 | return denominator; 46 | } 47 | 48 | @Override 49 | public int compareTo(Ratio o) { 50 | return getValue().compareTo(o.getValue()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/ReadCache.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | public class ReadCache { 7 | 8 | private Object[] cache; 9 | private int index; 10 | 11 | public ReadCache() { 12 | cache = new Object[WriteCache.MAX_CACHE_ENTRIES]; 13 | index = 0; 14 | } 15 | 16 | private boolean cacheCode(String s) { 17 | 18 | if((s.charAt(0) == Constants.SUB) && (!s.equals(Constants.MAP_AS_ARRAY))) 19 | return true; 20 | else 21 | return false; 22 | } 23 | 24 | private int codeToIndex(String s) { 25 | 26 | int sz = s.length(); 27 | if (sz == 2) { 28 | return ((int)s.charAt(1) - WriteCache.BASE_CHAR_IDX); 29 | } else { 30 | return (((int)s.charAt(1) - WriteCache.BASE_CHAR_IDX) * WriteCache.CACHE_CODE_DIGITS) + 31 | ((int)s.charAt(2) - WriteCache.BASE_CHAR_IDX); 32 | } 33 | } 34 | 35 | public Object cacheRead(String s, boolean asMapKey) { return cacheRead(s, asMapKey, null); } 36 | 37 | public Object cacheRead(String s, boolean asMapKey, AbstractParser p) { 38 | if(s.length() != 0) { 39 | if(cacheCode(s)) { 40 | return cache[codeToIndex(s)]; 41 | } else if(WriteCache.isCacheable(s, asMapKey)) { 42 | if(index == WriteCache.MAX_CACHE_ENTRIES) { 43 | init(); 44 | } 45 | return cache[index++] = (p != null ? p.parseString(s) : s); 46 | } 47 | } 48 | return p != null ? p.parseString(s) : s; 49 | } 50 | 51 | public ReadCache init(){ 52 | //need not clear array 53 | index = 0; 54 | return this; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/ReadHandlerMap.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit.impl; 16 | 17 | import com.cognitect.transit.ReadHandler; 18 | 19 | import java.util.*; 20 | 21 | public class ReadHandlerMap implements Map> { 22 | 23 | private final Map> handlers; 24 | 25 | public ReadHandlerMap(Map> customHandlers) { 26 | this.handlers = ReaderFactory.defaultHandlers(); 27 | if (customHandlers != null) { 28 | disallowOverridingGroundTypes(customHandlers); 29 | handlers.putAll(customHandlers); 30 | } 31 | } 32 | 33 | private static void disallowOverridingGroundTypes(Map> handlers) { 34 | if (handlers != null) { 35 | String groundTypeTags[] = {"_", "s", "?", "i", "d", "b", "'", "map", "array"}; 36 | for (String tag : groundTypeTags) { 37 | if (handlers.containsKey(tag)) { 38 | throw new IllegalArgumentException("Cannot override decoding for transit ground type, tag " + tag); 39 | } 40 | } 41 | } 42 | } 43 | 44 | private Map> getUnderlyingMap() { 45 | return handlers; 46 | } 47 | 48 | @Override 49 | public int size() { 50 | return handlers.size(); 51 | } 52 | 53 | @Override 54 | public boolean isEmpty() { 55 | return handlers.isEmpty(); 56 | } 57 | 58 | @Override 59 | public boolean containsKey(Object key) { 60 | return handlers.containsKey(key); 61 | } 62 | 63 | @Override 64 | public boolean containsValue(Object value) { 65 | return handlers.containsValue(value); 66 | } 67 | 68 | @Override 69 | public ReadHandler get(Object key) { 70 | return handlers.get(key); 71 | } 72 | 73 | @Override 74 | public ReadHandler put(String key, ReadHandler value) { 75 | throw new UnsupportedOperationException("ReadHandlerMap is read-only"); 76 | } 77 | 78 | @Override 79 | public ReadHandler remove(Object key) { 80 | throw new UnsupportedOperationException("ReadHandlerMap is read-only"); 81 | } 82 | 83 | @Override 84 | public void putAll(Map> m) { 85 | throw new UnsupportedOperationException("ReadHandlerMap is read-only"); 86 | } 87 | 88 | @Override 89 | public void clear() { 90 | throw new UnsupportedOperationException("ReadHandlerMap is read-only"); 91 | } 92 | 93 | @Override 94 | public Set keySet() { 95 | return handlers.keySet(); 96 | } 97 | 98 | @Override 99 | public Collection> values() { 100 | return handlers.values(); 101 | } 102 | 103 | @Override 104 | public Set>> entrySet() { 105 | return handlers.entrySet(); 106 | } 107 | 108 | @Override 109 | public int hashCode() { 110 | return handlers.hashCode(); 111 | } 112 | 113 | public boolean equals(Object other) { 114 | return (other instanceof ReadHandlerMap) && getUnderlyingMap().equals(((ReadHandlerMap)other).getUnderlyingMap()); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/ReadHandlers.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.*; 7 | 8 | import java.math.BigDecimal; 9 | import java.math.BigInteger; 10 | import java.util.*; 11 | 12 | public class ReadHandlers { 13 | 14 | public static class BigDecimalReadHandler implements ReadHandler { 15 | 16 | @Override 17 | public Object fromRep(Object rep) { 18 | return new BigDecimal((String)rep); 19 | } 20 | } 21 | 22 | public static class BinaryReadHandler implements ReadHandler { 23 | 24 | @Override 25 | public Object fromRep(String rep) { 26 | 27 | return Base64.getDecoder().decode(rep.getBytes()); 28 | } 29 | } 30 | 31 | public static class BooleanReadHandler implements ReadHandler { 32 | 33 | @Override 34 | public Object fromRep(String rep) { 35 | return rep.equals("t"); 36 | } 37 | } 38 | 39 | public static class CharacterReadHandler implements ReadHandler { 40 | 41 | @Override 42 | public Object fromRep(String rep) { 43 | 44 | return rep.charAt(0); 45 | } 46 | } 47 | 48 | public static class CmapReadHandler implements ArrayReadHandler, Object, Object> { 49 | 50 | @Override 51 | public Map fromRep(Object objects) { 52 | throw new UnsupportedOperationException(); 53 | } 54 | 55 | @Override 56 | public ArrayReader,Object> arrayReader() { 57 | return new ArrayReader, Object>() { 58 | private final Object MARKER = new Object(); 59 | Map m = null; 60 | Object next_key = MARKER; 61 | 62 | @Override 63 | public Object init() { 64 | return init(16); 65 | } 66 | 67 | @Override 68 | public Object init(int size) { 69 | m = new HashMap(size); 70 | return this; 71 | } 72 | 73 | @Override 74 | public Object add(Object ar, Object item) { 75 | if (next_key != MARKER) { 76 | m.put(next_key, item); 77 | next_key = MARKER; 78 | } else { 79 | next_key = item; 80 | } 81 | return this; 82 | } 83 | 84 | @Override 85 | public Map complete(Object ar) { 86 | return m; 87 | } 88 | }; 89 | } 90 | } 91 | 92 | public static class DoubleReadHandler implements ReadHandler { 93 | 94 | @Override 95 | public Object fromRep(String rep) { 96 | 97 | return Double.valueOf(rep); 98 | } 99 | } 100 | 101 | public static class SpecialNumberReadHandler implements ReadHandler { 102 | @Override 103 | public Double fromRep(String rep) { 104 | if (rep.equals("NaN")) { 105 | return Double.NaN; 106 | } else if (rep.equals("INF")) { 107 | return Double.POSITIVE_INFINITY; 108 | } else if (rep.equals("-INF")) { 109 | return Double.NEGATIVE_INFINITY; 110 | } else { 111 | throw new RuntimeException(); 112 | } 113 | } 114 | } 115 | 116 | public static class IdentityReadHandler implements ReadHandler { 117 | 118 | @Override 119 | public Object fromRep(Object rep) { 120 | return rep; 121 | } 122 | } 123 | 124 | public static class IntegerReadHandler implements ReadHandler { 125 | 126 | @Override 127 | public Object fromRep(String rep) { 128 | try { 129 | return Long.parseLong(rep); 130 | }catch(NumberFormatException e) { 131 | throw new RuntimeException(e); 132 | } 133 | } 134 | } 135 | 136 | public static class BigIntegerReadHandler implements ReadHandler { 137 | 138 | @Override 139 | public Object fromRep(String rep) { 140 | return new BigInteger(rep); 141 | } 142 | } 143 | 144 | public static class KeywordReadHandler implements ReadHandler { 145 | 146 | @Override 147 | public Object fromRep(String rep) { 148 | return TransitFactory.keyword(rep); 149 | } 150 | } 151 | 152 | public static class ListReadHandler implements ArrayReadHandler,List, Object, Object> { 153 | 154 | @Override 155 | public List fromRep(Object objects) { 156 | throw new UnsupportedOperationException(); 157 | } 158 | 159 | @Override 160 | public ArrayReader, List, Object> arrayReader() { 161 | return new ArrayReader,List,Object>() { 162 | @Override 163 | public List init() { 164 | return new LinkedList(); 165 | } 166 | 167 | @Override 168 | public List init(int size) { 169 | return init(); 170 | } 171 | 172 | @Override 173 | public List add(List a, Object item) { 174 | a.add(item); 175 | return a; 176 | } 177 | 178 | @Override 179 | public List complete(List a) { 180 | return a; 181 | } 182 | }; 183 | } 184 | 185 | } 186 | 187 | public static class NullReadHandler implements ReadHandler { 188 | 189 | @Override 190 | public Object fromRep(Object ignored) { return null; } 191 | } 192 | 193 | public static class RatioReadHandler implements ReadHandler> { 194 | 195 | @Override 196 | public Object fromRep(List rep) { 197 | return new RatioImpl(rep.get(0), rep.get(1)); 198 | } 199 | } 200 | 201 | public static class SetReadHandler implements ArrayReadHandler,Set,Object, Object> { 202 | 203 | 204 | @Override 205 | public Set fromRep(Object objects) { 206 | throw new UnsupportedOperationException(); 207 | } 208 | 209 | @Override 210 | public ArrayReader, Set, Object> arrayReader() { 211 | return new ArrayReader,Set,Object>() { 212 | @Override 213 | public Set init() { 214 | return init(16); 215 | } 216 | 217 | @Override 218 | public Set init(int size) { 219 | return new HashSet(size); 220 | } 221 | 222 | @Override 223 | public Set add(Set a, Object item) { 224 | a.add(item); 225 | return a; 226 | } 227 | 228 | @Override 229 | public Set complete(Set a) { 230 | return a; 231 | } 232 | }; 233 | } 234 | } 235 | 236 | public static class SymbolReadHandler implements ReadHandler { 237 | 238 | @Override 239 | public Object fromRep(String rep) { 240 | return TransitFactory.symbol(rep); 241 | } 242 | } 243 | 244 | public static class VerboseTimeReadHandler implements ReadHandler { 245 | 246 | @Override 247 | public Object fromRep(String rep) { 248 | Calendar t = javax.xml.bind.DatatypeConverter.parseDateTime(rep); 249 | t.setTimeZone(TimeZone.getTimeZone("Zulu")); 250 | return t.getTime(); 251 | } 252 | } 253 | 254 | public static class TimeReadHandler implements ReadHandler { 255 | 256 | @Override 257 | public Object fromRep(Object rep) { 258 | Long n; 259 | if (rep instanceof Long) 260 | n = (Long) rep; 261 | else 262 | n = Long.decode((String) rep); 263 | 264 | Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Zulu")); 265 | cal.setTimeInMillis(n); 266 | return cal.getTime(); 267 | } 268 | } 269 | 270 | 271 | public static class URIReadHandler implements ReadHandler { 272 | 273 | @Override 274 | public Object fromRep(String rep) { return new URIImpl(rep); } 275 | } 276 | 277 | public static class UUIDReadHandler implements ReadHandler { 278 | 279 | @Override 280 | @SuppressWarnings("unchecked") 281 | public Object fromRep(Object rep) { 282 | 283 | if(rep instanceof String) { 284 | return UUID.fromString((String) rep); 285 | } 286 | else { 287 | List l = (List) rep; 288 | return new UUID(l.get(0), l.get(1)); 289 | } 290 | } 291 | } 292 | 293 | public static class LinkReadHandler implements ReadHandler> { 294 | @Override 295 | public Object fromRep(Map rep) { 296 | return new LinkImpl(rep); 297 | } 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/ReaderFactory.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.*; 7 | import com.cognitect.transit.SPI.ReaderSPI; 8 | import com.fasterxml.jackson.core.JsonFactory; 9 | import org.msgpack.MessagePack; 10 | 11 | import java.io.InputStream; 12 | import java.util.HashMap; 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | public class ReaderFactory { 17 | 18 | private static Map>, ReadHandlerMap> handlerCache = new Cache>, ReadHandlerMap>(); 19 | 20 | public static Map> defaultHandlers() { 21 | 22 | Map> handlers = new HashMap>(); 23 | 24 | handlers.put(":", new ReadHandlers.KeywordReadHandler()); 25 | handlers.put("$", new ReadHandlers.SymbolReadHandler()); 26 | handlers.put("i", new ReadHandlers.IntegerReadHandler()); 27 | handlers.put("?", new ReadHandlers.BooleanReadHandler()); 28 | handlers.put("_", new ReadHandlers.NullReadHandler()); 29 | handlers.put("f", new ReadHandlers.BigDecimalReadHandler()); 30 | handlers.put("n", new ReadHandlers.BigIntegerReadHandler()); 31 | handlers.put("d", new ReadHandlers.DoubleReadHandler()); 32 | handlers.put("z", new ReadHandlers.SpecialNumberReadHandler()); 33 | handlers.put("c", new ReadHandlers.CharacterReadHandler()); 34 | handlers.put("t", new ReadHandlers.VerboseTimeReadHandler()); 35 | handlers.put("m", new ReadHandlers.TimeReadHandler()); 36 | handlers.put("r", new ReadHandlers.URIReadHandler()); 37 | handlers.put("u", new ReadHandlers.UUIDReadHandler()); 38 | handlers.put("b", new ReadHandlers.BinaryReadHandler()); 39 | handlers.put("\'", new ReadHandlers.IdentityReadHandler()); 40 | handlers.put("set", new ReadHandlers.SetReadHandler()); 41 | handlers.put("list", new ReadHandlers.ListReadHandler()); 42 | handlers.put("ratio", new ReadHandlers.RatioReadHandler()); 43 | handlers.put("cmap", new ReadHandlers.CmapReadHandler()); 44 | handlers.put("link", new ReadHandlers.LinkReadHandler()); 45 | return handlers; 46 | } 47 | 48 | public static DefaultReadHandler> defaultDefaultHandler() { 49 | return new DefaultReadHandler>() { 50 | @Override 51 | public TaggedValue fromRep(String tag, Object rep) { 52 | return TransitFactory.taggedValue(tag, rep); 53 | } 54 | }; 55 | } 56 | 57 | private static Map> handlerMap(Map> customHandlers) { 58 | if (customHandlers instanceof ReadHandlerMap) { 59 | return customHandlers; 60 | } 61 | 62 | synchronized (ReaderFactory.class) { 63 | ReadHandlerMap readHandlerMap = handlerCache.get(customHandlers); 64 | if (readHandlerMap == null) { 65 | readHandlerMap = new ReadHandlerMap(customHandlers); 66 | handlerCache.put(customHandlers, readHandlerMap); 67 | } 68 | return readHandlerMap; 69 | } 70 | } 71 | 72 | private static DefaultReadHandler defaultHandler(DefaultReadHandler customDefaultHandler) { 73 | return customDefaultHandler != null ? customDefaultHandler : defaultDefaultHandler(); 74 | } 75 | 76 | public static Reader getJsonInstance(InputStream in, 77 | Map> handlers, 78 | DefaultReadHandler customDefaultHandler) { 79 | return new JsonReaderImpl(in, handlerMap(handlers), defaultHandler(customDefaultHandler)); 80 | } 81 | 82 | public static Reader getMsgpackInstance(InputStream in, 83 | Map> handlers, 84 | DefaultReadHandler customDefaultHandler) { 85 | return new MsgPackReaderImpl(in, handlerMap(handlers), defaultHandler(customDefaultHandler)); 86 | } 87 | 88 | private abstract static class ReaderImpl implements Reader, ReaderSPI { 89 | 90 | InputStream in; 91 | Map> handlers; 92 | DefaultReadHandler defaultHandler; 93 | MapReader, Object, Object> mapBuilder; 94 | ArrayReader, Object> listBuilder; 95 | ReadCache cache; 96 | AbstractParser p; 97 | boolean initialized; 98 | 99 | public ReaderImpl(InputStream in, Map> handlers, DefaultReadHandler defaultHandler) { 100 | this.initialized = false; 101 | this.in = in; 102 | this.handlers = handlers; 103 | this.defaultHandler = defaultHandler; 104 | this.cache = new ReadCache(); 105 | } 106 | 107 | @Override 108 | @SuppressWarnings("unchecked") 109 | public T read() { 110 | if (!initialized) initialize(); 111 | try { 112 | return (T) p.parse(cache.init()); 113 | } catch (Throwable e) { 114 | throw new RuntimeException(e); 115 | } 116 | } 117 | 118 | @Override 119 | public Reader setBuilders(MapReader, Object, Object> mapBuilder, 120 | ArrayReader, Object> listBuilder) { 121 | if (initialized) throw new IllegalStateException("Cannot set builders after read has been called"); 122 | this.mapBuilder = mapBuilder; 123 | this.listBuilder = listBuilder; 124 | return this; 125 | } 126 | 127 | private void ensureBuilders() { 128 | if (mapBuilder == null) mapBuilder = new MapBuilderImpl(); 129 | if (listBuilder == null) listBuilder = new ListBuilderImpl(); 130 | } 131 | 132 | protected void initialize() { 133 | ensureBuilders(); 134 | p = createParser(); 135 | initialized = true; 136 | } 137 | 138 | protected abstract AbstractParser createParser(); 139 | } 140 | 141 | private static class JsonReaderImpl extends ReaderImpl { 142 | 143 | public JsonReaderImpl(InputStream in, Map> handlers, DefaultReadHandler defaultHandler) { 144 | super(in, handlers, defaultHandler); 145 | } 146 | 147 | @Override 148 | protected AbstractParser createParser() { 149 | try { 150 | JsonFactory jf = new JsonFactory(); 151 | return new JsonParser(jf.createParser(in), handlers, defaultHandler, 152 | mapBuilder, listBuilder); 153 | } catch (Throwable e) { 154 | throw new RuntimeException(e); 155 | } 156 | } 157 | } 158 | 159 | private static class MsgPackReaderImpl extends ReaderImpl { 160 | 161 | public MsgPackReaderImpl(InputStream in, Map> handlers, DefaultReadHandler defaultHandler) { 162 | super(in, handlers, defaultHandler); 163 | } 164 | 165 | @Override 166 | protected AbstractParser createParser() { 167 | MessagePack mp = new MessagePack(); 168 | return new MsgpackParser(mp.createUnpacker(in), handlers, defaultHandler, 169 | mapBuilder, listBuilder); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/SymbolImpl.java: -------------------------------------------------------------------------------- 1 | // All rights reserved. 2 | // Copyright (c) Cognitect, Inc. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.Named; 7 | import com.cognitect.transit.Symbol; 8 | 9 | public class SymbolImpl implements Symbol, Comparable, Named { 10 | 11 | final String ns; 12 | final String name; 13 | String _str; 14 | 15 | public SymbolImpl(String nsname) { 16 | int i = nsname.indexOf('/'); 17 | if(i == -1 || nsname.equals("/")) { 18 | ns = null; 19 | name = nsname.intern(); 20 | } else { 21 | ns = nsname.substring(0, i); 22 | name = nsname.substring(i + 1); 23 | } 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | if(_str == null){ 29 | if(ns != null) 30 | _str = (ns + "/" + name).intern(); 31 | else 32 | _str = name; 33 | } 34 | return _str; 35 | } 36 | 37 | @Override 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | @Override 43 | public String getNamespace() { 44 | return ns; 45 | } 46 | 47 | @Override 48 | public String getValue() { return toString(); } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if(o == this) 53 | return true; 54 | 55 | if(o instanceof SymbolImpl && ((SymbolImpl)o).getValue().equals(getValue())) 56 | return true; 57 | else 58 | return false; 59 | } 60 | 61 | @Override 62 | public int hashCode() { 63 | return 19 * getValue().hashCode(); 64 | } 65 | 66 | @Override 67 | public int compareTo(Symbol symbol) { 68 | return getValue().compareTo(((SymbolImpl)symbol).getValue()); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Tag.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | public class Tag { 7 | private String value; 8 | public Tag(String value) { 9 | this.value = value; 10 | } 11 | public String getValue() { 12 | return value; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/TagFinder.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | public interface TagFinder { 7 | 8 | String getTag(Object o); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/TagProvider.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | public interface TagProvider { 7 | public String getTag(Object o); 8 | public String getTagAfterPossibleTransform(Object o); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/TagProviderAware.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | public interface TagProviderAware { 7 | void setTagProvider(TagProvider tagProvider); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/TaggedValueImpl.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.TaggedValue; 7 | 8 | public class TaggedValueImpl implements TaggedValue { 9 | 10 | private final String tag; 11 | private final T rep; 12 | 13 | public TaggedValueImpl(String tag, T rep) { 14 | this.tag = tag; 15 | this.rep = rep; 16 | } 17 | 18 | public String getTag() { 19 | return tag; 20 | } 21 | 22 | public T getRep() { 23 | return rep; 24 | } 25 | 26 | @Override 27 | public boolean equals(Object o) { 28 | 29 | if(this == o) 30 | return true; 31 | 32 | if(!(o instanceof TaggedValueImpl)) 33 | return false; 34 | 35 | TaggedValueImpl other = (TaggedValueImpl)o; 36 | return (this.tag.equals(other.getTag()) && 37 | this.rep.equals(other.getRep())); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | 43 | int result = 17; 44 | result = 31 * result * tag.hashCode(); 45 | result = 31 * result * rep.hashCode(); 46 | 47 | return result; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/URIImpl.java: -------------------------------------------------------------------------------- 1 | // All rights reserved. 2 | // Copyright (c) Cognitect, Inc. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.URI; 7 | 8 | public class URIImpl implements URI, Comparable { 9 | 10 | String uri; 11 | 12 | public URIImpl(String uri) { 13 | this.uri = uri; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return uri; 19 | } 20 | 21 | @Override 22 | public String getValue() { return uri; } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if(o == this) 27 | return true; 28 | 29 | if(o instanceof URIImpl && ((URIImpl)o).getValue().equals(getValue())) 30 | return true; 31 | else 32 | return false; 33 | } 34 | 35 | @Override 36 | public int hashCode() { 37 | return 19 * getValue().hashCode(); 38 | } 39 | 40 | @Override 41 | public int compareTo(URI uri) { 42 | return getValue().compareTo(((URIImpl)uri).getValue()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/Util.java: -------------------------------------------------------------------------------- 1 | package com.cognitect.transit.impl; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.Collection; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * Created by fogus on 4/2/14. 10 | */ 11 | public class Util { 12 | 13 | public static long numberToPrimitiveLong(Object o) throws Exception { 14 | long i; 15 | 16 | if(o instanceof Long) 17 | i = ((Long)o).longValue(); 18 | else if(o instanceof Integer) 19 | i = ((Integer)o).longValue(); 20 | else if(o instanceof Short) 21 | i = ((Short)o).longValue(); 22 | else if(o instanceof Byte) 23 | i = ((Byte)o).longValue(); 24 | else 25 | throw new Exception("Unknown integer type: " + o.getClass()); 26 | 27 | return i; 28 | } 29 | 30 | static String maybePrefix(String prefix, String tag, String s){ 31 | if(prefix == null && tag == null) 32 | return s; 33 | prefix = (prefix == null) ? "" : prefix; 34 | tag = (tag == null) ? "" : tag; 35 | StringBuilder sb = new StringBuilder(prefix.length()+tag.length()+s.length()); 36 | return sb.append(prefix).append(tag).append(s).toString(); 37 | } 38 | 39 | public static long arraySize(Object a) { 40 | if(a instanceof Collection) 41 | return ((Collection)a).size(); 42 | else if (a.getClass().isArray()) 43 | return Array.getLength(a); 44 | else if (a instanceof Iterable) { 45 | int i = 0; 46 | for (Object o : (Iterable) a) { 47 | i++; 48 | } 49 | return i; 50 | } else if (a instanceof List) { 51 | return ((List)a).size(); 52 | } 53 | else 54 | throw new UnsupportedOperationException("arraySize not supported on this type " + a.getClass().getSimpleName()); 55 | 56 | } 57 | 58 | public static long mapSize(Object m) { 59 | if(m instanceof Collection) 60 | return ((Collection) m).size(); 61 | else if (m instanceof Map) 62 | return ((Map)m).size(); 63 | else 64 | throw new UnsupportedOperationException("mapSize not supported on this type " + m.getClass().getSimpleName()); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/WriteCache.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class WriteCache { 10 | 11 | public static final int MIN_SIZE_CACHEABLE = 4; 12 | public static final int CACHE_CODE_DIGITS = 44; 13 | public static final int MAX_CACHE_ENTRIES = CACHE_CODE_DIGITS * CACHE_CODE_DIGITS; 14 | public static final int BASE_CHAR_IDX = 48; 15 | 16 | private final Map cache; 17 | private int index; 18 | private boolean enabled; 19 | 20 | public WriteCache() { this(true); } 21 | 22 | public WriteCache(boolean enabled) { 23 | index = 0; 24 | cache = new HashMap(MAX_CACHE_ENTRIES); 25 | this.enabled = enabled; 26 | } 27 | 28 | public static boolean isCacheable(String s, boolean asMapKey) { 29 | return (s.length() >= MIN_SIZE_CACHEABLE) && 30 | (asMapKey || 31 | (s.charAt(0) == Constants.ESC && 32 | (s.charAt(1) == ':' || s.charAt(1) == '$' || s.charAt(1) == '#'))); 33 | } 34 | 35 | private String indexToCode(int index) { 36 | int hi = index / CACHE_CODE_DIGITS; 37 | int lo = index % CACHE_CODE_DIGITS; 38 | if (hi == 0) { 39 | return Constants.SUB_STR + (char)(lo + BASE_CHAR_IDX); 40 | } else { 41 | return Constants.SUB_STR + (char)(hi + BASE_CHAR_IDX) + (char)(lo + BASE_CHAR_IDX); 42 | } 43 | } 44 | 45 | public String cacheWrite(String s, boolean asMapKey) { 46 | 47 | if(enabled && isCacheable(s, asMapKey)) { 48 | String val = cache.get(s); 49 | if(val != null) 50 | return val; 51 | else { 52 | if(index == MAX_CACHE_ENTRIES) 53 | init(); 54 | String code = indexToCode(index++); 55 | cache.put(s, code); 56 | } 57 | } 58 | return s; 59 | } 60 | 61 | public WriteCache init(){ 62 | index = 0; 63 | cache.clear(); 64 | return this; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/WriteHandlerMap.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit.impl; 16 | 17 | import com.cognitect.transit.*; 18 | 19 | import java.math.BigDecimal; 20 | import java.math.BigInteger; 21 | import java.util.*; 22 | import java.util.function.Function; 23 | 24 | public class WriteHandlerMap implements TagProvider, Map> { 25 | 26 | private static Map> defaultHandlers() { 27 | 28 | Map> handlers = new HashMap>(); 29 | 30 | WriteHandler integerHandler = new WriteHandlers.IntegerWriteHandler(); 31 | WriteHandler uriHandler = new WriteHandlers.ToStringWriteHandler("r"); 32 | WriteHandler arrayHandler = new WriteHandlers.ArrayWriteHandler(); 33 | 34 | handlers.put(Boolean.class, new WriteHandlers.BooleanWriteHandler()); 35 | handlers.put(null, new WriteHandlers.NullWriteHandler()); 36 | handlers.put(String.class, new WriteHandlers.ToStringWriteHandler("s")); 37 | handlers.put(Integer.class, integerHandler); 38 | handlers.put(Long.class, integerHandler); 39 | handlers.put(Short.class, integerHandler); 40 | handlers.put(Byte.class, integerHandler); 41 | handlers.put(BigInteger.class, new WriteHandlers.ToStringWriteHandler("n")); 42 | handlers.put(Float.class, new WriteHandlers.FloatWriteHandler()); 43 | handlers.put(Double.class, new WriteHandlers.DoubleWriteHandler()); 44 | handlers.put(BigDecimal.class, new WriteHandlers.ToStringWriteHandler("f")); 45 | handlers.put(Character.class, new WriteHandlers.ToStringWriteHandler("c")); 46 | handlers.put(Keyword.class, new WriteHandlers.KeywordWriteHandler()); 47 | handlers.put(Symbol.class, new WriteHandlers.ToStringWriteHandler("$")); 48 | handlers.put(byte[].class, new WriteHandlers.BinaryWriteHandler()); 49 | handlers.put(UUID.class, new WriteHandlers.UUIDWriteHandler()); 50 | handlers.put(java.net.URI.class, uriHandler); 51 | handlers.put(com.cognitect.transit.URI.class, uriHandler); 52 | handlers.put(List.class, new WriteHandlers.ListWriteHandler()); 53 | handlers.put(Object[].class, arrayHandler); 54 | handlers.put(int[].class, arrayHandler); 55 | handlers.put(long[].class, arrayHandler); 56 | handlers.put(float[].class, arrayHandler); 57 | handlers.put(double[].class, arrayHandler); 58 | handlers.put(short[].class, arrayHandler); 59 | handlers.put(boolean[].class, arrayHandler); 60 | handlers.put(char[].class, arrayHandler); 61 | handlers.put(Set.class, new WriteHandlers.SetWriteHandler()); 62 | handlers.put(Date.class, new WriteHandlers.TimeWriteHandler()); 63 | handlers.put(Ratio.class, new WriteHandlers.RatioWriteHandler()); 64 | handlers.put(LinkImpl.class, new WriteHandlers.LinkWriteHandler()); 65 | handlers.put(Quote.class, new WriteHandlers.QuoteAbstractEmitter()); 66 | handlers.put(TaggedValue.class, new WriteHandlers.TaggedValueWriteHandler()); 67 | 68 | return Collections.unmodifiableMap(handlers); 69 | } 70 | 71 | public final static Map> defaults = defaultHandlers(); 72 | 73 | private final Map> handlers; 74 | private WriteHandlerMap verboseHandlerMap; 75 | Function transform = null; 76 | 77 | public WriteHandlerMap() { 78 | this(null); 79 | } 80 | 81 | public WriteHandlerMap(Map> customHandlers) { 82 | handlers = new HashMap>(); 83 | if (customHandlers instanceof WriteHandlerMap) { 84 | handlers.putAll(customHandlers); 85 | } else { 86 | handlers.putAll(defaults); 87 | if (customHandlers != null) { 88 | handlers.putAll(customHandlers); 89 | } 90 | } 91 | handlers.put(Map.class, new WriteHandlers.MapWriteHandler()); 92 | setTagProvider(handlers); 93 | } 94 | 95 | public WriteHandlerMap verboseWriteHandlerMap() { 96 | if (verboseHandlerMap == null) { 97 | Map> verboseHandlers = new HashMap>(handlers.size()); 98 | for (Map.Entry> entry : handlers.entrySet()) { 99 | WriteHandler verboseHandler = entry.getValue().getVerboseHandler(); 100 | verboseHandlers.put( 101 | entry.getKey(), 102 | (verboseHandler == null) ? entry.getValue() : verboseHandler); 103 | } 104 | verboseHandlerMap = new WriteHandlerMap(verboseHandlers); 105 | } 106 | return verboseHandlerMap; 107 | } 108 | 109 | private void setTagProvider(Map> handlers) { 110 | Iterator> i = handlers.values().iterator(); 111 | while(i.hasNext()) { 112 | WriteHandler h = i.next(); 113 | if(h instanceof TagProviderAware) 114 | ((TagProviderAware)h).setTagProvider(this); 115 | } 116 | } 117 | 118 | private Map> getUnderlyingMap() { 119 | return handlers; 120 | } 121 | 122 | @Override 123 | public int size() { 124 | return handlers.size(); 125 | } 126 | 127 | @Override 128 | public boolean isEmpty() { 129 | return handlers.isEmpty(); 130 | } 131 | 132 | @Override 133 | public boolean containsKey(Object key) { 134 | return handlers.containsKey(key); 135 | } 136 | 137 | @Override 138 | public boolean containsValue(Object value) { 139 | return handlers.containsValue(value); 140 | } 141 | 142 | @Override 143 | public WriteHandler get(Object key) { 144 | return handlers.get(key); 145 | } 146 | 147 | @Override 148 | public WriteHandler put(Class key, WriteHandler value) { 149 | throw new UnsupportedOperationException("WriteHandlerMap is read-only"); 150 | } 151 | 152 | @Override 153 | public WriteHandler remove(Object key) { 154 | throw new UnsupportedOperationException("WriteHandlerMap is read-only"); 155 | } 156 | 157 | @Override 158 | public void putAll(Map> m) { 159 | throw new UnsupportedOperationException("WriteHandlerMap is read-only"); 160 | } 161 | 162 | @Override 163 | public void clear() { 164 | throw new UnsupportedOperationException("WriteHandlerMap is read-only"); 165 | } 166 | 167 | @Override 168 | public Set keySet() { 169 | return handlers.keySet(); 170 | } 171 | 172 | @Override 173 | public Collection> values() { 174 | return handlers.values(); 175 | } 176 | 177 | @Override 178 | public Set>> entrySet() { 179 | return handlers.entrySet(); 180 | } 181 | 182 | @Override 183 | public int hashCode() { 184 | return handlers.hashCode(); 185 | } 186 | 187 | public boolean equals(Object other) { 188 | return (other instanceof WriteHandlerMap) && getUnderlyingMap().equals(((WriteHandlerMap)other).getUnderlyingMap()); 189 | } 190 | 191 | public WriteHandler getHandler(Object o) { 192 | Class c = (o != null) ? o.getClass() : null; 193 | WriteHandler h = null; 194 | 195 | if(h == null) h = handlers.get(c); 196 | if(h == null) h = checkBaseClasses(c); 197 | if(h == null) h = checkBaseInterfaces(c); 198 | 199 | return (WriteHandler) h; 200 | } 201 | 202 | private WriteHandler checkBaseClasses(Class c) { 203 | for(Class base = c.getSuperclass(); base != Object.class; base = base.getSuperclass()) { 204 | WriteHandler h = handlers.get(base); 205 | if(h != null) { 206 | handlers.put(c, h); 207 | return h; 208 | } 209 | } 210 | return null; 211 | } 212 | 213 | private WriteHandler checkBaseInterfaces(Class c) { 214 | Map> possibles = new HashMap>(); 215 | for (Class base = c; base != Object.class; base = base.getSuperclass()) { 216 | for (Class itf : base.getInterfaces()) { 217 | WriteHandler h = handlers.get(itf); 218 | if (h != null) possibles.put(itf, h); 219 | } 220 | } 221 | switch (possibles.size()) { 222 | case 0: return null; 223 | case 1: { 224 | WriteHandler h = possibles.values().iterator().next(); 225 | handlers.put(c, h); 226 | return h; 227 | } 228 | default: throw new RuntimeException("More than one match for " + c); 229 | } 230 | } 231 | 232 | @Override 233 | public String getTag(Object o) { 234 | WriteHandler h = getHandler(o); 235 | if (h == null) return null; 236 | return h.tag(o); 237 | } 238 | 239 | @Override 240 | public String getTagAfterPossibleTransform(Object o) { 241 | if (transform != null) 242 | return this.getTag(transform.apply(o)); 243 | else 244 | return this.getTag(o); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/WriteHandlers.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.*; 7 | 8 | import java.math.BigInteger; 9 | import java.util.*; 10 | 11 | public class WriteHandlers { 12 | public static class ArrayWriteHandler extends AbstractWriteHandler { 13 | 14 | public ArrayWriteHandler() { 15 | } 16 | 17 | @Override 18 | public String tag(Object ignored) { 19 | return "array"; 20 | } 21 | 22 | @Override 23 | public Object rep(Object o) { 24 | return o; 25 | } 26 | } 27 | 28 | public static class BinaryWriteHandler extends AbstractWriteHandler { 29 | 30 | @Override 31 | public String tag(byte[] ignored) { return "b"; } 32 | 33 | @Override 34 | public Object rep(byte[] o) { return o; } 35 | } 36 | 37 | public static class BooleanWriteHandler extends AbstractWriteHandler { 38 | 39 | @Override 40 | public String tag(Boolean ignored) { 41 | return "?"; 42 | } 43 | 44 | @Override 45 | public Object rep(Boolean o) { 46 | return o; 47 | } 48 | 49 | @Override 50 | public String stringRep(Boolean o) { 51 | return o.toString(); 52 | } 53 | } 54 | 55 | public static class KeywordWriteHandler extends AbstractWriteHandler { 56 | 57 | @Override 58 | public String tag(Keyword ignored) { 59 | return ":"; 60 | } 61 | 62 | @Override 63 | public Object rep(Keyword o) { 64 | return stringRep(o); 65 | } 66 | 67 | @Override 68 | public String stringRep(Keyword o) { 69 | return o.toString().substring(1); 70 | } 71 | } 72 | 73 | public static class ListWriteHandler extends AbstractWriteHandler, Object> { 74 | 75 | @Override 76 | public String tag(List o) { 77 | if (o instanceof RandomAccess) // ArrayList, Stack, Vector 78 | return "array"; 79 | else 80 | return "list"; 81 | } 82 | 83 | @Override 84 | public Object rep(List o) { 85 | if (o instanceof LinkedList) 86 | return TransitFactory.taggedValue("array", o); 87 | else 88 | return o; 89 | } 90 | } 91 | 92 | public static class MapWriteHandler extends AbstractWriteHandler, Object> 93 | implements TagProviderAware { 94 | 95 | private TagProvider tagProvider; 96 | 97 | @Override 98 | public void setTagProvider(TagProvider tagProvider) { 99 | this.tagProvider = tagProvider; 100 | } 101 | 102 | private boolean stringableKeys(Map m) { 103 | 104 | Iterator i = m.keySet().iterator(); 105 | while(i.hasNext()) { 106 | Object key = i.next(); 107 | String tag = tagProvider.getTagAfterPossibleTransform(key); 108 | 109 | if(tag != null && tag.length() > 1) 110 | return false; 111 | else if (tag == null && !(key instanceof String)) { 112 | return false; 113 | } 114 | } 115 | 116 | return true; 117 | } 118 | 119 | @Override 120 | public String tag(Map o) { 121 | 122 | if(stringableKeys(o)) 123 | return "map"; 124 | else 125 | return "cmap"; 126 | } 127 | 128 | @Override 129 | public Object rep(Map o) { 130 | 131 | if(stringableKeys(o)) { 132 | return o.entrySet(); 133 | } 134 | else { 135 | List l = new ArrayList(2*o.size()); 136 | Iterator> i = o.entrySet().iterator(); 137 | while(i.hasNext()) { 138 | Map.Entry e = i.next(); 139 | l.add(e.getKey()); 140 | l.add(e.getValue()); 141 | } 142 | return TransitFactory.taggedValue("array", l); 143 | } 144 | } 145 | } 146 | 147 | public static class NullWriteHandler extends AbstractWriteHandler { 148 | 149 | @Override 150 | public String tag(Object ignored) { 151 | return "_"; 152 | } 153 | 154 | @Override 155 | public Object rep(Object ignored) { 156 | return null; 157 | } 158 | 159 | @Override 160 | public String stringRep(Object ignored) { 161 | return ""; 162 | } 163 | } 164 | 165 | public static class FloatWriteHandler extends AbstractWriteHandler { 166 | 167 | @Override 168 | public String tag(Float d) { 169 | if (d.isNaN() || d.isInfinite()) { 170 | return "z"; 171 | } else { 172 | return "d"; 173 | } 174 | } 175 | 176 | @Override 177 | public Object rep(Float d) { 178 | if (d.isNaN()) { 179 | return "NaN"; 180 | } else if (d == Float.POSITIVE_INFINITY) { 181 | return "INF"; 182 | } else if (d == Float.NEGATIVE_INFINITY) { 183 | return "-INF"; 184 | } else { 185 | return d; 186 | } 187 | } 188 | 189 | @Override 190 | public String stringRep(Float d) { 191 | return this.rep(d).toString(); 192 | } 193 | } 194 | 195 | public static class DoubleWriteHandler extends AbstractWriteHandler { 196 | 197 | @Override 198 | public String tag(Double d) { 199 | if (d.isNaN() || d.isInfinite()) { 200 | return "z"; 201 | } else { 202 | return "d"; 203 | } 204 | } 205 | 206 | @Override 207 | public Object rep(Double d) { 208 | if (d.isNaN()) { 209 | return "NaN"; 210 | } else if (d == Double.POSITIVE_INFINITY) { 211 | return "INF"; 212 | } else if (d == Double.NEGATIVE_INFINITY) { 213 | return "-INF"; 214 | } else { 215 | return d; 216 | } 217 | } 218 | 219 | @Override 220 | public String stringRep(Double d) { 221 | return this.rep(d).toString(); 222 | } 223 | } 224 | 225 | public static class IntegerWriteHandler extends AbstractWriteHandler { 226 | @Override 227 | public String tag(Number ignored) { 228 | return "i"; 229 | } 230 | 231 | @Override 232 | public Number rep(Number o) { 233 | return o; 234 | } 235 | 236 | @Override 237 | public String stringRep(Number o) { 238 | return o.toString(); 239 | } 240 | } 241 | 242 | public static class QuoteAbstractEmitter extends AbstractWriteHandler { 243 | 244 | @Override 245 | public String tag(Object ignored) { 246 | return "'"; 247 | } 248 | 249 | @Override 250 | public Object rep(Object o) { 251 | return ((Quote)o).o; 252 | } 253 | 254 | @Override 255 | public String stringRep(Object o) { 256 | throw new RuntimeException(); 257 | } 258 | } 259 | 260 | public static class RatioWriteHandler extends AbstractWriteHandler { 261 | 262 | @Override 263 | public String tag(Ratio o) { 264 | return "ratio"; 265 | } 266 | 267 | @Override 268 | public Object rep(Ratio o) { 269 | Ratio r = (Ratio)o; 270 | List l = new ArrayList(); 271 | l.add(r.getNumerator()); 272 | l.add(r.getDenominator()); 273 | return l; 274 | } 275 | } 276 | 277 | public static class SetWriteHandler extends AbstractWriteHandler, Object> { 278 | 279 | @Override 280 | public String tag(Set ignored) { 281 | return "set"; 282 | } 283 | 284 | @Override 285 | public Object rep(Set o) { 286 | return TransitFactory.taggedValue("array", o); 287 | } 288 | } 289 | 290 | public static class TaggedValueWriteHandler extends AbstractWriteHandler { 291 | 292 | @Override 293 | public String tag(TaggedValue o) { return o.getTag(); } 294 | 295 | @Override 296 | public Object rep(TaggedValue o) { return o.getRep(); } 297 | } 298 | 299 | public static class TimeWriteHandler implements WriteHandler { 300 | @Override 301 | public String tag(Date ignored) { 302 | return "m"; 303 | } 304 | 305 | @Override 306 | public Object rep(Date o) { return o.getTime(); } 307 | 308 | @Override 309 | public String stringRep(Date o) { 310 | return rep(o).toString(); 311 | } 312 | 313 | @Override 314 | @SuppressWarnings("unchecked") 315 | public WriteHandler getVerboseHandler() { 316 | return new WriteHandler() { 317 | @Override 318 | public String tag(Date ignored) { 319 | return "t"; 320 | } 321 | 322 | @Override 323 | public String rep(Date o) { return AbstractParser.getDateTimeFormat().format(o); } 324 | 325 | @Override 326 | public String stringRep(Date o) { 327 | return (String) rep(o); 328 | } 329 | 330 | @Override 331 | @SuppressWarnings("unchecked") 332 | public WriteHandler getVerboseHandler() { 333 | return this; 334 | } 335 | }; 336 | } 337 | } 338 | 339 | public static class ToStringWriteHandler extends AbstractWriteHandler { 340 | 341 | private final String t; 342 | 343 | public ToStringWriteHandler(String t) { 344 | this.t = t; 345 | } 346 | 347 | @Override 348 | public String tag(Object ignored) { 349 | return t; 350 | } 351 | 352 | @Override 353 | public Object rep(Object o) { 354 | return o.toString(); 355 | } 356 | 357 | @Override 358 | public String stringRep(Object o) { 359 | return (String) rep(o); 360 | } 361 | } 362 | 363 | public static class UUIDWriteHandler extends AbstractWriteHandler { 364 | 365 | @Override 366 | public String tag(UUID ignored) { 367 | return "u"; 368 | } 369 | 370 | @Override 371 | public Object rep(UUID uuid) { 372 | long[] l = new long[2]; 373 | l[0] = uuid.getMostSignificantBits(); 374 | l[1] = uuid.getLeastSignificantBits(); 375 | return l; 376 | } 377 | 378 | @Override 379 | public String stringRep(UUID o) { 380 | return o.toString(); 381 | } 382 | } 383 | 384 | public static class LinkWriteHandler extends AbstractWriteHandler { 385 | @Override 386 | public String tag(LinkImpl o) { 387 | return "link"; 388 | } 389 | 390 | @Override 391 | public Object rep(LinkImpl o) { 392 | return o.toMap(); 393 | } 394 | } 395 | 396 | } 397 | -------------------------------------------------------------------------------- /src/main/java/com/cognitect/transit/impl/WriterFactory.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Cognitect, Inc. 2 | // All rights reserved. 3 | 4 | package com.cognitect.transit.impl; 5 | 6 | import com.cognitect.transit.WriteHandler; 7 | import com.cognitect.transit.Writer; 8 | import com.fasterxml.jackson.core.JsonFactory; 9 | import com.fasterxml.jackson.core.JsonGenerator; 10 | import org.msgpack.MessagePack; 11 | import org.msgpack.packer.Packer; 12 | 13 | import java.io.IOException; 14 | import java.io.OutputStream; 15 | import java.util.Map; 16 | import java.util.function.Function; 17 | 18 | public class WriterFactory { 19 | 20 | private static final Map>, WriteHandlerMap> handlerCache = new Cache>, WriteHandlerMap>(); 21 | 22 | private static WriteHandlerMap buildWriteHandlerMap(Map> customHandlers) { 23 | if (customHandlers instanceof WriteHandlerMap) 24 | return new WriteHandlerMap(customHandlers); 25 | 26 | WriteHandlerMap writeHandlerMap; 27 | synchronized (handlerCache) { 28 | writeHandlerMap = handlerCache.get(customHandlers); 29 | if (writeHandlerMap == null) { 30 | writeHandlerMap = new WriteHandlerMap(customHandlers); 31 | handlerCache.put(customHandlers, writeHandlerMap); 32 | } 33 | } 34 | return new WriteHandlerMap(writeHandlerMap); 35 | } 36 | 37 | private static WriteHandlerMap verboseHandlerMap(Map> customHandlers) { 38 | return buildWriteHandlerMap(customHandlers).verboseWriteHandlerMap(); 39 | } 40 | 41 | public static Writer getJsonInstance(final OutputStream out, Map> customHandlers, WriteHandler defaultWriteHandler, boolean verboseMode) throws IOException { 42 | return getJsonInstance(out, customHandlers, defaultWriteHandler, verboseMode, null); 43 | } 44 | 45 | public static Writer getJsonInstance(final OutputStream out, Map> customHandlers, WriteHandler defaultWriteHandler, boolean verboseMode, Function transform) throws IOException { 46 | 47 | JsonGenerator gen = new JsonFactory().createGenerator(out); 48 | final JsonEmitter emitter; 49 | 50 | if (verboseMode) { 51 | emitter = new JsonVerboseEmitter(gen, verboseHandlerMap(customHandlers), defaultWriteHandler, transform); 52 | } else { 53 | emitter = new JsonEmitter(gen, buildWriteHandlerMap(customHandlers), defaultWriteHandler, transform); 54 | } 55 | 56 | final WriteCache writeCache = new WriteCache(!verboseMode); 57 | 58 | return new Writer() { 59 | @Override 60 | public void write(T o) { 61 | try { 62 | emitter.emit(o, false, writeCache.init()); 63 | out.flush(); 64 | } catch (Throwable e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | }; 69 | } 70 | 71 | public static Writer getMsgpackInstance(final OutputStream out, Map> customHandlers, WriteHandler defaultWriteHandler) throws IOException { 72 | return getMsgpackInstance(out, customHandlers, defaultWriteHandler, null); 73 | } 74 | 75 | public static Writer getMsgpackInstance(final OutputStream out, Map> customHandlers, WriteHandler defaultWriteHandler, Function transform) throws IOException { 76 | 77 | Packer packer = new MessagePack().createPacker(out); 78 | 79 | final MsgpackEmitter emitter = new MsgpackEmitter(packer, buildWriteHandlerMap(customHandlers), defaultWriteHandler, transform); 80 | 81 | final WriteCache writeCache = new WriteCache(true); 82 | 83 | return new Writer() { 84 | @Override 85 | public void write(T o) { 86 | try { 87 | emitter.emit(o, false, writeCache.init()); 88 | out.flush(); 89 | } catch (Throwable e) { 90 | throw new RuntimeException(e); 91 | } 92 | } 93 | }; 94 | } 95 | 96 | public static WriteHandler defaultDefaultHandler() { 97 | return new WriteHandler() { 98 | private String throwException(Object o) { 99 | throw new RuntimeException("Not supported " + o); 100 | } 101 | 102 | @Override 103 | public String tag(Object o) { 104 | return throwException(o); 105 | } 106 | 107 | @Override 108 | public Object rep(Object o) { 109 | return throwException(o); 110 | } 111 | 112 | @Override 113 | public String stringRep(Object o) { 114 | return throwException(o); 115 | } 116 | 117 | @Override 118 | public WriteHandler getVerboseHandler() { 119 | return null; 120 | } 121 | }; 122 | } 123 | 124 | @Deprecated 125 | public static Writer getMsgpackInstance(final OutputStream out, Map> customHandlers) throws IOException { 126 | return getMsgpackInstance(out, customHandlers, null); 127 | } 128 | 129 | @Deprecated 130 | public static Writer getJsonInstance(final OutputStream out, Map> customHandlers, boolean verboseMode) throws IOException { 131 | return getJsonInstance(out, customHandlers, null, verboseMode); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/test/java/com/cognitect/transit/TestRoundtrip.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | public class TestRoundtrip { 18 | 19 | public static void main(String [] args) throws Exception { 20 | 21 | String formatArg = args[0]; 22 | 23 | TransitFactory.Format format; 24 | if(formatArg.equals("msgpack")) 25 | format = TransitFactory.Format.MSGPACK; 26 | else if(formatArg.equals("json-verbose")) 27 | format = TransitFactory.Format.JSON_VERBOSE; 28 | else if(formatArg.equals("json")) 29 | format = TransitFactory.Format.JSON; 30 | else 31 | throw new IllegalArgumentException("No format '" + formatArg + "'"); 32 | 33 | Reader reader = TransitFactory.reader(format, System.in); 34 | Writer writer = TransitFactory.writer(format, System.out); 35 | 36 | try { 37 | while(true) { 38 | Object o = reader.read(); 39 | writer.write(o); 40 | } 41 | } 42 | catch(Exception e) { 43 | writer.write(e.getMessage()); 44 | } 45 | finally { 46 | System.out.flush(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/java/com/cognitect/transit/TransitJSONMachineModeTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | import junit.framework.Test; 18 | import junit.framework.TestCase; 19 | import junit.framework.TestSuite; 20 | 21 | import java.io.*; 22 | import java.util.*; 23 | 24 | public class TransitJSONMachineModeTest extends TestCase { 25 | 26 | public TransitJSONMachineModeTest(String testName) { 27 | super(testName); 28 | } 29 | 30 | public static Test suite() { 31 | return new TestSuite(TransitJSONMachineModeTest.class); 32 | } 33 | 34 | // Reading 35 | 36 | public Reader reader(String s) throws IOException { 37 | 38 | InputStream in = new ByteArrayInputStream(s.getBytes()); 39 | return TransitFactory.reader(TransitFactory.Format.JSON, in); 40 | } 41 | 42 | public void testReadTime() throws Exception { 43 | Date d = new Date(); 44 | long t = d.getTime(); 45 | Date dt = reader("\"~m" + t + "\"").read(); 46 | assertEquals(t, dt.getTime()); 47 | 48 | List l = reader("[\"~m" + t + "\"]").read(); 49 | dt = (Date) l.get(0); 50 | 51 | assertEquals(t, dt.getTime()); 52 | 53 | List human = reader("[\"~t1776-07-04T12:00:00.000Z\",\"~t1970-01-01T00:00:00.000Z\",\"~t2000-01-01T12:00:00.000Z\",\"~t2014-04-07T22:17:17.000Z\"]").read(); 54 | assertEquals(4, human.size()); 55 | 56 | List machine = reader("[\"~m-6106017600000\",\"~m0\",\"~m946728000000\",\"~m1396909037000\"]").read(); 57 | assertEquals(4, machine.size()); 58 | 59 | for (int i = 0; i < human.size(); i++) { 60 | Date dh = (Date) human.get(i); 61 | Date dm = (Date) machine.get(i); 62 | 63 | assertEquals(dh.compareTo(dm), 0); 64 | } 65 | } 66 | 67 | public void testReadMap() throws IOException { 68 | Map m = reader("[\"^ \",\"foo\",1,\"bar\",2]").read(); 69 | 70 | assertTrue(m instanceof HashMap); 71 | assertTrue(m.containsKey("foo")); 72 | assertTrue(m.containsKey("bar")); 73 | assertEquals(1L, m.get("foo")); 74 | assertEquals(2L, m.get("bar")); 75 | } 76 | 77 | public void testReadMapWithNested() throws IOException { 78 | Map m = reader("[\"^ \",\"foo\",1,\"bar\",[\"^ \",\"baz\",3]]").read(); 79 | 80 | assertTrue(m instanceof HashMap); 81 | assertTrue(m.get("bar") instanceof HashMap); 82 | assertTrue(((Map)m.get("bar")).containsKey("baz")); 83 | assertEquals(3L, ((Map)m.get("bar")).get("baz")); 84 | } 85 | 86 | // Writing 87 | 88 | public String write(Object o) throws Exception { 89 | 90 | OutputStream out = new ByteArrayOutputStream(); 91 | Writer w = TransitFactory.writer(TransitFactory.Format.JSON, out); 92 | w.write(o); 93 | return out.toString(); 94 | 95 | } 96 | 97 | public boolean isEqual(Object o1, Object o2) { 98 | if(o1 instanceof Boolean) 99 | return o1 == o2; 100 | else 101 | return false; 102 | } 103 | 104 | public void testRoundTrip() throws Exception { 105 | 106 | Object inObject = true; 107 | 108 | OutputStream out = new ByteArrayOutputStream(); 109 | Writer w = TransitFactory.writer(TransitFactory.Format.JSON, out); 110 | w.write(inObject); 111 | String s = out.toString(); 112 | InputStream in = new ByteArrayInputStream(s.getBytes()); 113 | Reader reader = TransitFactory.reader(TransitFactory.Format.JSON, in); 114 | Object outObject = reader.read(); 115 | 116 | assertTrue(isEqual(inObject, outObject)); 117 | } 118 | 119 | public void testWriteMap() throws Exception { 120 | Map m = new HashMap(); 121 | m.put("foo", 1); 122 | 123 | assertEquals("[\"^ \",\"foo\",1]", write(m)); 124 | 125 | Map m2 = new LinkedHashMap(); 126 | m2.put("foo", 1); 127 | m2.put("bar", 2); 128 | 129 | assertEquals("[\"^ \",\"foo\",1,\"bar\",2]", write(m2)); 130 | } 131 | 132 | public void testWriteEmptyMap() throws Exception { 133 | 134 | Map m = new HashMap(); 135 | assertEquals("[\"^ \"]", write(m)); 136 | } 137 | 138 | public String scalar(String value) { 139 | return "[\"~#'\","+value+"]"; 140 | } 141 | 142 | public void testWriteTime() throws Exception { 143 | 144 | final Date d = new Date(); 145 | long tm = d.getTime(); 146 | String t = write(d); 147 | List l = new ArrayList() {{ add(d); }}; 148 | 149 | assertEquals(scalar("\"~m" + tm + "\""), t); 150 | 151 | t = write(l); 152 | assertEquals("[\"~m" + tm + "\"]", t); 153 | 154 | final Date da[] = {new Date(-6106017600000l), 155 | new Date(0), 156 | new Date(946728000000l), 157 | new Date(1396909037000l)}; 158 | 159 | l = Arrays.asList(da); 160 | t = write(l); 161 | assertEquals( "[\"~m" + da[0].getTime() + "\"," 162 | + "\"~m" + da[1].getTime() + "\"," 163 | + "\"~m" + da[2].getTime() + "\"," 164 | + "\"~m" + da[3].getTime() + "\"]", t); 165 | } 166 | 167 | public void testWritingArrayMarkerDirectly() throws Exception { 168 | List l1 = new ArrayList() {{ add("^ "); }}; 169 | String s = write(l1); 170 | assertEquals("[\"~^ \"]", s); 171 | List l2 = (List) reader(s).read(); 172 | assertEquals(l1, l2); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/test/java/com/cognitect/transit/TransitMPTest.java: -------------------------------------------------------------------------------- 1 | // Copyright 2014 Cognitect. All Rights Reserved. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS-IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.cognitect.transit; 16 | 17 | import com.cognitect.transit.impl.JsonParser; 18 | import com.cognitect.transit.impl.Tag; 19 | import com.cognitect.transit.impl.WriteCache; 20 | import junit.framework.Test; 21 | import junit.framework.TestCase; 22 | import junit.framework.TestSuite; 23 | import org.msgpack.MessagePack; 24 | import org.msgpack.packer.Packer; 25 | 26 | import java.io.ByteArrayInputStream; 27 | import java.io.ByteArrayOutputStream; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.math.BigDecimal; 31 | import java.math.BigInteger; 32 | import java.net.URISyntaxException; 33 | import java.text.SimpleDateFormat; 34 | import java.util.*; 35 | 36 | public class TransitMPTest extends TestCase { 37 | 38 | public TransitMPTest(String testName) { 39 | super(testName); 40 | } 41 | 42 | public static Test suite() { 43 | return new TestSuite(TransitMPTest.class); 44 | } 45 | 46 | // Reading 47 | 48 | public Reader readerOf(Object... things) throws IOException { 49 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 50 | MessagePack msgpack = new MessagePack(); 51 | Packer packer = msgpack.createPacker(out); 52 | 53 | for (Object o : things) { 54 | packer.write(o); 55 | } 56 | 57 | InputStream in = new ByteArrayInputStream(out.toByteArray()); 58 | return TransitFactory.reader(TransitFactory.Format.MSGPACK, in); 59 | 60 | } 61 | 62 | public void testReadString() throws IOException { 63 | 64 | assertEquals("foo", readerOf("foo").read()); 65 | assertEquals("~foo", readerOf("~~foo").read()); 66 | assertEquals("`foo", readerOf("~`foo").read()); 67 | assertEquals("foo", ((Tag)readerOf("~#foo").read()).getValue()); 68 | assertEquals("^foo", readerOf("~^foo").read()); 69 | } 70 | 71 | public void testReadBoolean() throws IOException { 72 | 73 | assertTrue((Boolean)readerOf("~?t").read()); 74 | assertFalse((Boolean) readerOf("~?f").read()); 75 | 76 | Map thing = new HashMap() {{ 77 | put("~?t", 1); 78 | put("~?f", 2); 79 | }}; 80 | 81 | Map m = readerOf(thing).read(); 82 | assertEquals(1L, m.get(true)); 83 | assertEquals(2L, m.get(false)); 84 | } 85 | 86 | public void testReadNull() throws IOException { 87 | assertNull(readerOf("~_").read()); 88 | } 89 | 90 | public void testReadKeyword() throws IOException { 91 | 92 | Object v = readerOf("~:foo").read(); 93 | assertEquals(":foo", v.toString()); 94 | 95 | List thing = new ArrayList() {{ 96 | add("~:foo"); 97 | add("^" + (char)WriteCache.BASE_CHAR_IDX); 98 | add("^" + (char)WriteCache.BASE_CHAR_IDX); 99 | }}; 100 | 101 | List v2 = readerOf(thing).read(); 102 | assertEquals(":foo", v2.get(0).toString()); 103 | assertEquals(":foo", v2.get(1).toString()); 104 | assertEquals(":foo", v2.get(2).toString()); 105 | 106 | } 107 | 108 | public void testReadInteger() throws IOException { 109 | 110 | Reader r = readerOf("~i42"); 111 | assertEquals(42L, (long) r.read()); 112 | 113 | r = readerOf("~n4256768765123454321897654321234567"); 114 | assertEquals(0, (new BigInteger("4256768765123454321897654321234567")).compareTo((BigInteger)r.read())); 115 | } 116 | 117 | public void testReadDouble() throws IOException { 118 | 119 | assertEquals(new Double("42.5"), readerOf("~d42.5").read()); 120 | } 121 | 122 | public void testReadBigDecimal() throws IOException { 123 | 124 | assertEquals(0, (new BigDecimal("42.5")).compareTo((BigDecimal)readerOf("~f42.5").read())); 125 | } 126 | 127 | private long readTimeString(String timeString) throws IOException { 128 | return ((Date)readerOf("~t" + timeString).read()).getTime(); 129 | } 130 | 131 | private SimpleDateFormat formatter(String formatString) { 132 | 133 | SimpleDateFormat df = new SimpleDateFormat(formatString); 134 | df.setTimeZone(java.util.TimeZone.getTimeZone("UTC")); 135 | return df; 136 | } 137 | 138 | private void assertReadsFormat(String formatString) throws Exception { 139 | 140 | Date d = new Date(); 141 | SimpleDateFormat df = formatter(formatString); 142 | String ds = df.format(d); 143 | assertEquals(df.parse(ds).getTime(), readTimeString(ds)); 144 | } 145 | 146 | public void testReadTime() throws Exception { 147 | 148 | Date d = new Date(); 149 | final long t = d.getTime(); 150 | String timeString = JsonParser.getDateTimeFormat().format(d); 151 | 152 | assertEquals(t, readTimeString(timeString)); 153 | 154 | assertReadsFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); 155 | assertReadsFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 156 | assertReadsFormat("yyyy-MM-dd'T'HH:mm:ss.SSS-00:00"); 157 | 158 | Map thing = new HashMap() {{ 159 | put("~#m", t); 160 | }}; 161 | 162 | assertEquals(t, ((Date)readerOf(thing).read()).getTime()); 163 | } 164 | 165 | public void testReadUUID() throws IOException { 166 | 167 | UUID uuid = UUID.randomUUID(); 168 | final long hi64 = uuid.getMostSignificantBits(); 169 | final long lo64 = uuid.getLeastSignificantBits(); 170 | 171 | assertEquals(0, uuid.compareTo((UUID)readerOf("~u" + uuid.toString()).read())); 172 | 173 | List thing = new ArrayList() {{ 174 | add("~#u"); 175 | add(new ArrayList() {{ 176 | add(hi64); 177 | add(lo64); 178 | }}); 179 | }}; 180 | 181 | assertEquals(0, uuid.compareTo((UUID)readerOf(thing).read())); 182 | } 183 | 184 | public void testReadURI() throws URISyntaxException, IOException { 185 | 186 | URI uri = TransitFactory.uri("http://www.foo.com"); 187 | 188 | assertEquals(0, uri.compareTo((URI)readerOf("~rhttp://www.foo.com").read())); 189 | } 190 | 191 | public void testReadSymbol() throws IOException { 192 | 193 | Reader r = readerOf("~$foo"); 194 | Object v = r.read(); 195 | assertEquals("foo", v.toString()); 196 | } 197 | 198 | public void testReadCharacter() throws IOException { 199 | 200 | assertEquals('f', (char) readerOf("~cf").read()); 201 | } 202 | 203 | // Binary data tests 204 | 205 | public void testReadBinary() throws IOException { 206 | 207 | byte[] bytes = "foobarbaz".getBytes(); 208 | byte[] encodedBytes = Base64.getEncoder().encode(bytes); 209 | byte[] decoded = readerOf("~b" + new String(encodedBytes)).read(); 210 | 211 | assertEquals(bytes.length, decoded.length); 212 | 213 | boolean same = true; 214 | for(int i=0;i i = m.entrySet().iterator(); 411 | while(i.hasNext()) { 412 | Map.Entry e = i.next(); 413 | if((Long)e.getValue() == 1L) { 414 | Ratio r = (Ratio)e.getKey(); 415 | assertEquals(BigInteger.valueOf(1), r.getNumerator()); 416 | assertEquals(BigInteger.valueOf(2), r.getDenominator()); 417 | } 418 | else if((Long)e.getValue() == 2L) { 419 | List l = (List)e.getKey(); 420 | assertEquals(1L, l.get(0)); 421 | assertEquals(2L, l.get(1)); 422 | assertEquals(3L, l.get(2)); 423 | } 424 | } 425 | } 426 | 427 | public void testReadMany() throws IOException { 428 | 429 | Reader r = readerOf(true, null, false, "foo", 42.2, 42); 430 | assertTrue((Boolean)r.read()); 431 | assertNull(r.read()); 432 | assertFalse((Boolean) r.read()); 433 | assertEquals("foo", r.read()); 434 | assertEquals(42.2, r.read()); 435 | assertEquals(42L, (long) r.read()); 436 | } 437 | 438 | public void testWriteReadTime() throws Exception { 439 | 440 | final Date da[] = {new Date(-6106017600000l), 441 | new Date(0), 442 | new Date(946728000000l), 443 | new Date(1396909037000l)}; 444 | 445 | List l = Arrays.asList(da); 446 | 447 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 448 | Writer w = TransitFactory.writer(TransitFactory.Format.MSGPACK, out); 449 | w.write(l); 450 | ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); 451 | Reader r = TransitFactory.reader(TransitFactory.Format.MSGPACK, in); 452 | Object o = r.read(); 453 | } 454 | 455 | public void testWriteReadSpecialNumbers() throws Exception { 456 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 457 | Writer w = TransitFactory.writer(TransitFactory.Format.MSGPACK, out); 458 | w.write(Double.NaN); 459 | w.write(Float.NaN); 460 | w.write(Double.POSITIVE_INFINITY); 461 | w.write(Float.POSITIVE_INFINITY); 462 | w.write(Double.NEGATIVE_INFINITY); 463 | w.write(Float.NEGATIVE_INFINITY); 464 | ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); 465 | Reader r = TransitFactory.reader(TransitFactory.Format.MSGPACK, in); 466 | assert((Double)r.read()).isNaN(); 467 | assert((Double)r.read()).isNaN(); 468 | assertEquals(Double.POSITIVE_INFINITY, (Double)r.read()); 469 | assertEquals(Double.POSITIVE_INFINITY, (Double)r.read()); 470 | assertEquals(Double.NEGATIVE_INFINITY, (Double)r.read()); 471 | assertEquals(Double.NEGATIVE_INFINITY, (Double)r.read()); 472 | } 473 | 474 | } 475 | --------------------------------------------------------------------------------