Imported Upstream version 2.5.1
Komal Sukhani
8 years ago
0 | .classpath | |
1 | .project | |
2 | .settings | |
3 | .checkstyle | |
4 | eclipsebin | |
5 | ||
6 | bin | |
7 | gen | |
8 | build | |
9 | out | |
10 | lib | |
11 | ||
12 | target | |
13 | pom.xml.* | |
14 | release.properties | |
15 | ||
16 | .idea | |
17 | *.iml | |
18 | classes | |
19 | ||
20 | obj | |
21 | ||
22 | .DS_Store |
0 | Change Log | |
1 | ========== | |
2 | ||
3 | Version 2.5.1 *(2014-12-03)* | |
4 | ---------------------------- | |
5 | ||
6 | * New: `StringLiteral` type which encapsulates the behavior of `stringLiteral`. | |
7 | * Fix: Use canonical name when emitting a class import. | |
8 | * Fix: Apply type compression to varargs and array types. | |
9 | * Fix: Restore binary compatibility with pre-2.5 versions. | |
10 | ||
11 | ||
12 | Version 2.5.0 *(2014-04-18)* | |
13 | ---------------------------- | |
14 | ||
15 | * New: Methods in interfaces will always have no body declaration. | |
16 | * New: Control flow begin declaration now supports String format arguments. | |
17 | * Fix: Truncate any generic type when emitting constructors. | |
18 | * Fix: Do not emit trailing whitespace after '=' at end-of-line. | |
19 | ||
20 | ||
21 | Version 2.4.0 *(2014-01-10)* | |
22 | ---------------------------- | |
23 | ||
24 | * New: Properly indent hanging lines in field initializers. | |
25 | * New: `emitEnumValue` variant which exposes a boolean of whether the current value is the last. | |
26 | ||
27 | ||
28 | Version 2.3.1 *(2013-12-16)* | |
29 | ---------------------------- | |
30 | ||
31 | * Fix: Properly handle subpackages of `java.lang` in `compressType`. | |
32 | ||
33 | ||
34 | Version 2.3.0 *(2013-11-24)* | |
35 | ---------------------------- | |
36 | ||
37 | * New: Configurable indent level via `setIndent`. | |
38 | * New: `beginConstructor` method is a semantically clearer alternative for constructors. | |
39 | * New: `emitEnumValues` method emits a list of values followed by semicolon. | |
40 | * `emitImports` now supports `Class` arguments directly. | |
41 | * Previously-deprecated, `int`-based modifier methods have been removed. | |
42 | ||
43 | ||
44 | Version 2.2.1 *(2013-10-23)* | |
45 | ---------------------------- | |
46 | ||
47 | * Fix: Do not emit trailing whitespace for empty Javadoc lines. | |
48 | ||
49 | ||
50 | Version 2.2.0 *(2013-09-25)* | |
51 | ---------------------------- | |
52 | ||
53 | * `setCompressingTypes` controls whether types are emitted as fully-qualified or not. | |
54 | ||
55 | ||
56 | Version 2.1.2 *(2013-08-23)* | |
57 | ---------------------------- | |
58 | ||
59 | * Attempt to keep annotations on a single line. | |
60 | ||
61 | ||
62 | Version 2.1.1 *(2013-07-23)* | |
63 | ---------------------------- | |
64 | ||
65 | * Fix: `stringLiteral` now correctly handles escapes and control characters. | |
66 | ||
67 | ||
68 | Version 2.1.0 *(2013-07-15)* | |
69 | ---------------------------- | |
70 | ||
71 | * New: All methods now take a `Set` of `Modifier`s rather than an `int`. The `int` methods are | |
72 | now deprecated for removal in version 3.0. | |
73 | * Annotations with a single "value" attribute will now omit the key. | |
74 | ||
75 | ||
76 | Version 2.0.1 *(2013-06-17)* | |
77 | ---------------------------- | |
78 | ||
79 | * Correct casing of `emitSingleLineComment`. | |
80 | ||
81 | ||
82 | Version 2.0.0 *(2013-06-06)* | |
83 | ---------------------------- | |
84 | ||
85 | * Package name is now `com.squareup.javawriter`. | |
86 | * Support declaring `throws` clause on methods. | |
87 | ||
88 | ||
89 | Version 1.0.5 *(2013-05-08)* | |
90 | ---------------------------- | |
91 | ||
92 | * Fix: Fully qualify types whose simple name matches an import. | |
93 | ||
94 | ||
95 | Version 1.0.4 *(2013-03-15)* | |
96 | ---------------------------- | |
97 | ||
98 | * Fix: Static import emit now properly supports method imports. | |
99 | ||
100 | ||
101 | Version 1.0.3 *(2013-02-21)* | |
102 | ----------------------------- | |
103 | ||
104 | * Add support for emitting static imports. | |
105 | ||
106 | ||
107 | Version 1.0.2 *(2013-02-11)* | |
108 | ---------------------------- | |
109 | ||
110 | * Add `type` API for helping build generic types. | |
111 | * Minor performance improvements. | |
112 | ||
113 | ||
114 | Version 1.0.1 *(2013-02-03)* | |
115 | ---------------------------- | |
116 | ||
117 | * Expose `compressType` API. | |
118 | ||
119 | ||
120 | Version 1.0.0 *(2013-02-01)* | |
121 | ---------------------------- | |
122 | ||
123 | Initial release. |
0 | Contributing | |
1 | ============ | |
2 | ||
3 | If you would like to contribute code you can do so through GitHub by forking | |
4 | the repository and sending a pull request. | |
5 | ||
6 | When submitting code, please make every effort to follow existing conventions | |
7 | and style in order to keep the code as readable as possible. Please also make | |
8 | sure your code compiles by running `mvn clean verify`. Checkstyle failures | |
9 | during compilation indicate errors in your style and can be viewed in the | |
10 | `checkstyle-result.xml` file. | |
11 | ||
12 | Before your code can be accepted into the project you must also sign the | |
13 | [Individual Contributor License Agreement (CLA)][1]. | |
14 | ||
15 | ||
16 | [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 |
0 | ||
1 | Apache License | |
2 | Version 2.0, January 2004 | |
3 | http://www.apache.org/licenses/ | |
4 | ||
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |
6 | ||
7 | 1. Definitions. | |
8 | ||
9 | "License" shall mean the terms and conditions for use, reproduction, | |
10 | and distribution as defined by Sections 1 through 9 of this document. | |
11 | ||
12 | "Licensor" shall mean the copyright owner or entity authorized by | |
13 | the copyright owner that is granting the License. | |
14 | ||
15 | "Legal Entity" shall mean the union of the acting entity and all | |
16 | other entities that control, are controlled by, or are under common | |
17 | control with that entity. For the purposes of this definition, | |
18 | "control" means (i) the power, direct or indirect, to cause the | |
19 | direction or management of such entity, whether by contract or | |
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the | |
21 | outstanding shares, or (iii) beneficial ownership of such entity. | |
22 | ||
23 | "You" (or "Your") shall mean an individual or Legal Entity | |
24 | exercising permissions granted by this License. | |
25 | ||
26 | "Source" form shall mean the preferred form for making modifications, | |
27 | including but not limited to software source code, documentation | |
28 | source, and configuration files. | |
29 | ||
30 | "Object" form shall mean any form resulting from mechanical | |
31 | transformation or translation of a Source form, including but | |
32 | not limited to compiled object code, generated documentation, | |
33 | and conversions to other media types. | |
34 | ||
35 | "Work" shall mean the work of authorship, whether in Source or | |
36 | Object form, made available under the License, as indicated by a | |
37 | copyright notice that is included in or attached to the work | |
38 | (an example is provided in the Appendix below). | |
39 | ||
40 | "Derivative Works" shall mean any work, whether in Source or Object | |
41 | form, that is based on (or derived from) the Work and for which the | |
42 | editorial revisions, annotations, elaborations, or other modifications | |
43 | represent, as a whole, an original work of authorship. For the purposes | |
44 | of this License, Derivative Works shall not include works that remain | |
45 | separable from, or merely link (or bind by name) to the interfaces of, | |
46 | the Work and Derivative Works thereof. | |
47 | ||
48 | "Contribution" shall mean any work of authorship, including | |
49 | the original version of the Work and any modifications or additions | |
50 | to that Work or Derivative Works thereof, that is intentionally | |
51 | submitted to Licensor for inclusion in the Work by the copyright owner | |
52 | or by an individual or Legal Entity authorized to submit on behalf of | |
53 | the copyright owner. For the purposes of this definition, "submitted" | |
54 | means any form of electronic, verbal, or written communication sent | |
55 | to the Licensor or its representatives, including but not limited to | |
56 | communication on electronic mailing lists, source code control systems, | |
57 | and issue tracking systems that are managed by, or on behalf of, the | |
58 | Licensor for the purpose of discussing and improving the Work, but | |
59 | excluding communication that is conspicuously marked or otherwise | |
60 | designated in writing by the copyright owner as "Not a Contribution." | |
61 | ||
62 | "Contributor" shall mean Licensor and any individual or Legal Entity | |
63 | on behalf of whom a Contribution has been received by Licensor and | |
64 | subsequently incorporated within the Work. | |
65 | ||
66 | 2. Grant of Copyright License. Subject to the terms and conditions of | |
67 | this License, each Contributor hereby grants to You a perpetual, | |
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
69 | copyright license to reproduce, prepare Derivative Works of, | |
70 | publicly display, publicly perform, sublicense, and distribute the | |
71 | Work and such Derivative Works in Source or Object form. | |
72 | ||
73 | 3. Grant of Patent License. Subject to the terms and conditions of | |
74 | this License, each Contributor hereby grants to You a perpetual, | |
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |
76 | (except as stated in this section) patent license to make, have made, | |
77 | use, offer to sell, sell, import, and otherwise transfer the Work, | |
78 | where such license applies only to those patent claims licensable | |
79 | by such Contributor that are necessarily infringed by their | |
80 | Contribution(s) alone or by combination of their Contribution(s) | |
81 | with the Work to which such Contribution(s) was submitted. If You | |
82 | institute patent litigation against any entity (including a | |
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work | |
84 | or a Contribution incorporated within the Work constitutes direct | |
85 | or contributory patent infringement, then any patent licenses | |
86 | granted to You under this License for that Work shall terminate | |
87 | as of the date such litigation is filed. | |
88 | ||
89 | 4. Redistribution. You may reproduce and distribute copies of the | |
90 | Work or Derivative Works thereof in any medium, with or without | |
91 | modifications, and in Source or Object form, provided that You | |
92 | meet the following conditions: | |
93 | ||
94 | (a) You must give any other recipients of the Work or | |
95 | Derivative Works a copy of this License; and | |
96 | ||
97 | (b) You must cause any modified files to carry prominent notices | |
98 | stating that You changed the files; and | |
99 | ||
100 | (c) You must retain, in the Source form of any Derivative Works | |
101 | that You distribute, all copyright, patent, trademark, and | |
102 | attribution notices from the Source form of the Work, | |
103 | excluding those notices that do not pertain to any part of | |
104 | the Derivative Works; and | |
105 | ||
106 | (d) If the Work includes a "NOTICE" text file as part of its | |
107 | distribution, then any Derivative Works that You distribute must | |
108 | include a readable copy of the attribution notices contained | |
109 | within such NOTICE file, excluding those notices that do not | |
110 | pertain to any part of the Derivative Works, in at least one | |
111 | of the following places: within a NOTICE text file distributed | |
112 | as part of the Derivative Works; within the Source form or | |
113 | documentation, if provided along with the Derivative Works; or, | |
114 | within a display generated by the Derivative Works, if and | |
115 | wherever such third-party notices normally appear. The contents | |
116 | of the NOTICE file are for informational purposes only and | |
117 | do not modify the License. You may add Your own attribution | |
118 | notices within Derivative Works that You distribute, alongside | |
119 | or as an addendum to the NOTICE text from the Work, provided | |
120 | that such additional attribution notices cannot be construed | |
121 | as modifying the License. | |
122 | ||
123 | You may add Your own copyright statement to Your modifications and | |
124 | may provide additional or different license terms and conditions | |
125 | for use, reproduction, or distribution of Your modifications, or | |
126 | for any such Derivative Works as a whole, provided Your use, | |
127 | reproduction, and distribution of the Work otherwise complies with | |
128 | the conditions stated in this License. | |
129 | ||
130 | 5. Submission of Contributions. Unless You explicitly state otherwise, | |
131 | any Contribution intentionally submitted for inclusion in the Work | |
132 | by You to the Licensor shall be under the terms and conditions of | |
133 | this License, without any additional terms or conditions. | |
134 | Notwithstanding the above, nothing herein shall supersede or modify | |
135 | the terms of any separate license agreement you may have executed | |
136 | with Licensor regarding such Contributions. | |
137 | ||
138 | 6. Trademarks. This License does not grant permission to use the trade | |
139 | names, trademarks, service marks, or product names of the Licensor, | |
140 | except as required for reasonable and customary use in describing the | |
141 | origin of the Work and reproducing the content of the NOTICE file. | |
142 | ||
143 | 7. Disclaimer of Warranty. Unless required by applicable law or | |
144 | agreed to in writing, Licensor provides the Work (and each | |
145 | Contributor provides its Contributions) on an "AS IS" BASIS, | |
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
147 | implied, including, without limitation, any warranties or conditions | |
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |
149 | PARTICULAR PURPOSE. You are solely responsible for determining the | |
150 | appropriateness of using or redistributing the Work and assume any | |
151 | risks associated with Your exercise of permissions under this License. | |
152 | ||
153 | 8. Limitation of Liability. In no event and under no legal theory, | |
154 | whether in tort (including negligence), contract, or otherwise, | |
155 | unless required by applicable law (such as deliberate and grossly | |
156 | negligent acts) or agreed to in writing, shall any Contributor be | |
157 | liable to You for damages, including any direct, indirect, special, | |
158 | incidental, or consequential damages of any character arising as a | |
159 | result of this License or out of the use or inability to use the | |
160 | Work (including but not limited to damages for loss of goodwill, | |
161 | work stoppage, computer failure or malfunction, or any and all | |
162 | other commercial damages or losses), even if such Contributor | |
163 | has been advised of the possibility of such damages. | |
164 | ||
165 | 9. Accepting Warranty or Additional Liability. While redistributing | |
166 | the Work or Derivative Works thereof, You may choose to offer, | |
167 | and charge a fee for, acceptance of support, warranty, indemnity, | |
168 | or other liability obligations and/or rights consistent with this | |
169 | License. However, in accepting such obligations, You may act only | |
170 | on Your own behalf and on Your sole responsibility, not on behalf | |
171 | of any other Contributor, and only if You agree to indemnify, | |
172 | defend, and hold each Contributor harmless for any liability | |
173 | incurred by, or claims asserted against, such Contributor by reason | |
174 | of your accepting any such warranty or additional liability. | |
175 | ||
176 | END OF TERMS AND CONDITIONS | |
177 | ||
178 | APPENDIX: How to apply the Apache License to your work. | |
179 | ||
180 | To apply the Apache License to your work, attach the following | |
181 | boilerplate notice, with the fields enclosed by brackets "[]" | |
182 | replaced with your own identifying information. (Don't include | |
183 | the brackets!) The text should be enclosed in the appropriate | |
184 | comment syntax for the file format. We also recommend that a | |
185 | file or class name and description of purpose be included on the | |
186 | same "printed page" as the copyright notice for easier | |
187 | identification within third-party archives. | |
188 | ||
189 | Copyright [yyyy] [name of copyright owner] | |
190 | ||
191 | Licensed under the Apache License, Version 2.0 (the "License"); | |
192 | you may not use this file except in compliance with the License. | |
193 | You may obtain a copy of the License at | |
194 | ||
195 | http://www.apache.org/licenses/LICENSE-2.0 | |
196 | ||
197 | Unless required by applicable law or agreed to in writing, software | |
198 | distributed under the License is distributed on an "AS IS" BASIS, | |
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
200 | See the License for the specific language governing permissions and | |
201 | limitations under the License. |
0 | Java Writer | |
1 | =========== | |
2 | ||
3 | `JavaWriter` is a utility class which aids in generating Java source files. | |
4 | ||
5 | Source file generation can useful when doing things such as annotation processing or interacting | |
6 | with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate | |
7 | the need to write boilerplate while also keeping a single source of truth for the metadata. | |
8 | ||
9 | ||
10 | ||
11 | Example | |
12 | ------- | |
13 | ||
14 | ```java | |
15 | writer.emitPackage("com.example") | |
16 | .beginType("com.example.Person", "class", EnumSet.of(PUBLIC, FINAL)) | |
17 | .emitField("String", "firstName", EnumSet.of(PRIVATE)) | |
18 | .emitField("String", "lastName", EnumSet.of(PRIVATE)) | |
19 | .emitJavadoc("Returns the person's full name.") | |
20 | .beginMethod("String", "getName", EnumSet.of(PUBLIC)) | |
21 | .emitStatement("return firstName + \" \" + lastName") | |
22 | .endMethod() | |
23 | .endType(); | |
24 | ``` | |
25 | ||
26 | Would produce the following source output: | |
27 | ||
28 | ```java | |
29 | package com.example; | |
30 | ||
31 | public final class Person { | |
32 | private String firstName; | |
33 | private String lastName; | |
34 | /** | |
35 | * Returns the person's full name. | |
36 | */ | |
37 | public String getName() { | |
38 | return firstName + " " + lastName; | |
39 | } | |
40 | } | |
41 | ``` | |
42 | ||
43 | ||
44 | ||
45 | Download | |
46 | -------- | |
47 | ||
48 | Download [the latest .jar][dl] or depend via Maven: | |
49 | ```xml | |
50 | <dependency> | |
51 | <groupId>com.squareup</groupId> | |
52 | <artifactId>javawriter</artifactId> | |
53 | <version>2.5.1</version> | |
54 | </dependency> | |
55 | ``` | |
56 | or Gradle: | |
57 | ```groovy | |
58 | compile 'com.squareup:javawriter:2.5.1' | |
59 | ``` | |
60 | ||
61 | ||
62 | ||
63 | License | |
64 | ------- | |
65 | ||
66 | Copyright 2013 Square, Inc. | |
67 | ||
68 | Licensed under the Apache License, Version 2.0 (the "License"); | |
69 | you may not use this file except in compliance with the License. | |
70 | You may obtain a copy of the License at | |
71 | ||
72 | http://www.apache.org/licenses/LICENSE-2.0 | |
73 | ||
74 | Unless required by applicable law or agreed to in writing, software | |
75 | distributed under the License is distributed on an "AS IS" BASIS, | |
76 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
77 | See the License for the specific language governing permissions and | |
78 | limitations under the License. | |
79 | ||
80 | ||
81 | ||
82 | [dl]: https://search.maven.org/remote_content?g=com.squareup&a=javawriter&v=LATEST |
0 | <?xml version="1.0"?> | |
1 | <!DOCTYPE module PUBLIC | |
2 | "-//Puppy Crawl//DTD Check Configuration 1.2//EN" | |
3 | "http://www.puppycrawl.com/dtds/configuration_1_2.dtd"> | |
4 | ||
5 | <module name="Checker"> | |
6 | <module name="NewlineAtEndOfFile"/> | |
7 | <module name="FileLength"/> | |
8 | <module name="FileTabCharacter"/> | |
9 | ||
10 | <!-- Trailing spaces --> | |
11 | <module name="RegexpSingleline"> | |
12 | <property name="format" value="\s+$"/> | |
13 | <property name="message" value="Line has trailing spaces."/> | |
14 | </module> | |
15 | ||
16 | <module name="TreeWalker"> | |
17 | <property name="cacheFile" value="${checkstyle.cache.file}"/> | |
18 | ||
19 | <!-- Checks for Javadoc comments. --> | |
20 | <!-- See http://checkstyle.sf.net/config_javadoc.html --> | |
21 | <!--module name="JavadocMethod"/--> | |
22 | <!--module name="JavadocType"/--> | |
23 | <!--module name="JavadocVariable"/--> | |
24 | <module name="JavadocStyle"/> | |
25 | ||
26 | ||
27 | <!-- Checks for Naming Conventions. --> | |
28 | <!-- See http://checkstyle.sf.net/config_naming.html --> | |
29 | <module name="ConstantName"/> | |
30 | <module name="LocalFinalVariableName"/> | |
31 | <module name="LocalVariableName"/> | |
32 | <module name="MemberName"/> | |
33 | <module name="MethodName"/> | |
34 | <module name="PackageName"/> | |
35 | <module name="ParameterName"/> | |
36 | <module name="StaticVariableName"/> | |
37 | <module name="TypeName"/> | |
38 | ||
39 | ||
40 | <!-- Checks for imports --> | |
41 | <!-- See http://checkstyle.sf.net/config_import.html --> | |
42 | <module name="AvoidStarImport"/> | |
43 | <module name="IllegalImport"/> | |
44 | <!-- defaults to sun.* packages --> | |
45 | <module name="RedundantImport"/> | |
46 | <module name="UnusedImports"/> | |
47 | ||
48 | ||
49 | <!-- Checks for Size Violations. --> | |
50 | <!-- See http://checkstyle.sf.net/config_sizes.html --> | |
51 | <module name="LineLength"> | |
52 | <property name="max" value="100"/> | |
53 | </module> | |
54 | <module name="MethodLength"/> | |
55 | <module name="ParameterNumber"/> | |
56 | ||
57 | ||
58 | <!-- Checks for whitespace --> | |
59 | <!-- See http://checkstyle.sf.net/config_whitespace.html --> | |
60 | <module name="GenericWhitespace"/> | |
61 | <module name="EmptyForIteratorPad"/> | |
62 | <module name="MethodParamPad"/> | |
63 | <module name="NoWhitespaceAfter"/> | |
64 | <module name="NoWhitespaceBefore"/> | |
65 | <module name="OperatorWrap"/> | |
66 | <module name="ParenPad"/> | |
67 | <module name="TypecastParenPad"/> | |
68 | <module name="WhitespaceAfter"/> | |
69 | <module name="WhitespaceAround"/> | |
70 | ||
71 | ||
72 | <!-- Modifier Checks --> | |
73 | <!-- See http://checkstyle.sf.net/config_modifiers.html --> | |
74 | <module name="ModifierOrder"/> | |
75 | <module name="RedundantModifier"/> | |
76 | ||
77 | ||
78 | <!-- Checks for blocks. You know, those {}'s --> | |
79 | <!-- See http://checkstyle.sf.net/config_blocks.html --> | |
80 | <module name="AvoidNestedBlocks"/> | |
81 | <!--module name="EmptyBlock"/--> | |
82 | <module name="LeftCurly"/> | |
83 | <module name="NeedBraces"/> | |
84 | <module name="RightCurly"/> | |
85 | ||
86 | ||
87 | <!-- Checks for common coding problems --> | |
88 | <!-- See http://checkstyle.sf.net/config_coding.html --> | |
89 | <!--module name="AvoidInlineConditionals"/--> | |
90 | <module name="CovariantEquals"/> | |
91 | <module name="DoubleCheckedLocking"/> | |
92 | <module name="EmptyStatement"/> | |
93 | <module name="EqualsAvoidNull"/> | |
94 | <module name="EqualsHashCode"/> | |
95 | <!--module name="HiddenField"/--> | |
96 | <module name="IllegalInstantiation"/> | |
97 | <module name="InnerAssignment"/> | |
98 | <module name="MagicNumber"/> | |
99 | <module name="MissingSwitchDefault"/> | |
100 | <module name="RedundantThrows"/> | |
101 | <module name="SimplifyBooleanExpression"/> | |
102 | <module name="SimplifyBooleanReturn"/> | |
103 | ||
104 | <!-- Checks for class design --> | |
105 | <!-- See http://checkstyle.sf.net/config_design.html --> | |
106 | <!--module name="DesignForExtension"/--> | |
107 | <module name="FinalClass"/> | |
108 | <module name="HideUtilityClassConstructor"/> | |
109 | <module name="InterfaceIsType"/> | |
110 | <!--module name="VisibilityModifier"/--> | |
111 | ||
112 | ||
113 | <!-- Miscellaneous other checks. --> | |
114 | <!-- See http://checkstyle.sf.net/config_misc.html --> | |
115 | <module name="ArrayTypeStyle"/> | |
116 | <!--module name="FinalParameters"/--> | |
117 | <module name="TodoComment"/> | |
118 | <module name="UpperEll"/> | |
119 | </module> | |
120 | </module> |
0 | #!/bin/bash | |
1 | ||
2 | set -ex | |
3 | ||
4 | REPO="git@github.com:square/javawriter.git" | |
5 | GROUP_ID="com.squareup" | |
6 | ARTIFACT_ID="javawriter" | |
7 | ||
8 | DIR=temp-clone | |
9 | ||
10 | # Delete any existing temporary website clone | |
11 | rm -rf $DIR | |
12 | ||
13 | # Clone the current repo into temp folder | |
14 | git clone $REPO $DIR | |
15 | ||
16 | # Move working directory into temp folder | |
17 | cd $DIR | |
18 | ||
19 | # Checkout and track the gh-pages branch | |
20 | git checkout -t origin/gh-pages | |
21 | ||
22 | # Delete everything | |
23 | rm -rf * | |
24 | ||
25 | # Download the latest javadoc | |
26 | curl -L "https://search.maven.org/remote_content?g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip | |
27 | unzip javadoc.zip | |
28 | rm javadoc.zip | |
29 | ||
30 | # Stage all files in git and create a commit | |
31 | git add . | |
32 | git add -u | |
33 | git commit -m "Website at $(date)" | |
34 | ||
35 | # Push the new files up to GitHub | |
36 | git push origin gh-pages | |
37 | ||
38 | # Delete our temp folder | |
39 | cd .. | |
40 | rm -rf $DIR |
0 | <?xml version="1.0" encoding="UTF-8"?> | |
1 | ||
2 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |
3 | <modelVersion>4.0.0</modelVersion> | |
4 | ||
5 | <parent> | |
6 | <groupId>org.sonatype.oss</groupId> | |
7 | <artifactId>oss-parent</artifactId> | |
8 | <version>7</version> | |
9 | </parent> | |
10 | ||
11 | <groupId>com.squareup</groupId> | |
12 | <artifactId>javawriter</artifactId> | |
13 | <version>2.5.1</version> | |
14 | ||
15 | <name>JavaWriter</name> | |
16 | <description>A utility class which aids in generating Java source files.</description> | |
17 | <url>http://github.com/square/javawriter/</url> | |
18 | ||
19 | <properties> | |
20 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |
21 | ||
22 | <java.version>1.6</java.version> | |
23 | <junit.version>4.10</junit.version> | |
24 | <fest.version>2.0M8</fest.version> | |
25 | </properties> | |
26 | ||
27 | <scm> | |
28 | <url>http://github.com/square/javawriter/</url> | |
29 | <connection>scm:git:git://github.com/square/javawriter.git</connection> | |
30 | <developerConnection>scm:git:ssh://git@github.com/square/javawriter.git</developerConnection> | |
31 | <tag>HEAD</tag> | |
32 | </scm> | |
33 | ||
34 | <issueManagement> | |
35 | <system>GitHub Issues</system> | |
36 | <url>http://github.com/square/javawriter/issues</url> | |
37 | </issueManagement> | |
38 | ||
39 | <licenses> | |
40 | <license> | |
41 | <name>Apache 2.0</name> | |
42 | <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> | |
43 | </license> | |
44 | </licenses> | |
45 | ||
46 | <organization> | |
47 | <name>Square, Inc.</name> | |
48 | <url>http://squareup.com</url> | |
49 | </organization> | |
50 | ||
51 | <dependencies> | |
52 | <dependency> | |
53 | <groupId>junit</groupId> | |
54 | <artifactId>junit</artifactId> | |
55 | <version>${junit.version}</version> | |
56 | <scope>test</scope> | |
57 | </dependency> | |
58 | <dependency> | |
59 | <groupId>org.easytesting</groupId> | |
60 | <artifactId>fest-assert-core</artifactId> | |
61 | <version>${fest.version}</version> | |
62 | <scope>test</scope> | |
63 | </dependency> | |
64 | </dependencies> | |
65 | ||
66 | <build> | |
67 | <plugins> | |
68 | <plugin> | |
69 | <groupId>org.apache.maven.plugins</groupId> | |
70 | <artifactId>maven-compiler-plugin</artifactId> | |
71 | <version>3.0</version> | |
72 | <configuration> | |
73 | <source>${java.version}</source> | |
74 | <target>${java.version}</target> | |
75 | </configuration> | |
76 | </plugin> | |
77 | ||
78 | <plugin> | |
79 | <groupId>org.apache.maven.plugins</groupId> | |
80 | <artifactId>maven-checkstyle-plugin</artifactId> | |
81 | <version>2.9.1</version> | |
82 | <configuration> | |
83 | <failsOnError>true</failsOnError> | |
84 | <configLocation>checkstyle.xml</configLocation> | |
85 | <consoleOutput>true</consoleOutput> | |
86 | </configuration> | |
87 | <executions> | |
88 | <execution> | |
89 | <phase>verify</phase> | |
90 | <goals> | |
91 | <goal>checkstyle</goal> | |
92 | </goals> | |
93 | </execution> | |
94 | </executions> | |
95 | </plugin> | |
96 | </plugins> | |
97 | </build> | |
98 | </project> |
0 | // Copyright 2013 Square, Inc. | |
1 | package com.squareup.javawriter; | |
2 | ||
3 | import java.io.Closeable; | |
4 | import java.io.IOException; | |
5 | import java.io.Writer; | |
6 | import java.lang.annotation.Annotation; | |
7 | import java.util.ArrayDeque; | |
8 | import java.util.ArrayList; | |
9 | import java.util.Arrays; | |
10 | import java.util.Collection; | |
11 | import java.util.Collections; | |
12 | import java.util.Deque; | |
13 | import java.util.EnumSet; | |
14 | import java.util.Iterator; | |
15 | import java.util.LinkedHashMap; | |
16 | import java.util.List; | |
17 | import java.util.Map; | |
18 | import java.util.Map.Entry; | |
19 | import java.util.Set; | |
20 | import java.util.TreeSet; | |
21 | import java.util.regex.Matcher; | |
22 | import java.util.regex.Pattern; | |
23 | import javax.lang.model.element.Modifier; | |
24 | ||
25 | import static javax.lang.model.element.Modifier.ABSTRACT; | |
26 | ||
27 | /** A utility class which aids in generating Java source files. */ | |
28 | public class JavaWriter implements Closeable { | |
29 | private static final Pattern TYPE_TRAILER = Pattern.compile("(.*?)(\\.\\.\\.|(?:\\[\\])+)$"); | |
30 | private static final Pattern TYPE_PATTERN = Pattern.compile("(?:[\\w$]+\\.)*([\\w\\.*$]+)"); | |
31 | private static final int MAX_SINGLE_LINE_ATTRIBUTES = 3; | |
32 | private static final String INDENT = " "; | |
33 | ||
34 | /** Map fully qualified type names to their short names. */ | |
35 | private final Map<String, String> importedTypes = new LinkedHashMap<String, String>(); | |
36 | ||
37 | private String packagePrefix; | |
38 | private final Deque<Scope> scopes = new ArrayDeque<Scope>(); | |
39 | private final Deque<String> types = new ArrayDeque<String>(); | |
40 | private final Writer out; | |
41 | private boolean isCompressingTypes = true; | |
42 | private String indent = INDENT; | |
43 | ||
44 | /** | |
45 | * @param out the stream to which Java source will be written. This should be a buffered stream. | |
46 | */ | |
47 | public JavaWriter(Writer out) { | |
48 | this.out = out; | |
49 | } | |
50 | ||
51 | public void setCompressingTypes(boolean isCompressingTypes) { | |
52 | this.isCompressingTypes = isCompressingTypes; | |
53 | } | |
54 | ||
55 | public boolean isCompressingTypes() { | |
56 | return isCompressingTypes; | |
57 | } | |
58 | ||
59 | public void setIndent(String indent) { | |
60 | this.indent = indent; | |
61 | } | |
62 | ||
63 | public String getIndent() { | |
64 | return indent; | |
65 | } | |
66 | ||
67 | /** Emit a package declaration and empty line. */ | |
68 | public JavaWriter emitPackage(String packageName) throws IOException { | |
69 | if (this.packagePrefix != null) { | |
70 | throw new IllegalStateException(); | |
71 | } | |
72 | if (packageName.isEmpty()) { | |
73 | this.packagePrefix = ""; | |
74 | } else { | |
75 | out.write("package "); | |
76 | out.write(packageName); | |
77 | out.write(";\n\n"); | |
78 | this.packagePrefix = packageName + "."; | |
79 | } | |
80 | return this; | |
81 | } | |
82 | ||
83 | /** | |
84 | * Emit an import for each {@code type} provided. For the duration of the file, all references to | |
85 | * these classes will be automatically shortened. | |
86 | */ | |
87 | public JavaWriter emitImports(String... types) throws IOException { | |
88 | return emitImports(Arrays.asList(types)); | |
89 | } | |
90 | ||
91 | /** | |
92 | * Emit an import for each {@code type} provided. For the duration of the file, all references to | |
93 | * these classes will be automatically shortened. | |
94 | */ | |
95 | public JavaWriter emitImports(Class<?>... types) throws IOException { | |
96 | List<String> classNames = new ArrayList<String>(types.length); | |
97 | for (Class<?> classToImport : types) { | |
98 | classNames.add(classToImport.getCanonicalName()); | |
99 | } | |
100 | return emitImports(classNames); | |
101 | } | |
102 | ||
103 | /** | |
104 | * Emit an import for each {@code type} in the provided {@code Collection}. For the duration of | |
105 | * the file, all references to these classes will be automatically shortened. | |
106 | */ | |
107 | public JavaWriter emitImports(Collection<String> types) throws IOException { | |
108 | for (String type : new TreeSet<String>(types)) { | |
109 | Matcher matcher = TYPE_PATTERN.matcher(type); | |
110 | if (!matcher.matches()) { | |
111 | throw new IllegalArgumentException(type); | |
112 | } | |
113 | if (importedTypes.put(type, matcher.group(1)) != null) { | |
114 | throw new IllegalArgumentException(type); | |
115 | } | |
116 | out.write("import "); | |
117 | out.write(type); | |
118 | out.write(";\n"); | |
119 | } | |
120 | return this; | |
121 | } | |
122 | ||
123 | /** | |
124 | * Emit a static import for each {@code type} provided. For the duration of the file, | |
125 | * all references to these classes will be automatically shortened. | |
126 | */ | |
127 | public JavaWriter emitStaticImports(String... types) throws IOException { | |
128 | return emitStaticImports(Arrays.asList(types)); | |
129 | } | |
130 | ||
131 | /** | |
132 | * Emit a static import for each {@code type} in the provided {@code Collection}. For the | |
133 | * duration of the file, all references to these classes will be automatically shortened. | |
134 | */ | |
135 | public JavaWriter emitStaticImports(Collection<String> types) throws IOException { | |
136 | for (String type : new TreeSet<String>(types)) { | |
137 | Matcher matcher = TYPE_PATTERN.matcher(type); | |
138 | if (!matcher.matches()) { | |
139 | throw new IllegalArgumentException(type); | |
140 | } | |
141 | if (importedTypes.put(type, matcher.group(1)) != null) { | |
142 | throw new IllegalArgumentException(type); | |
143 | } | |
144 | out.write("import static "); | |
145 | out.write(type); | |
146 | out.write(";\n"); | |
147 | } | |
148 | return this; | |
149 | } | |
150 | ||
151 | /** | |
152 | * Emits a name like {@code java.lang.String} or {@code java.util.List<java.lang.String>}, | |
153 | * compressing it with imports if possible. Type compression will only be enabled if | |
154 | * {@link #isCompressingTypes} is true. | |
155 | */ | |
156 | private JavaWriter emitCompressedType(String type) throws IOException { | |
157 | if (isCompressingTypes) { | |
158 | out.write(compressType(type)); | |
159 | } else { | |
160 | out.write(type); | |
161 | } | |
162 | return this; | |
163 | } | |
164 | ||
165 | /** Try to compress a fully-qualified class name to only the class name. */ | |
166 | public String compressType(String type) { | |
167 | Matcher trailer = TYPE_TRAILER.matcher(type); | |
168 | if (trailer.matches()) { | |
169 | type = trailer.group(1); | |
170 | } | |
171 | ||
172 | StringBuilder sb = new StringBuilder(); | |
173 | if (this.packagePrefix == null) { | |
174 | throw new IllegalStateException(); | |
175 | } | |
176 | ||
177 | Matcher m = TYPE_PATTERN.matcher(type); | |
178 | int pos = 0; | |
179 | while (true) { | |
180 | boolean found = m.find(pos); | |
181 | ||
182 | // Copy non-matching characters like "<". | |
183 | int typeStart = found ? m.start() : type.length(); | |
184 | sb.append(type, pos, typeStart); | |
185 | ||
186 | if (!found) { | |
187 | break; | |
188 | } | |
189 | ||
190 | // Copy a single class name, shortening it if possible. | |
191 | String name = m.group(0); | |
192 | String imported = importedTypes.get(name); | |
193 | if (imported != null) { | |
194 | sb.append(imported); | |
195 | } else if (isClassInPackage(name, packagePrefix)) { | |
196 | String compressed = name.substring(packagePrefix.length()); | |
197 | if (isAmbiguous(compressed)) { | |
198 | sb.append(name); | |
199 | } else { | |
200 | sb.append(compressed); | |
201 | } | |
202 | } else if (isClassInPackage(name, "java.lang.")) { | |
203 | sb.append(name.substring("java.lang.".length())); | |
204 | } else { | |
205 | sb.append(name); | |
206 | } | |
207 | pos = m.end(); | |
208 | } | |
209 | ||
210 | if (trailer.matches()) { | |
211 | sb.append(trailer.group(2)); | |
212 | } | |
213 | return sb.toString(); | |
214 | } | |
215 | ||
216 | private static boolean isClassInPackage(String name, String packagePrefix) { | |
217 | if (name.startsWith(packagePrefix)) { | |
218 | if (name.indexOf('.', packagePrefix.length()) == -1) { | |
219 | return true; | |
220 | } | |
221 | // check to see if the part after the package looks like a class | |
222 | if (Character.isUpperCase(name.charAt(packagePrefix.length()))) { | |
223 | return true; | |
224 | } | |
225 | } | |
226 | return false; | |
227 | } | |
228 | ||
229 | /** | |
230 | * Returns true if the imports contain a class with same simple name as {@code compressed}. | |
231 | * | |
232 | * @param compressed simple name of the type | |
233 | */ | |
234 | private boolean isAmbiguous(String compressed) { | |
235 | return importedTypes.values().contains(compressed); | |
236 | } | |
237 | ||
238 | /** | |
239 | * Emits an initializer declaration. | |
240 | * | |
241 | * @param isStatic true if it should be an static initializer, false for an instance initializer. | |
242 | */ | |
243 | public JavaWriter beginInitializer(boolean isStatic) throws IOException { | |
244 | indent(); | |
245 | if (isStatic) { | |
246 | out.write("static"); | |
247 | out.write(" {\n"); | |
248 | } else { | |
249 | out.write("{\n"); | |
250 | } | |
251 | scopes.push(Scope.INITIALIZER); | |
252 | return this; | |
253 | } | |
254 | ||
255 | /** Ends the current initializer declaration. */ | |
256 | public JavaWriter endInitializer() throws IOException { | |
257 | popScope(Scope.INITIALIZER); | |
258 | indent(); | |
259 | out.write("}\n"); | |
260 | return this; | |
261 | } | |
262 | ||
263 | /** | |
264 | * Emits a type declaration. | |
265 | * | |
266 | * @param kind such as "class", "interface" or "enum". | |
267 | */ | |
268 | public JavaWriter beginType(String type, String kind) throws IOException { | |
269 | return beginType(type, kind, EnumSet.noneOf(Modifier.class), null); | |
270 | } | |
271 | ||
272 | /** | |
273 | * Emits a type declaration. | |
274 | * | |
275 | * @param kind such as "class", "interface" or "enum". | |
276 | */ | |
277 | public JavaWriter beginType(String type, String kind, Set<Modifier> modifiers) | |
278 | throws IOException { | |
279 | return beginType(type, kind, modifiers, null); | |
280 | } | |
281 | ||
282 | /** | |
283 | * Emits a type declaration. | |
284 | * | |
285 | * @param kind such as "class", "interface" or "enum". | |
286 | * @param extendsType the class to extend, or null for no extends clause. | |
287 | */ | |
288 | public JavaWriter beginType(String type, String kind, Set<Modifier> modifiers, String extendsType, | |
289 | String... implementsTypes) throws IOException { | |
290 | indent(); | |
291 | emitModifiers(modifiers); | |
292 | out.write(kind); | |
293 | out.write(" "); | |
294 | emitCompressedType(type); | |
295 | if (extendsType != null) { | |
296 | out.write(" extends "); | |
297 | emitCompressedType(extendsType); | |
298 | } | |
299 | if (implementsTypes.length > 0) { | |
300 | out.write("\n"); | |
301 | indent(); | |
302 | out.write(" implements "); | |
303 | for (int i = 0; i < implementsTypes.length; i++) { | |
304 | if (i != 0) { | |
305 | out.write(", "); | |
306 | } | |
307 | emitCompressedType(implementsTypes[i]); | |
308 | } | |
309 | } | |
310 | out.write(" {\n"); | |
311 | scopes.push("interface".equals(kind) ? Scope.INTERFACE_DECLARATION : Scope.TYPE_DECLARATION); | |
312 | types.push(type); | |
313 | return this; | |
314 | } | |
315 | ||
316 | /** Completes the current type declaration. */ | |
317 | public JavaWriter endType() throws IOException { | |
318 | popScope(Scope.TYPE_DECLARATION, Scope.INTERFACE_DECLARATION); | |
319 | types.pop(); | |
320 | indent(); | |
321 | out.write("}\n"); | |
322 | return this; | |
323 | } | |
324 | ||
325 | /** Emits a field declaration. */ | |
326 | public JavaWriter emitField(String type, String name) throws IOException { | |
327 | return emitField(type, name, EnumSet.noneOf(Modifier.class), null); | |
328 | } | |
329 | ||
330 | /** Emits a field declaration. */ | |
331 | public JavaWriter emitField(String type, String name, Set<Modifier> modifiers) | |
332 | throws IOException { | |
333 | return emitField(type, name, modifiers, null); | |
334 | } | |
335 | ||
336 | /** Emits a field declaration. */ | |
337 | public JavaWriter emitField(String type, String name, Set<Modifier> modifiers, | |
338 | String initialValue) throws IOException { | |
339 | indent(); | |
340 | emitModifiers(modifiers); | |
341 | emitCompressedType(type); | |
342 | out.write(" "); | |
343 | out.write(name); | |
344 | ||
345 | if (initialValue != null) { | |
346 | out.write(" ="); | |
347 | if (!initialValue.startsWith("\n")) { | |
348 | out.write(" "); | |
349 | } | |
350 | ||
351 | String[] lines = initialValue.split("\n", -1); | |
352 | out.write(lines[0]); | |
353 | for (int i = 1; i < lines.length; i++) { | |
354 | out.write("\n"); | |
355 | hangingIndent(); | |
356 | out.write(lines[i]); | |
357 | } | |
358 | } | |
359 | out.write(";\n"); | |
360 | return this; | |
361 | } | |
362 | ||
363 | /** | |
364 | * Emit a method declaration. | |
365 | * | |
366 | * <p>A {@code null} return type may be used to indicate a constructor, but | |
367 | * {@link #beginConstructor(Set, String...)} should be preferred. This behavior may be removed in | |
368 | * a future release. | |
369 | * | |
370 | * @param returnType the method's return type, or null for constructors | |
371 | * @param name the method name, or the fully qualified class name for constructors. | |
372 | * @param modifiers the set of modifiers to be applied to the method | |
373 | * @param parameters alternating parameter types and names. | |
374 | */ | |
375 | public JavaWriter beginMethod(String returnType, String name, Set<Modifier> modifiers, | |
376 | String... parameters) throws IOException { | |
377 | return beginMethod(returnType, name, modifiers, Arrays.asList(parameters), null); | |
378 | } | |
379 | ||
380 | /** | |
381 | * Emit a method declaration. | |
382 | * | |
383 | * <p>A {@code null} return type may be used to indicate a constructor, but | |
384 | * {@link #beginConstructor(Set, List, List)} should be preferred. This behavior may be removed in | |
385 | * a future release. | |
386 | * | |
387 | * @param returnType the method's return type, or null for constructors. | |
388 | * @param name the method name, or the fully qualified class name for constructors. | |
389 | * @param modifiers the set of modifiers to be applied to the method | |
390 | * @param parameters alternating parameter types and names. | |
391 | * @param throwsTypes the classes to throw, or null for no throws clause. | |
392 | */ | |
393 | public JavaWriter beginMethod(String returnType, String name, Set<Modifier> modifiers, | |
394 | List<String> parameters, List<String> throwsTypes) throws IOException { | |
395 | indent(); | |
396 | emitModifiers(modifiers); | |
397 | if (returnType != null) { | |
398 | emitCompressedType(returnType); | |
399 | out.write(" "); | |
400 | out.write(name); | |
401 | } else { | |
402 | emitCompressedType(name); | |
403 | } | |
404 | out.write("("); | |
405 | if (parameters != null) { | |
406 | for (int p = 0; p < parameters.size();) { | |
407 | if (p != 0) { | |
408 | out.write(", "); | |
409 | } | |
410 | emitCompressedType(parameters.get(p++)); | |
411 | out.write(" "); | |
412 | emitCompressedType(parameters.get(p++)); | |
413 | } | |
414 | } | |
415 | out.write(")"); | |
416 | if (throwsTypes != null && throwsTypes.size() > 0) { | |
417 | out.write("\n"); | |
418 | indent(); | |
419 | out.write(" throws "); | |
420 | for (int i = 0; i < throwsTypes.size(); i++) { | |
421 | if (i != 0) { | |
422 | out.write(", "); | |
423 | } | |
424 | emitCompressedType(throwsTypes.get(i)); | |
425 | } | |
426 | } | |
427 | if (modifiers.contains(ABSTRACT) || Scope.INTERFACE_DECLARATION.equals(scopes.peek())) { | |
428 | out.write(";\n"); | |
429 | scopes.push(Scope.ABSTRACT_METHOD); | |
430 | } else { | |
431 | out.write(" {\n"); | |
432 | scopes.push(returnType == null ? Scope.CONSTRUCTOR : Scope.NON_ABSTRACT_METHOD); | |
433 | } | |
434 | return this; | |
435 | } | |
436 | ||
437 | public JavaWriter beginConstructor(Set<Modifier> modifiers, String... parameters) | |
438 | throws IOException { | |
439 | beginMethod(null, rawType(types.peekFirst()), modifiers, parameters); | |
440 | return this; | |
441 | } | |
442 | ||
443 | public JavaWriter beginConstructor(Set<Modifier> modifiers, | |
444 | List<String> parameters, List<String> throwsTypes) | |
445 | throws IOException { | |
446 | beginMethod(null, rawType(types.peekFirst()), modifiers, parameters, throwsTypes); | |
447 | return this; | |
448 | } | |
449 | ||
450 | /** Emits some Javadoc comments with line separated by {@code \n}. */ | |
451 | public JavaWriter emitJavadoc(String javadoc, Object... params) throws IOException { | |
452 | String formatted = String.format(javadoc, params); | |
453 | ||
454 | indent(); | |
455 | out.write("/**\n"); | |
456 | for (String line : formatted.split("\n")) { | |
457 | indent(); | |
458 | out.write(" *"); | |
459 | if (!line.isEmpty()) { | |
460 | out.write(" "); | |
461 | out.write(line); | |
462 | } | |
463 | out.write("\n"); | |
464 | } | |
465 | indent(); | |
466 | out.write(" */\n"); | |
467 | return this; | |
468 | } | |
469 | ||
470 | /** Emits a single line comment. */ | |
471 | public JavaWriter emitSingleLineComment(String comment, Object... args) throws IOException { | |
472 | indent(); | |
473 | out.write("// "); | |
474 | out.write(String.format(comment, args)); | |
475 | out.write("\n"); | |
476 | return this; | |
477 | } | |
478 | ||
479 | public JavaWriter emitEmptyLine() throws IOException { | |
480 | out.write("\n"); | |
481 | return this; | |
482 | } | |
483 | ||
484 | public JavaWriter emitEnumValue(String name) throws IOException { | |
485 | indent(); | |
486 | out.write(name); | |
487 | out.write(",\n"); | |
488 | return this; | |
489 | } | |
490 | ||
491 | /** | |
492 | * A simple switch to emit the proper enum depending if its last causing it to be terminated | |
493 | * by a semi-colon ({@code ;}). | |
494 | */ | |
495 | public JavaWriter emitEnumValue(String name, boolean isLast) throws IOException { | |
496 | return isLast ? emitLastEnumValue(name) : emitEnumValue(name); | |
497 | } | |
498 | ||
499 | private JavaWriter emitLastEnumValue(String name) throws IOException { | |
500 | indent(); | |
501 | out.write(name); | |
502 | out.write(";\n"); | |
503 | return this; | |
504 | } | |
505 | ||
506 | /** Emit a list of enum values followed by a semi-colon ({@code ;}). */ | |
507 | public JavaWriter emitEnumValues(Iterable<String> names) throws IOException { | |
508 | Iterator<String> iterator = names.iterator(); | |
509 | ||
510 | while (iterator.hasNext()) { | |
511 | String name = iterator.next(); | |
512 | if (iterator.hasNext()) { | |
513 | emitEnumValue(name); | |
514 | } else { | |
515 | emitLastEnumValue(name); | |
516 | } | |
517 | } | |
518 | ||
519 | return this; | |
520 | } | |
521 | ||
522 | /** Equivalent to {@code annotation(annotation, emptyMap())}. */ | |
523 | public JavaWriter emitAnnotation(String annotation) throws IOException { | |
524 | return emitAnnotation(annotation, Collections.<String, Object>emptyMap()); | |
525 | } | |
526 | ||
527 | /** Equivalent to {@code annotation(annotationType.getName(), emptyMap())}. */ | |
528 | public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType) throws IOException { | |
529 | return emitAnnotation(type(annotationType), Collections.<String, Object>emptyMap()); | |
530 | } | |
531 | ||
532 | /** | |
533 | * Annotates the next element with {@code annotationType} and a {@code value}. | |
534 | * | |
535 | * @param value an object used as the default (value) parameter of the annotation. The value will | |
536 | * be encoded using Object.toString(); use {@link #stringLiteral} for String values. Object | |
537 | * arrays are written one element per line. | |
538 | */ | |
539 | public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType, Object value) | |
540 | throws IOException { | |
541 | return emitAnnotation(type(annotationType), value); | |
542 | } | |
543 | ||
544 | /** | |
545 | * Annotates the next element with {@code annotation} and a {@code value}. | |
546 | * | |
547 | * @param value an object used as the default (value) parameter of the annotation. The value will | |
548 | * be encoded using Object.toString(); use {@link #stringLiteral} for String values. Object | |
549 | * arrays are written one element per line. | |
550 | */ | |
551 | public JavaWriter emitAnnotation(String annotation, Object value) throws IOException { | |
552 | indent(); | |
553 | out.write("@"); | |
554 | emitCompressedType(annotation); | |
555 | out.write("("); | |
556 | emitAnnotationValue(value); | |
557 | out.write(")"); | |
558 | out.write("\n"); | |
559 | return this; | |
560 | } | |
561 | ||
562 | /** Equivalent to {@code annotation(annotationType.getName(), attributes)}. */ | |
563 | public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType, | |
564 | Map<String, ?> attributes) throws IOException { | |
565 | return emitAnnotation(type(annotationType), attributes); | |
566 | } | |
567 | ||
568 | /** | |
569 | * Annotates the next element with {@code annotation} and {@code attributes}. | |
570 | * | |
571 | * @param attributes a map from annotation attribute names to their values. Values are encoded | |
572 | * using Object.toString(); use {@link #stringLiteral} for String values. Object arrays are | |
573 | * written one element per line. | |
574 | */ | |
575 | public JavaWriter emitAnnotation(String annotation, Map<String, ?> attributes) | |
576 | throws IOException { | |
577 | indent(); | |
578 | out.write("@"); | |
579 | emitCompressedType(annotation); | |
580 | switch (attributes.size()) { | |
581 | case 0: | |
582 | break; | |
583 | case 1: | |
584 | Entry<String, ?> onlyEntry = attributes.entrySet().iterator().next(); | |
585 | out.write("("); | |
586 | if (!"value".equals(onlyEntry.getKey())) { | |
587 | out.write(onlyEntry.getKey()); | |
588 | out.write(" = "); | |
589 | } | |
590 | emitAnnotationValue(onlyEntry.getValue()); | |
591 | out.write(")"); | |
592 | break; | |
593 | default: | |
594 | boolean split = attributes.size() > MAX_SINGLE_LINE_ATTRIBUTES | |
595 | || containsArray(attributes.values()); | |
596 | out.write("("); | |
597 | scopes.push(Scope.ANNOTATION_ATTRIBUTE); | |
598 | String separator = split ? "\n" : ""; | |
599 | for (Map.Entry<String, ?> entry : attributes.entrySet()) { | |
600 | out.write(separator); | |
601 | separator = split ? ",\n" : ", "; | |
602 | if (split) { | |
603 | indent(); | |
604 | } | |
605 | out.write(entry.getKey()); | |
606 | out.write(" = "); | |
607 | Object value = entry.getValue(); | |
608 | emitAnnotationValue(value); | |
609 | } | |
610 | popScope(Scope.ANNOTATION_ATTRIBUTE); | |
611 | if (split) { | |
612 | out.write("\n"); | |
613 | indent(); | |
614 | } | |
615 | out.write(")"); | |
616 | break; | |
617 | } | |
618 | out.write("\n"); | |
619 | return this; | |
620 | } | |
621 | ||
622 | private boolean containsArray(Collection<?> values) { | |
623 | for (Object value : values) { | |
624 | if (value instanceof Object[]) { | |
625 | return true; | |
626 | } | |
627 | } | |
628 | return false; | |
629 | } | |
630 | ||
631 | /** | |
632 | * Writes a single annotation value. If the value is an array, each element in the array will be | |
633 | * written to its own line. | |
634 | */ | |
635 | private JavaWriter emitAnnotationValue(Object value) throws IOException { | |
636 | if (value instanceof Object[]) { | |
637 | out.write("{"); | |
638 | boolean firstValue = true; | |
639 | scopes.push(Scope.ANNOTATION_ARRAY_VALUE); | |
640 | for (Object o : ((Object[]) value)) { | |
641 | if (firstValue) { | |
642 | firstValue = false; | |
643 | out.write("\n"); | |
644 | } else { | |
645 | out.write(",\n"); | |
646 | } | |
647 | indent(); | |
648 | out.write(o.toString()); | |
649 | } | |
650 | popScope(Scope.ANNOTATION_ARRAY_VALUE); | |
651 | out.write("\n"); | |
652 | indent(); | |
653 | out.write("}"); | |
654 | } else { | |
655 | out.write(value.toString()); | |
656 | } | |
657 | return this; | |
658 | } | |
659 | ||
660 | /** | |
661 | * @param pattern a code pattern like "int i = %s". Newlines will be further indented. Should not | |
662 | * contain trailing semicolon. | |
663 | */ | |
664 | public JavaWriter emitStatement(String pattern, Object... args) throws IOException { | |
665 | checkInMethod(); | |
666 | String[] lines = String.format(pattern, args).split("\n", -1); | |
667 | indent(); | |
668 | out.write(lines[0]); | |
669 | for (int i = 1; i < lines.length; i++) { | |
670 | out.write("\n"); | |
671 | hangingIndent(); | |
672 | out.write(lines[i]); | |
673 | } | |
674 | out.write(";\n"); | |
675 | return this; | |
676 | } | |
677 | ||
678 | /** | |
679 | * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". Shouldn't | |
680 | * contain braces or newline characters. | |
681 | */ | |
682 | // NOTE: This method is for binary compatibility with previous versions. | |
683 | public JavaWriter beginControlFlow(String controlFlow) throws IOException { | |
684 | return beginControlFlow(controlFlow, new Object[0]); | |
685 | } | |
686 | ||
687 | /** | |
688 | * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". Shouldn't | |
689 | * contain braces or newline characters. | |
690 | */ | |
691 | public JavaWriter beginControlFlow(String controlFlow, Object... args) throws IOException { | |
692 | checkInMethod(); | |
693 | indent(); | |
694 | out.write(String.format(controlFlow, args)); | |
695 | out.write(" {\n"); | |
696 | scopes.push(Scope.CONTROL_FLOW); | |
697 | return this; | |
698 | } | |
699 | ||
700 | /** | |
701 | * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". | |
702 | * Shouldn't contain braces or newline characters. | |
703 | */ | |
704 | // NOTE: This method is for binary compatibility with previous versions. | |
705 | public JavaWriter nextControlFlow(String controlFlow) throws IOException { | |
706 | return nextControlFlow(controlFlow, new Object[0]); | |
707 | } | |
708 | ||
709 | /** | |
710 | * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". | |
711 | * Shouldn't contain braces or newline characters. | |
712 | */ | |
713 | public JavaWriter nextControlFlow(String controlFlow, Object... args) throws IOException { | |
714 | popScope(Scope.CONTROL_FLOW); | |
715 | indent(); | |
716 | scopes.push(Scope.CONTROL_FLOW); | |
717 | out.write("} "); | |
718 | out.write(String.format(controlFlow, args)); | |
719 | out.write(" {\n"); | |
720 | return this; | |
721 | } | |
722 | ||
723 | public JavaWriter endControlFlow() throws IOException { | |
724 | return endControlFlow(null); | |
725 | } | |
726 | ||
727 | /** | |
728 | * @param controlFlow the optional control flow construct and its code, such as | |
729 | * "while(foo == 20)". Only used for "do/while" control flows. | |
730 | */ | |
731 | // NOTE: This method is for binary compatibility with previous versions. | |
732 | public JavaWriter endControlFlow(String controlFlow) throws IOException { | |
733 | return endControlFlow(controlFlow, new Object[0]); | |
734 | } | |
735 | ||
736 | /** | |
737 | * @param controlFlow the optional control flow construct and its code, such as | |
738 | * "while(foo == 20)". Only used for "do/while" control flows. | |
739 | */ | |
740 | public JavaWriter endControlFlow(String controlFlow, Object... args) throws IOException { | |
741 | popScope(Scope.CONTROL_FLOW); | |
742 | indent(); | |
743 | if (controlFlow != null) { | |
744 | out.write("} "); | |
745 | out.write(String.format(controlFlow, args)); | |
746 | out.write(";\n"); | |
747 | } else { | |
748 | out.write("}\n"); | |
749 | } | |
750 | return this; | |
751 | } | |
752 | ||
753 | /** Completes the current method declaration. */ | |
754 | public JavaWriter endMethod() throws IOException { | |
755 | Scope popped = scopes.pop(); | |
756 | // support calling a constructor a "method" to support the legacy code | |
757 | if (popped == Scope.NON_ABSTRACT_METHOD || popped == Scope.CONSTRUCTOR) { | |
758 | indent(); | |
759 | out.write("}\n"); | |
760 | } else if (popped != Scope.ABSTRACT_METHOD) { | |
761 | throw new IllegalStateException(); | |
762 | } | |
763 | return this; | |
764 | } | |
765 | ||
766 | /** Completes the current constructor declaration. */ | |
767 | public JavaWriter endConstructor() throws IOException { | |
768 | popScope(Scope.CONSTRUCTOR); | |
769 | indent(); | |
770 | out.write("}\n"); | |
771 | return this; | |
772 | } | |
773 | ||
774 | /** | |
775 | * Returns the string literal representing {@code data}, including wrapping quotes. | |
776 | * | |
777 | * @deprecated use {@link StringLiteral} and its {@link StringLiteral#literal()} method instead. | |
778 | */ | |
779 | @Deprecated | |
780 | public static String stringLiteral(String data) { | |
781 | return StringLiteral.forValue(data).literal(); | |
782 | } | |
783 | ||
784 | /** Build a string representation of a type and optionally its generic type arguments. */ | |
785 | public static String type(Class<?> raw, String... parameters) { | |
786 | if (parameters.length == 0) { | |
787 | return raw.getCanonicalName(); | |
788 | } | |
789 | if (raw.getTypeParameters().length != parameters.length) { | |
790 | throw new IllegalArgumentException(); | |
791 | } | |
792 | StringBuilder result = new StringBuilder(); | |
793 | result.append(raw.getCanonicalName()); | |
794 | result.append("<"); | |
795 | result.append(parameters[0]); | |
796 | for (int i = 1; i < parameters.length; i++) { | |
797 | result.append(", "); | |
798 | result.append(parameters[i]); | |
799 | } | |
800 | result.append(">"); | |
801 | return result.toString(); | |
802 | } | |
803 | ||
804 | /** Build a string representation of the raw type for a (optionally generic) type. */ | |
805 | public static String rawType(String type) { | |
806 | int lessThanIndex = type.indexOf('<'); | |
807 | if (lessThanIndex != -1) { | |
808 | return type.substring(0, lessThanIndex); | |
809 | } | |
810 | return type; | |
811 | } | |
812 | ||
813 | @Override public void close() throws IOException { | |
814 | out.close(); | |
815 | } | |
816 | ||
817 | /** Emits the modifiers to the writer. */ | |
818 | private void emitModifiers(Set<Modifier> modifiers) throws IOException { | |
819 | if (modifiers.isEmpty()) { | |
820 | return; | |
821 | } | |
822 | // Use an EnumSet to ensure the proper ordering | |
823 | if (!(modifiers instanceof EnumSet)) { | |
824 | modifiers = EnumSet.copyOf(modifiers); | |
825 | } | |
826 | for (Modifier modifier : modifiers) { | |
827 | out.append(modifier.toString()).append(' '); | |
828 | } | |
829 | } | |
830 | ||
831 | private void indent() throws IOException { | |
832 | for (int i = 0, count = scopes.size(); i < count; i++) { | |
833 | out.write(indent); | |
834 | } | |
835 | } | |
836 | ||
837 | private void hangingIndent() throws IOException { | |
838 | for (int i = 0, count = scopes.size() + 2; i < count; i++) { | |
839 | out.write(indent); | |
840 | } | |
841 | } | |
842 | ||
843 | private static final EnumSet<Scope> METHOD_SCOPES = EnumSet.of( | |
844 | Scope.NON_ABSTRACT_METHOD, Scope.CONSTRUCTOR, Scope.CONTROL_FLOW, Scope.INITIALIZER); | |
845 | ||
846 | private void checkInMethod() { | |
847 | if (!METHOD_SCOPES.contains(scopes.peekFirst())) { | |
848 | throw new IllegalArgumentException(); | |
849 | } | |
850 | } | |
851 | ||
852 | private void popScope(Scope... expected) { | |
853 | if (!EnumSet.copyOf(Arrays.asList(expected)).contains(scopes.pop())) { | |
854 | throw new IllegalStateException(); | |
855 | } | |
856 | } | |
857 | ||
858 | private enum Scope { | |
859 | TYPE_DECLARATION, | |
860 | INTERFACE_DECLARATION, | |
861 | ABSTRACT_METHOD, | |
862 | NON_ABSTRACT_METHOD, | |
863 | CONSTRUCTOR, | |
864 | CONTROL_FLOW, | |
865 | ANNOTATION_ATTRIBUTE, | |
866 | ANNOTATION_ARRAY_VALUE, | |
867 | INITIALIZER | |
868 | } | |
869 | } |
0 | // Copyright 2014 Square, Inc. | |
1 | package com.squareup.javawriter; | |
2 | ||
3 | import java.util.Formatter; | |
4 | ||
5 | /** | |
6 | * Represents a string literal as found in Java source code. | |
7 | */ | |
8 | public final class StringLiteral { | |
9 | /** Returns a new {@link StringLiteral} instance for the intended value of the literal. */ | |
10 | public static StringLiteral forValue(String value) { | |
11 | return new StringLiteral(value, stringLiteral(value)); | |
12 | } | |
13 | ||
14 | /** Returns the string literal representing {@code data}, including wrapping quotes. */ | |
15 | private static String stringLiteral(String value) { | |
16 | StringBuilder result = new StringBuilder(); | |
17 | result.append('"'); | |
18 | for (int i = 0; i < value.length(); i++) { | |
19 | char c = value.charAt(i); | |
20 | switch (c) { | |
21 | case '"': | |
22 | result.append("\\\""); | |
23 | break; | |
24 | case '\\': | |
25 | result.append("\\\\"); | |
26 | break; | |
27 | case '\b': | |
28 | result.append("\\b"); | |
29 | break; | |
30 | case '\t': | |
31 | result.append("\\t"); | |
32 | break; | |
33 | case '\n': | |
34 | result.append("\\n"); | |
35 | break; | |
36 | case '\f': | |
37 | result.append("\\f"); | |
38 | break; | |
39 | case '\r': | |
40 | result.append("\\r"); | |
41 | break; | |
42 | default: | |
43 | if (Character.isISOControl(c)) { | |
44 | new Formatter(result).format("\\u%04x", (int) c); | |
45 | } else { | |
46 | result.append(c); | |
47 | } | |
48 | } | |
49 | } | |
50 | result.append('"'); | |
51 | return result.toString(); | |
52 | } | |
53 | ||
54 | private final String value; | |
55 | private final String literal; | |
56 | ||
57 | private StringLiteral(String value, String literal) { | |
58 | this.value = value; | |
59 | this.literal = literal; | |
60 | } | |
61 | ||
62 | public String value() { | |
63 | return value; | |
64 | } | |
65 | ||
66 | public String literal() { | |
67 | return literal; | |
68 | } | |
69 | ||
70 | @Override | |
71 | public String toString() { | |
72 | return literal; | |
73 | } | |
74 | ||
75 | @Override | |
76 | public boolean equals(Object obj) { | |
77 | if (obj == this) { | |
78 | return true; | |
79 | } else if (obj instanceof StringLiteral) { | |
80 | return this.value.equals(((StringLiteral) obj).value); | |
81 | } else { | |
82 | return false; | |
83 | } | |
84 | } | |
85 | ||
86 | @Override | |
87 | public int hashCode() { | |
88 | return value.hashCode(); | |
89 | } | |
90 | } |
0 | // Copyright 2013 Square, Inc. | |
1 | package com.squareup.javawriter; | |
2 | ||
3 | import com.example.Binding; | |
4 | import java.io.IOException; | |
5 | import java.io.StringWriter; | |
6 | import java.util.ArrayList; | |
7 | import java.util.Arrays; | |
8 | import java.util.Collections; | |
9 | import java.util.EnumSet; | |
10 | import java.util.Iterator; | |
11 | import java.util.LinkedHashMap; | |
12 | import java.util.LinkedHashSet; | |
13 | import java.util.List; | |
14 | import java.util.Map; | |
15 | import java.util.Set; | |
16 | import javax.lang.model.element.Modifier; | |
17 | import org.junit.Test; | |
18 | ||
19 | import static javax.lang.model.element.Modifier.ABSTRACT; | |
20 | import static javax.lang.model.element.Modifier.FINAL; | |
21 | import static javax.lang.model.element.Modifier.PRIVATE; | |
22 | import static javax.lang.model.element.Modifier.PUBLIC; | |
23 | import static javax.lang.model.element.Modifier.STATIC; | |
24 | import static org.fest.assertions.api.Assertions.assertThat; | |
25 | import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown; | |
26 | ||
27 | public final class JavaWriterTest { | |
28 | private final StringWriter stringWriter = new StringWriter(); | |
29 | private final JavaWriter javaWriter = new JavaWriter(stringWriter); | |
30 | ||
31 | @Test public void typeDeclaration() throws IOException { | |
32 | javaWriter.emitPackage("com.squareup"); | |
33 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
34 | javaWriter.endType(); | |
35 | assertCode("" | |
36 | + "package com.squareup;\n" | |
37 | + "\n" | |
38 | + "public final class Foo {\n" | |
39 | + "}\n"); | |
40 | } | |
41 | ||
42 | @Test public void enumDeclaration() throws IOException { | |
43 | javaWriter.emitPackage("com.squareup"); | |
44 | javaWriter.beginType("com.squareup.Foo", "enum", EnumSet.of(PUBLIC)); | |
45 | javaWriter.emitEnumValue("BAR"); | |
46 | javaWriter.emitEnumValue("BAZ"); | |
47 | javaWriter.endType(); | |
48 | assertCode("" | |
49 | + "package com.squareup;\n" | |
50 | + "\n" | |
51 | + "public enum Foo {\n" | |
52 | + " BAR,\n" | |
53 | + " BAZ,\n" | |
54 | + "}\n"); | |
55 | } | |
56 | ||
57 | @Test public void enumDeclarationWithMethod() throws IOException{ | |
58 | javaWriter.emitPackage("com.squareup"); | |
59 | javaWriter.beginType("com.squareup.Foo", "enum", EnumSet.of(PUBLIC)); | |
60 | javaWriter.emitEnumValues(Arrays.asList("BAR", "BAZ")); | |
61 | javaWriter.beginMethod("void", "foo", EnumSet.of(PUBLIC)); | |
62 | javaWriter.endMethod(); | |
63 | javaWriter.endType(); | |
64 | assertCode("" | |
65 | + "package com.squareup;\n" | |
66 | + "\n" | |
67 | + "public enum Foo {\n" | |
68 | + " BAR,\n" | |
69 | + " BAZ;\n" | |
70 | + " public void foo() {\n" | |
71 | + " }\n" | |
72 | + "}\n"); | |
73 | } | |
74 | ||
75 | @Test public void enumDeclarationWithAnnotationAndMethod() throws IOException{ | |
76 | javaWriter.emitPackage("com.squareup"); | |
77 | javaWriter.beginType("com.squareup.Foo", "enum", EnumSet.of(PUBLIC)); | |
78 | List<String> list = Arrays.asList("BAR", "BAZ"); | |
79 | int index = 0; | |
80 | for (Iterator<String> i = list.iterator(); i.hasNext(); ) { | |
81 | javaWriter.emitAnnotation("ProtoEnum", index); | |
82 | javaWriter.emitEnumValue(i.next(), !i.hasNext()); | |
83 | index++; | |
84 | } | |
85 | javaWriter.beginMethod("void", "foo", EnumSet.of(PUBLIC)); | |
86 | javaWriter.endMethod(); | |
87 | javaWriter.endType(); | |
88 | assertCode("" | |
89 | + "package com.squareup;\n" | |
90 | + "\n" | |
91 | + "public enum Foo {\n" | |
92 | + " @ProtoEnum(0)\n" | |
93 | + " BAR,\n" | |
94 | + " @ProtoEnum(1)\n" | |
95 | + " BAZ;\n" | |
96 | + " public void foo() {\n" | |
97 | + " }\n" | |
98 | + "}\n"); | |
99 | } | |
100 | ||
101 | @Test public void fieldDeclaration() throws IOException { | |
102 | javaWriter.emitPackage("com.squareup"); | |
103 | javaWriter.beginType("com.squareup.Foo", "class"); | |
104 | javaWriter.emitField("java.lang.String", "string", EnumSet.of(PRIVATE, STATIC)); | |
105 | javaWriter.endType(); | |
106 | assertCode("" | |
107 | + "package com.squareup;\n" | |
108 | + "\n" | |
109 | + "class Foo {\n" | |
110 | + " private static String string;\n" | |
111 | + "}\n"); | |
112 | } | |
113 | ||
114 | @Test public void fieldDeclarationWithInitialValue() throws IOException { | |
115 | javaWriter.emitPackage("com.squareup"); | |
116 | javaWriter.beginType("com.squareup.Foo", "class"); | |
117 | javaWriter.emitField("java.lang.String", "string", EnumSet.noneOf(Modifier.class), | |
118 | "\"bar\" + \"baz\""); | |
119 | javaWriter.endType(); | |
120 | assertCode("" | |
121 | + "package com.squareup;\n" | |
122 | + "\n" | |
123 | + "class Foo {\n" | |
124 | + " String string = \"bar\" + \"baz\";\n" | |
125 | + "}\n"); | |
126 | } | |
127 | ||
128 | @Test public void fieldDeclarationWithWrappingInitialValue() throws IOException { | |
129 | javaWriter.emitPackage("com.squareup"); | |
130 | javaWriter.beginType("com.squareup.Foo", "class"); | |
131 | javaWriter.emitField("java.lang.String", "string", EnumSet.noneOf(Modifier.class), | |
132 | "\"bar\"\n+ \"baz\"\n+ \"biz\""); | |
133 | javaWriter.endType(); | |
134 | assertCode("" | |
135 | + "package com.squareup;\n" | |
136 | + "\n" | |
137 | + "class Foo {\n" | |
138 | + " String string = \"bar\"\n" | |
139 | + " + \"baz\"\n" | |
140 | + " + \"biz\";\n" | |
141 | + "}\n"); | |
142 | } | |
143 | ||
144 | // If the initializer begins with a newline, don't emit a space after the '='. | |
145 | @Test public void fieldDeclarationWithNewlineInitialValue() throws IOException { | |
146 | javaWriter.emitPackage("com.squareup"); | |
147 | javaWriter.beginType("com.squareup.Foo", "class"); | |
148 | javaWriter.emitField("java.lang.String", "string", EnumSet.noneOf(Modifier.class), | |
149 | "\n\"bar\"\n+ \"baz\"\n+ \"biz\""); | |
150 | javaWriter.endType(); | |
151 | assertCode("" | |
152 | + "package com.squareup;\n" | |
153 | + "\n" | |
154 | + "class Foo {\n" | |
155 | + " String string =\n" | |
156 | + " \"bar\"\n" | |
157 | + " + \"baz\"\n" | |
158 | + " + \"biz\";\n" | |
159 | + "}\n"); | |
160 | } | |
161 | ||
162 | @Test public void abstractMethodDeclaration() throws IOException { | |
163 | javaWriter.emitPackage("com.squareup"); | |
164 | javaWriter.beginType("com.squareup.Foo", "class"); | |
165 | javaWriter.beginMethod("java.lang.String", "foo", EnumSet.of(ABSTRACT, PUBLIC), | |
166 | "java.lang.Object", "object", "java.lang.String", "s"); | |
167 | javaWriter.endMethod(); | |
168 | javaWriter.endType(); | |
169 | assertCode("" | |
170 | + "package com.squareup;\n" | |
171 | + "\n" | |
172 | + "class Foo {\n" | |
173 | + " public abstract String foo(Object object, String s);\n" | |
174 | + "}\n"); | |
175 | } | |
176 | ||
177 | @Test public void abstractMethodDeclarationWithThrows() throws IOException { | |
178 | javaWriter.emitPackage("com.squareup"); | |
179 | javaWriter.beginType("com.squareup.Foo", "class"); | |
180 | javaWriter.beginMethod("java.lang.String", "foo", EnumSet.of(ABSTRACT, PUBLIC), | |
181 | Arrays.asList("java.lang.Object", "object", "java.lang.String", "s"), | |
182 | Arrays.asList("java.io.IOException")); | |
183 | javaWriter.endMethod(); | |
184 | javaWriter.endType(); | |
185 | assertCode("" | |
186 | + "package com.squareup;\n" | |
187 | + "\n" | |
188 | + "class Foo {\n" | |
189 | + " public abstract String foo(Object object, String s)\n" | |
190 | + " throws java.io.IOException;\n" | |
191 | + "}\n"); | |
192 | } | |
193 | ||
194 | @Test public void nonAbstractMethodDeclaration() throws IOException { | |
195 | javaWriter.emitPackage("com.squareup"); | |
196 | javaWriter.beginType("com.squareup.Foo", "class"); | |
197 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s"); | |
198 | javaWriter.endMethod(); | |
199 | javaWriter.endType(); | |
200 | assertCode("" | |
201 | + "package com.squareup;\n" | |
202 | + "\n" | |
203 | + "class Foo {\n" | |
204 | + " int foo(String s) {\n" | |
205 | + " }\n" | |
206 | + "}\n"); | |
207 | } | |
208 | ||
209 | @Test public void nonAbstractMethodDeclarationWithThrows() throws IOException { | |
210 | javaWriter.emitPackage("com.squareup"); | |
211 | javaWriter.beginType("com.squareup.Foo", "class"); | |
212 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), | |
213 | Arrays.asList("java.lang.String", "s"), Arrays.asList("java.io.IOException")); | |
214 | javaWriter.endMethod(); | |
215 | javaWriter.endType(); | |
216 | assertCode("" | |
217 | + "package com.squareup;\n" | |
218 | + "\n" | |
219 | + "class Foo {\n" | |
220 | + " int foo(String s)\n" | |
221 | + " throws java.io.IOException {\n" | |
222 | + " }\n" | |
223 | + "}\n"); | |
224 | } | |
225 | ||
226 | @Test public void interfaceMethodDeclaration() throws IOException { | |
227 | javaWriter.emitPackage("com.squareup"); | |
228 | javaWriter.beginType("com.squareup.Foo", "interface"); | |
229 | javaWriter.beginMethod("java.lang.String", "foo", EnumSet.noneOf(Modifier.class), | |
230 | "java.lang.Object", "object", "java.lang.String", "s"); | |
231 | javaWriter.endMethod(); | |
232 | javaWriter.endType(); | |
233 | assertCode("" | |
234 | + "package com.squareup;\n" | |
235 | + "\n" | |
236 | + "interface Foo {\n" | |
237 | + " String foo(Object object, String s);\n" | |
238 | + "}\n"); | |
239 | } | |
240 | ||
241 | @Test public void interfaceMethodDeclarationWithThrows() throws IOException { | |
242 | javaWriter.emitPackage("com.squareup"); | |
243 | javaWriter.beginType("com.squareup.Foo", "interface"); | |
244 | javaWriter.beginMethod("java.lang.String", "foo", EnumSet.noneOf(Modifier.class), | |
245 | Arrays.asList("java.lang.Object", "object", "java.lang.String", "s"), | |
246 | Arrays.asList("java.io.IOException")); | |
247 | javaWriter.endMethod(); | |
248 | javaWriter.endType(); | |
249 | assertCode("" | |
250 | + "package com.squareup;\n" | |
251 | + "\n" | |
252 | + "interface Foo {\n" | |
253 | + " String foo(Object object, String s)\n" | |
254 | + " throws java.io.IOException;\n" | |
255 | + "}\n"); | |
256 | } | |
257 | ||
258 | @Test public void constructorDeclaration() throws IOException { | |
259 | javaWriter.emitPackage("com.squareup"); | |
260 | javaWriter.beginType("com.squareup.Foo", "class"); | |
261 | javaWriter.beginConstructor(EnumSet.of(PUBLIC), "java.lang.String", "s"); | |
262 | javaWriter.endConstructor(); | |
263 | javaWriter.endType(); | |
264 | assertCode("" | |
265 | + "package com.squareup;\n" | |
266 | + "\n" | |
267 | + "class Foo {\n" | |
268 | + " public Foo(String s) {\n" | |
269 | + " }\n" | |
270 | + "}\n"); | |
271 | } | |
272 | ||
273 | @Test public void simpleConstructor() throws IOException { | |
274 | javaWriter.emitPackage("com.squareup"); | |
275 | javaWriter.beginType("com.squareup.Foo", "class"); | |
276 | javaWriter.beginConstructor(EnumSet.of(PUBLIC), "java.lang.String", "s"); | |
277 | javaWriter.emitStatement("if (%s == null) throw new NullPointerException()", "s"); | |
278 | javaWriter.endConstructor(); | |
279 | javaWriter.endType(); | |
280 | assertCode("" | |
281 | + "package com.squareup;\n" | |
282 | + "\n" | |
283 | + "class Foo {\n" | |
284 | + " public Foo(String s) {\n" | |
285 | + " if (s == null) throw new NullPointerException();\n" | |
286 | + " }\n" | |
287 | + "}\n"); | |
288 | } | |
289 | ||
290 | @Test public void genericsConstructor() throws IOException { | |
291 | javaWriter.emitPackage("com.squareup"); | |
292 | javaWriter.beginType("com.squareup.Foo<T>", "class"); | |
293 | javaWriter.emitField("T", "fooType", EnumSet.of(PRIVATE)); | |
294 | javaWriter.beginConstructor(EnumSet.of(PUBLIC), "T", "s"); | |
295 | javaWriter.emitStatement("if (%s == null) throw new NullPointerException()", "s"); | |
296 | javaWriter.endConstructor(); | |
297 | javaWriter.beginMethod("T", "getFooType", EnumSet.of(PUBLIC)); | |
298 | javaWriter.emitStatement("return fooType"); | |
299 | javaWriter.endMethod(); | |
300 | javaWriter.endType(); | |
301 | assertCode("" | |
302 | + "package com.squareup;\n" | |
303 | + "\n" | |
304 | + "class Foo<T> {\n" | |
305 | + " private T fooType;\n" | |
306 | + " public Foo(T s) {\n" | |
307 | + " if (s == null) throw new NullPointerException();\n" | |
308 | + " }\n" | |
309 | + " public T getFooType() {\n" | |
310 | + " return fooType;\n" | |
311 | + " }\n" | |
312 | + "}\n"); | |
313 | } | |
314 | ||
315 | @Test public void constructorDeclarationInNestedTypes() throws IOException { | |
316 | javaWriter.emitPackage("com.squareup"); | |
317 | javaWriter.beginType("com.squareup.Foo", "class"); | |
318 | javaWriter.beginConstructor(EnumSet.of(PUBLIC), "java.lang.String", "s"); | |
319 | javaWriter.endConstructor(); | |
320 | javaWriter.beginType("com.squareup.Bar", "class"); | |
321 | javaWriter.beginConstructor(EnumSet.noneOf(Modifier.class)); | |
322 | javaWriter.endConstructor(); | |
323 | javaWriter.endType(); | |
324 | javaWriter.endType(); | |
325 | assertCode("" | |
326 | + "package com.squareup;\n" | |
327 | + "\n" | |
328 | + "class Foo {\n" | |
329 | + " public Foo(String s) {\n" | |
330 | + " }\n" | |
331 | + " class Bar {\n" | |
332 | + " Bar() {\n" | |
333 | + " }\n" | |
334 | + " }\n" | |
335 | + "}\n"); | |
336 | } | |
337 | ||
338 | @Test public void constructorDeclarationWithThrows() throws IOException { | |
339 | javaWriter.emitPackage("com.squareup"); | |
340 | javaWriter.beginType("com.squareup.Foo", "class"); | |
341 | javaWriter.beginConstructor(EnumSet.of(PUBLIC), | |
342 | Arrays.asList("java.lang.String", "s"), Arrays.asList("java.io.IOException")); | |
343 | javaWriter.endConstructor(); | |
344 | javaWriter.endType(); | |
345 | assertCode("" | |
346 | + "package com.squareup;\n" | |
347 | + "\n" | |
348 | + "class Foo {\n" | |
349 | + " public Foo(String s)\n" | |
350 | + " throws java.io.IOException {\n" | |
351 | + " }\n" | |
352 | + "}\n"); | |
353 | } | |
354 | ||
355 | @Test public void statement() throws IOException { | |
356 | javaWriter.emitPackage("com.squareup"); | |
357 | javaWriter.beginType("com.squareup.Foo", "class"); | |
358 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s"); | |
359 | javaWriter.emitStatement("int j = s.length() + %s", 13); | |
360 | javaWriter.endMethod(); | |
361 | javaWriter.endType(); | |
362 | assertCode("" | |
363 | + "package com.squareup;\n" | |
364 | + "\n" | |
365 | + "class Foo {\n" | |
366 | + " int foo(String s) {\n" | |
367 | + " int j = s.length() + 13;\n" | |
368 | + " }\n" | |
369 | + "}\n"); | |
370 | } | |
371 | ||
372 | @Test public void statementPrecededByComment() throws IOException { | |
373 | javaWriter.emitPackage("com.squareup"); | |
374 | javaWriter.beginType("com.squareup.Foo", "class"); | |
375 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s"); | |
376 | javaWriter.emitSingleLineComment("foo"); | |
377 | javaWriter.emitStatement("int j = s.length() + %s", 13); | |
378 | javaWriter.endMethod(); | |
379 | javaWriter.endType(); | |
380 | assertCode("" | |
381 | + "package com.squareup;\n" | |
382 | + "\n" | |
383 | + "class Foo {\n" | |
384 | + " int foo(String s) {\n" | |
385 | + " // foo\n" | |
386 | + " int j = s.length() + 13;\n" | |
387 | + " }\n" | |
388 | + "}\n"); | |
389 | } | |
390 | ||
391 | @Test public void multiLineStatement() throws IOException { | |
392 | javaWriter.emitPackage("com.squareup"); | |
393 | javaWriter.beginType("com.squareup.Triangle", "class"); | |
394 | javaWriter.beginMethod("double", "pythagorean", EnumSet.noneOf(Modifier.class), | |
395 | "int", "a", "int", "b"); | |
396 | javaWriter.emitStatement("int cSquared = a * a\n+ b * b"); | |
397 | javaWriter.emitStatement("return Math.sqrt(cSquared)"); | |
398 | javaWriter.endMethod(); | |
399 | javaWriter.endType(); | |
400 | assertCode("" | |
401 | + "package com.squareup;\n" | |
402 | + "\n" | |
403 | + "class Triangle {\n" | |
404 | + " double pythagorean(int a, int b) {\n" | |
405 | + " int cSquared = a * a\n" | |
406 | + " + b * b;\n" | |
407 | + " return Math.sqrt(cSquared);\n" | |
408 | + " }\n" | |
409 | + "}\n"); | |
410 | } | |
411 | ||
412 | @Test public void addImport() throws IOException { | |
413 | javaWriter.emitPackage("com.squareup"); | |
414 | javaWriter.emitImports("java.util.ArrayList"); | |
415 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
416 | javaWriter.emitField("java.util.ArrayList", "list", EnumSet.noneOf(Modifier.class), | |
417 | "new java.util.ArrayList()"); | |
418 | javaWriter.endType(); | |
419 | assertCode("" | |
420 | + "package com.squareup;\n" | |
421 | + "\n" | |
422 | + "import java.util.ArrayList;\n" | |
423 | + "public final class Foo {\n" | |
424 | + " ArrayList list = new java.util.ArrayList();\n" | |
425 | + "}\n"); | |
426 | } | |
427 | ||
428 | @Test public void addImportAsClass() throws IOException { | |
429 | javaWriter.emitPackage("com.squareup"); | |
430 | javaWriter.emitImports(ArrayList.class); | |
431 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
432 | javaWriter.emitField("java.util.ArrayList", "list", EnumSet.noneOf(Modifier.class), | |
433 | "new java.util.ArrayList()"); | |
434 | javaWriter.endType(); | |
435 | assertCode("" | |
436 | + "package com.squareup;\n" | |
437 | + "\n" | |
438 | + "import java.util.ArrayList;\n" | |
439 | + "public final class Foo {\n" | |
440 | + " ArrayList list = new java.util.ArrayList();\n" | |
441 | + "}\n"); | |
442 | } | |
443 | ||
444 | @Test public void addStaticImport() throws IOException { | |
445 | javaWriter.emitPackage("com.squareup"); | |
446 | javaWriter.emitStaticImports("java.lang.System.getProperty"); | |
447 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
448 | javaWriter.emitField("String", "bar", EnumSet.noneOf(Modifier.class), "getProperty(\"bar\")"); | |
449 | javaWriter.endType(); | |
450 | assertCode("" | |
451 | + "package com.squareup;\n" | |
452 | + "\n" | |
453 | + "import static java.lang.System.getProperty;\n" | |
454 | + "public final class Foo {\n" | |
455 | + " String bar = getProperty(\"bar\");\n" | |
456 | + "}\n"); | |
457 | } | |
458 | ||
459 | @Test | |
460 | public void addNestedClassImportAsClass() throws IOException { | |
461 | javaWriter.emitPackage("com.squareup"); | |
462 | javaWriter.emitImports(NestedClass.class); | |
463 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
464 | javaWriter.emitField("com.squareup.javawriter.JavaWriterTest.NestedClass", "nestedClass", | |
465 | EnumSet.noneOf(Modifier.class), "new NestedClass()"); | |
466 | javaWriter.endType(); | |
467 | assertCode("" | |
468 | + "package com.squareup;\n" | |
469 | + "\n" | |
470 | + "import com.squareup.javawriter.JavaWriterTest.NestedClass;\n" | |
471 | + "public final class Foo {\n" | |
472 | + " NestedClass nestedClass = new NestedClass();\n" | |
473 | + "}\n"); | |
474 | } | |
475 | ||
476 | public static class NestedClass {} | |
477 | ||
478 | @Test public void addStaticWildcardImport() throws IOException { | |
479 | javaWriter.emitPackage("com.squareup"); | |
480 | javaWriter.emitStaticImports("java.lang.System.*"); | |
481 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
482 | javaWriter.emitField("String", "bar", EnumSet.noneOf(Modifier.class), "getProperty(\"bar\")"); | |
483 | javaWriter.endType(); | |
484 | assertCode("" | |
485 | + "package com.squareup;\n" | |
486 | + "\n" | |
487 | + "import static java.lang.System.*;\n" | |
488 | + "public final class Foo {\n" | |
489 | + " String bar = getProperty(\"bar\");\n" | |
490 | + "}\n"); | |
491 | } | |
492 | ||
493 | @Test public void emptyImports() throws IOException { | |
494 | javaWriter.emitPackage("com.squareup"); | |
495 | javaWriter.emitImports(Collections.<String>emptyList()); | |
496 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
497 | javaWriter.endType(); | |
498 | assertCode("" | |
499 | + "package com.squareup;\n" | |
500 | + "\n" | |
501 | + "public final class Foo {\n" | |
502 | + "}\n"); | |
503 | } | |
504 | ||
505 | @Test public void emptyStaticImports() throws IOException { | |
506 | javaWriter.emitPackage("com.squareup"); | |
507 | javaWriter.emitStaticImports(Collections.<String>emptyList()); | |
508 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
509 | javaWriter.endType(); | |
510 | assertCode("" | |
511 | + "package com.squareup;\n" | |
512 | + "\n" | |
513 | + "public final class Foo {\n" | |
514 | + "}\n"); | |
515 | } | |
516 | ||
517 | @Test public void addImportFromSubpackage() throws IOException { | |
518 | javaWriter.emitPackage("com.squareup"); | |
519 | javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL)); | |
520 | javaWriter.emitField("com.squareup.bar.Baz", "baz"); | |
521 | javaWriter.endType(); | |
522 | assertCode("" | |
523 | + "package com.squareup;\n" | |
524 | + "\n" | |
525 | + "public final class Foo {\n" | |
526 | + " com.squareup.bar.Baz baz;\n" | |
527 | + "}\n"); | |
528 | } | |
529 | ||
530 | @Test public void ifControlFlow() throws IOException { | |
531 | javaWriter.emitPackage("com.squareup"); | |
532 | javaWriter.beginType("com.squareup.Foo", "class"); | |
533 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s"); | |
534 | javaWriter.beginControlFlow("if (s.isEmpty())"); | |
535 | javaWriter.emitStatement("int j = s.length() + %s", 13); | |
536 | javaWriter.endControlFlow(); | |
537 | javaWriter.endMethod(); | |
538 | javaWriter.endType(); | |
539 | assertCode("" | |
540 | + "package com.squareup;\n" | |
541 | + "\n" | |
542 | + "class Foo {\n" | |
543 | + " int foo(String s) {\n" | |
544 | + " if (s.isEmpty()) {\n" | |
545 | + " int j = s.length() + 13;\n" | |
546 | + " }\n" | |
547 | + " }\n" | |
548 | + "}\n"); | |
549 | } | |
550 | ||
551 | @Test public void doWhileControlFlow() throws IOException { | |
552 | javaWriter.emitPackage("com.squareup"); | |
553 | javaWriter.beginType("com.squareup.Foo", "class"); | |
554 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s"); | |
555 | javaWriter.beginControlFlow("do"); | |
556 | javaWriter.emitStatement("int j = s.length() + %s", 13); | |
557 | javaWriter.endControlFlow("while (s.isEmpty())"); | |
558 | javaWriter.endMethod(); | |
559 | javaWriter.endType(); | |
560 | assertCode("" | |
561 | + "package com.squareup;\n" | |
562 | + "\n" | |
563 | + "class Foo {\n" | |
564 | + " int foo(String s) {\n" | |
565 | + " do {\n" | |
566 | + " int j = s.length() + 13;\n" | |
567 | + " } while (s.isEmpty());\n" | |
568 | + " }\n" | |
569 | + "}\n"); | |
570 | } | |
571 | ||
572 | @Test public void tryCatchFinallyControlFlow() throws IOException { | |
573 | javaWriter.emitPackage("com.squareup"); | |
574 | javaWriter.beginType("com.squareup.Foo", "class"); | |
575 | javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s"); | |
576 | javaWriter.beginControlFlow("try"); | |
577 | javaWriter.emitStatement("int j = s.length() + %s", 13); | |
578 | javaWriter.nextControlFlow("catch (RuntimeException e)"); | |
579 | javaWriter.emitStatement("e.printStackTrace()"); | |
580 | javaWriter.nextControlFlow("finally"); | |
581 | javaWriter.emitStatement("int k = %s", 13); | |
582 | javaWriter.endControlFlow(); | |
583 | javaWriter.endMethod(); | |
584 | javaWriter.endType(); | |
585 | assertCode("" | |
586 | + "package com.squareup;\n" | |
587 | + "\n" | |
588 | + "class Foo {\n" | |
589 | + " int foo(String s) {\n" | |
590 | + " try {\n" | |
591 | + " int j = s.length() + 13;\n" | |
592 | + " } catch (RuntimeException e) {\n" | |
593 | + " e.printStackTrace();\n" | |
594 | + " } finally {\n" | |
595 | + " int k = 13;\n" | |
596 | + " }\n" | |
597 | + " }\n" | |
598 | + "}\n"); | |
599 | } | |
600 | ||
601 | @Test public void annotatedType() throws IOException { | |
602 | javaWriter.emitPackage("com.squareup"); | |
603 | javaWriter.emitImports("javax.inject.Singleton"); | |
604 | javaWriter.emitAnnotation("javax.inject.Singleton"); | |
605 | javaWriter.emitAnnotation(SuppressWarnings.class, | |
606 | StringLiteral.forValue("unchecked")); | |
607 | javaWriter.beginType("com.squareup.Foo", "class"); | |
608 | javaWriter.endType(); | |
609 | assertCode("" | |
610 | + "package com.squareup;\n" | |
611 | + "\n" | |
612 | + "import javax.inject.Singleton;\n" | |
613 | + "@Singleton\n" | |
614 | + "@SuppressWarnings(\"unchecked\")\n" | |
615 | + "class Foo {\n" | |
616 | + "}\n"); | |
617 | } | |
618 | ||
619 | @Test public void annotatedMember() throws IOException { | |
620 | javaWriter.emitPackage("com.squareup"); | |
621 | javaWriter.beginType("com.squareup.Foo", "class"); | |
622 | javaWriter.emitAnnotation(Deprecated.class); | |
623 | javaWriter.emitField("java.lang.String", "s"); | |
624 | javaWriter.endType(); | |
625 | assertCode("" | |
626 | + "package com.squareup;\n" | |
627 | + "\n" | |
628 | + "class Foo {\n" | |
629 | + " @Deprecated\n" | |
630 | + " String s;\n" | |
631 | + "}\n"); | |
632 | } | |
633 | ||
634 | @Test public void annotatedWithSingleAttribute() throws IOException { | |
635 | Map<String, Object> attributes = new LinkedHashMap<String, Object>(); | |
636 | attributes.put("overrides", true); | |
637 | ||
638 | javaWriter.emitPackage("com.squareup"); | |
639 | javaWriter.emitAnnotation("Module", attributes); | |
640 | javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class)); | |
641 | javaWriter.endType(); | |
642 | assertCode("" | |
643 | + "package com.squareup;\n" | |
644 | + "\n" | |
645 | + "@Module(overrides = true)\n" | |
646 | + "class FooModule {\n" | |
647 | + "}\n"); | |
648 | } | |
649 | ||
650 | @Test public void annotatedWithSingleValueAttribute() throws IOException { | |
651 | Map<String, Object> attributes = new LinkedHashMap<String, Object>(); | |
652 | attributes.put("value", StringLiteral.forValue("blah.Generator")); | |
653 | ||
654 | javaWriter.emitPackage("com.squareup"); | |
655 | javaWriter.emitAnnotation("Generated", attributes); | |
656 | javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class)); | |
657 | javaWriter.endType(); | |
658 | assertCode("" | |
659 | + "package com.squareup;\n" | |
660 | + "\n" | |
661 | + "@Generated(\"blah.Generator\")\n" | |
662 | + "class FooModule {\n" | |
663 | + "}\n"); | |
664 | } | |
665 | ||
666 | @Test public void annotatedWithTwoNonArrayAttributes() throws IOException { | |
667 | Map<String, Object> attributes = new LinkedHashMap<String, Object>(); | |
668 | attributes.put("overrides", true); | |
669 | attributes.put("foo", "bar"); | |
670 | ||
671 | javaWriter.emitPackage("com.squareup"); | |
672 | javaWriter.emitAnnotation("Module", attributes); | |
673 | javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class)); | |
674 | javaWriter.endType(); | |
675 | assertCode("" | |
676 | + "package com.squareup;\n" | |
677 | + "\n" | |
678 | + "@Module(overrides = true, foo = bar)\n" | |
679 | + "class FooModule {\n" | |
680 | + "}\n"); | |
681 | } | |
682 | ||
683 | @Test public void annotatedWithThreeNonArrayAttributes() throws IOException { | |
684 | Map<String, Object> attributes = new LinkedHashMap<String, Object>(); | |
685 | attributes.put("overrides", true); | |
686 | attributes.put("foo", "bar"); | |
687 | attributes.put("bar", "baz"); | |
688 | ||
689 | javaWriter.emitPackage("com.squareup"); | |
690 | javaWriter.emitAnnotation("Module", attributes); | |
691 | javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class)); | |
692 | javaWriter.endType(); | |
693 | assertCode("" | |
694 | + "package com.squareup;\n" | |
695 | + "\n" | |
696 | + "@Module(overrides = true, foo = bar, bar = baz)\n" | |
697 | + "class FooModule {\n" | |
698 | + "}\n"); | |
699 | } | |
700 | ||
701 | @Test public void annotatedWithAttributes() throws IOException { | |
702 | Map<String, Object> attributes = new LinkedHashMap<String, Object>(); | |
703 | attributes.put("overrides", true); | |
704 | attributes.put("entryPoints", new Object[] { "entryPointA", "entryPointB", "entryPointC" }); | |
705 | attributes.put("staticInjections", "com.squareup.Quux"); | |
706 | ||
707 | javaWriter.emitPackage("com.squareup"); | |
708 | javaWriter.emitAnnotation("Module", attributes); | |
709 | javaWriter.beginType("com.squareup.FooModule", "class"); | |
710 | javaWriter.endType(); | |
711 | assertCode("" | |
712 | + "package com.squareup;\n" | |
713 | + "\n" | |
714 | + "@Module(\n" | |
715 | + " overrides = true,\n" | |
716 | + " entryPoints = {\n" | |
717 | + " entryPointA,\n" | |
718 | + " entryPointB,\n" | |
719 | + " entryPointC\n" | |
720 | + " },\n" | |
721 | + " staticInjections = com.squareup.Quux\n" | |
722 | + ")\n" | |
723 | + "class FooModule {\n" | |
724 | + "}\n"); | |
725 | } | |
726 | ||
727 | @Test public void parameterizedType() throws IOException { | |
728 | javaWriter.emitPackage("com.squareup"); | |
729 | javaWriter.emitImports("java.util.Map", "java.util.Date"); | |
730 | javaWriter.beginType("com.squareup.Foo", "class"); | |
731 | javaWriter.emitField("java.util.Map<java.lang.String, java.util.Date>", "map", | |
732 | EnumSet.noneOf(Modifier.class)); | |
733 | javaWriter.endType(); | |
734 | assertCode("" | |
735 | + "package com.squareup;\n" | |
736 | + "\n" | |
737 | + "import java.util.Date;\n" | |
738 | + "import java.util.Map;\n" | |
739 | + "class Foo {\n" | |
740 | + " Map<String, Date> map;\n" | |
741 | + "}\n"); | |
742 | } | |
743 | ||
744 | @Test public void eolComment() throws IOException { | |
745 | javaWriter.emitSingleLineComment("foo"); | |
746 | assertCode("// foo\n"); | |
747 | } | |
748 | ||
749 | @Test public void javadoc() throws IOException { | |
750 | javaWriter.emitJavadoc("foo"); | |
751 | assertCode("" | |
752 | + "/**\n" | |
753 | + " * foo\n" | |
754 | + " */\n"); | |
755 | } | |
756 | ||
757 | @Test public void multilineJavadoc() throws IOException { | |
758 | javaWriter.emitJavadoc("0123456789 0123456789 0123456789 0123456789 0123456789 0123456789\n" | |
759 | + "0123456789 0123456789 0123456789 0123456789"); | |
760 | assertCode("" | |
761 | + "/**\n" | |
762 | + " * 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789\n" | |
763 | + " * 0123456789 0123456789 0123456789 0123456789\n" | |
764 | + " */\n"); | |
765 | } | |
766 | ||
767 | @Test public void multilineJavadocDoesNotEmitTrailingSpaceForEmptyLines() throws IOException { | |
768 | javaWriter.emitJavadoc("Foo\n\nBar"); | |
769 | assertCode("" | |
770 | + "/**\n" | |
771 | + " * Foo\n" | |
772 | + " *\n" | |
773 | + " * Bar\n" | |
774 | + " */\n" | |
775 | ); | |
776 | } | |
777 | ||
778 | @Test public void testType() { | |
779 | assertThat(JavaWriter.type(String.class)).as("simple type").isEqualTo("java.lang.String"); | |
780 | assertThat(JavaWriter.type(Set.class)).as("raw type").isEqualTo("java.util.Set"); | |
781 | assertThat(JavaWriter.type(Set.class, "?")).as("wildcard type").isEqualTo("java.util.Set<?>"); | |
782 | assertThat(JavaWriter.type(Map.class, JavaWriter.type(String.class), "?")) | |
783 | .as("mixed type and wildcard generic type parameters") | |
784 | .isEqualTo("java.util.Map<java.lang.String, ?>"); | |
785 | try { | |
786 | JavaWriter.type(String.class, "foo"); | |
787 | failBecauseExceptionWasNotThrown(IllegalArgumentException.class); | |
788 | } catch (Throwable e) { | |
789 | assertThat(e).as("parameterized non-generic").isInstanceOf(IllegalArgumentException.class); | |
790 | } | |
791 | try { | |
792 | JavaWriter.type(Map.class, "foo"); | |
793 | failBecauseExceptionWasNotThrown(IllegalArgumentException.class); | |
794 | } catch (Throwable e) { | |
795 | assertThat(e).as("too few type arguments").isInstanceOf(IllegalArgumentException.class); | |
796 | } | |
797 | try { | |
798 | JavaWriter.type(Set.class, "foo", "bar"); | |
799 | failBecauseExceptionWasNotThrown(IllegalArgumentException.class); | |
800 | } catch (Throwable e) { | |
801 | assertThat(e).as("too many type arguments").isInstanceOf(IllegalArgumentException.class); | |
802 | } | |
803 | } | |
804 | ||
805 | @Test public void testRawType() { | |
806 | assertThat(JavaWriter.rawType(JavaWriter.type(Set.class))) | |
807 | .as("raw type").isEqualTo("java.util.Set"); | |
808 | assertThat(JavaWriter.rawType(JavaWriter.type(Set.class, "?"))) | |
809 | .as("wildcard type").isEqualTo("java.util.Set"); | |
810 | assertThat(JavaWriter.rawType(JavaWriter.type(Set.class, "String"))) | |
811 | .as("parameterized type").isEqualTo("java.util.Set"); | |
812 | assertThat(JavaWriter.rawType(JavaWriter.type(Map.class, "String", "Integer"))) | |
813 | .as("parameterized type").isEqualTo("java.util.Map"); | |
814 | assertThat(JavaWriter.rawType("java.util.Set<com.example.Binding<com.blah.Foo.Blah>>")) | |
815 | .as("nested parameterized type").isEqualTo("java.util.Set"); | |
816 | } | |
817 | ||
818 | @Test public void compressType() throws IOException { | |
819 | javaWriter.emitPackage("com.blah"); | |
820 | javaWriter.emitImports(Set.class.getCanonicalName(), Binding.class.getCanonicalName()); | |
821 | String actual = | |
822 | javaWriter.compressType("java.util.Set<com.example.Binding<com.blah.Foo.Blah>>"); | |
823 | assertThat(actual).isEqualTo("Set<Binding<Foo.Blah>>"); | |
824 | } | |
825 | ||
826 | @Test public void compressDeeperType() throws IOException { | |
827 | javaWriter.emitPackage("com.blah"); | |
828 | javaWriter.emitImports(Binding.class.getCanonicalName()); | |
829 | String actual = javaWriter.compressType("com.example.Binding<com.blah.foo.Foo.Blah>"); | |
830 | assertThat(actual).isEqualTo("Binding<com.blah.foo.Foo.Blah>"); | |
831 | } | |
832 | ||
833 | @Test public void compressNestedType() throws IOException { | |
834 | javaWriter.emitPackage("com.blah"); | |
835 | String actual = javaWriter.compressType("com.blah.Enclosing.Nested"); | |
836 | assertThat(actual).isEqualTo("Enclosing.Nested"); | |
837 | } | |
838 | ||
839 | @Test public void compressWildcardType() throws IOException { | |
840 | javaWriter.emitPackage("com.blah"); | |
841 | javaWriter.emitImports(Binding.class.getCanonicalName()); | |
842 | String actual = javaWriter.compressType("com.example.Binding<? extends com.blah.Foo.Blah>"); | |
843 | assertThat(actual).isEqualTo("Binding<? extends Foo.Blah>"); | |
844 | } | |
845 | ||
846 | @Test public void compressSimpleNameCollisionInSamePackage() throws IOException { | |
847 | javaWriter.emitPackage("denominator"); | |
848 | javaWriter.emitImports("javax.inject.Provider", "dagger.internal.Binding"); | |
849 | String actual = javaWriter.compressType("dagger.internal.Binding<denominator.Provider>"); | |
850 | assertThat(actual).isEqualTo("Binding<denominator.Provider>"); | |
851 | } | |
852 | ||
853 | @Test public void compressJavaLangClass() throws IOException { | |
854 | javaWriter.emitPackage("com.blah"); | |
855 | String actual = javaWriter.compressType("java.lang.Class"); | |
856 | assertThat(actual).isEqualTo("Class"); | |
857 | } | |
858 | ||
859 | @Test public void compressJavaLangSubPackageClass() throws IOException { | |
860 | javaWriter.emitPackage("com.blah"); | |
861 | String actual = javaWriter.compressType("java.lang.annotation.Annotation"); | |
862 | assertThat(actual).isEqualTo("java.lang.annotation.Annotation"); | |
863 | } | |
864 | ||
865 | @Test public void compressVarargsType() throws IOException { | |
866 | javaWriter.emitPackage("com.blah"); | |
867 | javaWriter.emitImports("java.util.File"); | |
868 | String actual = javaWriter.compressType("java.util.File..."); | |
869 | assertThat(actual).isEqualTo("File..."); | |
870 | } | |
871 | ||
872 | @Test public void compressArrayType() throws IOException { | |
873 | javaWriter.emitPackage("com.blah"); | |
874 | javaWriter.emitImports("java.util.File"); | |
875 | String actual1 = javaWriter.compressType("java.util.File[]"); | |
876 | assertThat(actual1).isEqualTo("File[]"); | |
877 | String actual2 = javaWriter.compressType("java.util.File[][][]"); | |
878 | assertThat(actual2).isEqualTo("File[][][]"); | |
879 | } | |
880 | ||
881 | @Test public void configurableIndent() throws IOException { | |
882 | javaWriter.setIndent(" "); | |
883 | javaWriter.emitPackage("com.squareup"); | |
884 | javaWriter.beginType("com.squareup.Foo", "class"); | |
885 | javaWriter.emitField("String", "bar"); | |
886 | javaWriter.endType(); | |
887 | assertCode("" | |
888 | + "package com.squareup;\n" | |
889 | + "\n" | |
890 | + "class Foo {\n" | |
891 | + " String bar;\n" | |
892 | + "}\n"); | |
893 | } | |
894 | ||
895 | @Test public void outOfOrderModifierSet() throws IOException { | |
896 | Set<Modifier> modifiers = new LinkedHashSet<Modifier>(Arrays.asList(FINAL, PUBLIC)); | |
897 | javaWriter.emitPackage("com.squareup"); | |
898 | javaWriter.beginType("com.squareup.Foo", "class", modifiers); | |
899 | javaWriter.endType(); | |
900 | assertCode("" | |
901 | + "package com.squareup;\n" | |
902 | + "\n" | |
903 | + "public final class Foo {\n" | |
904 | + "}\n"); | |
905 | } | |
906 | ||
907 | @Test public void emptyNonEnumModifierSet() throws IOException { | |
908 | javaWriter.emitPackage("com.squareup"); | |
909 | javaWriter.beginType("com.squareup.Foo", "class", new LinkedHashSet<Modifier>()); | |
910 | javaWriter.endType(); | |
911 | assertCode("" | |
912 | + "package com.squareup;\n" | |
913 | + "\n" | |
914 | + "class Foo {\n" | |
915 | + "}\n"); | |
916 | } | |
917 | ||
918 | private void assertCode(String expected) { | |
919 | assertThat(stringWriter.toString()).isEqualTo(expected); | |
920 | } | |
921 | } |
0 | // Copyright 2014 Square, Inc. | |
1 | package com.squareup.javawriter; | |
2 | ||
3 | import org.junit.Test; | |
4 | import org.junit.runner.RunWith; | |
5 | import org.junit.runners.JUnit4; | |
6 | ||
7 | import static org.fest.assertions.api.Assertions.assertThat; | |
8 | ||
9 | @RunWith(JUnit4.class) | |
10 | public final class StringLiteralTest { | |
11 | @Test public void stringLiteral() { | |
12 | assertThat(StringLiteral.forValue("").toString()).isEqualTo("\"\""); | |
13 | assertThat(StringLiteral.forValue("JavaWriter").toString()).isEqualTo("\"JavaWriter\""); | |
14 | assertThat(StringLiteral.forValue("\\").toString()).isEqualTo("\"\\\\\""); | |
15 | assertThat(StringLiteral.forValue("\"").toString()).isEqualTo("\"\\\"\""); | |
16 | assertThat(StringLiteral.forValue("\b").toString()).isEqualTo("\"\\b\""); | |
17 | assertThat(StringLiteral.forValue("\t").toString()).isEqualTo("\"\\t\""); | |
18 | assertThat(StringLiteral.forValue("\n").toString()).isEqualTo("\"\\n\""); | |
19 | assertThat(StringLiteral.forValue("\f").toString()).isEqualTo("\"\\f\""); | |
20 | assertThat(StringLiteral.forValue("\r").toString()).isEqualTo("\"\\r\""); | |
21 | ||
22 | // Control characters | |
23 | for (char i = 0x1; i <= 0x1f; i++) { | |
24 | checkCharEscape(i); | |
25 | } | |
26 | for (char i = 0x7f; i <= 0x9f; i++) { | |
27 | checkCharEscape(i); | |
28 | } | |
29 | } | |
30 | ||
31 | private void checkCharEscape(char codePoint) { | |
32 | String test = "" + codePoint; | |
33 | String expected; | |
34 | switch (codePoint) { | |
35 | case 8: expected = "\"\\b\""; break; | |
36 | case 9: expected = "\"\\t\""; break; | |
37 | case 10: expected = "\"\\n\""; break; | |
38 | case 12: expected = "\"\\f\""; break; | |
39 | case 13: expected = "\"\\r\""; break; | |
40 | default: expected = "\"\\u" + String.format("%04x", (int) codePoint) + "\""; | |
41 | } | |
42 | assertThat(StringLiteral.forValue(test).toString()).isEqualTo(expected); | |
43 | } | |
44 | ||
45 | } |