Codebase list javawriter / ebb3721
Imported Upstream version 2.5.1 Komal Sukhani 8 years ago
14 changed file(s) with 2648 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
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 language: java
1
2 notifications:
3 email: false
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.example;
2
3 public class Binding<T> {
4 }
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 }