pax_global_header00006660000000000000000000000064133554547600014526gustar00rootroot0000000000000052 comment=2f4182ec303e020e592d8893cf612214df5bf96a javapoet-1.11.1/000077500000000000000000000000001335545476000134205ustar00rootroot00000000000000javapoet-1.11.1/.buildscript/000077500000000000000000000000001335545476000160225ustar00rootroot00000000000000javapoet-1.11.1/.buildscript/deploy_snapshot.sh000077500000000000000000000017611335545476000216010ustar00rootroot00000000000000#!/bin/bash # # Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo. # # Adapted from https://coderwall.com/p/9b_lfq and # https://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ SLUG="square/javapoet" JDK="oraclejdk8" BRANCH="master" set -e if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'." elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'." elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "Skipping snapshot deployment: was pull request." elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'." else echo "Deploying snapshot..." mvn clean source:jar javadoc:jar deploy --settings=".buildscript/settings.xml" -Dmaven.test.skip=true echo "Snapshot deployed!" fi javapoet-1.11.1/.buildscript/settings.xml000066400000000000000000000003351335545476000204050ustar00rootroot00000000000000 sonatype-nexus-snapshots ${env.CI_DEPLOY_USERNAME} ${env.CI_DEPLOY_PASSWORD} javapoet-1.11.1/.gitignore000066400000000000000000000002261335545476000154100ustar00rootroot00000000000000.classpath .project .settings .checkstyle eclipsebin bin gen build out lib target pom.xml.* release.properties .idea *.iml classes obj .DS_Store javapoet-1.11.1/.travis.yml000066400000000000000000000025151335545476000155340ustar00rootroot00000000000000language: java matrix: include: - env: JDK='Oracle JDK 8' jdk: oraclejdk8 - env: JDK='Oracle JDK 9' jdk: oraclejdk9 - env: JDK='Oracle JDK 10' install: . ./install-jdk.sh -F 10 -L BCL - env: JDK='OpenJDK 10' install: . ./install-jdk.sh -F 10 -L GPL - env: JDK='Oracle JDK 11' install: . ./install-jdk.sh -F 11 -L BCL - env: JDK='OpenJDK 11' install: . ./install-jdk.sh -F 11 -L GPL allow_failures: # ErrorProne/javac is not yet working on JDK 11 - env: JDK='Oracle JDK 11' - env: JDK='OpenJDK 11' # Direct usage of `install-jdk.sh` might be superseded by https://github.com/travis-ci/travis-build/pull/1347 before_install: - unset _JAVA_OPTIONS - wget https://github.com/sormuras/bach/raw/1.0.1/install-jdk.sh after_success: - .buildscript/deploy_snapshot.sh env: global: - secure: "nkVNCk8H2orIZOmow0t+Qub1lFQCYpJgNZf17zYI5x0JVqQNCqkcTYYDHqzwkvkmixXFCrfYZQuXy7x2qg9zjCX+vmhlmiMWwe8dNa34OLTseuuR2irS0C8nRGRYxKM7EGenRZSqbFVUksKRm2iWnHKxtmCzeDaS7MoMit2wdUo=" - secure: "j8+hPaZnyM+UlOBYOEA96fPbVWbN6bMQ28SGQnFMwxo2axHi9ww9Au1N7002HzHnxX8iyesdWFBigArnEL8zKEoXH9Bmur0sn3Ys4bu72C3ozscP4cjXfYSHj8aVLp1EIMdQPDF7MkCccx9l7ONdsW0ltmdiVUtDxzqkH+63WLU=" branches: except: - gh-pages notifications: email: false sudo: false cache: directories: - $HOME/.m2 javapoet-1.11.1/CHANGELOG.md000066400000000000000000000251011335545476000152300ustar00rootroot00000000000000Change Log ========== JavaPoet 1.11.1 *(2018-05-16)* ----------------------------- * Fix: JavaPoet 1.11 had a regression where `TypeName.get()` would throw on error types, masking other errors in an annotation processing round. This is fixed with a test to prevent future regressions! JavaPoet 1.11.0 *(2018-04-29)* ----------------------------- * New: Support `TYPE_USE` annotations on each enclosing `ClassName`. * New: Work around a compiler bug in `TypeName.get(TypeElement)`. There was a problem getting an element's kind when building from source ABIs. JavaPoet 1.10.0 *(2018-01-27)* ----------------------------- * **JavaPoet now requires Java 8 or newer.** * New: `$Z` as an optional newline (zero-width space) if a line may exceed 100 chars. * New: `CodeBlock.join()` and `CodeBlock.joining()` let you join codeblocks by delimiters. * New: Add `CodeBlock.Builder.isEmpty()`. * New: `addStatement(CodeBlock)` overloads for `CodeBlock` and `MethodSpec`. * Fix: Include annotations when emitting type variables. * Fix: Use the right imports for annotated type parameters. * Fix: Don't incorrectly escape classnames that start with `$`. JavaPoet 1.9.0 *(2017-05-13)* ----------------------------- * Fix: Don't emit incorrect code when the declared type's signature references another type with the same simple name. * Fix: Support anonymous inner classes in `ClassName.get()`. * New: `MethodSpec.Builder.addNamedCode()` and `TypeSpec.anonymousClassBuilder(CodeBlock)`. JavaPoet 1.8.0 *(2016-11-09)* ----------------------------- * New: Basic support for line wrapping. Use `$W` to insert a Wrappable Whitespace character. It'll emit either a single space or a newline with appropriate indentation. * New: Named arguments in `CodeBlock`. These are intended to make larger code snippets easier to read: ``` Map map = new LinkedHashMap<>(); map.put("count", 3); map.put("greeting", "Hello, "); map.put("system", System.class); String template = "" + "for (int i = 0; i < $count:L; i++) {\n" + " $system:T.out.println($greeting:S + list.get(i));\n" + "}\n"; CodeBlock.Builder builder = CodeBlock.builder(); builder.addNamed(template, map); ``` * New: `addJavadoc(CodeBlock)` overloads for TypeSpec, MethodSpec, and FieldSpec. * New: `MethodSpec.addComment()` makes it easy to add a `// single-line comment.` * New: `ClassName.getReflectionName()` returns a string like `java.util.Map$Entry`. * Fix: Always write UTF-8. Previously JavaPoet would use the system default charset which was potentially inconsistent across environments. * Fix: Permit (constant) fields to be defined in annotation types. JavaPoet 1.7.0 *(2016-04-26)* ----------------------------- * New: Support parameterized types that enclose other types, like `Outer.Inner`. * New: `TypeName.isBoxedPrimitive()`. JavaPoet 1.6.1 *(2016-03-21)* ----------------------------- * Fix: Double quotes and backslashes in string literals were not properly quoted in 1.6.0. This is now fixed. JavaPoet 1.6.0 *(2016-03-19)* ----------------------------- * New: Revive `CodeBlock.of()`, a handy factory method for building code blocks. * New: Add `TypeSpec` factory methods that take a `ClassName`. * New: `TypeName.annotated()` adds an annotation to a type. * New: `TypeVariableName.withBounds()` adds bounds to a type variable. * New: `TypeSpec.Builder.addInitializerBlock()` adds an instance initializer. * Fix: Make `TypeSpec.Kind` enum public. This can be used to check if a `TypeSpec` is a class, interface, enum, or annotation. * Fix: Don’t break import resolution on annotated types. * Fix: Forbid unexpected modifiers like `private` on annotation members. * Fix: Deduplicate exceptions in `MethodSpec.Builder`. * Fix: Treat `ErrorType` like a regular `DeclaredType` in `TypeName.get()`. This should make it easier to write annotation processors. JavaPoet 1.5.1 *(2016-01-10)* ----------------------------- * Fix: Annotated `TypeName` instances are only equal if their annotations are equal. JavaPoet 1.5.0 *(2016-01-10)* ----------------------------- * New: `import static`! See `JavaFile.Builder.addStaticImport()` variants. * New: Overload `NameAllocator.newName(String)` for creating a one-off name without a tag. * Fix: AnnotationSpec escapes character literals properly. * Fix: Don't stack overflow when `TypeVariableName` is part of `ParameterizedTypeName`. * Fix: Reporting not used indexed arguments in like `add("$1S", "a", "b")`. * Fix: Prevent import of types located in the default package, i.e. have no package name. JavaPoet 1.4.0 *(2015-11-13)* ----------------------------- * New: `AnnotationSpec.get(Annotation)`. * New: Type annotations! `TypeName.annotated()` can attach annotations like `@Nullable` directly to types. This works for both top-level types and type parameters as in `List<@Nullable String>`. * New: `equals()` and `hashCode()` on `AnnotationSpec`, `CodeBlock`, `FieldSpec`, `JavaFile`, `MethodSpec`, `ParameterSpec`, `TypeName`, and `TypeSpec`. * New: `NameAllocator.clone()` to refine a NameAllocator for use in an inner scope code block. * Fix: Don't stack overflow when `TypeVariableName` gets a self-referential type. * Fix: Better handling of name collisions on imports. Previously JavaPoet did the wrong thing when a referenced type and a nested types had the same name. JavaPoet 1.3.0 *(2015-09-20)* ----------------------------- * New: `NameAllocator` API makes it easy to declare non-conflicting names. * New: Support annotations on enum values. * Fix: Avoid infinite recursion in `TypeName.get(TypeMirror)`. * Fix: Use qualified name for conflicting simple names in the same file. * Fix: Better messages for parameter indexing errors. JavaPoet 1.2.0 *(2015-07-04)* ----------------------------- * New: Arguments may have positional indexes like `$1T` and `$2N`. Indexes can be used to refer to the same argument multiple times in a single format string. * New: Permit Javadoc on enum constants. * New: Class initializer blocks with `addStaticBlock()`. * Fix: `MethodSpec.overriding()` retains annotations. JavaPoet 1.1.0 *(2015-05-25)* ----------------------------- * New: Eager validation of argument types like `$T` and `$N`. * New: `MethodSpec.varargs(boolean)` to generate varags methods. * New: `AnnotationSpec.get()` and `MethodSpec.overriding()` to create annotations and methods from the `javax.lang.model` API. * New: `JavaFile.toJavaFileObject()`. * New: Java 8 `DEFAULT` modifier. * New: `toBuilder()` methods to build upon objects already constructed. * New: Generate `@interface` annotation types. * New: `TypeName.box()` and `TypeName.unbox()` convenience APIs. * Fix: `nextControlFlow()` accepts arguments. * Fix: Reject duplicate calls to set the superclass. * Fix: `WildcardTypeName.get(WildcardType)` no longer throws a `NullPointerException`. * Fix: Don't allow double field initialization. JavaPoet 1.0.0 *(2015-01-28)* ----------------------------- * This update is a complete rewrite. The project name is now `javapoet`. We renamed the it so you can simultaneously use the old JavaWriter API and our new builder-based APIs in one project. * Immutable value objects and builders. Instead of streaming the `.java` file from top to bottom, you now define members in whatever way is convenient. * We now use our own models for type names. * Imports are now added automatically. JavaWriter 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. JavaWriter 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. JavaWriter 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. JavaWriter 2.3.1 *(2013-12-16)* ------------------------------- * Fix: Properly handle subpackages of `java.lang` in `compressType`. JavaWriter 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. JavaWriter 2.2.1 *(2013-10-23)* ------------------------------- * Fix: Do not emit trailing whitespace for empty Javadoc lines. JavaWriter 2.2.0 *(2013-09-25)* ------------------------------- * `setCompressingTypes` controls whether types are emitted as fully-qualified or not. JavaWriter 2.1.2 *(2013-08-23)* ------------------------------- * Attempt to keep annotations on a single line. JavaWriter 2.1.1 *(2013-07-23)* ------------------------------- * Fix: `stringLiteral` now correctly handles escapes and control characters. JavaWriter 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 JavaPoet 1.0. * Annotations with a single "value" attribute will now omit the key. JavaWriter 2.0.1 *(2013-06-17)* ------------------------------- * Correct casing of `emitSingleLineComment`. JavaWriter 2.0.0 *(2013-06-06)* ------------------------------- * Package name is now `com.squareup.javawriter`. * Support declaring `throws` clause on methods. JavaWriter 1.0.5 *(2013-05-08)* ------------------------------- * Fix: Fully qualify types whose simple name matches an import. JavaWriter 1.0.4 *(2013-03-15)* ------------------------------- * Fix: Static import emit now properly supports method imports. JavaWriter 1.0.3 *(2013-02-21)* ------------------------------- * Add support for emitting static imports. JavaWriter 1.0.2 *(2013-02-11)* ------------------------------- * Add `type` API for helping build generic types. * Minor performance improvements. JavaWriter 1.0.1 *(2013-02-03)* ------------------------------- * Expose `compressType` API. JavaWriter 1.0.0 *(2013-02-01)* ------------------------------- Initial release. javapoet-1.11.1/CONTRIBUTING.md000066400000000000000000000013221335545476000156470ustar00rootroot00000000000000Contributing ============ 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 javapoet-1.11.1/LICENSE.txt000066400000000000000000000261361335545476000152530ustar00rootroot00000000000000 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. javapoet-1.11.1/README.md000066400000000000000000000634771335545476000147200ustar00rootroot00000000000000JavaPoet ======== `JavaPoet` is a Java API for generating `.java` source files. Source file generation can be 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 Here's a (boring) `HelloWorld` class: ```java package com.example.helloworld; public final class HelloWorld { public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); } } ``` And this is the (exciting) code to generate it with JavaPoet: ```java MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args") .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(System.out); ``` To declare the main method, we've created a `MethodSpec` "main" configured with modifiers, return type, parameters and code statements. We add the main method to a `HelloWorld` class, and then add that to a `HelloWorld.java` file. In this case we write the file to `System.out`, but we could also get it as a string (`JavaFile.toString()`) or write it to the file system (`JavaFile.writeTo()`). The [Javadoc][javadoc] catalogs the complete JavaPoet API, which we explore below. ### Code & Control Flow Most of JavaPoet's API uses plain old immutable Java objects. There's also builders, method chaining and varargs to make the API friendly. JavaPoet offers models for classes & interfaces (`TypeSpec`), fields (`FieldSpec`), methods & constructors (`MethodSpec`), parameters (`ParameterSpec`) and annotations (`AnnotationSpec`). But the _body_ of methods and constructors is not modeled. There's no expression class, no statement class or syntax tree nodes. Instead, JavaPoet uses strings for code blocks: ```java MethodSpec main = MethodSpec.methodBuilder("main") .addCode("" + "int total = 0;\n" + "for (int i = 0; i < 10; i++) {\n" + " total += i;\n" + "}\n") .build(); ``` Which generates this: ```java void main() { int total = 0; for (int i = 0; i < 10; i++) { total += i; } } ``` The manual semicolons, line wrapping, and indentation are tedious and so JavaPoet offers APIs to make it easier. There's `addStatement()` which takes care of semicolons and newline, and `beginControlFlow()` + `endControlFlow()` which are used together for braces, newlines, and indentation: ```java MethodSpec main = MethodSpec.methodBuilder("main") .addStatement("int total = 0") .beginControlFlow("for (int i = 0; i < 10; i++)") .addStatement("total += i") .endControlFlow() .build(); ``` This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10, we want to make the operation and range configurable. Here's a method that generates a method: ```java private MethodSpec computeRange(String name, int from, int to, String op) { return MethodSpec.methodBuilder(name) .returns(int.class) .addStatement("int result = 1") .beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)") .addStatement("result = result " + op + " i") .endControlFlow() .addStatement("return result") .build(); } ``` And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`: ```java int multiply10to20() { int result = 1; for (int i = 10; i < 20; i++) { result = result * i; } return result; } ``` Methods generating methods! And since JavaPoet generates source instead of bytecode, you can read through it to make sure it's right. ### $L for Literals The string-concatenation in calls to `beginControlFlow()` and `addStatement` is distracting. Too many operators. To address this, JavaPoet offers a syntax inspired-by but incompatible-with [`String.format()`][formatter]. It accepts **`$L`** to emit a **literal** value in the output. This works just like `Formatter`'s `%s`: ```java private MethodSpec computeRange(String name, int from, int to, String op) { return MethodSpec.methodBuilder(name) .returns(int.class) .addStatement("int result = 0") .beginControlFlow("for (int i = $L; i < $L; i++)", from, to) .addStatement("result = result $L i", op) .endControlFlow() .addStatement("return result") .build(); } ``` Literals are emitted directly to the output code with no escaping. Arguments for literals may be strings, primitives, and a few JavaPoet types described below. ### $S for Strings When emitting code that includes string literals, we can use **`$S`** to emit a **string**, complete with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which returns its own name: ```java public static void main(String[] args) throws Exception { TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(whatsMyName("slimShady")) .addMethod(whatsMyName("eminem")) .addMethod(whatsMyName("marshallMathers")) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(System.out); } private static MethodSpec whatsMyName(String name) { return MethodSpec.methodBuilder(name) .returns(String.class) .addStatement("return $S", name) .build(); } ``` In this case, using `$S` gives us quotation marks: ```java public final class HelloWorld { String slimShady() { return "slimShady"; } String eminem() { return "eminem"; } String marshallMathers() { return "marshallMathers"; } } ``` ### $T for Types We Java programmers love our types: they make our code easier to understand. And JavaPoet is on board. It has rich built-in support for types, including automatic generation of `import` statements. Just use **`$T`** to reference **types**: ```java MethodSpec today = MethodSpec.methodBuilder("today") .returns(Date.class) .addStatement("return new $T()", Date.class) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(today) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(System.out); ``` That generates the following `.java` file, complete with the necessary `import`: ```java package com.example.helloworld; import java.util.Date; public final class HelloWorld { Date today() { return new Date(); } } ``` We passed `Date.class` to reference a class that just-so-happens to be available when we're generating code. This doesn't need to be the case. Here's a similar example, but this one references a class that doesn't exist (yet): ```java ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); MethodSpec today = MethodSpec.methodBuilder("tomorrow") .returns(hoverboard) .addStatement("return new $T()", hoverboard) .build(); ``` And that not-yet-existent class is imported as well: ```java package com.example.helloworld; import com.mattel.Hoverboard; public final class HelloWorld { Hoverboard tomorrow() { return new Hoverboard(); } } ``` The `ClassName` type is very important, and you'll need it frequently when you're using JavaPoet. It can identify any _declared_ class. Declared types are just the beginning of Java's rich type system: we also have arrays, parameterized types, wildcard types, and type variables. JavaPoet has classes for building each of these: ```java ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); ClassName list = ClassName.get("java.util", "List"); ClassName arrayList = ClassName.get("java.util", "ArrayList"); TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard); MethodSpec beyond = MethodSpec.methodBuilder("beyond") .returns(listOfHoverboards) .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList) .addStatement("result.add(new $T())", hoverboard) .addStatement("result.add(new $T())", hoverboard) .addStatement("result.add(new $T())", hoverboard) .addStatement("return result") .build(); ``` JavaPoet will decompose each type and import its components where possible. ```java package com.example.helloworld; import com.mattel.Hoverboard; import java.util.ArrayList; import java.util.List; public final class HelloWorld { List beyond() { List result = new ArrayList<>(); result.add(new Hoverboard()); result.add(new Hoverboard()); result.add(new Hoverboard()); return result; } } ``` #### Import static JavaPoet supports `import static`. It does it via explicitly collecting type member names. Let's enhance the previous example with some static sugar: ```java ... ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards"); MethodSpec beyond = MethodSpec.methodBuilder("beyond") .returns(listOfHoverboards) .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList) .addStatement("result.add($T.createNimbus(2000))", hoverboard) .addStatement("result.add($T.createNimbus(\"2001\"))", hoverboard) .addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", hoverboard, namedBoards) .addStatement("$T.sort(result)", Collections.class) .addStatement("return result.isEmpty() ? $T.emptyList() : result", Collections.class) .build(); TypeSpec hello = TypeSpec.classBuilder("HelloWorld") .addMethod(beyond) .build(); JavaFile.builder("com.example.helloworld", hello) .addStaticImport(hoverboard, "createNimbus") .addStaticImport(namedBoards, "*") .addStaticImport(Collections.class, "*") .build(); ``` JavaPoet will first add your `import static` block to the file as configured, match and mangle all calls accordingly and also import all other types as needed. ```java package com.example.helloworld; import static com.mattel.Hoverboard.Boards.*; import static com.mattel.Hoverboard.createNimbus; import static java.util.Collections.*; import com.mattel.Hoverboard; import java.util.ArrayList; import java.util.List; class HelloWorld { List beyond() { List result = new ArrayList<>(); result.add(createNimbus(2000)); result.add(createNimbus("2001")); result.add(createNimbus(THUNDERBOLT)); sort(result); return result.isEmpty() ? emptyList() : result; } } ``` ### $N for Names Generated code is often self-referential. Use **`$N`** to refer to another generated declaration by its name. Here's a method that calls another: ```java public String byteToHex(int b) { char[] result = new char[2]; result[0] = hexDigit((b >>> 4) & 0xf); result[1] = hexDigit(b & 0xf); return new String(result); } public char hexDigit(int i) { return (char) (i < 10 ? i + '0' : i - 10 + 'a'); } ``` When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()` method using `$N`: ```java MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit") .addParameter(int.class, "i") .returns(char.class) .addStatement("return (char) (i < 10 ? i + '0' : i - 10 + 'a')") .build(); MethodSpec byteToHex = MethodSpec.methodBuilder("byteToHex") .addParameter(int.class, "b") .returns(String.class) .addStatement("char[] result = new char[2]") .addStatement("result[0] = $N((b >>> 4) & 0xf)", hexDigit) .addStatement("result[1] = $N(b & 0xf)", hexDigit) .addStatement("return new String(result)") .build(); ``` ### Code block format strings Code blocks may specify the values for their placeholders in a few ways. Only one style may be used for each operation on a code block. #### Relative Arguments Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each example, we generate code to say "I ate 3 tacos" ```java CodeBlock.builder().add("I ate $L $L", 3, "tacos") ``` #### Positional Arguments Place an integer index (1-based) before the placeholder in the format string to specify which argument to use. ```java CodeBlock.builder().add("I ate $2L $1L", "tacos", 3) ``` #### Named Arguments Use the syntax `$argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()` with a map containing all argument keys in the format string. Argument names use characters in `a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character. ```java Map map = new LinkedHashMap<>(); map.put("food", "tacos"); map.put("count", 3); CodeBlock.builder().addNamed("I ate $count:L $food:L", map) ``` ### Methods All of the above methods have a code body. Use `Modifiers.ABSTRACT` to get a method without any body. This is only legal if the enclosing class is either abstract or an interface. ```java MethodSpec flux = MethodSpec.methodBuilder("flux") .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addMethod(flux) .build(); ``` Which generates this: ```java public abstract class HelloWorld { protected abstract void flux(); } ``` The other modifiers work where permitted. Note that when specifying modifiers, JavaPoet uses [`javax.lang.model.element.Modifier`][modifier], a class that is not available on Android. This limitation applies to code-generating-code only; the output code runs everywhere: JVMs, Android, and GWT. Methods also have parameters, exceptions, varargs, Javadoc, annotations, type variables, and a return type. All of these are configured with `MethodSpec.Builder`. ### Constructors `MethodSpec` is a slight misnomer; it can also be used for constructors: ```java MethodSpec flux = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(String.class, "greeting") .addStatement("this.$N = $N", "greeting", "greeting") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL) .addMethod(flux) .build(); ``` Which generates this: ```java public class HelloWorld { private final String greeting; public HelloWorld(String greeting) { this.greeting = greeting; } } ``` For the most part, constructors work just like methods. When emitting code, JavaPoet will place constructors before methods in the output file. ### Parameters Declare parameters on methods and constructors with either `ParameterSpec.builder()` or `MethodSpec`'s convenient `addParameter()` API: ```java ParameterSpec android = ParameterSpec.builder(String.class, "android") .addModifiers(Modifier.FINAL) .build(); MethodSpec welcomeOverlords = MethodSpec.methodBuilder("welcomeOverlords") .addParameter(android) .addParameter(String.class, "robot", Modifier.FINAL) .build(); ``` Though the code above to generate `android` and `robot` parameters is different, the output is the same: ```java void welcomeOverlords(final String android, final String robot) { } ``` The extended `Builder` form is necessary when the parameter has annotations (such as `@Nullable`). ### Fields Like parameters, fields can be created either with builders or by using convenient helper methods: ```java FieldSpec android = FieldSpec.builder(String.class, "android") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(android) .addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL) .build(); ``` Which generates: ```java public class HelloWorld { private final String android; private final String robot; } ``` The extended `Builder` form is necessary when a field has Javadoc, annotations, or a field initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code blocks above: ```java FieldSpec android = FieldSpec.builder(String.class, "android") .addModifiers(Modifier.PRIVATE, Modifier.FINAL) .initializer("$S + $L", "Lollipop v.", 5.0d) .build(); ``` Which generates: ```java private final String android = "Lollipop v." + 5.0; ``` ### Interfaces JavaPoet has no trouble with interfaces. Note that interface methods must always be `PUBLIC ABSTRACT` and interface fields must always be `PUBLIC STATIC FINAL`. These modifiers are necessary when defining the interface: ```java TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC) .addField(FieldSpec.builder(String.class, "ONLY_THING_THAT_IS_CONSTANT") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", "change") .build()) .addMethod(MethodSpec.methodBuilder("beep") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .build()) .build(); ``` But these modifiers are omitted when the code is generated. These are the defaults so we don't need to include them for `javac`'s benefit! ```java public interface HelloWorld { String ONLY_THING_THAT_IS_CONSTANT = "change"; void beep(); } ``` ### Enums Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value: ```java TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK") .addEnumConstant("SCISSORS") .addEnumConstant("PAPER") .build(); ``` To generate this: ```java public enum Roshambo { ROCK, SCISSORS, PAPER } ``` Fancy enums are supported, where the enum values override methods or call a superclass constructor. Here's a comprehensive example: ```java TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("$S", "fist") .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addStatement("return $S", "avalanche!") .returns(String.class) .build()) .build()) .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace") .build()) .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat") .build()) .addField(String.class, "handsign", Modifier.PRIVATE, Modifier.FINAL) .addMethod(MethodSpec.constructorBuilder() .addParameter(String.class, "handsign") .addStatement("this.$N = $N", "handsign", "handsign") .build()) .build(); ``` Which generates this: ```java public enum Roshambo { ROCK("fist") { @Override public String toString() { return "avalanche!"; } }, SCISSORS("peace"), PAPER("flat"); private final String handsign; Roshambo(String handsign) { this.handsign = handsign; } } ``` ### Anonymous Inner Classes In the enum code, we used `Types.anonymousInnerClass()`. Anonymous inner classes can also be used in code blocks. They are values that can be referenced with `$L`: ```java TypeSpec comparator = TypeSpec.anonymousClassBuilder("") .addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class)) .addMethod(MethodSpec.methodBuilder("compare") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addParameter(String.class, "a") .addParameter(String.class, "b") .returns(int.class) .addStatement("return $N.length() - $N.length()", "a", "b") .build()) .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addMethod(MethodSpec.methodBuilder("sortByLength") .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings") .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator) .build()) .build(); ``` This generates a method that contains a class that contains a method: ```java void sortByLength(List strings) { Collections.sort(strings, new Comparator() { @Override public int compare(String a, String b) { return a.length() - b.length(); } }); } ``` One particularly tricky part of defining anonymous inner classes is the arguments to the superclass constructor. In the above code we're passing the empty string for no arguments: `TypeSpec.anonymousClassBuilder("")`. To pass different parameters use JavaPoet's code block syntax with commas to separate arguments. ### Annotations Simple annotations are easy: ```java MethodSpec toString = MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .returns(String.class) .addModifiers(Modifier.PUBLIC) .addStatement("return $S", "Hoverboard") .build(); ``` Which generates this method with an `@Override` annotation: ```java @Override public String toString() { return "Hoverboard"; } ``` Use `AnnotationSpec.builder()` to set properties on annotations: ```java MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(Headers.class) .addMember("accept", "$S", "application/json; charset=utf-8") .addMember("userAgent", "$S", "Square Cash") .build()) .addParameter(LogRecord.class, "logRecord") .returns(LogReceipt.class) .build(); ``` Which generates this annotation with `accept` and `userAgent` properties: ```java @Headers( accept = "application/json; charset=utf-8", userAgent = "Square Cash" ) LogReceipt recordEvent(LogRecord logRecord); ``` When you get fancy, annotation values can be annotations themselves. Use `$L` for embedded annotations: ```java MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(HeaderList.class) .addMember("value", "$L", AnnotationSpec.builder(Header.class) .addMember("name", "$S", "Accept") .addMember("value", "$S", "application/json; charset=utf-8") .build()) .addMember("value", "$L", AnnotationSpec.builder(Header.class) .addMember("name", "$S", "User-Agent") .addMember("value", "$S", "Square Cash") .build()) .build()) .addParameter(LogRecord.class, "logRecord") .returns(LogReceipt.class) .build(); ``` Which generates this: ```java @HeaderList({ @Header(name = "Accept", value = "application/json; charset=utf-8"), @Header(name = "User-Agent", value = "Square Cash") }) LogReceipt recordEvent(LogRecord logRecord); ``` Note that you can call `addMember()` multiple times with the same property name to populate a list of values for that property. ### Javadoc Fields, methods and types can be documented with Javadoc: ```java MethodSpec dismiss = MethodSpec.methodBuilder("dismiss") .addJavadoc("Hides {@code message} from the caller's history. Other\n" + "participants in the conversation will continue to see the\n" + "message in their own history unless they also delete it.\n") .addJavadoc("\n") .addJavadoc("

