[javawriter] 01/05: Imported Upstream version 2.5.1
komal sukhani
komal-guest at moszumanska.debian.org
Fri Jul 17 14:29:48 UTC 2015
This is an automated email from the git hooks/post-receive script.
komal-guest pushed a commit to branch master
in repository javawriter.
commit ebb3721a8d2c81439639bfbdfa0b7b857229346c
Author: Komal Sukhani <komaldsukhani at gmail.com>
Date: Thu Jul 16 17:24:31 2015 +0530
Imported Upstream version 2.5.1
---
.gitignore | 23 +
.travis.yml | 4 +
CHANGELOG.md | 124 +++
CONTRIBUTING.md | 17 +
LICENSE.txt | 202 +++++
README.md | 83 ++
checkstyle.xml | 121 +++
deploy_javadoc.sh | 41 +
pom.xml | 99 +++
.../java/com/squareup/javawriter/JavaWriter.java | 870 +++++++++++++++++++
.../com/squareup/javawriter/StringLiteral.java | 91 ++
src/test/java/com/example/Binding.java | 5 +
.../com/squareup/javawriter/JavaWriterTest.java | 922 +++++++++++++++++++++
.../com/squareup/javawriter/StringLiteralTest.java | 46 +
14 files changed, 2648 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..be0d31a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+.classpath
+.project
+.settings
+.checkstyle
+eclipsebin
+
+bin
+gen
+build
+out
+lib
+
+target
+pom.xml.*
+release.properties
+
+.idea
+*.iml
+classes
+
+obj
+
+.DS_Store
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1896e30
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: java
+
+notifications:
+ email: false
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a4630f2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,124 @@
+Change Log
+==========
+
+Version 2.5.1 *(2014-12-03)*
+----------------------------
+
+ * New: `StringLiteral` type which encapsulates the behavior of `stringLiteral`.
+ * Fix: Use canonical name when emitting a class import.
+ * Fix: Apply type compression to varargs and array types.
+ * Fix: Restore binary compatibility with pre-2.5 versions.
+
+
+Version 2.5.0 *(2014-04-18)*
+----------------------------
+
+ * New: Methods in interfaces will always have no body declaration.
+ * New: Control flow begin declaration now supports String format arguments.
+ * Fix: Truncate any generic type when emitting constructors.
+ * Fix: Do not emit trailing whitespace after '=' at end-of-line.
+
+
+Version 2.4.0 *(2014-01-10)*
+----------------------------
+
+ * New: Properly indent hanging lines in field initializers.
+ * New: `emitEnumValue` variant which exposes a boolean of whether the current value is the last.
+
+
+Version 2.3.1 *(2013-12-16)*
+----------------------------
+
+ * Fix: Properly handle subpackages of `java.lang` in `compressType`.
+
+
+Version 2.3.0 *(2013-11-24)*
+----------------------------
+
+ * New: Configurable indent level via `setIndent`.
+ * New: `beginConstructor` method is a semantically clearer alternative for constructors.
+ * New: `emitEnumValues` method emits a list of values followed by semicolon.
+ * `emitImports` now supports `Class` arguments directly.
+ * Previously-deprecated, `int`-based modifier methods have been removed.
+
+
+Version 2.2.1 *(2013-10-23)*
+----------------------------
+
+ * Fix: Do not emit trailing whitespace for empty Javadoc lines.
+
+
+Version 2.2.0 *(2013-09-25)*
+----------------------------
+
+ * `setCompressingTypes` controls whether types are emitted as fully-qualified or not.
+
+
+Version 2.1.2 *(2013-08-23)*
+----------------------------
+
+ * Attempt to keep annotations on a single line.
+
+
+Version 2.1.1 *(2013-07-23)*
+----------------------------
+
+ * Fix: `stringLiteral` now correctly handles escapes and control characters.
+
+
+Version 2.1.0 *(2013-07-15)*
+----------------------------
+
+ * New: All methods now take a `Set` of `Modifier`s rather than an `int`. The `int` methods are
+ now deprecated for removal in version 3.0.
+ * Annotations with a single "value" attribute will now omit the key.
+
+
+Version 2.0.1 *(2013-06-17)*
+----------------------------
+
+ * Correct casing of `emitSingleLineComment`.
+
+
+Version 2.0.0 *(2013-06-06)*
+----------------------------
+
+ * Package name is now `com.squareup.javawriter`.
+ * Support declaring `throws` clause on methods.
+
+
+Version 1.0.5 *(2013-05-08)*
+----------------------------
+
+ * Fix: Fully qualify types whose simple name matches an import.
+
+
+Version 1.0.4 *(2013-03-15)*
+----------------------------
+
+ * Fix: Static import emit now properly supports method imports.
+
+
+Version 1.0.3 *(2013-02-21)*
+-----------------------------
+
+ * Add support for emitting static imports.
+
+
+Version 1.0.2 *(2013-02-11)*
+----------------------------
+
+ * Add `type` API for helping build generic types.
+ * Minor performance improvements.
+
+
+Version 1.0.1 *(2013-02-03)*
+----------------------------
+
+ * Expose `compressType` API.
+
+
+Version 1.0.0 *(2013-02-01)*
+----------------------------
+
+Initial release.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..8131805
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,17 @@
+Contributing
+============
+
+If you would like to contribute code you can do so through GitHub by forking
+the repository and sending a pull request.
+
+When submitting code, please make every effort to follow existing conventions
+and style in order to keep the code as readable as possible. Please also make
+sure your code compiles by running `mvn clean verify`. Checkstyle failures
+during compilation indicate errors in your style and can be viewed in the
+`checkstyle-result.xml` file.
+
+Before your code can be accepted into the project you must also sign the
+[Individual Contributor License Agreement (CLA)][1].
+
+
+ [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1529ff1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,83 @@
+Java Writer
+===========
+
+`JavaWriter` is a utility class which aids in generating Java source files.
+
+Source file generation can useful when doing things such as annotation processing or interacting
+with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate
+the need to write boilerplate while also keeping a single source of truth for the metadata.
+
+
+
+Example
+-------
+
+```java
+writer.emitPackage("com.example")
+ .beginType("com.example.Person", "class", EnumSet.of(PUBLIC, FINAL))
+ .emitField("String", "firstName", EnumSet.of(PRIVATE))
+ .emitField("String", "lastName", EnumSet.of(PRIVATE))
+ .emitJavadoc("Returns the person's full name.")
+ .beginMethod("String", "getName", EnumSet.of(PUBLIC))
+ .emitStatement("return firstName + \" \" + lastName")
+ .endMethod()
+ .endType();
+```
+
+Would produce the following source output:
+
+```java
+package com.example;
+
+public final class Person {
+ private String firstName;
+ private String lastName;
+ /**
+ * Returns the person's full name.
+ */
+ public String getName() {
+ return firstName + " " + lastName;
+ }
+}
+```
+
+
+
+Download
+--------
+
+Download [the latest .jar][dl] or depend via Maven:
+```xml
+<dependency>
+ <groupId>com.squareup</groupId>
+ <artifactId>javawriter</artifactId>
+ <version>2.5.1</version>
+</dependency>
+```
+or Gradle:
+```groovy
+compile 'com.squareup:javawriter:2.5.1'
+```
+
+
+
+License
+-------
+
+ Copyright 2013 Square, Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+
+ [dl]: https://search.maven.org/remote_content?g=com.squareup&a=javawriter&v=LATEST
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..6b919c9
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+ "-//Puppy Crawl//DTD Check Configuration 1.2//EN"
+ "http://www.puppycrawl.com/dtds/configuration_1_2.dtd">
+
+<module name="Checker">
+ <module name="NewlineAtEndOfFile"/>
+ <module name="FileLength"/>
+ <module name="FileTabCharacter"/>
+
+ <!-- Trailing spaces -->
+ <module name="RegexpSingleline">
+ <property name="format" value="\s+$"/>
+ <property name="message" value="Line has trailing spaces."/>
+ </module>
+
+ <module name="TreeWalker">
+ <property name="cacheFile" value="${checkstyle.cache.file}"/>
+
+ <!-- Checks for Javadoc comments. -->
+ <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+ <!--module name="JavadocMethod"/-->
+ <!--module name="JavadocType"/-->
+ <!--module name="JavadocVariable"/-->
+ <module name="JavadocStyle"/>
+
+
+ <!-- Checks for Naming Conventions. -->
+ <!-- See http://checkstyle.sf.net/config_naming.html -->
+ <module name="ConstantName"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="MethodName"/>
+ <module name="PackageName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+ <module name="TypeName"/>
+
+
+ <!-- Checks for imports -->
+ <!-- See http://checkstyle.sf.net/config_import.html -->
+ <module name="AvoidStarImport"/>
+ <module name="IllegalImport"/>
+ <!-- defaults to sun.* packages -->
+ <module name="RedundantImport"/>
+ <module name="UnusedImports"/>
+
+
+ <!-- Checks for Size Violations. -->
+ <!-- See http://checkstyle.sf.net/config_sizes.html -->
+ <module name="LineLength">
+ <property name="max" value="100"/>
+ </module>
+ <module name="MethodLength"/>
+ <module name="ParameterNumber"/>
+
+
+ <!-- Checks for whitespace -->
+ <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+ <module name="GenericWhitespace"/>
+ <module name="EmptyForIteratorPad"/>
+ <module name="MethodParamPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="OperatorWrap"/>
+ <module name="ParenPad"/>
+ <module name="TypecastParenPad"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+
+
+ <!-- Modifier Checks -->
+ <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+ <module name="ModifierOrder"/>
+ <module name="RedundantModifier"/>
+
+
+ <!-- Checks for blocks. You know, those {}'s -->
+ <!-- See http://checkstyle.sf.net/config_blocks.html -->
+ <module name="AvoidNestedBlocks"/>
+ <!--module name="EmptyBlock"/-->
+ <module name="LeftCurly"/>
+ <module name="NeedBraces"/>
+ <module name="RightCurly"/>
+
+
+ <!-- Checks for common coding problems -->
+ <!-- See http://checkstyle.sf.net/config_coding.html -->
+ <!--module name="AvoidInlineConditionals"/-->
+ <module name="CovariantEquals"/>
+ <module name="DoubleCheckedLocking"/>
+ <module name="EmptyStatement"/>
+ <module name="EqualsAvoidNull"/>
+ <module name="EqualsHashCode"/>
+ <!--module name="HiddenField"/-->
+ <module name="IllegalInstantiation"/>
+ <module name="InnerAssignment"/>
+ <module name="MagicNumber"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="RedundantThrows"/>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+
+ <!-- Checks for class design -->
+ <!-- See http://checkstyle.sf.net/config_design.html -->
+ <!--module name="DesignForExtension"/-->
+ <module name="FinalClass"/>
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InterfaceIsType"/>
+ <!--module name="VisibilityModifier"/-->
+
+
+ <!-- Miscellaneous other checks. -->
+ <!-- See http://checkstyle.sf.net/config_misc.html -->
+ <module name="ArrayTypeStyle"/>
+ <!--module name="FinalParameters"/-->
+ <module name="TodoComment"/>
+ <module name="UpperEll"/>
+ </module>
+</module>
diff --git a/deploy_javadoc.sh b/deploy_javadoc.sh
new file mode 100755
index 0000000..3074cd2
--- /dev/null
+++ b/deploy_javadoc.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+set -ex
+
+REPO="git at github.com:square/javawriter.git"
+GROUP_ID="com.squareup"
+ARTIFACT_ID="javawriter"
+
+DIR=temp-clone
+
+# Delete any existing temporary website clone
+rm -rf $DIR
+
+# Clone the current repo into temp folder
+git clone $REPO $DIR
+
+# Move working directory into temp folder
+cd $DIR
+
+# Checkout and track the gh-pages branch
+git checkout -t origin/gh-pages
+
+# Delete everything
+rm -rf *
+
+# Download the latest javadoc
+curl -L "https://search.maven.org/remote_content?g=$GROUP_ID&a=$ARTIFACT_ID&v=LATEST&c=javadoc" > javadoc.zip
+unzip javadoc.zip
+rm javadoc.zip
+
+# Stage all files in git and create a commit
+git add .
+git add -u
+git commit -m "Website at $(date)"
+
+# Push the new files up to GitHub
+git push origin gh-pages
+
+# Delete our temp folder
+cd ..
+rm -rf $DIR
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..187e92a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.sonatype.oss</groupId>
+ <artifactId>oss-parent</artifactId>
+ <version>7</version>
+ </parent>
+
+ <groupId>com.squareup</groupId>
+ <artifactId>javawriter</artifactId>
+ <version>2.5.1</version>
+
+ <name>JavaWriter</name>
+ <description>A utility class which aids in generating Java source files.</description>
+ <url>http://github.com/square/javawriter/</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+
+ <java.version>1.6</java.version>
+ <junit.version>4.10</junit.version>
+ <fest.version>2.0M8</fest.version>
+ </properties>
+
+ <scm>
+ <url>http://github.com/square/javawriter/</url>
+ <connection>scm:git:git://github.com/square/javawriter.git</connection>
+ <developerConnection>scm:git:ssh://git@github.com/square/javawriter.git</developerConnection>
+ <tag>HEAD</tag>
+ </scm>
+
+ <issueManagement>
+ <system>GitHub Issues</system>
+ <url>http://github.com/square/javawriter/issues</url>
+ </issueManagement>
+
+ <licenses>
+ <license>
+ <name>Apache 2.0</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+
+ <organization>
+ <name>Square, Inc.</name>
+ <url>http://squareup.com</url>
+ </organization>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>${junit.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easytesting</groupId>
+ <artifactId>fest-assert-core</artifactId>
+ <version>${fest.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.0</version>
+ <configuration>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.9.1</version>
+ <configuration>
+ <failsOnError>true</failsOnError>
+ <configLocation>checkstyle.xml</configLocation>
+ <consoleOutput>true</consoleOutput>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>verify</phase>
+ <goals>
+ <goal>checkstyle</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/src/main/java/com/squareup/javawriter/JavaWriter.java b/src/main/java/com/squareup/javawriter/JavaWriter.java
new file mode 100644
index 0000000..52eae07
--- /dev/null
+++ b/src/main/java/com/squareup/javawriter/JavaWriter.java
@@ -0,0 +1,870 @@
+// Copyright 2013 Square, Inc.
+package com.squareup.javawriter;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.lang.model.element.Modifier;
+
+import static javax.lang.model.element.Modifier.ABSTRACT;
+
+/** A utility class which aids in generating Java source files. */
+public class JavaWriter implements Closeable {
+ private static final Pattern TYPE_TRAILER = Pattern.compile("(.*?)(\\.\\.\\.|(?:\\[\\])+)$");
+ private static final Pattern TYPE_PATTERN = Pattern.compile("(?:[\\w$]+\\.)*([\\w\\.*$]+)");
+ private static final int MAX_SINGLE_LINE_ATTRIBUTES = 3;
+ private static final String INDENT = " ";
+
+ /** Map fully qualified type names to their short names. */
+ private final Map<String, String> importedTypes = new LinkedHashMap<String, String>();
+
+ private String packagePrefix;
+ private final Deque<Scope> scopes = new ArrayDeque<Scope>();
+ private final Deque<String> types = new ArrayDeque<String>();
+ private final Writer out;
+ private boolean isCompressingTypes = true;
+ private String indent = INDENT;
+
+ /**
+ * @param out the stream to which Java source will be written. This should be a buffered stream.
+ */
+ public JavaWriter(Writer out) {
+ this.out = out;
+ }
+
+ public void setCompressingTypes(boolean isCompressingTypes) {
+ this.isCompressingTypes = isCompressingTypes;
+ }
+
+ public boolean isCompressingTypes() {
+ return isCompressingTypes;
+ }
+
+ public void setIndent(String indent) {
+ this.indent = indent;
+ }
+
+ public String getIndent() {
+ return indent;
+ }
+
+ /** Emit a package declaration and empty line. */
+ public JavaWriter emitPackage(String packageName) throws IOException {
+ if (this.packagePrefix != null) {
+ throw new IllegalStateException();
+ }
+ if (packageName.isEmpty()) {
+ this.packagePrefix = "";
+ } else {
+ out.write("package ");
+ out.write(packageName);
+ out.write(";\n\n");
+ this.packagePrefix = packageName + ".";
+ }
+ return this;
+ }
+
+ /**
+ * Emit an import for each {@code type} provided. For the duration of the file, all references to
+ * these classes will be automatically shortened.
+ */
+ public JavaWriter emitImports(String... types) throws IOException {
+ return emitImports(Arrays.asList(types));
+ }
+
+ /**
+ * Emit an import for each {@code type} provided. For the duration of the file, all references to
+ * these classes will be automatically shortened.
+ */
+ public JavaWriter emitImports(Class<?>... types) throws IOException {
+ List<String> classNames = new ArrayList<String>(types.length);
+ for (Class<?> classToImport : types) {
+ classNames.add(classToImport.getCanonicalName());
+ }
+ return emitImports(classNames);
+ }
+
+ /**
+ * Emit an import for each {@code type} in the provided {@code Collection}. For the duration of
+ * the file, all references to these classes will be automatically shortened.
+ */
+ public JavaWriter emitImports(Collection<String> types) throws IOException {
+ for (String type : new TreeSet<String>(types)) {
+ Matcher matcher = TYPE_PATTERN.matcher(type);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException(type);
+ }
+ if (importedTypes.put(type, matcher.group(1)) != null) {
+ throw new IllegalArgumentException(type);
+ }
+ out.write("import ");
+ out.write(type);
+ out.write(";\n");
+ }
+ return this;
+ }
+
+ /**
+ * Emit a static import for each {@code type} provided. For the duration of the file,
+ * all references to these classes will be automatically shortened.
+ */
+ public JavaWriter emitStaticImports(String... types) throws IOException {
+ return emitStaticImports(Arrays.asList(types));
+ }
+
+ /**
+ * Emit a static import for each {@code type} in the provided {@code Collection}. For the
+ * duration of the file, all references to these classes will be automatically shortened.
+ */
+ public JavaWriter emitStaticImports(Collection<String> types) throws IOException {
+ for (String type : new TreeSet<String>(types)) {
+ Matcher matcher = TYPE_PATTERN.matcher(type);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException(type);
+ }
+ if (importedTypes.put(type, matcher.group(1)) != null) {
+ throw new IllegalArgumentException(type);
+ }
+ out.write("import static ");
+ out.write(type);
+ out.write(";\n");
+ }
+ return this;
+ }
+
+ /**
+ * Emits a name like {@code java.lang.String} or {@code java.util.List<java.lang.String>},
+ * compressing it with imports if possible. Type compression will only be enabled if
+ * {@link #isCompressingTypes} is true.
+ */
+ private JavaWriter emitCompressedType(String type) throws IOException {
+ if (isCompressingTypes) {
+ out.write(compressType(type));
+ } else {
+ out.write(type);
+ }
+ return this;
+ }
+
+ /** Try to compress a fully-qualified class name to only the class name. */
+ public String compressType(String type) {
+ Matcher trailer = TYPE_TRAILER.matcher(type);
+ if (trailer.matches()) {
+ type = trailer.group(1);
+ }
+
+ StringBuilder sb = new StringBuilder();
+ if (this.packagePrefix == null) {
+ throw new IllegalStateException();
+ }
+
+ Matcher m = TYPE_PATTERN.matcher(type);
+ int pos = 0;
+ while (true) {
+ boolean found = m.find(pos);
+
+ // Copy non-matching characters like "<".
+ int typeStart = found ? m.start() : type.length();
+ sb.append(type, pos, typeStart);
+
+ if (!found) {
+ break;
+ }
+
+ // Copy a single class name, shortening it if possible.
+ String name = m.group(0);
+ String imported = importedTypes.get(name);
+ if (imported != null) {
+ sb.append(imported);
+ } else if (isClassInPackage(name, packagePrefix)) {
+ String compressed = name.substring(packagePrefix.length());
+ if (isAmbiguous(compressed)) {
+ sb.append(name);
+ } else {
+ sb.append(compressed);
+ }
+ } else if (isClassInPackage(name, "java.lang.")) {
+ sb.append(name.substring("java.lang.".length()));
+ } else {
+ sb.append(name);
+ }
+ pos = m.end();
+ }
+
+ if (trailer.matches()) {
+ sb.append(trailer.group(2));
+ }
+ return sb.toString();
+ }
+
+ private static boolean isClassInPackage(String name, String packagePrefix) {
+ if (name.startsWith(packagePrefix)) {
+ if (name.indexOf('.', packagePrefix.length()) == -1) {
+ return true;
+ }
+ // check to see if the part after the package looks like a class
+ if (Character.isUpperCase(name.charAt(packagePrefix.length()))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the imports contain a class with same simple name as {@code compressed}.
+ *
+ * @param compressed simple name of the type
+ */
+ private boolean isAmbiguous(String compressed) {
+ return importedTypes.values().contains(compressed);
+ }
+
+ /**
+ * Emits an initializer declaration.
+ *
+ * @param isStatic true if it should be an static initializer, false for an instance initializer.
+ */
+ public JavaWriter beginInitializer(boolean isStatic) throws IOException {
+ indent();
+ if (isStatic) {
+ out.write("static");
+ out.write(" {\n");
+ } else {
+ out.write("{\n");
+ }
+ scopes.push(Scope.INITIALIZER);
+ return this;
+ }
+
+ /** Ends the current initializer declaration. */
+ public JavaWriter endInitializer() throws IOException {
+ popScope(Scope.INITIALIZER);
+ indent();
+ out.write("}\n");
+ return this;
+ }
+
+ /**
+ * Emits a type declaration.
+ *
+ * @param kind such as "class", "interface" or "enum".
+ */
+ public JavaWriter beginType(String type, String kind) throws IOException {
+ return beginType(type, kind, EnumSet.noneOf(Modifier.class), null);
+ }
+
+ /**
+ * Emits a type declaration.
+ *
+ * @param kind such as "class", "interface" or "enum".
+ */
+ public JavaWriter beginType(String type, String kind, Set<Modifier> modifiers)
+ throws IOException {
+ return beginType(type, kind, modifiers, null);
+ }
+
+ /**
+ * Emits a type declaration.
+ *
+ * @param kind such as "class", "interface" or "enum".
+ * @param extendsType the class to extend, or null for no extends clause.
+ */
+ public JavaWriter beginType(String type, String kind, Set<Modifier> modifiers, String extendsType,
+ String... implementsTypes) throws IOException {
+ indent();
+ emitModifiers(modifiers);
+ out.write(kind);
+ out.write(" ");
+ emitCompressedType(type);
+ if (extendsType != null) {
+ out.write(" extends ");
+ emitCompressedType(extendsType);
+ }
+ if (implementsTypes.length > 0) {
+ out.write("\n");
+ indent();
+ out.write(" implements ");
+ for (int i = 0; i < implementsTypes.length; i++) {
+ if (i != 0) {
+ out.write(", ");
+ }
+ emitCompressedType(implementsTypes[i]);
+ }
+ }
+ out.write(" {\n");
+ scopes.push("interface".equals(kind) ? Scope.INTERFACE_DECLARATION : Scope.TYPE_DECLARATION);
+ types.push(type);
+ return this;
+ }
+
+ /** Completes the current type declaration. */
+ public JavaWriter endType() throws IOException {
+ popScope(Scope.TYPE_DECLARATION, Scope.INTERFACE_DECLARATION);
+ types.pop();
+ indent();
+ out.write("}\n");
+ return this;
+ }
+
+ /** Emits a field declaration. */
+ public JavaWriter emitField(String type, String name) throws IOException {
+ return emitField(type, name, EnumSet.noneOf(Modifier.class), null);
+ }
+
+ /** Emits a field declaration. */
+ public JavaWriter emitField(String type, String name, Set<Modifier> modifiers)
+ throws IOException {
+ return emitField(type, name, modifiers, null);
+ }
+
+ /** Emits a field declaration. */
+ public JavaWriter emitField(String type, String name, Set<Modifier> modifiers,
+ String initialValue) throws IOException {
+ indent();
+ emitModifiers(modifiers);
+ emitCompressedType(type);
+ out.write(" ");
+ out.write(name);
+
+ if (initialValue != null) {
+ out.write(" =");
+ if (!initialValue.startsWith("\n")) {
+ out.write(" ");
+ }
+
+ String[] lines = initialValue.split("\n", -1);
+ out.write(lines[0]);
+ for (int i = 1; i < lines.length; i++) {
+ out.write("\n");
+ hangingIndent();
+ out.write(lines[i]);
+ }
+ }
+ out.write(";\n");
+ return this;
+ }
+
+ /**
+ * Emit a method declaration.
+ *
+ * <p>A {@code null} return type may be used to indicate a constructor, but
+ * {@link #beginConstructor(Set, String...)} should be preferred. This behavior may be removed in
+ * a future release.
+ *
+ * @param returnType the method's return type, or null for constructors
+ * @param name the method name, or the fully qualified class name for constructors.
+ * @param modifiers the set of modifiers to be applied to the method
+ * @param parameters alternating parameter types and names.
+ */
+ public JavaWriter beginMethod(String returnType, String name, Set<Modifier> modifiers,
+ String... parameters) throws IOException {
+ return beginMethod(returnType, name, modifiers, Arrays.asList(parameters), null);
+ }
+
+ /**
+ * Emit a method declaration.
+ *
+ * <p>A {@code null} return type may be used to indicate a constructor, but
+ * {@link #beginConstructor(Set, List, List)} should be preferred. This behavior may be removed in
+ * a future release.
+ *
+ * @param returnType the method's return type, or null for constructors.
+ * @param name the method name, or the fully qualified class name for constructors.
+ * @param modifiers the set of modifiers to be applied to the method
+ * @param parameters alternating parameter types and names.
+ * @param throwsTypes the classes to throw, or null for no throws clause.
+ */
+ public JavaWriter beginMethod(String returnType, String name, Set<Modifier> modifiers,
+ List<String> parameters, List<String> throwsTypes) throws IOException {
+ indent();
+ emitModifiers(modifiers);
+ if (returnType != null) {
+ emitCompressedType(returnType);
+ out.write(" ");
+ out.write(name);
+ } else {
+ emitCompressedType(name);
+ }
+ out.write("(");
+ if (parameters != null) {
+ for (int p = 0; p < parameters.size();) {
+ if (p != 0) {
+ out.write(", ");
+ }
+ emitCompressedType(parameters.get(p++));
+ out.write(" ");
+ emitCompressedType(parameters.get(p++));
+ }
+ }
+ out.write(")");
+ if (throwsTypes != null && throwsTypes.size() > 0) {
+ out.write("\n");
+ indent();
+ out.write(" throws ");
+ for (int i = 0; i < throwsTypes.size(); i++) {
+ if (i != 0) {
+ out.write(", ");
+ }
+ emitCompressedType(throwsTypes.get(i));
+ }
+ }
+ if (modifiers.contains(ABSTRACT) || Scope.INTERFACE_DECLARATION.equals(scopes.peek())) {
+ out.write(";\n");
+ scopes.push(Scope.ABSTRACT_METHOD);
+ } else {
+ out.write(" {\n");
+ scopes.push(returnType == null ? Scope.CONSTRUCTOR : Scope.NON_ABSTRACT_METHOD);
+ }
+ return this;
+ }
+
+ public JavaWriter beginConstructor(Set<Modifier> modifiers, String... parameters)
+ throws IOException {
+ beginMethod(null, rawType(types.peekFirst()), modifiers, parameters);
+ return this;
+ }
+
+ public JavaWriter beginConstructor(Set<Modifier> modifiers,
+ List<String> parameters, List<String> throwsTypes)
+ throws IOException {
+ beginMethod(null, rawType(types.peekFirst()), modifiers, parameters, throwsTypes);
+ return this;
+ }
+
+ /** Emits some Javadoc comments with line separated by {@code \n}. */
+ public JavaWriter emitJavadoc(String javadoc, Object... params) throws IOException {
+ String formatted = String.format(javadoc, params);
+
+ indent();
+ out.write("/**\n");
+ for (String line : formatted.split("\n")) {
+ indent();
+ out.write(" *");
+ if (!line.isEmpty()) {
+ out.write(" ");
+ out.write(line);
+ }
+ out.write("\n");
+ }
+ indent();
+ out.write(" */\n");
+ return this;
+ }
+
+ /** Emits a single line comment. */
+ public JavaWriter emitSingleLineComment(String comment, Object... args) throws IOException {
+ indent();
+ out.write("// ");
+ out.write(String.format(comment, args));
+ out.write("\n");
+ return this;
+ }
+
+ public JavaWriter emitEmptyLine() throws IOException {
+ out.write("\n");
+ return this;
+ }
+
+ public JavaWriter emitEnumValue(String name) throws IOException {
+ indent();
+ out.write(name);
+ out.write(",\n");
+ return this;
+ }
+
+ /**
+ * A simple switch to emit the proper enum depending if its last causing it to be terminated
+ * by a semi-colon ({@code ;}).
+ */
+ public JavaWriter emitEnumValue(String name, boolean isLast) throws IOException {
+ return isLast ? emitLastEnumValue(name) : emitEnumValue(name);
+ }
+
+ private JavaWriter emitLastEnumValue(String name) throws IOException {
+ indent();
+ out.write(name);
+ out.write(";\n");
+ return this;
+ }
+
+ /** Emit a list of enum values followed by a semi-colon ({@code ;}). */
+ public JavaWriter emitEnumValues(Iterable<String> names) throws IOException {
+ Iterator<String> iterator = names.iterator();
+
+ while (iterator.hasNext()) {
+ String name = iterator.next();
+ if (iterator.hasNext()) {
+ emitEnumValue(name);
+ } else {
+ emitLastEnumValue(name);
+ }
+ }
+
+ return this;
+ }
+
+ /** Equivalent to {@code annotation(annotation, emptyMap())}. */
+ public JavaWriter emitAnnotation(String annotation) throws IOException {
+ return emitAnnotation(annotation, Collections.<String, Object>emptyMap());
+ }
+
+ /** Equivalent to {@code annotation(annotationType.getName(), emptyMap())}. */
+ public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType) throws IOException {
+ return emitAnnotation(type(annotationType), Collections.<String, Object>emptyMap());
+ }
+
+ /**
+ * Annotates the next element with {@code annotationType} and a {@code value}.
+ *
+ * @param value an object used as the default (value) parameter of the annotation. The value will
+ * be encoded using Object.toString(); use {@link #stringLiteral} for String values. Object
+ * arrays are written one element per line.
+ */
+ public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType, Object value)
+ throws IOException {
+ return emitAnnotation(type(annotationType), value);
+ }
+
+ /**
+ * Annotates the next element with {@code annotation} and a {@code value}.
+ *
+ * @param value an object used as the default (value) parameter of the annotation. The value will
+ * be encoded using Object.toString(); use {@link #stringLiteral} for String values. Object
+ * arrays are written one element per line.
+ */
+ public JavaWriter emitAnnotation(String annotation, Object value) throws IOException {
+ indent();
+ out.write("@");
+ emitCompressedType(annotation);
+ out.write("(");
+ emitAnnotationValue(value);
+ out.write(")");
+ out.write("\n");
+ return this;
+ }
+
+ /** Equivalent to {@code annotation(annotationType.getName(), attributes)}. */
+ public JavaWriter emitAnnotation(Class<? extends Annotation> annotationType,
+ Map<String, ?> attributes) throws IOException {
+ return emitAnnotation(type(annotationType), attributes);
+ }
+
+ /**
+ * Annotates the next element with {@code annotation} and {@code attributes}.
+ *
+ * @param attributes a map from annotation attribute names to their values. Values are encoded
+ * using Object.toString(); use {@link #stringLiteral} for String values. Object arrays are
+ * written one element per line.
+ */
+ public JavaWriter emitAnnotation(String annotation, Map<String, ?> attributes)
+ throws IOException {
+ indent();
+ out.write("@");
+ emitCompressedType(annotation);
+ switch (attributes.size()) {
+ case 0:
+ break;
+ case 1:
+ Entry<String, ?> onlyEntry = attributes.entrySet().iterator().next();
+ out.write("(");
+ if (!"value".equals(onlyEntry.getKey())) {
+ out.write(onlyEntry.getKey());
+ out.write(" = ");
+ }
+ emitAnnotationValue(onlyEntry.getValue());
+ out.write(")");
+ break;
+ default:
+ boolean split = attributes.size() > MAX_SINGLE_LINE_ATTRIBUTES
+ || containsArray(attributes.values());
+ out.write("(");
+ scopes.push(Scope.ANNOTATION_ATTRIBUTE);
+ String separator = split ? "\n" : "";
+ for (Map.Entry<String, ?> entry : attributes.entrySet()) {
+ out.write(separator);
+ separator = split ? ",\n" : ", ";
+ if (split) {
+ indent();
+ }
+ out.write(entry.getKey());
+ out.write(" = ");
+ Object value = entry.getValue();
+ emitAnnotationValue(value);
+ }
+ popScope(Scope.ANNOTATION_ATTRIBUTE);
+ if (split) {
+ out.write("\n");
+ indent();
+ }
+ out.write(")");
+ break;
+ }
+ out.write("\n");
+ return this;
+ }
+
+ private boolean containsArray(Collection<?> values) {
+ for (Object value : values) {
+ if (value instanceof Object[]) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Writes a single annotation value. If the value is an array, each element in the array will be
+ * written to its own line.
+ */
+ private JavaWriter emitAnnotationValue(Object value) throws IOException {
+ if (value instanceof Object[]) {
+ out.write("{");
+ boolean firstValue = true;
+ scopes.push(Scope.ANNOTATION_ARRAY_VALUE);
+ for (Object o : ((Object[]) value)) {
+ if (firstValue) {
+ firstValue = false;
+ out.write("\n");
+ } else {
+ out.write(",\n");
+ }
+ indent();
+ out.write(o.toString());
+ }
+ popScope(Scope.ANNOTATION_ARRAY_VALUE);
+ out.write("\n");
+ indent();
+ out.write("}");
+ } else {
+ out.write(value.toString());
+ }
+ return this;
+ }
+
+ /**
+ * @param pattern a code pattern like "int i = %s". Newlines will be further indented. Should not
+ * contain trailing semicolon.
+ */
+ public JavaWriter emitStatement(String pattern, Object... args) throws IOException {
+ checkInMethod();
+ String[] lines = String.format(pattern, args).split("\n", -1);
+ indent();
+ out.write(lines[0]);
+ for (int i = 1; i < lines.length; i++) {
+ out.write("\n");
+ hangingIndent();
+ out.write(lines[i]);
+ }
+ out.write(";\n");
+ return this;
+ }
+
+ /**
+ * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". Shouldn't
+ * contain braces or newline characters.
+ */
+ // NOTE: This method is for binary compatibility with previous versions.
+ public JavaWriter beginControlFlow(String controlFlow) throws IOException {
+ return beginControlFlow(controlFlow, new Object[0]);
+ }
+
+ /**
+ * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". Shouldn't
+ * contain braces or newline characters.
+ */
+ public JavaWriter beginControlFlow(String controlFlow, Object... args) throws IOException {
+ checkInMethod();
+ indent();
+ out.write(String.format(controlFlow, args));
+ out.write(" {\n");
+ scopes.push(Scope.CONTROL_FLOW);
+ return this;
+ }
+
+ /**
+ * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)".
+ * Shouldn't contain braces or newline characters.
+ */
+ // NOTE: This method is for binary compatibility with previous versions.
+ public JavaWriter nextControlFlow(String controlFlow) throws IOException {
+ return nextControlFlow(controlFlow, new Object[0]);
+ }
+
+ /**
+ * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)".
+ * Shouldn't contain braces or newline characters.
+ */
+ public JavaWriter nextControlFlow(String controlFlow, Object... args) throws IOException {
+ popScope(Scope.CONTROL_FLOW);
+ indent();
+ scopes.push(Scope.CONTROL_FLOW);
+ out.write("} ");
+ out.write(String.format(controlFlow, args));
+ out.write(" {\n");
+ return this;
+ }
+
+ public JavaWriter endControlFlow() throws IOException {
+ return endControlFlow(null);
+ }
+
+ /**
+ * @param controlFlow the optional control flow construct and its code, such as
+ * "while(foo == 20)". Only used for "do/while" control flows.
+ */
+ // NOTE: This method is for binary compatibility with previous versions.
+ public JavaWriter endControlFlow(String controlFlow) throws IOException {
+ return endControlFlow(controlFlow, new Object[0]);
+ }
+
+ /**
+ * @param controlFlow the optional control flow construct and its code, such as
+ * "while(foo == 20)". Only used for "do/while" control flows.
+ */
+ public JavaWriter endControlFlow(String controlFlow, Object... args) throws IOException {
+ popScope(Scope.CONTROL_FLOW);
+ indent();
+ if (controlFlow != null) {
+ out.write("} ");
+ out.write(String.format(controlFlow, args));
+ out.write(";\n");
+ } else {
+ out.write("}\n");
+ }
+ return this;
+ }
+
+ /** Completes the current method declaration. */
+ public JavaWriter endMethod() throws IOException {
+ Scope popped = scopes.pop();
+ // support calling a constructor a "method" to support the legacy code
+ if (popped == Scope.NON_ABSTRACT_METHOD || popped == Scope.CONSTRUCTOR) {
+ indent();
+ out.write("}\n");
+ } else if (popped != Scope.ABSTRACT_METHOD) {
+ throw new IllegalStateException();
+ }
+ return this;
+ }
+
+ /** Completes the current constructor declaration. */
+ public JavaWriter endConstructor() throws IOException {
+ popScope(Scope.CONSTRUCTOR);
+ indent();
+ out.write("}\n");
+ return this;
+ }
+
+ /**
+ * Returns the string literal representing {@code data}, including wrapping quotes.
+ *
+ * @deprecated use {@link StringLiteral} and its {@link StringLiteral#literal()} method instead.
+ */
+ @Deprecated
+ public static String stringLiteral(String data) {
+ return StringLiteral.forValue(data).literal();
+ }
+
+ /** Build a string representation of a type and optionally its generic type arguments. */
+ public static String type(Class<?> raw, String... parameters) {
+ if (parameters.length == 0) {
+ return raw.getCanonicalName();
+ }
+ if (raw.getTypeParameters().length != parameters.length) {
+ throw new IllegalArgumentException();
+ }
+ StringBuilder result = new StringBuilder();
+ result.append(raw.getCanonicalName());
+ result.append("<");
+ result.append(parameters[0]);
+ for (int i = 1; i < parameters.length; i++) {
+ result.append(", ");
+ result.append(parameters[i]);
+ }
+ result.append(">");
+ return result.toString();
+ }
+
+ /** Build a string representation of the raw type for a (optionally generic) type. */
+ public static String rawType(String type) {
+ int lessThanIndex = type.indexOf('<');
+ if (lessThanIndex != -1) {
+ return type.substring(0, lessThanIndex);
+ }
+ return type;
+ }
+
+ @Override public void close() throws IOException {
+ out.close();
+ }
+
+ /** Emits the modifiers to the writer. */
+ private void emitModifiers(Set<Modifier> modifiers) throws IOException {
+ if (modifiers.isEmpty()) {
+ return;
+ }
+ // Use an EnumSet to ensure the proper ordering
+ if (!(modifiers instanceof EnumSet)) {
+ modifiers = EnumSet.copyOf(modifiers);
+ }
+ for (Modifier modifier : modifiers) {
+ out.append(modifier.toString()).append(' ');
+ }
+ }
+
+ private void indent() throws IOException {
+ for (int i = 0, count = scopes.size(); i < count; i++) {
+ out.write(indent);
+ }
+ }
+
+ private void hangingIndent() throws IOException {
+ for (int i = 0, count = scopes.size() + 2; i < count; i++) {
+ out.write(indent);
+ }
+ }
+
+ private static final EnumSet<Scope> METHOD_SCOPES = EnumSet.of(
+ Scope.NON_ABSTRACT_METHOD, Scope.CONSTRUCTOR, Scope.CONTROL_FLOW, Scope.INITIALIZER);
+
+ private void checkInMethod() {
+ if (!METHOD_SCOPES.contains(scopes.peekFirst())) {
+ throw new IllegalArgumentException();
+ }
+ }
+
+ private void popScope(Scope... expected) {
+ if (!EnumSet.copyOf(Arrays.asList(expected)).contains(scopes.pop())) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private enum Scope {
+ TYPE_DECLARATION,
+ INTERFACE_DECLARATION,
+ ABSTRACT_METHOD,
+ NON_ABSTRACT_METHOD,
+ CONSTRUCTOR,
+ CONTROL_FLOW,
+ ANNOTATION_ATTRIBUTE,
+ ANNOTATION_ARRAY_VALUE,
+ INITIALIZER
+ }
+}
diff --git a/src/main/java/com/squareup/javawriter/StringLiteral.java b/src/main/java/com/squareup/javawriter/StringLiteral.java
new file mode 100644
index 0000000..c0a846d
--- /dev/null
+++ b/src/main/java/com/squareup/javawriter/StringLiteral.java
@@ -0,0 +1,91 @@
+// Copyright 2014 Square, Inc.
+package com.squareup.javawriter;
+
+import java.util.Formatter;
+
+/**
+ * Represents a string literal as found in Java source code.
+ */
+public final class StringLiteral {
+ /** Returns a new {@link StringLiteral} instance for the intended value of the literal. */
+ public static StringLiteral forValue(String value) {
+ return new StringLiteral(value, stringLiteral(value));
+ }
+
+ /** Returns the string literal representing {@code data}, including wrapping quotes. */
+ private static String stringLiteral(String value) {
+ StringBuilder result = new StringBuilder();
+ result.append('"');
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ switch (c) {
+ case '"':
+ result.append("\\\"");
+ break;
+ case '\\':
+ result.append("\\\\");
+ break;
+ case '\b':
+ result.append("\\b");
+ break;
+ case '\t':
+ result.append("\\t");
+ break;
+ case '\n':
+ result.append("\\n");
+ break;
+ case '\f':
+ result.append("\\f");
+ break;
+ case '\r':
+ result.append("\\r");
+ break;
+ default:
+ if (Character.isISOControl(c)) {
+ new Formatter(result).format("\\u%04x", (int) c);
+ } else {
+ result.append(c);
+ }
+ }
+ }
+ result.append('"');
+ return result.toString();
+ }
+
+ private final String value;
+ private final String literal;
+
+ private StringLiteral(String value, String literal) {
+ this.value = value;
+ this.literal = literal;
+ }
+
+ public String value() {
+ return value;
+ }
+
+ public String literal() {
+ return literal;
+ }
+
+ @Override
+ public String toString() {
+ return literal;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof StringLiteral) {
+ return this.value.equals(((StringLiteral) obj).value);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/test/java/com/example/Binding.java b/src/test/java/com/example/Binding.java
new file mode 100644
index 0000000..678c0c4
--- /dev/null
+++ b/src/test/java/com/example/Binding.java
@@ -0,0 +1,5 @@
+// Copyright 2013 Square, Inc.
+package com.example;
+
+public class Binding<T> {
+}
diff --git a/src/test/java/com/squareup/javawriter/JavaWriterTest.java b/src/test/java/com/squareup/javawriter/JavaWriterTest.java
new file mode 100644
index 0000000..d97f20a
--- /dev/null
+++ b/src/test/java/com/squareup/javawriter/JavaWriterTest.java
@@ -0,0 +1,922 @@
+// Copyright 2013 Square, Inc.
+package com.squareup.javawriter;
+
+import com.example.Binding;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.element.Modifier;
+import org.junit.Test;
+
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+import static javax.lang.model.element.Modifier.PRIVATE;
+import static javax.lang.model.element.Modifier.PUBLIC;
+import static javax.lang.model.element.Modifier.STATIC;
+import static org.fest.assertions.api.Assertions.assertThat;
+import static org.fest.assertions.api.Assertions.failBecauseExceptionWasNotThrown;
+
+public final class JavaWriterTest {
+ private final StringWriter stringWriter = new StringWriter();
+ private final JavaWriter javaWriter = new JavaWriter(stringWriter);
+
+ @Test public void typeDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public final class Foo {\n"
+ + "}\n");
+ }
+
+ @Test public void enumDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "enum", EnumSet.of(PUBLIC));
+ javaWriter.emitEnumValue("BAR");
+ javaWriter.emitEnumValue("BAZ");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public enum Foo {\n"
+ + " BAR,\n"
+ + " BAZ,\n"
+ + "}\n");
+ }
+
+ @Test public void enumDeclarationWithMethod() throws IOException{
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "enum", EnumSet.of(PUBLIC));
+ javaWriter.emitEnumValues(Arrays.asList("BAR", "BAZ"));
+ javaWriter.beginMethod("void", "foo", EnumSet.of(PUBLIC));
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public enum Foo {\n"
+ + " BAR,\n"
+ + " BAZ;\n"
+ + " public void foo() {\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void enumDeclarationWithAnnotationAndMethod() throws IOException{
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "enum", EnumSet.of(PUBLIC));
+ List<String> list = Arrays.asList("BAR", "BAZ");
+ int index = 0;
+ for (Iterator<String> i = list.iterator(); i.hasNext(); ) {
+ javaWriter.emitAnnotation("ProtoEnum", index);
+ javaWriter.emitEnumValue(i.next(), !i.hasNext());
+ index++;
+ }
+ javaWriter.beginMethod("void", "foo", EnumSet.of(PUBLIC));
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public enum Foo {\n"
+ + " @ProtoEnum(0)\n"
+ + " BAR,\n"
+ + " @ProtoEnum(1)\n"
+ + " BAZ;\n"
+ + " public void foo() {\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void fieldDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitField("java.lang.String", "string", EnumSet.of(PRIVATE, STATIC));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " private static String string;\n"
+ + "}\n");
+ }
+
+ @Test public void fieldDeclarationWithInitialValue() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitField("java.lang.String", "string", EnumSet.noneOf(Modifier.class),
+ "\"bar\" + \"baz\"");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " String string = \"bar\" + \"baz\";\n"
+ + "}\n");
+ }
+
+ @Test public void fieldDeclarationWithWrappingInitialValue() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitField("java.lang.String", "string", EnumSet.noneOf(Modifier.class),
+ "\"bar\"\n+ \"baz\"\n+ \"biz\"");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " String string = \"bar\"\n"
+ + " + \"baz\"\n"
+ + " + \"biz\";\n"
+ + "}\n");
+ }
+
+ // If the initializer begins with a newline, don't emit a space after the '='.
+ @Test public void fieldDeclarationWithNewlineInitialValue() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitField("java.lang.String", "string", EnumSet.noneOf(Modifier.class),
+ "\n\"bar\"\n+ \"baz\"\n+ \"biz\"");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " String string =\n"
+ + " \"bar\"\n"
+ + " + \"baz\"\n"
+ + " + \"biz\";\n"
+ + "}\n");
+ }
+
+ @Test public void abstractMethodDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("java.lang.String", "foo", EnumSet.of(ABSTRACT, PUBLIC),
+ "java.lang.Object", "object", "java.lang.String", "s");
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " public abstract String foo(Object object, String s);\n"
+ + "}\n");
+ }
+
+ @Test public void abstractMethodDeclarationWithThrows() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("java.lang.String", "foo", EnumSet.of(ABSTRACT, PUBLIC),
+ Arrays.asList("java.lang.Object", "object", "java.lang.String", "s"),
+ Arrays.asList("java.io.IOException"));
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " public abstract String foo(Object object, String s)\n"
+ + " throws java.io.IOException;\n"
+ + "}\n");
+ }
+
+ @Test public void nonAbstractMethodDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s");
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s) {\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void nonAbstractMethodDeclarationWithThrows() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class),
+ Arrays.asList("java.lang.String", "s"), Arrays.asList("java.io.IOException"));
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s)\n"
+ + " throws java.io.IOException {\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void interfaceMethodDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "interface");
+ javaWriter.beginMethod("java.lang.String", "foo", EnumSet.noneOf(Modifier.class),
+ "java.lang.Object", "object", "java.lang.String", "s");
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "interface Foo {\n"
+ + " String foo(Object object, String s);\n"
+ + "}\n");
+ }
+
+ @Test public void interfaceMethodDeclarationWithThrows() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "interface");
+ javaWriter.beginMethod("java.lang.String", "foo", EnumSet.noneOf(Modifier.class),
+ Arrays.asList("java.lang.Object", "object", "java.lang.String", "s"),
+ Arrays.asList("java.io.IOException"));
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "interface Foo {\n"
+ + " String foo(Object object, String s)\n"
+ + " throws java.io.IOException;\n"
+ + "}\n");
+ }
+
+ @Test public void constructorDeclaration() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginConstructor(EnumSet.of(PUBLIC), "java.lang.String", "s");
+ javaWriter.endConstructor();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " public Foo(String s) {\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void simpleConstructor() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginConstructor(EnumSet.of(PUBLIC), "java.lang.String", "s");
+ javaWriter.emitStatement("if (%s == null) throw new NullPointerException()", "s");
+ javaWriter.endConstructor();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " public Foo(String s) {\n"
+ + " if (s == null) throw new NullPointerException();\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void genericsConstructor() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo<T>", "class");
+ javaWriter.emitField("T", "fooType", EnumSet.of(PRIVATE));
+ javaWriter.beginConstructor(EnumSet.of(PUBLIC), "T", "s");
+ javaWriter.emitStatement("if (%s == null) throw new NullPointerException()", "s");
+ javaWriter.endConstructor();
+ javaWriter.beginMethod("T", "getFooType", EnumSet.of(PUBLIC));
+ javaWriter.emitStatement("return fooType");
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo<T> {\n"
+ + " private T fooType;\n"
+ + " public Foo(T s) {\n"
+ + " if (s == null) throw new NullPointerException();\n"
+ + " }\n"
+ + " public T getFooType() {\n"
+ + " return fooType;\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void constructorDeclarationInNestedTypes() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginConstructor(EnumSet.of(PUBLIC), "java.lang.String", "s");
+ javaWriter.endConstructor();
+ javaWriter.beginType("com.squareup.Bar", "class");
+ javaWriter.beginConstructor(EnumSet.noneOf(Modifier.class));
+ javaWriter.endConstructor();
+ javaWriter.endType();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " public Foo(String s) {\n"
+ + " }\n"
+ + " class Bar {\n"
+ + " Bar() {\n"
+ + " }\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void constructorDeclarationWithThrows() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginConstructor(EnumSet.of(PUBLIC),
+ Arrays.asList("java.lang.String", "s"), Arrays.asList("java.io.IOException"));
+ javaWriter.endConstructor();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " public Foo(String s)\n"
+ + " throws java.io.IOException {\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void statement() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s");
+ javaWriter.emitStatement("int j = s.length() + %s", 13);
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s) {\n"
+ + " int j = s.length() + 13;\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void statementPrecededByComment() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s");
+ javaWriter.emitSingleLineComment("foo");
+ javaWriter.emitStatement("int j = s.length() + %s", 13);
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s) {\n"
+ + " // foo\n"
+ + " int j = s.length() + 13;\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void multiLineStatement() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Triangle", "class");
+ javaWriter.beginMethod("double", "pythagorean", EnumSet.noneOf(Modifier.class),
+ "int", "a", "int", "b");
+ javaWriter.emitStatement("int cSquared = a * a\n+ b * b");
+ javaWriter.emitStatement("return Math.sqrt(cSquared)");
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Triangle {\n"
+ + " double pythagorean(int a, int b) {\n"
+ + " int cSquared = a * a\n"
+ + " + b * b;\n"
+ + " return Math.sqrt(cSquared);\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void addImport() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitImports("java.util.ArrayList");
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.emitField("java.util.ArrayList", "list", EnumSet.noneOf(Modifier.class),
+ "new java.util.ArrayList()");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import java.util.ArrayList;\n"
+ + "public final class Foo {\n"
+ + " ArrayList list = new java.util.ArrayList();\n"
+ + "}\n");
+ }
+
+ @Test public void addImportAsClass() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitImports(ArrayList.class);
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.emitField("java.util.ArrayList", "list", EnumSet.noneOf(Modifier.class),
+ "new java.util.ArrayList()");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import java.util.ArrayList;\n"
+ + "public final class Foo {\n"
+ + " ArrayList list = new java.util.ArrayList();\n"
+ + "}\n");
+ }
+
+ @Test public void addStaticImport() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitStaticImports("java.lang.System.getProperty");
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.emitField("String", "bar", EnumSet.noneOf(Modifier.class), "getProperty(\"bar\")");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import static java.lang.System.getProperty;\n"
+ + "public final class Foo {\n"
+ + " String bar = getProperty(\"bar\");\n"
+ + "}\n");
+ }
+
+ @Test
+ public void addNestedClassImportAsClass() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitImports(NestedClass.class);
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.emitField("com.squareup.javawriter.JavaWriterTest.NestedClass", "nestedClass",
+ EnumSet.noneOf(Modifier.class), "new NestedClass()");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import com.squareup.javawriter.JavaWriterTest.NestedClass;\n"
+ + "public final class Foo {\n"
+ + " NestedClass nestedClass = new NestedClass();\n"
+ + "}\n");
+ }
+
+ public static class NestedClass {}
+
+ @Test public void addStaticWildcardImport() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitStaticImports("java.lang.System.*");
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.emitField("String", "bar", EnumSet.noneOf(Modifier.class), "getProperty(\"bar\")");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import static java.lang.System.*;\n"
+ + "public final class Foo {\n"
+ + " String bar = getProperty(\"bar\");\n"
+ + "}\n");
+ }
+
+ @Test public void emptyImports() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitImports(Collections.<String>emptyList());
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public final class Foo {\n"
+ + "}\n");
+ }
+
+ @Test public void emptyStaticImports() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitStaticImports(Collections.<String>emptyList());
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public final class Foo {\n"
+ + "}\n");
+ }
+
+ @Test public void addImportFromSubpackage() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class", EnumSet.of(PUBLIC, FINAL));
+ javaWriter.emitField("com.squareup.bar.Baz", "baz");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public final class Foo {\n"
+ + " com.squareup.bar.Baz baz;\n"
+ + "}\n");
+ }
+
+ @Test public void ifControlFlow() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s");
+ javaWriter.beginControlFlow("if (s.isEmpty())");
+ javaWriter.emitStatement("int j = s.length() + %s", 13);
+ javaWriter.endControlFlow();
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s) {\n"
+ + " if (s.isEmpty()) {\n"
+ + " int j = s.length() + 13;\n"
+ + " }\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void doWhileControlFlow() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s");
+ javaWriter.beginControlFlow("do");
+ javaWriter.emitStatement("int j = s.length() + %s", 13);
+ javaWriter.endControlFlow("while (s.isEmpty())");
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s) {\n"
+ + " do {\n"
+ + " int j = s.length() + 13;\n"
+ + " } while (s.isEmpty());\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void tryCatchFinallyControlFlow() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.beginMethod("int", "foo", EnumSet.noneOf(Modifier.class), "java.lang.String", "s");
+ javaWriter.beginControlFlow("try");
+ javaWriter.emitStatement("int j = s.length() + %s", 13);
+ javaWriter.nextControlFlow("catch (RuntimeException e)");
+ javaWriter.emitStatement("e.printStackTrace()");
+ javaWriter.nextControlFlow("finally");
+ javaWriter.emitStatement("int k = %s", 13);
+ javaWriter.endControlFlow();
+ javaWriter.endMethod();
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " int foo(String s) {\n"
+ + " try {\n"
+ + " int j = s.length() + 13;\n"
+ + " } catch (RuntimeException e) {\n"
+ + " e.printStackTrace();\n"
+ + " } finally {\n"
+ + " int k = 13;\n"
+ + " }\n"
+ + " }\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedType() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitImports("javax.inject.Singleton");
+ javaWriter.emitAnnotation("javax.inject.Singleton");
+ javaWriter.emitAnnotation(SuppressWarnings.class,
+ StringLiteral.forValue("unchecked"));
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import javax.inject.Singleton;\n"
+ + "@Singleton\n"
+ + "@SuppressWarnings(\"unchecked\")\n"
+ + "class Foo {\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedMember() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitAnnotation(Deprecated.class);
+ javaWriter.emitField("java.lang.String", "s");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " @Deprecated\n"
+ + " String s;\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedWithSingleAttribute() throws IOException {
+ Map<String, Object> attributes = new LinkedHashMap<String, Object>();
+ attributes.put("overrides", true);
+
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitAnnotation("Module", attributes);
+ javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "@Module(overrides = true)\n"
+ + "class FooModule {\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedWithSingleValueAttribute() throws IOException {
+ Map<String, Object> attributes = new LinkedHashMap<String, Object>();
+ attributes.put("value", StringLiteral.forValue("blah.Generator"));
+
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitAnnotation("Generated", attributes);
+ javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "@Generated(\"blah.Generator\")\n"
+ + "class FooModule {\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedWithTwoNonArrayAttributes() throws IOException {
+ Map<String, Object> attributes = new LinkedHashMap<String, Object>();
+ attributes.put("overrides", true);
+ attributes.put("foo", "bar");
+
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitAnnotation("Module", attributes);
+ javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "@Module(overrides = true, foo = bar)\n"
+ + "class FooModule {\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedWithThreeNonArrayAttributes() throws IOException {
+ Map<String, Object> attributes = new LinkedHashMap<String, Object>();
+ attributes.put("overrides", true);
+ attributes.put("foo", "bar");
+ attributes.put("bar", "baz");
+
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitAnnotation("Module", attributes);
+ javaWriter.beginType("com.squareup.FooModule", "class", EnumSet.noneOf(Modifier.class));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "@Module(overrides = true, foo = bar, bar = baz)\n"
+ + "class FooModule {\n"
+ + "}\n");
+ }
+
+ @Test public void annotatedWithAttributes() throws IOException {
+ Map<String, Object> attributes = new LinkedHashMap<String, Object>();
+ attributes.put("overrides", true);
+ attributes.put("entryPoints", new Object[] { "entryPointA", "entryPointB", "entryPointC" });
+ attributes.put("staticInjections", "com.squareup.Quux");
+
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitAnnotation("Module", attributes);
+ javaWriter.beginType("com.squareup.FooModule", "class");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "@Module(\n"
+ + " overrides = true,\n"
+ + " entryPoints = {\n"
+ + " entryPointA,\n"
+ + " entryPointB,\n"
+ + " entryPointC\n"
+ + " },\n"
+ + " staticInjections = com.squareup.Quux\n"
+ + ")\n"
+ + "class FooModule {\n"
+ + "}\n");
+ }
+
+ @Test public void parameterizedType() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.emitImports("java.util.Map", "java.util.Date");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitField("java.util.Map<java.lang.String, java.util.Date>", "map",
+ EnumSet.noneOf(Modifier.class));
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "import java.util.Date;\n"
+ + "import java.util.Map;\n"
+ + "class Foo {\n"
+ + " Map<String, Date> map;\n"
+ + "}\n");
+ }
+
+ @Test public void eolComment() throws IOException {
+ javaWriter.emitSingleLineComment("foo");
+ assertCode("// foo\n");
+ }
+
+ @Test public void javadoc() throws IOException {
+ javaWriter.emitJavadoc("foo");
+ assertCode(""
+ + "/**\n"
+ + " * foo\n"
+ + " */\n");
+ }
+
+ @Test public void multilineJavadoc() throws IOException {
+ javaWriter.emitJavadoc("0123456789 0123456789 0123456789 0123456789 0123456789 0123456789\n"
+ + "0123456789 0123456789 0123456789 0123456789");
+ assertCode(""
+ + "/**\n"
+ + " * 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789\n"
+ + " * 0123456789 0123456789 0123456789 0123456789\n"
+ + " */\n");
+ }
+
+ @Test public void multilineJavadocDoesNotEmitTrailingSpaceForEmptyLines() throws IOException {
+ javaWriter.emitJavadoc("Foo\n\nBar");
+ assertCode(""
+ + "/**\n"
+ + " * Foo\n"
+ + " *\n"
+ + " * Bar\n"
+ + " */\n"
+ );
+ }
+
+ @Test public void testType() {
+ assertThat(JavaWriter.type(String.class)).as("simple type").isEqualTo("java.lang.String");
+ assertThat(JavaWriter.type(Set.class)).as("raw type").isEqualTo("java.util.Set");
+ assertThat(JavaWriter.type(Set.class, "?")).as("wildcard type").isEqualTo("java.util.Set<?>");
+ assertThat(JavaWriter.type(Map.class, JavaWriter.type(String.class), "?"))
+ .as("mixed type and wildcard generic type parameters")
+ .isEqualTo("java.util.Map<java.lang.String, ?>");
+ try {
+ JavaWriter.type(String.class, "foo");
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (Throwable e) {
+ assertThat(e).as("parameterized non-generic").isInstanceOf(IllegalArgumentException.class);
+ }
+ try {
+ JavaWriter.type(Map.class, "foo");
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (Throwable e) {
+ assertThat(e).as("too few type arguments").isInstanceOf(IllegalArgumentException.class);
+ }
+ try {
+ JavaWriter.type(Set.class, "foo", "bar");
+ failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+ } catch (Throwable e) {
+ assertThat(e).as("too many type arguments").isInstanceOf(IllegalArgumentException.class);
+ }
+ }
+
+ @Test public void testRawType() {
+ assertThat(JavaWriter.rawType(JavaWriter.type(Set.class)))
+ .as("raw type").isEqualTo("java.util.Set");
+ assertThat(JavaWriter.rawType(JavaWriter.type(Set.class, "?")))
+ .as("wildcard type").isEqualTo("java.util.Set");
+ assertThat(JavaWriter.rawType(JavaWriter.type(Set.class, "String")))
+ .as("parameterized type").isEqualTo("java.util.Set");
+ assertThat(JavaWriter.rawType(JavaWriter.type(Map.class, "String", "Integer")))
+ .as("parameterized type").isEqualTo("java.util.Map");
+ assertThat(JavaWriter.rawType("java.util.Set<com.example.Binding<com.blah.Foo.Blah>>"))
+ .as("nested parameterized type").isEqualTo("java.util.Set");
+ }
+
+ @Test public void compressType() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ javaWriter.emitImports(Set.class.getCanonicalName(), Binding.class.getCanonicalName());
+ String actual =
+ javaWriter.compressType("java.util.Set<com.example.Binding<com.blah.Foo.Blah>>");
+ assertThat(actual).isEqualTo("Set<Binding<Foo.Blah>>");
+ }
+
+ @Test public void compressDeeperType() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ javaWriter.emitImports(Binding.class.getCanonicalName());
+ String actual = javaWriter.compressType("com.example.Binding<com.blah.foo.Foo.Blah>");
+ assertThat(actual).isEqualTo("Binding<com.blah.foo.Foo.Blah>");
+ }
+
+ @Test public void compressNestedType() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ String actual = javaWriter.compressType("com.blah.Enclosing.Nested");
+ assertThat(actual).isEqualTo("Enclosing.Nested");
+ }
+
+ @Test public void compressWildcardType() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ javaWriter.emitImports(Binding.class.getCanonicalName());
+ String actual = javaWriter.compressType("com.example.Binding<? extends com.blah.Foo.Blah>");
+ assertThat(actual).isEqualTo("Binding<? extends Foo.Blah>");
+ }
+
+ @Test public void compressSimpleNameCollisionInSamePackage() throws IOException {
+ javaWriter.emitPackage("denominator");
+ javaWriter.emitImports("javax.inject.Provider", "dagger.internal.Binding");
+ String actual = javaWriter.compressType("dagger.internal.Binding<denominator.Provider>");
+ assertThat(actual).isEqualTo("Binding<denominator.Provider>");
+ }
+
+ @Test public void compressJavaLangClass() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ String actual = javaWriter.compressType("java.lang.Class");
+ assertThat(actual).isEqualTo("Class");
+ }
+
+ @Test public void compressJavaLangSubPackageClass() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ String actual = javaWriter.compressType("java.lang.annotation.Annotation");
+ assertThat(actual).isEqualTo("java.lang.annotation.Annotation");
+ }
+
+ @Test public void compressVarargsType() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ javaWriter.emitImports("java.util.File");
+ String actual = javaWriter.compressType("java.util.File...");
+ assertThat(actual).isEqualTo("File...");
+ }
+
+ @Test public void compressArrayType() throws IOException {
+ javaWriter.emitPackage("com.blah");
+ javaWriter.emitImports("java.util.File");
+ String actual1 = javaWriter.compressType("java.util.File[]");
+ assertThat(actual1).isEqualTo("File[]");
+ String actual2 = javaWriter.compressType("java.util.File[][][]");
+ assertThat(actual2).isEqualTo("File[][][]");
+ }
+
+ @Test public void configurableIndent() throws IOException {
+ javaWriter.setIndent(" ");
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class");
+ javaWriter.emitField("String", "bar");
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + " String bar;\n"
+ + "}\n");
+ }
+
+ @Test public void outOfOrderModifierSet() throws IOException {
+ Set<Modifier> modifiers = new LinkedHashSet<Modifier>(Arrays.asList(FINAL, PUBLIC));
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class", modifiers);
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "public final class Foo {\n"
+ + "}\n");
+ }
+
+ @Test public void emptyNonEnumModifierSet() throws IOException {
+ javaWriter.emitPackage("com.squareup");
+ javaWriter.beginType("com.squareup.Foo", "class", new LinkedHashSet<Modifier>());
+ javaWriter.endType();
+ assertCode(""
+ + "package com.squareup;\n"
+ + "\n"
+ + "class Foo {\n"
+ + "}\n");
+ }
+
+ private void assertCode(String expected) {
+ assertThat(stringWriter.toString()).isEqualTo(expected);
+ }
+}
diff --git a/src/test/java/com/squareup/javawriter/StringLiteralTest.java b/src/test/java/com/squareup/javawriter/StringLiteralTest.java
new file mode 100644
index 0000000..bd5f8bf
--- /dev/null
+++ b/src/test/java/com/squareup/javawriter/StringLiteralTest.java
@@ -0,0 +1,46 @@
+// Copyright 2014 Square, Inc.
+package com.squareup.javawriter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import static org.fest.assertions.api.Assertions.assertThat;
+
+ at RunWith(JUnit4.class)
+public final class StringLiteralTest {
+ @Test public void stringLiteral() {
+ assertThat(StringLiteral.forValue("").toString()).isEqualTo("\"\"");
+ assertThat(StringLiteral.forValue("JavaWriter").toString()).isEqualTo("\"JavaWriter\"");
+ assertThat(StringLiteral.forValue("\\").toString()).isEqualTo("\"\\\\\"");
+ assertThat(StringLiteral.forValue("\"").toString()).isEqualTo("\"\\\"\"");
+ assertThat(StringLiteral.forValue("\b").toString()).isEqualTo("\"\\b\"");
+ assertThat(StringLiteral.forValue("\t").toString()).isEqualTo("\"\\t\"");
+ assertThat(StringLiteral.forValue("\n").toString()).isEqualTo("\"\\n\"");
+ assertThat(StringLiteral.forValue("\f").toString()).isEqualTo("\"\\f\"");
+ assertThat(StringLiteral.forValue("\r").toString()).isEqualTo("\"\\r\"");
+
+ // Control characters
+ for (char i = 0x1; i <= 0x1f; i++) {
+ checkCharEscape(i);
+ }
+ for (char i = 0x7f; i <= 0x9f; i++) {
+ checkCharEscape(i);
+ }
+ }
+
+ private void checkCharEscape(char codePoint) {
+ String test = "" + codePoint;
+ String expected;
+ switch (codePoint) {
+ case 8: expected = "\"\\b\""; break;
+ case 9: expected = "\"\\t\""; break;
+ case 10: expected = "\"\\n\""; break;
+ case 12: expected = "\"\\f\""; break;
+ case 13: expected = "\"\\r\""; break;
+ default: expected = "\"\\u" + String.format("%04x", (int) codePoint) + "\"";
+ }
+ assertThat(StringLiteral.forValue(test).toString()).isEqualTo(expected);
+ }
+
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/javawriter.git
More information about the pkg-java-commits
mailing list