Use {@link #delete($T)} to delete the entire\n" + "conversation for all participants.\n", Conversation.class) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addParameter(Message.class, "message") .build(); ``` Which generates this: ```java /** * Hides {@code message} from the caller's history. Other * participants in the conversation will continue to see the * message in their own history unless they also delete it. * *

Use {@link #delete(Conversation)} to delete the entire * conversation for all participants. */ void dismiss(Message message); ``` Use `$T` when referencing types in Javadoc to get automatic imports. Download -------- Download [the latest .jar][dl] or depend via Maven: ```xml com.squareup javapoet 1.11.1 ``` or Gradle: ```groovy compile 'com.squareup:javapoet:1.11.1' ``` Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap]. License ------- Copyright 2015 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. JavaWriter ========== JavaPoet is the successor to [JavaWriter][javawriter]. New projects should prefer JavaPoet because it has a stronger code model: it understands types and can manage imports automatically. JavaPoet is also better suited to composition: rather than streaming the contents of a `.java` file top-to-bottom in a single pass, a file can be assembled as a tree of declarations. JavaWriter continues to be available in [GitHub][javawriter] and [Maven Central][javawriter_maven]. [dl]: https://search.maven.org/remote_content?g=com.squareup&a=javapoet&v=LATEST [snap]: https://oss.sonatype.org/content/repositories/snapshots/com/squareup/javapoet/ [javadoc]: https://square.github.io/javapoet/1.x/javapoet/ [javawriter]: https://github.com/square/javapoet/tree/javawriter_2 [javawriter_maven]: https://search.maven.org/#artifactdetails%7Ccom.squareup%7Cjavawriter%7C2.5.1%7Cjar [formatter]: https://developer.android.com/reference/java/util/Formatter.html [modifier]: https://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Modifier.html javapoet-1.11.1/checkstyle.xml000066400000000000000000000125621335545476000163060ustar00rootroot00000000000000 javapoet-1.11.1/pom.xml000066400000000000000000000113601335545476000147360ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.squareup javapoet 1.11.1 JavaPoet Use beautiful Java code to generate beautiful Java code. http://github.com/square/javapoet/ UTF-8 1.8 4.12 0.39 0.15 http://github.com/square/javapoet/ scm:git:git://github.com/square/javapoet.git scm:git:ssh://git@github.com/square/javapoet.git HEAD GitHub Issues http://github.com/square/javapoet/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt Square, Inc. http://squareup.com com.google.truth truth ${truth.version} test com.google.testing.compile compile-testing ${compile-testing.version} test junit junit ${junit.version} test com.google.jimfs jimfs 1.1 test org.mockito mockito-core 2.13.0 test org.eclipse.jdt.core.compiler ecj 4.6.1 test org.apache.maven.plugins maven-compiler-plugin 3.7.0 javac-with-errorprone true ${java.version} ${java.version} org.codehaus.plexus plexus-compiler-javac-errorprone 2.8.2 com.google.errorprone error_prone_core 2.3.1 org.apache.maven.plugins maven-checkstyle-plugin 2.17 com.puppycrawl.tools checkstyle 8.7 true checkstyle.xml true verify checkstyle org.apache.maven.plugins maven-jar-plugin 3.0.2 com.squareup.javapoet javapoet-1.11.1/src/000077500000000000000000000000001335545476000142075ustar00rootroot00000000000000javapoet-1.11.1/src/main/000077500000000000000000000000001335545476000151335ustar00rootroot00000000000000javapoet-1.11.1/src/main/java/000077500000000000000000000000001335545476000160545ustar00rootroot00000000000000javapoet-1.11.1/src/main/java/com/000077500000000000000000000000001335545476000166325ustar00rootroot00000000000000javapoet-1.11.1/src/main/java/com/squareup/000077500000000000000000000000001335545476000204775ustar00rootroot00000000000000javapoet-1.11.1/src/main/java/com/squareup/javapoet/000077500000000000000000000000001335545476000223105ustar00rootroot00000000000000javapoet-1.11.1/src/main/java/com/squareup/javapoet/AnnotationSpec.java000066400000000000000000000226751335545476000261140ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import static com.squareup.javapoet.Util.characterLiteralWithoutSingleQuotes; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; /** A generated annotation on a declaration. */ public final class AnnotationSpec { public final TypeName type; public final Map> members; private AnnotationSpec(Builder builder) { this.type = builder.type; this.members = Util.immutableMultimap(builder.members); } void emit(CodeWriter codeWriter, boolean inline) throws IOException { String whitespace = inline ? "" : "\n"; String memberSeparator = inline ? ", " : ",\n"; if (members.isEmpty()) { // @Singleton codeWriter.emit("@$T", type); } else if (members.size() == 1 && members.containsKey("value")) { // @Named("foo") codeWriter.emit("@$T(", type); emitAnnotationValues(codeWriter, whitespace, memberSeparator, members.get("value")); codeWriter.emit(")"); } else { // Inline: // @Column(name = "updated_at", nullable = false) // // Not inline: // @Column( // name = "updated_at", // nullable = false // ) codeWriter.emit("@$T(" + whitespace, type); codeWriter.indent(2); for (Iterator>> i = members.entrySet().iterator(); i.hasNext(); ) { Map.Entry> entry = i.next(); codeWriter.emit("$L = ", entry.getKey()); emitAnnotationValues(codeWriter, whitespace, memberSeparator, entry.getValue()); if (i.hasNext()) codeWriter.emit(memberSeparator); } codeWriter.unindent(2); codeWriter.emit(whitespace + ")"); } } private void emitAnnotationValues(CodeWriter codeWriter, String whitespace, String memberSeparator, List values) throws IOException { if (values.size() == 1) { codeWriter.indent(2); codeWriter.emit(values.get(0)); codeWriter.unindent(2); return; } codeWriter.emit("{" + whitespace); codeWriter.indent(2); boolean first = true; for (CodeBlock codeBlock : values) { if (!first) codeWriter.emit(memberSeparator); codeWriter.emit(codeBlock); first = false; } codeWriter.unindent(2); codeWriter.emit(whitespace + "}"); } public static AnnotationSpec get(Annotation annotation) { return get(annotation, false); } public static AnnotationSpec get(Annotation annotation, boolean includeDefaultValues) { Builder builder = builder(annotation.annotationType()); try { Method[] methods = annotation.annotationType().getDeclaredMethods(); Arrays.sort(methods, Comparator.comparing(Method::getName)); for (Method method : methods) { Object value = method.invoke(annotation); if (!includeDefaultValues) { if (Objects.deepEquals(value, method.getDefaultValue())) { continue; } } if (value.getClass().isArray()) { for (int i = 0; i < Array.getLength(value); i++) { builder.addMemberForValue(method.getName(), Array.get(value, i)); } continue; } if (value instanceof Annotation) { builder.addMember(method.getName(), "$L", get((Annotation) value)); continue; } builder.addMemberForValue(method.getName(), value); } } catch (Exception e) { throw new RuntimeException("Reflecting " + annotation + " failed!", e); } return builder.build(); } public static AnnotationSpec get(AnnotationMirror annotation) { TypeElement element = (TypeElement) annotation.getAnnotationType().asElement(); AnnotationSpec.Builder builder = AnnotationSpec.builder(ClassName.get(element)); Visitor visitor = new Visitor(builder); for (ExecutableElement executableElement : annotation.getElementValues().keySet()) { String name = executableElement.getSimpleName().toString(); AnnotationValue value = annotation.getElementValues().get(executableElement); value.accept(visitor, name); } return builder.build(); } public static Builder builder(ClassName type) { checkNotNull(type, "type == null"); return new Builder(type); } public static Builder builder(Class type) { return builder(ClassName.get(type)); } public Builder toBuilder() { Builder builder = new Builder(type); for (Map.Entry> entry : members.entrySet()) { builder.members.put(entry.getKey(), new ArrayList<>(entry.getValue())); } return builder; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { StringBuilder out = new StringBuilder(); try { CodeWriter codeWriter = new CodeWriter(out); codeWriter.emit("$L", this); return out.toString(); } catch (IOException e) { throw new AssertionError(); } } public static final class Builder { private final TypeName type; private final Map> members = new LinkedHashMap<>(); private Builder(TypeName type) { this.type = type; } public Builder addMember(String name, String format, Object... args) { return addMember(name, CodeBlock.of(format, args)); } public Builder addMember(String name, CodeBlock codeBlock) { checkNotNull(name, "name == null"); checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); List values = members.computeIfAbsent(name, k -> new ArrayList<>()); values.add(codeBlock); return this; } /** * Delegates to {@link #addMember(String, String, Object...)}, with parameter {@code format} * depending on the given {@code value} object. Falls back to {@code "$L"} literal format if * the class of the given {@code value} object is not supported. */ Builder addMemberForValue(String memberName, Object value) { checkNotNull(memberName, "memberName == null"); checkNotNull(value, "value == null, constant non-null value expected for %s", memberName); checkArgument(SourceVersion.isName(memberName), "not a valid name: %s", memberName); if (value instanceof Class) { return addMember(memberName, "$T.class", value); } if (value instanceof Enum) { return addMember(memberName, "$T.$L", value.getClass(), ((Enum) value).name()); } if (value instanceof String) { return addMember(memberName, "$S", value); } if (value instanceof Float) { return addMember(memberName, "$Lf", value); } if (value instanceof Character) { return addMember(memberName, "'$L'", characterLiteralWithoutSingleQuotes((char) value)); } return addMember(memberName, "$L", value); } public AnnotationSpec build() { return new AnnotationSpec(this); } } /** * Annotation value visitor adding members to the given builder instance. */ private static class Visitor extends SimpleAnnotationValueVisitor8 { final Builder builder; Visitor(Builder builder) { super(builder); this.builder = builder; } @Override protected Builder defaultAction(Object o, String name) { return builder.addMemberForValue(name, o); } @Override public Builder visitAnnotation(AnnotationMirror a, String name) { return builder.addMember(name, "$L", get(a)); } @Override public Builder visitEnumConstant(VariableElement c, String name) { return builder.addMember(name, "$T.$L", c.asType(), c.getSimpleName()); } @Override public Builder visitType(TypeMirror t, String name) { return builder.addMember(name, "$T.class", t); } @Override public Builder visitArray(List values, String name) { for (AnnotationValue value : values) { value.accept(this, name); } return builder; } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/ArrayTypeName.java000066400000000000000000000067671335545476000257140ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.ArrayType; import static com.squareup.javapoet.Util.checkNotNull; public final class ArrayTypeName extends TypeName { public final TypeName componentType; private ArrayTypeName(TypeName componentType) { this(componentType, new ArrayList<>()); } private ArrayTypeName(TypeName componentType, List annotations) { super(annotations); this.componentType = checkNotNull(componentType, "rawType == null"); } @Override public ArrayTypeName annotated(List annotations) { return new ArrayTypeName(componentType, concatAnnotations(annotations)); } @Override public TypeName withoutAnnotations() { return new ArrayTypeName(componentType); } @Override CodeWriter emit(CodeWriter out) throws IOException { return emit(out, false); } CodeWriter emit(CodeWriter out, boolean varargs) throws IOException { emitLeafType(out); return emitBrackets(out, varargs); } private CodeWriter emitLeafType(CodeWriter out) throws IOException { if (TypeName.asArray(componentType) != null) { return TypeName.asArray(componentType).emitLeafType(out); } return componentType.emit(out); } private CodeWriter emitBrackets(CodeWriter out, boolean varargs) throws IOException { if (isAnnotated()) { out.emit(" "); emitAnnotations(out); } if (TypeName.asArray(componentType) == null) { // Last bracket. return out.emit(varargs ? "..." : "[]"); } out.emit("[]"); return TypeName.asArray(componentType) .emitBrackets(out, varargs); } /** Returns an array type whose elements are all instances of {@code componentType}. */ public static ArrayTypeName of(TypeName componentType) { return new ArrayTypeName(componentType); } /** Returns an array type whose elements are all instances of {@code componentType}. */ public static ArrayTypeName of(Type componentType) { return of(TypeName.get(componentType)); } /** Returns an array type equivalent to {@code mirror}. */ public static ArrayTypeName get(ArrayType mirror) { return get(mirror, new LinkedHashMap<>()); } static ArrayTypeName get( ArrayType mirror, Map typeVariables) { return new ArrayTypeName(get(mirror.getComponentType(), typeVariables)); } /** Returns an array type equivalent to {@code type}. */ public static ArrayTypeName get(GenericArrayType type) { return get(type, new LinkedHashMap<>()); } static ArrayTypeName get(GenericArrayType type, Map map) { return ArrayTypeName.of(get(type.getGenericComponentType(), map)); } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/ClassName.java000066400000000000000000000245021335545476000250240ustar00rootroot00000000000000/* * Copyright (C) 2014 Google, 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. */ package com.squareup.javapoet; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.SimpleElementVisitor8; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; /** A fully-qualified class name for top-level and member classes. */ public final class ClassName extends TypeName implements Comparable { public static final ClassName OBJECT = ClassName.get(Object.class); /** The package name of this class, or "" if this is in the default package. */ final String packageName; /** The enclosing class, or null if this is not enclosed in another class. */ final ClassName enclosingClassName; /** This class name, like "Entry" for java.util.Map.Entry. */ final String simpleName; /** The full class name like "java.util.Map.Entry". */ final String canonicalName; private ClassName(String packageName, ClassName enclosingClassName, String simpleName) { this(packageName, enclosingClassName, simpleName, Collections.emptyList()); } private ClassName(String packageName, ClassName enclosingClassName, String simpleName, List annotations) { super(annotations); this.packageName = packageName; this.enclosingClassName = enclosingClassName; this.simpleName = simpleName; this.canonicalName = enclosingClassName != null ? (enclosingClassName.canonicalName + '.' + simpleName) : (packageName.isEmpty() ? simpleName : packageName + '.' + simpleName); } @Override public ClassName annotated(List annotations) { return new ClassName(packageName, enclosingClassName, simpleName, concatAnnotations(annotations)); } @Override public ClassName withoutAnnotations() { if (!isAnnotated()) return this; ClassName resultEnclosingClassName = enclosingClassName != null ? enclosingClassName.withoutAnnotations() : null; return new ClassName(packageName, resultEnclosingClassName, simpleName); } @Override public boolean isAnnotated() { return super.isAnnotated() || (enclosingClassName != null && enclosingClassName.isAnnotated()); } /** * Returns the package name, like {@code "java.util"} for {@code Map.Entry}. Returns the empty * string for the default package. */ public String packageName() { return packageName; } /** * Returns the enclosing class, like {@link Map} for {@code Map.Entry}. Returns null if this class * is not nested in another class. */ public ClassName enclosingClassName() { return enclosingClassName; } /** * Returns the top class in this nesting group. Equivalent to chained calls to {@link * #enclosingClassName()} until the result's enclosing class is null. */ public ClassName topLevelClassName() { return enclosingClassName != null ? enclosingClassName.topLevelClassName() : this; } /** Return the binary name of a class. */ public String reflectionName() { return enclosingClassName != null ? (enclosingClassName.reflectionName() + '$' + simpleName) : (packageName.isEmpty() ? simpleName : packageName + '.' + simpleName); } public List simpleNames() { List simpleNames = new ArrayList<>(); if (enclosingClassName != null) { simpleNames.addAll(enclosingClassName().simpleNames()); } simpleNames.add(simpleName); return simpleNames; } /** * Returns a class that shares the same enclosing package or class. If this class is enclosed by * another class, this is equivalent to {@code enclosingClassName().nestedClass(name)}. Otherwise * it is equivalent to {@code get(packageName(), name)}. */ public ClassName peerClass(String name) { return new ClassName(packageName, enclosingClassName, name); } /** * Returns a new {@link ClassName} instance for the specified {@code name} as nested inside this * class. */ public ClassName nestedClass(String name) { return new ClassName(packageName, this, name); } /** Returns the simple name of this class, like {@code "Entry"} for {@link Map.Entry}. */ public String simpleName() { return simpleName; } public static ClassName get(Class clazz) { checkNotNull(clazz, "clazz == null"); checkArgument(!clazz.isPrimitive(), "primitive types cannot be represented as a ClassName"); checkArgument(!void.class.equals(clazz), "'void' type cannot be represented as a ClassName"); checkArgument(!clazz.isArray(), "array types cannot be represented as a ClassName"); String anonymousSuffix = ""; while (clazz.isAnonymousClass()) { int lastDollar = clazz.getName().lastIndexOf('$'); anonymousSuffix = clazz.getName().substring(lastDollar) + anonymousSuffix; clazz = clazz.getEnclosingClass(); } String name = clazz.getSimpleName() + anonymousSuffix; if (clazz.getEnclosingClass() == null) { // Avoid unreliable Class.getPackage(). https://github.com/square/javapoet/issues/295 int lastDot = clazz.getName().lastIndexOf('.'); String packageName = (lastDot != -1) ? clazz.getName().substring(0, lastDot) : null; return new ClassName(packageName, null, name); } return ClassName.get(clazz.getEnclosingClass()).nestedClass(name); } /** * Returns a new {@link ClassName} instance for the given fully-qualified class name string. This * method assumes that the input is ASCII and follows typical Java style (lowercase package * names, UpperCamelCase class names) and may produce incorrect results or throw * {@link IllegalArgumentException} otherwise. For that reason, {@link #get(Class)} and * {@link #get(Class)} should be preferred as they can correctly create {@link ClassName} * instances without such restrictions. */ public static ClassName bestGuess(String classNameString) { // Add the package name, like "java.util.concurrent", or "" for no package. int p = 0; while (p < classNameString.length() && Character.isLowerCase(classNameString.codePointAt(p))) { p = classNameString.indexOf('.', p) + 1; checkArgument(p != 0, "couldn't make a guess for %s", classNameString); } String packageName = p == 0 ? "" : classNameString.substring(0, p - 1); // Add class names like "Map" and "Entry". ClassName className = null; for (String simpleName : classNameString.substring(p).split("\\.", -1)) { checkArgument(!simpleName.isEmpty() && Character.isUpperCase(simpleName.codePointAt(0)), "couldn't make a guess for %s", classNameString); className = new ClassName(packageName, className, simpleName); } return className; } /** * Returns a class name created from the given parts. For example, calling this with package name * {@code "java.util"} and simple names {@code "Map"}, {@code "Entry"} yields {@link Map.Entry}. */ public static ClassName get(String packageName, String simpleName, String... simpleNames) { ClassName className = new ClassName(packageName, null, simpleName); for (String name : simpleNames) { className = className.nestedClass(name); } return className; } /** Returns the class name for {@code element}. */ public static ClassName get(TypeElement element) { checkNotNull(element, "element == null"); String simpleName = element.getSimpleName().toString(); return element.getEnclosingElement().accept(new SimpleElementVisitor8() { @Override public ClassName visitPackage(PackageElement packageElement, Void p) { return new ClassName(packageElement.getQualifiedName().toString(), null, simpleName); } @Override public ClassName visitType(TypeElement enclosingClass, Void p) { return ClassName.get(enclosingClass).nestedClass(simpleName); } @Override public ClassName visitUnknown(Element unknown, Void p) { return get("", simpleName); } @Override public ClassName defaultAction(Element enclosingElement, Void p) { throw new IllegalArgumentException("Unexpected type nesting: " + element); } }, null); } @Override public int compareTo(ClassName o) { return canonicalName.compareTo(o.canonicalName); } @Override CodeWriter emit(CodeWriter out) throws IOException { boolean charsEmitted = false; for (ClassName className : enclosingClasses()) { String simpleName; if (charsEmitted) { // We've already emitted an enclosing class. Emit as we go. out.emit("."); simpleName = className.simpleName; } else if (className.isAnnotated() || className == this) { // We encountered the first enclosing class that must be emitted. String qualifiedName = out.lookupName(className); int dot = qualifiedName.lastIndexOf('.'); if (dot != -1) { out.emitAndIndent(qualifiedName.substring(0, dot + 1)); simpleName = qualifiedName.substring(dot + 1); charsEmitted = true; } else { simpleName = qualifiedName; } } else { // Don't emit this enclosing type. Keep going so we can be more precise. continue; } if (className.isAnnotated()) { if (charsEmitted) out.emit(" "); className.emitAnnotations(out); } out.emit(simpleName); charsEmitted = true; } return out; } /** Returns all enclosing classes in this, outermost first. */ private List enclosingClasses() { List result = new ArrayList<>(); for (ClassName c = this; c != null; c = c.enclosingClassName) { result.add(c); } Collections.reverse(result); return result; } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/CodeBlock.java000066400000000000000000000375341335545476000250140ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collector; import java.util.stream.StreamSupport; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; import static com.squareup.javapoet.Util.checkArgument; /** * A fragment of a .java file, potentially containing declarations, statements, and documentation. * Code blocks are not necessarily well-formed Java code, and are not validated. This class assumes * javac will check correctness later! * *

Code blocks support placeholders like {@link java.text.Format}. Where {@link String#format} * uses percent {@code %} to reference target values, this class uses dollar sign {@code $} and has * its own set of permitted placeholders: * *

    *
  • {@code $L} emits a literal value with no escaping. Arguments for literals may be * strings, primitives, {@linkplain TypeSpec type declarations}, {@linkplain AnnotationSpec * annotations} and even other code blocks. *
  • {@code $N} emits a name, using name collision avoidance where necessary. Arguments * for names may be strings (actually any {@linkplain CharSequence character sequence}), * {@linkplain ParameterSpec parameters}, {@linkplain FieldSpec fields}, {@linkplain * MethodSpec methods}, and {@linkplain TypeSpec types}. *
  • {@code $S} escapes the value as a string, wraps it with double quotes, and emits * that. For example, {@code 6" sandwich} is emitted {@code "6\" sandwich"}. *
  • {@code $T} emits a type reference. Types will be imported if possible. Arguments * for types may be {@linkplain Class classes}, {@linkplain javax.lang.model.type.TypeMirror ,* type mirrors}, and {@linkplain javax.lang.model.element.Element elements}. *
  • {@code $$} emits a dollar sign. *
  • {@code $W} emits a space or a newline, depending on its position on the line. This prefers * to wrap lines before 100 columns. *
  • {@code $Z} acts as a zero-width space. This prefers to wrap lines before 100 columns. *
  • {@code $>} increases the indentation level. *
  • {@code $<} decreases the indentation level. *
  • {@code $[} begins a statement. For multiline statements, every line after the first line * is double-indented. *
  • {@code $]} ends a statement. *
*/ public final class CodeBlock { private static final Pattern NAMED_ARGUMENT = Pattern.compile("\\$(?[\\w_]+):(?[\\w]).*"); private static final Pattern LOWERCASE = Pattern.compile("[a-z]+[\\w_]*"); /** A heterogeneous list containing string literals and value placeholders. */ final List formatParts; final List args; private CodeBlock(Builder builder) { this.formatParts = Util.immutableList(builder.formatParts); this.args = Util.immutableList(builder.args); } public boolean isEmpty() { return formatParts.isEmpty(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { StringBuilder out = new StringBuilder(); try { new CodeWriter(out).emit(this); return out.toString(); } catch (IOException e) { throw new AssertionError(); } } public static CodeBlock of(String format, Object... args) { return new Builder().add(format, args).build(); } /** * Joins {@code codeBlocks} into a single {@link CodeBlock}, each separated by {@code separator}. * For example, joining {@code String s}, {@code Object o} and {@code int i} using {@code ", "} * would produce {@code String s, Object o, int i}. */ public static CodeBlock join(Iterable codeBlocks, String separator) { return StreamSupport.stream(codeBlocks.spliterator(), false).collect(joining(separator)); } /** * A {@link Collector} implementation that joins {@link CodeBlock} instances together into one * separated by {@code separator}. For example, joining {@code String s}, {@code Object o} and * {@code int i} using {@code ", "} would produce {@code String s, Object o, int i}. */ public static Collector joining(String separator) { return Collector.of( () -> new CodeBlockJoiner(separator, builder()), CodeBlockJoiner::add, CodeBlockJoiner::merge, CodeBlockJoiner::join); } /** * A {@link Collector} implementation that joins {@link CodeBlock} instances together into one * separated by {@code separator}. For example, joining {@code String s}, {@code Object o} and * {@code int i} using {@code ", "} would produce {@code String s, Object o, int i}. */ public static Collector joining( String separator, String prefix, String suffix) { Builder builder = builder().add("$N", prefix); return Collector.of( () -> new CodeBlockJoiner(separator, builder), CodeBlockJoiner::add, CodeBlockJoiner::merge, joiner -> { builder.add(CodeBlock.of("$N", suffix)); return joiner.join(); }); } public static Builder builder() { return new Builder(); } public Builder toBuilder() { Builder builder = new Builder(); builder.formatParts.addAll(formatParts); builder.args.addAll(args); return builder; } public static final class Builder { final List formatParts = new ArrayList<>(); final List args = new ArrayList<>(); private Builder() { } public boolean isEmpty() { return formatParts.isEmpty(); } /** * Adds code using named arguments. * *

Named arguments specify their name after the '$' followed by : and the corresponding type * character. Argument names consist of characters in {@code a-z, A-Z, 0-9, and _} and must * start with a lowercase character. * *

For example, to refer to the type {@link java.lang.Integer} with the argument name {@code * clazz} use a format string containing {@code $clazz:T} and include the key {@code clazz} with * value {@code java.lang.Integer.class} in the argument map. */ public Builder addNamed(String format, Map arguments) { int p = 0; for (String argument : arguments.keySet()) { checkArgument(LOWERCASE.matcher(argument).matches(), "argument '%s' must start with a lowercase character", argument); } while (p < format.length()) { int nextP = format.indexOf("$", p); if (nextP == -1) { formatParts.add(format.substring(p, format.length())); break; } if (p != nextP) { formatParts.add(format.substring(p, nextP)); p = nextP; } Matcher matcher = null; int colon = format.indexOf(':', p); if (colon != -1) { int endIndex = Math.min(colon + 2, format.length()); matcher = NAMED_ARGUMENT.matcher(format.substring(p, endIndex)); } if (matcher != null && matcher.lookingAt()) { String argumentName = matcher.group("argumentName"); checkArgument(arguments.containsKey(argumentName), "Missing named argument for $%s", argumentName); char formatChar = matcher.group("typeChar").charAt(0); addArgument(format, formatChar, arguments.get(argumentName)); formatParts.add("$" + formatChar); p += matcher.regionEnd(); } else { checkArgument(p < format.length() - 1, "dangling $ at end"); checkArgument(isNoArgPlaceholder(format.charAt(p + 1)), "unknown format $%s at %s in '%s'", format.charAt(p + 1), p + 1, format); formatParts.add(format.substring(p, p + 2)); p += 2; } } return this; } /** * Add code with positional or relative arguments. * *

Relative arguments map 1:1 with the placeholders in the format string. * *

Positional arguments use an index after the placeholder to identify which argument index * to use. For example, for a literal to reference the 3rd argument: "$3L" (1 based index) * *

Mixing relative and positional arguments in a call to add is invalid and will result in an * error. */ public Builder add(String format, Object... args) { boolean hasRelative = false; boolean hasIndexed = false; int relativeParameterCount = 0; int[] indexedParameterCount = new int[args.length]; for (int p = 0; p < format.length(); ) { if (format.charAt(p) != '$') { int nextP = format.indexOf('$', p + 1); if (nextP == -1) nextP = format.length(); formatParts.add(format.substring(p, nextP)); p = nextP; continue; } p++; // '$'. // Consume zero or more digits, leaving 'c' as the first non-digit char after the '$'. int indexStart = p; char c; do { checkArgument(p < format.length(), "dangling format characters in '%s'", format); c = format.charAt(p++); } while (c >= '0' && c <= '9'); int indexEnd = p - 1; // If 'c' doesn't take an argument, we're done. if (isNoArgPlaceholder(c)) { checkArgument( indexStart == indexEnd, "$$, $>, $<, $[, $], $W, and $Z may not have an index"); formatParts.add("$" + c); continue; } // Find either the indexed argument, or the relative argument. (0-based). int index; if (indexStart < indexEnd) { index = Integer.parseInt(format.substring(indexStart, indexEnd)) - 1; hasIndexed = true; if (args.length > 0) { indexedParameterCount[index % args.length]++; // modulo is needed, checked below anyway } } else { index = relativeParameterCount; hasRelative = true; relativeParameterCount++; } checkArgument(index >= 0 && index < args.length, "index %d for '%s' not in range (received %s arguments)", index + 1, format.substring(indexStart - 1, indexEnd + 1), args.length); checkArgument(!hasIndexed || !hasRelative, "cannot mix indexed and positional parameters"); addArgument(format, c, args[index]); formatParts.add("$" + c); } if (hasRelative) { checkArgument(relativeParameterCount >= args.length, "unused arguments: expected %s, received %s", relativeParameterCount, args.length); } if (hasIndexed) { List unused = new ArrayList<>(); for (int i = 0; i < args.length; i++) { if (indexedParameterCount[i] == 0) { unused.add("$" + (i + 1)); } } String s = unused.size() == 1 ? "" : "s"; checkArgument(unused.isEmpty(), "unused argument%s: %s", s, String.join(", ", unused)); } return this; } private boolean isNoArgPlaceholder(char c) { return c == '$' || c == '>' || c == '<' || c == '[' || c == ']' || c == 'W' || c == 'Z'; } private void addArgument(String format, char c, Object arg) { switch (c) { case 'N': this.args.add(argToName(arg)); break; case 'L': this.args.add(argToLiteral(arg)); break; case 'S': this.args.add(argToString(arg)); break; case 'T': this.args.add(argToType(arg)); break; default: throw new IllegalArgumentException( String.format("invalid format string: '%s'", format)); } } private String argToName(Object o) { if (o instanceof CharSequence) return o.toString(); if (o instanceof ParameterSpec) return ((ParameterSpec) o).name; if (o instanceof FieldSpec) return ((FieldSpec) o).name; if (o instanceof MethodSpec) return ((MethodSpec) o).name; if (o instanceof TypeSpec) return ((TypeSpec) o).name; throw new IllegalArgumentException("expected name but was " + o); } private Object argToLiteral(Object o) { return o; } private String argToString(Object o) { return o != null ? String.valueOf(o) : null; } private TypeName argToType(Object o) { if (o instanceof TypeName) return (TypeName) o; if (o instanceof TypeMirror) return TypeName.get((TypeMirror) o); if (o instanceof Element) return TypeName.get(((Element) o).asType()); if (o instanceof Type) return TypeName.get((Type) o); throw new IllegalArgumentException("expected type but was " + o); } /** * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". * Shouldn't contain braces or newline characters. */ public Builder beginControlFlow(String controlFlow, Object... args) { add(controlFlow + " {\n", args); indent(); return this; } /** * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". * Shouldn't contain braces or newline characters. */ public Builder nextControlFlow(String controlFlow, Object... args) { unindent(); add("} " + controlFlow + " {\n", args); indent(); return this; } public Builder endControlFlow() { unindent(); add("}\n"); return this; } /** * @param controlFlow the optional control flow construct and its code, such as * "while(foo == 20)". Only used for "do/while" control flows. */ public Builder endControlFlow(String controlFlow, Object... args) { unindent(); add("} " + controlFlow + ";\n", args); return this; } public Builder addStatement(String format, Object... args) { add("$["); add(format, args); add(";\n$]"); return this; } public Builder addStatement(CodeBlock codeBlock) { return addStatement("$L", codeBlock); } public Builder add(CodeBlock codeBlock) { formatParts.addAll(codeBlock.formatParts); args.addAll(codeBlock.args); return this; } public Builder indent() { this.formatParts.add("$>"); return this; } public Builder unindent() { this.formatParts.add("$<"); return this; } public CodeBlock build() { return new CodeBlock(this); } } private static final class CodeBlockJoiner { private final String delimiter; private final Builder builder; private boolean first = true; CodeBlockJoiner(String delimiter, Builder builder) { this.delimiter = delimiter; this.builder = builder; } CodeBlockJoiner add(CodeBlock codeBlock) { if (!first) { builder.add(delimiter); } first = false; builder.add(codeBlock); return this; } CodeBlockJoiner merge(CodeBlockJoiner other) { CodeBlock otherBlock = other.builder.build(); if (!otherBlock.isEmpty()) { add(otherBlock); } return this; } CodeBlock join() { return builder.build(); } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/CodeWriter.java000066400000000000000000000402011335545476000252170ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.lang.model.SourceVersion; import javax.lang.model.element.Modifier; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; import static com.squareup.javapoet.Util.checkState; import static com.squareup.javapoet.Util.stringLiteralWithDoubleQuotes; import static java.lang.String.join; /** * Converts a {@link JavaFile} to a string suitable to both human- and javac-consumption. This * honors imports, indentation, and deferred variable names. */ final class CodeWriter { /** Sentinel value that indicates that no user-provided package has been set. */ private static final String NO_PACKAGE = new String(); private final String indent; private final LineWrapper out; private int indentLevel; private boolean javadoc = false; private boolean comment = false; private String packageName = NO_PACKAGE; private final List typeSpecStack = new ArrayList<>(); private final Set staticImportClassNames; private final Set staticImports; private final Map importedTypes; private final Map importableTypes = new LinkedHashMap<>(); private final Set referencedNames = new LinkedHashSet<>(); private boolean trailingNewline; /** * When emitting a statement, this is the line of the statement currently being written. The first * line of a statement is indented normally and subsequent wrapped lines are double-indented. This * is -1 when the currently-written line isn't part of a statement. */ int statementLine = -1; CodeWriter(Appendable out) { this(out, " ", Collections.emptySet()); } CodeWriter(Appendable out, String indent, Set staticImports) { this(out, indent, Collections.emptyMap(), staticImports); } CodeWriter(Appendable out, String indent, Map importedTypes, Set staticImports) { this.out = new LineWrapper(out, indent, 100); this.indent = checkNotNull(indent, "indent == null"); this.importedTypes = checkNotNull(importedTypes, "importedTypes == null"); this.staticImports = checkNotNull(staticImports, "staticImports == null"); this.staticImportClassNames = new LinkedHashSet<>(); for (String signature : staticImports) { staticImportClassNames.add(signature.substring(0, signature.lastIndexOf('.'))); } } public Map importedTypes() { return importedTypes; } public CodeWriter indent() { return indent(1); } public CodeWriter indent(int levels) { indentLevel += levels; return this; } public CodeWriter unindent() { return unindent(1); } public CodeWriter unindent(int levels) { checkArgument(indentLevel - levels >= 0, "cannot unindent %s from %s", levels, indentLevel); indentLevel -= levels; return this; } public CodeWriter pushPackage(String packageName) { checkState(this.packageName == NO_PACKAGE, "package already set: %s", this.packageName); this.packageName = checkNotNull(packageName, "packageName == null"); return this; } public CodeWriter popPackage() { checkState(this.packageName != NO_PACKAGE, "package not set"); this.packageName = NO_PACKAGE; return this; } public CodeWriter pushType(TypeSpec type) { this.typeSpecStack.add(type); return this; } public CodeWriter popType() { this.typeSpecStack.remove(typeSpecStack.size() - 1); return this; } public void emitComment(CodeBlock codeBlock) throws IOException { trailingNewline = true; // Force the '//' prefix for the comment. comment = true; try { emit(codeBlock); emit("\n"); } finally { comment = false; } } public void emitJavadoc(CodeBlock javadocCodeBlock) throws IOException { if (javadocCodeBlock.isEmpty()) return; emit("/**\n"); javadoc = true; try { emit(javadocCodeBlock); } finally { javadoc = false; } emit(" */\n"); } public void emitAnnotations(List annotations, boolean inline) throws IOException { for (AnnotationSpec annotationSpec : annotations) { annotationSpec.emit(this, inline); emit(inline ? " " : "\n"); } } /** * Emits {@code modifiers} in the standard order. Modifiers in {@code implicitModifiers} will not * be emitted. */ public void emitModifiers(Set modifiers, Set implicitModifiers) throws IOException { if (modifiers.isEmpty()) return; for (Modifier modifier : EnumSet.copyOf(modifiers)) { if (implicitModifiers.contains(modifier)) continue; emitAndIndent(modifier.name().toLowerCase(Locale.US)); emitAndIndent(" "); } } public void emitModifiers(Set modifiers) throws IOException { emitModifiers(modifiers, Collections.emptySet()); } /** * Emit type variables with their bounds. This should only be used when declaring type variables; * everywhere else bounds are omitted. */ public void emitTypeVariables(List typeVariables) throws IOException { if (typeVariables.isEmpty()) return; emit("<"); boolean firstTypeVariable = true; for (TypeVariableName typeVariable : typeVariables) { if (!firstTypeVariable) emit(", "); emitAnnotations(typeVariable.annotations, true); emit("$L", typeVariable.name); boolean firstBound = true; for (TypeName bound : typeVariable.bounds) { emit(firstBound ? " extends $T" : " & $T", bound); firstBound = false; } firstTypeVariable = false; } emit(">"); } public CodeWriter emit(String s) throws IOException { return emitAndIndent(s); } public CodeWriter emit(String format, Object... args) throws IOException { return emit(CodeBlock.of(format, args)); } public CodeWriter emit(CodeBlock codeBlock) throws IOException { int a = 0; ClassName deferredTypeName = null; // used by "import static" logic ListIterator partIterator = codeBlock.formatParts.listIterator(); while (partIterator.hasNext()) { String part = partIterator.next(); switch (part) { case "$L": emitLiteral(codeBlock.args.get(a++)); break; case "$N": emitAndIndent((String) codeBlock.args.get(a++)); break; case "$S": String string = (String) codeBlock.args.get(a++); // Emit null as a literal null: no quotes. emitAndIndent(string != null ? stringLiteralWithDoubleQuotes(string, indent) : "null"); break; case "$T": TypeName typeName = (TypeName) codeBlock.args.get(a++); // defer "typeName.emit(this)" if next format part will be handled by the default case if (typeName instanceof ClassName && partIterator.hasNext()) { if (!codeBlock.formatParts.get(partIterator.nextIndex()).startsWith("$")) { ClassName candidate = (ClassName) typeName; if (staticImportClassNames.contains(candidate.canonicalName)) { checkState(deferredTypeName == null, "pending type for static import?!"); deferredTypeName = candidate; break; } } } typeName.emit(this); break; case "$$": emitAndIndent("$"); break; case "$>": indent(); break; case "$<": unindent(); break; case "$[": checkState(statementLine == -1, "statement enter $[ followed by statement enter $["); statementLine = 0; break; case "$]": checkState(statementLine != -1, "statement exit $] has no matching statement enter $["); if (statementLine > 0) { unindent(2); // End a multi-line statement. Decrease the indentation level. } statementLine = -1; break; case "$W": out.wrappingSpace(indentLevel + 2); break; case "$Z": out.zeroWidthSpace(indentLevel + 2); break; default: // handle deferred type if (deferredTypeName != null) { if (part.startsWith(".")) { if (emitStaticImportMember(deferredTypeName.canonicalName, part)) { // okay, static import hit and all was emitted, so clean-up and jump to next part deferredTypeName = null; break; } } deferredTypeName.emit(this); deferredTypeName = null; } emitAndIndent(part); break; } } return this; } public CodeWriter emitWrappingSpace() throws IOException { out.wrappingSpace(indentLevel + 2); return this; } private static String extractMemberName(String part) { checkArgument(Character.isJavaIdentifierStart(part.charAt(0)), "not an identifier: %s", part); for (int i = 1; i <= part.length(); i++) { if (!SourceVersion.isIdentifier(part.substring(0, i))) { return part.substring(0, i - 1); } } return part; } private boolean emitStaticImportMember(String canonical, String part) throws IOException { String partWithoutLeadingDot = part.substring(1); if (partWithoutLeadingDot.isEmpty()) return false; char first = partWithoutLeadingDot.charAt(0); if (!Character.isJavaIdentifierStart(first)) return false; String explicit = canonical + "." + extractMemberName(partWithoutLeadingDot); String wildcard = canonical + ".*"; if (staticImports.contains(explicit) || staticImports.contains(wildcard)) { emitAndIndent(partWithoutLeadingDot); return true; } return false; } private void emitLiteral(Object o) throws IOException { if (o instanceof TypeSpec) { TypeSpec typeSpec = (TypeSpec) o; typeSpec.emit(this, null, Collections.emptySet()); } else if (o instanceof AnnotationSpec) { AnnotationSpec annotationSpec = (AnnotationSpec) o; annotationSpec.emit(this, true); } else if (o instanceof CodeBlock) { CodeBlock codeBlock = (CodeBlock) o; emit(codeBlock); } else { emitAndIndent(String.valueOf(o)); } } /** * Returns the best name to identify {@code className} with in the current context. This uses the * available imports and the current scope to find the shortest name available. It does not honor * names visible due to inheritance. */ String lookupName(ClassName className) { // Find the shortest suffix of className that resolves to className. This uses both local type // names (so `Entry` in `Map` refers to `Map.Entry`). Also uses imports. boolean nameResolved = false; for (ClassName c = className; c != null; c = c.enclosingClassName()) { ClassName resolved = resolve(c.simpleName()); nameResolved = resolved != null; if (resolved != null && Objects.equals(resolved.canonicalName, c.canonicalName)) { int suffixOffset = c.simpleNames().size() - 1; return join(".", className.simpleNames().subList( suffixOffset, className.simpleNames().size())); } } // If the name resolved but wasn't a match, we're stuck with the fully qualified name. if (nameResolved) { return className.canonicalName; } // If the class is in the same package, we're done. if (Objects.equals(packageName, className.packageName())) { referencedNames.add(className.topLevelClassName().simpleName()); return join(".", className.simpleNames()); } // We'll have to use the fully-qualified name. Mark the type as importable for a future pass. if (!javadoc) { importableType(className); } return className.canonicalName; } private void importableType(ClassName className) { if (className.packageName().isEmpty()) { return; } ClassName topLevelClassName = className.topLevelClassName(); String simpleName = topLevelClassName.simpleName(); ClassName replaced = importableTypes.put(simpleName, topLevelClassName); if (replaced != null) { importableTypes.put(simpleName, replaced); // On collision, prefer the first inserted. } } /** * Returns the class referenced by {@code simpleName}, using the current nesting context and * imports. */ // TODO(jwilson): also honor superclass members when resolving names. private ClassName resolve(String simpleName) { // Match a child of the current (potentially nested) class. for (int i = typeSpecStack.size() - 1; i >= 0; i--) { TypeSpec typeSpec = typeSpecStack.get(i); for (TypeSpec visibleChild : typeSpec.typeSpecs) { if (Objects.equals(visibleChild.name, simpleName)) { return stackClassName(i, simpleName); } } } // Match the top-level class. if (typeSpecStack.size() > 0 && Objects.equals(typeSpecStack.get(0).name, simpleName)) { return ClassName.get(packageName, simpleName); } // Match an imported type. ClassName importedType = importedTypes.get(simpleName); if (importedType != null) return importedType; // No match. return null; } /** Returns the class named {@code simpleName} when nested in the class at {@code stackDepth}. */ private ClassName stackClassName(int stackDepth, String simpleName) { ClassName className = ClassName.get(packageName, typeSpecStack.get(0).name); for (int i = 1; i <= stackDepth; i++) { className = className.nestedClass(typeSpecStack.get(i).name); } return className.nestedClass(simpleName); } /** * Emits {@code s} with indentation as required. It's important that all code that writes to * {@link #out} does it through here, since we emit indentation lazily in order to avoid * unnecessary trailing whitespace. */ CodeWriter emitAndIndent(String s) throws IOException { boolean first = true; for (String line : s.split("\n", -1)) { // Emit a newline character. Make sure blank lines in Javadoc & comments look good. if (!first) { if ((javadoc || comment) && trailingNewline) { emitIndentation(); out.append(javadoc ? " *" : "//"); } out.append("\n"); trailingNewline = true; if (statementLine != -1) { if (statementLine == 0) { indent(2); // Begin multiple-line statement. Increase the indentation level. } statementLine++; } } first = false; if (line.isEmpty()) continue; // Don't indent empty lines. // Emit indentation and comment prefix if necessary. if (trailingNewline) { emitIndentation(); if (javadoc) { out.append(" * "); } else if (comment) { out.append("// "); } } out.append(line); trailingNewline = false; } return this; } private void emitIndentation() throws IOException { for (int j = 0; j < indentLevel; j++) { out.append(indent); } } /** * Returns the types that should have been imported for this code. If there were any simple name * collisions, that type's first use is imported. */ Map suggestedImports() { Map result = new LinkedHashMap<>(importableTypes); result.keySet().removeAll(referencedNames); return result; } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/FieldSpec.java000066400000000000000000000126331335545476000250160ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.lang.model.SourceVersion; import javax.lang.model.element.Modifier; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; import static com.squareup.javapoet.Util.checkState; /** A generated field declaration. */ public final class FieldSpec { public final TypeName type; public final String name; public final CodeBlock javadoc; public final List annotations; public final Set modifiers; public final CodeBlock initializer; private FieldSpec(Builder builder) { this.type = checkNotNull(builder.type, "type == null"); this.name = checkNotNull(builder.name, "name == null"); this.javadoc = builder.javadoc.build(); this.annotations = Util.immutableList(builder.annotations); this.modifiers = Util.immutableSet(builder.modifiers); this.initializer = (builder.initializer == null) ? CodeBlock.builder().build() : builder.initializer; } public boolean hasModifier(Modifier modifier) { return modifiers.contains(modifier); } void emit(CodeWriter codeWriter, Set implicitModifiers) throws IOException { codeWriter.emitJavadoc(javadoc); codeWriter.emitAnnotations(annotations, false); codeWriter.emitModifiers(modifiers, implicitModifiers); codeWriter.emit("$T $L", type, name); if (!initializer.isEmpty()) { codeWriter.emit(" = "); codeWriter.emit(initializer); } codeWriter.emit(";\n"); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { StringBuilder out = new StringBuilder(); try { CodeWriter codeWriter = new CodeWriter(out); emit(codeWriter, Collections.emptySet()); return out.toString(); } catch (IOException e) { throw new AssertionError(); } } public static Builder builder(TypeName type, String name, Modifier... modifiers) { checkNotNull(type, "type == null"); checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); return new Builder(type, name) .addModifiers(modifiers); } public static Builder builder(Type type, String name, Modifier... modifiers) { return builder(TypeName.get(type), name, modifiers); } public Builder toBuilder() { Builder builder = new Builder(type, name); builder.javadoc.add(javadoc); builder.annotations.addAll(annotations); builder.modifiers.addAll(modifiers); builder.initializer = initializer.isEmpty() ? null : initializer; return builder; } public static final class Builder { private final TypeName type; private final String name; private final CodeBlock.Builder javadoc = CodeBlock.builder(); private final List annotations = new ArrayList<>(); private final List modifiers = new ArrayList<>(); private CodeBlock initializer = null; private Builder(TypeName type, String name) { this.type = type; this.name = name; } public Builder addJavadoc(String format, Object... args) { javadoc.add(format, args); return this; } public Builder addJavadoc(CodeBlock block) { javadoc.add(block); return this; } public Builder addAnnotations(Iterable annotationSpecs) { checkArgument(annotationSpecs != null, "annotationSpecs == null"); for (AnnotationSpec annotationSpec : annotationSpecs) { this.annotations.add(annotationSpec); } return this; } public Builder addAnnotation(AnnotationSpec annotationSpec) { this.annotations.add(annotationSpec); return this; } public Builder addAnnotation(ClassName annotation) { this.annotations.add(AnnotationSpec.builder(annotation).build()); return this; } public Builder addAnnotation(Class annotation) { return addAnnotation(ClassName.get(annotation)); } public Builder addModifiers(Modifier... modifiers) { Collections.addAll(this.modifiers, modifiers); return this; } public Builder initializer(String format, Object... args) { return initializer(CodeBlock.of(format, args)); } public Builder initializer(CodeBlock codeBlock) { checkState(this.initializer == null, "initializer was already set"); this.initializer = checkNotNull(codeBlock, "codeBlock == null"); return this; } public FieldSpec build() { return new FieldSpec(this); } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/JavaFile.java000066400000000000000000000222601335545476000246360ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.annotation.processing.Filer; import javax.lang.model.element.Element; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import javax.tools.SimpleJavaFileObject; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; import static java.nio.charset.StandardCharsets.UTF_8; /** A Java file containing a single top level class. */ public final class JavaFile { private static final Appendable NULL_APPENDABLE = new Appendable() { @Override public Appendable append(CharSequence charSequence) { return this; } @Override public Appendable append(CharSequence charSequence, int start, int end) { return this; } @Override public Appendable append(char c) { return this; } }; public final CodeBlock fileComment; public final String packageName; public final TypeSpec typeSpec; public final boolean skipJavaLangImports; private final Set staticImports; private final String indent; private JavaFile(Builder builder) { this.fileComment = builder.fileComment.build(); this.packageName = builder.packageName; this.typeSpec = builder.typeSpec; this.skipJavaLangImports = builder.skipJavaLangImports; this.staticImports = Util.immutableSet(builder.staticImports); this.indent = builder.indent; } public void writeTo(Appendable out) throws IOException { // First pass: emit the entire class, just to collect the types we'll need to import. CodeWriter importsCollector = new CodeWriter(NULL_APPENDABLE, indent, staticImports); emit(importsCollector); Map suggestedImports = importsCollector.suggestedImports(); // Second pass: write the code, taking advantage of the imports. CodeWriter codeWriter = new CodeWriter(out, indent, suggestedImports, staticImports); emit(codeWriter); } /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */ public void writeTo(Path directory) throws IOException { checkArgument(Files.notExists(directory) || Files.isDirectory(directory), "path %s exists but is not a directory.", directory); Path outputDirectory = directory; if (!packageName.isEmpty()) { for (String packageComponent : packageName.split("\\.")) { outputDirectory = outputDirectory.resolve(packageComponent); } Files.createDirectories(outputDirectory); } Path outputPath = outputDirectory.resolve(typeSpec.name + ".java"); try (Writer writer = new OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8)) { writeTo(writer); } } /** Writes this to {@code directory} as UTF-8 using the standard directory structure. */ public void writeTo(File directory) throws IOException { writeTo(directory.toPath()); } /** Writes this to {@code filer}. */ public void writeTo(Filer filer) throws IOException { String fileName = packageName.isEmpty() ? typeSpec.name : packageName + "." + typeSpec.name; List originatingElements = typeSpec.originatingElements; JavaFileObject filerSourceFile = filer.createSourceFile(fileName, originatingElements.toArray(new Element[originatingElements.size()])); try (Writer writer = filerSourceFile.openWriter()) { writeTo(writer); } catch (Exception e) { try { filerSourceFile.delete(); } catch (Exception ignored) { } throw e; } } private void emit(CodeWriter codeWriter) throws IOException { codeWriter.pushPackage(packageName); if (!fileComment.isEmpty()) { codeWriter.emitComment(fileComment); } if (!packageName.isEmpty()) { codeWriter.emit("package $L;\n", packageName); codeWriter.emit("\n"); } if (!staticImports.isEmpty()) { for (String signature : staticImports) { codeWriter.emit("import static $L;\n", signature); } codeWriter.emit("\n"); } int importedTypesCount = 0; for (ClassName className : new TreeSet<>(codeWriter.importedTypes().values())) { if (skipJavaLangImports && className.packageName().equals("java.lang")) continue; codeWriter.emit("import $L;\n", className.withoutAnnotations()); importedTypesCount++; } if (importedTypesCount > 0) { codeWriter.emit("\n"); } typeSpec.emit(codeWriter, null, Collections.emptySet()); codeWriter.popPackage(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { try { StringBuilder result = new StringBuilder(); writeTo(result); return result.toString(); } catch (IOException e) { throw new AssertionError(); } } public JavaFileObject toJavaFileObject() { URI uri = URI.create((packageName.isEmpty() ? typeSpec.name : packageName.replace('.', '/') + '/' + typeSpec.name) + Kind.SOURCE.extension); return new SimpleJavaFileObject(uri, Kind.SOURCE) { private final long lastModified = System.currentTimeMillis(); @Override public String getCharContent(boolean ignoreEncodingErrors) { return JavaFile.this.toString(); } @Override public InputStream openInputStream() throws IOException { return new ByteArrayInputStream(getCharContent(true).getBytes(UTF_8)); } @Override public long getLastModified() { return lastModified; } }; } public static Builder builder(String packageName, TypeSpec typeSpec) { checkNotNull(packageName, "packageName == null"); checkNotNull(typeSpec, "typeSpec == null"); return new Builder(packageName, typeSpec); } public Builder toBuilder() { Builder builder = new Builder(packageName, typeSpec); builder.fileComment.add(fileComment); builder.skipJavaLangImports = skipJavaLangImports; builder.indent = indent; return builder; } public static final class Builder { private final String packageName; private final TypeSpec typeSpec; private final CodeBlock.Builder fileComment = CodeBlock.builder(); private final Set staticImports = new TreeSet<>(); private boolean skipJavaLangImports; private String indent = " "; private Builder(String packageName, TypeSpec typeSpec) { this.packageName = packageName; this.typeSpec = typeSpec; } public Builder addFileComment(String format, Object... args) { this.fileComment.add(format, args); return this; } public Builder addStaticImport(Enum constant) { return addStaticImport(ClassName.get(constant.getDeclaringClass()), constant.name()); } public Builder addStaticImport(Class clazz, String... names) { return addStaticImport(ClassName.get(clazz), names); } public Builder addStaticImport(ClassName className, String... names) { checkArgument(className != null, "className == null"); checkArgument(names != null, "names == null"); checkArgument(names.length > 0, "names array is empty"); for (String name : names) { checkArgument(name != null, "null entry in names array: %s", Arrays.toString(names)); staticImports.add(className.canonicalName + "." + name); } return this; } /** * Call this to omit imports for classes in {@code java.lang}, such as {@code java.lang.String}. * *

By default, JavaPoet explicitly imports types in {@code java.lang} to defend against * naming conflicts. Suppose an (ill-advised) class is named {@code com.example.String}. When * {@code java.lang} imports are skipped, generated code in {@code com.example} that references * {@code java.lang.String} will get {@code com.example.String} instead. */ public Builder skipJavaLangImports(boolean skipJavaLangImports) { this.skipJavaLangImports = skipJavaLangImports; return this; } public Builder indent(String indent) { this.indent = indent; return this; } public JavaFile build() { return new JavaFile(this); } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/LineWrapper.java000066400000000000000000000104641335545476000254100ustar00rootroot00000000000000/* * Copyright (C) 2016 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. */ package com.squareup.javapoet; import java.io.IOException; import static com.squareup.javapoet.Util.checkNotNull; /** * Implements soft line wrapping on an appendable. To use, append characters using {@link #append} * or soft-wrapping spaces using {@link #wrappingSpace}. */ final class LineWrapper { private final Appendable out; private final String indent; private final int columnLimit; private boolean closed; /** Characters written since the last wrapping space that haven't yet been flushed. */ private final StringBuilder buffer = new StringBuilder(); /** The number of characters since the most recent newline. Includes both out and the buffer. */ private int column = 0; /** * -1 if we have no buffering; otherwise the number of {@code indent}s to write after wrapping. */ private int indentLevel = -1; /** * Null if we have no buffering; otherwise the type to pass to the next call to {@link #flush}. */ private FlushType nextFlush; LineWrapper(Appendable out, String indent, int columnLimit) { checkNotNull(out, "out == null"); this.out = out; this.indent = indent; this.columnLimit = columnLimit; } /** Emit {@code s}. This may be buffered to permit line wraps to be inserted. */ void append(String s) throws IOException { if (closed) throw new IllegalStateException("closed"); if (nextFlush != null) { int nextNewline = s.indexOf('\n'); // If s doesn't cause the current line to cross the limit, buffer it and return. We'll decide // whether or not we have to wrap it later. if (nextNewline == -1 && column + s.length() <= columnLimit) { buffer.append(s); column += s.length(); return; } // Wrap if appending s would overflow the current line. boolean wrap = nextNewline == -1 || column + nextNewline > columnLimit; flush(wrap ? FlushType.WRAP : nextFlush); } out.append(s); int lastNewline = s.lastIndexOf('\n'); column = lastNewline != -1 ? s.length() - lastNewline - 1 : column + s.length(); } /** Emit either a space or a newline character. */ void wrappingSpace(int indentLevel) throws IOException { if (closed) throw new IllegalStateException("closed"); if (this.nextFlush != null) flush(nextFlush); column++; // Increment the column even though the space is deferred to next call to flush(). this.nextFlush = FlushType.SPACE; this.indentLevel = indentLevel; } /** Emit a newline character if the line will exceed it's limit, otherwise do nothing. */ void zeroWidthSpace(int indentLevel) throws IOException { if (closed) throw new IllegalStateException("closed"); if (column == 0) return; if (this.nextFlush != null) flush(nextFlush); this.nextFlush = FlushType.EMPTY; this.indentLevel = indentLevel; } /** Flush any outstanding text and forbid future writes to this line wrapper. */ void close() throws IOException { if (nextFlush != null) flush(nextFlush); closed = true; } /** Write the space followed by any buffered text that follows it. */ private void flush(FlushType flushType) throws IOException { switch (flushType) { case WRAP: out.append('\n'); for (int i = 0; i < indentLevel; i++) { out.append(indent); } column = indentLevel * indent.length(); column += buffer.length(); break; case SPACE: out.append(' '); break; case EMPTY: break; default: throw new IllegalArgumentException("Unknown FlushType: " + flushType); } out.append(buffer); buffer.delete(0, buffer.length()); indentLevel = -1; nextFlush = null; } private enum FlushType { WRAP, SPACE, EMPTY; } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/MethodSpec.java000066400000000000000000000405671335545476000252220ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.Types; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; import static com.squareup.javapoet.Util.checkState; /** A generated constructor or method declaration. */ public final class MethodSpec { static final String CONSTRUCTOR = ""; public final String name; public final CodeBlock javadoc; public final List annotations; public final Set modifiers; public final List typeVariables; public final TypeName returnType; public final List parameters; public final boolean varargs; public final List exceptions; public final CodeBlock code; public final CodeBlock defaultValue; private MethodSpec(Builder builder) { CodeBlock code = builder.code.build(); checkArgument(code.isEmpty() || !builder.modifiers.contains(Modifier.ABSTRACT), "abstract method %s cannot have code", builder.name); checkArgument(!builder.varargs || lastParameterIsArray(builder.parameters), "last parameter of varargs method %s must be an array", builder.name); this.name = checkNotNull(builder.name, "name == null"); this.javadoc = builder.javadoc.build(); this.annotations = Util.immutableList(builder.annotations); this.modifiers = Util.immutableSet(builder.modifiers); this.typeVariables = Util.immutableList(builder.typeVariables); this.returnType = builder.returnType; this.parameters = Util.immutableList(builder.parameters); this.varargs = builder.varargs; this.exceptions = Util.immutableList(builder.exceptions); this.defaultValue = builder.defaultValue; this.code = code; } private boolean lastParameterIsArray(List parameters) { return !parameters.isEmpty() && TypeName.asArray((parameters.get(parameters.size() - 1).type)) != null; } void emit(CodeWriter codeWriter, String enclosingName, Set implicitModifiers) throws IOException { codeWriter.emitJavadoc(javadoc); codeWriter.emitAnnotations(annotations, false); codeWriter.emitModifiers(modifiers, implicitModifiers); if (!typeVariables.isEmpty()) { codeWriter.emitTypeVariables(typeVariables); codeWriter.emit(" "); } if (isConstructor()) { codeWriter.emit("$L($Z", enclosingName); } else { codeWriter.emit("$T $L($Z", returnType, name); } boolean firstParameter = true; for (Iterator i = parameters.iterator(); i.hasNext(); ) { ParameterSpec parameter = i.next(); if (!firstParameter) codeWriter.emit(",").emitWrappingSpace(); parameter.emit(codeWriter, !i.hasNext() && varargs); firstParameter = false; } codeWriter.emit(")"); if (defaultValue != null && !defaultValue.isEmpty()) { codeWriter.emit(" default "); codeWriter.emit(defaultValue); } if (!exceptions.isEmpty()) { codeWriter.emitWrappingSpace().emit("throws"); boolean firstException = true; for (TypeName exception : exceptions) { if (!firstException) codeWriter.emit(","); codeWriter.emitWrappingSpace().emit("$T", exception); firstException = false; } } if (hasModifier(Modifier.ABSTRACT)) { codeWriter.emit(";\n"); } else if (hasModifier(Modifier.NATIVE)) { // Code is allowed to support stuff like GWT JSNI. codeWriter.emit(code); codeWriter.emit(";\n"); } else { codeWriter.emit(" {\n"); codeWriter.indent(); codeWriter.emit(code); codeWriter.unindent(); codeWriter.emit("}\n"); } } public boolean hasModifier(Modifier modifier) { return modifiers.contains(modifier); } public boolean isConstructor() { return name.equals(CONSTRUCTOR); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { StringBuilder out = new StringBuilder(); try { CodeWriter codeWriter = new CodeWriter(out); emit(codeWriter, "Constructor", Collections.emptySet()); return out.toString(); } catch (IOException e) { throw new AssertionError(); } } public static Builder methodBuilder(String name) { return new Builder(name); } public static Builder constructorBuilder() { return new Builder(CONSTRUCTOR); } /** * Returns a new method spec builder that overrides {@code method}. * *

This will copy its visibility modifiers, type parameters, return type, name, parameters, and * throws declarations. An {@link Override} annotation will be added. * *

Note that in JavaPoet 1.2 through 1.7 this method retained annotations from the method and * parameters of the overridden method. Since JavaPoet 1.8 annotations must be added separately. */ public static Builder overriding(ExecutableElement method) { checkNotNull(method, "method == null"); Element enclosingClass = method.getEnclosingElement(); if (enclosingClass.getModifiers().contains(Modifier.FINAL)) { throw new IllegalArgumentException("Cannot override method on final class " + enclosingClass); } Set modifiers = method.getModifiers(); if (modifiers.contains(Modifier.PRIVATE) || modifiers.contains(Modifier.FINAL) || modifiers.contains(Modifier.STATIC)) { throw new IllegalArgumentException("cannot override method with modifiers: " + modifiers); } String methodName = method.getSimpleName().toString(); MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(methodName); methodBuilder.addAnnotation(Override.class); modifiers = new LinkedHashSet<>(modifiers); modifiers.remove(Modifier.ABSTRACT); modifiers.remove(Modifier.DEFAULT); methodBuilder.addModifiers(modifiers); for (TypeParameterElement typeParameterElement : method.getTypeParameters()) { TypeVariable var = (TypeVariable) typeParameterElement.asType(); methodBuilder.addTypeVariable(TypeVariableName.get(var)); } methodBuilder.returns(TypeName.get(method.getReturnType())); methodBuilder.addParameters(ParameterSpec.parametersOf(method)); methodBuilder.varargs(method.isVarArgs()); for (TypeMirror thrownType : method.getThrownTypes()) { methodBuilder.addException(TypeName.get(thrownType)); } return methodBuilder; } /** * Returns a new method spec builder that overrides {@code method} as a member of {@code * enclosing}. This will resolve type parameters: for example overriding {@link * Comparable#compareTo} in a type that implements {@code Comparable}, the {@code T} * parameter will be resolved to {@code Movie}. * *

This will copy its visibility modifiers, type parameters, return type, name, parameters, and * throws declarations. An {@link Override} annotation will be added. * *

Note that in JavaPoet 1.2 through 1.7 this method retained annotations from the method and * parameters of the overridden method. Since JavaPoet 1.8 annotations must be added separately. */ public static Builder overriding( ExecutableElement method, DeclaredType enclosing, Types types) { ExecutableType executableType = (ExecutableType) types.asMemberOf(enclosing, method); List resolvedParameterTypes = executableType.getParameterTypes(); List resolvedThrownTypes = executableType.getThrownTypes(); TypeMirror resolvedReturnType = executableType.getReturnType(); Builder builder = overriding(method); builder.returns(TypeName.get(resolvedReturnType)); for (int i = 0, size = builder.parameters.size(); i < size; i++) { ParameterSpec parameter = builder.parameters.get(i); TypeName type = TypeName.get(resolvedParameterTypes.get(i)); builder.parameters.set(i, parameter.toBuilder(type, parameter.name).build()); } builder.exceptions.clear(); for (int i = 0, size = resolvedThrownTypes.size(); i < size; i++) { builder.addException(TypeName.get(resolvedThrownTypes.get(i))); } return builder; } public Builder toBuilder() { Builder builder = new Builder(name); builder.javadoc.add(javadoc); builder.annotations.addAll(annotations); builder.modifiers.addAll(modifiers); builder.typeVariables.addAll(typeVariables); builder.returnType = returnType; builder.parameters.addAll(parameters); builder.exceptions.addAll(exceptions); builder.code.add(code); builder.varargs = varargs; builder.defaultValue = defaultValue; return builder; } public static final class Builder { private final String name; private final CodeBlock.Builder javadoc = CodeBlock.builder(); private final List annotations = new ArrayList<>(); private final List modifiers = new ArrayList<>(); private List typeVariables = new ArrayList<>(); private TypeName returnType; private final List parameters = new ArrayList<>(); private final Set exceptions = new LinkedHashSet<>(); private final CodeBlock.Builder code = CodeBlock.builder(); private boolean varargs; private CodeBlock defaultValue; private Builder(String name) { checkNotNull(name, "name == null"); checkArgument(name.equals(CONSTRUCTOR) || SourceVersion.isName(name), "not a valid name: %s", name); this.name = name; this.returnType = name.equals(CONSTRUCTOR) ? null : TypeName.VOID; } public Builder addJavadoc(String format, Object... args) { javadoc.add(format, args); return this; } public Builder addJavadoc(CodeBlock block) { javadoc.add(block); return this; } public Builder addAnnotations(Iterable annotationSpecs) { checkArgument(annotationSpecs != null, "annotationSpecs == null"); for (AnnotationSpec annotationSpec : annotationSpecs) { this.annotations.add(annotationSpec); } return this; } public Builder addAnnotation(AnnotationSpec annotationSpec) { this.annotations.add(annotationSpec); return this; } public Builder addAnnotation(ClassName annotation) { this.annotations.add(AnnotationSpec.builder(annotation).build()); return this; } public Builder addAnnotation(Class annotation) { return addAnnotation(ClassName.get(annotation)); } public Builder addModifiers(Modifier... modifiers) { checkNotNull(modifiers, "modifiers == null"); Collections.addAll(this.modifiers, modifiers); return this; } public Builder addModifiers(Iterable modifiers) { checkNotNull(modifiers, "modifiers == null"); for (Modifier modifier : modifiers) { this.modifiers.add(modifier); } return this; } public Builder addTypeVariables(Iterable typeVariables) { checkArgument(typeVariables != null, "typeVariables == null"); for (TypeVariableName typeVariable : typeVariables) { this.typeVariables.add(typeVariable); } return this; } public Builder addTypeVariable(TypeVariableName typeVariable) { typeVariables.add(typeVariable); return this; } public Builder returns(TypeName returnType) { checkState(!name.equals(CONSTRUCTOR), "constructor cannot have return type."); this.returnType = returnType; return this; } public Builder returns(Type returnType) { return returns(TypeName.get(returnType)); } public Builder addParameters(Iterable parameterSpecs) { checkArgument(parameterSpecs != null, "parameterSpecs == null"); for (ParameterSpec parameterSpec : parameterSpecs) { this.parameters.add(parameterSpec); } return this; } public Builder addParameter(ParameterSpec parameterSpec) { this.parameters.add(parameterSpec); return this; } public Builder addParameter(TypeName type, String name, Modifier... modifiers) { return addParameter(ParameterSpec.builder(type, name, modifiers).build()); } public Builder addParameter(Type type, String name, Modifier... modifiers) { return addParameter(TypeName.get(type), name, modifiers); } public Builder varargs() { return varargs(true); } public Builder varargs(boolean varargs) { this.varargs = varargs; return this; } public Builder addExceptions(Iterable exceptions) { checkArgument(exceptions != null, "exceptions == null"); for (TypeName exception : exceptions) { this.exceptions.add(exception); } return this; } public Builder addException(TypeName exception) { this.exceptions.add(exception); return this; } public Builder addException(Type exception) { return addException(TypeName.get(exception)); } public Builder addCode(String format, Object... args) { code.add(format, args); return this; } public Builder addNamedCode(String format, Map args) { code.addNamed(format, args); return this; } public Builder addCode(CodeBlock codeBlock) { code.add(codeBlock); return this; } public Builder addComment(String format, Object... args) { code.add("// " + format + "\n", args); return this; } public Builder defaultValue(String format, Object... args) { return defaultValue(CodeBlock.of(format, args)); } public Builder defaultValue(CodeBlock codeBlock) { checkState(this.defaultValue == null, "defaultValue was already set"); this.defaultValue = checkNotNull(codeBlock, "codeBlock == null"); return this; } /** * @param controlFlow the control flow construct and its code, such as "if (foo == 5)". * Shouldn't contain braces or newline characters. */ public Builder beginControlFlow(String controlFlow, Object... args) { code.beginControlFlow(controlFlow, args); return this; } /** * @param controlFlow the control flow construct and its code, such as "else if (foo == 10)". * Shouldn't contain braces or newline characters. */ public Builder nextControlFlow(String controlFlow, Object... args) { code.nextControlFlow(controlFlow, args); return this; } public Builder endControlFlow() { code.endControlFlow(); return this; } /** * @param controlFlow the optional control flow construct and its code, such as * "while(foo == 20)". Only used for "do/while" control flows. */ public Builder endControlFlow(String controlFlow, Object... args) { code.endControlFlow(controlFlow, args); return this; } public Builder addStatement(String format, Object... args) { code.addStatement(format, args); return this; } public Builder addStatement(CodeBlock codeBlock) { code.addStatement(codeBlock); return this; } public MethodSpec build() { return new MethodSpec(this); } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/NameAllocator.java000066400000000000000000000136671335545476000257110ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.UUID; import javax.lang.model.SourceVersion; import static com.squareup.javapoet.Util.checkNotNull; /** * Assigns Java identifier names to avoid collisions, keywords, and invalid characters. To use, * first create an instance and allocate all of the names that you need. Typically this is a * mix of user-supplied names and constants:

   {@code
 *
 *   NameAllocator nameAllocator = new NameAllocator();
 *   for (MyProperty property : properties) {
 *     nameAllocator.newName(property.name(), property);
 *   }
 *   nameAllocator.newName("sb", "string builder");
 * }
* * Pass a unique tag object to each allocation. The tag scopes the name, and can be used to look up * the allocated name later. Typically the tag is the object that is being named. In the above * example we use {@code property} for the user-supplied property names, and {@code "string * builder"} for our constant string builder. * *

Once we've allocated names we can use them when generating code:

   {@code
 *
 *   MethodSpec.Builder builder = MethodSpec.methodBuilder("toString")
 *       .addAnnotation(Override.class)
 *       .addModifiers(Modifier.PUBLIC)
 *       .returns(String.class);
 *
 *   builder.addStatement("$1T $2N = new $1T()",
 *       StringBuilder.class, nameAllocator.get("string builder"));
 *   for (MyProperty property : properties) {
 *     builder.addStatement("$N.append($N)",
 *         nameAllocator.get("string builder"), nameAllocator.get(property));
 *   }
 *   builder.addStatement("return $N", nameAllocator.get("string builder"));
 *   return builder.build();
 * }
* * The above code generates unique names if presented with conflicts. Given user-supplied properties * with names {@code ab} and {@code sb} this generates the following:
   {@code
 *
 *   @Override
 *   public String toString() {
 *     StringBuilder sb_ = new StringBuilder();
 *     sb_.append(ab);
 *     sb_.append(sb);
 *     return sb_.toString();
 *   }
 * }
* * The underscore is appended to {@code sb} to avoid conflicting with the user-supplied {@code sb} * property. Underscores are also prefixed for names that start with a digit, and used to replace * name-unsafe characters like space or dash. * *

When dealing with multiple independent inner scopes, use a {@link #clone()} of the * NameAllocator used for the outer scope to further refine name allocation for a specific inner * scope. */ public final class NameAllocator implements Cloneable { private final Set allocatedNames; private final Map tagToName; public NameAllocator() { this(new LinkedHashSet<>(), new LinkedHashMap<>()); } private NameAllocator(LinkedHashSet allocatedNames, LinkedHashMap tagToName) { this.allocatedNames = allocatedNames; this.tagToName = tagToName; } /** * Return a new name using {@code suggestion} that will not be a Java identifier or clash with * other names. */ public String newName(String suggestion) { return newName(suggestion, UUID.randomUUID().toString()); } /** * Return a new name using {@code suggestion} that will not be a Java identifier or clash with * other names. The returned value can be queried multiple times by passing {@code tag} to * {@link #get(Object)}. */ public String newName(String suggestion, Object tag) { checkNotNull(suggestion, "suggestion"); checkNotNull(tag, "tag"); suggestion = toJavaIdentifier(suggestion); while (SourceVersion.isKeyword(suggestion) || !allocatedNames.add(suggestion)) { suggestion = suggestion + "_"; } String replaced = tagToName.put(tag, suggestion); if (replaced != null) { tagToName.put(tag, replaced); // Put things back as they were! throw new IllegalArgumentException("tag " + tag + " cannot be used for both '" + replaced + "' and '" + suggestion + "'"); } return suggestion; } public static String toJavaIdentifier(String suggestion) { StringBuilder result = new StringBuilder(); for (int i = 0; i < suggestion.length(); ) { int codePoint = suggestion.codePointAt(i); if (i == 0 && !Character.isJavaIdentifierStart(codePoint) && Character.isJavaIdentifierPart(codePoint)) { result.append("_"); } int validCodePoint = Character.isJavaIdentifierPart(codePoint) ? codePoint : '_'; result.appendCodePoint(validCodePoint); i += Character.charCount(codePoint); } return result.toString(); } /** Retrieve a name created with {@link #newName(String, Object)}. */ public String get(Object tag) { String result = tagToName.get(tag); if (result == null) { throw new IllegalArgumentException("unknown tag: " + tag); } return result; } /** * Create a deep copy of this NameAllocator. Useful to create multiple independent refinements * of a NameAllocator to be used in the respective definition of multiples, independently-scoped, * inner code blocks. * * @return A deep copy of this NameAllocator. */ @Override public NameAllocator clone() { return new NameAllocator( new LinkedHashSet<>(this.allocatedNames), new LinkedHashMap<>(this.tagToName)); } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/ParameterSpec.java000066400000000000000000000123161335545476000257110ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.lang.model.SourceVersion; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.VariableElement; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; /** A generated parameter declaration. */ public final class ParameterSpec { public final String name; public final List annotations; public final Set modifiers; public final TypeName type; private ParameterSpec(Builder builder) { this.name = checkNotNull(builder.name, "name == null"); this.annotations = Util.immutableList(builder.annotations); this.modifiers = Util.immutableSet(builder.modifiers); this.type = checkNotNull(builder.type, "type == null"); } public boolean hasModifier(Modifier modifier) { return modifiers.contains(modifier); } void emit(CodeWriter codeWriter, boolean varargs) throws IOException { codeWriter.emitAnnotations(annotations, true); codeWriter.emitModifiers(modifiers); if (varargs) { TypeName.asArray(type).emit(codeWriter, true); } else { type.emit(codeWriter); } codeWriter.emit(" $L", name); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { StringBuilder out = new StringBuilder(); try { CodeWriter codeWriter = new CodeWriter(out); emit(codeWriter, false); return out.toString(); } catch (IOException e) { throw new AssertionError(); } } public static ParameterSpec get(VariableElement element) { TypeName type = TypeName.get(element.asType()); String name = element.getSimpleName().toString(); return ParameterSpec.builder(type, name) .addModifiers(element.getModifiers()) .build(); } static List parametersOf(ExecutableElement method) { List result = new ArrayList<>(); for (VariableElement parameter : method.getParameters()) { result.add(ParameterSpec.get(parameter)); } return result; } public static Builder builder(TypeName type, String name, Modifier... modifiers) { checkNotNull(type, "type == null"); checkArgument(SourceVersion.isName(name), "not a valid name: %s", name); return new Builder(type, name) .addModifiers(modifiers); } public static Builder builder(Type type, String name, Modifier... modifiers) { return builder(TypeName.get(type), name, modifiers); } public Builder toBuilder() { return toBuilder(type, name); } Builder toBuilder(TypeName type, String name) { Builder builder = new Builder(type, name); builder.annotations.addAll(annotations); builder.modifiers.addAll(modifiers); return builder; } public static final class Builder { private final TypeName type; private final String name; private final List annotations = new ArrayList<>(); private final List modifiers = new ArrayList<>(); private Builder(TypeName type, String name) { this.type = type; this.name = name; } public Builder addAnnotations(Iterable annotationSpecs) { checkArgument(annotationSpecs != null, "annotationSpecs == null"); for (AnnotationSpec annotationSpec : annotationSpecs) { this.annotations.add(annotationSpec); } return this; } public Builder addAnnotation(AnnotationSpec annotationSpec) { this.annotations.add(annotationSpec); return this; } public Builder addAnnotation(ClassName annotation) { this.annotations.add(AnnotationSpec.builder(annotation).build()); return this; } public Builder addAnnotation(Class annotation) { return addAnnotation(ClassName.get(annotation)); } public Builder addModifiers(Modifier... modifiers) { Collections.addAll(this.modifiers, modifiers); return this; } public Builder addModifiers(Iterable modifiers) { checkNotNull(modifiers, "modifiers == null"); for (Modifier modifier : modifiers) { this.modifiers.add(modifier); } return this; } public ParameterSpec build() { return new ParameterSpec(this); } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/ParameterizedTypeName.java000066400000000000000000000123301335545476000274110ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; public final class ParameterizedTypeName extends TypeName { private final ParameterizedTypeName enclosingType; public final ClassName rawType; public final List typeArguments; ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType, List typeArguments) { this(enclosingType, rawType, typeArguments, new ArrayList<>()); } private ParameterizedTypeName(ParameterizedTypeName enclosingType, ClassName rawType, List typeArguments, List annotations) { super(annotations); this.rawType = checkNotNull(rawType, "rawType == null").annotated(annotations); this.enclosingType = enclosingType; this.typeArguments = Util.immutableList(typeArguments); checkArgument(!this.typeArguments.isEmpty() || enclosingType != null, "no type arguments: %s", rawType); for (TypeName typeArgument : this.typeArguments) { checkArgument(!typeArgument.isPrimitive() && typeArgument != VOID, "invalid type parameter: %s", typeArgument); } } @Override public ParameterizedTypeName annotated(List annotations) { return new ParameterizedTypeName( enclosingType, rawType, typeArguments, concatAnnotations(annotations)); } @Override public TypeName withoutAnnotations() { return new ParameterizedTypeName( enclosingType, rawType.withoutAnnotations(), typeArguments, new ArrayList<>()); } @Override CodeWriter emit(CodeWriter out) throws IOException { if (enclosingType != null) { enclosingType.emit(out); out.emit("."); if (isAnnotated()) { out.emit(" "); emitAnnotations(out); } out.emit(rawType.simpleName()); } else { rawType.emit(out); } if (!typeArguments.isEmpty()) { out.emitAndIndent("<"); boolean firstParameter = true; for (TypeName parameter : typeArguments) { if (!firstParameter) out.emitAndIndent(", "); parameter.emit(out); firstParameter = false; } out.emitAndIndent(">"); } return out; } /** * Returns a new {@link ParameterizedTypeName} instance for the specified {@code name} as nested * inside this class. */ public ParameterizedTypeName nestedClass(String name) { checkNotNull(name, "name == null"); return new ParameterizedTypeName(this, rawType.nestedClass(name), new ArrayList<>(), new ArrayList<>()); } /** * Returns a new {@link ParameterizedTypeName} instance for the specified {@code name} as nested * inside this class, with the specified {@code typeArguments}. */ public ParameterizedTypeName nestedClass(String name, List typeArguments) { checkNotNull(name, "name == null"); return new ParameterizedTypeName(this, rawType.nestedClass(name), typeArguments, new ArrayList<>()); } /** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */ public static ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments) { return new ParameterizedTypeName(null, rawType, Arrays.asList(typeArguments)); } /** Returns a parameterized type, applying {@code typeArguments} to {@code rawType}. */ public static ParameterizedTypeName get(Class rawType, Type... typeArguments) { return new ParameterizedTypeName(null, ClassName.get(rawType), list(typeArguments)); } /** Returns a parameterized type equivalent to {@code type}. */ public static ParameterizedTypeName get(ParameterizedType type) { return get(type, new LinkedHashMap<>()); } /** Returns a parameterized type equivalent to {@code type}. */ static ParameterizedTypeName get(ParameterizedType type, Map map) { ClassName rawType = ClassName.get((Class) type.getRawType()); ParameterizedType ownerType = (type.getOwnerType() instanceof ParameterizedType) && !Modifier.isStatic(((Class) type.getRawType()).getModifiers()) ? (ParameterizedType) type.getOwnerType() : null; List typeArguments = TypeName.list(type.getActualTypeArguments(), map); return (ownerType != null) ? get(ownerType, map).nestedClass(rawType.simpleName(), typeArguments) : new ParameterizedTypeName(null, rawType, typeArguments); } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/TypeName.java000066400000000000000000000336711335545476000247070ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ErrorType; import javax.lang.model.type.NoType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleTypeVisitor8; /** * Any type in Java's type system, plus {@code void}. This class is an identifier for primitive * types like {@code int} and raw reference types like {@code String} and {@code List}. It also * identifies composite types like {@code char[]} and {@code Set}. * *

Type names are dumb identifiers only and do not model the values they name. For example, the * type name for {@code java.lang.List} doesn't know about the {@code size()} method, the fact that * lists are collections, or even that it accepts a single type parameter. * *

Instances of this class are immutable value objects that implement {@code equals()} and {@code * hashCode()} properly. * *

Referencing existing types

* *

Primitives and void are constants that you can reference directly: see {@link #INT}, {@link * #DOUBLE}, and {@link #VOID}. * *

In an annotation processor you can get a type name instance for a type mirror by calling * {@link #get(TypeMirror)}. In reflection code, you can use {@link #get(Type)}. * *

Defining new types

* *

Create new reference types like {@code com.example.HelloWorld} with {@link * ClassName#get(String, String, String...)}. To build composite types like {@code char[]} and * {@code Set}, use the factory methods on {@link ArrayTypeName}, {@link * ParameterizedTypeName}, {@link TypeVariableName}, and {@link WildcardTypeName}. */ public class TypeName { public static final TypeName VOID = new TypeName("void"); public static final TypeName BOOLEAN = new TypeName("boolean"); public static final TypeName BYTE = new TypeName("byte"); public static final TypeName SHORT = new TypeName("short"); public static final TypeName INT = new TypeName("int"); public static final TypeName LONG = new TypeName("long"); public static final TypeName CHAR = new TypeName("char"); public static final TypeName FLOAT = new TypeName("float"); public static final TypeName DOUBLE = new TypeName("double"); public static final ClassName OBJECT = ClassName.get("java.lang", "Object"); private static final ClassName BOXED_VOID = ClassName.get("java.lang", "Void"); private static final ClassName BOXED_BOOLEAN = ClassName.get("java.lang", "Boolean"); private static final ClassName BOXED_BYTE = ClassName.get("java.lang", "Byte"); private static final ClassName BOXED_SHORT = ClassName.get("java.lang", "Short"); private static final ClassName BOXED_INT = ClassName.get("java.lang", "Integer"); private static final ClassName BOXED_LONG = ClassName.get("java.lang", "Long"); private static final ClassName BOXED_CHAR = ClassName.get("java.lang", "Character"); private static final ClassName BOXED_FLOAT = ClassName.get("java.lang", "Float"); private static final ClassName BOXED_DOUBLE = ClassName.get("java.lang", "Double"); /** The name of this type if it is a keyword, or null. */ private final String keyword; public final List annotations; /** Lazily-initialized toString of this type name. */ private String cachedString; private TypeName(String keyword) { this(keyword, new ArrayList<>()); } private TypeName(String keyword, List annotations) { this.keyword = keyword; this.annotations = Util.immutableList(annotations); } // Package-private constructor to prevent third-party subclasses. TypeName(List annotations) { this(null, annotations); } public final TypeName annotated(AnnotationSpec... annotations) { return annotated(Arrays.asList(annotations)); } public TypeName annotated(List annotations) { Util.checkNotNull(annotations, "annotations == null"); return new TypeName(keyword, concatAnnotations(annotations)); } public TypeName withoutAnnotations() { return new TypeName(keyword); } protected final List concatAnnotations(List annotations) { List allAnnotations = new ArrayList<>(this.annotations); allAnnotations.addAll(annotations); return allAnnotations; } public boolean isAnnotated() { return !annotations.isEmpty(); } /** * Returns true if this is a primitive type like {@code int}. Returns false for all other types * types including boxed primitives and {@code void}. */ public boolean isPrimitive() { return keyword != null && this != VOID; } /** * Returns true if this is a boxed primitive type like {@code Integer}. Returns false for all * other types types including unboxed primitives and {@code java.lang.Void}. */ public boolean isBoxedPrimitive() { return this.equals(BOXED_BOOLEAN) || this.equals(BOXED_BYTE) || this.equals(BOXED_SHORT) || this.equals(BOXED_INT) || this.equals(BOXED_LONG) || this.equals(BOXED_CHAR) || this.equals(BOXED_FLOAT) || this.equals(BOXED_DOUBLE); } /** * Returns a boxed type if this is a primitive type (like {@code Integer} for {@code int}) or * {@code void}. Returns this type if boxing doesn't apply. */ public TypeName box() { if (keyword == null) return this; // Doesn't need boxing. if (this == VOID) return BOXED_VOID; if (this == BOOLEAN) return BOXED_BOOLEAN; if (this == BYTE) return BOXED_BYTE; if (this == SHORT) return BOXED_SHORT; if (this == INT) return BOXED_INT; if (this == LONG) return BOXED_LONG; if (this == CHAR) return BOXED_CHAR; if (this == FLOAT) return BOXED_FLOAT; if (this == DOUBLE) return BOXED_DOUBLE; throw new AssertionError(keyword); } /** * Returns an unboxed type if this is a boxed primitive type (like {@code int} for {@code * Integer}) or {@code Void}. Returns this type if it is already unboxed. * * @throws UnsupportedOperationException if this type isn't eligible for unboxing. */ public TypeName unbox() { if (keyword != null) return this; // Already unboxed. if (this.equals(BOXED_VOID)) return VOID; if (this.equals(BOXED_BOOLEAN)) return BOOLEAN; if (this.equals(BOXED_BYTE)) return BYTE; if (this.equals(BOXED_SHORT)) return SHORT; if (this.equals(BOXED_INT)) return INT; if (this.equals(BOXED_LONG)) return LONG; if (this.equals(BOXED_CHAR)) return CHAR; if (this.equals(BOXED_FLOAT)) return FLOAT; if (this.equals(BOXED_DOUBLE)) return DOUBLE; throw new UnsupportedOperationException("cannot unbox " + this); } @Override public final boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public final int hashCode() { return toString().hashCode(); } @Override public final String toString() { String result = cachedString; if (result == null) { try { StringBuilder resultBuilder = new StringBuilder(); CodeWriter codeWriter = new CodeWriter(resultBuilder); emit(codeWriter); result = resultBuilder.toString(); cachedString = result; } catch (IOException e) { throw new AssertionError(); } } return result; } CodeWriter emit(CodeWriter out) throws IOException { if (keyword == null) throw new AssertionError(); if (isAnnotated()) { out.emit(""); emitAnnotations(out); } return out.emitAndIndent(keyword); } CodeWriter emitAnnotations(CodeWriter out) throws IOException { for (AnnotationSpec annotation : annotations) { annotation.emit(out, true); out.emit(" "); } return out; } /** Returns a type name equivalent to {@code mirror}. */ public static TypeName get(TypeMirror mirror) { return get(mirror, new LinkedHashMap<>()); } static TypeName get(TypeMirror mirror, final Map typeVariables) { return mirror.accept(new SimpleTypeVisitor8() { @Override public TypeName visitPrimitive(PrimitiveType t, Void p) { switch (t.getKind()) { case BOOLEAN: return TypeName.BOOLEAN; case BYTE: return TypeName.BYTE; case SHORT: return TypeName.SHORT; case INT: return TypeName.INT; case LONG: return TypeName.LONG; case CHAR: return TypeName.CHAR; case FLOAT: return TypeName.FLOAT; case DOUBLE: return TypeName.DOUBLE; default: throw new AssertionError(); } } @Override public TypeName visitDeclared(DeclaredType t, Void p) { ClassName rawType = ClassName.get((TypeElement) t.asElement()); TypeMirror enclosingType = t.getEnclosingType(); TypeName enclosing = (enclosingType.getKind() != TypeKind.NONE) && !t.asElement().getModifiers().contains(Modifier.STATIC) ? enclosingType.accept(this, null) : null; if (t.getTypeArguments().isEmpty() && !(enclosing instanceof ParameterizedTypeName)) { return rawType; } List typeArgumentNames = new ArrayList<>(); for (TypeMirror mirror : t.getTypeArguments()) { typeArgumentNames.add(get(mirror, typeVariables)); } return enclosing instanceof ParameterizedTypeName ? ((ParameterizedTypeName) enclosing).nestedClass( rawType.simpleName(), typeArgumentNames) : new ParameterizedTypeName(null, rawType, typeArgumentNames); } @Override public TypeName visitError(ErrorType t, Void p) { return visitDeclared(t, p); } @Override public ArrayTypeName visitArray(ArrayType t, Void p) { return ArrayTypeName.get(t, typeVariables); } @Override public TypeName visitTypeVariable(javax.lang.model.type.TypeVariable t, Void p) { return TypeVariableName.get(t, typeVariables); } @Override public TypeName visitWildcard(javax.lang.model.type.WildcardType t, Void p) { return WildcardTypeName.get(t, typeVariables); } @Override public TypeName visitNoType(NoType t, Void p) { if (t.getKind() == TypeKind.VOID) return TypeName.VOID; return super.visitUnknown(t, p); } @Override protected TypeName defaultAction(TypeMirror e, Void p) { throw new IllegalArgumentException("Unexpected type mirror: " + e); } }, null); } /** Returns a type name equivalent to {@code type}. */ public static TypeName get(Type type) { return get(type, new LinkedHashMap<>()); } static TypeName get(Type type, Map map) { if (type instanceof Class) { Class classType = (Class) type; if (type == void.class) return VOID; if (type == boolean.class) return BOOLEAN; if (type == byte.class) return BYTE; if (type == short.class) return SHORT; if (type == int.class) return INT; if (type == long.class) return LONG; if (type == char.class) return CHAR; if (type == float.class) return FLOAT; if (type == double.class) return DOUBLE; if (classType.isArray()) return ArrayTypeName.of(get(classType.getComponentType(), map)); return ClassName.get(classType); } else if (type instanceof ParameterizedType) { return ParameterizedTypeName.get((ParameterizedType) type, map); } else if (type instanceof WildcardType) { return WildcardTypeName.get((WildcardType) type, map); } else if (type instanceof TypeVariable) { return TypeVariableName.get((TypeVariable) type, map); } else if (type instanceof GenericArrayType) { return ArrayTypeName.get((GenericArrayType) type, map); } else { throw new IllegalArgumentException("unexpected type: " + type); } } /** Converts an array of types to a list of type names. */ static List list(Type[] types) { return list(types, new LinkedHashMap<>()); } static List list(Type[] types, Map map) { List result = new ArrayList<>(types.length); for (Type type : types) { result.add(get(type, map)); } return result; } /** Returns the array component of {@code type}, or null if {@code type} is not an array. */ static TypeName arrayComponent(TypeName type) { return type instanceof ArrayTypeName ? ((ArrayTypeName) type).componentType : null; } /** Returns {@code type} as an array, or null if {@code type} is not an array. */ static ArrayTypeName asArray(TypeName type) { return type instanceof ArrayTypeName ? ((ArrayTypeName) type) : null; } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/TypeSpec.java000066400000000000000000000560101335545476000247110ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; 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.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; import static com.squareup.javapoet.Util.checkState; import static com.squareup.javapoet.Util.requireExactlyOneOf; /** A generated class, interface, or enum declaration. */ public final class TypeSpec { public final Kind kind; public final String name; public final CodeBlock anonymousTypeArguments; public final CodeBlock javadoc; public final List annotations; public final Set modifiers; public final List typeVariables; public final TypeName superclass; public final List superinterfaces; public final Map enumConstants; public final List fieldSpecs; public final CodeBlock staticBlock; public final CodeBlock initializerBlock; public final List methodSpecs; public final List typeSpecs; public final List originatingElements; private TypeSpec(Builder builder) { this.kind = builder.kind; this.name = builder.name; this.anonymousTypeArguments = builder.anonymousTypeArguments; this.javadoc = builder.javadoc.build(); this.annotations = Util.immutableList(builder.annotations); this.modifiers = Util.immutableSet(builder.modifiers); this.typeVariables = Util.immutableList(builder.typeVariables); this.superclass = builder.superclass; this.superinterfaces = Util.immutableList(builder.superinterfaces); this.enumConstants = Util.immutableMap(builder.enumConstants); this.fieldSpecs = Util.immutableList(builder.fieldSpecs); this.staticBlock = builder.staticBlock.build(); this.initializerBlock = builder.initializerBlock.build(); this.methodSpecs = Util.immutableList(builder.methodSpecs); this.typeSpecs = Util.immutableList(builder.typeSpecs); List originatingElementsMutable = new ArrayList<>(); originatingElementsMutable.addAll(builder.originatingElements); for (TypeSpec typeSpec : builder.typeSpecs) { originatingElementsMutable.addAll(typeSpec.originatingElements); } this.originatingElements = Util.immutableList(originatingElementsMutable); } /** * Creates a dummy type spec for type-resolution only (in CodeWriter) * while emitting the type declaration but before entering the type body. */ private TypeSpec(TypeSpec type) { assert type.anonymousTypeArguments == null; this.kind = type.kind; this.name = type.name; this.anonymousTypeArguments = null; this.javadoc = type.javadoc; this.annotations = Collections.emptyList(); this.modifiers = Collections.emptySet(); this.typeVariables = Collections.emptyList(); this.superclass = null; this.superinterfaces = Collections.emptyList(); this.enumConstants = Collections.emptyMap(); this.fieldSpecs = Collections.emptyList(); this.staticBlock = type.staticBlock; this.initializerBlock = type.initializerBlock; this.methodSpecs = Collections.emptyList(); this.typeSpecs = Collections.emptyList(); this.originatingElements = Collections.emptyList(); } public boolean hasModifier(Modifier modifier) { return modifiers.contains(modifier); } public static Builder classBuilder(String name) { return new Builder(Kind.CLASS, checkNotNull(name, "name == null"), null); } public static Builder classBuilder(ClassName className) { return classBuilder(checkNotNull(className, "className == null").simpleName()); } public static Builder interfaceBuilder(String name) { return new Builder(Kind.INTERFACE, checkNotNull(name, "name == null"), null); } public static Builder interfaceBuilder(ClassName className) { return interfaceBuilder(checkNotNull(className, "className == null").simpleName()); } public static Builder enumBuilder(String name) { return new Builder(Kind.ENUM, checkNotNull(name, "name == null"), null); } public static Builder enumBuilder(ClassName className) { return enumBuilder(checkNotNull(className, "className == null").simpleName()); } public static Builder anonymousClassBuilder(String typeArgumentsFormat, Object... args) { return anonymousClassBuilder(CodeBlock.builder() .add(typeArgumentsFormat, args) .build()); } public static Builder anonymousClassBuilder(CodeBlock typeArguments) { return new Builder(Kind.CLASS, null, typeArguments); } public static Builder annotationBuilder(String name) { return new Builder(Kind.ANNOTATION, checkNotNull(name, "name == null"), null); } public static Builder annotationBuilder(ClassName className) { return annotationBuilder(checkNotNull(className, "className == null").simpleName()); } public Builder toBuilder() { Builder builder = new Builder(kind, name, anonymousTypeArguments); builder.javadoc.add(javadoc); builder.annotations.addAll(annotations); builder.modifiers.addAll(modifiers); builder.typeVariables.addAll(typeVariables); builder.superclass = superclass; builder.superinterfaces.addAll(superinterfaces); builder.enumConstants.putAll(enumConstants); builder.fieldSpecs.addAll(fieldSpecs); builder.methodSpecs.addAll(methodSpecs); builder.typeSpecs.addAll(typeSpecs); builder.initializerBlock.add(initializerBlock); builder.staticBlock.add(staticBlock); return builder; } void emit(CodeWriter codeWriter, String enumName, Set implicitModifiers) throws IOException { // Nested classes interrupt wrapped line indentation. Stash the current wrapping state and put // it back afterwards when this type is complete. int previousStatementLine = codeWriter.statementLine; codeWriter.statementLine = -1; try { if (enumName != null) { codeWriter.emitJavadoc(javadoc); codeWriter.emitAnnotations(annotations, false); codeWriter.emit("$L", enumName); if (!anonymousTypeArguments.formatParts.isEmpty()) { codeWriter.emit("("); codeWriter.emit(anonymousTypeArguments); codeWriter.emit(")"); } if (fieldSpecs.isEmpty() && methodSpecs.isEmpty() && typeSpecs.isEmpty()) { return; // Avoid unnecessary braces "{}". } codeWriter.emit(" {\n"); } else if (anonymousTypeArguments != null) { TypeName supertype = !superinterfaces.isEmpty() ? superinterfaces.get(0) : superclass; codeWriter.emit("new $T(", supertype); codeWriter.emit(anonymousTypeArguments); codeWriter.emit(") {\n"); } else { // Push an empty type (specifically without nested types) for type-resolution. codeWriter.pushType(new TypeSpec(this)); codeWriter.emitJavadoc(javadoc); codeWriter.emitAnnotations(annotations, false); codeWriter.emitModifiers(modifiers, Util.union(implicitModifiers, kind.asMemberModifiers)); if (kind == Kind.ANNOTATION) { codeWriter.emit("$L $L", "@interface", name); } else { codeWriter.emit("$L $L", kind.name().toLowerCase(Locale.US), name); } codeWriter.emitTypeVariables(typeVariables); List extendsTypes; List implementsTypes; if (kind == Kind.INTERFACE) { extendsTypes = superinterfaces; implementsTypes = Collections.emptyList(); } else { extendsTypes = superclass.equals(ClassName.OBJECT) ? Collections.emptyList() : Collections.singletonList(superclass); implementsTypes = superinterfaces; } if (!extendsTypes.isEmpty()) { codeWriter.emit(" extends"); boolean firstType = true; for (TypeName type : extendsTypes) { if (!firstType) codeWriter.emit(","); codeWriter.emit(" $T", type); firstType = false; } } if (!implementsTypes.isEmpty()) { codeWriter.emit(" implements"); boolean firstType = true; for (TypeName type : implementsTypes) { if (!firstType) codeWriter.emit(","); codeWriter.emit(" $T", type); firstType = false; } } codeWriter.popType(); codeWriter.emit(" {\n"); } codeWriter.pushType(this); codeWriter.indent(); boolean firstMember = true; for (Iterator> i = enumConstants.entrySet().iterator(); i.hasNext(); ) { Map.Entry enumConstant = i.next(); if (!firstMember) codeWriter.emit("\n"); enumConstant.getValue().emit(codeWriter, enumConstant.getKey(), Collections.emptySet()); firstMember = false; if (i.hasNext()) { codeWriter.emit(",\n"); } else if (!fieldSpecs.isEmpty() || !methodSpecs.isEmpty() || !typeSpecs.isEmpty()) { codeWriter.emit(";\n"); } else { codeWriter.emit("\n"); } } // Static fields. for (FieldSpec fieldSpec : fieldSpecs) { if (!fieldSpec.hasModifier(Modifier.STATIC)) continue; if (!firstMember) codeWriter.emit("\n"); fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); firstMember = false; } if (!staticBlock.isEmpty()) { if (!firstMember) codeWriter.emit("\n"); codeWriter.emit(staticBlock); firstMember = false; } // Non-static fields. for (FieldSpec fieldSpec : fieldSpecs) { if (fieldSpec.hasModifier(Modifier.STATIC)) continue; if (!firstMember) codeWriter.emit("\n"); fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); firstMember = false; } // Initializer block. if (!initializerBlock.isEmpty()) { if (!firstMember) codeWriter.emit("\n"); codeWriter.emit(initializerBlock); firstMember = false; } // Constructors. for (MethodSpec methodSpec : methodSpecs) { if (!methodSpec.isConstructor()) continue; if (!firstMember) codeWriter.emit("\n"); methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers); firstMember = false; } // Methods (static and non-static). for (MethodSpec methodSpec : methodSpecs) { if (methodSpec.isConstructor()) continue; if (!firstMember) codeWriter.emit("\n"); methodSpec.emit(codeWriter, name, kind.implicitMethodModifiers); firstMember = false; } // Types. for (TypeSpec typeSpec : typeSpecs) { if (!firstMember) codeWriter.emit("\n"); typeSpec.emit(codeWriter, null, kind.implicitTypeModifiers); firstMember = false; } codeWriter.unindent(); codeWriter.popType(); codeWriter.emit("}"); if (enumName == null && anonymousTypeArguments == null) { codeWriter.emit("\n"); // If this type isn't also a value, include a trailing newline. } } finally { codeWriter.statementLine = previousStatementLine; } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (getClass() != o.getClass()) return false; return toString().equals(o.toString()); } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { StringBuilder out = new StringBuilder(); try { CodeWriter codeWriter = new CodeWriter(out); emit(codeWriter, null, Collections.emptySet()); return out.toString(); } catch (IOException e) { throw new AssertionError(); } } public enum Kind { CLASS( Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.emptySet()), INTERFACE( Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)), Util.immutableSet(Collections.singletonList(Modifier.STATIC))), ENUM( Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), Collections.singleton(Modifier.STATIC)), ANNOTATION( Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC)), Util.immutableSet(Collections.singletonList(Modifier.STATIC))); private final Set implicitFieldModifiers; private final Set implicitMethodModifiers; private final Set implicitTypeModifiers; private final Set asMemberModifiers; Kind(Set implicitFieldModifiers, Set implicitMethodModifiers, Set implicitTypeModifiers, Set asMemberModifiers) { this.implicitFieldModifiers = implicitFieldModifiers; this.implicitMethodModifiers = implicitMethodModifiers; this.implicitTypeModifiers = implicitTypeModifiers; this.asMemberModifiers = asMemberModifiers; } } public static final class Builder { private final Kind kind; private final String name; private final CodeBlock anonymousTypeArguments; private final CodeBlock.Builder javadoc = CodeBlock.builder(); private final List annotations = new ArrayList<>(); private final List modifiers = new ArrayList<>(); private final List typeVariables = new ArrayList<>(); private TypeName superclass = ClassName.OBJECT; private final List superinterfaces = new ArrayList<>(); private final Map enumConstants = new LinkedHashMap<>(); private final List fieldSpecs = new ArrayList<>(); private final CodeBlock.Builder staticBlock = CodeBlock.builder(); private final CodeBlock.Builder initializerBlock = CodeBlock.builder(); private final List methodSpecs = new ArrayList<>(); private final List typeSpecs = new ArrayList<>(); private final List originatingElements = new ArrayList<>(); private Builder(Kind kind, String name, CodeBlock anonymousTypeArguments) { checkArgument(name == null || SourceVersion.isName(name), "not a valid name: %s", name); this.kind = kind; this.name = name; this.anonymousTypeArguments = anonymousTypeArguments; } public Builder addJavadoc(String format, Object... args) { javadoc.add(format, args); return this; } public Builder addJavadoc(CodeBlock block) { javadoc.add(block); return this; } public Builder addAnnotations(Iterable annotationSpecs) { checkArgument(annotationSpecs != null, "annotationSpecs == null"); for (AnnotationSpec annotationSpec : annotationSpecs) { this.annotations.add(annotationSpec); } return this; } public Builder addAnnotation(AnnotationSpec annotationSpec) { checkNotNull(annotationSpec, "annotationSpec == null"); this.annotations.add(annotationSpec); return this; } public Builder addAnnotation(ClassName annotation) { return addAnnotation(AnnotationSpec.builder(annotation).build()); } public Builder addAnnotation(Class annotation) { return addAnnotation(ClassName.get(annotation)); } public Builder addModifiers(Modifier... modifiers) { checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); for (Modifier modifier : modifiers) { checkArgument(modifier != null, "modifiers contain null"); this.modifiers.add(modifier); } return this; } public Builder addTypeVariables(Iterable typeVariables) { checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); checkArgument(typeVariables != null, "typeVariables == null"); for (TypeVariableName typeVariable : typeVariables) { this.typeVariables.add(typeVariable); } return this; } public Builder addTypeVariable(TypeVariableName typeVariable) { checkState(anonymousTypeArguments == null, "forbidden on anonymous types."); typeVariables.add(typeVariable); return this; } public Builder superclass(TypeName superclass) { checkState(this.kind == Kind.CLASS, "only classes have super classes, not " + this.kind); checkState(this.superclass == ClassName.OBJECT, "superclass already set to " + this.superclass); checkArgument(!superclass.isPrimitive(), "superclass may not be a primitive"); this.superclass = superclass; return this; } public Builder superclass(Type superclass) { return superclass(TypeName.get(superclass)); } public Builder addSuperinterfaces(Iterable superinterfaces) { checkArgument(superinterfaces != null, "superinterfaces == null"); for (TypeName superinterface : superinterfaces) { addSuperinterface(superinterface); } return this; } public Builder addSuperinterface(TypeName superinterface) { checkArgument(superinterface != null, "superinterface == null"); this.superinterfaces.add(superinterface); return this; } public Builder addSuperinterface(Type superinterface) { return addSuperinterface(TypeName.get(superinterface)); } public Builder addEnumConstant(String name) { return addEnumConstant(name, anonymousClassBuilder("").build()); } public Builder addEnumConstant(String name, TypeSpec typeSpec) { checkState(kind == Kind.ENUM, "%s is not enum", this.name); checkArgument(typeSpec.anonymousTypeArguments != null, "enum constants must have anonymous type arguments"); checkArgument(SourceVersion.isName(name), "not a valid enum constant: %s", name); enumConstants.put(name, typeSpec); return this; } public Builder addFields(Iterable fieldSpecs) { checkArgument(fieldSpecs != null, "fieldSpecs == null"); for (FieldSpec fieldSpec : fieldSpecs) { addField(fieldSpec); } return this; } public Builder addField(FieldSpec fieldSpec) { if (kind == Kind.INTERFACE || kind == Kind.ANNOTATION) { requireExactlyOneOf(fieldSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE); Set check = EnumSet.of(Modifier.STATIC, Modifier.FINAL); checkState(fieldSpec.modifiers.containsAll(check), "%s %s.%s requires modifiers %s", kind, name, fieldSpec.name, check); } fieldSpecs.add(fieldSpec); return this; } public Builder addField(TypeName type, String name, Modifier... modifiers) { return addField(FieldSpec.builder(type, name, modifiers).build()); } public Builder addField(Type type, String name, Modifier... modifiers) { return addField(TypeName.get(type), name, modifiers); } public Builder addStaticBlock(CodeBlock block) { staticBlock.beginControlFlow("static").add(block).endControlFlow(); return this; } public Builder addInitializerBlock(CodeBlock block) { if ((kind != Kind.CLASS && kind != Kind.ENUM)) { throw new UnsupportedOperationException(kind + " can't have initializer blocks"); } initializerBlock.add("{\n") .indent() .add(block) .unindent() .add("}\n"); return this; } public Builder addMethods(Iterable methodSpecs) { checkArgument(methodSpecs != null, "methodSpecs == null"); for (MethodSpec methodSpec : methodSpecs) { addMethod(methodSpec); } return this; } public Builder addMethod(MethodSpec methodSpec) { if (kind == Kind.INTERFACE) { requireExactlyOneOf(methodSpec.modifiers, Modifier.ABSTRACT, Modifier.STATIC, Modifier.DEFAULT); requireExactlyOneOf(methodSpec.modifiers, Modifier.PUBLIC, Modifier.PRIVATE); } else if (kind == Kind.ANNOTATION) { checkState(methodSpec.modifiers.equals(kind.implicitMethodModifiers), "%s %s.%s requires modifiers %s", kind, name, methodSpec.name, kind.implicitMethodModifiers); } if (kind != Kind.ANNOTATION) { checkState(methodSpec.defaultValue == null, "%s %s.%s cannot have a default value", kind, name, methodSpec.name); } if (kind != Kind.INTERFACE) { checkState(!methodSpec.hasModifier(Modifier.DEFAULT), "%s %s.%s cannot be default", kind, name, methodSpec.name); } methodSpecs.add(methodSpec); return this; } public Builder addTypes(Iterable typeSpecs) { checkArgument(typeSpecs != null, "typeSpecs == null"); for (TypeSpec typeSpec : typeSpecs) { addType(typeSpec); } return this; } public Builder addType(TypeSpec typeSpec) { checkArgument(typeSpec.modifiers.containsAll(kind.implicitTypeModifiers), "%s %s.%s requires modifiers %s", kind, name, typeSpec.name, kind.implicitTypeModifiers); typeSpecs.add(typeSpec); return this; } public Builder addOriginatingElement(Element originatingElement) { originatingElements.add(originatingElement); return this; } public TypeSpec build() { checkArgument(kind != Kind.ENUM || !enumConstants.isEmpty(), "at least one enum constant is required for %s", name); boolean isAbstract = modifiers.contains(Modifier.ABSTRACT) || kind != Kind.CLASS; for (MethodSpec methodSpec : methodSpecs) { checkArgument(isAbstract || !methodSpec.hasModifier(Modifier.ABSTRACT), "non-abstract type %s cannot declare abstract method %s", name, methodSpec.name); } boolean superclassIsObject = superclass.equals(ClassName.OBJECT); int interestingSupertypeCount = (superclassIsObject ? 0 : 1) + superinterfaces.size(); checkArgument(anonymousTypeArguments == null || interestingSupertypeCount <= 1, "anonymous type has too many supertypes"); return new TypeSpec(this); } } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/TypeVariableName.java000066400000000000000000000144701335545476000263510ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import static com.squareup.javapoet.Util.checkArgument; import static com.squareup.javapoet.Util.checkNotNull; public final class TypeVariableName extends TypeName { public final String name; public final List bounds; private TypeVariableName(String name, List bounds) { this(name, bounds, new ArrayList<>()); } private TypeVariableName(String name, List bounds, List annotations) { super(annotations); this.name = checkNotNull(name, "name == null"); this.bounds = bounds; for (TypeName bound : this.bounds) { checkArgument(!bound.isPrimitive() && bound != VOID, "invalid bound: %s", bound); } } @Override public TypeVariableName annotated(List annotations) { return new TypeVariableName(name, bounds, annotations); } @Override public TypeName withoutAnnotations() { return new TypeVariableName(name, bounds); } public TypeVariableName withBounds(Type... bounds) { return withBounds(TypeName.list(bounds)); } public TypeVariableName withBounds(TypeName... bounds) { return withBounds(Arrays.asList(bounds)); } public TypeVariableName withBounds(List bounds) { ArrayList newBounds = new ArrayList<>(); newBounds.addAll(this.bounds); newBounds.addAll(bounds); return new TypeVariableName(name, newBounds, annotations); } private static TypeVariableName of(String name, List bounds) { // Strip java.lang.Object from bounds if it is present. List boundsNoObject = new ArrayList<>(bounds); boundsNoObject.remove(OBJECT); return new TypeVariableName(name, Collections.unmodifiableList(boundsNoObject)); } @Override CodeWriter emit(CodeWriter out) throws IOException { emitAnnotations(out); return out.emitAndIndent(name); } /** Returns type variable named {@code name} without bounds. */ public static TypeVariableName get(String name) { return TypeVariableName.of(name, Collections.emptyList()); } /** Returns type variable named {@code name} with {@code bounds}. */ public static TypeVariableName get(String name, TypeName... bounds) { return TypeVariableName.of(name, Arrays.asList(bounds)); } /** Returns type variable named {@code name} with {@code bounds}. */ public static TypeVariableName get(String name, Type... bounds) { return TypeVariableName.of(name, TypeName.list(bounds)); } /** Returns type variable equivalent to {@code mirror}. */ public static TypeVariableName get(TypeVariable mirror) { return get((TypeParameterElement) mirror.asElement()); } /** * Make a TypeVariableName for the given TypeMirror. This form is used internally to avoid * infinite recursion in cases like {@code Enum>}. When we encounter such a * thing, we will make a TypeVariableName without bounds and add that to the {@code typeVariables} * map before looking up the bounds. Then if we encounter this TypeVariable again while * constructing the bounds, we can just return it from the map. And, the code that put the entry * in {@code variables} will make sure that the bounds are filled in before returning. */ static TypeVariableName get( TypeVariable mirror, Map typeVariables) { TypeParameterElement element = (TypeParameterElement) mirror.asElement(); TypeVariableName typeVariableName = typeVariables.get(element); if (typeVariableName == null) { // Since the bounds field is public, we need to make it an unmodifiableList. But we control // the List that that wraps, which means we can change it before returning. List bounds = new ArrayList<>(); List visibleBounds = Collections.unmodifiableList(bounds); typeVariableName = new TypeVariableName(element.getSimpleName().toString(), visibleBounds); typeVariables.put(element, typeVariableName); for (TypeMirror typeMirror : element.getBounds()) { bounds.add(TypeName.get(typeMirror, typeVariables)); } bounds.remove(OBJECT); } return typeVariableName; } /** Returns type variable equivalent to {@code element}. */ public static TypeVariableName get(TypeParameterElement element) { String name = element.getSimpleName().toString(); List boundsMirrors = element.getBounds(); List boundsTypeNames = new ArrayList<>(); for (TypeMirror typeMirror : boundsMirrors) { boundsTypeNames.add(TypeName.get(typeMirror)); } return TypeVariableName.of(name, boundsTypeNames); } /** Returns type variable equivalent to {@code type}. */ public static TypeVariableName get(java.lang.reflect.TypeVariable type) { return get(type, new LinkedHashMap<>()); } /** @see #get(java.lang.reflect.TypeVariable, Map) */ static TypeVariableName get(java.lang.reflect.TypeVariable type, Map map) { TypeVariableName result = map.get(type); if (result == null) { List bounds = new ArrayList<>(); List visibleBounds = Collections.unmodifiableList(bounds); result = new TypeVariableName(type.getName(), visibleBounds); map.put(type, result); for (Type bound : type.getBounds()) { bounds.add(TypeName.get(bound, map)); } bounds.remove(OBJECT); } return result; } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/Util.java000066400000000000000000000111311335545476000240650ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; 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 static java.lang.Character.isISOControl; /** * Like Guava, but worse and standalone. This makes it easier to mix JavaPoet with libraries that * bring their own version of Guava. */ final class Util { private Util() { } static Map> immutableMultimap(Map> multimap) { LinkedHashMap> result = new LinkedHashMap<>(); for (Map.Entry> entry : multimap.entrySet()) { if (entry.getValue().isEmpty()) continue; result.put(entry.getKey(), immutableList(entry.getValue())); } return Collections.unmodifiableMap(result); } static Map immutableMap(Map map) { return Collections.unmodifiableMap(new LinkedHashMap<>(map)); } static void checkArgument(boolean condition, String format, Object... args) { if (!condition) throw new IllegalArgumentException(String.format(format, args)); } static T checkNotNull(T reference, String format, Object... args) { if (reference == null) throw new NullPointerException(String.format(format, args)); return reference; } static void checkState(boolean condition, String format, Object... args) { if (!condition) throw new IllegalStateException(String.format(format, args)); } static List immutableList(Collection collection) { return Collections.unmodifiableList(new ArrayList<>(collection)); } static Set immutableSet(Collection set) { return Collections.unmodifiableSet(new LinkedHashSet<>(set)); } static Set union(Set a, Set b) { Set result = new LinkedHashSet<>(); result.addAll(a); result.addAll(b); return result; } static void requireExactlyOneOf(Set modifiers, Modifier... mutuallyExclusive) { int count = 0; for (Modifier modifier : mutuallyExclusive) { if (modifiers.contains(modifier)) count++; } checkArgument(count == 1, "modifiers %s must contain one of %s", modifiers, Arrays.toString(mutuallyExclusive)); } static String characterLiteralWithoutSingleQuotes(char c) { // see https://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.6 switch (c) { case '\b': return "\\b"; /* \u0008: backspace (BS) */ case '\t': return "\\t"; /* \u0009: horizontal tab (HT) */ case '\n': return "\\n"; /* \u000a: linefeed (LF) */ case '\f': return "\\f"; /* \u000c: form feed (FF) */ case '\r': return "\\r"; /* \u000d: carriage return (CR) */ case '\"': return "\""; /* \u0022: double quote (") */ case '\'': return "\\'"; /* \u0027: single quote (') */ case '\\': return "\\\\"; /* \u005c: backslash (\) */ default: return isISOControl(c) ? String.format("\\u%04x", (int) c) : Character.toString(c); } } /** Returns the string literal representing {@code value}, including wrapping double quotes. */ static String stringLiteralWithDoubleQuotes(String value, String indent) { StringBuilder result = new StringBuilder(value.length() + 2); result.append('"'); for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); // trivial case: single quote must not be escaped if (c == '\'') { result.append("'"); continue; } // trivial case: double quotes must be escaped if (c == '\"') { result.append("\\\""); continue; } // default case: just let character literal do its work result.append(characterLiteralWithoutSingleQuotes(c)); // need to append indent after linefeed? if (c == '\n' && i + 1 < value.length()) { result.append("\"\n").append(indent).append(indent).append("+ \""); } } result.append('"'); return result.toString(); } } javapoet-1.11.1/src/main/java/com/squareup/javapoet/WildcardTypeName.java000066400000000000000000000111521335545476000263470ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.IOException; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; import static com.squareup.javapoet.Util.checkArgument; public final class WildcardTypeName extends TypeName { public final List upperBounds; public final List lowerBounds; private WildcardTypeName(List upperBounds, List lowerBounds) { this(upperBounds, lowerBounds, new ArrayList<>()); } private WildcardTypeName(List upperBounds, List lowerBounds, List annotations) { super(annotations); this.upperBounds = Util.immutableList(upperBounds); this.lowerBounds = Util.immutableList(lowerBounds); checkArgument(this.upperBounds.size() == 1, "unexpected extends bounds: %s", upperBounds); for (TypeName upperBound : this.upperBounds) { checkArgument(!upperBound.isPrimitive() && upperBound != VOID, "invalid upper bound: %s", upperBound); } for (TypeName lowerBound : this.lowerBounds) { checkArgument(!lowerBound.isPrimitive() && lowerBound != VOID, "invalid lower bound: %s", lowerBound); } } @Override public WildcardTypeName annotated(List annotations) { return new WildcardTypeName(upperBounds, lowerBounds, concatAnnotations(annotations)); } @Override public TypeName withoutAnnotations() { return new WildcardTypeName(upperBounds, lowerBounds); } @Override CodeWriter emit(CodeWriter out) throws IOException { if (lowerBounds.size() == 1) { return out.emit("? super $T", lowerBounds.get(0)); } return upperBounds.get(0).equals(TypeName.OBJECT) ? out.emit("?") : out.emit("? extends $T", upperBounds.get(0)); } /** * Returns a type that represents an unknown type that extends {@code bound}. For example, if * {@code bound} is {@code CharSequence.class}, this returns {@code ? extends CharSequence}. If * {@code bound} is {@code Object.class}, this returns {@code ?}, which is shorthand for {@code * ? extends Object}. */ public static WildcardTypeName subtypeOf(TypeName upperBound) { return new WildcardTypeName(Collections.singletonList(upperBound), Collections.emptyList()); } public static WildcardTypeName subtypeOf(Type upperBound) { return subtypeOf(TypeName.get(upperBound)); } /** * Returns a type that represents an unknown supertype of {@code bound}. For example, if {@code * bound} is {@code String.class}, this returns {@code ? super String}. */ public static WildcardTypeName supertypeOf(TypeName lowerBound) { return new WildcardTypeName(Collections.singletonList(OBJECT), Collections.singletonList(lowerBound)); } public static WildcardTypeName supertypeOf(Type lowerBound) { return supertypeOf(TypeName.get(lowerBound)); } public static TypeName get(javax.lang.model.type.WildcardType mirror) { return get(mirror, new LinkedHashMap<>()); } static TypeName get( javax.lang.model.type.WildcardType mirror, Map typeVariables) { TypeMirror extendsBound = mirror.getExtendsBound(); if (extendsBound == null) { TypeMirror superBound = mirror.getSuperBound(); if (superBound == null) { return subtypeOf(Object.class); } else { return supertypeOf(TypeName.get(superBound, typeVariables)); } } else { return subtypeOf(TypeName.get(extendsBound, typeVariables)); } } public static TypeName get(WildcardType wildcardName) { return get(wildcardName, new LinkedHashMap<>()); } static TypeName get(WildcardType wildcardName, Map map) { return new WildcardTypeName( list(wildcardName.getUpperBounds(), map), list(wildcardName.getLowerBounds(), map)); } } javapoet-1.11.1/src/test/000077500000000000000000000000001335545476000151665ustar00rootroot00000000000000javapoet-1.11.1/src/test/java/000077500000000000000000000000001335545476000161075ustar00rootroot00000000000000javapoet-1.11.1/src/test/java/com/000077500000000000000000000000001335545476000166655ustar00rootroot00000000000000javapoet-1.11.1/src/test/java/com/squareup/000077500000000000000000000000001335545476000205325ustar00rootroot00000000000000javapoet-1.11.1/src/test/java/com/squareup/javapoet/000077500000000000000000000000001335545476000223435ustar00rootroot00000000000000javapoet-1.11.1/src/test/java/com/squareup/javapoet/AbstractTypesTest.java000066400000000000000000000266411335545476000266470ustar00rootroot00000000000000/* * Copyright (C) 2014 Google, 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. */ package com.squareup.javapoet; import static com.google.common.truth.Truth.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import static javax.lang.model.util.ElementFilter.fieldsIn; import static org.junit.Assert.*; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import java.io.Serializable; import java.lang.annotation.Annotation; import java.nio.charset.Charset; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ErrorType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVisitor; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.JavaFileObject; import org.junit.Test; public abstract class AbstractTypesTest { protected abstract Elements getElements(); protected abstract Types getTypes(); private TypeElement getElement(Class clazz) { return getElements().getTypeElement(clazz.getCanonicalName()); } private TypeMirror getMirror(Class clazz) { return getElement(clazz).asType(); } @Test public void getBasicTypeMirror() { assertThat(TypeName.get(getMirror(Object.class))) .isEqualTo(ClassName.get(Object.class)); assertThat(TypeName.get(getMirror(Charset.class))) .isEqualTo(ClassName.get(Charset.class)); assertThat(TypeName.get(getMirror(AbstractTypesTest.class))) .isEqualTo(ClassName.get(AbstractTypesTest.class)); } @Test public void getParameterizedTypeMirror() { DeclaredType setType = getTypes().getDeclaredType(getElement(Set.class), getMirror(Object.class)); assertThat(TypeName.get(setType)) .isEqualTo(ParameterizedTypeName.get(ClassName.get(Set.class), ClassName.OBJECT)); } @Test public void errorTypes() { JavaFileObject hasErrorTypes = JavaFileObjects.forSourceLines( "com.squareup.tacos.ErrorTypes", "package com.squareup.tacos;", "", "@SuppressWarnings(\"hook-into-compiler\")", "class ErrorTypes {", " Tacos tacos;", " Ingredients.Guacamole guacamole;", "}"); Compilation compilation = javac().withProcessors(new AbstractProcessor() { @Override public boolean process(Set set, RoundEnvironment roundEnvironment) { TypeElement classFile = processingEnv.getElementUtils().getTypeElement("com.squareup.tacos.ErrorTypes"); List fields = fieldsIn(classFile.getEnclosedElements()); ErrorType topLevel = (ErrorType) fields.get(0).asType(); ErrorType member = (ErrorType) fields.get(1).asType(); assertThat(TypeName.get(topLevel)).isEqualTo(ClassName.get("", "Tacos")); assertThat(TypeName.get(member)).isEqualTo(ClassName.get("Ingredients", "Guacamole")); return false; } @Override public Set getSupportedAnnotationTypes() { return Collections.singleton("*"); } }).compile(hasErrorTypes); assertThat(compilation).failed(); } static class Parameterized< Simple, ExtendsClass extends Number, ExtendsInterface extends Runnable, ExtendsTypeVariable extends Simple, Intersection extends Number & Runnable, IntersectionOfInterfaces extends Runnable & Serializable> {} @Test public void getTypeVariableTypeMirror() { List typeVariables = getElement(Parameterized.class).getTypeParameters(); // Members of converted types use ClassName and not Class. ClassName number = ClassName.get(Number.class); ClassName runnable = ClassName.get(Runnable.class); ClassName serializable = ClassName.get(Serializable.class); assertThat(TypeName.get(typeVariables.get(0).asType())) .isEqualTo(TypeVariableName.get("Simple")); assertThat(TypeName.get(typeVariables.get(1).asType())) .isEqualTo(TypeVariableName.get("ExtendsClass", number)); assertThat(TypeName.get(typeVariables.get(2).asType())) .isEqualTo(TypeVariableName.get("ExtendsInterface", runnable)); assertThat(TypeName.get(typeVariables.get(3).asType())) .isEqualTo(TypeVariableName.get("ExtendsTypeVariable", TypeVariableName.get("Simple"))); assertThat(TypeName.get(typeVariables.get(4).asType())) .isEqualTo(TypeVariableName.get("Intersection", number, runnable)); assertThat(TypeName.get(typeVariables.get(5).asType())) .isEqualTo(TypeVariableName.get("IntersectionOfInterfaces", runnable, serializable)); assertThat(((TypeVariableName) TypeName.get(typeVariables.get(4).asType())).bounds) .containsExactly(number, runnable); } static class Recursive, Set>> {} @Test public void getTypeVariableTypeMirrorRecursive() { TypeMirror typeMirror = getElement(Recursive.class).asType(); ParameterizedTypeName typeName = (ParameterizedTypeName) TypeName.get(typeMirror); String className = Recursive.class.getCanonicalName(); assertThat(typeName.toString()).isEqualTo(className + ""); TypeVariableName typeVariableName = (TypeVariableName) typeName.typeArguments.get(0); try { typeVariableName.bounds.set(0, null); fail("Expected UnsupportedOperationException"); } catch (UnsupportedOperationException expected) { } assertThat(typeVariableName.toString()).isEqualTo("T"); assertThat(typeVariableName.bounds.toString()) .isEqualTo("[java.util.Map, java.util.Set>]"); } @Test public void getPrimitiveTypeMirror() { assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.BOOLEAN))) .isEqualTo(TypeName.BOOLEAN); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.BYTE))) .isEqualTo(TypeName.BYTE); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.SHORT))) .isEqualTo(TypeName.SHORT); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.INT))) .isEqualTo(TypeName.INT); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.LONG))) .isEqualTo(TypeName.LONG); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.CHAR))) .isEqualTo(TypeName.CHAR); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.FLOAT))) .isEqualTo(TypeName.FLOAT); assertThat(TypeName.get(getTypes().getPrimitiveType(TypeKind.DOUBLE))) .isEqualTo(TypeName.DOUBLE); } @Test public void getArrayTypeMirror() { assertThat(TypeName.get(getTypes().getArrayType(getMirror(Object.class)))) .isEqualTo(ArrayTypeName.of(ClassName.OBJECT)); } @Test public void getVoidTypeMirror() { assertThat(TypeName.get(getTypes().getNoType(TypeKind.VOID))) .isEqualTo(TypeName.VOID); } @Test public void getNullTypeMirror() { try { TypeName.get(getTypes().getNullType()); fail(); } catch (IllegalArgumentException expected) { } } @Test public void parameterizedType() throws Exception { ParameterizedTypeName type = ParameterizedTypeName.get(Map.class, String.class, Long.class); assertThat(type.toString()).isEqualTo("java.util.Map"); } @Test public void arrayType() throws Exception { ArrayTypeName type = ArrayTypeName.of(String.class); assertThat(type.toString()).isEqualTo("java.lang.String[]"); } @Test public void wildcardExtendsType() throws Exception { WildcardTypeName type = WildcardTypeName.subtypeOf(CharSequence.class); assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence"); } @Test public void wildcardExtendsObject() throws Exception { WildcardTypeName type = WildcardTypeName.subtypeOf(Object.class); assertThat(type.toString()).isEqualTo("?"); } @Test public void wildcardSuperType() throws Exception { WildcardTypeName type = WildcardTypeName.supertypeOf(String.class); assertThat(type.toString()).isEqualTo("? super java.lang.String"); } @Test public void wildcardMirrorNoBounds() throws Exception { WildcardType wildcard = getTypes().getWildcardType(null, null); TypeName type = TypeName.get(wildcard); assertThat(type.toString()).isEqualTo("?"); } @Test public void wildcardMirrorExtendsType() throws Exception { Types types = getTypes(); Elements elements = getElements(); TypeMirror charSequence = elements.getTypeElement(CharSequence.class.getName()).asType(); WildcardType wildcard = types.getWildcardType(charSequence, null); TypeName type = TypeName.get(wildcard); assertThat(type.toString()).isEqualTo("? extends java.lang.CharSequence"); } @Test public void wildcardMirrorSuperType() throws Exception { Types types = getTypes(); Elements elements = getElements(); TypeMirror string = elements.getTypeElement(String.class.getName()).asType(); WildcardType wildcard = types.getWildcardType(null, string); TypeName type = TypeName.get(wildcard); assertThat(type.toString()).isEqualTo("? super java.lang.String"); } @Test public void typeVariable() throws Exception { TypeVariableName type = TypeVariableName.get("T", CharSequence.class); assertThat(type.toString()).isEqualTo("T"); // (Bounds are only emitted in declaration.) } @Test public void box() throws Exception { assertThat(TypeName.INT.box()).isEqualTo(ClassName.get(Integer.class)); assertThat(TypeName.VOID.box()).isEqualTo(ClassName.get(Void.class)); assertThat(ClassName.get(Integer.class).box()).isEqualTo(ClassName.get(Integer.class)); assertThat(ClassName.get(Void.class).box()).isEqualTo(ClassName.get(Void.class)); assertThat(TypeName.OBJECT.box()).isEqualTo(TypeName.OBJECT); assertThat(ClassName.get(String.class).box()).isEqualTo(ClassName.get(String.class)); } @Test public void unbox() throws Exception { assertThat(TypeName.INT).isEqualTo(TypeName.INT.unbox()); assertThat(TypeName.VOID).isEqualTo(TypeName.VOID.unbox()); assertThat(ClassName.get(Integer.class).unbox()).isEqualTo(TypeName.INT.unbox()); assertThat(ClassName.get(Void.class).unbox()).isEqualTo(TypeName.VOID.unbox()); try { TypeName.OBJECT.unbox(); fail(); } catch (UnsupportedOperationException expected) { } try { ClassName.get(String.class).unbox(); fail(); } catch (UnsupportedOperationException expected) { } } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/AnnotatedTypeNameTest.java000066400000000000000000000215651335545476000274370ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.util.List; import java.util.Map; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; public class AnnotatedTypeNameTest { private final static String NN = NeverNull.class.getCanonicalName(); private final AnnotationSpec NEVER_NULL = AnnotationSpec.builder(NeverNull.class).build(); private final static String TUA = TypeUseAnnotation.class.getCanonicalName(); private final AnnotationSpec TYPE_USE_ANNOTATION = AnnotationSpec.builder(TypeUseAnnotation.class).build(); @Target(ElementType.TYPE_USE) public @interface NeverNull {} @Target(ElementType.TYPE_USE) public @interface TypeUseAnnotation {} @Test(expected=NullPointerException.class) public void nullAnnotationArray() { TypeName.BOOLEAN.annotated((AnnotationSpec[]) null); } @Test(expected=NullPointerException.class) public void nullAnnotationList() { TypeName.DOUBLE.annotated((List) null); } @Test public void annotated() { TypeName simpleString = TypeName.get(String.class); assertFalse(simpleString.isAnnotated()); assertEquals(simpleString, TypeName.get(String.class)); TypeName annotated = simpleString.annotated(NEVER_NULL); assertTrue(annotated.isAnnotated()); assertEquals(annotated, annotated.annotated()); } @Test public void annotatedType() { TypeName type = TypeName.get(String.class); TypeName actual = type.annotated(TYPE_USE_ANNOTATION); assertThat(actual.toString()).isEqualTo("java.lang. @" + TUA + " String"); } @Test public void annotatedTwice() { TypeName type = TypeName.get(String.class); TypeName actual = type.annotated(NEVER_NULL) .annotated(TYPE_USE_ANNOTATION); assertThat(actual.toString()) .isEqualTo("java.lang. @" + NN + " @" + TUA + " String"); } @Test public void annotatedParameterizedType() { TypeName type = ParameterizedTypeName.get(List.class, String.class); TypeName actual = type.annotated(TYPE_USE_ANNOTATION); assertThat(actual.toString()).isEqualTo("java.util. @" + TUA + " List"); } @Test public void annotatedArgumentOfParameterizedType() { TypeName type = TypeName.get(String.class).annotated(TYPE_USE_ANNOTATION); TypeName actual = ParameterizedTypeName.get(ClassName.get(List.class), type); assertThat(actual.toString()).isEqualTo("java.util.List"); } @Test public void annotatedWildcardTypeNameWithSuper() { TypeName type = TypeName.get(String.class).annotated(TYPE_USE_ANNOTATION); TypeName actual = WildcardTypeName.supertypeOf(type); assertThat(actual.toString()).isEqualTo("? super java.lang. @" + TUA + " String"); } @Test public void annotatedWildcardTypeNameWithExtends() { TypeName type = TypeName.get(String.class).annotated(TYPE_USE_ANNOTATION); TypeName actual = WildcardTypeName.subtypeOf(type); assertThat(actual.toString()).isEqualTo("? extends java.lang. @" + TUA + " String"); } @Test public void annotatedEquivalence() { annotatedEquivalence(TypeName.VOID); annotatedEquivalence(ArrayTypeName.get(Object[].class)); annotatedEquivalence(ClassName.get(Object.class)); annotatedEquivalence(ParameterizedTypeName.get(List.class, Object.class)); annotatedEquivalence(TypeVariableName.get(Object.class)); annotatedEquivalence(WildcardTypeName.get(Object.class)); } private void annotatedEquivalence(TypeName type) { assertFalse(type.isAnnotated()); assertEquals(type, type); assertEquals(type.annotated(TYPE_USE_ANNOTATION), type.annotated(TYPE_USE_ANNOTATION)); assertNotEquals(type, type.annotated(TYPE_USE_ANNOTATION)); assertEquals(type.hashCode(), type.hashCode()); assertEquals(type.annotated(TYPE_USE_ANNOTATION).hashCode(), type.annotated(TYPE_USE_ANNOTATION).hashCode()); assertNotEquals(type.hashCode(), type.annotated(TYPE_USE_ANNOTATION).hashCode()); } // https://github.com/square/javapoet/issues/431 @Test public void annotatedNestedType() { TypeName type = TypeName.get(Map.Entry.class).annotated(TYPE_USE_ANNOTATION); assertThat(type.toString()).isEqualTo("java.util.Map. @" + TUA + " Entry"); } @Test public void annotatedEnclosingAndNestedType() { TypeName type = ((ClassName) TypeName.get(Map.class).annotated(TYPE_USE_ANNOTATION)) .nestedClass("Entry").annotated(TYPE_USE_ANNOTATION); assertThat(type.toString()).isEqualTo("java.util. @" + TUA + " Map. @" + TUA + " Entry"); } // https://github.com/square/javapoet/issues/431 @Test public void annotatedNestedParameterizedType() { TypeName type = ParameterizedTypeName.get(Map.Entry.class, Byte.class, Byte.class) .annotated(TYPE_USE_ANNOTATION); assertThat(type.toString()) .isEqualTo("java.util.Map. @" + TUA + " Entry"); } @Test public void withoutAnnotationsOnAnnotatedEnclosingAndNestedType() { TypeName type = ((ClassName) TypeName.get(Map.class).annotated(TYPE_USE_ANNOTATION)) .nestedClass("Entry").annotated(TYPE_USE_ANNOTATION); assertThat(type.isAnnotated()).isTrue(); assertThat(type.withoutAnnotations()).isEqualTo(TypeName.get(Map.Entry.class)); } @Test public void withoutAnnotationsOnAnnotatedEnclosingType() { TypeName type = ((ClassName) TypeName.get(Map.class).annotated(TYPE_USE_ANNOTATION)) .nestedClass("Entry"); assertThat(type.isAnnotated()).isTrue(); assertThat(type.withoutAnnotations()).isEqualTo(TypeName.get(Map.Entry.class)); } @Test public void withoutAnnotationsOnAnnotatedNestedType() { TypeName type = ((ClassName) TypeName.get(Map.class)) .nestedClass("Entry").annotated(TYPE_USE_ANNOTATION); assertThat(type.isAnnotated()).isTrue(); assertThat(type.withoutAnnotations()).isEqualTo(TypeName.get(Map.Entry.class)); } // https://github.com/square/javapoet/issues/614 @Test public void annotatedArrayType() { TypeName type = ArrayTypeName.of(ClassName.get(Object.class)).annotated(TYPE_USE_ANNOTATION); assertThat(type.toString()).isEqualTo("java.lang.Object @" + TUA + " []"); } @Test public void annotatedArrayElementType() { TypeName type = ArrayTypeName.of(ClassName.get(Object.class).annotated(TYPE_USE_ANNOTATION)); assertThat(type.toString()).isEqualTo("java.lang. @" + TUA + " Object[]"); } // https://github.com/square/javapoet/issues/614 @Test public void annotatedOuterMultidimensionalArrayType() { TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class))) .annotated(TYPE_USE_ANNOTATION); assertThat(type.toString()).isEqualTo("java.lang.Object @" + TUA + " [][]"); } // https://github.com/square/javapoet/issues/614 @Test public void annotatedInnerMultidimensionalArrayType() { TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class)) .annotated(TYPE_USE_ANNOTATION)); assertThat(type.toString()).isEqualTo("java.lang.Object[] @" + TUA + " []"); } // https://github.com/square/javapoet/issues/614 @Test public void annotatedArrayTypeVarargsParameter() { TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class))) .annotated(TYPE_USE_ANNOTATION); MethodSpec varargsMethod = MethodSpec.methodBuilder("m") .addParameter( ParameterSpec.builder(type, "p") .build()) .varargs() .build(); assertThat(varargsMethod.toString()).isEqualTo("" + "void m(java.lang.Object @" + TUA + " []... p) {\n" + "}\n"); } // https://github.com/square/javapoet/issues/614 @Test public void annotatedArrayTypeInVarargsParameter() { TypeName type = ArrayTypeName.of(ArrayTypeName.of(ClassName.get(Object.class)) .annotated(TYPE_USE_ANNOTATION)); MethodSpec varargsMethod = MethodSpec.methodBuilder("m") .addParameter( ParameterSpec.builder(type, "p") .build()) .varargs() .build(); assertThat(varargsMethod.toString()).isEqualTo("" + "void m(java.lang.Object[] @" + TUA + " ... p) {\n" + "}\n"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/AnnotationSpecTest.java000066400000000000000000000324221335545476000267760ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import com.google.testing.compile.CompilationRule; import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import javax.lang.model.element.TypeElement; import org.junit.Rule; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; public final class AnnotationSpecTest { @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationA { } @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationB { } @Retention(RetentionPolicy.RUNTIME) public @interface AnnotationC { String value(); } public enum Breakfast { WAFFLES, PANCAKES; public String toString() { return name() + " with cherries!"; }; } @Retention(RetentionPolicy.RUNTIME) public @interface HasDefaultsAnnotation { byte a() default 5; short b() default 6; int c() default 7; long d() default 8; float e() default 9.0f; double f() default 10.0; char[] g() default {0, 0xCAFE, 'z', '€', 'ℕ', '"', '\'', '\t', '\n'}; boolean h() default true; Breakfast i() default Breakfast.WAFFLES; AnnotationA j() default @AnnotationA(); String k() default "maple"; Class l() default AnnotationB.class; int[] m() default {1, 2, 3}; Breakfast[] n() default {Breakfast.WAFFLES, Breakfast.PANCAKES}; Breakfast o(); int p(); AnnotationC q() default @AnnotationC("foo"); Class[] r() default {Byte.class, Short.class, Integer.class, Long.class}; } @HasDefaultsAnnotation( o = Breakfast.PANCAKES, p = 1701, f = 11.1, m = {9, 8, 1}, l = Override.class, j = @AnnotationA, q = @AnnotationC("bar"), r = {Float.class, Double.class}) public class IsAnnotated { // empty } @Rule public final CompilationRule compilation = new CompilationRule(); @Test public void equalsAndHashCode() { AnnotationSpec a = AnnotationSpec.builder(AnnotationC.class).build(); AnnotationSpec b = AnnotationSpec.builder(AnnotationC.class).build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = AnnotationSpec.builder(AnnotationC.class).addMember("value", "$S", "123").build(); b = AnnotationSpec.builder(AnnotationC.class).addMember("value", "$S", "123").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void defaultAnnotation() { String name = IsAnnotated.class.getCanonicalName(); TypeElement element = compilation.getElements().getTypeElement(name); AnnotationSpec annotation = AnnotationSpec.get(element.getAnnotationMirrors().get(0)); TypeSpec taco = TypeSpec.classBuilder("Taco") .addAnnotation(annotation) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.javapoet.AnnotationSpecTest;\n" + "import java.lang.Double;\n" + "import java.lang.Float;\n" + "import java.lang.Override;\n" + "\n" + "@AnnotationSpecTest.HasDefaultsAnnotation(\n" + " o = AnnotationSpecTest.Breakfast.PANCAKES,\n" + " p = 1701,\n" + " f = 11.1,\n" + " m = {\n" + " 9,\n" + " 8,\n" + " 1\n" + " },\n" + " l = Override.class,\n" + " j = @AnnotationSpecTest.AnnotationA,\n" + " q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n" + " r = {\n" + " Float.class,\n" + " Double.class\n" + " }\n" + ")\n" + "class Taco {\n" + "}\n"); } @Test public void defaultAnnotationWithImport() { String name = IsAnnotated.class.getCanonicalName(); TypeElement element = compilation.getElements().getTypeElement(name); AnnotationSpec annotation = AnnotationSpec.get(element.getAnnotationMirrors().get(0)); TypeSpec.Builder typeBuilder = TypeSpec.classBuilder(IsAnnotated.class.getSimpleName()); typeBuilder.addAnnotation(annotation); JavaFile file = JavaFile.builder("com.squareup.javapoet", typeBuilder.build()).build(); assertThat(file.toString()).isEqualTo( "package com.squareup.javapoet;\n" + "\n" + "import java.lang.Double;\n" + "import java.lang.Float;\n" + "import java.lang.Override;\n" + "\n" + "@AnnotationSpecTest.HasDefaultsAnnotation(\n" + " o = AnnotationSpecTest.Breakfast.PANCAKES,\n" + " p = 1701,\n" + " f = 11.1,\n" + " m = {\n" + " 9,\n" + " 8,\n" + " 1\n" + " },\n" + " l = Override.class,\n" + " j = @AnnotationSpecTest.AnnotationA,\n" + " q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n" + " r = {\n" + " Float.class,\n" + " Double.class\n" + " }\n" + ")\n" + "class IsAnnotated {\n" + "}\n" ); } @Test public void emptyArray() { AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class); builder.addMember("n", "$L", "{}"); assertThat(builder.build().toString()).isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = {}" + ")"); builder.addMember("m", "$L", "{}"); assertThat(builder.build().toString()) .isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = {}, m = {}" + ")"); } @Test public void dynamicArrayOfEnumConstants() { AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class); builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.PANCAKES.name()); assertThat(builder.build().toString()).isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + ")"); // builder = AnnotationSpec.builder(HasDefaultsAnnotation.class); builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.WAFFLES.name()); builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.PANCAKES.name()); assertThat(builder.build().toString()).isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = {" + "com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + "})"); builder = builder.build().toBuilder(); // idempotent assertThat(builder.build().toString()).isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = {" + "com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + "})"); builder.addMember("n", "$T.$L", Breakfast.class, Breakfast.WAFFLES.name()); assertThat(builder.build().toString()).isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "n = {" + "com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + ", com.squareup.javapoet.AnnotationSpecTest.Breakfast.WAFFLES" + "})"); } @Test public void defaultAnnotationToBuilder() { String name = IsAnnotated.class.getCanonicalName(); TypeElement element = compilation.getElements().getTypeElement(name); AnnotationSpec.Builder builder = AnnotationSpec.get(element.getAnnotationMirrors().get(0)) .toBuilder(); builder.addMember("m", "$L", 123); assertThat(builder.build().toString()).isEqualTo( "@com.squareup.javapoet.AnnotationSpecTest.HasDefaultsAnnotation(" + "o = com.squareup.javapoet.AnnotationSpecTest.Breakfast.PANCAKES" + ", p = 1701" + ", f = 11.1" + ", m = {9, 8, 1, 123}" + ", l = java.lang.Override.class" + ", j = @com.squareup.javapoet.AnnotationSpecTest.AnnotationA" + ", q = @com.squareup.javapoet.AnnotationSpecTest.AnnotationC(\"bar\")" + ", r = {java.lang.Float.class, java.lang.Double.class}" + ")"); } @Test public void reflectAnnotation() { HasDefaultsAnnotation annotation = IsAnnotated.class.getAnnotation(HasDefaultsAnnotation.class); AnnotationSpec spec = AnnotationSpec.get(annotation); TypeSpec taco = TypeSpec.classBuilder("Taco") .addAnnotation(spec) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.javapoet.AnnotationSpecTest;\n" + "import java.lang.Double;\n" + "import java.lang.Float;\n" + "import java.lang.Override;\n" + "\n" + "@AnnotationSpecTest.HasDefaultsAnnotation(\n" + " f = 11.1,\n" + " l = Override.class,\n" + " m = {\n" + " 9,\n" + " 8,\n" + " 1\n" + " },\n" + " o = AnnotationSpecTest.Breakfast.PANCAKES,\n" + " p = 1701,\n" + " q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n" + " r = {\n" + " Float.class,\n" + " Double.class\n" + " }\n" + ")\n" + "class Taco {\n" + "}\n"); } @Test public void reflectAnnotationWithDefaults() { HasDefaultsAnnotation annotation = IsAnnotated.class.getAnnotation(HasDefaultsAnnotation.class); AnnotationSpec spec = AnnotationSpec.get(annotation, true); TypeSpec taco = TypeSpec.classBuilder("Taco") .addAnnotation(spec) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.javapoet.AnnotationSpecTest;\n" + "import java.lang.Double;\n" + "import java.lang.Float;\n" + "import java.lang.Override;\n" + "\n" + "@AnnotationSpecTest.HasDefaultsAnnotation(\n" + " a = 5,\n" + " b = 6,\n" + " c = 7,\n" + " d = 8,\n" + " e = 9.0f,\n" + " f = 11.1,\n" + " g = {\n" + " '\\u0000',\n" + " '쫾',\n" + " 'z',\n" + " '€',\n" + " 'ℕ',\n" + " '\"',\n" + " '\\'',\n" + " '\\t',\n" + " '\\n'\n" + " },\n" + " h = true,\n" + " i = AnnotationSpecTest.Breakfast.WAFFLES,\n" + " j = @AnnotationSpecTest.AnnotationA,\n" + " k = \"maple\",\n" + " l = Override.class,\n" + " m = {\n" + " 9,\n" + " 8,\n" + " 1\n" + " },\n" + " n = {\n" + " AnnotationSpecTest.Breakfast.WAFFLES,\n" + " AnnotationSpecTest.Breakfast.PANCAKES\n" + " },\n" + " o = AnnotationSpecTest.Breakfast.PANCAKES,\n" + " p = 1701,\n" + " q = @AnnotationSpecTest.AnnotationC(\"bar\"),\n" + " r = {\n" + " Float.class,\n" + " Double.class\n" + " }\n" + ")\n" + "class Taco {\n" + "}\n"); } @Test public void disallowsNullMemberName() { AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class); try { AnnotationSpec.Builder $L = builder.addMember(null, "$L", ""); fail($L.build().toString()); } catch (NullPointerException e) { assertThat(e).hasMessageThat().isEqualTo("name == null"); } } @Test public void requiresValidMemberName() { AnnotationSpec.Builder builder = AnnotationSpec.builder(HasDefaultsAnnotation.class); try { AnnotationSpec.Builder $L = builder.addMember("@", "$L", ""); fail($L.build().toString()); } catch (IllegalArgumentException e) { assertThat(e).hasMessageThat().isEqualTo("not a valid name: @"); } } private String toString(TypeSpec typeSpec) { return JavaFile.builder("com.squareup.tacos", typeSpec).build().toString(); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/ClassNameTest.java000066400000000000000000000176551335545476000257320ustar00rootroot00000000000000/* * Copyright (C) 2014 Google, 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. */ package com.squareup.javapoet; import com.google.testing.compile.CompilationRule; import java.util.Map; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mockito; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; @RunWith(JUnit4.class) public final class ClassNameTest { @Rule public CompilationRule compilationRule = new CompilationRule(); @Test public void bestGuessForString_simpleClass() { assertThat(ClassName.bestGuess(String.class.getName())) .isEqualTo(ClassName.get("java.lang", "String")); } @Test public void bestGuessNonAscii() { ClassName className = ClassName.bestGuess( "com.\ud835\udc1andro\ud835\udc22d.\ud835\udc00ctiv\ud835\udc22ty"); assertEquals("com.\ud835\udc1andro\ud835\udc22d", className.packageName()); assertEquals("\ud835\udc00ctiv\ud835\udc22ty", className.simpleName()); } static class OuterClass { static class InnerClass {} } @Test public void bestGuessForString_nestedClass() { assertThat(ClassName.bestGuess(Map.Entry.class.getCanonicalName())) .isEqualTo(ClassName.get("java.util", "Map", "Entry")); assertThat(ClassName.bestGuess(OuterClass.InnerClass.class.getCanonicalName())) .isEqualTo(ClassName.get("com.squareup.javapoet", "ClassNameTest", "OuterClass", "InnerClass")); } @Test public void bestGuessForString_defaultPackage() { assertThat(ClassName.bestGuess("SomeClass")) .isEqualTo(ClassName.get("", "SomeClass")); assertThat(ClassName.bestGuess("SomeClass.Nested")) .isEqualTo(ClassName.get("", "SomeClass", "Nested")); assertThat(ClassName.bestGuess("SomeClass.Nested.EvenMore")) .isEqualTo(ClassName.get("", "SomeClass", "Nested", "EvenMore")); } @Test public void bestGuessForString_confusingInput() { assertBestGuessThrows(""); assertBestGuessThrows("."); assertBestGuessThrows(".Map"); assertBestGuessThrows("java"); assertBestGuessThrows("java.util"); assertBestGuessThrows("java.util."); assertBestGuessThrows("java..util.Map.Entry"); assertBestGuessThrows("java.util..Map.Entry"); assertBestGuessThrows("java.util.Map..Entry"); assertBestGuessThrows("com.test.$"); assertBestGuessThrows("com.test.LooksLikeAClass.pkg"); assertBestGuessThrows("!@#$gibberish%^&*"); } private void assertBestGuessThrows(String s) { try { ClassName.bestGuess(s); fail(); } catch (IllegalArgumentException expected) { } } @Test public void createNestedClass() { ClassName foo = ClassName.get("com.example", "Foo"); ClassName bar = foo.nestedClass("Bar"); assertThat(bar).isEqualTo(ClassName.get("com.example", "Foo", "Bar")); ClassName baz = bar.nestedClass("Baz"); assertThat(baz).isEqualTo(ClassName.get("com.example", "Foo", "Bar", "Baz")); } static class $Outer { static class $Inner {} } @Test public void classNameFromTypeElement() { Elements elements = compilationRule.getElements(); TypeElement object = elements.getTypeElement(Object.class.getCanonicalName()); assertThat(ClassName.get(object).toString()).isEqualTo("java.lang.Object"); TypeElement outer = elements.getTypeElement($Outer.class.getCanonicalName()); assertThat(ClassName.get(outer).toString()).isEqualTo("com.squareup.javapoet.ClassNameTest.$Outer"); TypeElement inner = elements.getTypeElement($Outer.$Inner.class.getCanonicalName()); assertThat(ClassName.get(inner).toString()).isEqualTo("com.squareup.javapoet.ClassNameTest.$Outer.$Inner"); } /** * Buck builds with "source-based ABI generation" and those builds don't support * {@link TypeElement#getKind()}. Test to confirm that we don't use that API. */ @Test public void classNameFromTypeElementDoesntUseGetKind() { Elements elements = compilationRule.getElements(); TypeElement object = elements.getTypeElement(Object.class.getCanonicalName()); assertThat(ClassName.get(preventGetKind(object)).toString()) .isEqualTo("java.lang.Object"); TypeElement outer = elements.getTypeElement($Outer.class.getCanonicalName()); assertThat(ClassName.get(preventGetKind(outer)).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest.$Outer"); TypeElement inner = elements.getTypeElement($Outer.$Inner.class.getCanonicalName()); assertThat(ClassName.get(preventGetKind(inner)).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest.$Outer.$Inner"); } /** Returns a new instance like {@code object} that throws on {@code getKind()}. */ private TypeElement preventGetKind(TypeElement object) { TypeElement spy = Mockito.spy(object); when(spy.getKind()).thenThrow(new AssertionError()); when(spy.getEnclosingElement()).thenAnswer(invocation -> { Object enclosingElement = invocation.callRealMethod(); return enclosingElement instanceof TypeElement ? preventGetKind((TypeElement) enclosingElement) : enclosingElement; }); return spy; } @Test public void classNameFromClass() { assertThat(ClassName.get(Object.class).toString()) .isEqualTo("java.lang.Object"); assertThat(ClassName.get(OuterClass.InnerClass.class).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest.OuterClass.InnerClass"); assertThat((ClassName.get(new Object() {}.getClass())).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest$1"); assertThat((ClassName.get(new Object() { Object inner = new Object() {}; }.inner.getClass())).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest$2$1"); assertThat((ClassName.get($Outer.class)).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest.$Outer"); assertThat((ClassName.get($Outer.$Inner.class)).toString()) .isEqualTo("com.squareup.javapoet.ClassNameTest.$Outer.$Inner"); } @Test public void peerClass() { assertThat(ClassName.get(Double.class).peerClass("Short")) .isEqualTo(ClassName.get(Short.class)); assertThat(ClassName.get("", "Double").peerClass("Short")) .isEqualTo(ClassName.get("", "Short")); assertThat(ClassName.get("a.b", "Combo", "Taco").peerClass("Burrito")) .isEqualTo(ClassName.get("a.b", "Combo", "Burrito")); } @Test public void fromClassRejectionTypes() { try { ClassName.get(int.class); fail(); } catch (IllegalArgumentException ignored) { } try { ClassName.get(void.class); fail(); } catch (IllegalArgumentException ignored) { } try { ClassName.get(Object[].class); fail(); } catch (IllegalArgumentException ignored) { } } @Test public void reflectionName() { assertEquals("java.lang.Object", TypeName.OBJECT.reflectionName()); assertEquals("java.lang.Thread$State", ClassName.get(Thread.State.class).reflectionName()); assertEquals("java.util.Map$Entry", ClassName.get(Map.Entry.class).reflectionName()); assertEquals("Foo", ClassName.get("", "Foo").reflectionName()); assertEquals("Foo$Bar$Baz", ClassName.get("", "Foo", "Bar", "Baz").reflectionName()); assertEquals("a.b.c.Foo$Bar$Baz", ClassName.get("a.b.c", "Foo", "Bar", "Baz").reflectionName()); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/CodeBlockTest.java000066400000000000000000000271351335545476000257030ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class CodeBlockTest { @Test public void equalsAndHashCode() { CodeBlock a = CodeBlock.builder().build(); CodeBlock b = CodeBlock.builder().build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = CodeBlock.builder().add("$L", "taco").build(); b = CodeBlock.builder().add("$L", "taco").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void of() { CodeBlock a = CodeBlock.of("$L taco", "delicious"); assertThat(a.toString()).isEqualTo("delicious taco"); } @Test public void isEmpty() { assertTrue(CodeBlock.builder().isEmpty()); assertTrue(CodeBlock.builder().add("").isEmpty()); assertFalse(CodeBlock.builder().add(" ").isEmpty()); } @Test public void indentCannotBeIndexed() { try { CodeBlock.builder().add("$1>", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void deindentCannotBeIndexed() { try { CodeBlock.builder().add("$1<", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void dollarSignEscapeCannotBeIndexed() { try { CodeBlock.builder().add("$1$", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void statementBeginningCannotBeIndexed() { try { CodeBlock.builder().add("$1[", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void statementEndingCannotBeIndexed() { try { CodeBlock.builder().add("$1]", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void nameFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1N", "taco").build(); assertThat(block.toString()).isEqualTo("taco"); } @Test public void literalFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1L", "taco").build(); assertThat(block.toString()).isEqualTo("taco"); } @Test public void stringFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1S", "taco").build(); assertThat(block.toString()).isEqualTo("\"taco\""); } @Test public void typeFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1T", String.class).build(); assertThat(block.toString()).isEqualTo("java.lang.String"); } @Test public void simpleNamedArgument() { Map map = new LinkedHashMap<>(); map.put("text", "taco"); CodeBlock block = CodeBlock.builder().addNamed("$text:S", map).build(); assertThat(block.toString()).isEqualTo("\"taco\""); } @Test public void repeatedNamedArgument() { Map map = new LinkedHashMap<>(); map.put("text", "tacos"); CodeBlock block = CodeBlock.builder() .addNamed("\"I like \" + $text:S + \". Do you like \" + $text:S + \"?\"", map) .build(); assertThat(block.toString()).isEqualTo( "\"I like \" + \"tacos\" + \". Do you like \" + \"tacos\" + \"?\""); } @Test public void namedAndNoArgFormat() { Map map = new LinkedHashMap<>(); map.put("text", "tacos"); CodeBlock block = CodeBlock.builder() .addNamed("$>\n$text:L for $$3.50", map).build(); assertThat(block.toString()).isEqualTo("\n tacos for $3.50"); } @Test public void missingNamedArgument() { try { Map map = new LinkedHashMap<>(); CodeBlock.builder().addNamed("$text:S", map).build(); fail(); } catch(IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("Missing named argument for $text"); } } @Test public void lowerCaseNamed() { try { Map map = new LinkedHashMap<>(); map.put("Text", "tacos"); CodeBlock block = CodeBlock.builder().addNamed("$Text:S", map).build(); fail(); } catch(IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("argument 'Text' must start with a lowercase character"); } } @Test public void multipleNamedArguments() { Map map = new LinkedHashMap<>(); map.put("pipe", System.class); map.put("text", "tacos"); CodeBlock block = CodeBlock.builder() .addNamed("$pipe:T.out.println(\"Let's eat some $text:L\");", map) .build(); assertThat(block.toString()).isEqualTo( "java.lang.System.out.println(\"Let's eat some tacos\");"); } @Test public void namedNewline() { Map map = new LinkedHashMap<>(); map.put("clazz", Integer.class); CodeBlock block = CodeBlock.builder().addNamed("$clazz:T\n", map).build(); assertThat(block.toString()).isEqualTo("java.lang.Integer\n"); } @Test public void danglingNamed() { Map map = new LinkedHashMap<>(); map.put("clazz", Integer.class); try { CodeBlock.builder().addNamed("$clazz:T$", map).build(); fail(); } catch(IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("dangling $ at end"); } } @Test public void indexTooHigh() { try { CodeBlock.builder().add("$2T", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 2 for '$2T' not in range (received 1 arguments)"); } } @Test public void indexIsZero() { try { CodeBlock.builder().add("$0T", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 0 for '$0T' not in range (received 1 arguments)"); } } @Test public void indexIsNegative() { try { CodeBlock.builder().add("$-1T", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("invalid format string: '$-1T'"); } } @Test public void indexWithoutFormatType() { try { CodeBlock.builder().add("$1", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("dangling format characters in '$1'"); } } @Test public void indexWithoutFormatTypeNotAtStringEnd() { try { CodeBlock.builder().add("$1 taco", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("invalid format string: '$1 taco'"); } } @Test public void indexButNoArguments() { try { CodeBlock.builder().add("$1T").build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 1 for '$1T' not in range (received 0 arguments)"); } } @Test public void formatIndicatorAlone() { try { CodeBlock.builder().add("$", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("dangling format characters in '$'"); } } @Test public void formatIndicatorWithoutIndexOrFormatType() { try { CodeBlock.builder().add("$ tacoString", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("invalid format string: '$ tacoString'"); } } @Test public void sameIndexCanBeUsedWithDifferentFormats() { CodeBlock block = CodeBlock.builder() .add("$1T.out.println($1S)", ClassName.get(System.class)) .build(); assertThat(block.toString()).isEqualTo("java.lang.System.out.println(\"java.lang.System\")"); } @Test public void tooManyStatementEnters() { CodeBlock codeBlock = CodeBlock.builder().add("$[$[").build(); try { // We can't report this error until rendering type because code blocks might be composed. codeBlock.toString(); fail(); } catch (IllegalStateException expected) { assertThat(expected).hasMessageThat().isEqualTo("statement enter $[ followed by statement enter $["); } } @Test public void statementExitWithoutStatementEnter() { CodeBlock codeBlock = CodeBlock.builder().add("$]").build(); try { // We can't report this error until rendering type because code blocks might be composed. codeBlock.toString(); fail(); } catch (IllegalStateException expected) { assertThat(expected).hasMessageThat().isEqualTo("statement exit $] has no matching statement enter $["); } } @Test public void join() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); codeBlocks.add(CodeBlock.of("$T", ClassName.get("world", "World"))); codeBlocks.add(CodeBlock.of("need tacos")); CodeBlock joined = CodeBlock.join(codeBlocks, " || "); assertThat(joined.toString()).isEqualTo("\"hello\" || world.World || need tacos"); } @Test public void joining() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); codeBlocks.add(CodeBlock.of("$T", ClassName.get("world", "World"))); codeBlocks.add(CodeBlock.of("need tacos")); CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ")); assertThat(joined.toString()).isEqualTo("\"hello\" || world.World || need tacos"); } @Test public void joiningSingle() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ")); assertThat(joined.toString()).isEqualTo("\"hello\""); } @Test public void joiningWithPrefixAndSuffix() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); codeBlocks.add(CodeBlock.of("$T", ClassName.get("world", "World"))); codeBlocks.add(CodeBlock.of("need tacos")); CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ", "start {", "} end")); assertThat(joined.toString()).isEqualTo("start {\"hello\" || world.World || need tacos} end"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/FieldSpecTest.java000066400000000000000000000031471335545476000257110ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import javax.lang.model.element.Modifier; public class FieldSpecTest { @Test public void equalsAndHashCode() { FieldSpec a = FieldSpec.builder(int.class, "foo").build(); FieldSpec b = FieldSpec.builder(int.class, "foo").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = FieldSpec.builder(int.class, "FOO", Modifier.PUBLIC, Modifier.STATIC).build(); b = FieldSpec.builder(int.class, "FOO", Modifier.PUBLIC, Modifier.STATIC).build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void nullAnnotationsAddition() { try { FieldSpec.builder(int.class, "foo").addAnnotations(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("annotationSpecs == null"); } } }javapoet-1.11.1/src/test/java/com/squareup/javapoet/FileReadingTest.java000066400000000000000000000116411335545476000262220ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import com.google.common.io.ByteStreams; import java.io.IOException; import java.net.URI; import java.util.Collections; import java.util.Locale; import java.util.concurrent.Callable; import javax.lang.model.element.Modifier; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaFileObject; import javax.tools.JavaFileObject.Kind; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import javax.tools.ToolProvider; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; @RunWith(JUnit4.class) public class FileReadingTest { // Used for storing compilation output. @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Test public void javaFileObjectUri() { TypeSpec type = TypeSpec.classBuilder("Test").build(); assertThat(JavaFile.builder("", type).build().toJavaFileObject().toUri()) .isEqualTo(URI.create("Test.java")); assertThat(JavaFile.builder("foo", type).build().toJavaFileObject().toUri()) .isEqualTo(URI.create("foo/Test.java")); assertThat(JavaFile.builder("com.example", type).build().toJavaFileObject().toUri()) .isEqualTo(URI.create("com/example/Test.java")); } @Test public void javaFileObjectKind() { JavaFile javaFile = JavaFile.builder("", TypeSpec.classBuilder("Test").build()).build(); assertThat(javaFile.toJavaFileObject().getKind()).isEqualTo(Kind.SOURCE); } @Test public void javaFileObjectCharacterContent() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test") .addJavadoc("Pi\u00f1ata\u00a1") .addMethod(MethodSpec.methodBuilder("fooBar").build()) .build(); JavaFile javaFile = JavaFile.builder("foo", type).build(); JavaFileObject javaFileObject = javaFile.toJavaFileObject(); // We can never have encoding issues (everything is in process) assertThat(javaFileObject.getCharContent(true)).isEqualTo(javaFile.toString()); assertThat(javaFileObject.getCharContent(false)).isEqualTo(javaFile.toString()); } @Test public void javaFileObjectInputStreamIsUtf8() throws IOException { JavaFile javaFile = JavaFile.builder("foo", TypeSpec.classBuilder("Test").build()) .addFileComment("Pi\u00f1ata\u00a1") .build(); byte[] bytes = ByteStreams.toByteArray(javaFile.toJavaFileObject().openInputStream()); // JavaPoet always uses UTF-8. assertThat(bytes).isEqualTo(javaFile.toString().getBytes(UTF_8)); } @Test public void compileJavaFile() throws Exception { final String value = "Hello World!"; TypeSpec type = TypeSpec.classBuilder("Test") .addModifiers(Modifier.PUBLIC) .addSuperinterface(ParameterizedTypeName.get(Callable.class, String.class)) .addMethod(MethodSpec.methodBuilder("call") .returns(String.class) .addModifiers(Modifier.PUBLIC) .addStatement("return $S", value) .build()) .build(); JavaFile javaFile = JavaFile.builder("foo", type).build(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnosticCollector = new DiagnosticCollector<>(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8); fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(temporaryFolder.newFolder())); CompilationTask task = compiler.getTask(null, fileManager, diagnosticCollector, Collections.emptySet(), Collections.emptySet(), Collections.singleton(javaFile.toJavaFileObject())); assertThat(task.call()).isTrue(); assertThat(diagnosticCollector.getDiagnostics()).isEmpty(); ClassLoader loader = fileManager.getClassLoader(StandardLocation.CLASS_OUTPUT); Callable test = Class.forName("foo.Test", true, loader) .asSubclass(Callable.class) .getDeclaredConstructor() .newInstance(); assertThat(Callable.class.getMethod("call").invoke(test)).isEqualTo(value); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/FileWritingTest.java000066400000000000000000000205071335545476000262750ustar00rootroot00000000000000/* * Copyright (C) 2014 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. */ package com.squareup.javapoet; import com.google.common.jimfs.Configuration; import com.google.common.jimfs.Jimfs; import java.io.File; import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.util.Date; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mockito; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public final class FileWritingTest { // Used for testing java.io File behavior. @Rule public final TemporaryFolder tmp = new TemporaryFolder(); // Used for testing java.nio.file Path behavior. private final FileSystem fs = Jimfs.newFileSystem(Configuration.unix()); private final Path fsRoot = fs.getRootDirectories().iterator().next(); // Used for testing annotation processor Filer behavior. private final TestFiler filer = new TestFiler(fs, fsRoot); @Test public void pathNotDirectory() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile javaFile = JavaFile.builder("example", type).build(); Path path = fs.getPath("/foo/bar"); Files.createDirectories(path.getParent()); Files.createFile(path); try { javaFile.writeTo(path); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage()).isEqualTo("path /foo/bar exists but is not a directory."); } } @Test public void fileNotDirectory() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile javaFile = JavaFile.builder("example", type).build(); File file = new File(tmp.newFolder("foo"), "bar"); file.createNewFile(); try { javaFile.writeTo(file); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage()).isEqualTo( "path " + file.getPath() + " exists but is not a directory."); } } @Test public void pathDefaultPackage() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile.builder("", type).build().writeTo(fsRoot); Path testPath = fsRoot.resolve("Test.java"); assertThat(Files.exists(testPath)).isTrue(); } @Test public void fileDefaultPackage() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile.builder("", type).build().writeTo(tmp.getRoot()); File testFile = new File(tmp.getRoot(), "Test.java"); assertThat(testFile.exists()).isTrue(); } @Test public void filerDefaultPackage() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile.builder("", type).build().writeTo(filer); Path testPath = fsRoot.resolve("Test.java"); assertThat(Files.exists(testPath)).isTrue(); } @Test public void pathNestedClasses() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile.builder("foo", type).build().writeTo(fsRoot); JavaFile.builder("foo.bar", type).build().writeTo(fsRoot); JavaFile.builder("foo.bar.baz", type).build().writeTo(fsRoot); Path fooPath = fsRoot.resolve(fs.getPath("foo", "Test.java")); Path barPath = fsRoot.resolve(fs.getPath("foo", "bar", "Test.java")); Path bazPath = fsRoot.resolve(fs.getPath("foo", "bar", "baz", "Test.java")); assertThat(Files.exists(fooPath)).isTrue(); assertThat(Files.exists(barPath)).isTrue(); assertThat(Files.exists(bazPath)).isTrue(); } @Test public void fileNestedClasses() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile.builder("foo", type).build().writeTo(tmp.getRoot()); JavaFile.builder("foo.bar", type).build().writeTo(tmp.getRoot()); JavaFile.builder("foo.bar.baz", type).build().writeTo(tmp.getRoot()); File fooDir = new File(tmp.getRoot(), "foo"); File fooFile = new File(fooDir, "Test.java"); File barDir = new File(fooDir, "bar"); File barFile = new File(barDir, "Test.java"); File bazDir = new File(barDir, "baz"); File bazFile = new File(bazDir, "Test.java"); assertThat(fooFile.exists()).isTrue(); assertThat(barFile.exists()).isTrue(); assertThat(bazFile.exists()).isTrue(); } @Test public void filerNestedClasses() throws IOException { TypeSpec type = TypeSpec.classBuilder("Test").build(); JavaFile.builder("foo", type).build().writeTo(filer); JavaFile.builder("foo.bar", type).build().writeTo(filer); JavaFile.builder("foo.bar.baz", type).build().writeTo(filer); Path fooPath = fsRoot.resolve(fs.getPath("foo", "Test.java")); Path barPath = fsRoot.resolve(fs.getPath("foo", "bar", "Test.java")); Path bazPath = fsRoot.resolve(fs.getPath("foo", "bar", "baz", "Test.java")); assertThat(Files.exists(fooPath)).isTrue(); assertThat(Files.exists(barPath)).isTrue(); assertThat(Files.exists(bazPath)).isTrue(); } @Test public void filerPassesOriginatingElements() throws IOException { Element element1_1 = Mockito.mock(Element.class); TypeSpec test1 = TypeSpec.classBuilder("Test1") .addOriginatingElement(element1_1) .build(); Element element2_1 = Mockito.mock(Element.class); Element element2_2 = Mockito.mock(Element.class); TypeSpec test2 = TypeSpec.classBuilder("Test2") .addOriginatingElement(element2_1) .addOriginatingElement(element2_2) .build(); JavaFile.builder("example", test1).build().writeTo(filer); JavaFile.builder("example", test2).build().writeTo(filer); Path testPath1 = fsRoot.resolve(fs.getPath("example", "Test1.java")); assertThat(filer.getOriginatingElements(testPath1)).containsExactly(element1_1); Path testPath2 = fsRoot.resolve(fs.getPath("example", "Test2.java")); assertThat(filer.getOriginatingElements(testPath2)).containsExactly(element2_1, element2_2); } @Test public void filerClassesWithTabIndent() throws IOException { TypeSpec test = TypeSpec.classBuilder("Test") .addField(Date.class, "madeFreshDate") .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(String[].class, "args") .addCode("$T.out.println($S);\n", System.class, "Hello World!") .build()) .build(); JavaFile.builder("foo", test).indent("\t").build().writeTo(filer); Path fooPath = fsRoot.resolve(fs.getPath("foo", "Test.java")); assertThat(Files.exists(fooPath)).isTrue(); String source = new String(Files.readAllBytes(fooPath)); assertThat(source).isEqualTo("" + "package foo;\n" + "\n" + "import java.lang.String;\n" + "import java.lang.System;\n" + "import java.util.Date;\n" + "\n" + "class Test {\n" + "\tDate madeFreshDate;\n" + "\n" + "\tpublic static void main(String[] args) {\n" + "\t\tSystem.out.println(\"Hello World!\");\n" + "\t}\n" + "}\n"); } /** * This test confirms that JavaPoet ignores the host charset and always uses UTF-8. The host * charset is customized with {@code -Dfile.encoding=ISO-8859-1}. */ @Test public void fileIsUtf8() throws IOException { JavaFile javaFile = JavaFile.builder("foo", TypeSpec.classBuilder("Taco").build()) .addFileComment("Pi\u00f1ata\u00a1") .build(); javaFile.writeTo(fsRoot); Path fooPath = fsRoot.resolve(fs.getPath("foo", "Taco.java")); assertThat(new String(Files.readAllBytes(fooPath), UTF_8)).isEqualTo("" + "// Pi\u00f1ata\u00a1\n" + "package foo;\n" + "\n" + "class Taco {\n" + "}\n"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/JavaFileTest.java000066400000000000000000000604151335545476000255350ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import javax.lang.model.element.Modifier; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public final class JavaFileTest { @Test public void importStaticReadmeExample() { ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards"); ClassName list = ClassName.get("java.util", "List"); ClassName arrayList = ClassName.get("java.util", "ArrayList"); TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard); MethodSpec beyond = MethodSpec.methodBuilder("beyond") .returns(listOfHoverboards) .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList) .addStatement("result.add($T.createNimbus(2000))", hoverboard) .addStatement("result.add($T.createNimbus(\"2001\"))", hoverboard) .addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", hoverboard, namedBoards) .addStatement("$T.sort(result)", Collections.class) .addStatement("return result.isEmpty() ? $T.emptyList() : result", Collections.class) .build(); TypeSpec hello = TypeSpec.classBuilder("HelloWorld") .addMethod(beyond) .build(); JavaFile example = JavaFile.builder("com.example.helloworld", hello) .addStaticImport(hoverboard, "createNimbus") .addStaticImport(namedBoards, "*") .addStaticImport(Collections.class, "*") .build(); assertThat(example.toString()).isEqualTo("" + "package com.example.helloworld;\n" + "\n" + "import static com.mattel.Hoverboard.Boards.*;\n" + "import static com.mattel.Hoverboard.createNimbus;\n" + "import static java.util.Collections.*;\n" + "\n" + "import com.mattel.Hoverboard;\n" + "import java.util.ArrayList;\n" + "import java.util.List;\n" + "\n" + "class HelloWorld {\n" + " List beyond() {\n" + " List result = new ArrayList<>();\n" + " result.add(createNimbus(2000));\n" + " result.add(createNimbus(\"2001\"));\n" + " result.add(createNimbus(THUNDERBOLT));\n" + " sort(result);\n" + " return result.isEmpty() ? emptyList() : result;\n" + " }\n" + "}\n"); } @Test public void importStaticForCrazyFormatsWorks() { MethodSpec method = MethodSpec.methodBuilder("method").build(); JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addStaticBlock(CodeBlock.builder() .addStatement("$T", Runtime.class) .addStatement("$T.a()", Runtime.class) .addStatement("$T.X", Runtime.class) .addStatement("$T$T", Runtime.class, Runtime.class) .addStatement("$T.$T", Runtime.class, Runtime.class) .addStatement("$1T$1T", Runtime.class) .addStatement("$1T$2L$1T", Runtime.class, "?") .addStatement("$1T$2L$2S$1T", Runtime.class, "?") .addStatement("$1T$2L$2S$1T$3N$1T", Runtime.class, "?", method) .addStatement("$T$L", Runtime.class, "?") .addStatement("$T$S", Runtime.class, "?") .addStatement("$T$N", Runtime.class, method) .build()) .build()) .addStaticImport(Runtime.class, "*") .build() .toString(); // don't look at the generated code... } @Test public void importStaticMixed() { JavaFile source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addStaticBlock(CodeBlock.builder() .addStatement("assert $1T.valueOf(\"BLOCKED\") == $1T.BLOCKED", Thread.State.class) .addStatement("$T.gc()", System.class) .addStatement("$1T.out.println($1T.nanoTime())", System.class) .build()) .addMethod(MethodSpec.constructorBuilder() .addParameter(Thread.State[].class, "states") .varargs(true) .build()) .build()) .addStaticImport(Thread.State.BLOCKED) .addStaticImport(System.class, "*") .addStaticImport(Thread.State.class, "valueOf") .build(); assertThat(source.toString()).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import static java.lang.System.*;\n" + "import static java.lang.Thread.State.BLOCKED;\n" + "import static java.lang.Thread.State.valueOf;\n" + "\n" + "import java.lang.Thread;\n" + "\n" + "class Taco {\n" + " static {\n" + " assert valueOf(\"BLOCKED\") == BLOCKED;\n" + " gc();\n" + " out.println(nanoTime());\n" + " }\n" + "\n" + " Taco(Thread.State... states) {\n" + " }\n" + "}\n"); } @Ignore("addStaticImport doesn't support members with $L") @Test public void importStaticDynamic() { JavaFile source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("main") .addStatement("$T.$L.println($S)", System.class, "out", "hello") .build()) .build()) .addStaticImport(System.class, "out") .build(); assertThat(source.toString()).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import static java.lang.System.out;\n" + "\n" + "class Taco {\n" + " void main() {\n" + " out.println(\"hello\");\n" + " }\n" + "}\n"); } @Test public void importStaticNone() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import java.lang.System;\n" + "import java.util.concurrent.TimeUnit;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " System.gc();\n" + " return TimeUnit.SECONDS.convert(minutes, TimeUnit.MINUTES);\n" + " }\n" + "}\n"); } @Test public void importStaticOnce() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .addStaticImport(TimeUnit.SECONDS) .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import static java.util.concurrent.TimeUnit.SECONDS;\n" + "\n" + "import java.lang.System;\n" + "import java.util.concurrent.TimeUnit;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " System.gc();\n" + " return SECONDS.convert(minutes, TimeUnit.MINUTES);\n" + " }\n" + "}\n"); } @Test public void importStaticTwice() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .addStaticImport(TimeUnit.SECONDS) .addStaticImport(TimeUnit.MINUTES) .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import static java.util.concurrent.TimeUnit.MINUTES;\n" + "import static java.util.concurrent.TimeUnit.SECONDS;\n" + "\n" + "import java.lang.System;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " System.gc();\n" + " return SECONDS.convert(minutes, MINUTES);\n" + " }\n" + "}\n"); } @Test public void importStaticUsingWildcards() { assertThat(JavaFile.builder("readme", importStaticTypeSpec("Util")) .addStaticImport(TimeUnit.class, "*") .addStaticImport(System.class, "*") .build().toString()).isEqualTo("" + "package readme;\n" + "\n" + "import static java.lang.System.*;\n" + "import static java.util.concurrent.TimeUnit.*;\n" + "\n" + "class Util {\n" + " public static long minutesToSeconds(long minutes) {\n" + " gc();\n" + " return SECONDS.convert(minutes, MINUTES);\n" + " }\n" + "}\n"); } private TypeSpec importStaticTypeSpec(String name) { MethodSpec method = MethodSpec.methodBuilder("minutesToSeconds") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(long.class) .addParameter(long.class, "minutes") .addStatement("$T.gc()", System.class) .addStatement("return $1T.SECONDS.convert(minutes, $1T.MINUTES)", TimeUnit.class) .build(); return TypeSpec.classBuilder(name).addMethod(method).build(); } @Test public void noImports() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco").build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void singleImport() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(Date.class, "madeFreshDate") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.Date;\n" + "\n" + "class Taco {\n" + " Date madeFreshDate;\n" + "}\n"); } @Test public void conflictingImports() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(Date.class, "madeFreshDate") .addField(ClassName.get("java.sql", "Date"), "madeFreshDatabaseDate") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.Date;\n" + "\n" + "class Taco {\n" + " Date madeFreshDate;\n" + "\n" + " java.sql.Date madeFreshDatabaseDate;\n" + "}\n"); } @Test public void annotatedTypeParam() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ParameterizedTypeName.get(ClassName.get(List.class), ClassName.get("com.squareup.meat", "Chorizo") .annotated(AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "Spicy")) .build())), "chorizo") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.meat.Chorizo;\n" + "import java.util.List;\n" + "\n" + "class Taco {\n" + " List<@Spicy Chorizo> chorizo;\n" + "}\n"); } @Test public void skipJavaLangImportsWithConflictingClassLast() throws Exception { // Whatever is used first wins! In this case the Float in java.lang is imported. String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ClassName.get("java.lang", "Float"), "litres") .addField(ClassName.get("com.squareup.soda", "Float"), "beverage") .build()) .skipJavaLangImports(true) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " Float litres;\n" + "\n" + " com.squareup.soda.Float beverage;\n" // Second 'Float' is fully qualified. + "}\n"); } @Test public void skipJavaLangImportsWithConflictingClassFirst() throws Exception { // Whatever is used first wins! In this case the Float in com.squareup.soda is imported. String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ClassName.get("com.squareup.soda", "Float"), "beverage") .addField(ClassName.get("java.lang", "Float"), "litres") .build()) .skipJavaLangImports(true) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.soda.Float;\n" + "\n" + "class Taco {\n" + " Float beverage;\n" + "\n" + " java.lang.Float litres;\n" // Second 'Float' is fully qualified. + "}\n"); } @Test public void conflictingParentName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("A") .addType(TypeSpec.classBuilder("B") .addType(TypeSpec.classBuilder("Twin").build()) .addType(TypeSpec.classBuilder("C") .addField(ClassName.get("com.squareup.tacos", "A", "Twin", "D"), "d") .build()) .build()) .addType(TypeSpec.classBuilder("Twin") .addType(TypeSpec.classBuilder("D") .build()) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class A {\n" + " class B {\n" + " class Twin {\n" + " }\n" + "\n" + " class C {\n" + " A.Twin.D d;\n" + " }\n" + " }\n" + "\n" + " class Twin {\n" + " class D {\n" + " }\n" + " }\n" + "}\n"); } @Test public void conflictingChildName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("A") .addType(TypeSpec.classBuilder("B") .addType(TypeSpec.classBuilder("C") .addField(ClassName.get("com.squareup.tacos", "A", "Twin", "D"), "d") .addType(TypeSpec.classBuilder("Twin").build()) .build()) .build()) .addType(TypeSpec.classBuilder("Twin") .addType(TypeSpec.classBuilder("D") .build()) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class A {\n" + " class B {\n" + " class C {\n" + " A.Twin.D d;\n" + "\n" + " class Twin {\n" + " }\n" + " }\n" + " }\n" + "\n" + " class Twin {\n" + " class D {\n" + " }\n" + " }\n" + "}\n"); } @Test public void conflictingNameOutOfScope() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("A") .addType(TypeSpec.classBuilder("B") .addType(TypeSpec.classBuilder("C") .addField(ClassName.get("com.squareup.tacos", "A", "Twin", "D"), "d") .addType(TypeSpec.classBuilder("Nested") .addType(TypeSpec.classBuilder("Twin").build()) .build()) .build()) .build()) .addType(TypeSpec.classBuilder("Twin") .addType(TypeSpec.classBuilder("D") .build()) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class A {\n" + " class B {\n" + " class C {\n" + " Twin.D d;\n" + "\n" + " class Nested {\n" + " class Twin {\n" + " }\n" + " }\n" + " }\n" + " }\n" + "\n" + " class Twin {\n" + " class D {\n" + " }\n" + " }\n" + "}\n"); } @Test public void nestedClassAndSuperclassShareName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ClassName.get("com.squareup.wire", "Message")) .addType(TypeSpec.classBuilder("Builder") .superclass(ClassName.get("com.squareup.wire", "Message", "Builder")) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.wire.Message;\n" + "\n" + "class Taco extends Message {\n" + " class Builder extends Message.Builder {\n" + " }\n" + "}\n"); } @Test public void classAndSuperclassShareName() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ClassName.get("com.taco.bell", "Taco")) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco extends com.taco.bell.Taco {\n" + "}\n"); } @Test public void conflictingAnnotation() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addAnnotation(ClassName.get("com.taco.bell", "Taco")) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@com.taco.bell.Taco\n" + "class Taco {\n" + "}\n"); } @Test public void conflictingAnnotationReferencedClass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addAnnotation(AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "MyAnno")) .addMember("value", "$T.class", ClassName.get("com.taco.bell", "Taco")) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@MyAnno(com.taco.bell.Taco.class)\n" + "class Taco {\n" + "}\n"); } @Test public void conflictingTypeVariableBound() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addTypeVariable( TypeVariableName.get("T", ClassName.get("com.taco.bell", "Taco"))) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void superclassReferencesSelf() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ParameterizedTypeName.get( ClassName.get(Comparable.class), ClassName.get("com.squareup.tacos", "Taco"))) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Comparable;\n" + "\n" + "class Taco extends Comparable {\n" + "}\n"); } /** https://github.com/square/javapoet/issues/366 */ @Test public void annotationIsNestedClass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("TestComponent") .addAnnotation(ClassName.get("dagger", "Component")) .addType(TypeSpec.classBuilder("Builder") .addAnnotation(ClassName.get("dagger", "Component", "Builder")) .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import dagger.Component;\n" + "\n" + "@Component\n" + "class TestComponent {\n" + " @Component.Builder\n" + " class Builder {\n" + " }\n" + "}\n"); } @Test public void defaultPackage() throws Exception { String source = JavaFile.builder("", TypeSpec.classBuilder("HelloWorld") .addMethod(MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addParameter(String[].class, "args") .addCode("$T.out.println($S);\n", System.class, "Hello World!") .build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "import java.lang.String;\n" + "import java.lang.System;\n" + "\n" + "class HelloWorld {\n" + " public static void main(String[] args) {\n" + " System.out.println(\"Hello World!\");\n" + " }\n" + "}\n"); } @Test public void defaultPackageTypesAreNotImported() throws Exception { String source = JavaFile.builder("hello", TypeSpec.classBuilder("World").addSuperinterface(ClassName.get("", "Test")).build()) .build() .toString(); assertThat(source).isEqualTo("" + "package hello;\n" + "\n" + "class World implements Test {\n" + "}\n"); } @Test public void topOfFileComment() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco").build()) .addFileComment("Generated $L by JavaPoet. DO NOT EDIT!", "2015-01-13") .build() .toString(); assertThat(source).isEqualTo("" + "// Generated 2015-01-13 by JavaPoet. DO NOT EDIT!\n" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void emptyLinesInTopOfFileComment() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco").build()) .addFileComment("\nGENERATED FILE:\n\nDO NOT EDIT!\n") .build() .toString(); assertThat(source).isEqualTo("" + "//\n" + "// GENERATED FILE:\n" + "//\n" + "// DO NOT EDIT!\n" + "//\n" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + "}\n"); } @Test public void packageClassConflictsWithNestedClass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .addField(ClassName.get("com.squareup.tacos", "A"), "a") .addType(TypeSpec.classBuilder("A").build()) .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " com.squareup.tacos.A a;\n" + "\n" + " class A {\n" + " }\n" + "}\n"); } @Test public void packageClassConflictsWithSuperlass() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco") .superclass(ClassName.get("com.taco.bell", "A")) .addField(ClassName.get("com.squareup.tacos", "A"), "a") .build()) .build() .toString(); assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco extends com.taco.bell.A {\n" + " A a;\n" + "}\n"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/LineWrapperTest.java000066400000000000000000000165061335545476000263060ustar00rootroot00000000000000/* * Copyright (C) 2016 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. */ package com.squareup.javapoet; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import static com.google.common.truth.Truth.assertThat; @RunWith(JUnit4.class) public final class LineWrapperTest { @Test public void wrap() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.wrappingSpace(2); lineWrapper.append("fghij"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde\n fghij"); } @Test public void noWrap() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.wrappingSpace(2); lineWrapper.append("fghi"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde fghi"); } @Test public void zeroWidthNoWrap() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.zeroWidthSpace(2); lineWrapper.append("fghij"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcdefghij"); } @Test public void nospaceWrapMax() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.zeroWidthSpace(2); lineWrapper.append("fghijk"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde\n fghijk"); } @Test public void multipleWrite() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("ab"); lineWrapper.wrappingSpace(1); lineWrapper.append("cd"); lineWrapper.wrappingSpace(1); lineWrapper.append("ef"); lineWrapper.wrappingSpace(1); lineWrapper.append("gh"); lineWrapper.wrappingSpace(1); lineWrapper.append("ij"); lineWrapper.wrappingSpace(1); lineWrapper.append("kl"); lineWrapper.wrappingSpace(1); lineWrapper.append("mn"); lineWrapper.wrappingSpace(1); lineWrapper.append("op"); lineWrapper.wrappingSpace(1); lineWrapper.append("qr"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("ab cd ef\n gh ij kl\n mn op qr"); } @Test public void fencepost() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.append("fghij"); lineWrapper.wrappingSpace(2); lineWrapper.append("k"); lineWrapper.append("lmnop"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcdefghij\n klmnop"); } @Test public void fencepostZeroWidth() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.append("fghij"); lineWrapper.zeroWidthSpace(2); lineWrapper.append("k"); lineWrapper.append("lmnop"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcdefghij\n klmnop"); } @Test public void overlyLongLinesWithoutLeadingSpace() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcdefghijkl"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcdefghijkl"); } @Test public void overlyLongLinesWithLeadingSpace() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.wrappingSpace(2); lineWrapper.append("abcdefghijkl"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("\n abcdefghijkl"); } @Test public void overlyLongLinesWithLeadingZeroWidth() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.zeroWidthSpace(2); lineWrapper.append("abcdefghijkl"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcdefghijkl"); } @Test public void noWrapEmbeddedNewlines() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.wrappingSpace(2); lineWrapper.append("fghi\njklmn"); lineWrapper.append("opqrstuvwxy"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde fghi\njklmnopqrstuvwxy"); } @Test public void wrapEmbeddedNewlines() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.wrappingSpace(2); lineWrapper.append("fghij\nklmn"); lineWrapper.append("opqrstuvwxy"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde\n fghij\nklmnopqrstuvwxy"); } @Test public void noWrapEmbeddedNewlines_ZeroWidth() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.zeroWidthSpace(2); lineWrapper.append("fghij\nklmn"); lineWrapper.append("opqrstuvwxyz"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcdefghij\nklmnopqrstuvwxyz"); } @Test public void wrapEmbeddedNewlines_ZeroWidth() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.zeroWidthSpace(2); lineWrapper.append("fghijk\nlmn"); lineWrapper.append("opqrstuvwxy"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde\n fghijk\nlmnopqrstuvwxy"); } @Test public void noWrapMultipleNewlines() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.wrappingSpace(2); lineWrapper.append("fghi\nklmnopq\nr"); lineWrapper.wrappingSpace(2); lineWrapper.append("stuvwxyz"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde fghi\nklmnopq\nr stuvwxyz"); } @Test public void wrapMultipleNewlines() throws Exception { StringBuffer out = new StringBuffer(); LineWrapper lineWrapper = new LineWrapper(out, " ", 10); lineWrapper.append("abcde"); lineWrapper.wrappingSpace(2); lineWrapper.append("fghi\nklmnopq\nrs"); lineWrapper.wrappingSpace(2); lineWrapper.append("tuvwxyz1"); lineWrapper.close(); assertThat(out.toString()).isEqualTo("abcde fghi\nklmnopq\nrs\n tuvwxyz1"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/MethodSpecTest.java000066400000000000000000000261221335545476000261040ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import com.google.testing.compile.CompilationRule; import java.io.Closeable; import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Target; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.TimeoutException; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.junit.Assert.fail; public final class MethodSpecTest { @Rule public final CompilationRule compilation = new CompilationRule(); private Elements elements; private Types types; @Before public void setUp() { elements = compilation.getElements(); types = compilation.getTypes(); } private TypeElement getElement(Class clazz) { return elements.getTypeElement(clazz.getCanonicalName()); } private ExecutableElement findFirst(Collection elements, String name) { for (ExecutableElement executableElement : elements) { if (executableElement.getSimpleName().toString().equals(name)) { return executableElement; } } throw new IllegalArgumentException(name + " not found in " + elements); } @Test public void nullAnnotationsAddition() { try { MethodSpec.methodBuilder("doSomething").addAnnotations(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("annotationSpecs == null"); } } @Test public void nullTypeVariablesAddition() { try { MethodSpec.methodBuilder("doSomething").addTypeVariables(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("typeVariables == null"); } } @Test public void nullParametersAddition() { try { MethodSpec.methodBuilder("doSomething").addParameters(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("parameterSpecs == null"); } } @Test public void nullExceptionsAddition() { try { MethodSpec.methodBuilder("doSomething").addExceptions(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("exceptions == null"); } } @Target(ElementType.PARAMETER) @interface Nullable { } abstract static class Everything { @Deprecated protected abstract Runnable everything( @Nullable String thing, List things) throws IOException, SecurityException; } abstract static class Generics { T run(R param) throws V { return null; } } abstract static class HasAnnotation { @Override public abstract String toString(); } interface Throws { void fail() throws R; } interface ExtendsOthers extends Callable, Comparable, Throws { } interface ExtendsIterableWithDefaultMethods extends Iterable { } final class FinalClass { void method() { } } abstract static class InvalidOverrideMethods { final void finalMethod() { } private void privateMethod() { } static void staticMethod() { } } @Test public void overrideEverything() { TypeElement classElement = getElement(Everything.class); ExecutableElement methodElement = getOnlyElement(methodsIn(classElement.getEnclosedElements())); MethodSpec method = MethodSpec.overriding(methodElement).build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "protected java.lang.Runnable " + "everything(\n" + " java.lang.String arg0, java.util.List arg1) throws java.io.IOException,\n" + " java.lang.SecurityException {\n" + "}\n"); } @Test public void overrideGenerics() { TypeElement classElement = getElement(Generics.class); ExecutableElement methodElement = getOnlyElement(methodsIn(classElement.getEnclosedElements())); MethodSpec method = MethodSpec.overriding(methodElement) .addStatement("return null") .build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + " T run(R param) throws V {\n" + " return null;\n" + "}\n"); } @Test public void overrideDoesNotCopyOverrideAnnotation() { TypeElement classElement = getElement(HasAnnotation.class); ExecutableElement exec = getOnlyElement(methodsIn(classElement.getEnclosedElements())); MethodSpec method = MethodSpec.overriding(exec).build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "public java.lang.String toString() {\n" + "}\n"); } @Test public void overrideDoesNotCopyDefaultModifier() { TypeElement classElement = getElement(ExtendsIterableWithDefaultMethods.class); DeclaredType classType = (DeclaredType) classElement.asType(); List methods = methodsIn(elements.getAllMembers(classElement)); ExecutableElement exec = findFirst(methods, "spliterator"); MethodSpec method = MethodSpec.overriding(exec, classType, types).build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "public java.util.Spliterator spliterator() {\n" + "}\n"); } @Test public void overrideExtendsOthersWorksWithActualTypeParameters() { TypeElement classElement = getElement(ExtendsOthers.class); DeclaredType classType = (DeclaredType) classElement.asType(); List methods = methodsIn(elements.getAllMembers(classElement)); ExecutableElement exec = findFirst(methods, "call"); MethodSpec method = MethodSpec.overriding(exec, classType, types).build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "public java.lang.Integer call() throws java.lang.Exception {\n" + "}\n"); exec = findFirst(methods, "compareTo"); method = MethodSpec.overriding(exec, classType, types).build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "public int compareTo(" + ExtendsOthers.class.getCanonicalName() + " arg0) {\n" + "}\n"); exec = findFirst(methods, "fail"); method = MethodSpec.overriding(exec, classType, types).build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "public void fail() throws java.lang.IllegalStateException {\n" + "}\n"); } @Test public void overrideFinalClassMethod() { TypeElement classElement = getElement(FinalClass.class); List methods = methodsIn(elements.getAllMembers(classElement)); try { MethodSpec.overriding(findFirst(methods, "method")); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo( "Cannot override method on final class com.squareup.javapoet.MethodSpecTest.FinalClass"); } } @Test public void overrideInvalidModifiers() { TypeElement classElement = getElement(InvalidOverrideMethods.class); List methods = methodsIn(elements.getAllMembers(classElement)); try { MethodSpec.overriding(findFirst(methods, "finalMethod")); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("cannot override method with modifiers: [final]"); } try { MethodSpec.overriding(findFirst(methods, "privateMethod")); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("cannot override method with modifiers: [private]"); } try { MethodSpec.overriding(findFirst(methods, "staticMethod")); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("cannot override method with modifiers: [static]"); } } @Test public void equalsAndHashCode() { MethodSpec a = MethodSpec.constructorBuilder().build(); MethodSpec b = MethodSpec.constructorBuilder().build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = MethodSpec.methodBuilder("taco").build(); b = MethodSpec.methodBuilder("taco").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); TypeElement classElement = getElement(Everything.class); ExecutableElement methodElement = getOnlyElement(methodsIn(classElement.getEnclosedElements())); a = MethodSpec.overriding(methodElement).build(); b = MethodSpec.overriding(methodElement).build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void duplicateExceptionsIgnored() { ClassName ioException = ClassName.get(IOException.class); ClassName timeoutException = ClassName.get(TimeoutException.class); MethodSpec methodSpec = MethodSpec.methodBuilder("duplicateExceptions") .addException(ioException) .addException(timeoutException) .addException(timeoutException) .addException(ioException) .build(); assertThat(methodSpec.exceptions).isEqualTo(Arrays.asList(ioException, timeoutException)); assertThat(methodSpec.toBuilder().addException(ioException).build().exceptions) .isEqualTo(Arrays.asList(ioException, timeoutException)); } @Test public void nullIsNotAValidMethodName() { try { MethodSpec.methodBuilder(null); fail("NullPointerException expected"); } catch (NullPointerException e) { assertThat(e.getMessage()).isEqualTo("name == null"); } } @Test public void addModifiersVarargsShouldNotBeNull() { try { MethodSpec.methodBuilder("taco") .addModifiers((Modifier[]) null); fail("NullPointerException expected"); } catch (NullPointerException e) { assertThat(e.getMessage()).isEqualTo("modifiers == null"); } } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/NameAllocatorTest.java000066400000000000000000000102111335545476000265620ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; public final class NameAllocatorTest { @Test public void usage() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("foo", 1)).isEqualTo("foo"); assertThat(nameAllocator.newName("bar", 2)).isEqualTo("bar"); assertThat(nameAllocator.get(1)).isEqualTo("foo"); assertThat(nameAllocator.get(2)).isEqualTo("bar"); } @Test public void nameCollision() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("foo")).isEqualTo("foo"); assertThat(nameAllocator.newName("foo")).isEqualTo("foo_"); assertThat(nameAllocator.newName("foo")).isEqualTo("foo__"); } @Test public void nameCollisionWithTag() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("foo", 1)).isEqualTo("foo"); assertThat(nameAllocator.newName("foo", 2)).isEqualTo("foo_"); assertThat(nameAllocator.newName("foo", 3)).isEqualTo("foo__"); assertThat(nameAllocator.get(1)).isEqualTo("foo"); assertThat(nameAllocator.get(2)).isEqualTo("foo_"); assertThat(nameAllocator.get(3)).isEqualTo("foo__"); } @Test public void characterMappingSubstitute() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("a-b", 1)).isEqualTo("a_b"); } @Test public void characterMappingSurrogate() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("a\uD83C\uDF7Ab", 1)).isEqualTo("a_b"); } @Test public void characterMappingInvalidStartButValidPart() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("1ab", 1)).isEqualTo("_1ab"); } @Test public void characterMappingInvalidStartIsInvalidPart() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("&ab", 1)).isEqualTo("_ab"); } @Test public void javaKeyword() throws Exception { NameAllocator nameAllocator = new NameAllocator(); assertThat(nameAllocator.newName("public", 1)).isEqualTo("public_"); assertThat(nameAllocator.get(1)).isEqualTo("public_"); } @Test public void tagReuseForbidden() throws Exception { NameAllocator nameAllocator = new NameAllocator(); nameAllocator.newName("foo", 1); try { nameAllocator.newName("bar", 1); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("tag 1 cannot be used for both 'foo' and 'bar'"); } } @Test public void useBeforeAllocateForbidden() throws Exception { NameAllocator nameAllocator = new NameAllocator(); try { nameAllocator.get(1); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("unknown tag: 1"); } } @Test public void cloneUsage() throws Exception { NameAllocator outterAllocator = new NameAllocator(); outterAllocator.newName("foo", 1); NameAllocator innerAllocator1 = outterAllocator.clone(); assertThat(innerAllocator1.newName("bar", 2)).isEqualTo("bar"); assertThat(innerAllocator1.newName("foo", 3)).isEqualTo("foo_"); NameAllocator innerAllocator2 = outterAllocator.clone(); assertThat(innerAllocator2.newName("foo", 2)).isEqualTo("foo_"); assertThat(innerAllocator2.newName("bar", 3)).isEqualTo("bar"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/ParameterSpecTest.java000066400000000000000000000031321335545476000266000ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import javax.lang.model.element.Modifier; public class ParameterSpecTest { @Test public void equalsAndHashCode() { ParameterSpec a = ParameterSpec.builder(int.class, "foo").build(); ParameterSpec b = ParameterSpec.builder(int.class, "foo").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.STATIC).build(); b = ParameterSpec.builder(int.class, "i").addModifiers(Modifier.STATIC).build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void nullAnnotationsAddition() { try { ParameterSpec.builder(int.class, "foo").addAnnotations(null); fail(); } catch (Exception e) { assertThat(e.getMessage()) .isEqualTo("annotationSpecs == null"); } } }javapoet-1.11.1/src/test/java/com/squareup/javapoet/TestFiler.java000066400000000000000000000062361335545476000251160ustar00rootroot00000000000000/* * Copyright (C) 2014 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. */ package com.squareup.javapoet; import java.io.IOException; import java.io.OutputStream; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.spi.FileSystemProvider; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import javax.annotation.processing.Filer; import javax.lang.model.element.Element; import javax.tools.FileObject; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; final class TestFiler implements Filer { class Source extends SimpleJavaFileObject { private final Path path; protected Source(Path path) { super(path.toUri(), Kind.SOURCE); this.path = path; } @Override public OutputStream openOutputStream() throws IOException { Path parent = path.getParent(); if (!Files.exists(parent)) fileSystemProvider.createDirectory(parent); return fileSystemProvider.newOutputStream(path); } } private final String separator; private final Path fileSystemRoot; private final FileSystemProvider fileSystemProvider; private final Map> originatingElementsMap; public TestFiler(FileSystem fileSystem, Path fsRoot) { separator = fileSystem.getSeparator(); fileSystemRoot = fsRoot; fileSystemProvider = fileSystem.provider(); originatingElementsMap = new LinkedHashMap<>(); } public Set getOriginatingElements(Path path) { return originatingElementsMap.get(path); } @Override public JavaFileObject createSourceFile( CharSequence name, Element... originatingElements) throws IOException { String relative = name.toString().replace(".", separator) + ".java"; // Assumes well-formed. Path path = fileSystemRoot.resolve(relative); originatingElementsMap.put(path, Util.immutableSet(Arrays.asList(originatingElements))); return new Source(path); } @Override public JavaFileObject createClassFile(CharSequence name, Element... originatingElements) throws IOException { throw new UnsupportedOperationException("Not implemented."); } @Override public FileObject createResource(JavaFileManager.Location location, CharSequence pkg, CharSequence relativeName, Element... originatingElements) throws IOException { throw new UnsupportedOperationException("Not implemented."); } @Override public FileObject getResource(JavaFileManager.Location location, CharSequence pkg, CharSequence relativeName) throws IOException { throw new UnsupportedOperationException("Not implemented."); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/TypeNameTest.java000066400000000000000000000171161335545476000255760ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Comparator; import java.util.List; import java.util.Set; import java.util.UUID; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; public class TypeNameTest { protected > E generic(E[] values) { return values[0]; } protected static class TestGeneric { class Inner {} class InnerGeneric {} static class NestedNonGeneric {} } protected static TestGeneric.Inner testGenericStringInner() { return null; } protected static TestGeneric.Inner testGenericIntInner() { return null; } protected static TestGeneric.InnerGeneric testGenericInnerLong() { return null; } protected static TestGeneric.InnerGeneric testGenericInnerInt() { return null; } protected static TestGeneric.NestedNonGeneric testNestedNonGeneric() { return null; } @Test public void genericType() throws Exception { Method recursiveEnum = getClass().getDeclaredMethod("generic", Enum[].class); TypeName.get(recursiveEnum.getReturnType()); TypeName.get(recursiveEnum.getGenericReturnType()); TypeName genericTypeName = TypeName.get(recursiveEnum.getParameterTypes()[0]); TypeName.get(recursiveEnum.getGenericParameterTypes()[0]); // Make sure the generic argument is present assertThat(genericTypeName.toString()).contains("Enum"); } @Test public void innerClassInGenericType() throws Exception { Method genericStringInner = getClass().getDeclaredMethod("testGenericStringInner"); TypeName.get(genericStringInner.getReturnType()); TypeName genericTypeName = TypeName.get(genericStringInner.getGenericReturnType()); assertNotEquals(TypeName.get(genericStringInner.getGenericReturnType()), TypeName.get(getClass().getDeclaredMethod("testGenericIntInner").getGenericReturnType())); // Make sure the generic argument is present assertThat(genericTypeName.toString()).isEqualTo( TestGeneric.class.getCanonicalName() + ".Inner"); } @Test public void innerGenericInGenericType() throws Exception { Method genericStringInner = getClass().getDeclaredMethod("testGenericInnerLong"); TypeName.get(genericStringInner.getReturnType()); TypeName genericTypeName = TypeName.get(genericStringInner.getGenericReturnType()); assertNotEquals(TypeName.get(genericStringInner.getGenericReturnType()), TypeName.get(getClass().getDeclaredMethod("testGenericInnerInt").getGenericReturnType())); // Make sure the generic argument is present assertThat(genericTypeName.toString()).isEqualTo( TestGeneric.class.getCanonicalName() + ".InnerGeneric"); } @Test public void innerStaticInGenericType() throws Exception { Method staticInGeneric = getClass().getDeclaredMethod("testNestedNonGeneric"); TypeName.get(staticInGeneric.getReturnType()); TypeName typeName = TypeName.get(staticInGeneric.getGenericReturnType()); // Make sure there are no generic arguments assertThat(typeName.toString()).isEqualTo( TestGeneric.class.getCanonicalName() + ".NestedNonGeneric"); } @Test public void equalsAndHashCodePrimitive() { assertEqualsHashCodeAndToString(TypeName.BOOLEAN, TypeName.BOOLEAN); assertEqualsHashCodeAndToString(TypeName.BYTE, TypeName.BYTE); assertEqualsHashCodeAndToString(TypeName.CHAR, TypeName.CHAR); assertEqualsHashCodeAndToString(TypeName.DOUBLE, TypeName.DOUBLE); assertEqualsHashCodeAndToString(TypeName.FLOAT, TypeName.FLOAT); assertEqualsHashCodeAndToString(TypeName.INT, TypeName.INT); assertEqualsHashCodeAndToString(TypeName.LONG, TypeName.LONG); assertEqualsHashCodeAndToString(TypeName.SHORT, TypeName.SHORT); assertEqualsHashCodeAndToString(TypeName.VOID, TypeName.VOID); } @Test public void equalsAndHashCodeArrayTypeName() { assertEqualsHashCodeAndToString(ArrayTypeName.of(Object.class), ArrayTypeName.of(Object.class)); assertEqualsHashCodeAndToString(TypeName.get(Object[].class), ArrayTypeName.of(Object.class)); } @Test public void equalsAndHashCodeClassName() { assertEqualsHashCodeAndToString(ClassName.get(Object.class), ClassName.get(Object.class)); assertEqualsHashCodeAndToString(TypeName.get(Object.class), ClassName.get(Object.class)); assertEqualsHashCodeAndToString(ClassName.bestGuess("java.lang.Object"), ClassName.get(Object.class)); } @Test public void equalsAndHashCodeParameterizedTypeName() { assertEqualsHashCodeAndToString(ParameterizedTypeName.get(Object.class), ParameterizedTypeName.get(Object.class)); assertEqualsHashCodeAndToString(ParameterizedTypeName.get(Set.class, UUID.class), ParameterizedTypeName.get(Set.class, UUID.class)); assertNotEquals(ClassName.get(List.class), ParameterizedTypeName.get(List.class, String.class)); } @Test public void equalsAndHashCodeTypeVariableName() { assertEqualsHashCodeAndToString(TypeVariableName.get(Object.class), TypeVariableName.get(Object.class)); TypeVariableName typeVar1 = TypeVariableName.get("T", Comparator.class, Serializable.class); TypeVariableName typeVar2 = TypeVariableName.get("T", Comparator.class, Serializable.class); assertEqualsHashCodeAndToString(typeVar1, typeVar2); } @Test public void equalsAndHashCodeWildcardTypeName() { assertEqualsHashCodeAndToString(WildcardTypeName.subtypeOf(Object.class), WildcardTypeName.subtypeOf(Object.class)); assertEqualsHashCodeAndToString(WildcardTypeName.subtypeOf(Serializable.class), WildcardTypeName.subtypeOf(Serializable.class)); assertEqualsHashCodeAndToString(WildcardTypeName.supertypeOf(String.class), WildcardTypeName.supertypeOf(String.class)); } @Test public void isPrimitive() throws Exception { assertThat(TypeName.INT.isPrimitive()).isTrue(); assertThat(ClassName.get("java.lang", "Integer").isPrimitive()).isFalse(); assertThat(ClassName.get("java.lang", "String").isPrimitive()).isFalse(); assertThat(TypeName.VOID.isPrimitive()).isFalse(); assertThat(ClassName.get("java.lang", "Void").isPrimitive()).isFalse(); } @Test public void isBoxedPrimitive() throws Exception { assertThat(TypeName.INT.isBoxedPrimitive()).isFalse(); assertThat(ClassName.get("java.lang", "Integer").isBoxedPrimitive()).isTrue(); assertThat(ClassName.get("java.lang", "String").isBoxedPrimitive()).isFalse(); assertThat(TypeName.VOID.isBoxedPrimitive()).isFalse(); assertThat(ClassName.get("java.lang", "Void").isBoxedPrimitive()).isFalse(); } private void assertEqualsHashCodeAndToString(TypeName a, TypeName b) { assertEquals(a.toString(), b.toString()); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); assertFalse(a.equals(null)); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/TypeSpecTest.java000066400000000000000000002452451335545476000256160ustar00rootroot00000000000000/* * Copyright (C) 2015 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. */ package com.squareup.javapoet; import com.google.common.collect.ImmutableMap; import com.google.testing.compile.CompilationRule; import java.io.IOException; import java.io.Serializable; import java.math.BigDecimal; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.EventListener; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.concurrent.Callable; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mockito; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; @RunWith(JUnit4.class) public final class TypeSpecTest { private final String tacosPackage = "com.squareup.tacos"; private static final String donutsPackage = "com.squareup.donuts"; @Rule public final CompilationRule compilation = new CompilationRule(); private TypeElement getElement(Class clazz) { return compilation.getElements().getTypeElement(clazz.getCanonicalName()); } @Test public void basic() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .returns(String.class) .addCode("return $S;\n", "taco") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " @Override\n" + " public final String toString() {\n" + " return \"taco\";\n" + " }\n" + "}\n"); assertEquals(472949424, taco.hashCode()); // update expected number if source changes } @Test public void interestingTypes() throws Exception { TypeName listOfAny = ParameterizedTypeName.get( ClassName.get(List.class), WildcardTypeName.subtypeOf(Object.class)); TypeName listOfExtends = ParameterizedTypeName.get( ClassName.get(List.class), WildcardTypeName.subtypeOf(Serializable.class)); TypeName listOfSuper = ParameterizedTypeName.get(ClassName.get(List.class), WildcardTypeName.supertypeOf(String.class)); TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(listOfAny, "extendsObject") .addField(listOfExtends, "extendsSerializable") .addField(listOfSuper, "superString") .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.Serializable;\n" + "import java.lang.String;\n" + "import java.util.List;\n" + "\n" + "class Taco {\n" + " List extendsObject;\n" + "\n" + " List extendsSerializable;\n" + "\n" + " List superString;\n" + "}\n"); } @Test public void anonymousInnerClass() throws Exception { ClassName foo = ClassName.get(tacosPackage, "Foo"); ClassName bar = ClassName.get(tacosPackage, "Bar"); ClassName thingThang = ClassName.get(tacosPackage, "Thing", "Thang"); TypeName thingThangOfFooBar = ParameterizedTypeName.get(thingThang, foo, bar); ClassName thung = ClassName.get(tacosPackage, "Thung"); ClassName simpleThung = ClassName.get(tacosPackage, "SimpleThung"); TypeName thungOfSuperBar = ParameterizedTypeName.get(thung, WildcardTypeName.supertypeOf(bar)); TypeName thungOfSuperFoo = ParameterizedTypeName.get(thung, WildcardTypeName.supertypeOf(foo)); TypeName simpleThungOfBar = ParameterizedTypeName.get(simpleThung, bar); ParameterSpec thungParameter = ParameterSpec.builder(thungOfSuperFoo, "thung") .addModifiers(Modifier.FINAL) .build(); TypeSpec aSimpleThung = TypeSpec.anonymousClassBuilder(CodeBlock.of("$N", thungParameter)) .superclass(simpleThungOfBar) .addMethod(MethodSpec.methodBuilder("doSomething") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .addParameter(bar, "bar") .addCode("/* code snippets */\n") .build()) .build(); TypeSpec aThingThang = TypeSpec.anonymousClassBuilder("") .superclass(thingThangOfFooBar) .addMethod(MethodSpec.methodBuilder("call") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(thungOfSuperBar) .addParameter(thungParameter) .addCode("return $L;\n", aSimpleThung) .build()) .build(); TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(FieldSpec.builder(thingThangOfFooBar, "NAME") .addModifiers(Modifier.STATIC, Modifier.FINAL, Modifier.FINAL) .initializer("$L", aThingThang) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "\n" + "class Taco {\n" + " static final Thing.Thang NAME = new Thing.Thang() {\n" + " @Override\n" + " public Thung call(final Thung thung) {\n" + " return new SimpleThung(thung) {\n" + " @Override\n" + " public void doSomething(Bar bar) {\n" + " /* code snippets */\n" + " }\n" + " };\n" + " }\n" + " };\n" + "}\n"); } @Test public void annotatedParameters() throws Exception { TypeSpec service = TypeSpec.classBuilder("Foo") .addMethod(MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(long.class, "id") .addParameter(ParameterSpec.builder(String.class, "one") .addAnnotation(ClassName.get(tacosPackage, "Ping")) .build()) .addParameter(ParameterSpec.builder(String.class, "two") .addAnnotation(ClassName.get(tacosPackage, "Ping")) .build()) .addParameter(ParameterSpec.builder(String.class, "three") .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "Pong")) .addMember("value", "$S", "pong") .build()) .build()) .addParameter(ParameterSpec.builder(String.class, "four") .addAnnotation(ClassName.get(tacosPackage, "Ping")) .build()) .addCode("/* code snippets */\n") .build()) .build(); assertThat(toString(service)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Foo {\n" + " public Foo(long id, @Ping String one, @Ping String two, @Pong(\"pong\") String three,\n" + " @Ping String four) {\n" + " /* code snippets */\n" + " }\n" + "}\n"); } /** * We had a bug where annotations were preventing us from doing the right thing when resolving * imports. https://github.com/square/javapoet/issues/422 */ @Test public void annotationsAndJavaLangTypes() throws Exception { ClassName freeRange = ClassName.get("javax.annotation", "FreeRange"); TypeSpec taco = TypeSpec.classBuilder("EthicalTaco") .addField(ClassName.get(String.class) .annotated(AnnotationSpec.builder(freeRange).build()), "meat") .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "import javax.annotation.FreeRange;\n" + "\n" + "class EthicalTaco {\n" + " @FreeRange String meat;\n" + "}\n"); } @Test public void retrofitStyleInterface() throws Exception { ClassName observable = ClassName.get(tacosPackage, "Observable"); ClassName fooBar = ClassName.get(tacosPackage, "FooBar"); ClassName thing = ClassName.get(tacosPackage, "Thing"); ClassName things = ClassName.get(tacosPackage, "Things"); ClassName map = ClassName.get("java.util", "Map"); ClassName string = ClassName.get("java.lang", "String"); ClassName headers = ClassName.get(tacosPackage, "Headers"); ClassName post = ClassName.get(tacosPackage, "POST"); ClassName body = ClassName.get(tacosPackage, "Body"); ClassName queryMap = ClassName.get(tacosPackage, "QueryMap"); ClassName header = ClassName.get(tacosPackage, "Header"); TypeSpec service = TypeSpec.interfaceBuilder("Service") .addMethod(MethodSpec.methodBuilder("fooBar") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addAnnotation(AnnotationSpec.builder(headers) .addMember("value", "$S", "Accept: application/json") .addMember("value", "$S", "User-Agent: foobar") .build()) .addAnnotation(AnnotationSpec.builder(post) .addMember("value", "$S", "/foo/bar") .build()) .returns(ParameterizedTypeName.get(observable, fooBar)) .addParameter(ParameterSpec.builder(ParameterizedTypeName.get(things, thing), "things") .addAnnotation(body) .build()) .addParameter(ParameterSpec.builder( ParameterizedTypeName.get(map, string, string), "query") .addAnnotation(AnnotationSpec.builder(queryMap) .addMember("encodeValues", "false") .build()) .build()) .addParameter(ParameterSpec.builder(string, "authorization") .addAnnotation(AnnotationSpec.builder(header) .addMember("value", "$S", "Authorization") .build()) .build()) .build()) .build(); assertThat(toString(service)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "import java.util.Map;\n" + "\n" + "interface Service {\n" + " @Headers({\n" + " \"Accept: application/json\",\n" + " \"User-Agent: foobar\"\n" + " })\n" + " @POST(\"/foo/bar\")\n" + " Observable fooBar(@Body Things things,\n" + " @QueryMap(encodeValues = false) Map query,\n" + " @Header(\"Authorization\") String authorization);\n" + "}\n"); } @Test public void annotatedField() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(FieldSpec.builder(String.class, "thing", Modifier.PRIVATE, Modifier.FINAL) .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "JsonAdapter")) .addMember("value", "$T.class", ClassName.get(tacosPackage, "Foo")) .build()) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " @JsonAdapter(Foo.class)\n" + " private final String thing;\n" + "}\n"); } @Test public void annotatedClass() throws Exception { ClassName someType = ClassName.get(tacosPackage, "SomeType"); TypeSpec taco = TypeSpec.classBuilder("Foo") .addAnnotation(AnnotationSpec.builder(ClassName.get(tacosPackage, "Something")) .addMember("hi", "$T.$N", someType, "FIELD") .addMember("hey", "$L", 12) .addMember("hello", "$S", "goodbye") .build()) .addModifiers(Modifier.PUBLIC) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@Something(\n" + " hi = SomeType.FIELD,\n" + " hey = 12,\n" + " hello = \"goodbye\"\n" + ")\n" + "public class Foo {\n" + "}\n"); } @Test public void addAnnotationDisallowsNull() { try { TypeSpec.classBuilder("Foo").addAnnotation((AnnotationSpec) null); fail(); } catch (NullPointerException expected) { assertThat(expected).hasMessageThat().isEqualTo("annotationSpec == null"); } try { TypeSpec.classBuilder("Foo").addAnnotation((ClassName) null); fail(); } catch (NullPointerException expected) { assertThat(expected).hasMessageThat().isEqualTo("type == null"); } try { TypeSpec.classBuilder("Foo").addAnnotation((Class) null); fail(); } catch (NullPointerException expected) { assertThat(expected).hasMessageThat().isEqualTo("clazz == null"); } } @Test public void enumWithSubclassing() throws Exception { TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("") .addJavadoc("Avalanche!\n") .build()) .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat") .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addCode("return $S;\n", "paper airplane!") .build()) .build()) .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace sign") .build()) .addField(String.class, "handPosition", Modifier.PRIVATE, Modifier.FINAL) .addMethod(MethodSpec.constructorBuilder() .addParameter(String.class, "handPosition") .addCode("this.handPosition = handPosition;\n") .build()) .addMethod(MethodSpec.constructorBuilder() .addCode("this($S);\n", "fist") .build()) .build(); assertThat(toString(roshambo)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "public enum Roshambo {\n" + " /**\n" + " * Avalanche!\n" + " */\n" + " ROCK,\n" + "\n" + " PAPER(\"flat\") {\n" + " @Override\n" + " public String toString() {\n" + " return \"paper airplane!\";\n" + " }\n" + " },\n" + "\n" + " SCISSORS(\"peace sign\");\n" + "\n" + " private final String handPosition;\n" + "\n" + " Roshambo(String handPosition) {\n" + " this.handPosition = handPosition;\n" + " }\n" + "\n" + " Roshambo() {\n" + " this(\"fist\");\n" + " }\n" + "}\n"); } /** https://github.com/square/javapoet/issues/193 */ @Test public void enumsMayDefineAbstractMethods() throws Exception { TypeSpec roshambo = TypeSpec.enumBuilder("Tortilla") .addModifiers(Modifier.PUBLIC) .addEnumConstant("CORN", TypeSpec.anonymousClassBuilder("") .addMethod(MethodSpec.methodBuilder("fold") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .build()) .build()) .addMethod(MethodSpec.methodBuilder("fold") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .build()) .build(); assertThat(toString(roshambo)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "\n" + "public enum Tortilla {\n" + " CORN {\n" + " @Override\n" + " public void fold() {\n" + " }\n" + " };\n" + "\n" + " public abstract void fold();\n" + "}\n"); } @Test public void enumConstantsRequired() throws Exception { try { TypeSpec.enumBuilder("Roshambo") .build(); fail(); } catch (IllegalArgumentException expected) { } } @Test public void onlyEnumsMayHaveEnumConstants() throws Exception { try { TypeSpec.classBuilder("Roshambo") .addEnumConstant("ROCK") .build(); fail(); } catch (IllegalStateException expected) { } } @Test public void enumWithMembersButNoConstructorCall() throws Exception { TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo") .addEnumConstant("SPOCK", TypeSpec.anonymousClassBuilder("") .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addCode("return $S;\n", "west side") .build()) .build()) .build(); assertThat(toString(roshambo)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "enum Roshambo {\n" + " SPOCK {\n" + " @Override\n" + " public String toString() {\n" + " return \"west side\";\n" + " }\n" + " }\n" + "}\n"); } /** https://github.com/square/javapoet/issues/253 */ @Test public void enumWithAnnotatedValues() throws Exception { TypeSpec roshambo = TypeSpec.enumBuilder("Roshambo") .addModifiers(Modifier.PUBLIC) .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("") .addAnnotation(Deprecated.class) .build()) .addEnumConstant("PAPER") .addEnumConstant("SCISSORS") .build(); assertThat(toString(roshambo)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Deprecated;\n" + "\n" + "public enum Roshambo {\n" + " @Deprecated\n" + " ROCK,\n" + "\n" + " PAPER,\n" + "\n" + " SCISSORS\n" + "}\n"); } @Test public void methodThrows() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addModifiers(Modifier.ABSTRACT) .addMethod(MethodSpec.methodBuilder("throwOne") .addException(IOException.class) .build()) .addMethod(MethodSpec.methodBuilder("throwTwo") .addException(IOException.class) .addException(ClassName.get(tacosPackage, "SourCreamException")) .build()) .addMethod(MethodSpec.methodBuilder("abstractThrow") .addModifiers(Modifier.ABSTRACT) .addException(IOException.class) .build()) .addMethod(MethodSpec.methodBuilder("nativeThrow") .addModifiers(Modifier.NATIVE) .addException(IOException.class) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.IOException;\n" + "\n" + "abstract class Taco {\n" + " void throwOne() throws IOException {\n" + " }\n" + "\n" + " void throwTwo() throws IOException, SourCreamException {\n" + " }\n" + "\n" + " abstract void abstractThrow() throws IOException;\n" + "\n" + " native void nativeThrow() throws IOException;\n" + "}\n"); } @Test public void typeVariables() throws Exception { TypeVariableName t = TypeVariableName.get("T"); TypeVariableName p = TypeVariableName.get("P", Number.class); ClassName location = ClassName.get(tacosPackage, "Location"); TypeSpec typeSpec = TypeSpec.classBuilder("Location") .addTypeVariable(t) .addTypeVariable(p) .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), p)) .addField(t, "label") .addField(p, "x") .addField(p, "y") .addMethod(MethodSpec.methodBuilder("compareTo") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(int.class) .addParameter(p, "p") .addCode("return 0;\n") .build()) .addMethod(MethodSpec.methodBuilder("of") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .addTypeVariable(t) .addTypeVariable(p) .returns(ParameterizedTypeName.get(location, t, p)) .addParameter(t, "label") .addParameter(p, "x") .addParameter(p, "y") .addCode("throw new $T($S);\n", UnsupportedOperationException.class, "TODO") .build()) .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Comparable;\n" + "import java.lang.Number;\n" + "import java.lang.Override;\n" + "import java.lang.UnsupportedOperationException;\n" + "\n" + "class Location implements Comparable

{\n" + " T label;\n" + "\n" + " P x;\n" + "\n" + " P y;\n" + "\n" + " @Override\n" + " public int compareTo(P p) {\n" + " return 0;\n" + " }\n" + "\n" + " public static Location of(T label, P x, P y) {\n" + " throw new UnsupportedOperationException(\"TODO\");\n" + " }\n" + "}\n"); } @Test public void typeVariableWithBounds() { AnnotationSpec a = AnnotationSpec.builder(ClassName.get("com.squareup.tacos", "A")).build(); TypeVariableName p = TypeVariableName.get("P", Number.class); TypeVariableName q = (TypeVariableName) TypeVariableName.get("Q", Number.class).annotated(a); TypeSpec typeSpec = TypeSpec.classBuilder("Location") .addTypeVariable(p.withBounds(Comparable.class)) .addTypeVariable(q.withBounds(Comparable.class)) .addField(p, "x") .addField(q, "y") .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Comparable;\n" + "import java.lang.Number;\n" + "\n" + "class Location

{\n" + " P x;\n" + "\n" + " @A Q y;\n" + "}\n"); } @Test public void classImplementsExtends() throws Exception { ClassName taco = ClassName.get(tacosPackage, "Taco"); ClassName food = ClassName.get("com.squareup.tacos", "Food"); TypeSpec typeSpec = TypeSpec.classBuilder("Taco") .addModifiers(Modifier.ABSTRACT) .superclass(ParameterizedTypeName.get(ClassName.get(AbstractSet.class), food)) .addSuperinterface(Serializable.class) .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), taco)) .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.Serializable;\n" + "import java.lang.Comparable;\n" + "import java.util.AbstractSet;\n" + "\n" + "abstract class Taco extends AbstractSet " + "implements Serializable, Comparable {\n" + "}\n"); } @Test public void classImplementsNestedClass() throws Exception { ClassName outer = ClassName.get(tacosPackage, "Outer"); ClassName inner = outer.nestedClass("Inner"); ClassName callable = ClassName.get(Callable.class); TypeSpec typeSpec = TypeSpec.classBuilder("Outer") .superclass(ParameterizedTypeName.get(callable, inner)) .addType(TypeSpec.classBuilder("Inner") .addModifiers(Modifier.STATIC) .build()) .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.concurrent.Callable;\n" + "\n" + "class Outer extends Callable {\n" + " static class Inner {\n" + " }\n" + "}\n"); } @Test public void enumImplements() throws Exception { TypeSpec typeSpec = TypeSpec.enumBuilder("Food") .addSuperinterface(Serializable.class) .addSuperinterface(Cloneable.class) .addEnumConstant("LEAN_GROUND_BEEF") .addEnumConstant("SHREDDED_CHEESE") .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.Serializable;\n" + "import java.lang.Cloneable;\n" + "\n" + "enum Food implements Serializable, Cloneable {\n" + " LEAN_GROUND_BEEF,\n" + "\n" + " SHREDDED_CHEESE\n" + "}\n"); } @Test public void interfaceExtends() throws Exception { ClassName taco = ClassName.get(tacosPackage, "Taco"); TypeSpec typeSpec = TypeSpec.interfaceBuilder("Taco") .addSuperinterface(Serializable.class) .addSuperinterface(ParameterizedTypeName.get(ClassName.get(Comparable.class), taco)) .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.Serializable;\n" + "import java.lang.Comparable;\n" + "\n" + "interface Taco extends Serializable, Comparable {\n" + "}\n"); } @Test public void nestedClasses() throws Exception { ClassName taco = ClassName.get(tacosPackage, "Combo", "Taco"); ClassName topping = ClassName.get(tacosPackage, "Combo", "Taco", "Topping"); ClassName chips = ClassName.get(tacosPackage, "Combo", "Chips"); ClassName sauce = ClassName.get(tacosPackage, "Combo", "Sauce"); TypeSpec typeSpec = TypeSpec.classBuilder("Combo") .addField(taco, "taco") .addField(chips, "chips") .addType(TypeSpec.classBuilder(taco.simpleName()) .addModifiers(Modifier.STATIC) .addField(ParameterizedTypeName.get(ClassName.get(List.class), topping), "toppings") .addField(sauce, "sauce") .addType(TypeSpec.enumBuilder(topping.simpleName()) .addEnumConstant("SHREDDED_CHEESE") .addEnumConstant("LEAN_GROUND_BEEF") .build()) .build()) .addType(TypeSpec.classBuilder(chips.simpleName()) .addModifiers(Modifier.STATIC) .addField(topping, "topping") .addField(sauce, "dippingSauce") .build()) .addType(TypeSpec.enumBuilder(sauce.simpleName()) .addEnumConstant("SOUR_CREAM") .addEnumConstant("SALSA") .addEnumConstant("QUESO") .addEnumConstant("MILD") .addEnumConstant("FIRE") .build()) .build(); assertThat(toString(typeSpec)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.List;\n" + "\n" + "class Combo {\n" + " Taco taco;\n" + "\n" + " Chips chips;\n" + "\n" + " static class Taco {\n" + " List toppings;\n" + "\n" + " Sauce sauce;\n" + "\n" + " enum Topping {\n" + " SHREDDED_CHEESE,\n" + "\n" + " LEAN_GROUND_BEEF\n" + " }\n" + " }\n" + "\n" + " static class Chips {\n" + " Taco.Topping topping;\n" + "\n" + " Sauce dippingSauce;\n" + " }\n" + "\n" + " enum Sauce {\n" + " SOUR_CREAM,\n" + "\n" + " SALSA,\n" + "\n" + " QUESO,\n" + "\n" + " MILD,\n" + "\n" + " FIRE\n" + " }\n" + "}\n"); } @Test public void annotation() throws Exception { TypeSpec annotation = TypeSpec.annotationBuilder("MyAnnotation") .addModifiers(Modifier.PUBLIC) .addMethod(MethodSpec.methodBuilder("test") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .defaultValue("$L", 0) .returns(int.class) .build()) .build(); assertThat(toString(annotation)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "public @interface MyAnnotation {\n" + " int test() default 0;\n" + "}\n" ); } @Test public void innerAnnotationInAnnotationDeclaration() throws Exception { TypeSpec bar = TypeSpec.annotationBuilder("Bar") .addMethod(MethodSpec.methodBuilder("value") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .defaultValue("@$T", Deprecated.class) .returns(Deprecated.class) .build()) .build(); assertThat(toString(bar)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Deprecated;\n" + "\n" + "@interface Bar {\n" + " Deprecated value() default @Deprecated;\n" + "}\n" ); } @Test public void annotationWithFields() { FieldSpec field = FieldSpec.builder(int.class, "FOO") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$L", 101) .build(); TypeSpec anno = TypeSpec.annotationBuilder("Anno") .addField(field) .build(); assertThat(toString(anno)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@interface Anno {\n" + " int FOO = 101;\n" + "}\n" ); } @Test public void classCannotHaveDefaultValueForMethod() throws Exception { try { TypeSpec.classBuilder("Tacos") .addMethod(MethodSpec.methodBuilder("test") .addModifiers(Modifier.PUBLIC) .defaultValue("0") .returns(int.class) .build()) .build(); fail(); } catch (IllegalStateException expected) { } } @Test public void classCannotHaveDefaultMethods() throws Exception { try { TypeSpec.classBuilder("Tacos") .addMethod(MethodSpec.methodBuilder("test") .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT) .returns(int.class) .addCode(CodeBlock.builder().addStatement("return 0").build()) .build()) .build(); fail(); } catch (IllegalStateException expected) { } } @Test public void interfaceStaticMethods() throws Exception { TypeSpec bar = TypeSpec.interfaceBuilder("Tacos") .addMethod(MethodSpec.methodBuilder("test") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(int.class) .addCode(CodeBlock.builder().addStatement("return 0").build()) .build()) .build(); assertThat(toString(bar)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "interface Tacos {\n" + " static int test() {\n" + " return 0;\n" + " }\n" + "}\n" ); } @Test public void interfaceDefaultMethods() throws Exception { TypeSpec bar = TypeSpec.interfaceBuilder("Tacos") .addMethod(MethodSpec.methodBuilder("test") .addModifiers(Modifier.PUBLIC, Modifier.DEFAULT) .returns(int.class) .addCode(CodeBlock.builder().addStatement("return 0").build()) .build()) .build(); assertThat(toString(bar)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "interface Tacos {\n" + " default int test() {\n" + " return 0;\n" + " }\n" + "}\n" ); } @Test public void referencedAndDeclaredSimpleNamesConflict() throws Exception { FieldSpec internalTop = FieldSpec.builder( ClassName.get(tacosPackage, "Top"), "internalTop").build(); FieldSpec internalBottom = FieldSpec.builder( ClassName.get(tacosPackage, "Top", "Middle", "Bottom"), "internalBottom").build(); FieldSpec externalTop = FieldSpec.builder( ClassName.get(donutsPackage, "Top"), "externalTop").build(); FieldSpec externalBottom = FieldSpec.builder( ClassName.get(donutsPackage, "Bottom"), "externalBottom").build(); TypeSpec top = TypeSpec.classBuilder("Top") .addField(internalTop) .addField(internalBottom) .addField(externalTop) .addField(externalBottom) .addType(TypeSpec.classBuilder("Middle") .addField(internalTop) .addField(internalBottom) .addField(externalTop) .addField(externalBottom) .addType(TypeSpec.classBuilder("Bottom") .addField(internalTop) .addField(internalBottom) .addField(externalTop) .addField(externalBottom) .build()) .build()) .build(); assertThat(toString(top)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.squareup.donuts.Bottom;\n" + "\n" + "class Top {\n" + " Top internalTop;\n" + "\n" + " Middle.Bottom internalBottom;\n" + "\n" + " com.squareup.donuts.Top externalTop;\n" + "\n" + " Bottom externalBottom;\n" + "\n" + " class Middle {\n" + " Top internalTop;\n" + "\n" + " Bottom internalBottom;\n" + "\n" + " com.squareup.donuts.Top externalTop;\n" + "\n" + " com.squareup.donuts.Bottom externalBottom;\n" + "\n" + " class Bottom {\n" + " Top internalTop;\n" + "\n" + " Bottom internalBottom;\n" + "\n" + " com.squareup.donuts.Top externalTop;\n" + "\n" + " com.squareup.donuts.Bottom externalBottom;\n" + " }\n" + " }\n" + "}\n"); } @Test public void simpleNamesConflictInThisAndOtherPackage() throws Exception { FieldSpec internalOther = FieldSpec.builder( ClassName.get(tacosPackage, "Other"), "internalOther").build(); FieldSpec externalOther = FieldSpec.builder( ClassName.get(donutsPackage, "Other"), "externalOther").build(); TypeSpec gen = TypeSpec.classBuilder("Gen") .addField(internalOther) .addField(externalOther) .build(); assertThat(toString(gen)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Gen {\n" + " Other internalOther;\n" + "\n" + " com.squareup.donuts.Other externalOther;\n" + "}\n"); } @Test public void originatingElementsIncludesThoseOfNestedTypes() { Element outerElement = Mockito.mock(Element.class); Element innerElement = Mockito.mock(Element.class); TypeSpec outer = TypeSpec.classBuilder("Outer") .addOriginatingElement(outerElement) .addType(TypeSpec.classBuilder("Inner") .addOriginatingElement(innerElement) .build()) .build(); assertThat(outer.originatingElements).containsExactly(outerElement, innerElement); } @Test public void intersectionType() { TypeVariableName typeVariable = TypeVariableName.get("T", Comparator.class, Serializable.class); TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("getComparator") .addTypeVariable(typeVariable) .returns(typeVariable) .addCode("return null;\n") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.Serializable;\n" + "import java.util.Comparator;\n" + "\n" + "class Taco {\n" + " T getComparator() {\n" + " return null;\n" + " }\n" + "}\n"); } @Test public void arrayType() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(int[].class, "ints") .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " int[] ints;\n" + "}\n"); } @Test public void javadoc() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addJavadoc("A hard or soft tortilla, loosely folded and filled with whatever {@link \n") .addJavadoc("{@link $T random} tex-mex stuff we could find in the pantry\n", Random.class) .addJavadoc(CodeBlock.of("and some {@link $T} cheese.\n", String.class)) .addField(FieldSpec.builder(boolean.class, "soft") .addJavadoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n") .build()) .addMethod(MethodSpec.methodBuilder("refold") .addJavadoc("Folds the back of this taco to reduce sauce leakage.\n" + "\n" + "

For {@link $T#KOREAN}, the front may also be folded.\n", Locale.class) .addParameter(Locale.class, "locale") .build()) .build(); // Mentioning a type in Javadoc will not cause an import to be added (java.util.Random here), // but the short name will be used if it's already imported (java.util.Locale here). assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.util.Locale;\n" + "\n" + "/**\n" + " * A hard or soft tortilla, loosely folded and filled with whatever {@link \n" + " * {@link java.util.Random random} tex-mex stuff we could find in the pantry\n" + " * and some {@link java.lang.String} cheese.\n" + " */\n" + "class Taco {\n" + " /**\n" + " * True for a soft flour tortilla; false for a crunchy corn tortilla.\n" + " */\n" + " boolean soft;\n" + "\n" + " /**\n" + " * Folds the back of this taco to reduce sauce leakage.\n" + " *\n" + " *

For {@link Locale#KOREAN}, the front may also be folded.\n" + " */\n" + " void refold(Locale locale) {\n" + " }\n" + "}\n"); } @Test public void annotationsInAnnotations() throws Exception { ClassName beef = ClassName.get(tacosPackage, "Beef"); ClassName chicken = ClassName.get(tacosPackage, "Chicken"); ClassName option = ClassName.get(tacosPackage, "Option"); ClassName mealDeal = ClassName.get(tacosPackage, "MealDeal"); TypeSpec menu = TypeSpec.classBuilder("Menu") .addAnnotation(AnnotationSpec.builder(mealDeal) .addMember("price", "$L", 500) .addMember("options", "$L", AnnotationSpec.builder(option) .addMember("name", "$S", "taco") .addMember("meat", "$T.class", beef) .build()) .addMember("options", "$L", AnnotationSpec.builder(option) .addMember("name", "$S", "quesadilla") .addMember("meat", "$T.class", chicken) .build()) .build()) .build(); assertThat(toString(menu)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "@MealDeal(\n" + " price = 500,\n" + " options = {\n" + " @Option(name = \"taco\", meat = Beef.class),\n" + " @Option(name = \"quesadilla\", meat = Chicken.class)\n" + " }\n" + ")\n" + "class Menu {\n" + "}\n"); } @Test public void varargs() throws Exception { TypeSpec taqueria = TypeSpec.classBuilder("Taqueria") .addMethod(MethodSpec.methodBuilder("prepare") .addParameter(int.class, "workers") .addParameter(Runnable[].class, "jobs") .varargs() .build()) .build(); assertThat(toString(taqueria)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Runnable;\n" + "\n" + "class Taqueria {\n" + " void prepare(int workers, Runnable... jobs) {\n" + " }\n" + "}\n"); } @Test public void codeBlocks() throws Exception { CodeBlock ifBlock = CodeBlock.builder() .beginControlFlow("if (!a.equals(b))") .addStatement("return i") .endControlFlow() .build(); CodeBlock methodBody = CodeBlock.builder() .addStatement("$T size = $T.min(listA.size(), listB.size())", int.class, Math.class) .beginControlFlow("for ($T i = 0; i < size; i++)", int.class) .addStatement("$T $N = $N.get(i)", String.class, "a", "listA") .addStatement("$T $N = $N.get(i)", String.class, "b", "listB") .add("$L", ifBlock) .endControlFlow() .addStatement("return size") .build(); CodeBlock fieldBlock = CodeBlock.builder() .add("$>$>") .add("\n$T.<$T, $T>builder()$>$>", ImmutableMap.class, String.class, String.class) .add("\n.add($S, $S)", '\'', "'") .add("\n.add($S, $S)", '&', "&") .add("\n.add($S, $S)", '<', "<") .add("\n.add($S, $S)", '>', ">") .add("\n.build()$<$<") .add("$<$<") .build(); FieldSpec escapeHtml = FieldSpec.builder(ParameterizedTypeName.get( Map.class, String.class, String.class), "ESCAPE_HTML") .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .initializer(fieldBlock) .build(); TypeSpec util = TypeSpec.classBuilder("Util") .addField(escapeHtml) .addMethod(MethodSpec.methodBuilder("commonPrefixLength") .returns(int.class) .addParameter(ParameterizedTypeName.get(List.class, String.class), "listA") .addParameter(ParameterizedTypeName.get(List.class, String.class), "listB") .addCode(methodBody) .build()) .build(); assertThat(toString(util)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import com.google.common.collect.ImmutableMap;\n" + "import java.lang.Math;\n" + "import java.lang.String;\n" + "import java.util.List;\n" + "import java.util.Map;\n" + "\n" + "class Util {\n" + " private static final Map ESCAPE_HTML = \n" + " ImmutableMap.builder()\n" + " .add(\"\'\", \"'\")\n" + " .add(\"&\", \"&\")\n" + " .add(\"<\", \"<\")\n" + " .add(\">\", \">\")\n" + " .build();\n" + "\n" + " int commonPrefixLength(List listA, List listB) {\n" + " int size = Math.min(listA.size(), listB.size());\n" + " for (int i = 0; i < size; i++) {\n" + " String a = listA.get(i);\n" + " String b = listB.get(i);\n" + " if (!a.equals(b)) {\n" + " return i;\n" + " }\n" + " }\n" + " return size;\n" + " }\n" + "}\n"); } @Test public void indexedElseIf() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("choices") .beginControlFlow("if ($1L != null || $1L == $2L)", "taco", "otherTaco") .addStatement("$T.out.println($S)", System.class, "only one taco? NOO!") .nextControlFlow("else if ($1L.$3L && $2L.$3L)", "taco", "otherTaco", "isSupreme()") .addStatement("$T.out.println($S)", System.class, "taco heaven") .endControlFlow() .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.System;\n" + "\n" + "class Taco {\n" + " void choices() {\n" + " if (taco != null || taco == otherTaco) {\n" + " System.out.println(\"only one taco? NOO!\");\n" + " } else if (taco.isSupreme() && otherTaco.isSupreme()) {\n" + " System.out.println(\"taco heaven\");\n" + " }\n" + " }\n" + "}\n"); } @Test public void elseIf() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("choices") .beginControlFlow("if (5 < 4) ") .addStatement("$T.out.println($S)", System.class, "wat") .nextControlFlow("else if (5 < 6)") .addStatement("$T.out.println($S)", System.class, "hello") .endControlFlow() .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.System;\n" + "\n" + "class Taco {\n" + " void choices() {\n" + " if (5 < 4) {\n" + " System.out.println(\"wat\");\n" + " } else if (5 < 6) {\n" + " System.out.println(\"hello\");\n" + " }\n" + " }\n" + "}\n"); } @Test public void doWhile() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("loopForever") .beginControlFlow("do") .addStatement("$T.out.println($S)", System.class, "hello") .endControlFlow("while (5 < 6)") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.System;\n" + "\n" + "class Taco {\n" + " void loopForever() {\n" + " do {\n" + " System.out.println(\"hello\");\n" + " } while (5 < 6);\n" + " }\n" + "}\n"); } @Test public void inlineIndent() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("inlineIndent") .addCode("if (3 < 4) {\n$>$T.out.println($S);\n$<}\n", System.class, "hello") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.System;\n" + "\n" + "class Taco {\n" + " void inlineIndent() {\n" + " if (3 < 4) {\n" + " System.out.println(\"hello\");\n" + " }\n" + " }\n" + "}\n"); } @Test public void defaultModifiersForInterfaceMembers() throws Exception { TypeSpec taco = TypeSpec.interfaceBuilder("Taco") .addField(FieldSpec.builder(String.class, "SHELL") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .initializer("$S", "crunchy corn") .build()) .addMethod(MethodSpec.methodBuilder("fold") .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .build()) .addType(TypeSpec.classBuilder("Topping") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "interface Taco {\n" + " String SHELL = \"crunchy corn\";\n" + "\n" + " void fold();\n" + "\n" + " class Topping {\n" + " }\n" + "}\n"); } @Test public void defaultModifiersForMemberInterfacesAndEnums() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addType(TypeSpec.classBuilder("Meat") .addModifiers(Modifier.STATIC) .build()) .addType(TypeSpec.interfaceBuilder("Tortilla") .addModifiers(Modifier.STATIC) .build()) .addType(TypeSpec.enumBuilder("Topping") .addModifiers(Modifier.STATIC) .addEnumConstant("SALSA") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " static class Meat {\n" + " }\n" + "\n" + " interface Tortilla {\n" + " }\n" + "\n" + " enum Topping {\n" + " SALSA\n" + " }\n" + "}\n"); } @Test public void membersOrdering() throws Exception { // Hand out names in reverse-alphabetical order to defend against unexpected sorting. TypeSpec taco = TypeSpec.classBuilder("Members") .addType(TypeSpec.classBuilder("Z").build()) .addType(TypeSpec.classBuilder("Y").build()) .addField(String.class, "X", Modifier.STATIC) .addField(String.class, "W") .addField(String.class, "V", Modifier.STATIC) .addField(String.class, "U") .addMethod(MethodSpec.methodBuilder("T").addModifiers(Modifier.STATIC).build()) .addMethod(MethodSpec.methodBuilder("S").build()) .addMethod(MethodSpec.methodBuilder("R").addModifiers(Modifier.STATIC).build()) .addMethod(MethodSpec.methodBuilder("Q").build()) .addMethod(MethodSpec.constructorBuilder().addParameter(int.class, "p").build()) .addMethod(MethodSpec.constructorBuilder().addParameter(long.class, "o").build()) .build(); // Static fields, instance fields, constructors, methods, classes. assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Members {\n" + " static String X;\n" + "\n" + " static String V;\n" + "\n" + " String W;\n" + "\n" + " String U;\n" + "\n" + " Members(int p) {\n" + " }\n" + "\n" + " Members(long o) {\n" + " }\n" + "\n" + " static void T() {\n" + " }\n" + "\n" + " void S() {\n" + " }\n" + "\n" + " static void R() {\n" + " }\n" + "\n" + " void Q() {\n" + " }\n" + "\n" + " class Z {\n" + " }\n" + "\n" + " class Y {\n" + " }\n" + "}\n"); } @Test public void nativeMethods() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("nativeInt") .addModifiers(Modifier.NATIVE) .returns(int.class) .build()) // GWT JSNI .addMethod(MethodSpec.methodBuilder("alert") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.NATIVE) .addParameter(String.class, "msg") .addCode(CodeBlock.builder() .add(" /*-{\n") .indent() .addStatement("$$wnd.alert(msg)") .unindent() .add("}-*/") .build()) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " native int nativeInt();\n" + "\n" + " public static native void alert(String msg) /*-{\n" + " $wnd.alert(msg);\n" + " }-*/;\n" + "}\n"); } @Test public void nullStringLiteral() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(FieldSpec.builder(String.class, "NULL") .initializer("$S", (Object) null) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " String NULL = null;\n" + "}\n"); } @Test public void annotationToString() throws Exception { AnnotationSpec annotation = AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "unused") .build(); assertThat(annotation.toString()).isEqualTo("@java.lang.SuppressWarnings(\"unused\")"); } @Test public void codeBlockToString() throws Exception { CodeBlock codeBlock = CodeBlock.builder() .addStatement("$T $N = $S.substring(0, 3)", String.class, "s", "taco") .build(); assertThat(codeBlock.toString()).isEqualTo("java.lang.String s = \"taco\".substring(0, 3);\n"); } @Test public void codeBlockAddStatementOfCodeBlockToString() throws Exception { CodeBlock contents = CodeBlock.of("$T $N = $S.substring(0, 3)", String.class, "s", "taco"); CodeBlock statement = CodeBlock.builder().addStatement(contents).build(); assertThat(statement.toString()).isEqualTo("java.lang.String s = \"taco\".substring(0, 3);\n"); } @Test public void fieldToString() throws Exception { FieldSpec field = FieldSpec.builder(String.class, "s", Modifier.FINAL) .initializer("$S.substring(0, 3)", "taco") .build(); assertThat(field.toString()) .isEqualTo("final java.lang.String s = \"taco\".substring(0, 3);\n"); } @Test public void methodToString() throws Exception { MethodSpec method = MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addStatement("return $S", "taco") .build(); assertThat(method.toString()).isEqualTo("" + "@java.lang.Override\n" + "public java.lang.String toString() {\n" + " return \"taco\";\n" + "}\n"); } @Test public void constructorToString() throws Exception { MethodSpec constructor = MethodSpec.constructorBuilder() .addModifiers(Modifier.PUBLIC) .addParameter(ClassName.get(tacosPackage, "Taco"), "taco") .addStatement("this.$N = $N", "taco", "taco") .build(); assertThat(constructor.toString()).isEqualTo("" + "public Constructor(com.squareup.tacos.Taco taco) {\n" + " this.taco = taco;\n" + "}\n"); } @Test public void parameterToString() throws Exception { ParameterSpec parameter = ParameterSpec.builder(ClassName.get(tacosPackage, "Taco"), "taco") .addModifiers(Modifier.FINAL) .addAnnotation(ClassName.get("javax.annotation", "Nullable")) .build(); assertThat(parameter.toString()) .isEqualTo("@javax.annotation.Nullable final com.squareup.tacos.Taco taco"); } @Test public void classToString() throws Exception { TypeSpec type = TypeSpec.classBuilder("Taco") .build(); assertThat(type.toString()).isEqualTo("" + "class Taco {\n" + "}\n"); } @Test public void anonymousClassToString() throws Exception { TypeSpec type = TypeSpec.anonymousClassBuilder("") .addSuperinterface(Runnable.class) .addMethod(MethodSpec.methodBuilder("run") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .build()) .build(); assertThat(type.toString()).isEqualTo("" + "new java.lang.Runnable() {\n" + " @java.lang.Override\n" + " public void run() {\n" + " }\n" + "}"); } @Test public void interfaceClassToString() throws Exception { TypeSpec type = TypeSpec.interfaceBuilder("Taco") .build(); assertThat(type.toString()).isEqualTo("" + "interface Taco {\n" + "}\n"); } @Test public void annotationDeclarationToString() throws Exception { TypeSpec type = TypeSpec.annotationBuilder("Taco") .build(); assertThat(type.toString()).isEqualTo("" + "@interface Taco {\n" + "}\n"); } private String toString(TypeSpec typeSpec) { return JavaFile.builder(tacosPackage, typeSpec).build().toString(); } @Test public void multilineStatement() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addStatement("return $S\n+ $S\n+ $S\n+ $S\n+ $S", "Taco(", "beef,", "lettuce,", "cheese", ")") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " @Override\n" + " public String toString() {\n" + " return \"Taco(\"\n" + " + \"beef,\"\n" + " + \"lettuce,\"\n" + " + \"cheese\"\n" + " + \")\";\n" + " }\n" + "}\n"); } @Test public void multilineStatementWithAnonymousClass() throws Exception { TypeName stringComparator = ParameterizedTypeName.get(Comparator.class, String.class); TypeName listOfString = ParameterizedTypeName.get(List.class, String.class); TypeSpec prefixComparator = TypeSpec.anonymousClassBuilder("") .addSuperinterface(stringComparator) .addMethod(MethodSpec.methodBuilder("compare") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(int.class) .addParameter(String.class, "a") .addParameter(String.class, "b") .addStatement("return a.substring(0, length)\n" + ".compareTo(b.substring(0, length))") .build()) .build(); TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("comparePrefix") .returns(stringComparator) .addParameter(int.class, "length", Modifier.FINAL) .addStatement("return $L", prefixComparator) .build()) .addMethod(MethodSpec.methodBuilder("sortPrefix") .addParameter(listOfString, "list") .addParameter(int.class, "length", Modifier.FINAL) .addStatement("$T.sort(\nlist,\n$L)", Collections.class, prefixComparator) .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "import java.util.Collections;\n" + "import java.util.Comparator;\n" + "import java.util.List;\n" + "\n" + "class Taco {\n" + " Comparator comparePrefix(final int length) {\n" + " return new Comparator() {\n" + " @Override\n" + " public int compare(String a, String b) {\n" + " return a.substring(0, length)\n" + " .compareTo(b.substring(0, length));\n" + " }\n" + " };\n" + " }\n" + "\n" + " void sortPrefix(List list, final int length) {\n" + " Collections.sort(\n" + " list,\n" + " new Comparator() {\n" + " @Override\n" + " public int compare(String a, String b) {\n" + " return a.substring(0, length)\n" + " .compareTo(b.substring(0, length));\n" + " }\n" + " });\n" + " }\n" + "}\n"); } @Test public void multilineStrings() throws Exception { TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(FieldSpec.builder(String.class, "toppings") .initializer("$S", "shell\nbeef\nlettuce\ncheese\n") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " String toppings = \"shell\\n\"\n" + " + \"beef\\n\"\n" + " + \"lettuce\\n\"\n" + " + \"cheese\\n\";\n" + "}\n"); } @Test public void doubleFieldInitialization() { try { FieldSpec.builder(String.class, "listA") .initializer("foo") .initializer("bar") .build(); fail(); } catch (IllegalStateException expected) { } try { FieldSpec.builder(String.class, "listA") .initializer(CodeBlock.builder().add("foo").build()) .initializer(CodeBlock.builder().add("bar").build()) .build(); fail(); } catch (IllegalStateException expected) { } } @Test public void nullAnnotationsAddition() { try { TypeSpec.classBuilder("Taco").addAnnotations(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("annotationSpecs == null"); } } @Test public void multipleAnnotationAddition() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addAnnotations(Arrays.asList( AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "unchecked") .build(), AnnotationSpec.builder(Deprecated.class).build())) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Deprecated;\n" + "import java.lang.SuppressWarnings;\n" + "\n" + "@SuppressWarnings(\"unchecked\")\n" + "@Deprecated\n" + "class Taco {\n" + "}\n"); } @Test public void nullFieldsAddition() { try { TypeSpec.classBuilder("Taco").addFields(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("fieldSpecs == null"); } } @Test public void multipleFieldAddition() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addFields(Arrays.asList( FieldSpec.builder(int.class, "ANSWER", Modifier.STATIC, Modifier.FINAL).build(), FieldSpec.builder(BigDecimal.class, "price", Modifier.PRIVATE).build())) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.math.BigDecimal;\n" + "\n" + "class Taco {\n" + " static final int ANSWER;\n" + "\n" + " private BigDecimal price;\n" + "}\n"); } @Test public void nullMethodsAddition() { try { TypeSpec.classBuilder("Taco").addMethods(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("methodSpecs == null"); } } @Test public void multipleMethodAddition() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethods(Arrays.asList( MethodSpec.methodBuilder("getAnswer") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(int.class) .addStatement("return $L", 42) .build(), MethodSpec.methodBuilder("getRandomQuantity") .addModifiers(Modifier.PUBLIC) .returns(int.class) .addJavadoc("chosen by fair dice roll ;)") .addStatement("return $L", 4) .build())) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " public static int getAnswer() {\n" + " return 42;\n" + " }\n" + "\n" + " /**\n" + " * chosen by fair dice roll ;) */\n" + " public int getRandomQuantity() {\n" + " return 4;\n" + " }\n" + "}\n"); } @Test public void nullSuperinterfacesAddition() { try { TypeSpec.classBuilder("Taco").addSuperinterfaces(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("superinterfaces == null"); } } @Test public void nullSingleSuperinterfaceAddition() { try { TypeSpec.classBuilder("Taco").addSuperinterface((TypeName) null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("superinterface == null"); } } @Test public void nullInSuperinterfaceIterableAddition() { List superinterfaces = new ArrayList<>(); superinterfaces.add(TypeName.get(List.class)); superinterfaces.add(null); try { TypeSpec.classBuilder("Taco").addSuperinterfaces(superinterfaces); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("superinterface == null"); } } @Test public void multipleSuperinterfaceAddition() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addSuperinterfaces(Arrays.asList( TypeName.get(Serializable.class), TypeName.get(EventListener.class))) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.io.Serializable;\n" + "import java.util.EventListener;\n" + "\n" + "class Taco implements Serializable, EventListener {\n" + "}\n"); } @Test public void nullModifiersAddition() { try { TypeSpec.classBuilder("Taco").addModifiers((Modifier) null); fail(); } catch(IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("modifiers contain null"); } } @Test public void nullTypeVariablesAddition() { try { TypeSpec.classBuilder("Taco").addTypeVariables(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("typeVariables == null"); } } @Test public void multipleTypeVariableAddition() { TypeSpec location = TypeSpec.classBuilder("Location") .addTypeVariables(Arrays.asList( TypeVariableName.get("T"), TypeVariableName.get("P", Number.class))) .build(); assertThat(toString(location)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Number;\n" + "\n" + "class Location {\n" + "}\n"); } @Test public void nullTypesAddition() { try { TypeSpec.classBuilder("Taco").addTypes(null); fail(); } catch (IllegalArgumentException expected) { assertThat(expected.getMessage()) .isEqualTo("typeSpecs == null"); } } @Test public void multipleTypeAddition() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addTypes(Arrays.asList( TypeSpec.classBuilder("Topping").build(), TypeSpec.classBuilder("Sauce").build())) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " class Topping {\n" + " }\n" + "\n" + " class Sauce {\n" + " }\n" + "}\n"); } @Test public void tryCatch() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(MethodSpec.methodBuilder("addTopping") .addParameter(ClassName.get("com.squareup.tacos", "Topping"), "topping") .beginControlFlow("try") .addCode("/* do something tricky with the topping */\n") .nextControlFlow("catch ($T e)", ClassName.get("com.squareup.tacos", "IllegalToppingException")) .endControlFlow() .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " void addTopping(Topping topping) {\n" + " try {\n" + " /* do something tricky with the topping */\n" + " } catch (IllegalToppingException e) {\n" + " }\n" + " }\n" + "}\n"); } @Test public void ifElse() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod( MethodSpec.methodBuilder("isDelicious") .addParameter(TypeName.INT, "count") .returns(TypeName.BOOLEAN) .beginControlFlow("if (count > 0)") .addStatement("return true") .nextControlFlow("else") .addStatement("return false") .endControlFlow() .build() ) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " boolean isDelicious(int count) {\n" + " if (count > 0) {\n" + " return true;\n" + " } else {\n" + " return false;\n" + " }\n" + " }\n" + "}\n"); } @Test public void literalFromAnything() { Object value = new Object() { @Override public String toString() { return "foo"; } }; assertThat(CodeBlock.of("$L", value).toString()).isEqualTo("foo"); } @Test public void nameFromCharSequence() { assertThat(CodeBlock.of("$N", "text").toString()).isEqualTo("text"); } @Test public void nameFromField() { FieldSpec field = FieldSpec.builder(String.class, "field").build(); assertThat(CodeBlock.of("$N", field).toString()).isEqualTo("field"); } @Test public void nameFromParameter() { ParameterSpec parameter = ParameterSpec.builder(String.class, "parameter").build(); assertThat(CodeBlock.of("$N", parameter).toString()).isEqualTo("parameter"); } @Test public void nameFromMethod() { MethodSpec method = MethodSpec.methodBuilder("method") .addModifiers(Modifier.ABSTRACT) .returns(String.class) .build(); assertThat(CodeBlock.of("$N", method).toString()).isEqualTo("method"); } @Test public void nameFromType() { TypeSpec type = TypeSpec.classBuilder("Type").build(); assertThat(CodeBlock.of("$N", type).toString()).isEqualTo("Type"); } @Test public void nameFromUnsupportedType() { try { CodeBlock.builder().add("$N", String.class); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("expected name but was " + String.class); } } @Test public void stringFromAnything() { Object value = new Object() { @Override public String toString() { return "foo"; } }; assertThat(CodeBlock.of("$S", value).toString()).isEqualTo("\"foo\""); } @Test public void stringFromNull() { assertThat(CodeBlock.of("$S", new Object[] {null}).toString()).isEqualTo("null"); } @Test public void typeFromTypeName() { TypeName typeName = TypeName.get(String.class); assertThat(CodeBlock.of("$T", typeName).toString()).isEqualTo("java.lang.String"); } @Test public void typeFromTypeMirror() { TypeMirror mirror = getElement(String.class).asType(); assertThat(CodeBlock.of("$T", mirror).toString()).isEqualTo("java.lang.String"); } @Test public void typeFromTypeElement() { TypeElement element = getElement(String.class); assertThat(CodeBlock.of("$T", element).toString()).isEqualTo("java.lang.String"); } @Test public void typeFromReflectType() { assertThat(CodeBlock.of("$T", String.class).toString()).isEqualTo("java.lang.String"); } @Test public void typeFromUnsupportedType() { try { CodeBlock.builder().add("$T", "java.lang.String"); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("expected type but was java.lang.String"); } } @Test public void tooFewArguments() { try { CodeBlock.builder().add("$S"); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 1 for '$S' not in range (received 0 arguments)"); } } @Test public void unusedArgumentsRelative() { try { CodeBlock.builder().add("$L $L", "a", "b", "c"); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("unused arguments: expected 2, received 3"); } } @Test public void unusedArgumentsIndexed() { try { CodeBlock.builder().add("$1L $2L", "a", "b", "c"); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("unused argument: $3"); } try { CodeBlock.builder().add("$1L $1L $1L", "a", "b", "c"); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("unused arguments: $2, $3"); } try { CodeBlock.builder().add("$3L $1L $3L $1L $3L", "a", "b", "c", "d"); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("unused arguments: $2, $4"); } } @Test public void superClassOnlyValidForClasses() { try { TypeSpec.annotationBuilder("A").superclass(ClassName.get(Object.class)); fail(); } catch (IllegalStateException expected) { } try { TypeSpec.enumBuilder("E").superclass(ClassName.get(Object.class)); fail(); } catch (IllegalStateException expected) { } try { TypeSpec.interfaceBuilder("I").superclass(ClassName.get(Object.class)); fail(); } catch (IllegalStateException expected) { } } @Test public void invalidSuperClass() { try { TypeSpec.classBuilder("foo") .superclass(ClassName.get(List.class)) .superclass(ClassName.get(Map.class)); fail(); } catch (IllegalStateException expected) { } try { TypeSpec.classBuilder("foo") .superclass(TypeName.INT); fail(); } catch (IllegalArgumentException expected) { } } @Test public void staticCodeBlock() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(String.class, "foo", Modifier.PRIVATE) .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .addStaticBlock(CodeBlock.builder() .addStatement("FOO = $S", "FOO") .build()) .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addCode("return FOO;\n") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " private static final String FOO;\n" + "\n" + " static {\n" + " FOO = \"FOO\";\n" + " }\n" + "\n" + " private String foo;\n" + "\n" + " @Override\n" + " public String toString() {\n" + " return FOO;\n" + " }\n" + "}\n"); } @Test public void initializerBlockInRightPlace() { TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(String.class, "foo", Modifier.PRIVATE) .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .addStaticBlock(CodeBlock.builder() .addStatement("FOO = $S", "FOO") .build()) .addMethod(MethodSpec.constructorBuilder().build()) .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addCode("return FOO;\n") .build()) .addInitializerBlock(CodeBlock.builder() .addStatement("foo = $S", "FOO") .build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " private static final String FOO;\n" + "\n" + " static {\n" + " FOO = \"FOO\";\n" + " }\n" + "\n" + " private String foo;\n" + "\n" + " {\n" + " foo = \"FOO\";\n" + " }\n" + "\n" + " Taco() {\n" + " }\n" + "\n" + " @Override\n" + " public String toString() {\n" + " return FOO;\n" + " }\n" + "}\n"); } @Test public void initializersToBuilder() { // Tests if toBuilder() contains correct static and instance initializers TypeSpec taco = TypeSpec.classBuilder("Taco") .addField(String.class, "foo", Modifier.PRIVATE) .addField(String.class, "FOO", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .addStaticBlock(CodeBlock.builder() .addStatement("FOO = $S", "FOO") .build()) .addMethod(MethodSpec.constructorBuilder().build()) .addMethod(MethodSpec.methodBuilder("toString") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC) .returns(String.class) .addCode("return FOO;\n") .build()) .addInitializerBlock(CodeBlock.builder() .addStatement("foo = $S", "FOO") .build()) .build(); TypeSpec recreatedTaco = taco.toBuilder().build(); assertThat(toString(taco)).isEqualTo(toString(recreatedTaco)); TypeSpec initializersAdded = taco.toBuilder() .addInitializerBlock(CodeBlock.builder() .addStatement("foo = $S", "instanceFoo") .build()) .addStaticBlock(CodeBlock.builder() .addStatement("FOO = $S", "staticFoo") .build()) .build(); assertThat(toString(initializersAdded)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.Override;\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " private static final String FOO;\n" + "\n" + " static {\n" + " FOO = \"FOO\";\n" + " }\n" + " static {\n" + " FOO = \"staticFoo\";\n" + " }\n" + "\n" + " private String foo;\n" + "\n" + " {\n" + " foo = \"FOO\";\n" + " }\n" + " {\n" + " foo = \"instanceFoo\";\n" + " }\n" + "\n" + " Taco() {\n" + " }\n" + "\n" + " @Override\n" + " public String toString() {\n" + " return FOO;\n" + " }\n" + "}\n"); } @Test public void initializerBlockUnsupportedExceptionOnInterface() { TypeSpec.Builder interfaceBuilder = TypeSpec.interfaceBuilder("Taco"); try { interfaceBuilder.addInitializerBlock(CodeBlock.builder().build()); fail("Exception expected"); } catch (UnsupportedOperationException e) { } } @Test public void initializerBlockUnsupportedExceptionOnAnnotation() { TypeSpec.Builder annotationBuilder = TypeSpec.annotationBuilder("Taco"); try { annotationBuilder.addInitializerBlock(CodeBlock.builder().build()); fail("Exception expected"); } catch (UnsupportedOperationException e) { } } @Test public void lineWrapping() { MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("call"); methodBuilder.addCode("$[call("); for (int i = 0; i < 32; i++) { methodBuilder.addParameter(String.class, "s" + i); methodBuilder.addCode(i > 0 ? ",$W$S" : "$S", i); } methodBuilder.addCode(");$]\n"); TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(methodBuilder.build()) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "import java.lang.String;\n" + "\n" + "class Taco {\n" + " void call(String s0, String s1, String s2, String s3, String s4, String s5, String s6, String s7,\n" + " String s8, String s9, String s10, String s11, String s12, String s13, String s14, String s15,\n" + " String s16, String s17, String s18, String s19, String s20, String s21, String s22,\n" + " String s23, String s24, String s25, String s26, String s27, String s28, String s29,\n" + " String s30, String s31) {\n" + " call(\"0\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \"7\", \"8\", \"9\", \"10\", \"11\", \"12\", \"13\", \"14\", \"15\", \"16\",\n" + " \"17\", \"18\", \"19\", \"20\", \"21\", \"22\", \"23\", \"24\", \"25\", \"26\", \"27\", \"28\", \"29\", \"30\", \"31\");\n" + " }\n" + "}\n"); } @Test public void lineWrappingWithZeroWidthSpace() { MethodSpec method = MethodSpec.methodBuilder("call") .addCode("$[iAmSickOfWaitingInLine($Z") .addCode("it, has, been, far, too, long, of, a, wait, and, i, would, like, to, eat, ") .addCode("this, is, a, run, on, sentence") .addCode(");$]\n") .build(); TypeSpec taco = TypeSpec.classBuilder("Taco") .addMethod(method) .build(); assertThat(toString(taco)).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + "class Taco {\n" + " void call() {\n" + " iAmSickOfWaitingInLine(\n" + " it, has, been, far, too, long, of, a, wait, and, i, would, like, to, eat, this, is, a, run, on, sentence);\n" + " }\n" + "}\n"); } @Test public void equalsAndHashCode() { TypeSpec a = TypeSpec.interfaceBuilder("taco").build(); TypeSpec b = TypeSpec.interfaceBuilder("taco").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = TypeSpec.classBuilder("taco").build(); b = TypeSpec.classBuilder("taco").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build(); b = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = TypeSpec.annotationBuilder("taco").build(); b = TypeSpec.annotationBuilder("taco").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void classNameFactories() { ClassName className = ClassName.get("com.example", "Example"); assertThat(TypeSpec.classBuilder(className).build().name).isEqualTo("Example"); assertThat(TypeSpec.interfaceBuilder(className).build().name).isEqualTo("Example"); assertThat(TypeSpec.enumBuilder(className).addEnumConstant("A").build().name).isEqualTo("Example"); assertThat(TypeSpec.annotationBuilder(className).build().name).isEqualTo("Example"); } } javapoet-1.11.1/src/test/java/com/squareup/javapoet/TypesEclipseTest.java000066400000000000000000000125071335545476000264640ustar00rootroot00000000000000/* * Copyright (C) 2014 Google, 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. */ package com.squareup.javapoet; import static com.google.common.base.Charsets.*; import static com.google.common.base.Preconditions.*; import java.util.Locale; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.junit.runners.model.Statement; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; @RunWith(JUnit4.class) public final class TypesEclipseTest extends AbstractTypesTest { /** * A {@link JUnit4} {@link Rule} that executes tests such that a instances of {@link Elements} and * {@link Types} are available during execution. * *

To use this rule in a test, just add the following field:

   {@code
   *   @Rule public CompilationRule compilationRule = new CompilationRule();}
   *
   * @author Gregory Kick
   */
  public static final class CompilationRule implements TestRule {
    private Elements elements;
    private Types types;

    @Override
    public Statement apply(final Statement base, Description description) {
      return new Statement() {
        @Override public void evaluate() throws Throwable {
          final AtomicReference thrown = new AtomicReference<>();
          boolean successful = compile(ImmutableList.of(new AbstractProcessor() {
            @Override
            public SourceVersion getSupportedSourceVersion() {
              return SourceVersion.latest();
            }

            @Override
            public Set getSupportedAnnotationTypes() {
              return ImmutableSet.of("*");
            }

            @Override
            public synchronized void init(ProcessingEnvironment processingEnv) {
              super.init(processingEnv);
              elements = processingEnv.getElementUtils();
              types = processingEnv.getTypeUtils();
            }

            @Override
            public boolean process(Set annotations,
                RoundEnvironment roundEnv) {
              // just run the test on the last round after compilation is over
              if (roundEnv.processingOver()) {
                try {
                  base.evaluate();
                } catch (Throwable e) {
                  thrown.set(e);
                }
              }
              return false;
            }
          }));
          checkState(successful);
          Throwable t = thrown.get();
          if (t != null) {
            throw t;
          }
        }
      };
    }

    /**
     * Returns the {@link Elements} instance associated with the current execution of the rule.
     *
     * @throws IllegalStateException if this method is invoked outside the execution of the rule.
     */
    public Elements getElements() {
      checkState(elements != null, "Not running within the rule");
      return elements;
    }

    /**
     * Returns the {@link Types} instance associated with the current execution of the rule.
     *
     * @throws IllegalStateException if this method is invoked outside the execution of the rule.
     */
    public Types getTypes() {
      checkState(elements != null, "Not running within the rule");
      return types;
    }

    static private boolean compile(Iterable processors) {
      JavaCompiler compiler = new EclipseCompiler();
      DiagnosticCollector diagnosticCollector =
          new DiagnosticCollector<>();
      JavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, Locale.getDefault(), UTF_8);
      JavaCompiler.CompilationTask task = compiler.getTask(
          null,
          fileManager,
          diagnosticCollector,
          ImmutableSet.of(),
          ImmutableSet.of(TypesEclipseTest.class.getCanonicalName()),
          ImmutableSet.of());
      task.setProcessors(processors);
      return task.call();
    }
  }

  @Rule public final CompilationRule compilation = new CompilationRule();

  @Override
  protected Elements getElements() {
    return compilation.getElements();
  }

  @Override
  protected Types getTypes() {
    return compilation.getTypes();
  }
}
javapoet-1.11.1/src/test/java/com/squareup/javapoet/TypesTest.java000066400000000000000000000022311335545476000251500ustar00rootroot00000000000000/*
 * Copyright (C) 2014 Google, 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.
 */
package com.squareup.javapoet;

import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

import org.junit.Rule;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import com.google.testing.compile.CompilationRule;

@RunWith(JUnit4.class)
public final class TypesTest extends AbstractTypesTest {
  @Rule public final CompilationRule compilation = new CompilationRule();

  @Override
  protected Elements getElements() {
    return compilation.getElements();
  }

  @Override
  protected Types getTypes() {
    return compilation.getTypes();
  }
}
javapoet-1.11.1/src/test/java/com/squareup/javapoet/UtilTest.java000066400000000000000000000071621335545476000247710ustar00rootroot00000000000000/*
 * Copyright (C) 2016 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.
 */
package com.squareup.javapoet;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

public class UtilTest {
  @Test public void characterLiteral() {
    assertEquals("a", Util.characterLiteralWithoutSingleQuotes('a'));
    assertEquals("b", Util.characterLiteralWithoutSingleQuotes('b'));
    assertEquals("c", Util.characterLiteralWithoutSingleQuotes('c'));
    assertEquals("%", Util.characterLiteralWithoutSingleQuotes('%'));
    // common escapes
    assertEquals("\\b", Util.characterLiteralWithoutSingleQuotes('\b'));
    assertEquals("\\t", Util.characterLiteralWithoutSingleQuotes('\t'));
    assertEquals("\\n", Util.characterLiteralWithoutSingleQuotes('\n'));
    assertEquals("\\f", Util.characterLiteralWithoutSingleQuotes('\f'));
    assertEquals("\\r", Util.characterLiteralWithoutSingleQuotes('\r'));
    assertEquals("\"", Util.characterLiteralWithoutSingleQuotes('"'));
    assertEquals("\\'", Util.characterLiteralWithoutSingleQuotes('\''));
    assertEquals("\\\\", Util.characterLiteralWithoutSingleQuotes('\\'));
    // octal escapes
    assertEquals("\\u0000", Util.characterLiteralWithoutSingleQuotes('\0'));
    assertEquals("\\u0007", Util.characterLiteralWithoutSingleQuotes('\7'));
    assertEquals("?", Util.characterLiteralWithoutSingleQuotes('\77'));
    assertEquals("\\u007f", Util.characterLiteralWithoutSingleQuotes('\177'));
    assertEquals("¿", Util.characterLiteralWithoutSingleQuotes('\277'));
    assertEquals("ÿ", Util.characterLiteralWithoutSingleQuotes('\377'));
    // unicode escapes
    assertEquals("\\u0000", Util.characterLiteralWithoutSingleQuotes('\u0000'));
    assertEquals("\\u0001", Util.characterLiteralWithoutSingleQuotes('\u0001'));
    assertEquals("\\u0002", Util.characterLiteralWithoutSingleQuotes('\u0002'));
    assertEquals("€", Util.characterLiteralWithoutSingleQuotes('\u20AC'));
    assertEquals("☃", Util.characterLiteralWithoutSingleQuotes('\u2603'));
    assertEquals("♠", Util.characterLiteralWithoutSingleQuotes('\u2660'));
    assertEquals("♣", Util.characterLiteralWithoutSingleQuotes('\u2663'));
    assertEquals("♥", Util.characterLiteralWithoutSingleQuotes('\u2665'));
    assertEquals("♦", Util.characterLiteralWithoutSingleQuotes('\u2666'));
    assertEquals("✵", Util.characterLiteralWithoutSingleQuotes('\u2735'));
    assertEquals("✺", Util.characterLiteralWithoutSingleQuotes('\u273A'));
    assertEquals("/", Util.characterLiteralWithoutSingleQuotes('\uFF0F'));
  }

  @Test public void stringLiteral() {
    stringLiteral("abc");
    stringLiteral("♦♥♠♣");
    stringLiteral("€\\t@\\t$", "€\t@\t$", " ");
    stringLiteral("abc();\\n\"\n  + \"def();", "abc();\ndef();", " ");
    stringLiteral("This is \\\"quoted\\\"!", "This is \"quoted\"!", " ");
    stringLiteral("e^{i\\\\pi}+1=0", "e^{i\\pi}+1=0", " ");
  }

  void stringLiteral(String string) {
    stringLiteral(string, string, " ");
  }

  void stringLiteral(String expected, String value, String indent) {
    assertEquals("\"" + expected + "\"", Util.stringLiteralWithDoubleQuotes(value, indent));
  }
}