pax_global_header00006660000000000000000000000064136570363260014525gustar00rootroot0000000000000052 comment=b06546a6865e5666eb705906391328b2681bf5a8 auto-auto-service-1.0-rc7/000077500000000000000000000000001365703632600154325ustar00rootroot00000000000000auto-auto-service-1.0-rc7/.gitignore000066400000000000000000000002751365703632600174260ustar00rootroot00000000000000.classpath .factorypath .project .settings eclipsebin bin gen build out lib target *.class pom.xml.* release.properties .idea *.iml classes obj .DS_Store *~ dependency-reduced-pom.xml auto-auto-service-1.0-rc7/.travis.yml000066400000000000000000000017431365703632600175500ustar00rootroot00000000000000sudo: false language: java install: - mvn -B -U -f build-pom.xml dependency:go-offline test clean --quiet --fail-never -DskipTests=true script: - mvn -B -U -f build-pom.xml verify --fail-at-end -Dsource.skip=true -Dmaven.javadoc.skip=true jdk: - openjdk9 - openjdk8 env: global: - secure: "bWNSSMURwYC0oZWIMZRd7dy5+JdoyZ060d427TAqFRJmOkYtlR+dBbBggjeJmM0PEkQDzeBrWwln/Vq3lnRPv8czA7hSg/R33r3GzTyi1GZhjCYN2mPW8qp4qgqlloh78aaOODUNSJsOtQqPDJPmhLLfD6UCY0eq9zHhweIjYdw=" - secure: "s5V9d8MKl7ZHqCxuYLljLSD4sp9KLtYkk9hVxEPqCLAi4zA70WkX9h+GZI1gAOpcavomfrWcgSDT2ZReiuNpwx7OtczdS4zB+s6mo4F598iRs4bhSLiPT+Hzvx6BSwf1ZKZTYEhrUPGmKOp2T29AxMV7D0Q+P7n574ubvpUuZmA=" - secure: "T24JAd60zthkeLBmenvZn6+qI43uvfuLwVb70Ljhbc19XDYEZV4Zm/kaafsisP5+F6kV4GjFaT+NCq2sJlwvPSMMRvU1JJgmNVh8TmtswkC/PHKonkMkOsj2KmFP0RRSPdvQv2NrSguZUq8mg+2pvnPO0qoPg4VeIODPGtAxNb8=" after_success: - util/generate-latest-docs.sh - util/publish-snapshot-on-commit.sh branches: only: - master - /^release.*$/ auto-auto-service-1.0-rc7/CONTRIBUTING.md000066400000000000000000000032151365703632600176640ustar00rootroot00000000000000Contributing ============ If you would like to contribute code to Auto 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. Where appropriate, please provide unit tests or integration tests. Unit tests should be JUnit based tests and can use either standard JUnit assertions or Truth assertions and be added to `/src/test/java`. Changes to code generation or other build-time behaviour should go into small maven projects using the `maven-invoker-plugin`. Examples of this are in `generator/src/it` and can include bean-shell verification scripts and other facilities provided by `maven-invoker-plugin`. Please make sure your code compiles by running `mvn clean verify` which will execute both unit and integration test phases. Additionally, consider using http://travis-ci.org to validate your branches before you even put them into pull requests. All pull requests will be validated by Travis-ci in any case and must pass before being merged. If you are adding or modifying files you may add your own copyright line, but please ensure that the form is consistent with the existing files, and please note that a Google, Inc. copyright line must appear in every copyright notice. All files are released with the Apache 2.0 license and any new files may only be accepted under the terms of that license. Before your code can be accepted into the project you must sign the [Individual Contributor License Agreement (CLA)][1]. [1]: https://developers.google.com/open-source/cla/individual auto-auto-service-1.0-rc7/LICENSE000066400000000000000000000261361365703632600164470ustar00rootroot00000000000000 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. auto-auto-service-1.0-rc7/README.md000066400000000000000000000050121365703632600167070ustar00rootroot00000000000000# Auto [![Build Status](https://travis-ci.org/google/auto.svg?branch=master)](https://travis-ci.org/google/auto) A collection of source code generators for [Java][java]. ## Auto‽ [Java][java] is full of code that is mechanical, repetitive, typically untested and sometimes the source of subtle bugs. _Sounds like a job for robots!_ The Auto subprojects are a collection of code generators that automate those types of tasks. They create the code you would have written, but without the bugs. Save time. Save code. Save sanity. ## Subprojects * [AutoFactory] - JSR-330-compatible factories [![Maven Central](https://img.shields.io/maven-central/v/com.google.auto.factory/auto-factory.svg)](https://mvnrepository.com/artifact/com.google.auto.factory/auto-factory) * [AutoService] - Provider-configuration files for [`ServiceLoader`] [![Maven Central](https://img.shields.io/maven-central/v/com.google.auto.service/auto-service.svg)](https://mvnrepository.com/artifact/com.google.auto.service/auto-service) * [AutoValue] - Immutable [value-type] code generation for Java 7+. [![Maven Central](https://img.shields.io/maven-central/v/com.google.auto.value/auto-value.svg)](https://mvnrepository.com/artifact/com.google.auto.value/auto-value) * [Common] - Helper utilities for writing annotation processors. [![Maven Central](https://img.shields.io/maven-central/v/com.google.auto/auto-common.svg)](https://mvnrepository.com/artifact/com.google.auto/auto-common) ## License Copyright 2013 Google LLC 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. [AutoFactory]: https://github.com/google/auto/tree/master/factory [AutoService]: https://github.com/google/auto/tree/master/service [AutoValue]: https://github.com/google/auto/tree/master/value [Common]: https://github.com/google/auto/tree/master/common [java]: https://en.wikipedia.org/wiki/Java_(programming_language) [value-type]: http://en.wikipedia.org/wiki/Value_object [`ServiceLoader`]: http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html auto-auto-service-1.0-rc7/build-pom.xml000066400000000000000000000014541365703632600200500ustar00rootroot00000000000000 4.0.0 build-only build-only NO_VERSION pom common factory service value sonatype-nexus-staging Nexus Release Repository file:///tmp/auto_project_maven_fake_repo/ sonatype-nexus-snapshots Sonatype Nexus Snapshots file:///tmp/auto_project_maven_fake_repo/ auto-auto-service-1.0-rc7/common/000077500000000000000000000000001365703632600167225ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/README.md000066400000000000000000000052431365703632600202050ustar00rootroot00000000000000Auto Common Utilities ======== ## Overview The Auto project has a set of common utilities to help ease use of the annotation processing environment. ## Utility classes of note * MoreTypes - utilities and Equivalence wrappers for TypeMirror and related subtypes * MoreElements - utilities for Element and related subtypes * SuperficialValidation - very simple scanner to ensure an Element is valid and free from distortion from upstream compilation errors * Visibility - utilities for working with Elements' visibility levels (public, protected, etc.) * BasicAnnotationProcessor/ProcessingStep - simple types that - implement a validating annotation processor - defer invalid elements until later - break processor actions into multiple steps (which may each handle different annotations) ## Usage/Setup Auto common utilities have a standard [Maven](http://maven.apache.org) setup which can also be used from Gradle, Ivy, Ant, or other systems which consume binary artifacts from the central Maven binary artifact repositories. ```xml com.google.auto auto-common 1.0-SNAPSHOT ``` ## Processor Resilience Auto Common Utilities is used by a variety of annotation processors in Google and new versions may have breaking changes. Users of auto-common are urged to use [shade](https://maven.apache.org/plugins/maven-shade-plugin/) or [jarjar](https://code.google.com/p/jarjar/) (or something similar) in packaging their processors so that conflicting versions of this library do not adversely interact with each other. For example, in a Maven build you can repackage `com.google.auto.common` into `your.processor.shaded.auto.common` like this: ```xml maven-shade-plugin package shade com.google.auto.common your.processor.shaded.auto.common ``` auto-auto-service-1.0-rc7/common/pom.xml000066400000000000000000000105101365703632600202340ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.google.auto auto-common HEAD-SNAPSHOT Auto Common Libraries Common utilities for creating annotation processors. https://github.com/google/auto/tree/master/common UTF-8 1.8 27.0.1-jre 1.0.1 http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD GitHub Issues http://github.com/google/auto/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt Google LLC http://www.google.com com.google.guava guava ${guava.version} com.squareup javapoet 1.9.0 true com.google.guava guava-testlib ${guava.version} test com.google.testing.compile compile-testing 0.18 test junit junit 4.12 test com.google.truth truth ${truth.version} test org.eclipse.jdt ecj 3.20.0 test maven-compiler-plugin 3.7.0 ${java.version} ${java.version} -Xlint:all true true org.codehaus.plexus plexus-java 0.9.4 org.apache.maven.plugins maven-jar-plugin 3.0.2 auto-auto-service-1.0-rc7/common/src/000077500000000000000000000000001365703632600175115ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/000077500000000000000000000000001365703632600204355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/java/000077500000000000000000000000001365703632600213565ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/java/com/000077500000000000000000000000001365703632600221345ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/java/com/google/000077500000000000000000000000001365703632600234105ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/000077500000000000000000000000001365703632600243605ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/000077500000000000000000000000001365703632600256505ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/AnnotationMirrors.java000066400000000000000000000154761365703632600322200ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.base.Equivalence; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.lang.annotation.Annotation; import java.util.Arrays; import java.util.List; import java.util.Map; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; /** * A utility class for working with {@link AnnotationMirror} instances. * * @author Gregory Kick */ public final class AnnotationMirrors { private static final Equivalence ANNOTATION_MIRROR_EQUIVALENCE = new Equivalence() { @Override protected boolean doEquivalent(AnnotationMirror left, AnnotationMirror right) { return MoreTypes.equivalence().equivalent(left.getAnnotationType(), right.getAnnotationType()) && AnnotationValues.equivalence().pairwise().equivalent( getAnnotationValuesWithDefaults(left).values(), getAnnotationValuesWithDefaults(right).values()); } @Override protected int doHash(AnnotationMirror annotation) { DeclaredType type = annotation.getAnnotationType(); Iterable annotationValues = getAnnotationValuesWithDefaults(annotation).values(); return Arrays.hashCode(new int[] {MoreTypes.equivalence().hash(type), AnnotationValues.equivalence().pairwise().hash(annotationValues)}); } }; /** * Returns an {@link Equivalence} for {@link AnnotationMirror} as some implementations * delegate equality tests to {@link Object#equals} whereas the documentation explicitly * states that instance/reference equality is not the proper test. */ public static Equivalence equivalence() { return ANNOTATION_MIRROR_EQUIVALENCE; } /** * Returns the {@link AnnotationMirror}'s map of {@link AnnotationValue} indexed by {@link * ExecutableElement}, supplying default values from the annotation if the annotation property has * not been set. This is equivalent to {@link * Elements#getElementValuesWithDefaults(AnnotationMirror)} but can be called statically without * an {@link Elements} instance. * *

The iteration order of elements of the returned map will be the order in which the {@link * ExecutableElement}s are defined in {@code annotation}'s {@linkplain * AnnotationMirror#getAnnotationType() type}. */ public static ImmutableMap getAnnotationValuesWithDefaults( AnnotationMirror annotation) { ImmutableMap.Builder values = ImmutableMap.builder(); Map declaredValues = annotation.getElementValues(); for (ExecutableElement method : ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) { // Must iterate and put in this order, to ensure consistency in generated code. if (declaredValues.containsKey(method)) { values.put(method, declaredValues.get(method)); } else if (method.getDefaultValue() != null) { values.put(method, method.getDefaultValue()); } else { throw new IllegalStateException( "Unset annotation value without default should never happen: " + MoreElements.asType(method.getEnclosingElement()).getQualifiedName() + '.' + method.getSimpleName() + "()"); } } return values.build(); } /** * Returns an {@link AnnotationValue} for the named element if such an element was * either declared in the usage represented by the provided {@link AnnotationMirror}, or if * such an element was defined with a default. * * @throws IllegalArgumentException if no element is defined with the given elementName. */ public static AnnotationValue getAnnotationValue( AnnotationMirror annotationMirror, String elementName) { return getAnnotationElementAndValue(annotationMirror, elementName).getValue(); } /** * Returns a {@link ExecutableElement} and its associated {@link AnnotationValue} if such * an element was either declared in the usage represented by the provided * {@link AnnotationMirror}, or if such an element was defined with a default. * * @throws IllegalArgumentException if no element is defined with the given elementName. */ public static Map.Entry getAnnotationElementAndValue( AnnotationMirror annotationMirror, final String elementName) { checkNotNull(annotationMirror); checkNotNull(elementName); for (Map.Entry entry : getAnnotationValuesWithDefaults(annotationMirror).entrySet()) { if (entry.getKey().getSimpleName().contentEquals(elementName)) { return entry; } } throw new IllegalArgumentException(String.format("@%s does not define an element %s()", MoreElements.asType(annotationMirror.getAnnotationType().asElement()).getQualifiedName(), elementName)); } /** * Returns all {@linkplain AnnotationMirror annotations} that are present on the given * {@link Element} which are themselves annotated with {@code annotationType}. */ public static ImmutableSet getAnnotatedAnnotations(Element element, final Class annotationType) { List annotations = element.getAnnotationMirrors(); return FluentIterable.from(annotations) .filter(new Predicate() { @Override public boolean apply(AnnotationMirror input) { return isAnnotationPresent(input.getAnnotationType().asElement(), annotationType); } }) .toSet(); } private AnnotationMirrors() {} } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/AnnotationValues.java000066400000000000000000000403001365703632600320020ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableList.toImmutableList; import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.function.Function; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor8; /** * A utility class for working with {@link AnnotationValue} instances. * * @author Christian Gruber */ public final class AnnotationValues { private static final Equivalence ANNOTATION_VALUE_EQUIVALENCE = new Equivalence() { @Override protected boolean doEquivalent(AnnotationValue left, AnnotationValue right) { return left.accept(new SimpleAnnotationValueVisitor8() { // LHS is not an annotation or array of annotation values, so just test equality. @Override protected Boolean defaultAction(Object left, AnnotationValue right) { return left.equals(right.accept( new SimpleAnnotationValueVisitor8() { @Override protected Object defaultAction(Object object, Void unused) { return object; } }, null)); } // LHS is an annotation mirror so test equivalence for RHS annotation mirrors // and false for other types. @Override public Boolean visitAnnotation(AnnotationMirror left, AnnotationValue right) { return right.accept( new SimpleAnnotationValueVisitor8() { @Override protected Boolean defaultAction(Object right, AnnotationMirror left) { return false; // Not an annotation mirror, so can't be equal to such. } @Override public Boolean visitAnnotation(AnnotationMirror right, AnnotationMirror left) { return AnnotationMirrors.equivalence().equivalent(left, right); } }, left); } // LHS is a list of annotation values have to collect-test equivalences, or false // for any other types. @Override public Boolean visitArray(List left, AnnotationValue right) { return right.accept( new SimpleAnnotationValueVisitor8>() { @Override protected Boolean defaultAction( Object ignored, List alsoIgnored) { return false; // Not an array, so can't be equal to such. } @SuppressWarnings("unchecked") // safe covariant cast @Override public Boolean visitArray( List right , List left) { return AnnotationValues.equivalence().pairwise().equivalent( (List) left, (List) right); } }, left); } @Override public Boolean visitType(TypeMirror left, AnnotationValue right) { return right.accept( new SimpleAnnotationValueVisitor8() { @Override protected Boolean defaultAction( Object ignored, TypeMirror alsoIgnored) { return false; // Not an annotation mirror, so can't be equal to such. } @Override public Boolean visitType(TypeMirror right, TypeMirror left) { return MoreTypes.equivalence().equivalent(left, right); } }, left); } }, right); } @Override protected int doHash(AnnotationValue value) { return value.accept(new SimpleAnnotationValueVisitor8() { @Override public Integer visitAnnotation(AnnotationMirror value, Void ignore) { return AnnotationMirrors.equivalence().hash(value); } @SuppressWarnings("unchecked") // safe covariant cast @Override public Integer visitArray( List values, Void ignore) { return AnnotationValues.equivalence().pairwise().hash((List) values); } @Override public Integer visitType(TypeMirror value, Void ignore) { return MoreTypes.equivalence().hash(value); } @Override protected Integer defaultAction(Object value, Void ignored) { return value.hashCode(); } }, null); } }; /** * Returns an {@link Equivalence} for {@link AnnotationValue} as annotation values may * contain {@link AnnotationMirror} instances some of whose implementations delegate * equality tests to {@link Object#equals} whereas the documentation explicitly states * that instance/reference equality is not the proper test. * * @see AnnotationMirrors#equivalence() */ public static Equivalence equivalence() { return ANNOTATION_VALUE_EQUIVALENCE; } private static class DefaultVisitor extends SimpleAnnotationValueVisitor8 { final Class clazz; DefaultVisitor(Class clazz) { this.clazz = checkNotNull(clazz); } @Override public T defaultAction(Object o, Void unused) { throw new IllegalArgumentException( "Expected a " + clazz.getSimpleName() + ", got instead: " + o); } } private static final class TypeMirrorVisitor extends DefaultVisitor { static final TypeMirrorVisitor INSTANCE = new TypeMirrorVisitor(); TypeMirrorVisitor() { super(DeclaredType.class); } @Override public DeclaredType visitType(TypeMirror value, Void unused) { return MoreTypes.asDeclared(value); } } ; /** * Returns the value as a class. * * @throws IllegalArgumentException if the value is not a class. */ public static DeclaredType getTypeMirror(AnnotationValue value) { return TypeMirrorVisitor.INSTANCE.visit(value); } private static final class AnnotationMirrorVisitor extends DefaultVisitor { static final AnnotationMirrorVisitor INSTANCE = new AnnotationMirrorVisitor(); AnnotationMirrorVisitor() { super(AnnotationMirror.class); } @Override public AnnotationMirror visitAnnotation(AnnotationMirror value, Void unused) { return value; } } ; /** * Returns the value as an AnnotationMirror. * * @throws IllegalArgumentException if the value is not an annotation. */ public static AnnotationMirror getAnnotationMirror(AnnotationValue value) { return AnnotationMirrorVisitor.INSTANCE.visit(value); } private static final class EnumVisitor extends DefaultVisitor { static final EnumVisitor INSTANCE = new EnumVisitor(); EnumVisitor() { super(VariableElement.class); } @Override public VariableElement visitEnumConstant(VariableElement value, Void unused) { return value; } } ; /** * Returns the value as a VariableElement. * * @throws IllegalArgumentException if the value is not an enum. */ public static VariableElement getEnum(AnnotationValue value) { return EnumVisitor.INSTANCE.visit(value); } private static T valueOfType(AnnotationValue annotationValue, Class type) { Object value = annotationValue.getValue(); if (!type.isInstance(value)) { throw new IllegalArgumentException( "Expected " + type.getSimpleName() + ", got instead: " + value); } return type.cast(value); } /** * Returns the value as a string. * * @throws IllegalArgumentException if the value is not a string. */ public static String getString(AnnotationValue value) { return valueOfType(value, String.class); } /** * Returns the value as an int. * * @throws IllegalArgumentException if the value is not an int. */ public static int getInt(AnnotationValue value) { return valueOfType(value, Integer.class); } /** * Returns the value as a long. * * @throws IllegalArgumentException if the value is not a long. */ public static long getLong(AnnotationValue value) { return valueOfType(value, Long.class); } /** * Returns the value as a byte. * * @throws IllegalArgumentException if the value is not a byte. */ public static byte getByte(AnnotationValue value) { return valueOfType(value, Byte.class); } /** * Returns the value as a short. * * @throws IllegalArgumentException if the value is not a short. */ public static short getShort(AnnotationValue value) { return valueOfType(value, Short.class); } /** * Returns the value as a float. * * @throws IllegalArgumentException if the value is not a float. */ public static float getFloat(AnnotationValue value) { return valueOfType(value, Float.class); } /** * Returns the value as a double. * * @throws IllegalArgumentException if the value is not a double. */ public static double getDouble(AnnotationValue value) { return valueOfType(value, Double.class); } /** * Returns the value as a boolean. * * @throws IllegalArgumentException if the value is not a boolean. */ public static boolean getBoolean(AnnotationValue value) { return valueOfType(value, Boolean.class); } /** * Returns the value as a char. * * @throws IllegalArgumentException if the value is not a char. */ public static char getChar(AnnotationValue value) { return valueOfType(value, Character.class); } private static final class ArrayVisitor extends SimpleAnnotationValueVisitor8, Void> { final Function visitT; ArrayVisitor(Function visitT) { this.visitT = checkNotNull(visitT); } @Override public ImmutableList defaultAction(Object o, Void unused) { throw new IllegalStateException("Expected an array, got instead: " + o); } @Override public ImmutableList visitArray(List values, Void unused) { return values.stream().map(visitT).collect(toImmutableList()); } } private static final ArrayVisitor TYPE_MIRRORS_VISITOR = new ArrayVisitor<>(AnnotationValues::getTypeMirror); /** * Returns the value as a list of classes. * * @throws IllegalArgumentException if the value is not an array of classes. */ public static ImmutableList getTypeMirrors(AnnotationValue value) { return TYPE_MIRRORS_VISITOR.visit(value); } private static final ArrayVisitor ANNOTATION_MIRRORS_VISITOR = new ArrayVisitor<>(AnnotationValues::getAnnotationMirror); /** * Returns the value as a list of annotations. * * @throws IllegalArgumentException if the value if not an array of annotations. */ public static ImmutableList getAnnotationMirrors(AnnotationValue value) { return ANNOTATION_MIRRORS_VISITOR.visit(value); } private static final ArrayVisitor ENUMS_VISITOR = new ArrayVisitor<>(AnnotationValues::getEnum); /** * Returns the value as a list of enums. * * @throws IllegalArgumentException if the value is not an array of enums. */ public static ImmutableList getEnums(AnnotationValue value) { return ENUMS_VISITOR.visit(value); } private static final ArrayVisitor STRINGS_VISITOR = new ArrayVisitor<>(AnnotationValues::getString); /** * Returns the value as a list of strings. * * @throws IllegalArgumentException if the value is not an array of strings. */ public static ImmutableList getStrings(AnnotationValue value) { return STRINGS_VISITOR.visit(value); } private static final ArrayVisitor INTS_VISITOR = new ArrayVisitor<>(AnnotationValues::getInt); /** * Returns the value as a list of integers. * * @throws IllegalArgumentException if the value is not an array of ints. */ public static ImmutableList getInts(AnnotationValue value) { return INTS_VISITOR.visit(value); } private static final ArrayVisitor LONGS_VISITOR = new ArrayVisitor<>(AnnotationValues::getLong); /** * Returns the value as a list of longs. * * @throws IllegalArgumentException if the value is not an array of longs. */ public static ImmutableList getLongs(AnnotationValue value) { return LONGS_VISITOR.visit(value); } private static final ArrayVisitor BYTES_VISITOR = new ArrayVisitor<>(AnnotationValues::getByte); /** * Returns the value as a list of bytes. * * @throws IllegalArgumentException if the value is not an array of bytes. */ public static ImmutableList getBytes(AnnotationValue value) { return BYTES_VISITOR.visit(value); } private static final ArrayVisitor SHORTS_VISITOR = new ArrayVisitor<>(AnnotationValues::getShort); /** * Returns the value as a list of shorts. * * @throws IllegalArgumentException if the value is not an array of shorts. */ public static ImmutableList getShorts(AnnotationValue value) { return SHORTS_VISITOR.visit(value); } private static final ArrayVisitor FLOATS_VISITOR = new ArrayVisitor<>(AnnotationValues::getFloat); /** * Returns the value as a list of floats. * * @throws IllegalArgumentException if the value is not an array of floats. */ public static ImmutableList getFloats(AnnotationValue value) { return FLOATS_VISITOR.visit(value); } private static final ArrayVisitor DOUBLES_VISITOR = new ArrayVisitor<>(AnnotationValues::getDouble); /** * Returns the value as a list of doubles. * * @throws IllegalArgumentException if the value is not an array of doubles. */ public static ImmutableList getDoubles(AnnotationValue value) { return DOUBLES_VISITOR.visit(value); } private static final ArrayVisitor BOOLEANS_VISITOR = new ArrayVisitor<>(AnnotationValues::getBoolean); /** * Returns the value as a list of booleans. * * @throws IllegalArgumentException if the value is not an array of booleans. */ public static ImmutableList getBooleans(AnnotationValue value) { return BOOLEANS_VISITOR.visit(value); } private static final ArrayVisitor CHARS_VISITOR = new ArrayVisitor<>(AnnotationValues::getChar); /** * Returns the value as a list of characters. * * @throws IllegalArgumentException if the value is not an array of chars. */ public static ImmutableList getChars(AnnotationValue value) { return CHARS_VISITOR.visit(value); } private static final ArrayVisitor ANNOTATION_VALUES_VISITOR = new ArrayVisitor<>(x -> x); /** * Returns the value as a list of {@link AnnotationValue}s. * * @throws IllegalArgumentException if the value is not an array. */ public static ImmutableList getAnnotationValues(AnnotationValue value) { return ANNOTATION_VALUES_VISITOR.visit(value); } private AnnotationValues() {} } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java000066400000000000000000000455621365703632600335030ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.auto.common.MoreElements.asExecutable; import static com.google.auto.common.MoreElements.asPackage; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.SuperficialValidation.validateElement; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Multimaps.filterKeys; import static javax.lang.model.element.ElementKind.PACKAGE; import static javax.tools.Diagnostic.Kind.ERROR; import com.google.common.base.Ascii; import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; import com.google.common.collect.Sets; import java.lang.annotation.Annotation; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ErrorType; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleElementVisitor8; /** * An abstract {@link Processor} implementation that defers processing of {@link Element}s to later * rounds if they cannot be processed. * *

Subclasses put their processing logic in {@link ProcessingStep} implementations. The steps are * passed to the processor by returning them in the {@link #initSteps()} method, and can access the * {@link ProcessingEnvironment} using {@link #processingEnv}. * *

Any logic that needs to happen once per round can be specified by overriding {@link * #postRound(RoundEnvironment)}. * *

Ill-formed elements are deferred

* * Any annotated element whose nearest enclosing type is not well-formed is deferred, and not passed * to any {@code ProcessingStep}. This helps processors to avoid many common pitfalls, such as * {@link ErrorType} instances, {@link ClassCastException}s and badly coerced types. * *

A non-package element is considered well-formed if its type, type parameters, parameters, * default values, supertypes, annotations, and enclosed elements are. Package elements are treated * similarly, except that their enclosed elements are not validated. See {@link * SuperficialValidation#validateElement(Element)} for details. * *

The primary disadvantage to this validation is that any element that forms a circular * dependency with a type generated by another {@code BasicAnnotationProcessor} will never compile * because the element will never be fully complete. All such compilations will fail with an error * message on the offending type that describes the issue. * *

Each {@code ProcessingStep} can defer elements

* *

Each {@code ProcessingStep} can defer elements by including them in the set returned by {@link * ProcessingStep#process(SetMultimap)}; elements deferred by a step will be passed back to that * step in a later round of processing. * *

This feature is useful when one processor may depend on code generated by another, independent * processor, in a way that isn't caught by the well-formedness check described above. For example, * if an element {@code A} cannot be processed because processing it depends on the existence of * some class {@code B}, then {@code A} should be deferred until a later round of processing, when * {@code B} will have been generated by another processor. * *

If {@code A} directly references {@code B}, then the well-formedness check will correctly * defer processing of {@code A} until {@code B} has been generated. * *

However, if {@code A} references {@code B} only indirectly (for example, from within a method * body), then the well-formedness check will not defer processing {@code A}, but a processing step * can reject {@code A}. */ public abstract class BasicAnnotationProcessor extends AbstractProcessor { private final Set deferredElementNames = new LinkedHashSet<>(); private final SetMultimap elementsDeferredBySteps = LinkedHashMultimap.create(); private Elements elements; private Messager messager; private ImmutableList steps; @Override public final synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.elements = processingEnv.getElementUtils(); this.messager = processingEnv.getMessager(); this.steps = ImmutableList.copyOf(initSteps()); } /** * Creates {@linkplain ProcessingStep processing steps} for this processor. {@link #processingEnv} * is guaranteed to be set when this method is invoked. */ protected abstract Iterable initSteps(); /** * An optional hook for logic to be executed at the end of each round. * * @deprecated use {@link #postRound(RoundEnvironment)} instead */ @Deprecated protected void postProcess() {} /** An optional hook for logic to be executed at the end of each round. */ protected void postRound(RoundEnvironment roundEnv) { if (!roundEnv.processingOver()) { postProcess(); } } private ImmutableSet> getSupportedAnnotationClasses() { checkState(steps != null); ImmutableSet.Builder> builder = ImmutableSet.builder(); for (ProcessingStep step : steps) { builder.addAll(step.annotations()); } return builder.build(); } /** * Returns the set of supported annotation types as a collected from registered {@linkplain * ProcessingStep processing steps}. */ @Override public final ImmutableSet getSupportedAnnotationTypes() { ImmutableSet.Builder builder = ImmutableSet.builder(); for (Class annotationClass : getSupportedAnnotationClasses()) { builder.add(annotationClass.getCanonicalName()); } return builder.build(); } @Override public final boolean process(Set annotations, RoundEnvironment roundEnv) { checkState(elements != null); checkState(messager != null); checkState(steps != null); // If this is the last round, report all of the missing elements if there // were no errors raised in the round; otherwise reporting the missing // elements just adds noise the output. if (roundEnv.processingOver()) { postRound(roundEnv); if (!roundEnv.errorRaised()) { reportMissingElements( ImmutableSet.builder() .addAll(deferredElementNames) .addAll(elementsDeferredBySteps.values()) .build()); } return false; } process(validElements(roundEnv)); postRound(roundEnv); return false; } /** Processes the valid elements, including those previously deferred by each step. */ private void process(ImmutableSetMultimap, Element> validElements) { for (ProcessingStep step : steps) { ImmutableSetMultimap, Element> stepElements = new ImmutableSetMultimap.Builder, Element>() .putAll(indexByAnnotation(elementsDeferredBySteps.get(step), step.annotations())) .putAll(filterKeys(validElements, Predicates.in(step.annotations()))) .build(); if (stepElements.isEmpty()) { elementsDeferredBySteps.removeAll(step); } else { Set rejectedElements = step.process(stepElements); elementsDeferredBySteps.replaceValues( step, transform(rejectedElements, ElementName::forAnnotatedElement)); } } } private void reportMissingElements(Set missingElementNames) { for (ElementName missingElementName : missingElementNames) { Optional missingElement = missingElementName.getElement(elements); if (missingElement.isPresent()) { messager.printMessage( ERROR, processingErrorMessage( "this " + Ascii.toLowerCase(missingElement.get().getKind().name())), missingElement.get()); } else { messager.printMessage(ERROR, processingErrorMessage(missingElementName.name())); } } } private String processingErrorMessage(String target) { return String.format( "[%s:MiscError] %s was unable to process %s because not all of its dependencies could be " + "resolved. Check for compilation errors or a circular dependency with generated " + "code.", getClass().getSimpleName(), getClass().getCanonicalName(), target); } /** * Returns the valid annotated elements contained in all of the deferred elements. If none are * found for a deferred element, defers it again. */ private ImmutableSetMultimap, Element> validElements( RoundEnvironment roundEnv) { ImmutableSet prevDeferredElementNames = ImmutableSet.copyOf(deferredElementNames); deferredElementNames.clear(); ImmutableSetMultimap.Builder, Element> deferredElementsByAnnotationBuilder = ImmutableSetMultimap.builder(); for (ElementName deferredElementName : prevDeferredElementNames) { Optional deferredElement = deferredElementName.getElement(elements); if (deferredElement.isPresent()) { findAnnotatedElements( deferredElement.get(), getSupportedAnnotationClasses(), deferredElementsByAnnotationBuilder); } else { deferredElementNames.add(deferredElementName); } } ImmutableSetMultimap, Element> deferredElementsByAnnotation = deferredElementsByAnnotationBuilder.build(); ImmutableSetMultimap.Builder, Element> validElements = ImmutableSetMultimap.builder(); Set validElementNames = new LinkedHashSet<>(); // Look at the elements we've found and the new elements from this round and validate them. for (Class annotationClass : getSupportedAnnotationClasses()) { // This should just call roundEnv.getElementsAnnotatedWith(Class) directly, but there is a bug // in some versions of eclipse that cause that method to crash. TypeElement annotationType = elements.getTypeElement(annotationClass.getCanonicalName()); Set roundElements = (annotationType == null) ? ImmutableSet.of() : roundEnv.getElementsAnnotatedWith(annotationType); ImmutableSet prevRoundElements = deferredElementsByAnnotation.get(annotationClass); for (Element element : Sets.union(roundElements, prevRoundElements)) { ElementName elementName = ElementName.forAnnotatedElement(element); boolean isValidElement = validElementNames.contains(elementName) || (!deferredElementNames.contains(elementName) && validateElement( element.getKind().equals(PACKAGE) ? element : getEnclosingType(element))); if (isValidElement) { validElements.put(annotationClass, element); validElementNames.add(elementName); } else { deferredElementNames.add(elementName); } } } return validElements.build(); } private ImmutableSetMultimap, Element> indexByAnnotation( Set annotatedElements, Set> annotationClasses) { ImmutableSetMultimap.Builder, Element> deferredElements = ImmutableSetMultimap.builder(); for (ElementName elementName : annotatedElements) { Optional element = elementName.getElement(elements); if (element.isPresent()) { findAnnotatedElements(element.get(), annotationClasses, deferredElements); } } return deferredElements.build(); } /** * Adds {@code element} and its enclosed elements to {@code annotatedElements} if they are * annotated with any annotations in {@code annotationClasses}. Does not traverse to member types * of {@code element}, so that if {@code Outer} is passed in the example below, looking for * {@code @X}, then {@code Outer}, {@code Outer.foo}, and {@code Outer.foo()} will be added to the * multimap, but neither {@code Inner} nor its members will. * *

   *   {@literal @}X class Outer {
   *     {@literal @}X Object foo;
   *     {@literal @}X void foo() {}
   *     {@literal @}X static class Inner {
   *       {@literal @}X Object bar;
   *       {@literal @}X void bar() {}
   *     }
   *   }
   * 
*/ private static void findAnnotatedElements( Element element, Set> annotationClasses, ImmutableSetMultimap.Builder, Element> annotatedElements) { for (Element enclosedElement : element.getEnclosedElements()) { if (!enclosedElement.getKind().isClass() && !enclosedElement.getKind().isInterface()) { findAnnotatedElements(enclosedElement, annotationClasses, annotatedElements); } } // element.getEnclosedElements() does NOT return parameter elements if (element instanceof ExecutableElement) { for (Element parameterElement : asExecutable(element).getParameters()) { findAnnotatedElements(parameterElement, annotationClasses, annotatedElements); } } for (Class annotationClass : annotationClasses) { if (isAnnotationPresent(element, annotationClass)) { annotatedElements.put(annotationClass, element); } } } /** * Returns the nearest enclosing {@link TypeElement} to the current element, throwing an {@link * IllegalArgumentException} if the provided {@link Element} is a {@link PackageElement} or is * otherwise not enclosed by a type. */ // TODO(cgruber) move to MoreElements and make public. private static TypeElement getEnclosingType(Element element) { return element.accept( new SimpleElementVisitor8() { @Override protected TypeElement defaultAction(Element e, Void p) { return e.getEnclosingElement().accept(this, p); } @Override public TypeElement visitType(TypeElement e, Void p) { return e; } @Override public TypeElement visitPackage(PackageElement e, Void p) { throw new IllegalArgumentException(); } }, null); } /** * The unit of processing logic that runs under the guarantee that all elements are complete and * well-formed. A step may reject elements that are not ready for processing but may be at a later * round. */ public interface ProcessingStep { /** The set of annotation types processed by this step. */ Set> annotations(); /** * The implementation of processing logic for the step. It is guaranteed that the keys in {@code * elementsByAnnotation} will be a subset of the set returned by {@link #annotations()}. * * @return the elements (a subset of the values of {@code elementsByAnnotation}) that this step * is unable to process, possibly until a later processing round. These elements will be * passed back to this step at the next round of processing. */ Set process( SetMultimap, Element> elementsByAnnotation); } /** * A package or type name. * *

It's unfortunate that we have to track types and packages separately, but since there are * two different methods to look them up in {@link Elements}, we end up with a lot of parallel * logic. :( * *

Packages declared (and annotated) in {@code package-info.java} are tracked as deferred * packages, type elements are tracked directly, and all other elements are tracked via their * nearest enclosing type. */ private static final class ElementName { private enum Kind { PACKAGE_NAME, TYPE_NAME, } private final Kind kind; private final String name; private ElementName(Kind kind, Name name) { this.kind = checkNotNull(kind); this.name = name.toString(); } /** * An {@link ElementName} for an annotated element. If {@code element} is a package, uses the * fully qualified name of the package. If it's a type, uses its fully qualified name. * Otherwise, uses the fully-qualified name of the nearest enclosing type. */ static ElementName forAnnotatedElement(Element element) { return element.getKind() == PACKAGE ? new ElementName(Kind.PACKAGE_NAME, asPackage(element).getQualifiedName()) : new ElementName(Kind.TYPE_NAME, getEnclosingType(element).getQualifiedName()); } /** The fully-qualified name of the element. */ String name() { return name; } /** * The {@link Element} whose fully-qualified name is {@link #name()}. Absent if the relevant * method on {@link Elements} returns {@code null}. */ Optional getElement(Elements elements) { return Optional.fromNullable( kind == Kind.PACKAGE_NAME ? elements.getPackageElement(name) : elements.getTypeElement(name)); } @Override public boolean equals(Object object) { if (!(object instanceof ElementName)) { return false; } ElementName that = (ElementName) object; return this.kind == that.kind && this.name.equals(that.name); } @Override public int hashCode() { return Objects.hash(kind, name); } } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/GeneratedAnnotationSpecs.java000066400000000000000000000104421365703632600334430ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.common; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import java.util.Optional; import javax.lang.model.SourceVersion; import javax.lang.model.util.Elements; /** Utility methods for writing {@code @Generated} annotations using JavaPoet. */ public final class GeneratedAnnotationSpecs { private GeneratedAnnotationSpecs() {} /** * Returns {@code @Generated("processorClass"} if either {@code * javax.annotation.processing.Generated} or {@code javax.annotation.Generated} is {@linkplain * GeneratedAnnotations#generatedAnnotation(Elements) available at compile time}. * * @deprecated prefer {@link #generatedAnnotationSpec(Elements, SourceVersion, Class)} */ @Deprecated public static Optional generatedAnnotationSpec( Elements elements, Class processorClass) { return generatedAnnotationSpecBuilder(elements, processorClass) .map(AnnotationSpec.Builder::build); } /** * Returns {@code @Generated(value = "processorClass", comments = "comments"} if either {@code * javax.annotation.processing.Generated} or {@code javax.annotation.Generated} is {@linkplain * GeneratedAnnotations#generatedAnnotation(Elements) available at compile time}. * * @deprecated prefer {@link #generatedAnnotationSpec(Elements, SourceVersion, Class, String)} */ @Deprecated public static Optional generatedAnnotationSpec( Elements elements, Class processorClass, String comments) { return generatedAnnotationSpecBuilder(elements, processorClass) .map(annotation -> annotation.addMember("comments", "$S", comments).build()); } /** * Returns {@code @Generated("processorClass"} for the target {@code SourceVersion}. * *

Returns {@code javax.annotation.processing.Generated} for JDK 9 and newer, {@code * javax.annotation.Generated} for earlier releases, and Optional#empty()} if the annotation is * not available. */ public static Optional generatedAnnotationSpec( Elements elements, SourceVersion sourceVersion, Class processorClass) { return generatedAnnotationSpecBuilder(elements, sourceVersion, processorClass) .map(AnnotationSpec.Builder::build); } /** * Returns {@code @Generated(value = "processorClass", comments = "comments"} for the target * {@code SourceVersion}. * *

Returns {@code javax.annotation.processing.Generated} for JDK 9 and newer, {@code * javax.annotation.Generated} for earlier releases, and Optional#empty()} if the annotation is * not available. */ public static Optional generatedAnnotationSpec( Elements elements, SourceVersion sourceVersion, Class processorClass, String comments) { return generatedAnnotationSpecBuilder(elements, sourceVersion, processorClass) .map(annotation -> annotation.addMember("comments", "$S", comments).build()); } private static Optional generatedAnnotationSpecBuilder( Elements elements, Class processorClass) { return GeneratedAnnotations.generatedAnnotation(elements) .map( generated -> AnnotationSpec.builder(ClassName.get(generated)) .addMember("value", "$S", processorClass.getCanonicalName())); } private static Optional generatedAnnotationSpecBuilder( Elements elements, SourceVersion sourceVersion, Class processorClass) { return GeneratedAnnotations.generatedAnnotation(elements, sourceVersion) .map( generated -> AnnotationSpec.builder(ClassName.get(generated)) .addMember("value", "$S", processorClass.getCanonicalName())); } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/GeneratedAnnotations.java000066400000000000000000000046401365703632600326330ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.common; import java.util.Optional; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; /** Utility methods for writing {@code @Generated} annotations. */ public final class GeneratedAnnotations { private GeneratedAnnotations() {} /** * Returns the element corresponding to the version of the {@code @Generated} annotation present * in the compile-time class- or module-path. * *

First looks for {@code javax.annotation.processing.Generated}, and then {@code * javax.annotation.Generated}. Returns whichever is in the classpath (or modulepath), or {@link * Optional#empty()} if neither is. * * @deprecated prefer {@link #generatedAnnotation(Elements, SourceVersion)} */ @Deprecated public static Optional generatedAnnotation(Elements elements) { TypeElement jdk9Generated = elements.getTypeElement("javax.annotation.processing.Generated"); if (jdk9Generated != null) { return Optional.of(jdk9Generated); } return Optional.ofNullable(elements.getTypeElement("javax.annotation.Generated")); } /** * Returns the element corresponding to the {@code @Generated} annotation present at the target * {@code SourceVersion}. * *

Returns {@code javax.annotation.processing.Generated} for JDK 9 and newer, {@code * javax.annotation.Generated} for earlier releases, and Optional#empty()} if the annotation is * not available. */ public static Optional generatedAnnotation( Elements elements, SourceVersion sourceVersion) { return Optional.ofNullable( elements.getTypeElement( sourceVersion.compareTo(SourceVersion.RELEASE_8) > 0 ? "javax.annotation.processing.Generated" : "javax.annotation.Generated")); } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/MoreElements.java000066400000000000000000000534741365703632600311270ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * Copyright (C) 2013 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.auto.common; import static javax.lang.model.element.ElementKind.PACKAGE; import static javax.lang.model.element.Modifier.STATIC; import com.google.auto.common.Overrides.ExplicitOverrides; import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.SetMultimap; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleElementVisitor8; import javax.lang.model.util.Types; /** * Static utility methods pertaining to {@link Element} instances. * * @author Gregory Kick */ @Beta public final class MoreElements { /** * An alternate implementation of {@link Elements#getPackageOf} that does not require an * {@link Elements} instance. * * @throws NullPointerException if {@code element} is {@code null} */ public static PackageElement getPackage(Element element) { while (element.getKind() != PACKAGE) { element = element.getEnclosingElement(); } return (PackageElement) element; } private static final class PackageElementVisitor extends CastingElementVisitor { private static final PackageElementVisitor INSTANCE = new PackageElementVisitor(); PackageElementVisitor() { super("package element"); } @Override public PackageElement visitPackage(PackageElement e, Void ignore) { return e; } } /** * Returns the given {@link Element} instance as {@link PackageElement}. * *

This method is functionally equivalent to an {@code instanceof} check and a cast, but should * always be used over that idiom as instructed in the documentation for {@link Element}. * * @throws NullPointerException if {@code element} is {@code null} * @throws IllegalArgumentException if {@code element} isn't a {@link PackageElement}. */ public static PackageElement asPackage(Element element) { return element.accept(PackageElementVisitor.INSTANCE, null); } private static final class TypeElementVisitor extends CastingElementVisitor { private static final TypeElementVisitor INSTANCE = new TypeElementVisitor(); TypeElementVisitor() { super("type element"); } @Override public TypeElement visitType(TypeElement e, Void ignore) { return e; } } /** * Returns true if the given {@link Element} instance is a {@link TypeElement}. * *

This method is functionally equivalent to an {@code instanceof} check, but should * always be used over that idiom as instructed in the documentation for {@link Element}. * * @throws NullPointerException if {@code element} is {@code null} */ public static boolean isType(Element element) { return element.getKind().isClass() || element.getKind().isInterface(); } /** * Returns the given {@link Element} instance as {@link TypeElement}. * *

This method is functionally equivalent to an {@code instanceof} check and a cast, but should * always be used over that idiom as instructed in the documentation for {@link Element}. * * @throws NullPointerException if {@code element} is {@code null} * @throws IllegalArgumentException if {@code element} isn't a {@link TypeElement}. */ public static TypeElement asType(Element element) { return element.accept(TypeElementVisitor.INSTANCE, null); } /** * Returns the given {@link Element} instance as {@link TypeParameterElement}. * *

This method is functionally equivalent to an {@code instanceof} check and a cast, but should * always be used over that idiom as instructed in the documentation for {@link Element}. * * @throws NullPointerException if {@code element} is {@code null} * @throws IllegalArgumentException if {@code element} isn't a {@link TypeParameterElement}. */ public static TypeParameterElement asTypeParameter(Element element) { return element.accept(TypeParameterElementVisitor.INSTANCE, null); } private static final class TypeParameterElementVisitor extends CastingElementVisitor { private static final TypeParameterElementVisitor INSTANCE = new TypeParameterElementVisitor(); TypeParameterElementVisitor() { super("type parameter element"); } @Override public TypeParameterElement visitTypeParameter(TypeParameterElement e, Void ignore) { return e; } } private static final class VariableElementVisitor extends CastingElementVisitor { private static final VariableElementVisitor INSTANCE = new VariableElementVisitor(); VariableElementVisitor() { super("variable element"); } @Override public VariableElement visitVariable(VariableElement e, Void ignore) { return e; } } /** * Returns the given {@link Element} instance as {@link VariableElement}. * *

This method is functionally equivalent to an {@code instanceof} check and a cast, but should * always be used over that idiom as instructed in the documentation for {@link Element}. * * @throws NullPointerException if {@code element} is {@code null} * @throws IllegalArgumentException if {@code element} isn't a {@link VariableElement}. */ public static VariableElement asVariable(Element element) { return element.accept(VariableElementVisitor.INSTANCE, null); } private static final class ExecutableElementVisitor extends CastingElementVisitor { private static final ExecutableElementVisitor INSTANCE = new ExecutableElementVisitor(); ExecutableElementVisitor() { super("executable element"); } @Override public ExecutableElement visitExecutable(ExecutableElement e, Void label) { return e; } } /** * Returns the given {@link Element} instance as {@link ExecutableElement}. * *

This method is functionally equivalent to an {@code instanceof} check and a cast, but should * always be used over that idiom as instructed in the documentation for {@link Element}. * * @throws NullPointerException if {@code element} is {@code null} * @throws IllegalArgumentException if {@code element} isn't a {@link ExecutableElement}. */ public static ExecutableElement asExecutable(Element element) { return element.accept(ExecutableElementVisitor.INSTANCE, null); } /** * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose * {@linkplain AnnotationMirror#getAnnotationType() annotation type} has the same canonical name * as that of {@code annotationClass}. This method is a safer alternative to calling * {@link Element#getAnnotation} and checking for {@code null} as it avoids any interaction with * annotation proxies. */ public static boolean isAnnotationPresent(Element element, Class annotationClass) { return getAnnotationMirror(element, annotationClass).isPresent(); } /** * Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on * {@code element}, or {@link Optional#absent()} if no such annotation exists. This method is a * safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with * annotation proxies. */ public static Optional getAnnotationMirror(Element element, Class annotationClass) { String annotationClassName = annotationClass.getCanonicalName(); for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { TypeElement annotationTypeElement = asType(annotationMirror.getAnnotationType().asElement()); if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) { return Optional.of(annotationMirror); } } return Optional.absent(); } /** * Returns a {@link Predicate} that can be used to filter elements by {@link Modifier}. * The predicate returns {@code true} if the input {@link Element} has all of the given * {@code modifiers}, perhaps in addition to others. * *

Here is an example how one could get a List of static methods of a class: *

{@code
   * FluentIterable.from(ElementFilter.methodsIn(clazzElement.getEnclosedElements()))
   *     .filter(MoreElements.hasModifiers(Modifier.STATIC).toList();
   * }
*/ public static Predicate hasModifiers(Modifier... modifiers) { return hasModifiers(ImmutableSet.copyOf(modifiers)); } /** * Returns a {@link Predicate} that can be used to filter elements by {@link Modifier}. * The predicate returns {@code true} if the input {@link Element} has all of the given * {@code modifiers}, perhaps in addition to others. * *

Here is an example how one could get a List of methods with certain modifiers of a class: *

{@code
   * Set modifiers = ...;
   * FluentIterable.from(ElementFilter.methodsIn(clazzElement.getEnclosedElements()))
   *     .filter(MoreElements.hasModifiers(modifiers).toList();}
   * 
*/ public static Predicate hasModifiers(final Set modifiers) { return new Predicate() { @Override public boolean apply(T input) { return input.getModifiers().containsAll(modifiers); } }; } /** * Returns the set of all non-private, non-static methods from {@code type}, including methods * that it inherits from its ancestors. Inherited methods that are overridden are not included in * the result. So if {@code type} defines {@code public String toString()}, the returned set will * contain that method, but not the {@code toString()} method defined by {@code Object}. * *

The returned set may contain more than one method with the same signature, if * {@code type} inherits those methods from different ancestors. For example, if it * inherits from unrelated interfaces {@code One} and {@code Two} which each define * {@code void foo();}, and if it does not itself override the {@code foo()} method, * then both {@code One.foo()} and {@code Two.foo()} will be in the returned set. * *

The order of the returned set is deterministic: within a class or interface, methods are in * the order they appear in the source code; methods in ancestors come before methods in * descendants; methods in interfaces come before methods in classes; and in a class or interface * that has more than one superinterface, the interfaces are in the order of their appearance in * {@code implements} or {@code extends}. * * @param type the type whose own and inherited methods are to be returned * @param elementUtils an {@link Elements} object, typically returned by * {@link javax.annotation.processing.AbstractProcessor#processingEnv processingEnv}.{@link javax.annotation.processing.ProcessingEnvironment#getElementUtils * getElementUtils()} * * @deprecated The method {@link #getLocalAndInheritedMethods(TypeElement, Types, Elements)} * has better consistency between Java compilers. */ @Deprecated public static ImmutableSet getLocalAndInheritedMethods( TypeElement type, Elements elementUtils) { Overrides overrides = new Overrides.NativeOverrides(elementUtils); return getLocalAndInheritedMethods(type, overrides); } /** * Returns the set of all non-private, non-static methods from {@code type}, including methods * that it inherits from its ancestors. Inherited methods that are overridden are not included in * the result. So if {@code type} defines {@code public String toString()}, the returned set will * contain that method, but not the {@code toString()} method defined by {@code Object}. * *

The returned set may contain more than one method with the same signature, if * {@code type} inherits those methods from different ancestors. For example, if it * inherits from unrelated interfaces {@code One} and {@code Two} which each define * {@code void foo();}, and if it does not itself override the {@code foo()} method, * then both {@code One.foo()} and {@code Two.foo()} will be in the returned set. * *

The order of the returned set is deterministic: within a class or interface, methods are in * the order they appear in the source code; methods in ancestors come before methods in * descendants; methods in interfaces come before methods in classes; and in a class or interface * that has more than one superinterface, the interfaces are in the order of their appearance in * {@code implements} or {@code extends}. * * @param type the type whose own and inherited methods are to be returned * @param typeUtils a {@link Types} object, typically returned by * {@link javax.annotation.processing.AbstractProcessor#processingEnv processingEnv}.{@link javax.annotation.processing.ProcessingEnvironment#getTypeUtils * getTypeUtils()} * @param elementUtils an {@link Elements} object, typically returned by * {@link javax.annotation.processing.AbstractProcessor#processingEnv processingEnv}.{@link javax.annotation.processing.ProcessingEnvironment#getElementUtils * getElementUtils()} */ public static ImmutableSet getLocalAndInheritedMethods( TypeElement type, Types typeUtils, Elements elementUtils) { return getLocalAndInheritedMethods(type, new ExplicitOverrides(typeUtils)); } private static ImmutableSet getLocalAndInheritedMethods( TypeElement type, Overrides overrides) { PackageElement pkg = getPackage(type); ImmutableSet.Builder methods = ImmutableSet.builder(); for (ExecutableElement method : getAllMethods(type, overrides)) { // Filter out all static and non-visible methods. if (!method.getModifiers().contains(STATIC) && methodVisibleFromPackage(method, pkg)) { methods.add(method); } } return methods.build(); } /** * Tests whether one method, as a member of a given type, overrides another method. * *

This method does the same thing as {@link Elements#overrides(ExecutableElement, * ExecutableElement, TypeElement)}, but in a way that is more consistent between compilers, in * particular between javac and ecj (the Eclipse compiler). * * @param overrider the first method, possible overrider * @param overridden the second method, possibly being overridden * @param type the type of which the first method is a member * @return {@code true} if and only if the first method overrides the second */ public static boolean overrides( ExecutableElement overrider, ExecutableElement overridden, TypeElement type, Types typeUtils) { return new ExplicitOverrides(typeUtils).overrides(overrider, overridden, type); } /** * Returns the set of all methods from {@code type}, including methods that it inherits * from its ancestors. Inherited methods that are overridden are not included in the * result. So if {@code type} defines {@code public String toString()}, the returned set * will contain that method, but not the {@code toString()} method defined by {@code Object}. * *

The returned set may contain more than one method with the same signature, if * {@code type} inherits those methods from different ancestors. For example, if it * inherits from unrelated interfaces {@code One} and {@code Two} which each define * {@code void foo();}, and if it does not itself override the {@code foo()} method, * then both {@code One.foo()} and {@code Two.foo()} will be in the returned set. * *

The order of the returned set is deterministic: within a class or interface, methods are in * the order they appear in the source code; methods in ancestors come before methods in * descendants; methods in interfaces come before methods in classes; and in a class or interface * that has more than one superinterface, the interfaces are in the order of their appearance in * {@code implements} or {@code extends}. * * @param type the type whose own and inherited methods are to be returned * @param typeUtils a {@link Types} object, typically returned by * {@link javax.annotation.processing.AbstractProcessor#processingEnv processingEnv}.{@link javax.annotation.processing.ProcessingEnvironment#getTypeUtils * getTypeUtils()} * @param elementUtils an {@link Elements} object, typically returned by * {@link javax.annotation.processing.AbstractProcessor#processingEnv processingEnv}.{@link javax.annotation.processing.ProcessingEnvironment#getElementUtils * getElementUtils()} */ public static ImmutableSet getAllMethods( TypeElement type, Types typeUtils, Elements elementUtils) { return getAllMethods(type, new ExplicitOverrides(typeUtils)); } private static ImmutableSet getAllMethods( TypeElement type, Overrides overrides) { SetMultimap methodMap = LinkedHashMultimap.create(); getAllMethods(type, methodMap); // Find methods that are overridden. We do this using `Elements.overrides`, which means // that it is inherently a quadratic operation, since we have to compare every method against // every other method. We reduce the performance impact by (a) grouping methods by name, since // a method cannot override another method with a different name, and (b) making sure that // methods in ancestor types precede those in descendant types, which means we only have to // check a method against the ones that follow it in that order. Set overridden = new LinkedHashSet(); for (Collection methods : methodMap.asMap().values()) { List methodList = ImmutableList.copyOf(methods); for (int i = 0; i < methodList.size(); i++) { ExecutableElement methodI = methodList.get(i); for (int j = i + 1; j < methodList.size(); j++) { ExecutableElement methodJ = methodList.get(j); if (overrides.overrides(methodJ, methodI, type)) { overridden.add(methodI); break; } } } } Set methods = new LinkedHashSet(methodMap.values()); methods.removeAll(overridden); return ImmutableSet.copyOf(methods); } // Add to `methods` the static and instance methods from `type`. This means all methods from // `type` itself and all methods it inherits from its ancestors. This method does not take // overriding into account, so it will add both an ancestor method and a descendant method that // overrides it. `methods` is a multimap from a method name to all of the methods with that name, // including methods that override or overload one another. Within those methods, those in // ancestor types always precede those in descendant types. private static void getAllMethods( TypeElement type, SetMultimap methods) { for (TypeMirror superInterface : type.getInterfaces()) { getAllMethods(MoreTypes.asTypeElement(superInterface), methods); } if (type.getSuperclass().getKind() != TypeKind.NONE) { // Visit the superclass after superinterfaces so we will always see the implementation of a // method after any interfaces that declared it. getAllMethods(MoreTypes.asTypeElement(type.getSuperclass()), methods); } for (ExecutableElement method : ElementFilter.methodsIn(type.getEnclosedElements())) { methods.put(method.getSimpleName().toString(), method); } } static boolean methodVisibleFromPackage(ExecutableElement method, PackageElement pkg) { // We use Visibility.ofElement rather than .effectiveVisibilityOfElement because it doesn't // really matter whether the containing class is visible. If you inherit a public method // then you have a public method, regardless of whether you inherit it from a public class. Visibility visibility = Visibility.ofElement(method); switch (visibility) { case PRIVATE: return false; case DEFAULT: return getPackage(method).equals(pkg); default: return true; } } private abstract static class CastingElementVisitor extends SimpleElementVisitor8 { private final String label; CastingElementVisitor(String label) { this.label = label; } @Override protected final T defaultAction(Element e, Void ignore) { throw new IllegalArgumentException(e + " does not represent a " + label); } } private MoreElements() {} } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/MoreTypes.java000066400000000000000000001114351365703632600304470ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static javax.lang.model.type.TypeKind.ARRAY; import static javax.lang.model.type.TypeKind.DECLARED; import static javax.lang.model.type.TypeKind.EXECUTABLE; import static javax.lang.model.type.TypeKind.INTERSECTION; import static javax.lang.model.type.TypeKind.TYPEVAR; import static javax.lang.model.type.TypeKind.WILDCARD; import com.google.common.base.Equivalence; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ErrorType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.IntersectionType; import javax.lang.model.type.NoType; import javax.lang.model.type.NullType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; /** * Utilities related to {@link TypeMirror} instances. * * @author Gregory Kick * @since 2.0 */ public final class MoreTypes { private static final class TypeEquivalence extends Equivalence { private static final TypeEquivalence INSTANCE = new TypeEquivalence(); @Override protected boolean doEquivalent(TypeMirror a, TypeMirror b) { return MoreTypes.equal(a, b, ImmutableSet.of()); } @Override protected int doHash(TypeMirror t) { return MoreTypes.hash(t, ImmutableSet.of()); } } /** * Returns an {@link Equivalence} that can be used to compare types. The standard way to compare * types is {@link javax.lang.model.util.Types#isSameType Types.isSameType}, but this alternative * may be preferred in a number of cases: * *

    *
  • If you don't have an instance of {@code Types}. *
  • If you want a reliable {@code hashCode()} for the types, for example to construct a set * of types using {@link java.util.HashSet} with {@link Equivalence#wrap(Object)}. *
  • If you want distinct type variables to be considered equal if they have the same names * and bounds. *
  • If you want wildcard types to compare equal if they have the same bounds. {@code * Types.isSameType} never considers wildcards equal, even when comparing a type to itself. *
*/ public static Equivalence equivalence() { return TypeEquivalence.INSTANCE; } // So EQUAL_VISITOR can be a singleton, we maintain visiting state, in particular which types // have been seen already, in this object. // The logic for handling recursive types like Comparable> is very tricky. // If we're not careful we'll end up with an infinite recursion. So we record the types that // we've already seen during the recursion, and if we see the same pair of types again we just // return true provisionally. But "the same pair of types" is itself poorly-defined. We can't // just say that it is an equal pair of TypeMirrors, because of course if we knew how to // determine that then we wouldn't need the complicated type visitor at all. On the other hand, // we can't say that it is an identical pair of TypeMirrors either, because there's no // guarantee that the TypeMirrors for the two Ts in Comparable> will be // represented by the same object, and indeed with the Eclipse compiler they aren't. We could // compare the corresponding Elements, since equality is well-defined there, but that's not enough // either, because the Element for Set is the same as the one for Set. So we // approximate by comparing the Elements and, if there are any type arguments, requiring them to // be identical. This may not be foolproof either but it is sufficient for all the cases we've // encountered so far. private static final class EqualVisitorParam { TypeMirror type; Set visiting; } private static class ComparedElements { final Element a; final ImmutableList aArguments; final Element b; final ImmutableList bArguments; ComparedElements( Element a, ImmutableList aArguments, Element b, ImmutableList bArguments) { this.a = a; this.aArguments = aArguments; this.b = b; this.bArguments = bArguments; } @Override public boolean equals(Object o) { if (o instanceof ComparedElements) { ComparedElements that = (ComparedElements) o; int nArguments = aArguments.size(); if (!this.a.equals(that.a) || !this.b.equals(that.b) || nArguments != bArguments.size()) { // The arguments must be the same size, but we check anyway. return false; } for (int i = 0; i < nArguments; i++) { if (aArguments.get(i) != bArguments.get(i)) { return false; } } return true; } else { return false; } } @Override public int hashCode() { return a.hashCode() * 31 + b.hashCode(); } } private static final class EqualVisitor extends SimpleTypeVisitor8 { private static final EqualVisitor INSTANCE = new EqualVisitor(); @Override protected Boolean defaultAction(TypeMirror a, EqualVisitorParam p) { return a.getKind().equals(p.type.getKind()); } @Override public Boolean visitArray(ArrayType a, EqualVisitorParam p) { if (p.type.getKind().equals(ARRAY)) { ArrayType b = (ArrayType) p.type; return equal(a.getComponentType(), b.getComponentType(), p.visiting); } return false; } @Override public Boolean visitDeclared(DeclaredType a, EqualVisitorParam p) { if (p.type.getKind().equals(DECLARED)) { DeclaredType b = (DeclaredType) p.type; Element aElement = a.asElement(); Element bElement = b.asElement(); Set newVisiting = visitingSetPlus( p.visiting, aElement, a.getTypeArguments(), bElement, b.getTypeArguments()); if (newVisiting.equals(p.visiting)) { // We're already visiting this pair of elements. // This can happen for example with Enum in Enum>. Return a // provisional true value since if the Elements are not in fact equal the original // visitor of Enum will discover that. We have to check both Elements being compared // though to avoid missing the fact that one of the types being compared // differs at exactly this point. return true; } return aElement.equals(bElement) && equal(enclosingType(a), enclosingType(b), newVisiting) && equalLists(a.getTypeArguments(), b.getTypeArguments(), newVisiting); } return false; } @Override @SuppressWarnings("TypeEquals") public Boolean visitError(ErrorType a, EqualVisitorParam p) { return a.equals(p.type); } @Override public Boolean visitExecutable(ExecutableType a, EqualVisitorParam p) { if (p.type.getKind().equals(EXECUTABLE)) { ExecutableType b = (ExecutableType) p.type; return equalLists(a.getParameterTypes(), b.getParameterTypes(), p.visiting) && equal(a.getReturnType(), b.getReturnType(), p.visiting) && equalLists(a.getThrownTypes(), b.getThrownTypes(), p.visiting) && equalLists(a.getTypeVariables(), b.getTypeVariables(), p.visiting); } return false; } @Override public Boolean visitIntersection(IntersectionType a, EqualVisitorParam p) { if (p.type.getKind().equals(INTERSECTION)) { IntersectionType b = (IntersectionType) p.type; return equalLists(a.getBounds(), b.getBounds(), p.visiting); } return false; } @Override public Boolean visitTypeVariable(TypeVariable a, EqualVisitorParam p) { if (p.type.getKind().equals(TYPEVAR)) { TypeVariable b = (TypeVariable) p.type; TypeParameterElement aElement = (TypeParameterElement) a.asElement(); TypeParameterElement bElement = (TypeParameterElement) b.asElement(); Set newVisiting = visitingSetPlus(p.visiting, aElement, bElement); if (newVisiting.equals(p.visiting)) { // We're already visiting this pair of elements. // This can happen with our friend Eclipse when looking at >. // It incorrectly reports the upper bound of T as T itself. return true; } // We use aElement.getBounds() instead of a.getUpperBound() to avoid having to deal with // the different way intersection types (like >) are // represented before and after Java 8. We do have an issue that this code may consider // that is different from , but it's very // hard to avoid that, and not likely to be much of a problem in practice. return equalLists(aElement.getBounds(), bElement.getBounds(), newVisiting) && equal(a.getLowerBound(), b.getLowerBound(), newVisiting) && a.asElement().getSimpleName().equals(b.asElement().getSimpleName()); } return false; } @Override public Boolean visitWildcard(WildcardType a, EqualVisitorParam p) { if (p.type.getKind().equals(WILDCARD)) { WildcardType b = (WildcardType) p.type; return equal(a.getExtendsBound(), b.getExtendsBound(), p.visiting) && equal(a.getSuperBound(), b.getSuperBound(), p.visiting); } return false; } @Override public Boolean visitUnknown(TypeMirror a, EqualVisitorParam p) { throw new UnsupportedOperationException(); } private Set visitingSetPlus( Set visiting, Element a, Element b) { ImmutableList noArguments = ImmutableList.of(); return visitingSetPlus(visiting, a, noArguments, b, noArguments); } private Set visitingSetPlus( Set visiting, Element a, List aArguments, Element b, List bArguments) { ComparedElements comparedElements = new ComparedElements( a, ImmutableList.copyOf(aArguments), b, ImmutableList.copyOf(bArguments)); Set newVisiting = new HashSet(visiting); newVisiting.add(comparedElements); return newVisiting; } } @SuppressWarnings("TypeEquals") private static boolean equal(TypeMirror a, TypeMirror b, Set visiting) { // TypeMirror.equals is not guaranteed to return true for types that are equal, but we can // assume that if it does return true then the types are equal. This check also avoids getting // stuck in infinite recursion when Eclipse decrees that the upper bound of the second K in // > is a distinct but equal K. // The javac implementation of ExecutableType, at least in some versions, does not take thrown // exceptions into account in its equals implementation, so avoid this optimization for // ExecutableType. if (Objects.equal(a, b) && !(a instanceof ExecutableType)) { return true; } EqualVisitorParam p = new EqualVisitorParam(); p.type = b; p.visiting = visiting; return (a == b) || (a != null && b != null && a.accept(EqualVisitor.INSTANCE, p)); } /** * Returns the type of the innermost enclosing instance, or null if there is none. This is the * same as {@link DeclaredType#getEnclosingType()} except that it returns null rather than * NoType for a static type. We need this because of * this bug whereby * the Eclipse compiler returns a value for static classes that is not NoType. */ private static TypeMirror enclosingType(DeclaredType t) { TypeMirror enclosing = t.getEnclosingType(); if (enclosing.getKind().equals(TypeKind.NONE) || t.asElement().getModifiers().contains(Modifier.STATIC)) { return null; } return enclosing; } private static boolean equalLists( List a, List b, Set visiting) { int size = a.size(); if (size != b.size()) { return false; } // Use iterators in case the Lists aren't RandomAccess Iterator aIterator = a.iterator(); Iterator bIterator = b.iterator(); while (aIterator.hasNext()) { if (!bIterator.hasNext()) { return false; } TypeMirror nextMirrorA = aIterator.next(); TypeMirror nextMirrorB = bIterator.next(); if (!equal(nextMirrorA, nextMirrorB, visiting)) { return false; } } return !aIterator.hasNext(); } private static final int HASH_SEED = 17; private static final int HASH_MULTIPLIER = 31; private static final class HashVisitor extends SimpleTypeVisitor8> { private static final HashVisitor INSTANCE = new HashVisitor(); int hashKind(int seed, TypeMirror t) { int result = seed * HASH_MULTIPLIER; result += t.getKind().hashCode(); return result; } @Override protected Integer defaultAction(TypeMirror e, Set visiting) { return hashKind(HASH_SEED, e); } @Override public Integer visitArray(ArrayType t, Set visiting) { int result = hashKind(HASH_SEED, t); result *= HASH_MULTIPLIER; result += t.getComponentType().accept(this, visiting); return result; } @Override public Integer visitDeclared(DeclaredType t, Set visiting) { Element element = t.asElement(); if (visiting.contains(element)) { return 0; } Set newVisiting = new HashSet(visiting); newVisiting.add(element); int result = hashKind(HASH_SEED, t); result *= HASH_MULTIPLIER; result += t.asElement().hashCode(); result *= HASH_MULTIPLIER; result += t.getEnclosingType().accept(this, newVisiting); result *= HASH_MULTIPLIER; result += hashList(t.getTypeArguments(), newVisiting); return result; } @Override public Integer visitExecutable(ExecutableType t, Set visiting) { int result = hashKind(HASH_SEED, t); result *= HASH_MULTIPLIER; result += hashList(t.getParameterTypes(), visiting); result *= HASH_MULTIPLIER; result += t.getReturnType().accept(this, visiting); result *= HASH_MULTIPLIER; result += hashList(t.getThrownTypes(), visiting); result *= HASH_MULTIPLIER; result += hashList(t.getTypeVariables(), visiting); return result; } @Override public Integer visitTypeVariable(TypeVariable t, Set visiting) { int result = hashKind(HASH_SEED, t); result *= HASH_MULTIPLIER; result += t.getLowerBound().accept(this, visiting); TypeParameterElement element = (TypeParameterElement) t.asElement(); for (TypeMirror bound : element.getBounds()) { result *= HASH_MULTIPLIER; result += bound.accept(this, visiting); } return result; } @Override public Integer visitWildcard(WildcardType t, Set visiting) { int result = hashKind(HASH_SEED, t); result *= HASH_MULTIPLIER; result += (t.getExtendsBound() == null) ? 0 : t.getExtendsBound().accept(this, visiting); result *= HASH_MULTIPLIER; result += (t.getSuperBound() == null) ? 0 : t.getSuperBound().accept(this, visiting); return result; } @Override public Integer visitUnknown(TypeMirror t, Set visiting) { throw new UnsupportedOperationException(); } }; private static int hashList(List mirrors, Set visiting) { int result = HASH_SEED; for (TypeMirror mirror : mirrors) { result *= HASH_MULTIPLIER; result += hash(mirror, visiting); } return result; } private static int hash(TypeMirror mirror, Set visiting) { return mirror == null ? 0 : mirror.accept(HashVisitor.INSTANCE, visiting); } /** * Returns the set of {@linkplain TypeElement types} that are referenced by the given {@link * TypeMirror}. */ public static ImmutableSet referencedTypes(TypeMirror type) { checkNotNull(type); ImmutableSet.Builder elements = ImmutableSet.builder(); type.accept(ReferencedTypes.INSTANCE, elements); return elements.build(); } private static final class ReferencedTypes extends SimpleTypeVisitor8> { private static final ReferencedTypes INSTANCE = new ReferencedTypes(); @Override public Void visitArray(ArrayType t, ImmutableSet.Builder p) { t.getComponentType().accept(this, p); return null; } @Override public Void visitDeclared(DeclaredType t, ImmutableSet.Builder p) { p.add(MoreElements.asType(t.asElement())); for (TypeMirror typeArgument : t.getTypeArguments()) { typeArgument.accept(this, p); } return null; } @Override public Void visitTypeVariable(TypeVariable t, ImmutableSet.Builder p) { t.getLowerBound().accept(this, p); t.getUpperBound().accept(this, p); return null; } @Override public Void visitWildcard(WildcardType t, ImmutableSet.Builder p) { TypeMirror extendsBound = t.getExtendsBound(); if (extendsBound != null) { extendsBound.accept(this, p); } TypeMirror superBound = t.getSuperBound(); if (superBound != null) { superBound.accept(this, p); } return null; } } /** * An alternate implementation of {@link Types#asElement} that does not require a {@link Types} * instance with the notable difference that it will throw {@link IllegalArgumentException} * instead of returning null if the {@link TypeMirror} can not be converted to an {@link Element}. * * @throws NullPointerException if {@code typeMirror} is {@code null} * @throws IllegalArgumentException if {@code typeMirror} cannot be converted to an {@link * Element} */ public static Element asElement(TypeMirror typeMirror) { return typeMirror.accept(AsElementVisitor.INSTANCE, null); } private static final class AsElementVisitor extends SimpleTypeVisitor8 { private static final AsElementVisitor INSTANCE = new AsElementVisitor(); @Override protected Element defaultAction(TypeMirror e, Void p) { throw new IllegalArgumentException(e + " cannot be converted to an Element"); } @Override public Element visitDeclared(DeclaredType t, Void p) { return t.asElement(); } @Override public Element visitError(ErrorType t, Void p) { return t.asElement(); } @Override public Element visitTypeVariable(TypeVariable t, Void p) { return t.asElement(); } }; // TODO(gak): consider removing these two methods as they're pretty trivial now public static TypeElement asTypeElement(TypeMirror mirror) { return MoreElements.asType(asElement(mirror)); } public static ImmutableSet asTypeElements(Iterable mirrors) { checkNotNull(mirrors); ImmutableSet.Builder builder = ImmutableSet.builder(); for (TypeMirror mirror : mirrors) { builder.add(asTypeElement(mirror)); } return builder.build(); } /** * Returns a {@link ArrayType} if the {@link TypeMirror} represents an array or throws an {@link * IllegalArgumentException}. */ public static ArrayType asArray(TypeMirror maybeArrayType) { return maybeArrayType.accept(ArrayTypeVisitor.INSTANCE, null); } private static final class ArrayTypeVisitor extends CastingTypeVisitor { private static final ArrayTypeVisitor INSTANCE = new ArrayTypeVisitor(); ArrayTypeVisitor() { super("array"); } @Override public ArrayType visitArray(ArrayType type, Void ignore) { return type; } } /** * Returns a {@link DeclaredType} if the {@link TypeMirror} represents a declared type such as a * class, interface, union/compound, or enum or throws an {@link IllegalArgumentException}. */ public static DeclaredType asDeclared(TypeMirror maybeDeclaredType) { return maybeDeclaredType.accept(DeclaredTypeVisitor.INSTANCE, null); } private static final class DeclaredTypeVisitor extends CastingTypeVisitor { private static final DeclaredTypeVisitor INSTANCE = new DeclaredTypeVisitor(); DeclaredTypeVisitor() { super("declared type"); } @Override public DeclaredType visitDeclared(DeclaredType type, Void ignore) { return type; } } /** * Returns a {@link ExecutableType} if the {@link TypeMirror} represents an executable type such * as may result from missing code, or bad compiles or throws an {@link IllegalArgumentException}. */ public static ErrorType asError(TypeMirror maybeErrorType) { return maybeErrorType.accept(ErrorTypeVisitor.INSTANCE, null); } private static final class ErrorTypeVisitor extends CastingTypeVisitor { private static final ErrorTypeVisitor INSTANCE = new ErrorTypeVisitor(); ErrorTypeVisitor() { super("error type"); } @Override public ErrorType visitError(ErrorType type, Void ignore) { return type; } } /** * Returns a {@link ExecutableType} if the {@link TypeMirror} represents an executable type such * as a method, constructor, or initializer or throws an {@link IllegalArgumentException}. */ public static ExecutableType asExecutable(TypeMirror maybeExecutableType) { return maybeExecutableType.accept(ExecutableTypeVisitor.INSTANCE, null); } private static final class ExecutableTypeVisitor extends CastingTypeVisitor { private static final ExecutableTypeVisitor INSTANCE = new ExecutableTypeVisitor(); ExecutableTypeVisitor() { super("executable type"); } @Override public ExecutableType visitExecutable(ExecutableType type, Void ignore) { return type; } } /** * Returns an {@link IntersectionType} if the {@link TypeMirror} represents an intersection-type * or throws an {@link IllegalArgumentException}. */ public static IntersectionType asIntersection(TypeMirror maybeIntersectionType) { return maybeIntersectionType.accept(IntersectionTypeVisitor.INSTANCE, null); } private static final class IntersectionTypeVisitor extends CastingTypeVisitor { private static final IntersectionTypeVisitor INSTANCE = new IntersectionTypeVisitor(); IntersectionTypeVisitor() { super("intersection type"); } @Override public IntersectionType visitIntersection(IntersectionType type, Void ignore) { return type; } } /** * Returns a {@link NoType} if the {@link TypeMirror} represents an non-type such as void, or * package, etc. or throws an {@link IllegalArgumentException}. */ public static NoType asNoType(TypeMirror maybeNoType) { return maybeNoType.accept(NoTypeVisitor.INSTANCE, null); } private static final class NoTypeVisitor extends CastingTypeVisitor { private static final NoTypeVisitor INSTANCE = new NoTypeVisitor(); NoTypeVisitor() { super("non-type"); } @Override public NoType visitNoType(NoType type, Void ignore) { return type; } } /** * Returns a {@link NullType} if the {@link TypeMirror} represents the null type or throws an * {@link IllegalArgumentException}. */ public static NullType asNullType(TypeMirror maybeNullType) { return maybeNullType.accept(NullTypeVisitor.INSTANCE, null); } private static final class NullTypeVisitor extends CastingTypeVisitor { private static final NullTypeVisitor INSTANCE = new NullTypeVisitor(); NullTypeVisitor() { super("null"); } @Override public NullType visitNull(NullType type, Void ignore) { return type; } } /** * Returns a {@link PrimitiveType} if the {@link TypeMirror} represents a primitive type or throws * an {@link IllegalArgumentException}. */ public static PrimitiveType asPrimitiveType(TypeMirror maybePrimitiveType) { return maybePrimitiveType.accept(PrimitiveTypeVisitor.INSTANCE, null); } private static final class PrimitiveTypeVisitor extends CastingTypeVisitor { private static final PrimitiveTypeVisitor INSTANCE = new PrimitiveTypeVisitor(); PrimitiveTypeVisitor() { super("primitive type"); } @Override public PrimitiveType visitPrimitive(PrimitiveType type, Void ignore) { return type; } } // // visitUnionType would go here, but isn't relevant for annotation processors // /** * Returns a {@link TypeVariable} if the {@link TypeMirror} represents a type variable or throws * an {@link IllegalArgumentException}. */ public static TypeVariable asTypeVariable(TypeMirror maybeTypeVariable) { return maybeTypeVariable.accept(TypeVariableVisitor.INSTANCE, null); } private static final class TypeVariableVisitor extends CastingTypeVisitor { private static final TypeVariableVisitor INSTANCE = new TypeVariableVisitor(); TypeVariableVisitor() { super("type variable"); } @Override public TypeVariable visitTypeVariable(TypeVariable type, Void ignore) { return type; } } /** * Returns a {@link WildcardType} if the {@link TypeMirror} represents a wildcard type or throws * an {@link IllegalArgumentException}. */ public static WildcardType asWildcard(TypeMirror maybeWildcardType) { return maybeWildcardType.accept(WildcardTypeVisitor.INSTANCE, null); } private static final class WildcardTypeVisitor extends CastingTypeVisitor { private static final WildcardTypeVisitor INSTANCE = new WildcardTypeVisitor(); WildcardTypeVisitor() { super("wildcard type"); } @Override public WildcardType visitWildcard(WildcardType type, Void ignore) { return type; } } /** * Returns true if the raw type underlying the given {@link TypeMirror} represents a type that can * be referenced by a {@link Class}. If this returns true, then {@link #isTypeOf} is guaranteed to * not throw. */ public static boolean isType(TypeMirror type) { return type.accept(IsTypeVisitor.INSTANCE, null); } private static final class IsTypeVisitor extends SimpleTypeVisitor8 { private static final IsTypeVisitor INSTANCE = new IsTypeVisitor(); @Override protected Boolean defaultAction(TypeMirror type, Void ignored) { return false; } @Override public Boolean visitNoType(NoType noType, Void p) { return noType.getKind().equals(TypeKind.VOID); } @Override public Boolean visitPrimitive(PrimitiveType type, Void p) { return true; } @Override public Boolean visitArray(ArrayType array, Void p) { return true; } @Override public Boolean visitDeclared(DeclaredType type, Void ignored) { return MoreElements.isType(type.asElement()); } } /** * Returns true if the raw type underlying the given {@link TypeMirror} represents the same raw * type as the given {@link Class} and throws an IllegalArgumentException if the {@link * TypeMirror} does not represent a type that can be referenced by a {@link Class} */ public static boolean isTypeOf(final Class clazz, TypeMirror type) { checkNotNull(clazz); return type.accept(new IsTypeOf(clazz), null); } private static final class IsTypeOf extends SimpleTypeVisitor8 { private final Class clazz; IsTypeOf(Class clazz) { this.clazz = clazz; } @Override protected Boolean defaultAction(TypeMirror type, Void ignored) { throw new IllegalArgumentException(type + " cannot be represented as a Class."); } @Override public Boolean visitNoType(NoType noType, Void p) { if (noType.getKind().equals(TypeKind.VOID)) { return clazz.equals(Void.TYPE); } throw new IllegalArgumentException(noType + " cannot be represented as a Class."); } @Override public Boolean visitPrimitive(PrimitiveType type, Void p) { switch (type.getKind()) { case BOOLEAN: return clazz.equals(Boolean.TYPE); case BYTE: return clazz.equals(Byte.TYPE); case CHAR: return clazz.equals(Character.TYPE); case DOUBLE: return clazz.equals(Double.TYPE); case FLOAT: return clazz.equals(Float.TYPE); case INT: return clazz.equals(Integer.TYPE); case LONG: return clazz.equals(Long.TYPE); case SHORT: return clazz.equals(Short.TYPE); default: throw new IllegalArgumentException(type + " cannot be represented as a Class."); } } @Override public Boolean visitArray(ArrayType array, Void p) { return clazz.isArray() && isTypeOf(clazz.getComponentType(), array.getComponentType()); } @Override public Boolean visitDeclared(DeclaredType type, Void ignored) { TypeElement typeElement = MoreElements.asType(type.asElement()); return typeElement.getQualifiedName().contentEquals(clazz.getCanonicalName()); } } /** * Returns the superclass of {@code type}, with any type parameters bound by {@code type}, or * {@link Optional#absent()} if {@code type} is an interface or {@link Object} or its superclass * is {@link Object}. */ // TODO(user): Remove unused parameter Elements? public static Optional nonObjectSuperclass(Types types, Elements elements, DeclaredType type) { checkNotNull(types); checkNotNull(elements); // This is no longer used, but here to avoid changing the API. checkNotNull(type); TypeMirror superclassType = asTypeElement(type).getSuperclass(); if (!isType(superclassType)) { // type is Object or an interface return Optional.absent(); } DeclaredType superclass = asDeclared(superclassType); if (isObjectType(superclass)) { return Optional.absent(); } if (superclass.getTypeArguments().isEmpty()) { return Optional.of(superclass); } // In the case where the super class has type parameters, TypeElement#getSuperclass gives // SuperClass rather than SuperClass, so use Types#directSupertypes instead. The javadoc // for Types#directSupertypes guarantees that a super class, if it exists, comes before any // interfaces. Thus, we can just get the first element in the list. return Optional.of(asDeclared(types.directSupertypes(type).get(0))); } private static boolean isObjectType(DeclaredType type) { return asTypeElement(type).getQualifiedName().contentEquals("java.lang.Object"); } /** * Resolves a {@link VariableElement} parameter to a method or constructor based on the given * container, or a member of a class. For parameters to a method or constructor, the variable's * enclosing element must be a supertype of the container type. For example, given a * {@code container} of type {@code Set}, and a variable corresponding to the {@code E e} * parameter in the {@code Set.add(E e)} method, this will return a TypeMirror for {@code String}. */ public static TypeMirror asMemberOf(Types types, DeclaredType container, VariableElement variable) { if (variable.getKind().equals(ElementKind.PARAMETER)) { ExecutableElement methodOrConstructor = MoreElements.asExecutable(variable.getEnclosingElement()); ExecutableType resolvedMethodOrConstructor = MoreTypes.asExecutable(types.asMemberOf(container, methodOrConstructor)); List parameters = methodOrConstructor.getParameters(); List parameterTypes = resolvedMethodOrConstructor.getParameterTypes(); checkState(parameters.size() == parameterTypes.size()); for (int i = 0; i < parameters.size(); i++) { // We need to capture the parameter type of the variable we're concerned about, // for later printing. This is the only way to do it since we can't use // types.asMemberOf on variables of methods. if (parameters.get(i).equals(variable)) { return parameterTypes.get(i); } } throw new IllegalStateException("Could not find variable: " + variable); } else { return types.asMemberOf(container, variable); } } private abstract static class CastingTypeVisitor extends SimpleTypeVisitor8 { private final String label; CastingTypeVisitor(String label) { this.label = label; } @Override protected T defaultAction(TypeMirror e, Void v) { throw new IllegalArgumentException(e + " does not represent a " + label); } } /** * Returns true if casting {@code Object} to the given type will elicit an unchecked warning from * the compiler. Only type variables and parameterized types such as {@code List} produce * such warnings. There will be no warning if the type's only type parameters are simple * wildcards, as in {@code Map}. */ public static boolean isConversionFromObjectUnchecked(TypeMirror type) { return new CastingUncheckedVisitor().visit(type, null); } /** * Visitor that tells whether a type is erased, in the sense of {@link #castIsUnchecked}. Each * visitX method returns true if its input parameter is true or if the type being visited is * erased. */ private static class CastingUncheckedVisitor extends SimpleTypeVisitor8 { CastingUncheckedVisitor() { super(false); } @Override public Boolean visitUnknown(TypeMirror t, Void p) { // We don't know whether casting is unchecked for this mysterious type but assume it is, // so we will insert a possibly unnecessary @SuppressWarnings("unchecked"). return true; } @Override public Boolean visitArray(ArrayType t, Void p) { return visit(t.getComponentType(), p); } @Override public Boolean visitDeclared(DeclaredType t, Void p) { return t.getTypeArguments().stream().anyMatch(CastingUncheckedVisitor::uncheckedTypeArgument); } @Override public Boolean visitTypeVariable(TypeVariable t, Void p) { return true; } // If a type has a type argument, then casting to the type is unchecked, except if the argument // is or . The same applies to all type arguments, so casting to Map // does not produce an unchecked warning for example. private static boolean uncheckedTypeArgument(TypeMirror arg) { if (arg.getKind().equals(TypeKind.WILDCARD)) { WildcardType wildcard = asWildcard(arg); if (wildcard.getExtendsBound() == null || isJavaLangObject(wildcard.getExtendsBound())) { // This is , unless there's a super bound, in which case it is and // is erased. return (wildcard.getSuperBound() != null); } } return true; } private static boolean isJavaLangObject(TypeMirror type) { if (type.getKind() != TypeKind.DECLARED) { return false; } TypeElement typeElement = asTypeElement(type); return typeElement.getQualifiedName().contentEquals("java.lang.Object"); } } private MoreTypes() {} } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/Overrides.java000066400000000000000000000466131365703632600304670ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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.google.auto.common; import com.google.common.base.Preconditions; import com.google.common.base.Verify; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import java.util.List; import java.util.Map; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; /** * Determines if one method overrides another. This class defines two ways of doing that: * {@code NativeOverrides} uses the method * {@link Elements#overrides(ExecutableElement, ExecutableElement, TypeElement)} while * {@code ExplicitOverrides} reimplements that method in a way that is more consistent between * compilers, in particular between javac and ecj (the Eclipse compiler). * * @author emcmanus@google.com (Éamonn McManus) */ abstract class Overrides { abstract boolean overrides( ExecutableElement overrider, ExecutableElement overridden, TypeElement in); static class NativeOverrides extends Overrides { private final Elements elementUtils; NativeOverrides(Elements elementUtils) { this.elementUtils = elementUtils; } @Override boolean overrides(ExecutableElement overrider, ExecutableElement overridden, TypeElement in) { return elementUtils.overrides(overrider, overridden, in); } } static class ExplicitOverrides extends Overrides { private final Types typeUtils; ExplicitOverrides(Types typeUtils) { this.typeUtils = typeUtils; } @Override public boolean overrides( ExecutableElement overrider, ExecutableElement overridden, TypeElement in) { if (!overrider.getSimpleName().equals(overridden.getSimpleName())) { // They must have the same name. return false; } // We should just be able to write overrider.equals(overridden) here, but that runs afoul // of a problem with Eclipse. If for example you look at the method Stream stream() in // Collection, as obtained by collectionTypeElement.getEnclosedElements(), it will not // compare equal to the method Stream stream() as obtained by // elementUtils.getAllMembers(listTypeElement), even though List inherits the method // from Collection. The reason is that, in ecj, getAllMembers does type substitution, // so the return type of stream() is Stream, where E' is the E from List rather than // the one from Collection. Instead we compare the enclosing element, which will be // Collection no matter how we got the method. If two methods are in the same type // then it's impossible for one to override the other, regardless of whether they are the // same method. if (overrider.getEnclosingElement().equals(overridden.getEnclosingElement())) { return false; } if (overridden.getModifiers().contains(Modifier.STATIC)) { // Static methods can't be overridden (though they can be hidden by other static methods). return false; } Visibility overriddenVisibility = Visibility.ofElement(overridden); Visibility overriderVisibility = Visibility.ofElement(overrider); if (overriddenVisibility.equals(Visibility.PRIVATE) || overriderVisibility.compareTo(overriddenVisibility) < 0) { // Private methods can't be overridden, and methods can't be overridden by less-visible // methods. The latter condition is enforced by the compiler so in theory we might report // an "incorrect" result here for code that javac would not have allowed. return false; } if (!isSubsignature(overrider, overridden, in)) { return false; } if (!MoreElements.methodVisibleFromPackage(overridden, MoreElements.getPackage(overrider))) { // If the overridden method is a package-private method in a different package then it // can't be overridden. return false; } TypeElement overriddenType; if (!(overridden.getEnclosingElement() instanceof TypeElement)) { return false; // We don't know how this could happen but we avoid blowing up if it does. } overriddenType = MoreElements.asType(overridden.getEnclosingElement()); // We erase the types before checking subtypes, because the TypeMirror we get for List is // not a subtype of the one we get for Collection since the two E instances are not the // same. For the purposes of overriding, type parameters in the containing type should not // matter because if the code compiles at all then they must be consistent. if (!typeUtils.isSubtype( typeUtils.erasure(in.asType()), typeUtils.erasure(overriddenType.asType()))) { return false; } if (in.getKind().isClass()) { // Method mC in or inherited by class C (JLS 8.4.8.1)... if (overriddenType.getKind().isClass()) { // ...overrides from C another method mA declared in class A. The only condition we // haven't checked is that C does not inherit mA. Ideally we could just write this: // return !elementUtils.getAllMembers(in).contains(overridden); // But that doesn't work in Eclipse. For example, getAllMembers(AbstractList) // contains List.isEmpty() where you might reasonably expect it to contain // AbstractCollection.isEmpty(). So we need to visit superclasses until we reach // one that declares the same method, and check that we haven't reached mA. We compare // the enclosing elements rather than the methods themselves for the reason described // at the start of the method. ExecutableElement inherited = methodFromSuperclasses(in, overridden); return !overridden.getEnclosingElement().equals(inherited.getEnclosingElement()); } else if (overriddenType.getKind().isInterface()) { // ...overrides from C another method mI declared in interface I. We've already checked // the conditions (assuming that the only alternative to mI being abstract or default is // mI being static, which we eliminated above). However, it appears that the logic here // is necessary in order to be compatible with javac's `overrides` method. An inherited // abstract method does not override another method. (But, if it is not inherited, // it does, including if `in` inherits a concrete method of the same name from its // superclass.) Here again we can use getAllMembers with javac but not with ecj. javac // says that getAllMembers(AbstractList) contains both AbstractCollection.size() and // List.size(), but ecj doesn't have the latter. The spec is not particularly clear so // either seems justifiable. So we need to look up the interface path that goes from `in` // to `overriddenType` (or the several paths if there are several) and apply similar logic // to methodFromSuperclasses above. if (overrider.getModifiers().contains(Modifier.ABSTRACT)) { ExecutableElement inherited = methodFromSuperinterfaces(in, overridden); return !overridden.getEnclosingElement().equals(inherited.getEnclosingElement()); } else { return true; } } else { // We don't know what this is so say no. return false; } } else { return in.getKind().isInterface(); // Method mI in or inherited by interface I (JLS 9.4.1.1). We've already checked everything. // If this is not an interface then we don't know what it is so we say no. } } private boolean isSubsignature( ExecutableElement overrider, ExecutableElement overridden, TypeElement in) { DeclaredType inType = MoreTypes.asDeclared(in.asType()); try { ExecutableType overriderExecutable = MoreTypes.asExecutable(typeUtils.asMemberOf(inType, overrider)); ExecutableType overriddenExecutable = MoreTypes.asExecutable(typeUtils.asMemberOf(inType, overridden)); return typeUtils.isSubsignature(overriderExecutable, overriddenExecutable); } catch (IllegalArgumentException e) { // This might mean that at least one of the methods is not in fact declared in or inherited // by `in` (in which case we should indeed return false); or it might mean that we are // tickling an Eclipse bug such as https://bugs.eclipse.org/bugs/show_bug.cgi?id=499026 // (in which case we fall back on explicit code to find the parameters). int nParams = overrider.getParameters().size(); if (overridden.getParameters().size() != nParams) { return false; } List overriderParams = erasedParameterTypes(overrider, in); List overriddenParams = erasedParameterTypes(overridden, in); if (overriderParams == null || overriddenParams == null) { // This probably means that one or other of the methods is not in `in`. return false; } for (int i = 0; i < nParams; i++) { if (!typeUtils.isSameType(overriderParams.get(i), overriddenParams.get(i))) { // If the erasures of the parameters don't correspond, return false. We erase so we // don't get any confusion about different type variables not comparing equal. return false; } } return true; } } /** * Returns the list of erased parameter types of the given method as they appear in the given * type. For example, if the method is {@code add(E)} from {@code List} and we ask how it * appears in {@code class NumberList implements List}, the answer will be * {@code Number}. That will also be the answer for {@code class NumberList * implements List}. The parameter types are erased since the purpose of this method is to * determine whether two methods are candidates for one to override the other. */ ImmutableList erasedParameterTypes(ExecutableElement method, TypeElement in) { if (method.getParameters().isEmpty()) { return ImmutableList.of(); } return new TypeSubstVisitor().erasedParameterTypes(method, in); } /** * Visitor that replaces type variables with their values in the types it sees. If we know * that {@code E} is {@code String}, then we can return {@code String} for {@code E}, * {@code List} for {@code List}, {@code String[]} for {@code E[]}, etc. We don't * have to cover all types here because (1) the type is going to end up being erased, and * (2) wildcards can't appear in direct supertypes. So for example it is illegal to write * {@code class MyList implements List}. It's legal to write * {@code class MyList implements List>} but that doesn't matter * because the {@code E} of the {@code List} is going to be erased to raw {@code Set}. */ private class TypeSubstVisitor extends SimpleTypeVisitor8 { /** * The bindings of type variables. We can put them all in one map because E in {@code List} * is not the same as E in {@code Collection}. As we ascend the type hierarchy we'll add * mappings for all the variables we see. We could equivalently create a new map for each type * we visit, but this is slightly simpler and probably about as performant. */ private final Map typeBindings = Maps.newLinkedHashMap(); ImmutableList erasedParameterTypes(ExecutableElement method, TypeElement in) { if (method.getEnclosingElement().equals(in)) { ImmutableList.Builder params = ImmutableList.builder(); for (VariableElement param : method.getParameters()) { params.add(typeUtils.erasure(visit(param.asType()))); } return params.build(); } // Make a list of supertypes we are going to visit recursively: the superclass, if there // is one, plus the superinterfaces. List supers = Lists.newArrayList(); if (in.getSuperclass().getKind() == TypeKind.DECLARED) { supers.add(in.getSuperclass()); } supers.addAll(in.getInterfaces()); for (TypeMirror supertype : supers) { DeclaredType declared = MoreTypes.asDeclared(supertype); TypeElement element = MoreElements.asType(declared.asElement()); List actuals = declared.getTypeArguments(); List formals = element.getTypeParameters(); Verify.verify(actuals.size() == formals.size()); for (int i = 0; i < actuals.size(); i++) { typeBindings.put(formals.get(i), actuals.get(i)); } ImmutableList params = erasedParameterTypes(method, element); if (params != null) { return params; } } return null; } @Override protected TypeMirror defaultAction(TypeMirror e, Void p) { return e; } @Override public TypeMirror visitTypeVariable(TypeVariable t, Void p) { Element element = typeUtils.asElement(t); if (element instanceof TypeParameterElement) { TypeParameterElement e = (TypeParameterElement) element; if (typeBindings.containsKey(e)) { return visit(typeBindings.get(e)); } } // We erase the upper bound to avoid infinite recursion. We can get away with erasure for // the reasons described above. return visit(typeUtils.erasure(t.getUpperBound())); } @Override public TypeMirror visitDeclared(DeclaredType t, Void p) { if (t.getTypeArguments().isEmpty()) { return t; } List newArgs = Lists.newArrayList(); for (TypeMirror arg : t.getTypeArguments()) { newArgs.add(visit(arg)); } return typeUtils.getDeclaredType(asTypeElement(t), newArgs.toArray(new TypeMirror[0])); } @Override public TypeMirror visitArray(ArrayType t, Void p) { return typeUtils.getArrayType(visit(t.getComponentType())); } } /** * Returns the given method as it appears in the given type. This is the method itself, * or the nearest override in a superclass of the given type, or null if the method is not * found in the given type or any of its superclasses. */ ExecutableElement methodFromSuperclasses(TypeElement in, ExecutableElement method) { for (TypeElement t = in; t != null; t = superclass(t)) { ExecutableElement tMethod = methodInType(t, method); if (tMethod != null) { return tMethod; } } return null; } /** * Returns the given interface method as it appears in the given type. This is the method * itself, or the nearest override in a superinterface of the given type, or null if the method * is not found in the given type or any of its transitive superinterfaces. */ ExecutableElement methodFromSuperinterfaces(TypeElement in, ExecutableElement method) { TypeElement methodContainer = MoreElements.asType(method.getEnclosingElement()); Preconditions.checkArgument(methodContainer.getKind().isInterface()); TypeMirror methodContainerType = typeUtils.erasure(methodContainer.asType()); ImmutableList types = ImmutableList.of(in); // On the first pass through this loop, `types` is the type we're starting from, // which might be a class or an interface. On later passes it is a list of direct // superinterfaces we saw in the previous pass, but only the ones that were assignable // to the interface that `method` appears in. while (!types.isEmpty()) { ImmutableList.Builder newTypes = ImmutableList.builder(); for (TypeElement t : types) { TypeMirror candidateType = typeUtils.erasure(t.asType()); if (typeUtils.isAssignable(candidateType, methodContainerType)) { ExecutableElement tMethod = methodInType(t, method); if (tMethod != null) { return tMethod; } newTypes.addAll(superinterfaces(t)); } if (t.getKind().isClass()) { TypeElement sup = superclass(t); if (sup != null) { newTypes.add(sup); } } } types = newTypes.build(); } return null; } /** * Returns the method from within the given type that has the same erased signature as the given * method, or null if there is no such method. */ private ExecutableElement methodInType(TypeElement type, ExecutableElement method) { int nParams = method.getParameters().size(); List params = erasedParameterTypes(method, type); if (params == null) { return null; } methods: for (ExecutableElement tMethod : ElementFilter.methodsIn(type.getEnclosedElements())) { if (tMethod.getSimpleName().equals(method.getSimpleName()) && tMethod.getParameters().size() == nParams) { for (int i = 0; i < nParams; i++) { TypeMirror tParamType = typeUtils.erasure(tMethod.getParameters().get(i).asType()); if (!typeUtils.isSameType(params.get(i), tParamType)) { continue methods; } } return tMethod; } } return null; } private TypeElement superclass(TypeElement type) { TypeMirror sup = type.getSuperclass(); if (sup.getKind() == TypeKind.DECLARED) { return MoreElements.asType(typeUtils.asElement(sup)); } else { return null; } } private ImmutableList superinterfaces(TypeElement type) { ImmutableList.Builder types = ImmutableList.builder(); for (TypeMirror sup : type.getInterfaces()) { types.add(MoreElements.asType(typeUtils.asElement(sup))); } return types.build(); } private TypeElement asTypeElement(TypeMirror typeMirror) { DeclaredType declaredType = MoreTypes.asDeclared(typeMirror); Element element = declaredType.asElement(); return MoreElements.asType(element); } } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/SimpleAnnotationMirror.java000066400000000000000000000120501365703632600331700ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.common; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static javax.lang.model.util.ElementFilter.methodsIn; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; /** * A simple implementation of the {@link AnnotationMirror} interface. * *

This type implements {@link #equals(Object)} and {@link #hashCode()} using {@link * AnnotationMirrors#equivalence} in accordance with the {@link AnnotationMirror} spec. Some {@link * AnnotationMirror}s, however, do not correctly implement equals, you should always compare them * using {@link AnnotationMirrors#equivalence} anyway. */ public final class SimpleAnnotationMirror implements AnnotationMirror { private final TypeElement annotationType; private final ImmutableMap namedValues; private final ImmutableMap elementValues; private SimpleAnnotationMirror( TypeElement annotationType, Map namedValues) { checkArgument( annotationType.getKind().equals(ElementKind.ANNOTATION_TYPE), "annotationType must be an annotation: %s", annotationType); Map values = new LinkedHashMap<>(); Map unusedValues = new LinkedHashMap<>(namedValues); List missingMembers = new ArrayList<>(); for (ExecutableElement method : methodsIn(annotationType.getEnclosedElements())) { String memberName = method.getSimpleName().toString(); if (unusedValues.containsKey(memberName)) { values.put(memberName, unusedValues.remove(memberName)); } else if (method.getDefaultValue() != null) { values.put(memberName, method.getDefaultValue()); } else { missingMembers.add(memberName); } } checkArgument( unusedValues.isEmpty(), "namedValues has entries for members that are not in %s: %s", annotationType, unusedValues); checkArgument( missingMembers.isEmpty(), "namedValues is missing entries for: %s", missingMembers); this.annotationType = annotationType; this.namedValues = ImmutableMap.copyOf(namedValues); this.elementValues = methodsIn(annotationType.getEnclosedElements()) .stream() .collect(toImmutableMap(e -> e, e -> values.get(e.getSimpleName().toString()))); } /** * An object representing an {@linkplain ElementKind#ANNOTATION_TYPE annotation} instance. If * {@code annotationType} has any annotation members, they must have default values. */ public static AnnotationMirror of(TypeElement annotationType) { return of(annotationType, ImmutableMap.of()); } /** * An object representing an {@linkplain ElementKind#ANNOTATION_TYPE annotation} instance. If * {@code annotationType} has any annotation members, they must either be present in {@code * namedValues} or have default values. */ public static AnnotationMirror of( TypeElement annotationType, Map namedValues) { return new SimpleAnnotationMirror(annotationType, namedValues); } @Override public DeclaredType getAnnotationType() { return MoreTypes.asDeclared(annotationType.asType()); } @Override public Map getElementValues() { return elementValues; } @Override public String toString() { StringBuilder builder = new StringBuilder("@").append(annotationType.getQualifiedName()); if (!namedValues.isEmpty()) { builder .append('(') .append(Joiner.on(", ").withKeyValueSeparator(" = ").join(namedValues)) .append(')'); } return builder.toString(); } @Override public boolean equals(Object other) { return other instanceof AnnotationMirror && AnnotationMirrors.equivalence().equivalent(this, (AnnotationMirror) other); } @Override public int hashCode() { return AnnotationMirrors.equivalence().hash(this); } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/SimpleTypeAnnotationValue.java000066400000000000000000000044641365703632600336460ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.common; import static com.google.common.base.Preconditions.checkArgument; import static javax.lang.model.type.TypeKind.ARRAY; import static javax.lang.model.type.TypeKind.DECLARED; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; import javax.lang.model.type.TypeMirror; /** * A simple implementation of the {@link AnnotationValue} interface for a class literal, e.g. an * annotation member of type {@code Class} or {@code Class}. */ public final class SimpleTypeAnnotationValue implements AnnotationValue { private final TypeMirror value; private SimpleTypeAnnotationValue(TypeMirror value) { checkArgument( value.getKind().isPrimitive() || value.getKind().equals(DECLARED) || value.getKind().equals(ARRAY), "value must be a primitive, array, or declared type, but was %s (%s)", value.getKind(), value); if (value.getKind().equals(DECLARED)) { checkArgument( MoreTypes.asDeclared(value).getTypeArguments().isEmpty(), "value must not be a parameterized type: %s", value); } this.value = value; } /** * An object representing an annotation value instance. * * @param value a primitive, array, or non-parameterized declared type */ public static AnnotationValue of(TypeMirror value) { return new SimpleTypeAnnotationValue(value); } @Override public TypeMirror getValue() { return value; } @Override public String toString() { return value + ".class"; } @Override public R accept(AnnotationValueVisitor visitor, P parameter) { return visitor.visitType(getValue(), parameter); } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/SuperficialValidation.java000066400000000000000000000245421365703632600330030ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import java.util.List; import java.util.Map; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; import javax.lang.model.element.Element; import javax.lang.model.element.ElementVisitor; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ErrorType; import javax.lang.model.type.ExecutableType; 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.AbstractElementVisitor8; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.SimpleTypeVisitor8; /** * A utility class that traverses {@link Element} instances and ensures that all type information * is present and resolvable. * * @author Gregory Kick */ public final class SuperficialValidation { public static boolean validateElements(Iterable elements) { for (Element element : elements) { if (!validateElement(element)) { return false; } } return true; } private static final ElementVisitor ELEMENT_VALIDATING_VISITOR = new AbstractElementVisitor8() { @Override public Boolean visitPackage(PackageElement e, Void p) { // don't validate enclosed elements because it will return types in the package return validateAnnotations(e.getAnnotationMirrors()); } @Override public Boolean visitType(TypeElement e, Void p) { return isValidBaseElement(e) && validateElements(e.getTypeParameters()) && validateTypes(e.getInterfaces()) && validateType(e.getSuperclass()); } @Override public Boolean visitVariable(VariableElement e, Void p) { return isValidBaseElement(e); } @Override public Boolean visitExecutable(ExecutableElement e, Void p) { AnnotationValue defaultValue = e.getDefaultValue(); return isValidBaseElement(e) && (defaultValue == null || validateAnnotationValue(defaultValue, e.getReturnType())) && validateType(e.getReturnType()) && validateTypes(e.getThrownTypes()) && validateElements(e.getTypeParameters()) && validateElements(e.getParameters()); } @Override public Boolean visitTypeParameter(TypeParameterElement e, Void p) { return isValidBaseElement(e) && validateTypes(e.getBounds()); } @Override public Boolean visitUnknown(Element e, Void p) { // just assume that unknown elements are OK return true; } }; public static boolean validateElement(Element element) { return element.accept(ELEMENT_VALIDATING_VISITOR, null); } private static boolean isValidBaseElement(Element e) { return validateType(e.asType()) && validateAnnotations(e.getAnnotationMirrors()) && validateElements(e.getEnclosedElements()); } private static boolean validateTypes(Iterable types) { for (TypeMirror type : types) { if (!validateType(type)) { return false; } } return true; } /* * This visitor does not test type variables specifically, but it seems that that is not actually * an issue. Javac turns the whole type parameter into an error type if it can't figure out the * bounds. */ private static final TypeVisitor TYPE_VALIDATING_VISITOR = new SimpleTypeVisitor8() { @Override protected Boolean defaultAction(TypeMirror t, Void p) { return true; } @Override public Boolean visitArray(ArrayType t, Void p) { return validateType(t.getComponentType()); } @Override public Boolean visitDeclared(DeclaredType t, Void p) { return validateTypes(t.getTypeArguments()); } @Override public Boolean visitError(ErrorType t, Void p) { return false; } @Override public Boolean visitUnknown(TypeMirror t, Void p) { // just make the default choice for unknown types return defaultAction(t, p); } @Override public Boolean visitWildcard(WildcardType t, Void p) { TypeMirror extendsBound = t.getExtendsBound(); TypeMirror superBound = t.getSuperBound(); return (extendsBound == null || validateType(extendsBound)) && (superBound == null || validateType(superBound)); } @Override public Boolean visitExecutable(ExecutableType t, Void p) { return validateTypes(t.getParameterTypes()) && validateType(t.getReturnType()) && validateTypes(t.getThrownTypes()) && validateTypes(t.getTypeVariables()); } }; private static boolean validateType(TypeMirror type) { return type.accept(TYPE_VALIDATING_VISITOR, null); } private static boolean validateAnnotations( Iterable annotationMirrors) { for (AnnotationMirror annotationMirror : annotationMirrors) { if (!validateAnnotation(annotationMirror)) { return false; } } return true; } private static boolean validateAnnotation(AnnotationMirror annotationMirror) { return validateType(annotationMirror.getAnnotationType()) && validateAnnotationValues(annotationMirror.getElementValues()); } @SuppressWarnings("unused") private static boolean validateAnnotationValues( Map valueMap) { for (Map.Entry valueEntry : valueMap.entrySet()) { TypeMirror expectedType = valueEntry.getKey().getReturnType(); if (!validateAnnotationValue(valueEntry.getValue(), expectedType)) { return false; } } return true; } private static final AnnotationValueVisitor VALUE_VALIDATING_VISITOR = new SimpleAnnotationValueVisitor8() { @Override protected Boolean defaultAction(Object o, TypeMirror expectedType) { return MoreTypes.isTypeOf(o.getClass(), expectedType); } @Override public Boolean visitUnknown(AnnotationValue av, TypeMirror expectedType) { // just take the default action for the unknown return defaultAction(av, expectedType); } @Override public Boolean visitAnnotation(AnnotationMirror a, TypeMirror expectedType) { return MoreTypes.equivalence().equivalent(a.getAnnotationType(), expectedType) && validateAnnotation(a); } @Override public Boolean visitArray(List values, TypeMirror expectedType) { if (!expectedType.getKind().equals(TypeKind.ARRAY)) { return false; } try { expectedType = MoreTypes.asArray(expectedType).getComponentType(); } catch (IllegalArgumentException e) { return false; // Not an array expected, ergo invalid. } for (AnnotationValue value : values) { if (!value.accept(this, expectedType)) { return false; } } return true; } @Override public Boolean visitEnumConstant(VariableElement enumConstant, TypeMirror expectedType) { return MoreTypes.equivalence().equivalent(enumConstant.asType(), expectedType) && validateElement(enumConstant); } @Override public Boolean visitType(TypeMirror type, TypeMirror ignored) { // We could check assignability here, but would require a Types instance. Since this // isn't really the sort of thing that shows up in a bad AST from upstream compilation // we ignore the expected type and just validate the type. It might be wrong, but // it's valid. return validateType(type); } @Override public Boolean visitBoolean(boolean b, TypeMirror expectedType) { return MoreTypes.isTypeOf(Boolean.TYPE, expectedType); } @Override public Boolean visitByte(byte b, TypeMirror expectedType) { return MoreTypes.isTypeOf(Byte.TYPE, expectedType); } @Override public Boolean visitChar(char c, TypeMirror expectedType) { return MoreTypes.isTypeOf(Character.TYPE, expectedType); } @Override public Boolean visitDouble(double d, TypeMirror expectedType) { return MoreTypes.isTypeOf(Double.TYPE, expectedType); } @Override public Boolean visitFloat(float f, TypeMirror expectedType) { return MoreTypes.isTypeOf(Float.TYPE, expectedType); } @Override public Boolean visitInt(int i, TypeMirror expectedType) { return MoreTypes.isTypeOf(Integer.TYPE, expectedType); } @Override public Boolean visitLong(long l, TypeMirror expectedType) { return MoreTypes.isTypeOf(Long.TYPE, expectedType); } @Override public Boolean visitShort(short s, TypeMirror expectedType) { return MoreTypes.isTypeOf(Short.TYPE, expectedType); } }; private static boolean validateAnnotationValue( AnnotationValue annotationValue, TypeMirror expectedType) { return annotationValue.accept(VALUE_VALIDATING_VISITOR, expectedType); } } auto-auto-service-1.0-rc7/common/src/main/java/com/google/auto/common/Visibility.java000066400000000000000000000055741365703632600306550ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.base.Preconditions.checkNotNull; import static javax.lang.model.element.ElementKind.PACKAGE; import com.google.common.base.Enums; import com.google.common.collect.Ordering; import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Modifier; /** * Represents the visibility of a given {@link Element}: {@code public}, {@code protected}, * {@code private} or default/package-private. * *

The constants for this enum are ordered according by increasing visibility. * * @author Gregory Kick */ public enum Visibility { PRIVATE, DEFAULT, PROTECTED, PUBLIC; // TODO(ronshapiro): remove this and reference ElementKind.MODULE directly once we start building // with -source 9 private static final ElementKind MODULE = Enums.getIfPresent(ElementKind.class, "MODULE").orNull(); /** * Returns the visibility of the given {@link Element}. While package and module elements don't * technically have a visibility associated with them, this method returns {@link #PUBLIC} for * them. */ public static Visibility ofElement(Element element) { checkNotNull(element); // packages and module don't have modifiers, but they're obviously "public" if (element.getKind().equals(PACKAGE) || element.getKind().equals(MODULE)) { return PUBLIC; } Set modifiers = element.getModifiers(); if (modifiers.contains(Modifier.PRIVATE)) { return PRIVATE; } else if (modifiers.contains(Modifier.PROTECTED)) { return PROTECTED; } else if (modifiers.contains(Modifier.PUBLIC)) { return PUBLIC; } else { return DEFAULT; } } /** * Returns effective visibility of the given element meaning that it takes into account the * visibility of its enclosing elements. */ public static Visibility effectiveVisibilityOfElement(Element element) { checkNotNull(element); Visibility effectiveVisibility = PUBLIC; Element currentElement = element; while (currentElement != null) { effectiveVisibility = Ordering.natural().min(effectiveVisibility, ofElement(currentElement)); currentElement = currentElement.getEnclosingElement(); } return effectiveVisibility; } } auto-auto-service-1.0-rc7/common/src/test/000077500000000000000000000000001365703632600204705ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/000077500000000000000000000000001365703632600214115ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/000077500000000000000000000000001365703632600221675ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/000077500000000000000000000000001365703632600234435ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/000077500000000000000000000000001365703632600244135ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/000077500000000000000000000000001365703632600257035ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java000066400000000000000000000170051365703632600331010ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.auto.common.AnnotationMirrorsTest.SimpleEnum.BLAH; import static com.google.auto.common.AnnotationMirrorsTest.SimpleEnum.FOO; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; import static org.junit.Assert.fail; import com.google.common.testing.EquivalenceTester; import com.google.testing.compile.CompilationRule; import java.util.Map; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleAnnotationValueVisitor6; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests {@link AnnotationMirrors}. */ @RunWith(JUnit4.class) public class AnnotationMirrorsTest { @Rule public CompilationRule compilationRule = new CompilationRule(); private Elements elements; @Before public void setUp() { this.elements = compilationRule.getElements(); } @interface SimpleAnnotation {} @SimpleAnnotation class SimplyAnnotated {} @SimpleAnnotation class AlsoSimplyAnnotated {} enum SimpleEnum { BLAH, FOO } @interface Outer { SimpleEnum value(); } @Outer(BLAH) static class TestClassBlah {} @Outer(BLAH) static class TestClassBlah2 {} @Outer(FOO) static class TestClassFoo {} @interface DefaultingOuter { SimpleEnum value() default SimpleEnum.BLAH; } @DefaultingOuter class TestWithDefaultingOuterDefault {} @DefaultingOuter(BLAH) class TestWithDefaultingOuterBlah {} @DefaultingOuter(FOO) class TestWithDefaultingOuterFoo {} @interface AnnotatedOuter { DefaultingOuter value(); } @AnnotatedOuter(@DefaultingOuter) class TestDefaultNestedAnnotated {} @AnnotatedOuter(@DefaultingOuter(BLAH)) class TestBlahNestedAnnotated {} @AnnotatedOuter(@DefaultingOuter(FOO)) class TestFooNestedAnnotated {} @interface OuterWithValueArray { DefaultingOuter[] value() default {}; } @OuterWithValueArray class TestValueArrayWithDefault {} @OuterWithValueArray({}) class TestValueArrayWithEmpty {} @OuterWithValueArray({@DefaultingOuter}) class TestValueArrayWithOneDefault {} @OuterWithValueArray(@DefaultingOuter(BLAH)) class TestValueArrayWithOneBlah {} @OuterWithValueArray(@DefaultingOuter(FOO)) class TestValueArrayWithOneFoo {} @OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter}) class TestValueArrayWithFooAndDefaultBlah {} @OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter(BLAH)}) class TestValueArrayWithFooBlah {} @OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter(BLAH)}) class TestValueArrayWithFooBlah2 {} // Different instances than on TestValueArrayWithFooBlah. @OuterWithValueArray({@DefaultingOuter(BLAH), @DefaultingOuter(FOO)}) class TestValueArrayWithBlahFoo {} @Test public void testEquivalences() { EquivalenceTester tester = EquivalenceTester.of(AnnotationMirrors.equivalence()); tester.addEquivalenceGroup( annotationOn(SimplyAnnotated.class), annotationOn(AlsoSimplyAnnotated.class)); tester.addEquivalenceGroup( annotationOn(TestClassBlah.class), annotationOn(TestClassBlah2.class)); tester.addEquivalenceGroup( annotationOn(TestClassFoo.class)); tester.addEquivalenceGroup( annotationOn(TestWithDefaultingOuterDefault.class), annotationOn(TestWithDefaultingOuterBlah.class)); tester.addEquivalenceGroup( annotationOn(TestWithDefaultingOuterFoo.class)); tester.addEquivalenceGroup( annotationOn(TestDefaultNestedAnnotated.class), annotationOn(TestBlahNestedAnnotated.class)); tester.addEquivalenceGroup( annotationOn(TestFooNestedAnnotated.class)); tester.addEquivalenceGroup( annotationOn(TestValueArrayWithDefault.class), annotationOn(TestValueArrayWithEmpty.class)); tester.addEquivalenceGroup( annotationOn(TestValueArrayWithOneDefault.class), annotationOn(TestValueArrayWithOneBlah.class)); tester.addEquivalenceGroup( annotationOn(TestValueArrayWithOneFoo.class)); tester.addEquivalenceGroup( annotationOn(TestValueArrayWithFooAndDefaultBlah.class), annotationOn(TestValueArrayWithFooBlah.class), annotationOn(TestValueArrayWithFooBlah2.class)); tester.addEquivalenceGroup( annotationOn(TestValueArrayWithBlahFoo.class)); tester.test(); } @interface Stringy { String value() default "default"; } @Stringy class StringyUnset {} @Stringy("foo") class StringySet {} @Test public void testGetDefaultValuesUnset() { assertThat(annotationOn(StringyUnset.class).getElementValues()).isEmpty(); Iterable values = AnnotationMirrors.getAnnotationValuesWithDefaults( annotationOn(StringyUnset.class)).values(); String value = getOnlyElement(values).accept(new SimpleAnnotationValueVisitor6() { @Override public String visitString(String value, Void ignored) { return value; } }, null); assertThat(value).isEqualTo("default"); } @Test public void testGetDefaultValuesSet() { Iterable values = AnnotationMirrors.getAnnotationValuesWithDefaults( annotationOn(StringySet.class)).values(); String value = getOnlyElement(values).accept(new SimpleAnnotationValueVisitor6() { @Override public String visitString(String value, Void ignored) { return value; } }, null); assertThat(value).isEqualTo("foo"); } @Test public void testGetValueEntry() { Map.Entry elementValue = AnnotationMirrors.getAnnotationElementAndValue( annotationOn(TestClassBlah.class), "value"); assertThat(elementValue.getKey().getSimpleName().toString()).isEqualTo("value"); assertThat(elementValue.getValue().getValue()).isInstanceOf(VariableElement.class); AnnotationValue value = AnnotationMirrors.getAnnotationValue( annotationOn(TestClassBlah.class), "value"); assertThat(value.getValue()).isInstanceOf(VariableElement.class); } @Test public void testGetValueEntryFailure() { try { AnnotationMirrors.getAnnotationValue(annotationOn(TestClassBlah.class), "a"); } catch (IllegalArgumentException e) { assertThat(e) .hasMessageThat() .isEqualTo( "@com.google.auto.common.AnnotationMirrorsTest.Outer does not define an element a()"); return; } fail("Should have thrown."); } private AnnotationMirror annotationOn(Class clazz) { return getOnlyElement(elements.getTypeElement(clazz.getCanonicalName()).getAnnotationMirrors()); } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/AnnotationValuesTest.java000066400000000000000000000272151365703632600327070ustar00rootroot00000000000000/* * Copyright 2019 Google LLC * * 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.google.auto.common; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; import com.google.common.truth.Correspondence; import com.google.testing.compile.CompilationRule; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; 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 org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests {@link AnnotationValues}. */ @RunWith(JUnit4.class) public final class AnnotationValuesTest { private @interface InsideAnnotation { int value(); } private static class GenericClass {} private static class InsideClassA {} private static class InsideClassB {} private @interface MultiValueAnnotation { Class classValue(); Class[] classValues(); Class genericClassValue(); InsideAnnotation insideAnnotationValue(); InsideAnnotation[] insideAnnotationValues(); String stringValue(); String[] stringValues(); Foo enumValue(); Foo[] enumValues(); int intValue(); int[] intValues(); long longValue(); long[] longValues(); byte byteValue(); byte[] byteValues(); short shortValue(); short[] shortValues(); float floatValue(); float[] floatValues(); double doubleValue(); double[] doubleValues(); boolean booleanValue(); boolean[] booleanValues(); char charValue(); char[] charValues(); } private enum Foo { BAR, BAZ, BAH; } @MultiValueAnnotation( classValue = InsideClassA.class, classValues = {InsideClassA.class, InsideClassB.class}, genericClassValue = GenericClass.class, insideAnnotationValue = @InsideAnnotation(19), insideAnnotationValues = {@InsideAnnotation(20), @InsideAnnotation(21)}, stringValue = "hello", stringValues = {"it's", "me"}, enumValue = Foo.BAR, enumValues = {Foo.BAZ, Foo.BAH}, intValue = 5, intValues = {1, 2}, longValue = 6L, longValues = {3L, 4L}, byteValue = (byte) 7, byteValues = {(byte) 8, (byte) 9}, shortValue = (short) 10, shortValues = {(short) 11, (short) 12}, floatValue = 13F, floatValues = {14F, 15F}, doubleValue = 16D, doubleValues = {17D, 18D}, booleanValue = true, booleanValues = {true, false}, charValue = 'a', charValues = {'b', 'c'}) private static class AnnotatedClass {} @Rule public final CompilationRule compilation = new CompilationRule(); private Elements elements; private Types types; private AnnotationMirror annotationMirror; @Before public void setUp() { elements = compilation.getElements(); types = compilation.getTypes(); TypeElement annotatedClass = getTypeElement(AnnotatedClass.class); annotationMirror = MoreElements.getAnnotationMirror(annotatedClass, MultiValueAnnotation.class).get(); } @Test public void getTypeMirror() { TypeElement insideClassA = getTypeElement(InsideClassA.class); AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "classValue"); assertThat(AnnotationValues.getTypeMirror(value).asElement()).isEqualTo(insideClassA); } @Test public void getTypeMirrorGenericClass() { TypeElement genericClass = getTypeElement(GenericClass.class); AnnotationValue gvalue = AnnotationMirrors.getAnnotationValue(annotationMirror, "genericClassValue"); assertThat(AnnotationValues.getTypeMirror(gvalue).asElement()).isEqualTo(genericClass); } @Test public void getTypeMirrors() { TypeMirror insideClassA = getTypeElement(InsideClassA.class).asType(); TypeMirror insideClassB = getTypeElement(InsideClassB.class).asType(); AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "classValues"); ImmutableList valueElements = AnnotationValues.getTypeMirrors(value); assertThat(valueElements) .comparingElementsUsing(Correspondence.from(types::isSameType, "has Same Type")) .containsExactly(insideClassA, insideClassB) .inOrder(); } @Test public void getAnnotationMirror() { TypeElement insideAnnotation = getTypeElement(InsideAnnotation.class); AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "insideAnnotationValue"); AnnotationMirror annotationMirror = AnnotationValues.getAnnotationMirror(value); assertThat(annotationMirror.getAnnotationType().asElement()).isEqualTo(insideAnnotation); assertThat(AnnotationMirrors.getAnnotationValue(annotationMirror, "value").getValue()) .isEqualTo(19); } @Test public void getAnnotationMirrors() { TypeElement insideAnnotation = getTypeElement(InsideAnnotation.class); AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "insideAnnotationValues"); ImmutableList annotationMirrors = AnnotationValues.getAnnotationMirrors(value); ImmutableList valueElements = annotationMirrors.stream() .map(AnnotationMirror::getAnnotationType) .map(DeclaredType::asElement) .collect(toImmutableList()); assertThat(valueElements).containsExactly(insideAnnotation, insideAnnotation); ImmutableList valuesStoredInAnnotation = annotationMirrors.stream() .map( annotationMirror -> AnnotationMirrors.getAnnotationValue(annotationMirror, "value").getValue()) .collect(toImmutableList()); assertThat(valuesStoredInAnnotation).containsExactly(20, 21).inOrder(); } @Test public void getString() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "stringValue"); assertThat(AnnotationValues.getString(value)).isEqualTo("hello"); } @Test public void getStrings() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "stringValues"); assertThat(AnnotationValues.getStrings(value)).containsExactly("it's", "me").inOrder(); } @Test public void getEnum() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "enumValue"); assertThat(AnnotationValues.getEnum(value)).isEqualTo(value.getValue()); } @Test public void getEnums() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "enumValues"); assertThat(getEnumNames(AnnotationValues.getEnums(value))) .containsExactly(Foo.BAZ.name(), Foo.BAH.name()) .inOrder(); } @Test public void getAnnotationValues() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "intValues"); ImmutableList values = AnnotationValues.getAnnotationValues(value); assertThat(values) .comparingElementsUsing(Correspondence.transforming(AnnotationValue::getValue, "has value")) .containsExactly(1, 2) .inOrder(); } @Test public void getInt() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "intValue"); assertThat(AnnotationValues.getInt(value)).isEqualTo(5); } @Test public void getInts() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "intValues"); assertThat(AnnotationValues.getInts(value)).containsExactly(1, 2).inOrder(); } @Test public void getLong() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "longValue"); assertThat(AnnotationValues.getLong(value)).isEqualTo(6L); } @Test public void getLongs() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "longValues"); assertThat(AnnotationValues.getLongs(value)).containsExactly(3L, 4L).inOrder(); } @Test public void getByte() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "byteValue"); assertThat(AnnotationValues.getByte(value)).isEqualTo((byte) 7); } @Test public void getBytes() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "byteValues"); assertThat(AnnotationValues.getBytes(value)).containsExactly((byte) 8, (byte) 9).inOrder(); } @Test public void getShort() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "shortValue"); assertThat(AnnotationValues.getShort(value)).isEqualTo((short) 10); } @Test public void getShorts() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "shortValues"); assertThat(AnnotationValues.getShorts(value)).containsExactly((short) 11, (short) 12).inOrder(); } @Test public void getFloat() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "floatValue"); assertThat(AnnotationValues.getFloat(value)).isEqualTo(13F); } @Test public void getFloats() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "floatValues"); assertThat(AnnotationValues.getFloats(value)).containsExactly(14F, 15F).inOrder(); } @Test public void getDouble() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "doubleValue"); assertThat(AnnotationValues.getDouble(value)).isEqualTo(16D); } @Test public void getDoubles() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "doubleValues"); assertThat(AnnotationValues.getDoubles(value)).containsExactly(17D, 18D).inOrder(); } @Test public void getBoolean() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "booleanValue"); assertThat(AnnotationValues.getBoolean(value)).isTrue(); } @Test public void getBooleans() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "booleanValues"); assertThat(AnnotationValues.getBooleans(value)).containsExactly(true, false).inOrder(); } @Test public void getChar() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "charValue"); assertThat(AnnotationValues.getChar(value)).isEqualTo('a'); } @Test public void getChars() { AnnotationValue value = AnnotationMirrors.getAnnotationValue(annotationMirror, "charValues"); assertThat(AnnotationValues.getChars(value)).containsExactly('b', 'c').inOrder(); } private TypeElement getTypeElement(Class clazz) { return elements.getTypeElement(clazz.getCanonicalName()); } private static ImmutableList getEnumNames(ImmutableList values) { return values.stream() .map(VariableElement::getSimpleName) .map(Name::toString) .collect(toImmutableList()); } } BasicAnnotationProcessorTest.java000066400000000000000000000354061365703632600343130ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.collect.Multimaps.transformValues; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertThat; import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; import static javax.tools.Diagnostic.Kind.ERROR; import static javax.tools.StandardLocation.SOURCE_OUTPUT; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.SetMultimap; import com.google.common.truth.Correspondence; import com.google.testing.compile.JavaFileObjects; import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Set; import javax.annotation.processing.Filer; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class BasicAnnotationProcessorTest { @Retention(RetentionPolicy.SOURCE) public @interface RequiresGeneratedCode {} /** * Rejects elements unless the class generated by {@link GeneratesCode}'s processor is present. */ private static final class RequiresGeneratedCodeProcessor extends BasicAnnotationProcessor { int rejectedRounds; final ImmutableList.Builder, Element>> processArguments = ImmutableList.builder(); @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override protected Iterable initSteps() { return ImmutableSet.of( new ProcessingStep() { @Override public Set process( SetMultimap, Element> elementsByAnnotation) { processArguments.add(ImmutableSetMultimap.copyOf(elementsByAnnotation)); TypeElement requiredClass = processingEnv.getElementUtils().getTypeElement("test.SomeGeneratedClass"); if (requiredClass == null) { rejectedRounds++; return ImmutableSet.copyOf(elementsByAnnotation.values()); } generateClass(processingEnv.getFiler(), "GeneratedByRequiresGeneratedCodeProcessor"); return ImmutableSet.of(); } @Override public Set> annotations() { return ImmutableSet.of(RequiresGeneratedCode.class); } }, new ProcessingStep() { @Override public Set> annotations() { return ImmutableSet.of(AnAnnotation.class); } @Override public Set process( SetMultimap, Element> elementsByAnnotation) { return ImmutableSet.of(); } }); } ImmutableList, Element>> processArguments() { return processArguments.build(); } } @Retention(RetentionPolicy.SOURCE) public @interface GeneratesCode {} /** Generates a class called {@code test.SomeGeneratedClass}. */ public class GeneratesCodeProcessor extends BasicAnnotationProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override protected Iterable initSteps() { return ImmutableSet.of( new ProcessingStep() { @Override public Set process( SetMultimap, Element> elementsByAnnotation) { generateClass(processingEnv.getFiler(), "SomeGeneratedClass"); return ImmutableSet.of(); } @Override public Set> annotations() { return ImmutableSet.of(GeneratesCode.class); } }); } } public @interface AnAnnotation {} /** When annotating a type {@code Foo}, generates a class called {@code FooXYZ}. */ public class AnAnnotationProcessor extends BasicAnnotationProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override protected Iterable initSteps() { return ImmutableSet.of( new ProcessingStep() { @Override public Set process( SetMultimap, Element> elementsByAnnotation) { for (Element element : elementsByAnnotation.values()) { generateClass(processingEnv.getFiler(), element.getSimpleName() + "XYZ"); } return ImmutableSet.of(); } @Override public Set> annotations() { return ImmutableSet.of(AnAnnotation.class); } }); } } /** An annotation which causes an annotation processing error. */ public @interface CauseError {} /** Report an error for any class annotated. */ public static class CauseErrorProcessor extends BasicAnnotationProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override protected Iterable initSteps() { return ImmutableSet.of( new ProcessingStep() { @Override public Set process( SetMultimap, Element> elementsByAnnotation) { for (Element e : elementsByAnnotation.values()) { processingEnv.getMessager().printMessage(ERROR, "purposeful error", e); } return ImmutableSet.copyOf(elementsByAnnotation.values()); } @Override public Set> annotations() { return ImmutableSet.of(CauseError.class); } }); } } @Test public void properlyDefersProcessing_typeElement() { JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", "package test;", "", "@" + RequiresGeneratedCode.class.getCanonicalName(), "public class ClassA {", " SomeGeneratedClass sgc;", "}"); JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", "package test;", "", "@" + GeneratesCode.class.getCanonicalName(), "public class ClassB {}"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject, classBFileObject)) .processedWith(requiresGeneratedCodeProcessor, new GeneratesCodeProcessor()) .compilesWithoutError() .and() .generatesFileNamed( SOURCE_OUTPUT, "test", "GeneratedByRequiresGeneratedCodeProcessor.java"); assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(0); } @Test public void properlyDefersProcessing_nestedTypeValidBeforeOuterType() { JavaFileObject source = JavaFileObjects.forSourceLines( "test.ValidInRound2", "package test;", "", "@" + AnAnnotation.class.getCanonicalName(), "public class ValidInRound2 {", " ValidInRound1XYZ vir1xyz;", " @" + AnAnnotation.class.getCanonicalName(), " static class ValidInRound1 {}", "}"); assertAbout(javaSource()) .that(source) .processedWith(new AnAnnotationProcessor()) .compilesWithoutError() .and() .generatesFileNamed(SOURCE_OUTPUT, "test", "ValidInRound2XYZ.java"); } @Retention(RetentionPolicy.SOURCE) public @interface ReferencesAClass { Class value(); } @Test public void properlyDefersProcessing_packageElement() { JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", "package test;", "", "@" + GeneratesCode.class.getCanonicalName(), "public class ClassA {", "}"); JavaFileObject packageFileObject = JavaFileObjects.forSourceLines("test.package-info", "@" + RequiresGeneratedCode.class.getCanonicalName(), "@" + ReferencesAClass.class.getCanonicalName() + "(SomeGeneratedClass.class)", "package test;"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject, packageFileObject)) .processedWith(requiresGeneratedCodeProcessor, new GeneratesCodeProcessor()) .compilesWithoutError() .and() .generatesFileNamed( SOURCE_OUTPUT, "test", "GeneratedByRequiresGeneratedCodeProcessor.java"); assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(0); } @Test public void properlyDefersProcessing_argumentElement() { JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", "package test;", "", "public class ClassA {", " SomeGeneratedClass sgc;", " public void myMethod(@" + RequiresGeneratedCode.class.getCanonicalName() + " int myInt)", " {}", "}"); JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", "package test;", "", "public class ClassB {", " public void myMethod(@" + GeneratesCode.class.getCanonicalName() + " int myInt) {}", "}"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject, classBFileObject)) .processedWith(requiresGeneratedCodeProcessor, new GeneratesCodeProcessor()) .compilesWithoutError() .and() .generatesFileNamed( SOURCE_OUTPUT, "test", "GeneratedByRequiresGeneratedCodeProcessor.java"); assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(0); } @Test public void properlyDefersProcessing_rejectsElement() { JavaFileObject classAFileObject = JavaFileObjects.forSourceLines( "test.ClassA", "package test;", "", "@" + RequiresGeneratedCode.class.getCanonicalName(), "public class ClassA {", " @" + AnAnnotation.class.getCanonicalName(), " public void method() {}", "}"); JavaFileObject classBFileObject = JavaFileObjects.forSourceLines("test.ClassB", "package test;", "", "@" + GeneratesCode.class.getCanonicalName(), "public class ClassB {}"); RequiresGeneratedCodeProcessor requiresGeneratedCodeProcessor = new RequiresGeneratedCodeProcessor(); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject, classBFileObject)) .processedWith(requiresGeneratedCodeProcessor, new GeneratesCodeProcessor()) .compilesWithoutError() .and() .generatesFileNamed( SOURCE_OUTPUT, "test", "GeneratedByRequiresGeneratedCodeProcessor.java"); assertThat(requiresGeneratedCodeProcessor.rejectedRounds).isEqualTo(1); // Re b/118372780: Assert that the right deferred elements are passed back, and not any enclosed // elements annotated with annotations from a different step. assertThat(requiresGeneratedCodeProcessor.processArguments()) .comparingElementsUsing(setMultimapValuesByString()) .containsExactly( ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA"), ImmutableSetMultimap.of(RequiresGeneratedCode.class, "test.ClassA")) .inOrder(); } private static Correspondence, SetMultimap> setMultimapValuesByString() { return Correspondence.from( (actual, expected) -> ImmutableSetMultimap.copyOf(transformValues(actual, Object::toString)).equals(expected), "is equivalent comparing multimap values by `toString()` to"); } @Test public void reportsMissingType() { JavaFileObject classAFileObject = JavaFileObjects.forSourceLines("test.ClassA", "package test;", "", "@" + RequiresGeneratedCode.class.getCanonicalName(), "public class ClassA {", " SomeGeneratedClass bar;", "}"); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject)) .processedWith(new RequiresGeneratedCodeProcessor()) .failsToCompile() .withErrorContaining(RequiresGeneratedCodeProcessor.class.getCanonicalName()) .in(classAFileObject).onLine(4); } @Test public void reportsMissingTypeSuppressedWhenOtherErrors() { JavaFileObject classAFileObject = JavaFileObjects.forSourceLines( "test.ClassA", "package test;", "", "@" + CauseError.class.getCanonicalName(), "public class ClassA {}"); assertAbout(javaSources()) .that(ImmutableList.of(classAFileObject)) .processedWith(new CauseErrorProcessor()) .failsToCompile() .withErrorCount(1) .withErrorContaining("purposeful"); } private static void generateClass(Filer filer, String generatedClassName) { PrintWriter writer = null; try { writer = new PrintWriter(filer.createSourceFile("test." + generatedClassName).openWriter()); writer.println("package test;"); writer.println("public class " + generatedClassName + " {}"); } catch (IOException e) { throw new RuntimeException(e); } finally { if (writer != null) { writer.close(); } } } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/GeneratedAnnotationsTest.java000066400000000000000000000213401365703632600335220ustar00rootroot00000000000000/* * Copyright 2018 Google LLC * * 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.google.auto.common; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assume.assumeTrue; import com.google.common.collect.ImmutableList; import com.google.common.reflect.Reflection; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; import java.io.File; import java.io.IOException; import java.io.UncheckedIOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.net.URI; import java.nio.file.Files; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaFileObject.Kind; import javax.tools.SimpleJavaFileObject; 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; /** {@link GeneratedAnnotations}Test */ @RunWith(JUnit4.class) public class GeneratedAnnotationsTest { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); private static final String JAVAX_ANNOTATION_PROCESSING_GENERATED = "javax.annotation.processing.Generated"; private static final String JAVAX_ANNOTATION_GENERATED = "javax.annotation.Generated"; /** * A toy annotation processor for testing. Matches on all annotations, and unconditionally * generates a source that uses {@code @Generated}. */ @SupportedAnnotationTypes("*") public static class TestProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } private boolean first = true; @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (first) { TypeSpec.Builder type = TypeSpec.classBuilder("G"); GeneratedAnnotationSpecs.generatedAnnotationSpec( processingEnv.getElementUtils(), processingEnv.getSourceVersion(), TestProcessor.class) .ifPresent(type::addAnnotation); JavaFile javaFile = JavaFile.builder("", type.build()).build(); try { javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { throw new UncheckedIOException(e); } first = false; } return false; } } /** * Run {@link TestProcessor} in a compilation with the given {@code options}, and prevent the * compilation from accessing classes with the qualified names in {@code maskFromClasspath}. */ private String runProcessor(ImmutableList options, String packageToMask) throws IOException { File tempDir = temporaryFolder.newFolder(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(/* diagnostics= */ null, /* locale= */ null, UTF_8); standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, ImmutableList.of(tempDir)); StandardJavaFileManager proxyFileManager = Reflection.newProxy( StandardJavaFileManager.class, new FileManagerInvocationHandler(standardFileManager, packageToMask)); CompilationTask task = compiler.getTask( /* out= */ null, proxyFileManager, /* diagnosticListener= */ null, options, /* classes= */ null, ImmutableList.of( new SimpleJavaFileObject(URI.create("test"), Kind.SOURCE) { @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return "class Test {}"; } })); task.setProcessors(ImmutableList.of(new TestProcessor())); assertThat(task.call()).isTrue(); return new String(Files.readAllBytes(tempDir.toPath().resolve("G.java")), UTF_8); } /** * Used to produce a {@link StandardJavaFileManager} where a certain package appears to have * no classes. The results are exactly those from the proxied {@code StandardJavaFileManager} * except for the {@link StandardJavaFileManager#list list} method when its {@code packageName} * argument is the given package, in which case the result is an empty list. * *

We can't use {@link javax.tools.ForwardingJavaFileManager} because at least some JDK * versions require the file manager to be a {@code StandardJavaFileManager} when the * {@code --release} flag is given. */ private static class FileManagerInvocationHandler implements InvocationHandler { private final StandardJavaFileManager fileManager; private final String packageToMask; FileManagerInvocationHandler(StandardJavaFileManager fileManager, String packageToMask) { this.fileManager = fileManager; this.packageToMask = packageToMask; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("list")) { String packageName = (String) args[1]; if (packageName.equals(packageToMask)) { return ImmutableList.of(); } } return method.invoke(fileManager, args); } } @Test public void source8() throws Exception { // Post-JDK8, we need to use --release 8 in order to be able to see the javax.annotation // package, which was deleted from the JDK in that release. On JDK8, there is no --release // option, so we have to use -source 8 -target 8. ImmutableList options = isJdk9OrLater() ? ImmutableList.of("--release", "8") : ImmutableList.of("-source", "8", "-target", "8"); String generated = runProcessor(options, null); assertThat(generated).contains(JAVAX_ANNOTATION_GENERATED); assertThat(generated).doesNotContain(JAVAX_ANNOTATION_PROCESSING_GENERATED); } @Test public void source8_masked() throws Exception { // It appears that the StandardJavaFileManager hack that removes a package does not work in // conjunction with --release. This is probably a bug. What we find is that // Elements.getTypeElement returns a value for javax.annotation.Generated even though // javax.annotation is being masked. It doesn't seem to go through the StandardJavaFileManager // interface to get it. So, we continue using the -source 8 -target 8 options. Those don't // actually get the JDK8 API when running post-JDK8, so we end up testing what we want. // // An alternative would be to delete this test method. JDK8 always has // javax.annotation.Generated so it isn't really meaningful to test it without. ImmutableList options = ImmutableList.of("-source", "8", "-target", "8"); String generated = runProcessor(options, "javax.annotation"); assertThat(generated).doesNotContain(JAVAX_ANNOTATION_GENERATED); assertThat(generated).doesNotContain(JAVAX_ANNOTATION_PROCESSING_GENERATED); } @Test public void source9() throws Exception { assumeTrue(isJdk9OrLater()); String generated = runProcessor(ImmutableList.of("-source", "9", "-target", "9"), null); assertThat(generated).doesNotContain(JAVAX_ANNOTATION_GENERATED); assertThat(generated).contains(JAVAX_ANNOTATION_PROCESSING_GENERATED); } @Test public void source9_masked() throws Exception { assumeTrue(isJdk9OrLater()); String generated = runProcessor( ImmutableList.of("-source", "9", "-target", "9"), "javax.annotation.processing"); assertThat(generated).doesNotContain(JAVAX_ANNOTATION_GENERATED); assertThat(generated).doesNotContain(JAVAX_ANNOTATION_PROCESSING_GENERATED); } private static boolean isJdk9OrLater() { return SourceVersion.latestSupported().compareTo(SourceVersion.RELEASE_8) > 0; } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/MoreElementsTest.java000066400000000000000000000431521365703632600320120ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.truth.Expect; import com.google.testing.compile.CompilationRule; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.AbstractList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; 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 org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class MoreElementsTest { @Rule public CompilationRule compilation = new CompilationRule(); @Rule public Expect expect = Expect.create(); private PackageElement javaLangPackageElement; private TypeElement objectElement; private TypeElement stringElement; @Before public void initializeTestElements() { Elements elements = compilation.getElements(); this.javaLangPackageElement = elements.getPackageElement("java.lang"); this.objectElement = elements.getTypeElement(Object.class.getCanonicalName()); this.stringElement = elements.getTypeElement(String.class.getCanonicalName()); } @Test public void getPackage() { assertThat(MoreElements.getPackage(stringElement)).isEqualTo(javaLangPackageElement); for (Element childElement : stringElement.getEnclosedElements()) { assertThat(MoreElements.getPackage(childElement)).isEqualTo(javaLangPackageElement); } } @Test public void asPackage() { assertThat(MoreElements.asPackage(javaLangPackageElement)) .isEqualTo(javaLangPackageElement); } @Test public void asPackage_illegalArgument() { try { MoreElements.asPackage(stringElement); fail(); } catch (IllegalArgumentException expected) {} } @Test public void asTypeElement() { Element typeElement = compilation.getElements().getTypeElement(String.class.getCanonicalName()); assertTrue(MoreElements.isType(typeElement)); assertThat(MoreElements.asType(typeElement)).isEqualTo(typeElement); } @Test public void asTypeElement_notATypeElement() { TypeElement typeElement = compilation.getElements().getTypeElement(String.class.getCanonicalName()); for (ExecutableElement e : ElementFilter.methodsIn(typeElement.getEnclosedElements())) { assertFalse(MoreElements.isType(e)); try { MoreElements.asType(e); fail(); } catch (IllegalArgumentException expected) { } } } @Test public void asTypeParameterElement() { Element typeParameterElement = getOnlyElement( compilation .getElements() .getTypeElement(List.class.getCanonicalName()) .getTypeParameters()); assertThat(MoreElements.asTypeParameter(typeParameterElement)).isEqualTo(typeParameterElement); } @Test public void asTypeParameterElement_illegalArgument() { try { MoreElements.asTypeParameter(javaLangPackageElement); fail(); } catch (IllegalArgumentException expected) { } } @Test public void asType() { assertThat(MoreElements.asType(stringElement)).isEqualTo(stringElement); } @Test public void asType_illegalArgument() { assertFalse(MoreElements.isType(javaLangPackageElement)); try { MoreElements.asType(javaLangPackageElement); fail(); } catch (IllegalArgumentException expected) {} } @Test public void asVariable() { for (Element variableElement : ElementFilter.fieldsIn(stringElement.getEnclosedElements())) { assertThat(MoreElements.asVariable(variableElement)).isEqualTo(variableElement); } } @Test public void asVariable_illegalArgument() { try { MoreElements.asVariable(javaLangPackageElement); fail(); } catch (IllegalArgumentException expected) {} } @Test public void asExecutable() { for (Element methodElement : ElementFilter.methodsIn(stringElement.getEnclosedElements())) { assertThat(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement); } for (Element methodElement : ElementFilter.constructorsIn(stringElement.getEnclosedElements())) { assertThat(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement); } } @Test public void asExecutable_illegalArgument() { try { MoreElements.asExecutable(javaLangPackageElement); fail(); } catch (IllegalArgumentException expected) {} } @Retention(RetentionPolicy.RUNTIME) private @interface InnerAnnotation {} @Documented @InnerAnnotation private @interface AnnotatedAnnotation {} @Test public void isAnnotationPresent() { TypeElement annotatedAnnotationElement = compilation.getElements().getTypeElement(AnnotatedAnnotation.class.getCanonicalName()); assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, Documented.class)) .isTrue(); assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, InnerAnnotation.class)) .isTrue(); assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, SuppressWarnings.class)) .isFalse(); } @Test public void getAnnotationMirror() { TypeElement element = compilation.getElements().getTypeElement(AnnotatedAnnotation.class.getCanonicalName()); Optional documented = MoreElements.getAnnotationMirror(element, Documented.class); Optional innerAnnotation = MoreElements.getAnnotationMirror(element, InnerAnnotation.class); Optional suppressWarnings = MoreElements.getAnnotationMirror(element, SuppressWarnings.class); expect.that(documented).isPresent(); expect.that(innerAnnotation).isPresent(); expect.that(suppressWarnings).isAbsent(); Element annotationElement = documented.get().getAnnotationType().asElement(); expect.that(MoreElements.isType(annotationElement)).isTrue(); expect.that(MoreElements.asType(annotationElement).getQualifiedName().toString()) .isEqualTo(Documented.class.getCanonicalName()); annotationElement = innerAnnotation.get().getAnnotationType().asElement(); expect.that(MoreElements.isType(annotationElement)).isTrue(); expect.that(MoreElements.asType(annotationElement).getQualifiedName().toString()) .isEqualTo(InnerAnnotation.class.getCanonicalName()); } private abstract static class ParentClass { static void staticMethod() {} abstract String foo(); private void privateMethod() {} } private interface ParentInterface { static void staticMethod() {} abstract int bar(); abstract int bar(long x); } private abstract static class Child extends ParentClass implements ParentInterface { static void staticMethod() {} @Override public int bar() { return 0; } abstract void baz(); void buh(int x) {} void buh(int x, int y) {} } @Test public void getLocalAndInheritedMethods_Old() { Elements elements = compilation.getElements(); Types types = compilation.getTypes(); TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT); TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG); TypeElement childType = elements.getTypeElement(Child.class.getCanonicalName()); @SuppressWarnings("deprecation") Set childTypeMethods = MoreElements.getLocalAndInheritedMethods(childType, elements); Set objectMethods = visibleMethodsFromObject(); assertThat(childTypeMethods).containsAtLeastElementsIn(objectMethods); Set nonObjectMethods = Sets.difference(childTypeMethods, objectMethods); assertThat(nonObjectMethods).containsExactly( getMethod(ParentInterface.class, "bar", longMirror), getMethod(ParentClass.class, "foo"), getMethod(Child.class, "bar"), getMethod(Child.class, "baz"), getMethod(Child.class, "buh", intMirror), getMethod(Child.class, "buh", intMirror, intMirror)) .inOrder();; } @Test public void getLocalAndInheritedMethods() { Elements elements = compilation.getElements(); Types types = compilation.getTypes(); TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT); TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG); TypeElement childType = elements.getTypeElement(Child.class.getCanonicalName()); @SuppressWarnings("deprecation") Set childTypeMethods = MoreElements.getLocalAndInheritedMethods(childType, types, elements); Set objectMethods = visibleMethodsFromObject(); assertThat(childTypeMethods).containsAtLeastElementsIn(objectMethods); Set nonObjectMethods = Sets.difference(childTypeMethods, objectMethods); assertThat(nonObjectMethods).containsExactly( getMethod(ParentInterface.class, "bar", longMirror), getMethod(ParentClass.class, "foo"), getMethod(Child.class, "bar"), getMethod(Child.class, "baz"), getMethod(Child.class, "buh", intMirror), getMethod(Child.class, "buh", intMirror, intMirror)) .inOrder(); } @Test public void getAllMethods() { Elements elements = compilation.getElements(); Types types = compilation.getTypes(); TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT); TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG); TypeElement childType = elements.getTypeElement(Child.class.getCanonicalName()); @SuppressWarnings("deprecation") Set childTypeMethods = MoreElements.getAllMethods(childType, types, elements); Set objectMethods = allMethodsFromObject(); assertThat(childTypeMethods).containsAtLeastElementsIn(objectMethods); Set nonObjectMethods = Sets.difference(childTypeMethods, objectMethods); assertThat(nonObjectMethods).containsExactly( getMethod(ParentInterface.class, "staticMethod"), getMethod(ParentInterface.class, "bar", longMirror), getMethod(ParentClass.class, "staticMethod"), getMethod(ParentClass.class, "foo"), getMethod(ParentClass.class, "privateMethod"), getMethod(Child.class, "staticMethod"), getMethod(Child.class, "bar"), getMethod(Child.class, "baz"), getMethod(Child.class, "buh", intMirror), getMethod(Child.class, "buh", intMirror, intMirror)) .inOrder(); } static class Injectable {} public static class MenuManager { public interface ParentComponent extends MenuItemA.ParentComponent, MenuItemB.ParentComponent {} } public static class MenuItemA { public interface ParentComponent { Injectable injectable(); } } public static class MenuItemB { public interface ParentComponent { Injectable injectable(); } } public static class Main { public interface ParentComponent extends MenuManager.ParentComponent {} } // Example from https://github.com/williamlian/daggerbug @Test public void getLocalAndInheritedMethods_DaggerBug() { Elements elementUtils = compilation.getElements(); TypeElement main = elementUtils.getTypeElement(Main.ParentComponent.class.getCanonicalName()); Set methods = MoreElements.getLocalAndInheritedMethods( main, compilation.getTypes(), elementUtils); assertThat(methods).hasSize(1); ExecutableElement method = methods.iterator().next(); assertThat(method.getSimpleName().toString()).isEqualTo("injectable"); assertThat(method.getParameters()).isEmpty(); } private Set visibleMethodsFromObject() { Types types = compilation.getTypes(); TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT); TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG); Set methods = new HashSet(); for (ExecutableElement method : ElementFilter.methodsIn(objectElement.getEnclosedElements())) { if (method.getModifiers().contains(Modifier.PUBLIC) || method.getModifiers().contains(Modifier.PROTECTED)) { methods.add(method); } } assertThat(methods) .containsAtLeast( getMethod(Object.class, "clone"), getMethod(Object.class, "finalize"), getMethod(Object.class, "wait"), getMethod(Object.class, "wait", longMirror), getMethod(Object.class, "wait", longMirror, intMirror)); return methods; } private Set allMethodsFromObject() { Types types = compilation.getTypes(); TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT); TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG); Set methods = new HashSet<>(); methods.addAll(ElementFilter.methodsIn(objectElement.getEnclosedElements())); assertThat(methods) .containsAtLeast( getMethod(Object.class, "clone"), getMethod(Object.class, "registerNatives"), getMethod(Object.class, "finalize"), getMethod(Object.class, "wait"), getMethod(Object.class, "wait", longMirror), getMethod(Object.class, "wait", longMirror, intMirror)); return methods; } private ExecutableElement getMethod(Class c, String methodName, TypeMirror... parameterTypes) { TypeElement type = compilation.getElements().getTypeElement(c.getCanonicalName()); Types types = compilation.getTypes(); ExecutableElement found = null; for (ExecutableElement method : ElementFilter.methodsIn(type.getEnclosedElements())) { if (method.getSimpleName().contentEquals(methodName) && method.getParameters().size() == parameterTypes.length) { boolean match = true; for (int i = 0; i < parameterTypes.length; i++) { TypeMirror expectedType = parameterTypes[i]; TypeMirror actualType = method.getParameters().get(i).asType(); match &= types.isSameType(expectedType, actualType); } if (match) { assertThat(found).isNull(); found = method; } } } assertWithMessage(methodName + Arrays.toString(parameterTypes)).that(found).isNotNull(); return found; } private abstract static class AbstractAbstractList extends AbstractList {} private static class ConcreteAbstractList extends AbstractList { @Override public int size() { return 0; } @Override public T get(int index) { throw new NoSuchElementException(); } } private Set abstractMethodNamesFrom(Set methods) { ImmutableSet.Builder abstractMethodNames = ImmutableSet.builder(); for (ExecutableElement method : methods) { if (method.getModifiers().contains(Modifier.ABSTRACT)) { abstractMethodNames.add(method.getSimpleName().toString()); } } return abstractMethodNames.build(); } // Test that getLocalAndInheritedMethods does the right thing with AbstractList. That class // inherits from Collection along two paths, via its parent AbstractCollection (which implements // Collection) and via its parent List (which extends Collection). Bugs in the past have meant // that the multiple paths might lead the code into thinking that all the abstract methods of // Collection were still abstract in the AbstractList subclasses here, even though most of them // are implemented in AbstractList. @Test public void getLocalAndInheritedMethods_AbstractList() { Elements elements = compilation.getElements(); TypeElement abstractType = elements.getTypeElement(AbstractAbstractList.class.getCanonicalName()); Set abstractTypeMethods = MoreElements.getLocalAndInheritedMethods(abstractType, elements); assertThat(abstractMethodNamesFrom(abstractTypeMethods)).containsExactly("get", "size"); TypeElement concreteType = elements.getTypeElement(ConcreteAbstractList.class.getCanonicalName()); Set concreteTypeMethods = MoreElements.getLocalAndInheritedMethods(concreteType, elements); assertThat(abstractMethodNamesFrom(concreteTypeMethods)).isEmpty(); } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java000066400000000000000000000156761365703632600327770ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.collect.Iterables; import com.google.testing.compile.CompilationRule; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests {@link MoreTypes#isTypeOf}. */ @RunWith(JUnit4.class) public class MoreTypesIsTypeOfTest { @Rule public CompilationRule compilationRule = new CompilationRule(); private Elements elements; @Before public void setUp() { this.elements = compilationRule.getElements(); } private interface TestType {} @Test public void isTypeOf_DeclaredType() { assertTrue(MoreTypes.isType(typeElementFor(TestType.class).asType())); assertWithMessage("mirror represents the TestType") .that(MoreTypes.isTypeOf(TestType.class, typeElementFor(TestType.class).asType())) .isTrue(); assertWithMessage("mirror does not represent a String") .that(MoreTypes.isTypeOf(String.class, typeElementFor(TestType.class).asType())) .isFalse(); } private interface ArrayType { String[] array(); } @Test public void isTypeOf_ArrayType() { assertTrue(MoreTypes.isType(typeElementFor(ArrayType.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(ArrayType.class)); assertWithMessage("array mirror represents an array Class object") .that(MoreTypes.isTypeOf(String[].class, type)) .isTrue(); } private interface PrimitiveBoolean { boolean method(); } @Test public void isTypeOf_PrimitiveBoolean() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveBoolean.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveBoolean.class)); assertWithMessage("mirror of a boolean").that(MoreTypes.isTypeOf(Boolean.TYPE, type)).isTrue(); } private interface PrimitiveByte { byte method(); } @Test public void isTypeOf_PrimitiveByte() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveByte.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveByte.class)); assertWithMessage("mirror of a byte").that(MoreTypes.isTypeOf(Byte.TYPE, type)).isTrue(); } private interface PrimitiveChar { char method(); } @Test public void isTypeOf_PrimitiveChar() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveChar.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveChar.class)); assertWithMessage("mirror of a char").that(MoreTypes.isTypeOf(Character.TYPE, type)).isTrue(); } private interface PrimitiveDouble { double method(); } @Test public void isTypeOf_PrimitiveDouble() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveDouble.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveDouble.class)); assertWithMessage("mirror of a double").that(MoreTypes.isTypeOf(Double.TYPE, type)).isTrue(); } private interface PrimitiveFloat { float method(); } @Test public void isTypeOf_PrimitiveFloat() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveFloat.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveFloat.class)); assertWithMessage("mirror of a float").that(MoreTypes.isTypeOf(Float.TYPE, type)).isTrue(); } private interface PrimitiveInt { int method(); } @Test public void isTypeOf_PrimitiveInt() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveInt.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveInt.class)); assertWithMessage("mirror of a int").that(MoreTypes.isTypeOf(Integer.TYPE, type)).isTrue(); } private interface PrimitiveLong { long method(); } @Test public void isTypeOf_PrimitiveLong() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveLong.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveLong.class)); assertWithMessage("mirror of a long").that(MoreTypes.isTypeOf(Long.TYPE, type)).isTrue(); } private interface PrimitiveShort { short method(); } @Test public void isTypeOf_PrimitiveShort() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveShort.class).asType())); TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveShort.class)); assertWithMessage("mirror of a short").that(MoreTypes.isTypeOf(Short.TYPE, type)).isTrue(); } private interface PrimitiveVoid { void method(); } @Test public void isTypeOf_void() { assertTrue(MoreTypes.isType(typeElementFor(PrimitiveVoid.class).asType())); TypeMirror primitive = extractReturnTypeFromHolder(typeElementFor(PrimitiveVoid.class)); assertWithMessage("mirror of a void").that(MoreTypes.isTypeOf(Void.TYPE, primitive)).isTrue(); } private interface DeclaredVoid { Void method(); } @Test public void isTypeOf_Void() { assertTrue(MoreTypes.isType(typeElementFor(DeclaredVoid.class).asType())); TypeMirror declared = extractReturnTypeFromHolder(typeElementFor(DeclaredVoid.class)); assertWithMessage("mirror of a void").that(MoreTypes.isTypeOf(Void.class, declared)).isTrue(); } @Test public void isTypeOf_fail() { assertFalse(MoreTypes.isType( getOnlyElement(typeElementFor(DeclaredVoid.class).getEnclosedElements()).asType())); TypeMirror method = getOnlyElement(typeElementFor(DeclaredVoid.class).getEnclosedElements()).asType(); try { MoreTypes.isTypeOf(String.class, method); fail(); } catch (IllegalArgumentException expected) {} } // Utility methods for this test. private TypeMirror extractReturnTypeFromHolder(TypeElement typeElement) { Element element = Iterables.getOnlyElement(typeElement.getEnclosedElements()); TypeMirror arrayType = MoreElements.asExecutable(element).getReturnType(); return arrayType; } private TypeElement typeElementFor(Class clazz) { return elements.getTypeElement(clazz.getCanonicalName()); } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/MoreTypesTest.java000066400000000000000000000537241365703632600313500ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.truth.Truth.assertThat; import static javax.lang.model.type.TypeKind.NONE; import static javax.lang.model.type.TypeKind.VOID; import static org.junit.Assert.fail; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.testing.EquivalenceTester; import com.google.common.truth.Expect; import com.google.testing.compile.CompilationRule; import java.lang.annotation.Annotation; import java.util.List; import java.util.Map; import java.util.Set; 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.PrimitiveType; 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.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class MoreTypesTest { @Rule public final CompilationRule compilationRule = new CompilationRule(); @Rule public final Expect expect = Expect.create(); @Test public void equivalence() { Types types = compilationRule.getTypes(); Elements elements = compilationRule.getElements(); TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType(); TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeElement mapElement = elements.getTypeElement(Map.class.getCanonicalName()); TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName()); TypeElement enumElement = elements.getTypeElement(Enum.class.getCanonicalName()); TypeElement container = elements.getTypeElement(Container.class.getCanonicalName()); TypeElement contained = elements.getTypeElement(Container.Contained.class.getCanonicalName()); TypeElement funkyBounds = elements.getTypeElement(FunkyBounds.class.getCanonicalName()); TypeElement funkyBounds2 = elements.getTypeElement(FunkyBounds2.class.getCanonicalName()); TypeElement funkierBounds = elements.getTypeElement(FunkierBounds.class.getCanonicalName()); TypeMirror funkyBoundsVar = ((DeclaredType) funkyBounds.asType()).getTypeArguments().get(0); TypeMirror funkyBounds2Var = ((DeclaredType) funkyBounds2.asType()).getTypeArguments().get(0); TypeMirror funkierBoundsVar = ((DeclaredType) funkierBounds.asType()).getTypeArguments().get(0); DeclaredType mapOfObjectToObjectType = types.getDeclaredType(mapElement, objectType, objectType); TypeMirror mapType = mapElement.asType(); DeclaredType setOfSetOfObject = types.getDeclaredType(setElement, types.getDeclaredType(setElement, objectType)); DeclaredType setOfSetOfString = types.getDeclaredType(setElement, types.getDeclaredType(setElement, stringType)); DeclaredType setOfSetOfSetOfObject = types.getDeclaredType(setElement, setOfSetOfObject); DeclaredType setOfSetOfSetOfString = types.getDeclaredType(setElement, setOfSetOfString); WildcardType wildcard = types.getWildcardType(null, null); DeclaredType containerOfObject = types.getDeclaredType(container, objectType); DeclaredType containerOfString = types.getDeclaredType(container, stringType); TypeMirror containedInObject = types.asMemberOf(containerOfObject, contained); TypeMirror containedInString = types.asMemberOf(containerOfString, contained); EquivalenceTester tester = EquivalenceTester.of(MoreTypes.equivalence()) .addEquivalenceGroup(types.getNullType()) .addEquivalenceGroup(types.getNoType(NONE)) .addEquivalenceGroup(types.getNoType(VOID)) .addEquivalenceGroup(objectType) .addEquivalenceGroup(stringType) .addEquivalenceGroup(containedInObject) .addEquivalenceGroup(containedInString) .addEquivalenceGroup(funkyBounds.asType()) .addEquivalenceGroup(funkyBounds2.asType()) .addEquivalenceGroup(funkierBounds.asType()) .addEquivalenceGroup(funkyBoundsVar, funkyBounds2Var) .addEquivalenceGroup(funkierBoundsVar) // Enum> .addEquivalenceGroup(enumElement.asType()) // Map .addEquivalenceGroup(mapType) .addEquivalenceGroup(mapOfObjectToObjectType) // Map .addEquivalenceGroup(types.getDeclaredType(mapElement, wildcard, wildcard)) // Map .addEquivalenceGroup(types.erasure(mapType), types.erasure(mapOfObjectToObjectType)) .addEquivalenceGroup(types.getDeclaredType(mapElement, objectType, stringType)) .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, objectType)) .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, stringType)) .addEquivalenceGroup(setOfSetOfObject) .addEquivalenceGroup(setOfSetOfString) .addEquivalenceGroup(setOfSetOfSetOfObject) .addEquivalenceGroup(setOfSetOfSetOfString) .addEquivalenceGroup(wildcard) // ? extends Object .addEquivalenceGroup(types.getWildcardType(objectType, null)) // ? extends String .addEquivalenceGroup(types.getWildcardType(stringType, null)) // ? super String .addEquivalenceGroup(types.getWildcardType(null, stringType)) // Map>> .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, types.getDeclaredType(mapElement, stringType, types.getDeclaredType(setElement, objectType)))) .addEquivalenceGroup(FAKE_ERROR_TYPE) ; for (TypeKind kind : TypeKind.values()) { if (kind.isPrimitive()) { PrimitiveType primitiveType = types.getPrimitiveType(kind); TypeMirror boxedPrimitiveType = types.boxedClass(primitiveType).asType(); tester.addEquivalenceGroup(primitiveType, types.unboxedType(boxedPrimitiveType)); tester.addEquivalenceGroup(boxedPrimitiveType); tester.addEquivalenceGroup(types.getArrayType(primitiveType)); tester.addEquivalenceGroup(types.getArrayType(boxedPrimitiveType)); } } ImmutableSet> testClasses = ImmutableSet.of( ExecutableElementsGroupA.class, ExecutableElementsGroupB.class, ExecutableElementsGroupC.class, ExecutableElementsGroupD.class, ExecutableElementsGroupE.class); for (Class testClass : testClasses) { ImmutableList equivalenceGroup = FluentIterable.from( elements.getTypeElement(testClass.getCanonicalName()).getEnclosedElements()) .transform(new Function() { @Override public TypeMirror apply(Element input) { return input.asType(); } }) .toList(); tester.addEquivalenceGroup(equivalenceGroup); } tester.test(); } @SuppressWarnings("unused") private static final class ExecutableElementsGroupA { ExecutableElementsGroupA() {} void a() {} public static void b() {} } @SuppressWarnings("unused") private static final class ExecutableElementsGroupB { ExecutableElementsGroupB(String s) {} void a(String s) {} public static void b(String s) {} } @SuppressWarnings("unused") private static final class ExecutableElementsGroupC { ExecutableElementsGroupC() throws Exception {} void a() throws Exception {} public static void b() throws Exception {} } @SuppressWarnings("unused") private static final class ExecutableElementsGroupD { ExecutableElementsGroupD() throws RuntimeException {} void a() throws RuntimeException {} public static void b() throws RuntimeException {} } @SuppressWarnings("unused") private static final class ExecutableElementsGroupE { ExecutableElementsGroupE() {} void a() {} public static void b() {} } @SuppressWarnings("unused") private static final class Container { private final class Contained {} } @SuppressWarnings("unused") private static final class FunkyBounds> {} @SuppressWarnings("unused") private static final class FunkyBounds2> {} @SuppressWarnings("unused") private static final class FunkierBounds & Cloneable> {} @Test public void testReferencedTypes() { Elements elements = compilationRule.getElements(); TypeElement testDataElement = elements .getTypeElement(ReferencedTypesTestData.class.getCanonicalName()); ImmutableMap fieldIndex = FluentIterable.from(ElementFilter.fieldsIn(testDataElement.getEnclosedElements())) .uniqueIndex(new Function() { @Override public String apply(VariableElement input) { return input.getSimpleName().toString(); } }); TypeElement objectElement = elements.getTypeElement(Object.class.getCanonicalName()); TypeElement stringElement = elements.getTypeElement(String.class.getCanonicalName()); TypeElement integerElement = elements.getTypeElement(Integer.class.getCanonicalName()); TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName()); TypeElement mapElement = elements.getTypeElement(Map.class.getCanonicalName()); TypeElement charSequenceElement = elements.getTypeElement(CharSequence.class.getCanonicalName()); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f1").asType())) .containsExactly(objectElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f2").asType())) .containsExactly(setElement, stringElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f3").asType())) .containsExactly(mapElement, stringElement, objectElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f4").asType())) .containsExactly(integerElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f5").asType())) .containsExactly(setElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f6").asType())) .containsExactly(setElement, charSequenceElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f7").asType())) .containsExactly(mapElement, stringElement, setElement, charSequenceElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f8").asType())) .containsExactly(stringElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f9").asType())) .containsExactly(stringElement); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f10").asType())).isEmpty(); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f11").asType())).isEmpty(); assertThat(MoreTypes.referencedTypes(fieldIndex.get("f12").asType())) .containsExactly(setElement, stringElement); } @SuppressWarnings("unused") // types used in compiler tests private static final class ReferencedTypesTestData { Object f1; Set f2; Map f3; Integer f4; Set f5; Set f6; Map> f7; String[] f8; String[][] f9; int f10; int[] f11; Set f12; } private static class Parent {} private static class ChildA extends Parent {} private static class ChildB extends Parent {} private static class GenericChild extends Parent {} private interface InterfaceType {} @Test public void asElement_throws() { TypeMirror javaDotLang = compilationRule.getElements().getPackageElement("java.lang").asType(); try { MoreTypes.asElement(javaDotLang); fail(); } catch (IllegalArgumentException expected) {} } @Test public void asElement() { Elements elements = compilationRule.getElements(); TypeElement stringElement = elements.getTypeElement("java.lang.String"); assertThat(MoreTypes.asElement(stringElement.asType())).isEqualTo(stringElement); TypeParameterElement setParameterElement = Iterables.getOnlyElement( compilationRule.getElements().getTypeElement("java.util.Set").getTypeParameters()); assertThat(MoreTypes.asElement(setParameterElement.asType())).isEqualTo(setParameterElement); // we don't test error types because those are very hard to get predictably } @Test public void testNonObjectSuperclass() { Types types = compilationRule.getTypes(); Elements elements = compilationRule.getElements(); TypeMirror numberType = elements.getTypeElement(Number.class.getCanonicalName()).asType(); TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeMirror integerType = elements.getTypeElement(Integer.class.getCanonicalName()).asType(); TypeElement parent = elements.getTypeElement(Parent.class.getCanonicalName()); TypeElement childA = elements.getTypeElement(ChildA.class.getCanonicalName()); TypeElement childB = elements.getTypeElement(ChildB.class.getCanonicalName()); TypeElement genericChild = elements.getTypeElement(GenericChild.class.getCanonicalName()); TypeMirror genericChildOfNumber = types.getDeclaredType(genericChild, numberType); TypeMirror genericChildOfInteger = types.getDeclaredType(genericChild, integerType); TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType(); TypeMirror interfaceType = elements.getTypeElement(InterfaceType.class.getCanonicalName()).asType(); assertThat(MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) objectType)) .isAbsent(); assertThat(MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) interfaceType)) .isAbsent(); assertThat(MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) parent.asType())) .isAbsent(); Optional parentOfChildA = MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) childA.asType()); Optional parentOfChildB = MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) childB.asType()); Optional parentOfGenericChild = MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) genericChild.asType()); Optional parentOfGenericChildOfNumber = MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) genericChildOfNumber); Optional parentOfGenericChildOfInteger = MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) genericChildOfInteger); EquivalenceTester tester = EquivalenceTester.of(MoreTypes.equivalence()) .addEquivalenceGroup(parentOfChildA.get(), types.getDeclaredType(parent, numberType), parentOfGenericChildOfNumber.get()) .addEquivalenceGroup(parentOfChildB.get(), types.getDeclaredType(parent, stringType)) .addEquivalenceGroup(parentOfGenericChild.get(), parent.asType()) .addEquivalenceGroup(parentOfGenericChildOfInteger.get(), types.getDeclaredType(parent, integerType)); tester.test(); } @Test public void testAsMemberOf_variableElement() { Types types = compilationRule.getTypes(); Elements elements = compilationRule.getElements(); TypeMirror numberType = elements.getTypeElement(Number.class.getCanonicalName()).asType(); TypeMirror stringType = elements.getTypeElement(String.class.getCanonicalName()).asType(); TypeMirror integerType = elements.getTypeElement(Integer.class.getCanonicalName()).asType(); TypeElement paramsElement = elements.getTypeElement(Params.class.getCanonicalName()); VariableElement tParam = Iterables.getOnlyElement(Iterables.getOnlyElement( ElementFilter.methodsIn(paramsElement.getEnclosedElements())).getParameters()); VariableElement tField = Iterables.getOnlyElement(ElementFilter.fieldsIn(paramsElement.getEnclosedElements())); DeclaredType numberParams = (DeclaredType) elements.getTypeElement(NumberParams.class.getCanonicalName()).asType(); DeclaredType stringParams = (DeclaredType) elements.getTypeElement(StringParams.class.getCanonicalName()).asType(); TypeElement genericParams = elements.getTypeElement(GenericParams.class.getCanonicalName()); DeclaredType genericParamsOfNumber = types.getDeclaredType(genericParams, numberType); DeclaredType genericParamsOfInteger = types.getDeclaredType(genericParams, integerType); TypeMirror fieldOfNumberParams = MoreTypes.asMemberOf(types, numberParams, tField); TypeMirror paramOfNumberParams = MoreTypes.asMemberOf(types, numberParams, tParam); TypeMirror fieldOfStringParams = MoreTypes.asMemberOf(types, stringParams, tField); TypeMirror paramOfStringParams = MoreTypes.asMemberOf(types, stringParams, tParam); TypeMirror fieldOfGenericOfNumber = MoreTypes.asMemberOf(types, genericParamsOfNumber, tField); TypeMirror paramOfGenericOfNumber = MoreTypes.asMemberOf(types, genericParamsOfNumber, tParam); TypeMirror fieldOfGenericOfInteger = MoreTypes.asMemberOf(types, genericParamsOfInteger, tField); TypeMirror paramOfGenericOfInteger = MoreTypes.asMemberOf(types, genericParamsOfInteger, tParam); EquivalenceTester tester = EquivalenceTester.of(MoreTypes.equivalence()) .addEquivalenceGroup(fieldOfNumberParams, paramOfNumberParams, fieldOfGenericOfNumber, paramOfGenericOfNumber, numberType) .addEquivalenceGroup(fieldOfStringParams, paramOfStringParams, stringType) .addEquivalenceGroup(fieldOfGenericOfInteger, paramOfGenericOfInteger, integerType); tester.test(); } private static class Params { @SuppressWarnings("unused") T t; @SuppressWarnings("unused") void add(T t) {} } private static class NumberParams extends Params {} private static class StringParams extends Params {} private static class GenericParams extends Params {} private static final ErrorType FAKE_ERROR_TYPE = new ErrorType() { @Override public TypeKind getKind() { return TypeKind.ERROR; } @Override public R accept(TypeVisitor v, P p) { return v.visitError(this, p); } @Override public List getTypeArguments() { return ImmutableList.of(); } @Override public TypeMirror getEnclosingType() { return null; } @Override public Element asElement() { return null; } // JDK8 Compatibility: public A[] getAnnotationsByType(Class annotationType) { return null; } public A getAnnotation(Class annotationType) { return null; } public List getAnnotationMirrors() { return null; } }; @Test public void testIsConversionFromObjectUnchecked_yes() { Elements elements = compilationRule.getElements(); TypeElement unchecked = elements.getTypeElement(Unchecked.class.getCanonicalName()); for (VariableElement field : ElementFilter.fieldsIn(unchecked.getEnclosedElements())) { TypeMirror type = field.asType(); expect .withMessage("Casting to %s is unchecked", type) .that(MoreTypes.isConversionFromObjectUnchecked(type)) .isTrue(); } } @Test public void testIsConversionFromObjectUnchecked_no() { Elements elements = compilationRule.getElements(); TypeElement notUnchecked = elements.getTypeElement(NotUnchecked.class.getCanonicalName()); for (VariableElement field : ElementFilter.fieldsIn(notUnchecked.getEnclosedElements())) { TypeMirror type = field.asType(); expect .withMessage("Casting to %s is not unchecked", type) .that(MoreTypes.isConversionFromObjectUnchecked(type)) .isFalse(); } } // The type of every field here is such that casting to it provokes an "unchecked" warning. @SuppressWarnings("unused") private static class Unchecked { private List listOfString; private List listOfExtendsCharSequence; private List listOfSuperCharSequence; private List listOfT; private List listOfArrayOfT; private T t; private T[] arrayOfT; private List[] arrayOfListOfT; private Map mapWildcardToString; private Map mapStringToWildcard; } // The type of every field here is such that casting to it doesn't provoke an "unchecked" warning. @SuppressWarnings("unused") private static class NotUnchecked { private String string; private int integer; private String[] arrayOfString; private int[] arrayOfInt; private Thread.State threadStateEnum; private List listOfWildcard; private List listOfWildcardExtendsObject; private Map mapWildcardToWildcard; } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/OverridesTest.java000066400000000000000000000553041365703632600313570ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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.google.auto.common; import static com.google.common.truth.Truth.assertThat; import static java.nio.charset.StandardCharsets.UTF_8; import static javax.lang.model.util.ElementFilter.methodsIn; import com.google.common.base.Converter; import com.google.common.base.Optional; import com.google.common.base.StandardSystemProperty; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Range; import com.google.common.io.Files; import com.google.common.truth.Expect; import com.google.testing.compile.CompilationRule; import java.io.File; import java.util.AbstractCollection; import java.util.AbstractList; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.TypeVisitor; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor6; import javax.lang.model.util.Types; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.model.Statement; /** * Tests that the {@link Overrides} class has behaviour consistent with javac. We test this in * two ways: once with {@link Overrides.ExplicitOverrides} using javac's own {@link Elements} and * {@link Types}, and once with it using the version of those objects from the Eclipse compiler * (ecj). * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(Parameterized.class) public class OverridesTest { @Parameterized.Parameters(name = "{0}") public static ImmutableList data() { return ImmutableList.of(CompilerType.JAVAC, CompilerType.ECJ); } @Rule public CompilationRule compilation = new CompilationRule(); @Rule public EcjCompilationRule ecjCompilation = new EcjCompilationRule(); @Rule public Expect expect = Expect.create(); public enum CompilerType { JAVAC { @Override void initUtils(OverridesTest test) { test.typeUtils = test.compilation.getTypes(); test.elementUtils = test.compilation.getElements(); } }, ECJ { @Override void initUtils(OverridesTest test) { test.typeUtils = test.ecjCompilation.types; test.elementUtils = test.ecjCompilation.elements; } }; abstract void initUtils(OverridesTest test); } private final CompilerType compilerType; private Types typeUtils; private Elements elementUtils; private Elements javacElementUtils; private Overrides javacOverrides; private Overrides.ExplicitOverrides explicitOverrides; public OverridesTest(CompilerType compilerType) { this.compilerType = compilerType; } @Before public void initializeTestElements() { javacElementUtils = compilation.getElements(); javacOverrides = new Overrides.NativeOverrides(javacElementUtils); compilerType.initUtils(this); explicitOverrides = new Overrides.ExplicitOverrides(typeUtils); } static class TypesForInheritance { interface One { void m(); void m(String x); void n(); } interface Two { void m(); void m(int x); } static class Parent { public void m() {} } static class ChildOfParent extends Parent {} static class ChildOfOne implements One { @Override public void m() {} @Override public void m(String x) {} @Override public void n() {} } static class ChildOfOneAndTwo implements One, Two { @Override public void m() {} @Override public void m(String x) {} @Override public void m(int x) {} @Override public void n() {} } static class ChildOfParentAndOne extends Parent implements One { @Override public void m() {} @Override public void m(String x) {} @Override public void n() {} } static class ChildOfParentAndOneAndTwo extends Parent implements One, Two { @Override public void m(String x) {} @Override public void m(int x) {} @Override public void n() {} } abstract static class AbstractChildOfOne implements One {} abstract static class AbstractChildOfOneAndTwo implements One, Two {} abstract static class AbstractChildOfParentAndOneAndTwo extends Parent implements One, Two {} } static class MoreTypesForInheritance { interface Key {} interface BindingType {} interface ContributionType {} interface HasKey { Key key(); } interface HasBindingType { BindingType bindingType(); } interface HasContributionType { ContributionType contributionType(); } abstract static class BindingDeclaration implements HasKey { abstract Optional bindingElement(); abstract Optional contributingModule(); } abstract static class MultibindingDeclaration extends BindingDeclaration implements HasBindingType, HasContributionType { @Override public abstract Key key(); @Override public abstract ContributionType contributionType(); @Override public abstract BindingType bindingType(); } } static class TypesForVisibility { public abstract static class PublicGrandparent { public abstract String foo(); } private static class PrivateParent extends PublicGrandparent { @Override public String foo() { return "foo"; } } static class Child extends PrivateParent {} } static class TypesForGenerics { interface XCollection { boolean add(E x); } interface XList extends XCollection { @Override public boolean add(E x); } static class StringList implements XList { @Override public boolean add(String x) { return false; } } } @SuppressWarnings("rawtypes") static class TypesForRaw { static class RawParent { void frob(List x) {} } static class RawChildOfRaw extends RawParent { @Override void frob(List x) {} } static class NonRawParent { void frob(List x) {} } static class RawChildOfNonRaw extends NonRawParent { @Override void frob(List x) {} } } @Test public void overridesInheritance() { checkOverridesInContainedClasses(TypesForInheritance.class); } @Test public void overridesMoreInheritance() { checkOverridesInContainedClasses(MoreTypesForInheritance.class); } @Test public void overridesVisibility() { checkOverridesInContainedClasses(TypesForVisibility.class); } @Test public void overridesGenerics() { checkOverridesInContainedClasses(TypesForGenerics.class); } @Test public void overridesRaw() { checkOverridesInContainedClasses(TypesForRaw.class); } // Test a tricky diamond inheritance hierarchy: // Collection // / \ // AbstractCollection List // \ / // AbstractList // This also tests that we do the right thing with generics, since naively the TypeMirror // that you get for List will not appear to be a subtype of the one you get for Collection // since the two Es are not the same. @Test public void overridesDiamond() { checkOverridesInSet(ImmutableSet.>of( Collection.class, List.class, AbstractCollection.class, AbstractList.class)); } private void checkOverridesInContainedClasses(Class container) { checkOverridesInSet(ImmutableSet.copyOf(container.getDeclaredClasses())); } private void checkOverridesInSet(ImmutableSet> testClasses) { assertThat(testClasses).isNotEmpty(); ImmutableSet.Builder testTypesBuilder = ImmutableSet.builder(); for (Class testClass : testClasses) { testTypesBuilder.add(getTypeElement(testClass)); } ImmutableSet testTypes = testTypesBuilder.build(); ImmutableSet.Builder testMethodsBuilder = ImmutableSet.builder(); for (TypeElement testType : testTypes) { testMethodsBuilder.addAll(methodsIn(testType.getEnclosedElements())); } ImmutableSet testMethods = testMethodsBuilder.build(); for (TypeElement in : testTypes) { TypeElement javacIn = javacType(in); List inMethods = methodsIn(elementUtils.getAllMembers(in)); for (ExecutableElement overrider : inMethods) { ExecutableElement javacOverrider = javacMethod(overrider); for (ExecutableElement overridden : testMethods) { ExecutableElement javacOverridden = javacMethod(overridden); boolean javacSays = javacOverrides.overrides(javacOverrider, javacOverridden, javacIn); boolean weSay = explicitOverrides.overrides(overrider, overridden, in); if (javacSays != weSay) { expect .withMessage( "%s.%s overrides %s.%s in %s: javac says %s, we say %s", overrider.getEnclosingElement(), overrider, overridden.getEnclosingElement(), overridden, in, javacSays, weSay) .fail(); } } } } } private TypeElement getTypeElement(Class c) { return elementUtils.getTypeElement(c.getCanonicalName()); } private ExecutableElement getMethod(TypeElement in, String name, TypeKind... parameterTypeKinds) { ExecutableElement found = null; methods: for (ExecutableElement method : methodsIn(in.getEnclosedElements())) { if (method.getSimpleName().contentEquals(name) && method.getParameters().size() == parameterTypeKinds.length) { for (int i = 0; i < parameterTypeKinds.length; i++) { if (method.getParameters().get(i).asType().getKind() != parameterTypeKinds[i]) { continue methods; } } assertThat(found).isNull(); found = method; } } assertThat(found).isNotNull(); return found; } // These skeletal parallels to the real collection classes ensure that the test is independent // of the details of those classes, for example whether List redeclares add(E) even though // it also inherits it from Collection. private interface XCollection { boolean add(E e); } private interface XList extends XCollection {} private abstract static class XAbstractCollection implements XCollection { @Override public boolean add(E e) { return false; } } private abstract static class XAbstractList extends XAbstractCollection implements XList { @Override public boolean add(E e) { return true; } } private abstract static class XStringList extends XAbstractList {} private abstract static class XAbstractStringList implements XList {} private abstract static class XNumberList extends XAbstractList {} // Parameter of add(E) in StringList is String. // That means that we successfully recorded E[AbstractList] = String and E[List] = E[AbstractList] // and String made it all the way through. @Test public void methodParameters_StringList() { TypeElement xAbstractList = getTypeElement(XAbstractList.class); TypeElement xStringList = getTypeElement(XStringList.class); TypeElement string = getTypeElement(String.class); ExecutableElement add = getMethod(xAbstractList, "add", TypeKind.TYPEVAR); List params = explicitOverrides.erasedParameterTypes(add, xStringList); List expectedParams = ImmutableList.of(string.asType()); assertTypeListsEqual(params, expectedParams); } // Parameter of add(E) in AbstractStringList is String. // That means that we successfully recorded E[List] = String and E[Collection] = E[List]. @Test public void methodParameters_AbstractStringList() { TypeElement xCollection = getTypeElement(XCollection.class); TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); TypeElement string = getTypeElement(String.class); ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); List params = explicitOverrides.erasedParameterTypes(add, xAbstractStringList); List expectedParams = ImmutableList.of(string.asType()); assertTypeListsEqual(params, expectedParams); } // Parameter of add(E) in NumberList is Number. // That means that we successfully recorded E[AbstractList] = Number and on from // there, with Number being used because it is the erasure of . @Test public void methodParams_NumberList() { TypeElement xCollection = getTypeElement(XCollection.class); TypeElement xNumberList = getTypeElement(XNumberList.class); TypeElement number = getTypeElement(Number.class); ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); List params = explicitOverrides.erasedParameterTypes(add, xNumberList); List expectedParams = ImmutableList.of(number.asType()); assertTypeListsEqual(params, expectedParams); } // This is derived from a class that provoked a StackOverflowError in an earlier version. private abstract static class StringToRangeConverter> extends Converter> { @Override protected String doBackward(Range b) { return null; } } @Test public void methodParams_RecursiveBound() { TypeElement stringToRangeConverter = getTypeElement(StringToRangeConverter.class); TypeElement range = getTypeElement(Range.class); ExecutableElement valueConverter = getMethod(stringToRangeConverter, "doBackward", TypeKind.DECLARED); List params = explicitOverrides.erasedParameterTypes(valueConverter, stringToRangeConverter); List expectedParams = ImmutableList.of(typeUtils.erasure(range.asType())); assertTypeListsEqual(params, expectedParams); } @Test public void methodFromSuperclasses() { TypeElement xAbstractCollection = getTypeElement(XAbstractCollection.class); TypeElement xAbstractList = getTypeElement(XAbstractList.class); TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); TypeElement xStringList = getTypeElement(XStringList.class); ExecutableElement add = getMethod(xAbstractCollection, "add", TypeKind.TYPEVAR); ExecutableElement addInAbstractStringList = explicitOverrides.methodFromSuperclasses(xAbstractStringList, add); assertThat(addInAbstractStringList).isNull(); ExecutableElement addInStringList = explicitOverrides.methodFromSuperclasses(xStringList, add); assertThat(addInStringList.getEnclosingElement()).isEqualTo(xAbstractList); } @Test public void methodFromSuperinterfaces() { TypeElement xCollection = getTypeElement(XCollection.class); TypeElement xAbstractList = getTypeElement(XAbstractList.class); TypeElement xAbstractStringList = getTypeElement(XAbstractStringList.class); TypeElement xNumberList = getTypeElement(XNumberList.class); TypeElement xList = getTypeElement(XList.class); ExecutableElement add = getMethod(xCollection, "add", TypeKind.TYPEVAR); ExecutableElement addInAbstractStringList = explicitOverrides.methodFromSuperinterfaces(xAbstractStringList, add); assertThat(addInAbstractStringList.getEnclosingElement()).isEqualTo(xCollection); ExecutableElement addInNumberList = explicitOverrides.methodFromSuperinterfaces(xNumberList, add); assertThat(addInNumberList.getEnclosingElement()).isEqualTo(xAbstractList); ExecutableElement addInList = explicitOverrides.methodFromSuperinterfaces(xList, add); assertThat(addInList.getEnclosingElement()).isEqualTo(xCollection); } private void assertTypeListsEqual(List actual, List expected) { assertThat(actual.size()).isEqualTo(expected.size()); for (int i = 0; i < actual.size(); i++) { assertThat(typeUtils.isSameType(actual.get(i), expected.get(i))).isTrue(); } } // TODO(emcmanus): replace this with something from compile-testing when that's available. /** * An equivalent to {@link CompilationRule} that uses ecj (the Eclipse compiler) instead of javac. * If the parameterized test is not selecting ecj then this rule has no effect. */ public class EcjCompilationRule implements TestRule { Elements elements; Types types; @Override public Statement apply(Statement base, Description description) { if (!compilerType.equals(CompilerType.ECJ)) { return base; } return new EcjCompilationStatement(base); } } private class EcjCompilationStatement extends Statement { private final Statement statement; EcjCompilationStatement(Statement base) { this.statement = base; } @Override public void evaluate() throws Throwable { File tmpDir = File.createTempFile("OverridesTest", "dir"); tmpDir.delete(); tmpDir.mkdir(); File dummySourceFile = new File(tmpDir, "Dummy.java"); try { Files.asCharSink(dummySourceFile, UTF_8).write("class Dummy {}"); evaluate(dummySourceFile); } finally { dummySourceFile.delete(); tmpDir.delete(); } } private void evaluate(File dummySourceFile) throws Throwable { JavaCompiler compiler = new EclipseCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, UTF_8); // This hack is only needed in a Google-internal Java 8 environment where symbolic links make // it hard for ecj to find the boot class path. Elsewhere it is unnecessary but harmless. File rtJar = new File(StandardSystemProperty.JAVA_HOME.value() + "/lib/rt.jar"); if (rtJar.exists()) { List bootClassPath = ImmutableList.builder() .add(rtJar) .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH)) .build(); fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath); } Iterable sources = fileManager.getJavaFileObjects(dummySourceFile); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, sources); EcjTestProcessor processor = new EcjTestProcessor(statement); task.setProcessors(ImmutableList.of(processor)); assertThat(task.call()).isTrue(); processor.maybeThrow(); } } @SupportedAnnotationTypes("*") private class EcjTestProcessor extends AbstractProcessor { private final Statement statement; private Throwable thrown; EcjTestProcessor(Statement statement) { this.statement = statement; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } @Override public boolean process( Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { ecjCompilation.elements = processingEnv.getElementUtils(); ecjCompilation.types = processingEnv.getTypeUtils(); try { statement.evaluate(); } catch (Throwable t) { thrown = t; } } return false; } void maybeThrow() throws Throwable { if (thrown != null) { throw thrown; } } } private TypeElement javacType(TypeElement type) { return javacElementUtils.getTypeElement(type.getQualifiedName().toString()); } private ExecutableElement javacMethod(ExecutableElement method) { if (elementUtils == javacElementUtils) { return method; } TypeElement containingType = MoreElements.asType(method.getEnclosingElement()); TypeElement javacContainingType = javacType(containingType); List candidates = new ArrayList(); methods: for (ExecutableElement javacMethod : methodsIn(javacContainingType.getEnclosedElements())) { if (javacMethod.getSimpleName().contentEquals(method.getSimpleName()) && javacMethod.getParameters().size() == method.getParameters().size()) { for (int i = 0; i < method.getParameters().size(); i++) { VariableElement parameter = method.getParameters().get(i); VariableElement javacParameter = javacMethod.getParameters().get(i); if (!erasedToString(parameter.asType()).equals(erasedToString(javacParameter.asType()))) { continue methods; } } candidates.add(javacMethod); } } if (candidates.size() == 1) { return candidates.get(0); } else { throw new IllegalStateException( "Expected one javac method matching " + method + " but found " + candidates); } } private static String erasedToString(TypeMirror type) { return ERASED_STRING_TYPE_VISITOR.visit(type); } private static final TypeVisitor ERASED_STRING_TYPE_VISITOR = new SimpleTypeVisitor6() { @Override protected String defaultAction(TypeMirror e, Void p) { return e.toString(); } @Override public String visitArray(ArrayType t, Void p) { return visit(t.getComponentType()) + "[]"; } @Override public String visitDeclared(DeclaredType t, Void p) { return MoreElements.asType(t.asElement()).getQualifiedName().toString(); } @Override public String visitTypeVariable(TypeVariable t, Void p) { return visit(t.getUpperBound()); } }; } SimpleAnnotationMirrorTest.java000066400000000000000000000106251365703632600340120ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/* * Copyright 2017 Google LLC * * 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.google.auto.common; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableMap; import com.google.testing.compile.CompilationRule; import java.util.HashMap; import java.util.Map; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; import javax.lang.model.element.TypeElement; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link SimpleAnnotationMirror}. */ @RunWith(JUnit4.class) public class SimpleAnnotationMirrorTest { @Rule public final CompilationRule compilation = new CompilationRule(); @interface EmptyAnnotation {} @interface AnnotationWithDefault { int value() default 3; } @interface MultipleValues { int value1(); int value2(); } @Test public void emptyAnnotation() { TypeElement emptyAnnotation = getTypeElement(EmptyAnnotation.class); AnnotationMirror simpleAnnotation = SimpleAnnotationMirror.of(emptyAnnotation); assertThat(simpleAnnotation.getAnnotationType()).isEqualTo(emptyAnnotation.asType()); assertThat(simpleAnnotation.getElementValues()).isEmpty(); } @Test public void multipleValues() { TypeElement multipleValues = getTypeElement(MultipleValues.class); Map values = new HashMap<>(); values.put("value1", intValue(1)); values.put("value2", intValue(2)); assertThat(SimpleAnnotationMirror.of(multipleValues, values).getElementValues()).hasSize(2); } @Test public void extraValues() { TypeElement multipleValues = getTypeElement(MultipleValues.class); Map values = new HashMap<>(); values.put("value1", intValue(1)); values.put("value2", intValue(2)); values.put("value3", intValue(3)); expectThrows(() -> SimpleAnnotationMirror.of(multipleValues, values)); } @Test public void defaultValue() { TypeElement withDefaults = getTypeElement(AnnotationWithDefault.class); AnnotationMirror annotation = SimpleAnnotationMirror.of(withDefaults); assertThat(annotation.getElementValues()).hasSize(1); assertThat(getOnlyElement(annotation.getElementValues().values()).getValue()).isEqualTo(3); } @Test public void overriddenDefaultValue() { TypeElement withDefaults = getTypeElement(AnnotationWithDefault.class); AnnotationMirror annotation = SimpleAnnotationMirror.of(withDefaults, ImmutableMap.of("value", intValue(4))); assertThat(annotation.getElementValues()).hasSize(1); assertThat(getOnlyElement(annotation.getElementValues().values()).getValue()).isEqualTo(4); } @Test public void missingValues() { TypeElement multipleValues = getTypeElement(MultipleValues.class); expectThrows(() -> SimpleAnnotationMirror.of(multipleValues)); } @Test public void notAnAnnotation() { TypeElement stringElement = getTypeElement(String.class); expectThrows(() -> SimpleAnnotationMirror.of(stringElement)); } private TypeElement getTypeElement(Class clazz) { return compilation.getElements().getTypeElement(clazz.getCanonicalName()); } private static void expectThrows(Runnable throwingRunnable) { try { throwingRunnable.run(); fail("Expected an IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } private static AnnotationValue intValue(int value) { return new AnnotationValue() { @Override public Object getValue() { return value; } @Override public R accept(AnnotationValueVisitor annotationValueVisitor, P p) { return annotationValueVisitor.visitInt(value, p); } }; } } SimpleTypeAnnotationValueTest.java000066400000000000000000000057061365703632600344620ustar00rootroot00000000000000auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/* * Copyright 2017 Google LLC * * 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.google.auto.common; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.testing.compile.CompilationRule; import java.util.List; import javax.lang.model.element.AnnotationValue; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.Types; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link SimpleTypeAnnotationValue}. */ @RunWith(JUnit4.class) public class SimpleTypeAnnotationValueTest { @Rule public final CompilationRule compilation = new CompilationRule(); private Types types; private Elements elements; private TypeMirror objectType; private PrimitiveType primitiveType; @Before public void setUp() { types = compilation.getTypes(); elements = compilation.getElements(); objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType(); primitiveType = types.getPrimitiveType(TypeKind.BOOLEAN); } @Test public void primitiveClass() { AnnotationValue annotationValue = SimpleTypeAnnotationValue.of(primitiveType); assertThat(annotationValue.getValue()).isEqualTo(primitiveType); } @Test public void arrays() { SimpleTypeAnnotationValue.of(types.getArrayType(objectType)); SimpleTypeAnnotationValue.of(types.getArrayType(primitiveType)); } @Test public void declaredType() { SimpleTypeAnnotationValue.of(objectType); } @Test public void visitorMethod() { SimpleTypeAnnotationValue.of(objectType).accept(new SimpleAnnotationValueVisitor8(){ @Override public Void visitType(TypeMirror typeMirror, Void aVoid) { // do nothing, expected case return null; } @Override protected Void defaultAction(Object o, Void aVoid) { throw new AssertionError(); } }, null); } @Test public void parameterizedType() { try { SimpleTypeAnnotationValue.of( types.getDeclaredType( elements.getTypeElement(List.class.getCanonicalName()), objectType)); fail("Expected an exception"); } catch (IllegalArgumentException expected) { } } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java000066400000000000000000000233321365703632600336720ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.common.truth.Truth.assertAbout; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; import com.google.common.collect.ImmutableSet; import com.google.testing.compile.JavaFileObjects; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.element.TypeElement; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class SuperficialValidationTest { @Test public void missingReturnType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "abstract class TestClass {", " abstract MissingType blah();", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void missingGenericReturnType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "abstract class TestClass {", " abstract MissingType blah();", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void missingReturnTypeTypeParameter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "import java.util.Map;", "import java.util.Set;", "", "abstract class TestClass {", " abstract Map, MissingType> blah();", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void missingTypeParameter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "class TestClass {}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void missingParameterType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "abstract class TestClass {", " abstract void foo(MissingType x);", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void missingAnnotation() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "@MissingAnnotation", "class TestClass {}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void handlesRecursiveTypeParams() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "class TestClass> {}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue(); } }) .compilesWithoutError(); } @Test public void handlesRecursiveType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "abstract class TestClass {", " abstract TestClass foo(TestClass x);", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue(); } }) .compilesWithoutError(); } @Test public void missingWildcardBound() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "import java.util.Set;", "", "class TestClass {", " Set extendsTest() {", " return null;", " }", "", " Set superTest() {", " return null;", " }", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void missingIntersection() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "test.TestClass", "package test;", "", "class TestClass {}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.TestClass"); assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); } }) .failsToCompile(); } @Test public void invalidAnnotationValue() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines("test.Outer", "package test;", "", "final class Outer {", " @interface TestAnnotation {", " Class[] classes();", " }", "", " @TestAnnotation(classes = Foo)", " static class TestClass {}", "}"); assertAbout(javaSource()) .that(javaFileObject) .processedWith( new AssertingProcessor() { @Override void runAssertions() { TypeElement testClassElement = processingEnv.getElementUtils().getTypeElement("test.Outer.TestClass"); assertWithMessage("testClassElement is valid") .that(SuperficialValidation.validateElement(testClassElement)) .isFalse(); } }) .failsToCompile(); } private abstract static class AssertingProcessor extends AbstractProcessor { @Override public Set getSupportedAnnotationTypes() { return ImmutableSet.of("*"); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { try { runAssertions(); } catch (Exception e) { throw new RuntimeException(e); } return false; } abstract void runAssertions() throws Exception; } } auto-auto-service-1.0-rc7/common/src/test/java/com/google/auto/common/VisibilityTest.java000066400000000000000000000146601365703632600315440ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.common; import static com.google.auto.common.Visibility.DEFAULT; import static com.google.auto.common.Visibility.PRIVATE; import static com.google.auto.common.Visibility.PROTECTED; import static com.google.auto.common.Visibility.PUBLIC; import static com.google.common.truth.Truth.assertThat; import com.google.testing.compile.CompilationRule; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.lang.model.element.Element; import javax.lang.model.util.Elements; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class VisibilityTest { @Rule public CompilationRule compilation = new CompilationRule(); @Test public void packageVisibility() { assertThat(Visibility.ofElement(compilation.getElements().getPackageElement("java.lang"))) .isEqualTo(PUBLIC); assertThat(Visibility.ofElement( compilation.getElements().getPackageElement("com.google.auto.common"))) .isEqualTo(PUBLIC); } @Test public void moduleVisibility() throws IllegalAccessException, InvocationTargetException { Method getModuleElement; try { getModuleElement = Elements.class.getMethod("getModuleElement", CharSequence.class); } catch (NoSuchMethodException e) { // TODO(ronshapiro): rewrite this test without reflection once we're on Java 9 return; } Element moduleElement = (Element) getModuleElement.invoke(compilation.getElements(), "java.base"); assertThat(Visibility.ofElement(moduleElement)).isEqualTo(PUBLIC); } @SuppressWarnings("unused") public static class PublicClass { public static class NestedPublicClass {} protected static class NestedProtectedClass {} static class NestedDefaultClass {} private static class NestedPrivateClass {} } @SuppressWarnings("unused") protected static class ProtectedClass { public static class NestedPublicClass {} protected static class NestedProtectedClass {} static class NestedDefaultClass {} private static class NestedPrivateClass {} } @SuppressWarnings("unused") static class DefaultClass { public static class NestedPublicClass {} protected static class NestedProtectedClass {} static class NestedDefaultClass {} private static class NestedPrivateClass {} } @SuppressWarnings("unused") private static class PrivateClass { public static class NestedPublicClass {} protected static class NestedProtectedClass {} static class NestedDefaultClass {} private static class NestedPrivateClass {} } @Test public void classVisibility() { assertThat(Visibility.ofElement(compilation.getElements().getTypeElement("java.util.Map"))) .isEqualTo(PUBLIC); assertThat(Visibility.ofElement( compilation.getElements().getTypeElement("java.util.Map.Entry"))) .isEqualTo(PUBLIC); assertThat(Visibility.ofElement( compilation.getElements().getTypeElement(PublicClass.class.getCanonicalName()))) .isEqualTo(PUBLIC); assertThat(Visibility.ofElement( compilation.getElements().getTypeElement(ProtectedClass.class.getCanonicalName()))) .isEqualTo(PROTECTED); assertThat(Visibility.ofElement( compilation.getElements().getTypeElement(DefaultClass.class.getCanonicalName()))) .isEqualTo(DEFAULT); assertThat(Visibility.ofElement( compilation.getElements().getTypeElement(PrivateClass.class.getCanonicalName()))) .isEqualTo(PRIVATE); } @Test public void effectiveClassVisibility() { assertThat(effectiveVisiblityOfClass(PublicClass.class)).isEqualTo(PUBLIC); assertThat(effectiveVisiblityOfClass(ProtectedClass.class)).isEqualTo(PROTECTED); assertThat(effectiveVisiblityOfClass(DefaultClass.class)).isEqualTo(DEFAULT); assertThat(effectiveVisiblityOfClass(PrivateClass.class)).isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(PublicClass.NestedPublicClass.class)) .isEqualTo(PUBLIC); assertThat(effectiveVisiblityOfClass(PublicClass.NestedProtectedClass.class)) .isEqualTo(PROTECTED); assertThat(effectiveVisiblityOfClass(PublicClass.NestedDefaultClass.class)) .isEqualTo(DEFAULT); assertThat(effectiveVisiblityOfClass(PublicClass.NestedPrivateClass.class)) .isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(ProtectedClass.NestedPublicClass.class)) .isEqualTo(PROTECTED); assertThat(effectiveVisiblityOfClass(ProtectedClass.NestedProtectedClass.class)) .isEqualTo(PROTECTED); assertThat(effectiveVisiblityOfClass(ProtectedClass.NestedDefaultClass.class)) .isEqualTo(DEFAULT); assertThat(effectiveVisiblityOfClass(ProtectedClass.NestedPrivateClass.class)) .isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(DefaultClass.NestedPublicClass.class)) .isEqualTo(DEFAULT); assertThat(effectiveVisiblityOfClass(DefaultClass.NestedProtectedClass.class)) .isEqualTo(DEFAULT); assertThat(effectiveVisiblityOfClass(DefaultClass.NestedDefaultClass.class)) .isEqualTo(DEFAULT); assertThat(effectiveVisiblityOfClass(DefaultClass.NestedPrivateClass.class)) .isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(PrivateClass.NestedPublicClass.class)) .isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(PrivateClass.NestedProtectedClass.class)) .isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(PrivateClass.NestedDefaultClass.class)) .isEqualTo(PRIVATE); assertThat(effectiveVisiblityOfClass(PrivateClass.NestedPrivateClass.class)) .isEqualTo(PRIVATE); } private Visibility effectiveVisiblityOfClass(Class clazz) { return Visibility.effectiveVisibilityOfElement( compilation.getElements().getTypeElement(clazz.getCanonicalName())); } } auto-auto-service-1.0-rc7/factory/000077500000000000000000000000001365703632600171015ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/README.md000066400000000000000000000056741365703632600203740ustar00rootroot00000000000000AutoFactory ====== A source code generator for JSR-330-compatible factories. AutoWhat‽ ------------- [Java][java] is full of [factories](http://en.wikipedia.org/wiki/Factory_method_pattern). They're mechanical, repetitive, typically untested and sometimes the source of subtle bugs. _Sounds like a job for robots!_ AutoFactory generates factories that can be used on their own or with [JSR-330](http://jcp.org/en/jsr/detail?id=330)-compatible [dependency injectors](http://en.wikipedia.org/wiki/Dependency_injection) from a simple annotation. Any combination of parameters can either be passed through factory methods or provided to the factory at construction time. They can implement interfaces or extend abstract classes. They're what you would have written, but without the bugs. Save time. Save code. Save sanity. Example ------- Say you have: ```java @AutoFactory final class SomeClass { private final String providedDepA; private final String depB; SomeClass(@Provided @AQualifier String providedDepA, String depB) { this.providedDepA = providedDepA; this.depB = depB; } // … } ``` AutoFactory will generate: ```java import javax.annotation.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated(value = "com.google.auto.factory.processor.AutoFactoryProcessor") final class SomeClassFactory { private final Provider providedDepAProvider; @Inject SomeClassFactory( @AQualifier Provider providedDepAProvider) { this.providedDepAProvider = providedDepAProvider; } SomeClass create(String depB) { return new SomeClass(providedDepAProvider.get(), depB); } } ``` > NOTE: AutoFactory only supports JSR-330 @Qualifier annotations. Older, > framework-specific annotations from Guice, Spring, etc are not > supported (though these all support JSR-330) Download -------- In order to activate code generation you will need to include `auto-factory-${version}.jar` in your build at compile time. In a Maven project, one would include the `auto-factory` artifact as an "optional" dependency: ```xml com.google.auto.factory auto-factory ${version} true ``` License ------- Copyright 2013 Google LLC 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. [java]: https://en.wikipedia.org/wiki/Java_(programming_language) auto-auto-service-1.0-rc7/factory/pom.xml000066400000000000000000000144271365703632600204260ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.google.auto.factory auto-factory HEAD-SNAPSHOT AutoFactory JSR-330-compatible factories. https://github.com/google/auto/tree/master/factory UTF-8 1.8 28.2-jre 1.0.1 http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD GitHub Issues http://github.com/google/auto/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt Google LLC http://www.google.com com.google.auto auto-common 0.10 com.google.auto.value auto-value-annotations 1.7 com.google.auto.value auto-value 1.7 provided com.google.auto.service auto-service 1.0-rc6 provided net.ltgt.gradle.incap incap 0.2 provided net.ltgt.gradle.incap incap-processor 0.2 provided com.google.googlejavaformat google-java-format 1.7 com.google.guava guava ${guava.version} com.squareup javapoet 1.12.1 javax.inject javax.inject 1 com.google.testing.compile compile-testing 0.18 test junit junit 4.13 test com.google.truth truth ${truth.version} test com.google.code.findbugs jsr305 3.0.2 test org.checkerframework checker-compat-qual 2.5.5 test maven-compiler-plugin 3.7.0 ${java.version} ${java.version} -Xlint:all true true org.codehaus.plexus plexus-java 0.9.4 maven-jar-plugin 3.0.2 maven-invoker-plugin 3.0.1 true ${project.build.directory}/it */pom.xml true integration-test install run org.immutables.tools maven-shade-plugin 4 auto-auto-service-1.0-rc7/factory/src/000077500000000000000000000000001365703632600176705ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/000077500000000000000000000000001365703632600203045ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/000077500000000000000000000000001365703632600224465ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/pom.xml000066400000000000000000000067211365703632600237710ustar00rootroot00000000000000 org.sonatype.oss oss-parent 7 4.0.0 com.google.auto.value.it.functional functional HEAD-SNAPSHOT Auto-Value Functional Integration Test com.google.auto.factory auto-factory @project.version@ com.google.code.findbugs jsr305 3.0.2 provided com.google.guava guava 27.0.1-jre com.google.inject guice 4.1.0 com.google.dagger dagger 2.13 com.google.dagger dagger-compiler 2.13 true junit junit 4.12 test com.google.truth truth 0.44 test org.apache.maven.plugins maven-jar-plugin 3.0.2 org.apache.maven.plugins maven-compiler-plugin 3.7.0 1.8 1.8 -Xlint:all true true org.codehaus.plexus plexus-java 0.9.4 auto-auto-service-1.0-rc7/factory/src/it/functional/src/000077500000000000000000000000001365703632600232355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/000077500000000000000000000000001365703632600241615ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/000077500000000000000000000000001365703632600251025ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/000077500000000000000000000000001365703632600256605ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/000077500000000000000000000000001365703632600271345ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/000077500000000000000000000000001365703632600301045ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/000077500000000000000000000000001365703632600315535ustar00rootroot00000000000000DaggerModule.java000066400000000000000000000021241365703632600346750ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import dagger.Module; import dagger.Provides; @Module final class DaggerModule { @Provides Dependency provideDependency(DependencyImpl impl) { return impl; } @Provides @Qualifier Dependency provideQualifiedDependency(QualifiedDependencyImpl impl) { return impl; } @Provides int providePrimitive() { return 1; } @Provides @Qualifier int provideQualifiedPrimitive() { return 2; } @Provides Number provideNumber() { return 3; } } Dependency.java000066400000000000000000000012171365703632600344160ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2013 Google LLC * * 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.google.auto.factory; public interface Dependency {} DependencyImpl.java000066400000000000000000000013411365703632600352360ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import javax.inject.Inject; public class DependencyImpl implements Dependency { @Inject DependencyImpl() {} } FactoryComponent.java000066400000000000000000000015301365703632600356300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2015 Google LLC * * 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.google.auto.factory; import dagger.Component; /** A component to materialize the factory using Dagger 2 */ @Component(modules = DaggerModule.class) interface FactoryComponent { FooFactory factory(); GenericFooFactory generatedFactory(); } FactoryInterface.java000066400000000000000000000012631365703632600355710ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2013 Google LLC * * 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.google.auto.factory; public interface FactoryInterface { Foo generate(String name); } auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/Foo.java000066400000000000000000000040201365703632600331350ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import javax.inject.Provider; @AutoFactory(implementing = FactoryInterface.class) public final class Foo { private final String name; private final Dependency dependency; private final Provider dependencyProvider; private final int primitive; private final int qualifiedPrimitive; Foo( String name, @Provided Dependency dependency, @Provided @Qualifier Provider dependencyProvider, @Provided int primitive, @Provided @Qualifier int qualifiedPrimitive) { this.name = name; this.dependency = dependency; this.dependencyProvider = dependencyProvider; this.primitive = primitive; this.qualifiedPrimitive = qualifiedPrimitive; } // Generates second factory method with a different name for the Dependency dependency. // Tests http://b/21632171. Foo( Object name, @Provided Dependency dependency2, @Provided @Qualifier Provider dependencyProvider, @Provided int primitive, @Provided @Qualifier int qualifiedPrimitive) { this(name.toString(), dependency2, dependencyProvider, primitive, qualifiedPrimitive); } String name() { return name; } Dependency dependency() { return dependency; } Provider dependencyProvider() { return dependencyProvider; } int primitive() { return primitive; } int qualifiedPrimitive() { return qualifiedPrimitive; } } GenericFoo.java000066400000000000000000000033641365703632600343650ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2019 Google LLC * * 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.google.auto.factory; import java.util.List; import javax.inject.Provider; @AutoFactory public class GenericFoo, C, E extends Enum> { private final A depA; private final B depB; private final IntAccessor depDIntAccessor; private final StringAccessor depDStringAccessor; private final E depE; GenericFoo( @Provided Provider depA, B depB, D depD, E depE) { this.depA = depA.get(); this.depB = depB; this.depDIntAccessor = depD; this.depDStringAccessor = depD; this.depE = depE; } public A getDepA() { return depA; } public B getDepB() { return depB; } public C passThrough(C value) { return value; } public IntAccessor getDepDIntAccessor() { return depDIntAccessor; } public StringAccessor getDepDStringAccessor() { return depDStringAccessor; } public E getDepE() { return depE; } public interface IntAccessor {} public interface StringAccessor {} public interface IntAndStringAccessor extends IntAccessor, StringAccessor {} public enum DepE { VALUE_1, VALUE_2 } } GuiceModule.java000066400000000000000000000020401365703632600345350ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import com.google.inject.AbstractModule; public class GuiceModule extends AbstractModule { @Override protected void configure() { bind(Dependency.class).to(DependencyImpl.class); bind(Dependency.class).annotatedWith(Qualifier.class).to(QualifiedDependencyImpl.class); bind(Integer.class).toInstance(1); bind(Integer.class).annotatedWith(Qualifier.class).toInstance(2); bind(Number.class).toInstance(3); } } QualifiedDependencyImpl.java000066400000000000000000000013651365703632600370700ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import javax.inject.Inject; public class QualifiedDependencyImpl implements Dependency { @Inject QualifiedDependencyImpl() {} } Qualifier.java000066400000000000000000000014311365703632600342570ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/main/java/com/google/auto/factory/* * Copyright 2015 Google LLC * * 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.google.auto.factory; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @javax.inject.Qualifier @Retention(RetentionPolicy.RUNTIME) @interface Qualifier {} auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/000077500000000000000000000000001365703632600242145ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/java/000077500000000000000000000000001365703632600251355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/java/com/000077500000000000000000000000001365703632600257135ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/java/com/google/000077500000000000000000000000001365703632600271675ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/java/com/google/auto/000077500000000000000000000000001365703632600301375ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/java/com/google/auto/factory/000077500000000000000000000000001365703632600316065ustar00rootroot00000000000000DependencyInjectionIntegrationTest.java000066400000000000000000000127701365703632600413660ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/it/functional/src/test/java/com/google/auto/factorypackage com.google.auto.factory; import static com.google.common.truth.Truth.assertThat; import com.google.auto.factory.GenericFoo.DepE; import com.google.auto.factory.GenericFoo.IntAndStringAccessor; import com.google.common.collect.ImmutableList; import com.google.inject.Guice; import com.google.inject.Key; import com.google.inject.TypeLiteral; import java.util.ArrayList; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class DependencyInjectionIntegrationTest { private static final IntAndStringAccessor INT_AND_STRING_ACCESSOR = new IntAndStringAccessor() {}; @Test public void daggerInjectedFactory() { FooFactory fooFactory = DaggerFactoryComponent.create().factory(); Foo one = fooFactory.create("A"); Foo two = fooFactory.create("B"); assertThat(one.name()).isEqualTo("A"); assertThat(one.dependency()).isNotNull(); assertThat(one.dependencyProvider()).isNotNull(); assertThat(one.dependencyProvider().get()).isInstanceOf(QualifiedDependencyImpl.class); assertThat(one.primitive()).isEqualTo(1); assertThat(one.qualifiedPrimitive()).isEqualTo(2); assertThat(two.name()).isEqualTo("B"); assertThat(two.dependency()).isNotNull(); assertThat(two.dependency()).isNotEqualTo(one.dependency()); assertThat(two.dependencyProvider()).isNotNull(); assertThat(two.dependencyProvider().get()).isInstanceOf(QualifiedDependencyImpl.class); assertThat(two.primitive()).isEqualTo(1); assertThat(two.qualifiedPrimitive()).isEqualTo(2); } @Test public void daggerInjectedGenericFactory() { GenericFooFactory genericFooFactory = DaggerFactoryComponent.create().generatedFactory(); GenericFoo, String, DepE> three = genericFooFactory.create(ImmutableList.of(3L), INT_AND_STRING_ACCESSOR, DepE.VALUE_1); ArrayList intAndStringAccessorArrayList = new ArrayList<>(); intAndStringAccessorArrayList.add(4.0); GenericFoo, Long, DepE> four = genericFooFactory.create( intAndStringAccessorArrayList, INT_AND_STRING_ACCESSOR, DepE.VALUE_2); assertThat(three.getDepA()).isEqualTo(3); ImmutableList unusedLongList = three.getDepB(); assertThat(three.getDepB()).containsExactly(3L); assertThat(three.getDepDIntAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(three.getDepDStringAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(three.passThrough("value")).isEqualTo("value"); assertThat(three.getDepE()).isEqualTo(DepE.VALUE_1); assertThat(four.getDepA()).isEqualTo(3); ArrayList unusedDoubleList = four.getDepB(); assertThat(four.getDepB()).isInstanceOf(ArrayList.class); assertThat(four.getDepB()).containsExactly(4.0); assertThat(four.getDepDIntAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(four.getDepDStringAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(four.passThrough(5L)).isEqualTo(5L); assertThat(four.getDepE()).isEqualTo(DepE.VALUE_2); } @Test public void guiceInjectedFactory() { FooFactory fooFactory = Guice.createInjector(new GuiceModule()).getInstance(FooFactory.class); Foo one = fooFactory.create("A"); Foo two = fooFactory.create("B"); assertThat(one.name()).isEqualTo("A"); assertThat(one.dependency()).isNotNull(); assertThat(one.dependencyProvider()).isNotNull(); assertThat(one.dependencyProvider().get()).isInstanceOf(QualifiedDependencyImpl.class); assertThat(one.primitive()).isEqualTo(1); assertThat(one.qualifiedPrimitive()).isEqualTo(2); assertThat(two.name()).isEqualTo("B"); assertThat(two.dependency()).isNotNull(); assertThat(two.dependency()).isNotEqualTo(one.dependency()); assertThat(two.dependencyProvider()).isNotNull(); assertThat(two.dependencyProvider().get()).isInstanceOf(QualifiedDependencyImpl.class); assertThat(two.primitive()).isEqualTo(1); assertThat(two.qualifiedPrimitive()).isEqualTo(2); } @Test public void guiceInjectedGenericFactory() { GenericFooFactory genericFooFactory = Guice.createInjector(new GuiceModule()) .getInstance(Key.get(new TypeLiteral>() {})); GenericFoo, String, DepE> three = genericFooFactory.create(ImmutableList.of(3L), INT_AND_STRING_ACCESSOR, DepE.VALUE_1); ArrayList intAndStringAccessorArrayList = new ArrayList<>(); intAndStringAccessorArrayList.add(4.0); GenericFoo, Long, DepE> four = genericFooFactory.create( intAndStringAccessorArrayList, INT_AND_STRING_ACCESSOR, DepE.VALUE_2); assertThat(three.getDepA()).isEqualTo(3); ImmutableList unusedLongList = three.getDepB(); assertThat(three.getDepB()).containsExactly(3L); assertThat(three.getDepDIntAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(three.getDepDStringAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(three.passThrough("value")).isEqualTo("value"); assertThat(three.getDepE()).isEqualTo(DepE.VALUE_1); assertThat(four.getDepA()).isEqualTo(3); ArrayList unusedDoubleList = four.getDepB(); assertThat(four.getDepB()).containsExactly(4.0); assertThat(four.getDepDIntAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(four.getDepDStringAccessor()).isEqualTo(INT_AND_STRING_ACCESSOR); assertThat(four.passThrough(5L)).isEqualTo(5L); assertThat(four.getDepE()).isEqualTo(DepE.VALUE_2); } } auto-auto-service-1.0-rc7/factory/src/main/000077500000000000000000000000001365703632600206145ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/000077500000000000000000000000001365703632600215355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/000077500000000000000000000000001365703632600223135ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/000077500000000000000000000000001365703632600235675ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/000077500000000000000000000000001365703632600245375ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/000077500000000000000000000000001365703632600262065ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/AutoFactory.java000066400000000000000000000052131365703632600313120ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import static java.lang.annotation.ElementType.CONSTRUCTOR; import static java.lang.annotation.ElementType.TYPE; import java.lang.annotation.Target; /** * An annotation to be applied to elements for which a factory should be automatically generated. * *

Visibility

*

The visibility of the generated factories will always be either {@code public} or default * visibility. The visibility of any given factory method is determined by the visibility of the * type being created. The generated factory is {@code public} if any of the factory methods are. * Any method that implements an interface method is necessarily public and any method that * overrides an abstract method has the same visibility as that method. * * @author Gregory Kick */ @Target({ TYPE, CONSTRUCTOR }) public @interface AutoFactory { /** * The simple name of the generated factory; the factory is always generated in the same * package as the annotated type. The default value (the empty string) will result in a factory * with the name of the type being created with {@code Factory} appended to the end. For example, * the default name for a factory for {@code MyType} will be {@code MyTypeFactory}. * *

If the annotated type is nested, then the generated factory's name will start with the * enclosing type names, separated by underscores. For example, the default name for a factory for * {@code Outer.Inner.ReallyInner} is {@code Outer_Inner_ReallyInnerFactory}. If {@code className} * is {@code Foo}, then the factory name is {@code Outer_Inner_Foo}. */ String className() default ""; /** * A list of interfaces that the generated factory is required to implement. */ Class[] implementing() default { }; /** * The type that the generated factory is require to extend. */ Class extending() default Object.class; /** * Whether or not the generated factory should be final. * Defaults to disallowing subclasses (generating the factory as final). */ boolean allowSubclasses() default false; } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/Provided.java000066400000000000000000000017231365703632600306300ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory; import static java.lang.annotation.ElementType.PARAMETER; import java.lang.annotation.Target; /** * An annotation to be applied to parameters that should be provided by an * {@linkplain javax.inject.Inject injected} {@link javax.inject.Provider} in a generated factory. * * @author Gregory Kick */ @Target(PARAMETER) public @interface Provided { } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/000077500000000000000000000000001365703632600302255ustar00rootroot00000000000000AnnotationValues.java000066400000000000000000000060331365703632600343050ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import java.util.List; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.SimpleAnnotationValueVisitor6; import javax.lang.model.util.SimpleTypeVisitor6; final class AnnotationValues { private AnnotationValues() {} static boolean asBoolean(AnnotationValue value) { return value.accept( new SimpleAnnotationValueVisitor6() { @Override protected Boolean defaultAction(Object o, Void p) { throw new IllegalArgumentException(); } @Override public Boolean visitBoolean(boolean b, Void p) { return b; } }, null); } static TypeElement asType(AnnotationValue value) { return value.accept( new SimpleAnnotationValueVisitor6() { @Override protected TypeElement defaultAction(Object o, Void p) { throw new IllegalArgumentException(); } @Override public TypeElement visitType(TypeMirror t, Void p) { return t.accept( new SimpleTypeVisitor6() { @Override protected TypeElement defaultAction(TypeMirror e, Void p) { throw new AssertionError(); } @Override public TypeElement visitDeclared(DeclaredType t, Void p) { return Iterables.getOnlyElement(ElementFilter.typesIn( ImmutableList.of(t.asElement()))); } }, null); } }, null); } static ImmutableList asList(AnnotationValue value) { return value.accept( new SimpleAnnotationValueVisitor6, Void>() { @Override protected ImmutableList defaultAction(Object o, Void p) { throw new IllegalArgumentException(); } @Override public ImmutableList visitArray( List vals, Void p) { return ImmutableList.copyOf(vals); } }, null); } } AutoFactoryDeclaration.java000066400000000000000000000173311365703632600354240ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.common.MoreElements.getPackage; import static com.google.auto.factory.processor.Elements2.isValidSupertypeForClass; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Iterables.getOnlyElement; import static javax.lang.model.element.ElementKind.PACKAGE; import static javax.lang.model.util.ElementFilter.typesIn; import static javax.tools.Diagnostic.Kind.ERROR; import com.google.auto.factory.AutoFactory; import com.google.auto.value.AutoValue; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.Arrays; import java.util.List; import java.util.Map; import javax.annotation.processing.Messager; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; /** * This is a value object that mirrors the static declaration of an {@link AutoFactory} annotation. * * @author Gregory Kick */ @AutoValue abstract class AutoFactoryDeclaration { abstract TypeElement targetType(); abstract Element target(); abstract Optional className(); abstract TypeElement extendingType(); abstract ImmutableSet implementingTypes(); abstract boolean allowSubclasses(); abstract AnnotationMirror mirror(); abstract ImmutableMap valuesMap(); String getFactoryName() { CharSequence packageName = getPackage(targetType()).getQualifiedName(); StringBuilder builder = new StringBuilder(packageName); if (packageName.length() > 0) { builder.append('.'); } if (className().isPresent()) { builder.append(className().get()); } else { for (String enclosingSimpleName : targetEnclosingSimpleNames()) { builder.append(enclosingSimpleName).append('_'); } builder.append(targetType().getSimpleName()).append("Factory"); } return builder.toString(); } private ImmutableList targetEnclosingSimpleNames() { ImmutableList.Builder simpleNames = ImmutableList.builder(); for (Element element = targetType().getEnclosingElement(); !element.getKind().equals(PACKAGE); element = element.getEnclosingElement()) { simpleNames.add(element.getSimpleName().toString()); } return simpleNames.build().reverse(); } static final class Factory { private final Elements elements; private final Messager messager; Factory(Elements elements, Messager messager) { this.elements = elements; this.messager = messager; } Optional createIfValid(Element element) { checkNotNull(element); AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get(); checkArgument(Mirrors.getQualifiedName(mirror.getAnnotationType()). contentEquals(AutoFactory.class.getName())); Map values = Mirrors.simplifyAnnotationValueMap(elements.getElementValuesWithDefaults(mirror)); checkState(values.size() == 4); // className value is a string, so we can just call toString AnnotationValue classNameValue = values.get("className"); String className = classNameValue.getValue().toString(); if (!className.isEmpty() && !isValidIdentifier(className)) { messager.printMessage(ERROR, String.format("\"%s\" is not a valid Java identifier", className), element, mirror, classNameValue); return Optional.absent(); } AnnotationValue extendingValue = checkNotNull(values.get("extending")); TypeElement extendingType = AnnotationValues.asType(extendingValue); if (extendingType == null) { messager.printMessage(ERROR, "Unable to find the type: " + extendingValue.getValue().toString(), element, mirror, extendingValue); return Optional.absent(); } else if (!isValidSupertypeForClass(extendingType)) { messager.printMessage(ERROR, String.format("%s is not a valid supertype for a factory. " + "Supertypes must be non-final classes.", extendingType.getQualifiedName()), element, mirror, extendingValue); return Optional.absent(); } ImmutableList noParameterConstructors = FluentIterable.from(ElementFilter.constructorsIn(extendingType.getEnclosedElements())) .filter(new Predicate() { @Override public boolean apply(ExecutableElement constructor) { return constructor.getParameters().isEmpty(); } }) .toList(); if (noParameterConstructors.size() == 0) { messager.printMessage(ERROR, String.format("%s is not a valid supertype for a factory. " + "Factory supertypes must have a no-arg constructor.", extendingType.getQualifiedName()), element, mirror, extendingValue); return Optional.absent(); } else if (noParameterConstructors.size() > 1) { throw new IllegalStateException("Multiple constructors with no parameters??"); } AnnotationValue implementingValue = checkNotNull(values.get("implementing")); ImmutableSet.Builder builder = ImmutableSet.builder(); for (AnnotationValue implementingTypeValue : AnnotationValues.asList(implementingValue)) { builder.add(AnnotationValues.asType(implementingTypeValue)); } ImmutableSet implementingTypes = builder.build(); AnnotationValue allowSubclassesValue = checkNotNull(values.get("allowSubclasses")); boolean allowSubclasses = AnnotationValues.asBoolean(allowSubclassesValue); return Optional.of( new AutoValue_AutoFactoryDeclaration( getAnnotatedType(element), element, className.isEmpty() ? Optional.absent() : Optional.of(className), extendingType, implementingTypes, allowSubclasses, mirror, ImmutableMap.copyOf(values))); } private static TypeElement getAnnotatedType(Element element) { List types = ImmutableList.of(); while (types.isEmpty()) { types = typesIn(Arrays.asList(element)); element = element.getEnclosingElement(); } return getOnlyElement(types); } static boolean isValidIdentifier(String identifier) { return SourceVersion.isIdentifier(identifier) && !SourceVersion.isKeyword(identifier); } } } AutoFactoryProcessor.java000066400000000000000000000227571365703632600351660ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import com.google.auto.common.MoreTypes; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import com.google.auto.service.AutoService; import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Iterables; import com.google.googlejavaformat.java.filer.FormattingFiler; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; 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.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; /** * The annotation processor that generates factories for {@link AutoFactory} annotations. * * @author Gregory Kick */ @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) @AutoService(Processor.class) public final class AutoFactoryProcessor extends AbstractProcessor { private FactoryDescriptorGenerator factoryDescriptorGenerator; private AutoFactoryDeclaration.Factory declarationFactory; private ProvidedChecker providedChecker; private Messager messager; private Elements elements; private Types types; private FactoryWriter factoryWriter; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); elements = processingEnv.getElementUtils(); types = processingEnv.getTypeUtils(); messager = processingEnv.getMessager(); factoryWriter = new FactoryWriter( new FormattingFiler(processingEnv.getFiler()), elements, processingEnv.getSourceVersion()); providedChecker = new ProvidedChecker(messager); declarationFactory = new AutoFactoryDeclaration.Factory(elements, messager); factoryDescriptorGenerator = new FactoryDescriptorGenerator(messager, types, declarationFactory); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { try { doProcess(roundEnv); } catch (Throwable e) { messager.printMessage(Kind.ERROR, "Failed to process @AutoFactory annotations:\n" + Throwables.getStackTraceAsString(e)); } return false; } private void doProcess(RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(Provided.class)) { providedChecker.checkProvidedParameter(element); } ImmutableListMultimap.Builder indexedMethods = ImmutableListMultimap.builder(); ImmutableSetMultimap.Builder implementationMethodDescriptorsBuilder = ImmutableSetMultimap.builder(); for (Element element : roundEnv.getElementsAnnotatedWith(AutoFactory.class)) { Optional declaration = declarationFactory.createIfValid(element); if (declaration.isPresent()) { String factoryName = declaration.get().getFactoryName(); TypeElement extendingType = declaration.get().extendingType(); implementationMethodDescriptorsBuilder.putAll( factoryName, implementationMethods(extendingType, element)); for (TypeElement implementingType : declaration.get().implementingTypes()) { implementationMethodDescriptorsBuilder.putAll( factoryName, implementationMethods(implementingType, element)); } } ImmutableSet descriptors = factoryDescriptorGenerator.generateDescriptor(element); for (FactoryMethodDescriptor descriptor : descriptors) { indexedMethods.put(descriptor.factoryName(), descriptor); } } ImmutableSetMultimap implementationMethodDescriptors = implementationMethodDescriptorsBuilder.build(); for (Entry> entry : indexedMethods.build().asMap().entrySet()) { ImmutableSet.Builder extending = ImmutableSet.builder(); ImmutableSortedSet.Builder implementing = ImmutableSortedSet.orderedBy( new Comparator() { @Override public int compare(TypeMirror first, TypeMirror second) { String firstName = MoreTypes.asTypeElement(first).getQualifiedName().toString(); String secondName = MoreTypes.asTypeElement(second).getQualifiedName().toString(); return firstName.compareTo(secondName); } }); boolean publicType = false; Boolean allowSubclasses = null; boolean skipCreation = false; for (FactoryMethodDescriptor methodDescriptor : entry.getValue()) { extending.add(methodDescriptor.declaration().extendingType().asType()); for (TypeElement implementingType : methodDescriptor.declaration().implementingTypes()) { implementing.add(implementingType.asType()); } publicType |= methodDescriptor.publicMethod(); if (allowSubclasses == null) { allowSubclasses = methodDescriptor.declaration().allowSubclasses(); } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) { skipCreation = true; messager.printMessage(Kind.ERROR, "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.", methodDescriptor.declaration().target(), methodDescriptor.declaration().mirror(), methodDescriptor.declaration().valuesMap().get("allowSubclasses")); } } if (!skipCreation) { try { factoryWriter.writeFactory( FactoryDescriptor.create( entry.getKey(), Iterables.getOnlyElement(extending.build()), implementing.build(), publicType, ImmutableSet.copyOf(entry.getValue()), implementationMethodDescriptors.get(entry.getKey()), allowSubclasses)); } catch (IOException e) { messager.printMessage(Kind.ERROR, "failed: " + e); } } } } private ImmutableSet implementationMethods( TypeElement supertype, Element autoFactoryElement) { ImmutableSet.Builder implementationMethodsBuilder = ImmutableSet.builder(); for (ExecutableElement implementationMethod : ElementFilter.methodsIn(elements.getAllMembers(supertype))) { if (implementationMethod.getModifiers().contains(Modifier.ABSTRACT)) { ExecutableType methodType = Elements2.getExecutableElementAsMemberOf( types, implementationMethod, supertype); ImmutableSet passedParameters = Parameter.forParameterList( implementationMethod.getParameters(), methodType.getParameterTypes(), types); implementationMethodsBuilder.add( ImplementationMethodDescriptor.builder() .name(implementationMethod.getSimpleName().toString()) .returnType(getAnnotatedType(autoFactoryElement)) .publicMethod() .passedParameters(passedParameters) .isVarArgs(implementationMethod.isVarArgs()) .build()); } } return implementationMethodsBuilder.build(); } private TypeMirror getAnnotatedType(Element element) { List types = ImmutableList.of(); while (types.isEmpty()) { types = ElementFilter.typesIn(Arrays.asList(element)); element = element.getEnclosingElement(); } return Iterables.getOnlyElement(types).asType(); } @Override public Set getSupportedAnnotationTypes() { return ImmutableSet.of(AutoFactory.class.getName(), Provided.class.getName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/Elements2.java000066400000000000000000000060221365703632600327260ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static javax.lang.model.element.ElementKind.CLASS; import static javax.lang.model.element.ElementKind.PACKAGE; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.STATIC; import com.google.common.collect.ImmutableSet; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Types; final class Elements2 { private Elements2() { } static ImmutableSet getConstructors(TypeElement type) { checkNotNull(type); checkArgument(type.getKind() == CLASS); return ImmutableSet.copyOf(ElementFilter.constructorsIn(type.getEnclosedElements())); } static boolean isValidSupertypeForClass(TypeElement type) { if (!type.getKind().equals(CLASS)) { return false; } if (type.getModifiers().contains(FINAL)) { return false; } if (!type.getEnclosingElement().getKind().equals(PACKAGE) && !type.getModifiers().contains(STATIC)) { return false; } if (type.getSimpleName().length() == 0) { return false; } return true; } /** * Given an executable element in a supertype, returns its ExecutableType when it is viewed as a * member of a subtype. */ static ExecutableType getExecutableElementAsMemberOf( Types types, ExecutableElement executableElement, TypeElement subTypeElement) { checkNotNull(types); checkNotNull(executableElement); checkNotNull(subTypeElement); TypeMirror subTypeMirror = subTypeElement.asType(); if (!subTypeMirror.getKind().equals(TypeKind.DECLARED)) { throw new IllegalStateException( "Expected subTypeElement.asType() to return a class/interface type."); } TypeMirror subExecutableTypeMirror = types.asMemberOf( (DeclaredType) subTypeMirror, executableElement); if (!subExecutableTypeMirror.getKind().equals(TypeKind.EXECUTABLE)) { throw new IllegalStateException("Expected subExecutableTypeMirror to be an executable type."); } return (ExecutableType) subExecutableTypeMirror; } } FactoryDescriptor.java000066400000000000000000000212611365703632600344610ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import com.google.auto.value.AutoValue; import com.google.common.base.CharMatcher; import com.google.common.base.Optional; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import java.util.Collection; import java.util.HashSet; import java.util.Map.Entry; import java.util.Set; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeMirror; /** * A value object representing a factory to be generated. * * @author Gregory Kick */ @AutoValue abstract class FactoryDescriptor { private static final CharMatcher invalidIdentifierCharacters = new CharMatcher() { @Override public boolean matches(char c) { return !Character.isJavaIdentifierPart(c); } }; abstract String name(); abstract TypeMirror extendingType(); abstract ImmutableSet implementingTypes(); abstract boolean publicType(); abstract ImmutableSet methodDescriptors(); abstract ImmutableSet implementationMethodDescriptors(); abstract boolean allowSubclasses(); abstract ImmutableMap providers(); final AutoFactoryDeclaration declaration() { return Iterables.getFirst(methodDescriptors(), null).declaration(); } private static class UniqueNameSet { private final Set uniqueNames = new HashSet(); /** * Generates a unique name using {@code base}. If {@code base} has not yet been added, it will * be returned as-is. If your {@code base} is healthy, this will always return {@code base}. */ String getUniqueName(CharSequence base) { String name = base.toString(); for (int differentiator = 2; !uniqueNames.add(name); differentiator++) { name = base.toString() + differentiator; } return name; } } static FactoryDescriptor create( String name, TypeMirror extendingType, ImmutableSet implementingTypes, boolean publicType, ImmutableSet methodDescriptors, ImmutableSet implementationMethodDescriptors, boolean allowSubclasses) { ImmutableSetMultimap.Builder parametersForProviders = ImmutableSetMultimap.builder(); for (FactoryMethodDescriptor descriptor : methodDescriptors) { for (Parameter parameter : descriptor.providedParameters()) { parametersForProviders.put(parameter.key(), parameter); } } ImmutableMap.Builder providersBuilder = ImmutableMap.builder(); UniqueNameSet uniqueNames = new UniqueNameSet(); for (Entry> entry : parametersForProviders.build().asMap().entrySet()) { Key key = entry.getKey(); switch (entry.getValue().size()) { case 0: throw new AssertionError(); case 1: Parameter parameter = Iterables.getOnlyElement(entry.getValue()); providersBuilder.put( key, ProviderField.create( uniqueNames.getUniqueName(parameter.name() + "Provider"), key, parameter.nullable())); break; default: String providerName = uniqueNames.getUniqueName( invalidIdentifierCharacters.replaceFrom(key.toString(), '_') + "Provider"); Optional nullable = Optional.absent(); for (Parameter param : entry.getValue()) { nullable = nullable.or(param.nullable()); } providersBuilder.put(key, ProviderField.create(providerName, key, nullable)); break; } } ImmutableBiMap duplicateMethodDescriptors = createDuplicateMethodDescriptorsBiMap( methodDescriptors, implementationMethodDescriptors); ImmutableSet deduplicatedMethodDescriptors = getDeduplicatedMethodDescriptors(methodDescriptors, duplicateMethodDescriptors); ImmutableSet deduplicatedImplementationMethodDescriptors = ImmutableSet.copyOf( Sets.difference(implementationMethodDescriptors, duplicateMethodDescriptors.values())); return new AutoValue_FactoryDescriptor( name, extendingType, implementingTypes, publicType, deduplicatedMethodDescriptors, deduplicatedImplementationMethodDescriptors, allowSubclasses, providersBuilder.build()); } /** * Creates a bi-map of duplicate {@link ImplementationMethodDescriptor}s by their respective * {@link FactoryMethodDescriptor}. */ private static ImmutableBiMap createDuplicateMethodDescriptorsBiMap( ImmutableSet factoryMethodDescriptors, ImmutableSet implementationMethodDescriptors) { ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); for (FactoryMethodDescriptor factoryMethodDescriptor : factoryMethodDescriptors) { for (ImplementationMethodDescriptor implementationMethodDescriptor : implementationMethodDescriptors) { boolean areDuplicateMethodDescriptors = areDuplicateMethodDescriptors(factoryMethodDescriptor, implementationMethodDescriptor); if (areDuplicateMethodDescriptors) { builder.put(factoryMethodDescriptor, implementationMethodDescriptor); break; } } } return builder.build(); } /** * Returns a set of deduplicated {@link FactoryMethodDescriptor}s from the set of original * descriptors and the bi-map of duplicate descriptors. * *

Modifies the duplicate {@link FactoryMethodDescriptor}s such that they are overriding and * reflect properties from the {@link ImplementationMethodDescriptor} they are implementing. */ private static ImmutableSet getDeduplicatedMethodDescriptors( ImmutableSet methodDescriptors, ImmutableBiMap duplicateMethodDescriptors) { ImmutableSet.Builder deduplicatedMethodDescriptors = ImmutableSet.builder(); for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) { ImplementationMethodDescriptor duplicateMethodDescriptor = duplicateMethodDescriptors.get(methodDescriptor); FactoryMethodDescriptor newMethodDescriptor = (duplicateMethodDescriptor != null) ? methodDescriptor .toBuilder() .overridingMethod(true) .publicMethod(duplicateMethodDescriptor.publicMethod()) .returnType(duplicateMethodDescriptor.returnType()) .build() : methodDescriptor; deduplicatedMethodDescriptors.add(newMethodDescriptor); } return deduplicatedMethodDescriptors.build(); } /** * Returns true if the given {@link FactoryMethodDescriptor} and * {@link ImplementationMethodDescriptor} are duplicates. * *

Descriptors are duplicates if they have the same name and if they have the same passed types * in the same order. */ private static boolean areDuplicateMethodDescriptors( FactoryMethodDescriptor factory, ImplementationMethodDescriptor implementation) { if (!factory.name().equals(implementation.name())) { return false; } // Descriptors are identical if they have the same passed types in the same order. return Iterables.elementsEqual( Iterables.transform(factory.passedParameters(), Parameter::type), Iterables.transform(implementation.passedParameters(), Parameter::type)); } } FactoryDescriptorGenerator.java000066400000000000000000000152301365703632600363270ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static javax.lang.model.element.Modifier.ABSTRACT; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.tools.Diagnostic.Kind.ERROR; import com.google.auto.common.MoreElements; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimaps; import javax.annotation.processing.Messager; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementKindVisitor6; import javax.lang.model.util.Types; /** * A service that traverses an element and returns the set of factory methods defined therein. * * @author Gregory Kick */ final class FactoryDescriptorGenerator { private final Messager messager; private final Types types; private final AutoFactoryDeclaration.Factory declarationFactory; FactoryDescriptorGenerator( Messager messager, Types types, AutoFactoryDeclaration.Factory declarationFactory) { this.messager = messager; this.types = types; this.declarationFactory = declarationFactory; } ImmutableSet generateDescriptor(Element element) { final AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get(); final Optional declaration = declarationFactory.createIfValid(element); if (!declaration.isPresent()) { return ImmutableSet.of(); } return element.accept(new ElementKindVisitor6, Void>() { @Override protected ImmutableSet defaultAction(Element e, Void p) { throw new AssertionError("@AutoFactory applied to an impossible element"); } @Override public ImmutableSet visitTypeAsClass(TypeElement type, Void p) { if (type.getModifiers().contains(ABSTRACT)) { // applied to an abstract factory messager.printMessage(ERROR, "Auto-factory doesn't support being applied to abstract classes.", type, mirror); return ImmutableSet.of(); } else { // applied to the type to be created ImmutableSet constructors = Elements2.getConstructors(type); if (constructors.isEmpty()) { return generateDescriptorForDefaultConstructor(declaration.get(), type); } else { return FluentIterable.from(constructors) .transform(new Function() { @Override public FactoryMethodDescriptor apply(ExecutableElement constructor) { return generateDescriptorForConstructor(declaration.get(), constructor); } }) .toSet(); } } } @Override public ImmutableSet visitTypeAsInterface(TypeElement type, Void p) { // applied to the factory interface messager.printMessage(ERROR, "Auto-factory doesn't support being applied to interfaces.", type, mirror); return ImmutableSet.of(); } @Override public ImmutableSet visitExecutableAsConstructor(ExecutableElement e, Void p) { // applied to a constructor of a type to be created return ImmutableSet.of(generateDescriptorForConstructor(declaration.get(), e)); } }, null); } FactoryMethodDescriptor generateDescriptorForConstructor(final AutoFactoryDeclaration declaration, ExecutableElement constructor) { checkNotNull(constructor); checkArgument(constructor.getKind() == ElementKind.CONSTRUCTOR); TypeElement classElement = MoreElements.asType(constructor.getEnclosingElement()); ImmutableListMultimap parameterMap = Multimaps.index(constructor.getParameters(), Functions.forPredicate( new Predicate() { @Override public boolean apply(VariableElement parameter) { return isAnnotationPresent(parameter, Provided.class); } })); ImmutableSet providedParameters = Parameter.forParameterList(parameterMap.get(true), types); ImmutableSet passedParameters = Parameter.forParameterList(parameterMap.get(false), types); return FactoryMethodDescriptor.builder(declaration) .name("create") .returnType(classElement.asType()) .publicMethod(classElement.getModifiers().contains(PUBLIC)) .providedParameters(providedParameters) .passedParameters(passedParameters) .creationParameters(Parameter.forParameterList(constructor.getParameters(), types)) .isVarArgs(constructor.isVarArgs()) .build(); } private ImmutableSet generateDescriptorForDefaultConstructor( AutoFactoryDeclaration declaration, TypeElement type) { return ImmutableSet.of( FactoryMethodDescriptor.builder(declaration) .name("create") .returnType(type.asType()) .publicMethod(type.getModifiers().contains(PUBLIC)) .passedParameters(ImmutableSet.of()) .creationParameters(ImmutableSet.of()) .providedParameters(ImmutableSet.of()) .build()); } } FactoryMethodDescriptor.java000066400000000000000000000053651365703632600356310ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import javax.lang.model.type.TypeMirror; /** * A value object representing a factory method to be generated. * * @author Gregory Kick */ @AutoValue abstract class FactoryMethodDescriptor { abstract AutoFactoryDeclaration declaration(); abstract String name(); abstract TypeMirror returnType(); abstract boolean publicMethod(); abstract boolean overridingMethod(); abstract ImmutableSet passedParameters(); abstract ImmutableSet providedParameters(); abstract ImmutableSet creationParameters(); abstract Builder toBuilder(); abstract boolean isVarArgs(); final String factoryName() { return declaration().getFactoryName(); } static Builder builder(AutoFactoryDeclaration declaration) { return new AutoValue_FactoryMethodDescriptor.Builder() .declaration(checkNotNull(declaration)) .publicMethod(false) .overridingMethod(false) .isVarArgs(false); } @AutoValue.Builder static abstract class Builder { abstract Builder declaration(AutoFactoryDeclaration declaration); abstract Builder name(String name); abstract Builder returnType(TypeMirror returnType); abstract Builder publicMethod(boolean publicMethod); abstract Builder overridingMethod(boolean overridingMethod); abstract Builder passedParameters(Iterable passedParameters); abstract Builder providedParameters(Iterable providedParameters); abstract Builder creationParameters(Iterable creationParameters); abstract Builder isVarArgs(boolean isVarargs); abstract FactoryMethodDescriptor buildImpl(); FactoryMethodDescriptor build() { FactoryMethodDescriptor descriptor = buildImpl(); checkState(descriptor.creationParameters().equals( Sets.union(descriptor.passedParameters(), descriptor.providedParameters()))); return descriptor; } } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java000066400000000000000000000314561365703632600337050ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec; import static com.squareup.javapoet.MethodSpec.constructorBuilder; import static com.squareup.javapoet.MethodSpec.methodBuilder; import static com.squareup.javapoet.TypeSpec.classBuilder; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import java.io.IOException; import java.util.Iterator; import javax.annotation.processing.Filer; import javax.inject.Inject; import javax.inject.Provider; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.Elements; final class FactoryWriter { private final Filer filer; private final Elements elements; private final SourceVersion sourceVersion; FactoryWriter(Filer filer, Elements elements, SourceVersion sourceVersion) { this.filer = filer; this.elements = elements; this.sourceVersion = sourceVersion; } private static final Joiner ARGUMENT_JOINER = Joiner.on(", "); void writeFactory(final FactoryDescriptor descriptor) throws IOException { String factoryName = getSimpleName(descriptor.name()).toString(); TypeSpec.Builder factory = classBuilder(factoryName) .addOriginatingElement(descriptor.declaration().targetType()); generatedAnnotationSpec( elements, sourceVersion, AutoFactoryProcessor.class, "https://github.com/google/auto/tree/master/factory") .ifPresent(factory::addAnnotation); if (!descriptor.allowSubclasses()) { factory.addModifiers(FINAL); } if (descriptor.publicType()) { factory.addModifiers(PUBLIC); } factory.superclass(TypeName.get(descriptor.extendingType())); for (TypeMirror implementingType : descriptor.implementingTypes()) { factory.addSuperinterface(TypeName.get(implementingType)); } ImmutableSet factoryTypeVariables = getFactoryTypeVariables(descriptor); addFactoryTypeParameters(factory, factoryTypeVariables); addConstructorAndProviderFields(factory, descriptor); addFactoryMethods(factory, descriptor, factoryTypeVariables); addImplementationMethods(factory, descriptor); addCheckNotNullMethod(factory, descriptor); JavaFile.builder(getPackage(descriptor.name()), factory.build()) .skipJavaLangImports(true) .build() .writeTo(filer); } private static void addFactoryTypeParameters( TypeSpec.Builder factory, ImmutableSet typeVariableNames) { factory.addTypeVariables(typeVariableNames); } private static void addConstructorAndProviderFields( TypeSpec.Builder factory, FactoryDescriptor descriptor) { MethodSpec.Builder constructor = constructorBuilder().addAnnotation(Inject.class); if (descriptor.publicType()) { constructor.addModifiers(PUBLIC); } Iterator providerFields = descriptor.providers().values().iterator(); for (int argumentIndex = 1; providerFields.hasNext(); argumentIndex++) { ProviderField provider = providerFields.next(); TypeName typeName = TypeName.get(provider.key().type().get()).box(); TypeName providerType = ParameterizedTypeName.get(ClassName.get(Provider.class), typeName); factory.addField(providerType, provider.name(), PRIVATE, FINAL); if (provider.key().qualifier().isPresent()) { // only qualify the constructor parameter providerType = providerType.annotated(AnnotationSpec.get(provider.key().qualifier().get())); } constructor.addParameter(providerType, provider.name()); constructor.addStatement("this.$1L = checkNotNull($1L, $2L)", provider.name(), argumentIndex); } factory.addMethod(constructor.build()); } private static void addFactoryMethods( TypeSpec.Builder factory, FactoryDescriptor descriptor, ImmutableSet factoryTypeVariables) { for (FactoryMethodDescriptor methodDescriptor : descriptor.methodDescriptors()) { MethodSpec.Builder method = MethodSpec.methodBuilder(methodDescriptor.name()) .addTypeVariables(getMethodTypeVariables(methodDescriptor, factoryTypeVariables)) .returns(TypeName.get(methodDescriptor.returnType())) .varargs(methodDescriptor.isVarArgs()); if (methodDescriptor.overridingMethod()) { method.addAnnotation(Override.class); } if (methodDescriptor.publicMethod()) { method.addModifiers(PUBLIC); } CodeBlock.Builder args = CodeBlock.builder(); method.addParameters(parameters(methodDescriptor.passedParameters())); Iterator parameters = methodDescriptor.creationParameters().iterator(); for (int argumentIndex = 1; parameters.hasNext(); argumentIndex++) { Parameter parameter = parameters.next(); boolean checkNotNull = !parameter.nullable().isPresent(); CodeBlock argument; if (methodDescriptor.passedParameters().contains(parameter)) { argument = CodeBlock.of(parameter.name()); if (parameter.isPrimitive()) { checkNotNull = false; } } else { ProviderField provider = descriptor.providers().get(parameter.key()); argument = CodeBlock.of(provider.name()); if (parameter.isProvider()) { // Providers are checked for nullness in the Factory's constructor. checkNotNull = false; } else { argument = CodeBlock.of("$L.get()", argument); } } if (checkNotNull) { argument = CodeBlock.of("checkNotNull($L, $L)", argument, argumentIndex); } args.add(argument); if (parameters.hasNext()) { args.add(", "); } } method.addStatement("return new $T($L)", methodDescriptor.returnType(), args.build()); factory.addMethod(method.build()); } } private static void addImplementationMethods( TypeSpec.Builder factory, FactoryDescriptor descriptor) { for (ImplementationMethodDescriptor methodDescriptor : descriptor.implementationMethodDescriptors()) { MethodSpec.Builder implementationMethod = methodBuilder(methodDescriptor.name()) .addAnnotation(Override.class) .returns(TypeName.get(methodDescriptor.returnType())) .varargs(methodDescriptor.isVarArgs()); if (methodDescriptor.publicMethod()) { implementationMethod.addModifiers(PUBLIC); } implementationMethod.addParameters(parameters(methodDescriptor.passedParameters())); implementationMethod.addStatement( "return create($L)", FluentIterable.from(methodDescriptor.passedParameters()) .transform( new Function() { @Override public String apply(Parameter parameter) { return parameter.name(); } }) .join(ARGUMENT_JOINER)); factory.addMethod(implementationMethod.build()); } } /** * {@link ParameterSpec}s to match {@code parameters}. Note that the type of the {@link * ParameterSpec}s match {@link Parameter#type()} and not {@link Key#type()}. */ private static Iterable parameters(Iterable parameters) { ImmutableList.Builder builder = ImmutableList.builder(); for (Parameter parameter : parameters) { ParameterSpec.Builder parameterBuilder = ParameterSpec.builder(TypeName.get(parameter.type().get()), parameter.name()); for (AnnotationMirror annotation : Iterables.concat(parameter.nullable().asSet(), parameter.key().qualifier().asSet())) { parameterBuilder.addAnnotation(AnnotationSpec.get(annotation)); } builder.add(parameterBuilder.build()); } return builder.build(); } private static void addCheckNotNullMethod( TypeSpec.Builder factory, FactoryDescriptor descriptor) { if (shouldGenerateCheckNotNull(descriptor)) { TypeVariableName typeVariable = TypeVariableName.get("T"); factory.addMethod( methodBuilder("checkNotNull") .addModifiers(PRIVATE, STATIC) .addTypeVariable(typeVariable) .returns(typeVariable) .addParameter(typeVariable, "reference") .addParameter(TypeName.INT, "argumentIndex") .beginControlFlow("if (reference == null)") .addStatement( "throw new $T($S + argumentIndex)", NullPointerException.class, "@AutoFactory method argument is null but is not marked @Nullable. Argument " + "index: ") .endControlFlow() .addStatement("return reference") .build()); } } private static boolean shouldGenerateCheckNotNull(FactoryDescriptor descriptor) { if (!descriptor.providers().isEmpty()) { return true; } for (FactoryMethodDescriptor method : descriptor.methodDescriptors()) { for (Parameter parameter : method.creationParameters()) { if (!parameter.nullable().isPresent() && !parameter.type().get().getKind().isPrimitive()) { return true; } } } return false; } private static CharSequence getSimpleName(CharSequence fullyQualifiedName) { int lastDot = lastIndexOf(fullyQualifiedName, '.'); return fullyQualifiedName.subSequence(lastDot + 1, fullyQualifiedName.length()); } private static String getPackage(CharSequence fullyQualifiedName) { int lastDot = lastIndexOf(fullyQualifiedName, '.'); return lastDot == -1 ? "" : fullyQualifiedName.subSequence(0, lastDot).toString(); } private static int lastIndexOf(CharSequence charSequence, char c) { for (int i = charSequence.length() - 1; i >= 0; i--) { if (charSequence.charAt(i) == c) { return i; } } return -1; } private static ImmutableSet getFactoryTypeVariables( FactoryDescriptor descriptor) { ImmutableSet.Builder typeVariables = ImmutableSet.builder(); for (ProviderField provider : descriptor.providers().values()) { typeVariables.addAll(getReferencedTypeParameterNames(provider.key().type().get())); } return typeVariables.build(); } private static ImmutableSet getMethodTypeVariables( FactoryMethodDescriptor methodDescriptor, ImmutableSet factoryTypeVariables) { ImmutableSet.Builder typeVariables = ImmutableSet.builder(); typeVariables.addAll(getReferencedTypeParameterNames(methodDescriptor.returnType())); for (Parameter parameter : methodDescriptor.passedParameters()) { typeVariables.addAll(getReferencedTypeParameterNames(parameter.type().get())); } return Sets.difference(typeVariables.build(), factoryTypeVariables).immutableCopy(); } private static ImmutableSet getReferencedTypeParameterNames(TypeMirror type) { ImmutableSet.Builder typeVariableNames = ImmutableSet.builder(); for (TypeVariable typeVariable : TypeVariables.getReferencedTypeVariables(type)) { typeVariableNames.add(TypeVariableName.get(typeVariable)); } return typeVariableNames.build(); } } ImplementationMethodDescriptor.java000066400000000000000000000031601365703632600371760ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableSet; import javax.lang.model.type.TypeMirror; @AutoValue abstract class ImplementationMethodDescriptor { abstract String name(); abstract TypeMirror returnType(); abstract boolean publicMethod(); abstract ImmutableSet passedParameters(); abstract boolean isVarArgs(); static Builder builder() { return new AutoValue_ImplementationMethodDescriptor.Builder() .publicMethod(true) .isVarArgs(false); } @AutoValue.Builder static abstract class Builder { abstract Builder name(String name); abstract Builder returnType(TypeMirror returnTypeElement); abstract Builder publicMethod(boolean publicMethod); final Builder publicMethod() { return publicMethod(true); } abstract Builder passedParameters(Iterable passedParameters); abstract Builder isVarArgs(boolean isVarargs); abstract ImplementationMethodDescriptor build(); } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/Key.java000066400000000000000000000075411365703632600316270ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.factory.processor.Mirrors.isProvider; import static com.google.auto.factory.processor.Mirrors.unwrapOptionalEquivalence; import static com.google.auto.factory.processor.Mirrors.wrapOptionalInEquivalence; import com.google.auto.common.AnnotationMirrors; import com.google.auto.common.MoreTypes; import com.google.auto.value.AutoValue; import com.google.common.base.Equivalence; import com.google.common.base.Optional; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableSet; import javax.inject.Qualifier; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; /** * A value object for types and qualifiers. * * @author Gregory Kick */ @AutoValue // TODO(ronshapiro): reuse dagger.model.Key? abstract class Key { abstract Equivalence.Wrapper type(); abstract Optional> qualifierWrapper(); Optional qualifier() { return unwrapOptionalEquivalence(qualifierWrapper()); } /** * Constructs a key based on the type {@code type} and any {@link Qualifier}s in {@code * annotations}. * *

If {@code type} is a {@code Provider}, the returned {@link Key}'s {@link #type()} is * {@code T}. If {@code type} is a primitive, the returned {@link Key}'s {@link #type()} is the * corresponding {@linkplain Types#boxedClass(PrimitiveType) boxed type}. * *

For example: * *
Input type {@code Key.type()} *
{@code String} {@code String} *
{@code Provider} {@code String} *
{@code int} {@code Integer} *
*/ static Key create( TypeMirror type, Iterable annotations, Types types) { ImmutableSet.Builder qualifiers = ImmutableSet.builder(); for (AnnotationMirror annotation : annotations) { if (isAnnotationPresent(annotation.getAnnotationType().asElement(), Qualifier.class)) { qualifiers.add(annotation); } } // TODO(gak): check for only one qualifier rather than using the first Optional qualifier = FluentIterable.from(qualifiers.build()).first(); TypeMirror keyType = isProvider(type) ? MoreTypes.asDeclared(type).getTypeArguments().get(0) : boxedType(type, types); return new AutoValue_Key( MoreTypes.equivalence().wrap(keyType), wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), qualifier)); } /** * If {@code type} is a primitive type, returns the boxed equivalent; otherwise returns * {@code type}. */ private static TypeMirror boxedType(TypeMirror type, Types types) { return type.getKind().isPrimitive() ? types.boxedClass(MoreTypes.asPrimitiveType(type)).asType() : type; } @Override public String toString() { String typeQualifiedName = MoreTypes.asTypeElement(type().get()).toString(); return qualifier().isPresent() ? qualifier().get() + "/" + typeQualifiedName : typeQualifiedName; } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java000066400000000000000000000077531365703632600325410ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import com.google.auto.common.MoreTypes; import com.google.common.base.Equivalence; import com.google.common.base.Optional; import com.google.common.collect.ImmutableMap; import java.lang.annotation.Annotation; import java.util.Map; import java.util.Map.Entry; import javax.inject.Provider; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleElementVisitor6; final class Mirrors { private Mirrors() { } static Name getQualifiedName(DeclaredType type) { return type.asElement().accept(new SimpleElementVisitor6() { @Override protected Name defaultAction(Element e, Void p) { throw new AssertionError("DeclaredTypes should be TypeElements"); } @Override public Name visitType(TypeElement e, Void p) { return e.getQualifiedName(); } }, null); } /** {@code true} if {@code type} is a {@link Provider}. */ static boolean isProvider(TypeMirror type) { return MoreTypes.isType(type) && MoreTypes.isTypeOf(Provider.class, type); } /** * Returns an annotation value map with {@link String} keys instead of {@link ExecutableElement} * instances. */ static ImmutableMap simplifyAnnotationValueMap( Map annotationValueMap) { ImmutableMap.Builder builder = ImmutableMap.builder(); for (Entry entry : annotationValueMap.entrySet()) { builder.put(entry.getKey().getSimpleName().toString(), entry.getValue()); } return builder.build(); } /** * Get the {@link AnnotationMirror} for the type {@code annotationType} present on the given * {@link Element} if it exists. */ static Optional getAnnotationMirror(Element element, Class annotationType) { String annotationName = annotationType.getName(); for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { if (getQualifiedName(annotationMirror.getAnnotationType()).contentEquals(annotationName)) { return Optional.of(annotationMirror); } } return Optional.absent(); } /** * Wraps an {@link Optional} of a type in an {@code Optional} of a {@link Equivalence.Wrapper} for * that type. */ // TODO(ronshapiro): this is used in AutoFactory and Dagger, consider moving it into auto-common. static Optional> wrapOptionalInEquivalence( Equivalence equivalence, Optional optional) { return optional.isPresent() ? Optional.of(equivalence.wrap(optional.get())) : Optional.>absent(); } /** * Unwraps an {@link Optional} of a {@link Equivalence.Wrapper} into an {@code Optional} of the * underlying type. */ static Optional unwrapOptionalEquivalence( Optional> wrappedOptional) { return wrappedOptional.isPresent() ? Optional.of(wrappedOptional.get().get()) : Optional.absent(); } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/Parameter.java000066400000000000000000000111431365703632600330100ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.factory.processor.Mirrors.unwrapOptionalEquivalence; import static com.google.auto.factory.processor.Mirrors.wrapOptionalInEquivalence; import static com.google.common.base.Preconditions.checkArgument; import com.google.auto.common.AnnotationMirrors; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.value.AutoValue; import com.google.common.base.Equivalence; import com.google.common.base.Optional; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import java.util.List; import java.util.Set; import javax.inject.Provider; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; /** * Model for a parameter from an {@link com.google.auto.factory.AutoFactory} constructor or * implementation method. */ @AutoValue abstract class Parameter { /** * The original type of the parameter, while {@code key().type()} erases the wrapped {@link * Provider}, if any. */ abstract Equivalence.Wrapper type(); boolean isProvider() { return Mirrors.isProvider(type().get()); } boolean isPrimitive() { return type().get().getKind().isPrimitive(); } /** The name of the parameter. */ abstract String name(); abstract Key key(); abstract Optional> nullableWrapper(); Optional nullable() { return unwrapOptionalEquivalence(nullableWrapper()); } private static Parameter forVariableElement( VariableElement variable, TypeMirror type, Types types) { Optional nullable = Optional.absent(); Iterable annotations = Iterables.concat(variable.getAnnotationMirrors(), type.getAnnotationMirrors()); for (AnnotationMirror annotation : annotations) { if (isNullable(annotation)) { nullable = Optional.of(annotation); break; } } Key key = Key.create(type, annotations, types); return new AutoValue_Parameter( MoreTypes.equivalence().wrap(type), variable.getSimpleName().toString(), key, wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), nullable)); } private static boolean isNullable(AnnotationMirror annotation) { TypeElement annotationType = MoreElements.asType(annotation.getAnnotationType().asElement()); return annotationType.getSimpleName().contentEquals("Nullable") || annotationType .getQualifiedName() .toString() // For NullableDecl and NullableType compatibility annotations .startsWith("org.checkerframework.checker.nullness.compatqual.Nullable"); } static ImmutableSet forParameterList( List variables, List variableTypes, Types types) { checkArgument(variables.size() == variableTypes.size()); ImmutableSet.Builder builder = ImmutableSet.builder(); Set names = Sets.newHashSetWithExpectedSize(variables.size()); for (int i = 0; i < variables.size(); i++) { Parameter parameter = forVariableElement(variables.get(i), variableTypes.get(i), types); checkArgument(names.add(parameter.name())); builder.add(parameter); } ImmutableSet parameters = builder.build(); checkArgument(variables.size() == parameters.size()); return parameters; } static ImmutableSet forParameterList( List variables, Types types) { List variableTypes = Lists.newArrayListWithExpectedSize(variables.size()); for (VariableElement var : variables) { variableTypes.add(var.asType()); } return forParameterList(variables, variableTypes, types); } } ProvidedChecker.java000066400000000000000000000056571365703632600340670ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.common.base.Preconditions.checkArgument; import static javax.tools.Diagnostic.Kind.ERROR; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import javax.annotation.processing.Messager; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementKindVisitor6; final class ProvidedChecker { private final Messager messager; ProvidedChecker(Messager messager) { this.messager = messager; } void checkProvidedParameter(Element element) { checkArgument(isAnnotationPresent(element, Provided.class), "%s not annoated with @Provided", element); element.accept(new ElementKindVisitor6() { @Override protected Void defaultAction(Element e, Void p) { throw new AssertionError("Provided can only be applied to parameters"); } @Override public Void visitVariableAsParameter(final VariableElement providedParameter, Void p) { providedParameter.getEnclosingElement().accept(new ElementKindVisitor6() { @Override protected Void defaultAction(Element e, Void p) { raiseError(providedParameter, "@%s may only be applied to constructor parameters"); return null; } @Override public Void visitExecutableAsConstructor(ExecutableElement constructor, Void p) { if (!(annotatedWithAutoFactory(constructor) || annotatedWithAutoFactory(constructor.getEnclosingElement()))) { raiseError(providedParameter, "@%s may only be applied to constructors requesting an auto-factory"); } return null; } }, p); return null; } }, null); } private void raiseError(VariableElement providedParameter, String messageFormat) { messager.printMessage(ERROR, String.format(messageFormat, Provided.class.getSimpleName()), providedParameter, Mirrors.getAnnotationMirror(providedParameter, Provided.class).get()); } private static boolean annotatedWithAutoFactory(Element e) { return isAnnotationPresent(e, AutoFactory.class); } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/ProviderField.java000066400000000000000000000027641365703632600336370ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.factory.processor.Mirrors.unwrapOptionalEquivalence; import static com.google.auto.factory.processor.Mirrors.wrapOptionalInEquivalence; import com.google.auto.common.AnnotationMirrors; import com.google.auto.value.AutoValue; import com.google.common.base.Equivalence; import com.google.common.base.Optional; import javax.lang.model.element.AnnotationMirror; @AutoValue abstract class ProviderField { abstract String name(); abstract Key key(); abstract Optional> nullableWrapper(); Optional nullable() { return unwrapOptionalEquivalence(nullableWrapper()); } static ProviderField create(String name, Key key, Optional nullable) { return new AutoValue_ProviderField( name, key, wrapOptionalInEquivalence(AnnotationMirrors.equivalence(), nullable)); } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/TypeVariables.java000066400000000000000000000100501365703632600336360ustar00rootroot00000000000000/* * Copyright 2019 Google LLC * * 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.google.auto.factory.processor; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableSet; import java.util.HashSet; import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.IntersectionType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.UnionType; import javax.lang.model.type.WildcardType; import javax.lang.model.util.SimpleTypeVisitor8; final class TypeVariables { private TypeVariables() {} static ImmutableSet getReferencedTypeVariables(TypeMirror type) { checkNotNull(type); return type.accept(ReferencedTypeVariables.INSTANCE, new HashSet<>()); } private static final class ReferencedTypeVariables extends SimpleTypeVisitor8, Set> { private static final ReferencedTypeVariables INSTANCE = new ReferencedTypeVariables(); ReferencedTypeVariables() { super(ImmutableSet.of()); } @Override public ImmutableSet visitArray(ArrayType t, Set visited) { return t.getComponentType().accept(this, visited); } @Override public ImmutableSet visitDeclared( DeclaredType t, Set visited) { if (!visited.add(t.asElement())) { return ImmutableSet.of(); } ImmutableSet.Builder typeVariables = ImmutableSet.builder(); for (TypeMirror typeArgument : t.getTypeArguments()) { typeVariables.addAll(typeArgument.accept(this, visited)); } return typeVariables.build(); } @Override public ImmutableSet visitTypeVariable( TypeVariable t, Set visited) { if (!visited.add(t.asElement())) { return ImmutableSet.of(); } ImmutableSet.Builder typeVariables = ImmutableSet.builder(); typeVariables.add(t); typeVariables.addAll(t.getLowerBound().accept(this, visited)); typeVariables.addAll(t.getUpperBound().accept(this, visited)); return typeVariables.build(); } @Override public ImmutableSet visitUnion( UnionType t, Set visited) { ImmutableSet.Builder typeVariables = ImmutableSet.builder(); for (TypeMirror unionType : t.getAlternatives()) { typeVariables.addAll(unionType.accept(this, visited)); } return typeVariables.build(); } @Override public ImmutableSet visitIntersection( IntersectionType t, Set visited) { ImmutableSet.Builder typeVariables = ImmutableSet.builder(); for (TypeMirror intersectionType : t.getBounds()) { typeVariables.addAll(intersectionType.accept(this, visited)); } return typeVariables.build(); } @Override public ImmutableSet visitWildcard( WildcardType t, Set visited) { ImmutableSet.Builder typeVariables = ImmutableSet.builder(); TypeMirror extendsBound = t.getExtendsBound(); if (extendsBound != null) { typeVariables.addAll(extendsBound.accept(this, visited)); } TypeMirror superBound = t.getSuperBound(); if (superBound != null) { typeVariables.addAll(superBound.accept(this, visited)); } return typeVariables.build(); } } } auto-auto-service-1.0-rc7/factory/src/main/java/com/google/auto/factory/processor/package-info.java000066400000000000000000000013641365703632600334200ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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. */ /** * This package contains the annotation processor that implements the * {@link com.google.auto.factory.AutoFactory} API. */ package com.google.auto.factory.processor;auto-auto-service-1.0-rc7/factory/src/test/000077500000000000000000000000001365703632600206475ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/000077500000000000000000000000001365703632600215705ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/000077500000000000000000000000001365703632600223465ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/google/000077500000000000000000000000001365703632600236225ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/google/auto/000077500000000000000000000000001365703632600245725ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/google/auto/factory/000077500000000000000000000000001365703632600262415ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/google/auto/factory/processor/000077500000000000000000000000001365703632600302605ustar00rootroot00000000000000AutoFactoryDeclarationTest.java000066400000000000000000000024011365703632600363070ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.auto.factory.processor.AutoFactoryDeclaration.Factory.isValidIdentifier; import static com.google.common.truth.Truth.assertThat; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class AutoFactoryDeclarationTest { @Test public void identifiers() { assertThat(isValidIdentifier("String")).isTrue(); assertThat(isValidIdentifier("9CantStartWithNumber")).isFalse(); assertThat(isValidIdentifier("enum")).isFalse(); assertThat(isValidIdentifier("goto")).isFalse(); assertThat(isValidIdentifier("InvalidCharacter!")).isFalse(); } } AutoFactoryProcessorTest.java000066400000000000000000000442451365703632600360550ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/java/com/google/auto/factory/processor/* * Copyright 2013 Google LLC * * 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.google.auto.factory.processor; import static com.google.common.truth.Truth.assertAbout; import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; import static com.google.testing.compile.JavaSourcesSubject.assertThat; import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.collect.ImmutableSet; import com.google.common.io.Resources; import com.google.testing.compile.CompilationRule; import com.google.testing.compile.JavaFileObjects; import java.io.IOException; import java.io.UncheckedIOException; import java.util.Collections; import java.util.List; import javax.lang.model.SourceVersion; import javax.tools.JavaFileObject; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Functional tests for the {@link AutoFactoryProcessor}. */ @RunWith(JUnit4.class) public class AutoFactoryProcessorTest { @Rule public final CompilationRule compilationRule = new CompilationRule(); @Test public void simpleClass() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/SimpleClass.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassFactory.java")); } @Test public void nestedClasses() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/NestedClasses.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources( loadExpectedFile("expected/NestedClasses_SimpleNestedClassFactory.java"), loadExpectedFile("expected/NestedClassCustomNamedFactory.java")); } @Test public void simpleClassNonFinal() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/SimpleClassNonFinal.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassNonFinalFactory.java")); } @Test public void publicClass() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/PublicClass.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/PublicClassFactory.java")); } @Test public void simpleClassCustomName() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/SimpleClassCustomName.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/CustomNamedFactory.java")); } @Test public void simpleClassMixedDeps() { assertAbout(javaSources()) .that( ImmutableSet.of( JavaFileObjects.forResource("good/SimpleClassMixedDeps.java"), JavaFileObjects.forResource("support/AQualifier.java"))) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassMixedDepsFactory.java")); } @Test public void simpleClassPassedDeps() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/SimpleClassPassedDeps.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassPassedDepsFactory.java")); } @Test public void simpleClassProvidedDeps() { assertAbout(javaSources()) .that( ImmutableSet.of( JavaFileObjects.forResource("support/AQualifier.java"), JavaFileObjects.forResource("support/BQualifier.java"), JavaFileObjects.forResource("good/SimpleClassProvidedDeps.java"))) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassProvidedDepsFactory.java")); } @Test public void simpleClassProvidedProviderDeps() { assertAbout(javaSources()) .that( ImmutableSet.of( JavaFileObjects.forResource("support/AQualifier.java"), JavaFileObjects.forResource("support/BQualifier.java"), JavaFileObjects.forResource("good/SimpleClassProvidedProviderDeps.java"))) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassProvidedProviderDepsFactory.java")); } @Test public void constructorAnnotated() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/ConstructorAnnotated.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/ConstructorAnnotatedFactory.java")); } @Test public void constructorAnnotatedNonFinal() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/ConstructorAnnotatedNonFinal.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/ConstructorAnnotatedNonFinalFactory.java")); } @Test public void simpleClassImplementingMarker() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/SimpleClassImplementingMarker.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassImplementingMarkerFactory.java")); } @Test public void simpleClassImplementingSimpleInterface() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/SimpleClassImplementingSimpleInterface.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources( loadExpectedFile("expected/SimpleClassImplementingSimpleInterfaceFactory.java")); } @Test public void mixedDepsImplementingInterfaces() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/MixedDepsImplementingInterfaces.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/MixedDepsImplementingInterfacesFactory.java")); } @Test public void failsWithMixedFinals() { JavaFileObject file = JavaFileObjects.forResource("bad/MixedFinals.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining( "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.") .in(file).onLine(24) .and().withErrorContaining( "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.") .in(file).onLine(27); } @Test public void providedButNoAutoFactory() { JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedButNoAutoFactory.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining( "@Provided may only be applied to constructors requesting an auto-factory") .in(file).onLine(21).atColumn(38); } @Test public void providedOnMethodParameter() { JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedOnMethodParameter.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining( "@Provided may only be applied to constructor parameters") .in(file).onLine(21).atColumn(23); } @Test public void invalidCustomName() { JavaFileObject file = JavaFileObjects.forResource("bad/InvalidCustomName.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining("\"SillyFactory!\" is not a valid Java identifier") .in(file).onLine(20); } @Test public void factoryExtendingAbstractClass() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/FactoryExtendingAbstractClass.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/FactoryExtendingAbstractClassFactory.java")); } @Test public void factoryExtendingAbstractClass_withConstructorParams() { JavaFileObject file = JavaFileObjects.forResource("good/FactoryExtendingAbstractClassWithConstructorParams.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining( "tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory " + "is not a valid supertype for a factory. " + "Factory supertypes must have a no-arg constructor.") .in(file).onLine(21); } @Test public void factoryExtendingAbstractClass_multipleConstructors() { JavaFileObject file = JavaFileObjects.forResource( "good/FactoryExtendingAbstractClassWithMultipleConstructors.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError(); } @Test public void factoryExtendingInterface() { JavaFileObject file = JavaFileObjects.forResource("bad/InterfaceSupertype.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining("java.lang.Runnable is not a valid supertype for a factory. " + "Supertypes must be non-final classes.") .in(file).onLine(20); } @Test public void factoryExtendingEnum() { JavaFileObject file = JavaFileObjects.forResource("bad/EnumSupertype.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining( "java.util.concurrent.TimeUnit is not a valid supertype for a factory. " + "Supertypes must be non-final classes.") .in(file).onLine(21); } @Test public void factoryExtendingFinalClass() { JavaFileObject file = JavaFileObjects.forResource("bad/FinalSupertype.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .failsToCompile() .withErrorContaining("java.lang.Boolean is not a valid supertype for a factory. " + "Supertypes must be non-final classes.") .in(file).onLine(20); } @Test public void factoryImplementingGenericInterfaceExtension() { JavaFileObject file = JavaFileObjects.forResource("good/FactoryImplementingGenericInterfaceExtension.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources( loadExpectedFile("expected/FactoryImplementingGenericInterfaceExtensionFactory.java")); } @Test public void multipleFactoriesImpementingInterface() { JavaFileObject file = JavaFileObjects.forResource("good/MultipleFactoriesImplementingInterface.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources( loadExpectedFile("expected/MultipleFactoriesImplementingInterface_ClassAFactory.java"), loadExpectedFile("expected/MultipleFactoriesImplementingInterface_ClassBFactory.java")); } @Test public void classUsingQualifierWithArgs() { assertAbout(javaSources()) .that( ImmutableSet.of( JavaFileObjects.forResource("support/QualifierWithArgs.java"), JavaFileObjects.forResource("good/ClassUsingQualifierWithArgs.java"))) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/ClassUsingQualifierWithArgsFactory.java")); } @Test public void factoryImplementingInterfaceWhichRedeclaresCreateMethods() { JavaFileObject file = JavaFileObjects.forResource("good/FactoryImplementingCreateMethod.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources( loadExpectedFile("expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java")); } @Test public void nullableParams() { assertAbout(javaSources()) .that( ImmutableSet.of( JavaFileObjects.forResource("good/SimpleClassNullableParameters.java"), JavaFileObjects.forResource("support/AQualifier.java"), JavaFileObjects.forResource("support/BQualifier.java"))) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassNullableParametersFactory.java")); } @Test public void customNullableType() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/CustomNullable.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/CustomNullableFactory.java")); } @Test public void checkerFrameworkNullableType() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/CheckerFrameworkNullable.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/CheckerFrameworkNullableFactory.java")); } @Test public void multipleProvidedParamsWithSameKey() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/MultipleProvidedParamsSameKey.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/MultipleProvidedParamsSameKeyFactory.java")); } @Test public void providerArgumentToCreateMethod() { assertAbout(javaSource()) .that(JavaFileObjects.forResource("good/ProviderArgumentToCreateMethod.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/ProviderArgumentToCreateMethodFactory.java")); } @Test public void multipleFactoriesConflictingParameterNames() { assertThat( JavaFileObjects.forResource("good/MultipleFactoriesConflictingParameterNames.java"), JavaFileObjects.forResource("support/AQualifier.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources( loadExpectedFile("expected/MultipleFactoriesConflictingParameterNamesFactory.java")); } @Test public void factoryVarargs() { assertThat(JavaFileObjects.forResource("good/SimpleClassVarargs.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/SimpleClassVarargsFactory.java")); } @Test public void onlyPrimitives() { assertThat(JavaFileObjects.forResource("good/OnlyPrimitives.java")) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/OnlyPrimitivesFactory.java")); } @Test public void defaultPackage() { JavaFileObject file = JavaFileObjects.forResource("good/DefaultPackage.java"); assertAbout(javaSource()) .that(file) .processedWith(new AutoFactoryProcessor()) .compilesWithoutError() .and() .generatesSources(loadExpectedFile("expected/DefaultPackageFactory.java")); } private JavaFileObject loadExpectedFile(String resourceName) { try { List sourceLines = Resources.readLines(Resources.getResource(resourceName), UTF_8); if (!isJavaxAnnotationProcessingGeneratedAvailable()) { replaceGeneratedImport(sourceLines); } return JavaFileObjects.forSourceLines( resourceName.replace('/', '.').replace(".java", ""), sourceLines); } catch (IOException e) { throw new UncheckedIOException(e); } } private boolean isJavaxAnnotationProcessingGeneratedAvailable() { return SourceVersion.latestSupported().compareTo(SourceVersion.RELEASE_8) > 0; } private static void replaceGeneratedImport(List sourceLines) { int i = 0; int firstImport = Integer.MAX_VALUE; int lastImport = -1; for (String line : sourceLines) { if (line.startsWith("import ") && !line.startsWith("import static ")) { firstImport = Math.min(firstImport, i); lastImport = Math.max(lastImport, i); } i++; } if (lastImport >= 0) { List importLines = sourceLines.subList(firstImport, lastImport + 1); importLines.replaceAll( line -> line.startsWith("import javax.annotation.processing.Generated;") ? "import javax.annotation.Generated;" : line); Collections.sort(importLines); } } } auto-auto-service-1.0-rc7/factory/src/test/resources/000077500000000000000000000000001365703632600226615ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/bad/000077500000000000000000000000001365703632600234075ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/bad/EnumSupertype.java000066400000000000000000000013741365703632600271040ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import java.util.concurrent.TimeUnit; @AutoFactory(extending = TimeUnit.class) final class InterfaceSupertype {} auto-auto-service-1.0-rc7/factory/src/test/resources/bad/FinalSupertype.java000066400000000000000000000013251365703632600272250ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory(extending = Boolean.class) final class InterfaceSupertype {} auto-auto-service-1.0-rc7/factory/src/test/resources/bad/InterfaceSupertype.java000066400000000000000000000013261365703632600300750ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory(extending = Runnable.class) final class InterfaceSupertype {} auto-auto-service-1.0-rc7/factory/src/test/resources/bad/InvalidCustomName.java000066400000000000000000000013271365703632600276370ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory(className = "SillyFactory!") final class InvalidCustomName { } auto-auto-service-1.0-rc7/factory/src/test/resources/bad/MixedFinals.java000066400000000000000000000015631365703632600264620ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; final class MixedFinals { @AutoFactory(allowSubclasses = false) MixedFinals() {} @AutoFactory(allowSubclasses = true) MixedFinals(String s) {} @AutoFactory(allowSubclasses = true) MixedFinals(String s, Integer i) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/bad/ProvidedButNoAutoFactory.java000066400000000000000000000013551365703632600311630ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.Provided; final class ProvidedButNoAutoFactory { ProvidedButNoAutoFactory(Object a, @Provided Object b) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/bad/ProvidedOnMethodParameter.java000066400000000000000000000013371365703632600313310ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.Provided; final class ProvidedOnMethodParameter { void blah(Object a, @Provided Object b) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/000077500000000000000000000000001365703632600244625ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/CheckerFrameworkNullableFactory.java000066400000000000000000000035141365703632600335610ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.checkerframework.checker.nullness.compatqual.NullableType; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class CheckerFrameworkNullableFactory { private final Provider java_lang_StringProvider; @Inject CheckerFrameworkNullableFactory( Provider java_lang_StringProvider) { this.java_lang_StringProvider = checkNotNull(java_lang_StringProvider, 1); } CheckerFrameworkNullable create( @NullableDecl String nullableDecl, @NullableType String nullableType) { return new CheckerFrameworkNullable( nullableDecl, java_lang_StringProvider.get(), nullableType, java_lang_StringProvider.get()); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } ClassUsingQualifierWithArgsFactory.java000066400000000000000000000031111365703632600341600ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2015 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class ClassUsingQualifierWithArgsFactory { private final Provider providedDepAProvider; @Inject ClassUsingQualifierWithArgsFactory( @QualifierWithArgs(name="Fred", count=3) Provider providedDepAProvider) { this.providedDepAProvider = checkNotNull(providedDepAProvider, 1); } ClassUsingQualifierWithArgs create() { return new ClassUsingQualifierWithArgs(checkNotNull(providedDepAProvider.get(), 1)); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/ConstructorAnnotatedFactory.java000066400000000000000000000034071365703632600330440ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class ConstructorAnnotatedFactory { private final Provider objProvider; @Inject ConstructorAnnotatedFactory(Provider objProvider) { this.objProvider = checkNotNull(objProvider, 1); } ConstructorAnnotated create() { return new ConstructorAnnotated(); } ConstructorAnnotated create(String s) { return new ConstructorAnnotated(checkNotNull(s, 1)); } ConstructorAnnotated create(int i) { return new ConstructorAnnotated(checkNotNull(objProvider.get(), 1), i); } ConstructorAnnotated create(char c) { return new ConstructorAnnotated(checkNotNull(objProvider.get(), 1), c); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } ConstructorAnnotatedNonFinalFactory.java000066400000000000000000000035211365703632600344070ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) class ConstructorAnnotatedNonFinalFactory { private final Provider objProvider; @Inject ConstructorAnnotatedNonFinalFactory(Provider objProvider) { this.objProvider = checkNotNull(objProvider, 1); } ConstructorAnnotatedNonFinal create() { return new ConstructorAnnotatedNonFinal(); } ConstructorAnnotatedNonFinal create(String s) { return new ConstructorAnnotatedNonFinal(checkNotNull(s, 1)); } ConstructorAnnotatedNonFinal create(int i) { return new ConstructorAnnotatedNonFinal(checkNotNull(objProvider.get(), 1), i); } ConstructorAnnotatedNonFinal create(char c) { return new ConstructorAnnotatedNonFinal(checkNotNull(objProvider.get(), 1), c); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/CustomNamedFactory.java000066400000000000000000000017241365703632600311000ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class CustomNamedFactory { @Inject CustomNamedFactory() {} SimpleClassCustomName create() { return new SimpleClassCustomName(); } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/CustomNullableFactory.java000066400000000000000000000027471365703632600316200ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class CustomNullableFactory { private final Provider objectProvider; @Inject CustomNullableFactory(Provider objectProvider) { this.objectProvider = checkNotNull(objectProvider, 1); } CustomNullable create(@CustomNullable.Nullable String string) { return new CustomNullable(string, objectProvider.get()); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/DefaultPackageFactory.java000066400000000000000000000017321365703632600315200ustar00rootroot00000000000000/* * Copyright 2019 Google LLC * * 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. */ import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) public final class DefaultPackageFactory { @Inject public DefaultPackageFactory() {} public DefaultPackage create() { return new DefaultPackage(); } } FactoryExtendingAbstractClassFactory.java000066400000000000000000000022351365703632600345270ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class FactoryExtendingAbstractClassFactory extends FactoryExtendingAbstractClass.AbstractFactory { @Inject FactoryExtendingAbstractClassFactory() {} FactoryExtendingAbstractClass create() { return new FactoryExtendingAbstractClass(); } @Override public FactoryExtendingAbstractClass newInstance() { return create(); } } FactoryImplementingCreateMethod_ConcreteClassFactory.java000066400000000000000000000041001365703632600376460ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2016 Google LLC * * 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 tests; import java.util.List; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class FactoryImplementingCreateMethod_ConcreteClassFactory implements FactoryImplementingCreateMethod.FactoryInterfaceWithCreateMethod { @Inject FactoryImplementingCreateMethod_ConcreteClassFactory() {} @Override public FactoryImplementingCreateMethod.ConcreteClass create() { return new FactoryImplementingCreateMethod.ConcreteClass(); } @Override public FactoryImplementingCreateMethod.ConcreteClass create(int aDifferentArgumentName) { return new FactoryImplementingCreateMethod.ConcreteClass(aDifferentArgumentName); } @Override public FactoryImplementingCreateMethod.ConcreteClass create(List genericWithDifferentArgumentName) { return new FactoryImplementingCreateMethod.ConcreteClass( checkNotNull(genericWithDifferentArgumentName, 1)); } FactoryImplementingCreateMethod.ConcreteClass create(int a, boolean b) { return new FactoryImplementingCreateMethod.ConcreteClass(a, b); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } FactoryImplementingGenericInterfaceExtensionFactory.java000066400000000000000000000034041365703632600375720ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class FactoryImplementingGenericInterfaceExtensionFactory implements FactoryImplementingGenericInterfaceExtension.MyFactory { private final Provider sProvider; @Inject FactoryImplementingGenericInterfaceExtensionFactory(Provider sProvider) { this.sProvider = checkNotNull(sProvider, 1); } FactoryImplementingGenericInterfaceExtension create(Integer i) { return new FactoryImplementingGenericInterfaceExtension( checkNotNull(sProvider.get(), 1), checkNotNull(i, 2)); } @Override public FactoryImplementingGenericInterfaceExtension make(Integer arg) { return create(arg); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } MixedDepsImplementingInterfacesFactory.java000066400000000000000000000040271365703632600350400ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; /** * @author Gregory Kick */ @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class MixedDepsImplementingInterfacesFactory implements MixedDepsImplementingInterfaces.FromInt, MixedDepsImplementingInterfaces.FromObject, MixedDepsImplementingInterfaces.MarkerA, MixedDepsImplementingInterfaces.MarkerB { private final Provider sProvider; @Inject MixedDepsImplementingInterfacesFactory(Provider sProvider) { this.sProvider = checkNotNull(sProvider, 1); } MixedDepsImplementingInterfaces create(int i) { return new MixedDepsImplementingInterfaces(checkNotNull(sProvider.get(), 1), i); } MixedDepsImplementingInterfaces create(Object o) { return new MixedDepsImplementingInterfaces(checkNotNull(o, 1)); } @Override public MixedDepsImplementingInterfaces fromInt(int i) { return create(i); } @Override public MixedDepsImplementingInterfaces fromObject(Object o) { return create(o); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } MultipleFactoriesConflictingParameterNamesFactory.java000066400000000000000000000051621365703632600372420ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class MultipleFactoriesConflictingParameterNamesFactory { private final Provider stringProvider; private final Provider java_lang_ObjectProvider; private final Provider stringProvider2; private final Provider _tests_AQualifier_java_lang_ObjectProvider; @Inject MultipleFactoriesConflictingParameterNamesFactory( Provider stringProvider, Provider java_lang_ObjectProvider, @AQualifier Provider stringProvider2, @AQualifier Provider _tests_AQualifier_java_lang_ObjectProvider) { this.stringProvider = checkNotNull(stringProvider, 1); this.java_lang_ObjectProvider = checkNotNull(java_lang_ObjectProvider, 2); this.stringProvider2 = checkNotNull(stringProvider2, 3); this._tests_AQualifier_java_lang_ObjectProvider = checkNotNull(_tests_AQualifier_java_lang_ObjectProvider, 4); } MultipleFactoriesConflictingParameterNames create(Object unused) { return new MultipleFactoriesConflictingParameterNames( checkNotNull(stringProvider.get(), 1), checkNotNull(java_lang_ObjectProvider.get(), 2), java_lang_ObjectProvider, checkNotNull(unused, 4)); } MultipleFactoriesConflictingParameterNames create() { return new MultipleFactoriesConflictingParameterNames( checkNotNull(stringProvider2.get(), 1), checkNotNull(_tests_AQualifier_java_lang_ObjectProvider.get(), 2), _tests_AQualifier_java_lang_ObjectProvider); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } MultipleFactoriesImplementingInterface_ClassAFactory.java000066400000000000000000000024071365703632600376540ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2015 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class MultipleFactoriesImplementingInterface_ClassAFactory implements MultipleFactoriesImplementingInterface.Base.Factory { @Inject MultipleFactoriesImplementingInterface_ClassAFactory() {} MultipleFactoriesImplementingInterface.ClassA create() { return new MultipleFactoriesImplementingInterface.ClassA(); } @Override public MultipleFactoriesImplementingInterface.ClassA abstractNonDefaultCreate() { return create(); } } MultipleFactoriesImplementingInterface_ClassBFactory.java000066400000000000000000000024071365703632600376550ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2015 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class MultipleFactoriesImplementingInterface_ClassBFactory implements MultipleFactoriesImplementingInterface.Base.Factory { @Inject MultipleFactoriesImplementingInterface_ClassBFactory() {} MultipleFactoriesImplementingInterface.ClassB create() { return new MultipleFactoriesImplementingInterface.ClassB(); } @Override public MultipleFactoriesImplementingInterface.ClassB abstractNonDefaultCreate() { return create(); } } MultipleProvidedParamsSameKeyFactory.java000066400000000000000000000033451365703632600345160ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class MultipleProvidedParamsSameKeyFactory { private final Provider java_lang_StringProvider; @Inject MultipleProvidedParamsSameKeyFactory(Provider java_lang_StringProvider) { this.java_lang_StringProvider = checkNotNull(java_lang_StringProvider, 1); } MultipleProvidedParamsSameKey create() { return new MultipleProvidedParamsSameKey( checkNotNull(java_lang_StringProvider.get(), 1), checkNotNull(java_lang_StringProvider.get(), 2), java_lang_StringProvider.get(), java_lang_StringProvider, java_lang_StringProvider); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/NestedClassCustomNamedFactory.java000066400000000000000000000020401365703632600332210ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class NestedClassCustomNamedFactory { @Inject NestedClassCustomNamedFactory() {} NestedClasses.SimpleNestedClassWithCustomFactory create() { return new NestedClasses.SimpleNestedClassWithCustomFactory(); } } NestedClasses_SimpleNestedClassFactory.java000066400000000000000000000020201365703632600347720ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class NestedClasses_SimpleNestedClassFactory { @Inject NestedClasses_SimpleNestedClassFactory() {} NestedClasses.SimpleNestedClass create() { return new NestedClasses.SimpleNestedClass(); } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/OnlyPrimitivesFactory.java000066400000000000000000000017351365703632600316600ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class OnlyPrimitivesFactory { @Inject OnlyPrimitivesFactory() {} OnlyPrimitives create(int i, long l) { return new OnlyPrimitives(i, l); } } ProviderArgumentToCreateMethodFactory.java000066400000000000000000000031311365703632600346610ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class ProviderArgumentToCreateMethodFactory implements ProviderArgumentToCreateMethod.CustomCreator{ @Inject ProviderArgumentToCreateMethodFactory() {} ProviderArgumentToCreateMethod create(Provider stringProvider) { return new ProviderArgumentToCreateMethod(checkNotNull(stringProvider, 1)); } @Override public ProviderArgumentToCreateMethod newInstance(Provider stringProvider) { return create(stringProvider); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/PublicClassFactory.java000066400000000000000000000017251365703632600310660ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) public final class PublicClassFactory { @Inject public PublicClassFactory() {} public PublicClass create() { return new PublicClass(); } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/SimpleClassFactory.java000066400000000000000000000017001365703632600310720ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassFactory { @Inject SimpleClassFactory() {} SimpleClass create() { return new SimpleClass(); } } SimpleClassImplementingMarkerFactory.java000066400000000000000000000020771365703632600345360ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import java.util.RandomAccess; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassImplementingMarkerFactory implements RandomAccess { @Inject SimpleClassImplementingMarkerFactory() {} SimpleClassImplementingMarker create() { return new SimpleClassImplementingMarker(); } } SimpleClassImplementingSimpleInterfaceFactory.java000066400000000000000000000023261365703632600363640ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassImplementingSimpleInterfaceFactory implements SimpleClassImplementingSimpleInterface.SimpleInterface { @Inject SimpleClassImplementingSimpleInterfaceFactory() {} SimpleClassImplementingSimpleInterface create() { return new SimpleClassImplementingSimpleInterface(); } @Override public SimpleClassImplementingSimpleInterface newInstance() { return create(); } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/SimpleClassMixedDepsFactory.java000066400000000000000000000030731365703632600327020ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassMixedDepsFactory { private final Provider providedDepAProvider; @Inject SimpleClassMixedDepsFactory( @AQualifier Provider providedDepAProvider) { this.providedDepAProvider = checkNotNull(providedDepAProvider, 1); } SimpleClassMixedDeps create(String depB) { return new SimpleClassMixedDeps( checkNotNull(providedDepAProvider.get(), 1), checkNotNull(depB, 2)); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/SimpleClassNonFinalFactory.java000066400000000000000000000017321365703632600325240ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) class SimpleClassNonFinalFactory { @Inject SimpleClassNonFinalFactory() {} SimpleClassNonFinal create() { return new SimpleClassNonFinal(); } } SimpleClassNullableParametersFactory.java000066400000000000000000000037621365703632600345300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.Nullable; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassNullableParametersFactory { private final Provider providedNullableProvider; private final Provider providedQualifiedNullableProvider; @Inject SimpleClassNullableParametersFactory( Provider providedNullableProvider, @BQualifier Provider providedQualifiedNullableProvider) { this.providedNullableProvider = checkNotNull(providedNullableProvider, 1); this.providedQualifiedNullableProvider = checkNotNull(providedQualifiedNullableProvider, 2); } SimpleClassNullableParameters create( @Nullable String nullable, @Nullable @AQualifier String qualifiedNullable) { return new SimpleClassNullableParameters( nullable, qualifiedNullable, providedNullableProvider.get(), providedQualifiedNullableProvider.get()); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/SimpleClassPassedDepsFactory.java000066400000000000000000000025251365703632600330540ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassPassedDepsFactory { @Inject SimpleClassPassedDepsFactory() {} SimpleClassPassedDeps create(String depA, String depB) { return new SimpleClassPassedDeps(checkNotNull(depA, 1), checkNotNull(depB, 2)); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/SimpleClassProvidedDepsFactory.java000066400000000000000000000044531365703632600334130ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassProvidedDepsFactory { private final Provider providedPrimitiveAProvider; private final Provider providedPrimitiveBProvider; private final Provider providedDepAProvider; private final Provider providedDepBProvider; @Inject SimpleClassProvidedDepsFactory( @AQualifier Provider providedPrimitiveAProvider, @BQualifier Provider providedPrimitiveBProvider, @AQualifier Provider providedDepAProvider, @BQualifier Provider providedDepBProvider) { this.providedPrimitiveAProvider = checkNotNull(providedPrimitiveAProvider, 1); this.providedPrimitiveBProvider = checkNotNull(providedPrimitiveBProvider, 2); this.providedDepAProvider = checkNotNull(providedDepAProvider, 3); this.providedDepBProvider = checkNotNull(providedDepBProvider, 4); } SimpleClassProvidedDeps create() { return new SimpleClassProvidedDeps( checkNotNull(providedPrimitiveAProvider.get(), 1), checkNotNull(providedPrimitiveBProvider.get(), 2), checkNotNull(providedDepAProvider.get(), 3), checkNotNull(providedDepBProvider.get(), 4)); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } SimpleClassProvidedProviderDepsFactory.java000066400000000000000000000033641365703632600350470ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/expected/* * Copyright 2013 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; import javax.inject.Provider; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassProvidedProviderDepsFactory { private final Provider providedDepAProvider; private final Provider providedDepBProvider; @Inject SimpleClassProvidedProviderDepsFactory( @AQualifier Provider providedDepAProvider, @BQualifier Provider providedDepBProvider) { this.providedDepAProvider = checkNotNull(providedDepAProvider, 1); this.providedDepBProvider = checkNotNull(providedDepBProvider, 2); } SimpleClassProvidedProviderDeps create() { return new SimpleClassProvidedProviderDeps(providedDepAProvider, providedDepBProvider); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/expected/SimpleClassVarargsFactory.java000066400000000000000000000026731365703632600324320ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import javax.annotation.processing.Generated; import javax.inject.Inject; @Generated( value = "com.google.auto.factory.processor.AutoFactoryProcessor", comments = "https://github.com/google/auto/tree/master/factory" ) final class SimpleClassVarargsFactory implements SimpleClassVarargs.InterfaceWithVarargs { @Inject SimpleClassVarargsFactory() {} SimpleClassVarargs create(String... args) { return new SimpleClassVarargs(checkNotNull(args, 1)); } @Override public SimpleClassVarargs build(String... args) { return create(args); } private static T checkNotNull(T reference, int argumentIndex) { if (reference == null) { throw new NullPointerException( "@AutoFactory method argument is null but is not marked @Nullable. Argument index: " + argumentIndex); } return reference; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/000077500000000000000000000000001365703632600236115ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/CheckerFrameworkNullable.java000066400000000000000000000021361365703632600313570ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.checkerframework.checker.nullness.compatqual.NullableType; @AutoFactory final class CheckerFrameworkNullable { CheckerFrameworkNullable( @NullableDecl String nullableDecl, @Provided @NullableDecl String providedNullableDecl, @NullableType String nullableType, @Provided @NullableType String providedNullableType) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/ClassUsingQualifierWithArgs.java000066400000000000000000000017501365703632600320450ustar00rootroot00000000000000/* * Copyright 2015 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; /** * @author Justine Tunney */ @AutoFactory @SuppressWarnings("unused") final class ClassUsingQualifierWithArgs { private final String providedDepA; ClassUsingQualifierWithArgs( @Provided @QualifierWithArgs(name = "Fred", count = 3) String providedDepA) { this.providedDepA = providedDepA; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/ConstructorAnnotated.java000066400000000000000000000017421365703632600306430ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; final class ConstructorAnnotated { @AutoFactory ConstructorAnnotated() {} ConstructorAnnotated(Object obj) {} @AutoFactory ConstructorAnnotated(String s) {} @AutoFactory ConstructorAnnotated(@Provided Object obj, int i) {} @AutoFactory ConstructorAnnotated(@Provided Object obj, char c) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/ConstructorAnnotatedNonFinal.java000066400000000000000000000021621365703632600322650ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; final class ConstructorAnnotatedNonFinal { @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal() {} ConstructorAnnotatedNonFinal(Object obj) {} @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal(String s) {} @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal(@Provided Object obj, int i) {} @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal(@Provided Object obj, char c) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/CustomNullable.java000066400000000000000000000017461365703632600274150ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; @AutoFactory final class CustomNullable { private final String string; private final Object object; CustomNullable( @CustomNullable.Nullable String string, @CustomNullable.Nullable @Provided Object object) { this.string = string; this.object = object; } @interface Nullable {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/DefaultPackage.java000066400000000000000000000012561365703632600273200ustar00rootroot00000000000000/* * Copyright 2019 Google LLC * * 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. */ import com.google.auto.factory.AutoFactory; @AutoFactory public final class DefaultPackage {} auto-auto-service-1.0-rc7/factory/src/test/resources/good/FactoryExtendingAbstractClass.java000066400000000000000000000016151365703632600324060ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import tests.FactoryExtendingAbstractClass.AbstractFactory; @AutoFactory(extending = AbstractFactory.class) final class FactoryExtendingAbstractClass { static abstract class AbstractFactory { abstract FactoryExtendingAbstractClass newInstance(); } } FactoryExtendingAbstractClassWithConstructorParams.java000066400000000000000000000017761365703632600366050ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory; @AutoFactory(extending = AbstractFactory.class) final class FactoryExtendingAbstractClassWithConstructorParams { static abstract class AbstractFactory { protected AbstractFactory(Object obj) {} abstract FactoryExtendingAbstractClassWithConstructorParams newInstance(); } } FactoryExtendingAbstractClassWithMultipleConstructors.java000066400000000000000000000020521365703632600373240ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import tests.FactoryExtendingAbstractClassWithMultipleConstructors.AbstractFactory; @AutoFactory(extending = AbstractFactory.class) final class FactoryExtendingAbstractClassWithMultipleConstructors { static abstract class AbstractFactory { protected AbstractFactory(Object obj) {} protected AbstractFactory() {} abstract FactoryExtendingAbstractClassWithMultipleConstructors newInstance(); } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/FactoryImplementingCreateMethod.java000066400000000000000000000026721365703632600327300ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import java.util.List; final class FactoryImplementingCreateMethod { interface Interface {} interface FactoryInterfaceWithCreateMethod { Interface create(); Interface create(int a); Interface create(List generic); } @AutoFactory(implementing = FactoryInterfaceWithCreateMethod.class) static class ConcreteClass implements Interface { // Will generate a method with a signature that matches one from the interface. ConcreteClass() {} // Will generate a method with a signature that matches one from the interface. ConcreteClass(int aDifferentArgumentName) {} // Will generate a method with a signature that matches one from the interface. ConcreteClass(List genericWithDifferentArgumentName) {} ConcreteClass(int a, boolean b) {} } } FactoryImplementingGenericInterfaceExtension.java000066400000000000000000000020251365703632600353670ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; class FactoryImplementingGenericInterfaceExtension { @AutoFactory(implementing = MyFactory.class) FactoryImplementingGenericInterfaceExtension(@Provided String s, Integer i) {} interface MyFactory extends GenericFactory {} interface GenericFactory { T make(S arg); } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/MixedDepsImplementingInterfaces.java000066400000000000000000000023201365703632600327100ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; /** * @author Gregory Kick */ final class MixedDepsImplementingInterfaces { @AutoFactory(implementing = {FromInt.class, MarkerA.class}) MixedDepsImplementingInterfaces(@Provided String s, int i) {} @AutoFactory(implementing = {FromObject.class, MarkerB.class}) MixedDepsImplementingInterfaces(Object o) {} interface FromInt { MixedDepsImplementingInterfaces fromInt(int i); } interface FromObject { MixedDepsImplementingInterfaces fromObject(Object o); } interface MarkerA {} interface MarkerB {} } MultipleFactoriesConflictingParameterNames.java000066400000000000000000000026051365703632600350400ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import javax.inject.Provider; class MultipleFactoriesConflictingParameterNames { @AutoFactory MultipleFactoriesConflictingParameterNames( @Provided String string, @Provided Object duplicatedKey_nameDoesntMatter, @Provided Provider duplicatedKeyProvider_nameDoesntMatter, // used to disambiguate with the second constructor since qualifiers aren't part of the type // system Object unused) {} @AutoFactory MultipleFactoriesConflictingParameterNames( @Provided @AQualifier String string, @Provided @AQualifier Object qualifiedDuplicatedKey_nameDoesntMatter, @Provided @AQualifier Provider qualifiedDuplicatedKeyProvider_nameDoesntMatter) {} } MultipleFactoriesImplementingInterface.java000066400000000000000000000017631365703632600342310ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/* * Copyright 2015 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; class MultipleFactoriesImplementingInterface { static interface Base { static interface Factory { public abstract Base abstractNonDefaultCreate(); } } @AutoFactory(implementing = Base.Factory.class) static class ClassA implements Base { } @AutoFactory(implementing = Base.Factory.class) static class ClassB implements Base {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/MultipleProvidedParamsSameKey.java000066400000000000000000000025241365703632600323720ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import javax.annotation.Nullable; import javax.inject.Provider; @AutoFactory final class MultipleProvidedParamsSameKey { private final String one; private final String two; private final String three; private final Provider providerOne; private final Provider providerTwo; public MultipleProvidedParamsSameKey( @Provided String one, @Provided String two, @Nullable @Provided String three, @Provided Provider providerOne, @Provided Provider providerTwo) { this.one = one; this.two = two; this.three = three; this.providerOne = providerOne; this.providerTwo = providerTwo; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/NestedClasses.java000066400000000000000000000015331365703632600272160ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; final class NestedClasses { @AutoFactory static final class SimpleNestedClass {} @AutoFactory(className = "NestedClassCustomNamedFactory") static final class SimpleNestedClassWithCustomFactory {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/OnlyPrimitives.java000066400000000000000000000013651365703632600274560ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; /** @author Ron Shapiro */ @AutoFactory final class OnlyPrimitives { OnlyPrimitives(int i, long l) {} } auto-auto-service-1.0-rc7/factory/src/test/resources/good/ProviderArgumentToCreateMethod.java000066400000000000000000000020761365703632600325460ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import javax.inject.Provider; @AutoFactory(implementing = ProviderArgumentToCreateMethod.CustomCreator.class) final class ProviderArgumentToCreateMethod { private final Provider stringProvider; ProviderArgumentToCreateMethod(Provider stringProvider) { this.stringProvider = stringProvider; } interface CustomCreator { ProviderArgumentToCreateMethod newInstance(Provider stringProvider); } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/PublicClass.java000066400000000000000000000012721365703632600266620ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory public final class PublicClass {} auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClass.java000066400000000000000000000012631365703632600266750ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory final class SimpleClass {} auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassCustomName.java000066400000000000000000000013371365703632600306730ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory(className = "CustomNamedFactory") final class SimpleClassCustomName {} auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassImplementingMarker.java000066400000000000000000000013771365703632600324160ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import java.util.RandomAccess; @AutoFactory(implementing = RandomAccess.class) class SimpleClassImplementingMarker { } SimpleClassImplementingSimpleInterface.java000066400000000000000000000016261365703632600341650ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/good/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import tests.SimpleClassImplementingSimpleInterface.SimpleInterface; @AutoFactory(implementing = SimpleInterface.class) final class SimpleClassImplementingSimpleInterface { interface SimpleInterface { SimpleClassImplementingSimpleInterface newInstance(); } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassMixedDeps.java000066400000000000000000000017661365703632600305100ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; /** * @author Gregory Kick */ @AutoFactory @SuppressWarnings("unused") final class SimpleClassMixedDeps { private final String providedDepA; private final String depB; SimpleClassMixedDeps(@Provided @AQualifier String providedDepA, String depB) { this.providedDepA = providedDepA; this.depB = depB; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassNonFinal.java000066400000000000000000000013231365703632600303170ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory(allowSubclasses = true) final class SimpleClassNonFinal {} auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassNullableParameters.java000066400000000000000000000030241365703632600323750ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import javax.annotation.Nullable; @AutoFactory @SuppressWarnings("unused") final class SimpleClassNullableParameters { @Nullable private final String nullable; @Nullable private final String qualifiedNullable; @Nullable private final String providedNullable; @Nullable private final String providedQualifiedNullable; // TODO(ronshapiro): with Java 8, test Provider<@Nullable String> parameters and provider fields SimpleClassNullableParameters( @Nullable String nullable, @Nullable @AQualifier String qualifiedNullable, @Nullable @Provided String providedNullable, @Nullable @Provided @BQualifier String providedQualifiedNullable) { this.nullable = nullable; this.qualifiedNullable = qualifiedNullable; this.providedNullable = providedNullable; this.providedQualifiedNullable = providedQualifiedNullable; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassPassedDeps.java000066400000000000000000000016311365703632600306500ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; /** * @author Gregory Kick */ @AutoFactory @SuppressWarnings("unused") final class SimpleClassPassedDeps { private final String depA; private final String depB; SimpleClassPassedDeps(String depA, String depB) { this.depA = depA; this.depB = depB; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassProvidedDeps.java000066400000000000000000000025301365703632600312040ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; /** * @author Gregory Kick */ @AutoFactory @SuppressWarnings("unused") final class SimpleClassProvidedDeps { private final int providedPrimitiveA; private final int providedPrimitiveB; private final String providedDepA; private final String providedDepB; SimpleClassProvidedDeps( @Provided @AQualifier int providedPrimitiveA, @Provided @BQualifier int providedPrimitiveB, @Provided @AQualifier String providedDepA, @Provided @BQualifier String providedDepB) { this.providedPrimitiveA = providedPrimitiveA; this.providedPrimitiveB = providedPrimitiveB; this.providedDepA = providedDepA; this.providedDepB = providedDepB; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassProvidedProviderDeps.java000066400000000000000000000022171365703632600327210ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; import com.google.auto.factory.Provided; import javax.inject.Provider; /** * @author Gregory Kick */ @AutoFactory @SuppressWarnings("unused") final class SimpleClassProvidedProviderDeps { private final Provider providedDepA; private final Provider providedDepB; SimpleClassProvidedProviderDeps( @Provided @AQualifier Provider providedDepA, @Provided @BQualifier Provider providedDepB) { this.providedDepA = providedDepA; this.providedDepB = providedDepB; } } auto-auto-service-1.0-rc7/factory/src/test/resources/good/SimpleClassVarargs.java000066400000000000000000000016601365703632600302240ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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 tests; import com.google.auto.factory.AutoFactory; @AutoFactory(implementing = SimpleClassVarargs.InterfaceWithVarargs.class) final class SimpleClassVarargs { private final String[] args; SimpleClassVarargs(String... args) { this.args = args; } interface InterfaceWithVarargs { SimpleClassVarargs build(String... args); } } auto-auto-service-1.0-rc7/factory/src/test/resources/support/000077500000000000000000000000001365703632600243755ustar00rootroot00000000000000auto-auto-service-1.0-rc7/factory/src/test/resources/support/AQualifier.java000066400000000000000000000015561365703632600272710ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import javax.inject.Qualifier; /** * @author Gregory Kick */ @Documented @Qualifier @Retention(RUNTIME) @interface AQualifier {} auto-auto-service-1.0-rc7/factory/src/test/resources/support/BQualifier.java000066400000000000000000000015561365703632600272720ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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 tests; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import javax.inject.Qualifier; /** * @author Gregory Kick */ @Documented @Qualifier @Retention(RUNTIME) @interface BQualifier {} auto-auto-service-1.0-rc7/factory/src/test/resources/support/QualifierWithArgs.java000066400000000000000000000016301365703632600306320ustar00rootroot00000000000000/* * Copyright 2015 Google LLC * * 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 tests; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import javax.inject.Qualifier; /** * @author Justine Tunney */ @Documented @Qualifier @Retention(RUNTIME) @interface QualifierWithArgs { String name(); int count(); } auto-auto-service-1.0-rc7/service/000077500000000000000000000000001365703632600170725ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/README.md000066400000000000000000000064141365703632600203560ustar00rootroot00000000000000# AutoService A configuration/metadata generator for java.util.ServiceLoader-style service providers ## AutoWhat‽ [Java][java] annotation processors and other systems use [java.util.ServiceLoader][sl] to register implementations of well-known types using META-INF metadata. However, it is easy for a developer to forget to update or correctly specify the service descriptors. \ AutoService generates this metadata for the developer, for any class annotated with `@AutoService`, avoiding typos, providing resistance to errors from refactoring, etc. ## Example Say you have: ```java package foo.bar; import javax.annotation.processing.Processor; @AutoService(Processor.class) final class MyProcessor implements Processor { // … } ``` AutoService will generate the file `META-INF/services/javax.annotation.processing.Processor` in the output classes folder. The file will contain: ``` foo.bar.MyProcessor ``` In the case of javax.annotation.processing.Processor, if this metadata file is included in a jar, and that jar is on javac's classpath, then `javac` will automatically load it, and include it in its normal annotation processing environment. Other users of java.util.ServiceLoader may use the infrastructure to different ends, but this metadata will provide auto-loading appropriately. ## Getting Started You will need `auto-service-annotations-${version}.jar` in your compile-time classpath, and you will need `auto-service-${version}.jar` in your annotation-processor classpath. In Maven, you can write: ```xml com.google.auto.service auto-service-annotations ${auto-service.version} ... maven-compiler-plugin com.google.auto.service auto-service ${auto-service.version} ``` Alternatively, you can include the processor itself (which transitively depends on the annotation) in your compile-time classpath. (However, note that doing so may pull unnecessary classes into your runtime classpath.) ```xml com.google.auto.service auto-service ${version} true ``` ## License Copyright 2013 Google LLC 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. [java]: https://en.wikipedia.org/wiki/Java_(programming_language) [sl]: http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html auto-auto-service-1.0-rc7/service/annotations/000077500000000000000000000000001365703632600214275ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/pom.xml000066400000000000000000000043651365703632600227540ustar00rootroot00000000000000 4.0.0 com.google.auto.service auto-service-aggregator 1.0-rc7 com.google.auto.service auto-service-annotations 1.0-rc7 AutoService Provider-configuration files for ServiceLoader. https://github.com/google/auto/tree/master/service http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD maven-jar-plugin com.google.auto.service maven-compiler-plugin -proc:none 1.8 1.8 auto-auto-service-1.0-rc7/service/annotations/src/000077500000000000000000000000001365703632600222165ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/000077500000000000000000000000001365703632600231425ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/java/000077500000000000000000000000001365703632600240635ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/java/com/000077500000000000000000000000001365703632600246415ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/java/com/google/000077500000000000000000000000001365703632600261155ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/java/com/google/auto/000077500000000000000000000000001365703632600270655ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/java/com/google/auto/service/000077500000000000000000000000001365703632600305255ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/annotations/src/main/java/com/google/auto/service/AutoService.java000066400000000000000000000030511365703632600336200ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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.google.auto.service; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.CLASS; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * An annotation for service providers as described in {@link java.util.ServiceLoader}. The * annotation processor generates the configuration files that allow the annotated class to be * loaded with {@link java.util.ServiceLoader#load(Class)}. * *

The annotated class must conform to the service provider specification. Specifically, it must: * *

    *
  • be a non-inner, non-anonymous, concrete class *
  • have a publicly accessible no-arg constructor *
  • implement the interface type returned by {@code value()} *
*/ @Documented @Retention(CLASS) @Target(TYPE) public @interface AutoService { /** Returns the interfaces implemented by this service provider. */ Class[] value(); } auto-auto-service-1.0-rc7/service/pom.xml000066400000000000000000000102361365703632600204110ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.google.auto.service auto-service-aggregator 1.0-rc7 AutoService Aggregator Aggregator POM for @AutoService pom https://github.com/google/auto/tree/master/service UTF-8 1.8 27.0.1-jre 1.0.1 http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD GitHub Issues http://github.com/google/auto/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt Google LLC http://www.google.com annotations processor com.google.guava guava ${guava.version} com.google.guava guava-gwt ${guava.version} com.google.testing.compile compile-testing 0.18 com.google.truth truth ${truth.version} junit junit 4.12 org.apache.maven.plugins maven-compiler-plugin 3.7.0 ${java.version} ${java.version} -Xlint:all true true org.codehaus.plexus plexus-java 0.9.4 org.apache.maven.plugins maven-jar-plugin 3.0.2 auto-auto-service-1.0-rc7/service/processor/000077500000000000000000000000001365703632600211115ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/pom.xml000066400000000000000000000060361365703632600224330ustar00rootroot00000000000000 4.0.0 com.google.auto.service auto-service-aggregator 1.0-rc7 com.google.auto.service auto-service 1.0-rc7 AutoService Processor Provider-configuration files for ServiceLoader. https://github.com/google/auto/tree/master/service http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD com.google.auto.service auto-service-annotations ${project.version} com.google.auto auto-common 0.10 com.google.guava guava com.google.testing.compile compile-testing test junit junit test com.google.truth truth test org.apache.maven.plugins maven-jar-plugin org.apache.maven.plugins maven-compiler-plugin -proc:none 1.8 1.8 auto-auto-service-1.0-rc7/service/processor/src/000077500000000000000000000000001365703632600217005ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/000077500000000000000000000000001365703632600226245ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/000077500000000000000000000000001365703632600235455ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/000077500000000000000000000000001365703632600243235ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/000077500000000000000000000000001365703632600255775ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/auto/000077500000000000000000000000001365703632600265475ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/auto/service/000077500000000000000000000000001365703632600302075ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/auto/service/processor/000077500000000000000000000000001365703632600322265ustar00rootroot00000000000000AutoServiceProcessor.java000066400000000000000000000261161365703632600371510ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/auto/service/processor/* * Copyright 2008 Google LLC * * 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.google.auto.service.processor; import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.MoreElements.getAnnotationMirror; import static com.google.common.collect.ImmutableSet.toImmutableSet; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Filer; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedOptions; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; import javax.tools.FileObject; import javax.tools.StandardLocation; /** * Processes {@link AutoService} annotations and generates the service provider * configuration files described in {@link java.util.ServiceLoader}. *

* Processor Options:

    *
  • debug - turns on debug statements
  • *
*/ @SupportedOptions({ "debug", "verify" }) public class AutoServiceProcessor extends AbstractProcessor { @VisibleForTesting static final String MISSING_SERVICES_ERROR = "No service interfaces provided for element!"; /** * Maps the class names of service provider interfaces to the * class names of the concrete classes which implement them. *

* For example, * {@code "com.google.apphosting.LocalRpcService" -> * "com.google.apphosting.datastore.LocalDatastoreService"} */ private Multimap providers = HashMultimap.create(); @Override public ImmutableSet getSupportedAnnotationTypes() { return ImmutableSet.of(AutoService.class.getName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** *

    *
  1. For each class annotated with {@link AutoService}
      *
    • Verify the {@link AutoService} interface value is correct *
    • Categorize the class by its service interface *
    * *
  2. For each {@link AutoService} interface
      *
    • Create a file named {@code META-INF/services/} *
    • For each {@link AutoService} annotated class for this interface
        *
      • Create an entry in the file *
      *
    *
*/ @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { try { return processImpl(annotations, roundEnv); } catch (Exception e) { // We don't allow exceptions of any kind to propagate to the compiler StringWriter writer = new StringWriter(); e.printStackTrace(new PrintWriter(writer)); fatalError(writer.toString()); return true; } } private boolean processImpl(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { generateConfigFiles(); } else { processAnnotations(annotations, roundEnv); } return true; } private void processAnnotations(Set annotations, RoundEnvironment roundEnv) { Set elements = roundEnv.getElementsAnnotatedWith(AutoService.class); log(annotations.toString()); log(elements.toString()); for (Element e : elements) { // TODO(gak): check for error trees? TypeElement providerImplementer = (TypeElement) e; AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get(); Set providerInterfaces = getValueFieldOfClasses(annotationMirror); if (providerInterfaces.isEmpty()) { error(MISSING_SERVICES_ERROR, e, annotationMirror); continue; } for (DeclaredType providerInterface : providerInterfaces) { TypeElement providerType = MoreTypes.asTypeElement(providerInterface); log("provider interface: " + providerType.getQualifiedName()); log("provider implementer: " + providerImplementer.getQualifiedName()); if (checkImplementer(providerImplementer, providerType)) { providers.put(getBinaryName(providerType), getBinaryName(providerImplementer)); } else { String message = "ServiceProviders must implement their service provider interface. " + providerImplementer.getQualifiedName() + " does not implement " + providerType.getQualifiedName(); error(message, e, annotationMirror); } } } } private void generateConfigFiles() { Filer filer = processingEnv.getFiler(); for (String providerInterface : providers.keySet()) { String resourceFile = "META-INF/services/" + providerInterface; log("Working on resource file: " + resourceFile); try { SortedSet allServices = Sets.newTreeSet(); try { // would like to be able to print the full path // before we attempt to get the resource in case the behavior // of filer.getResource does change to match the spec, but there's // no good way to resolve CLASS_OUTPUT without first getting a resource. FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile); log("Looking for existing resource file at " + existingFile.toUri()); Set oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream()); log("Existing service entries: " + oldServices); allServices.addAll(oldServices); } catch (IOException e) { // According to the javadoc, Filer.getResource throws an exception // if the file doesn't already exist. In practice this doesn't // appear to be the case. Filer.getResource will happily return a // FileObject that refers to a non-existent file but will throw // IOException if you try to open an input stream for it. log("Resource file did not already exist."); } Set newServices = new HashSet(providers.get(providerInterface)); if (allServices.containsAll(newServices)) { log("No new service entries being added."); return; } allServices.addAll(newServices); log("New service file contents: " + allServices); FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile); OutputStream out = fileObject.openOutputStream(); ServicesFiles.writeServiceFile(allServices, out); out.close(); log("Wrote to: " + fileObject.toUri()); } catch (IOException e) { fatalError("Unable to create " + resourceFile + ", " + e); return; } } } /** * Verifies {@link ServiceProvider} constraints on the concrete provider class. * Note that these constraints are enforced at runtime via the ServiceLoader, * we're just checking them at compile time to be extra nice to our users. */ private boolean checkImplementer(TypeElement providerImplementer, TypeElement providerType) { String verify = processingEnv.getOptions().get("verify"); if (verify == null || !Boolean.valueOf(verify)) { return true; } // TODO: We're currently only enforcing the subtype relationship // constraint. It would be nice to enforce them all. Types types = processingEnv.getTypeUtils(); return types.isSubtype(providerImplementer.asType(), providerType.asType()); } /** * Returns the binary name of a reference type. For example, * {@code com.google.Foo$Bar}, instead of {@code com.google.Foo.Bar}. * */ private String getBinaryName(TypeElement element) { return getBinaryNameImpl(element, element.getSimpleName().toString()); } private String getBinaryNameImpl(TypeElement element, String className) { Element enclosingElement = element.getEnclosingElement(); if (enclosingElement instanceof PackageElement) { PackageElement pkg = (PackageElement) enclosingElement; if (pkg.isUnnamed()) { return className; } return pkg.getQualifiedName() + "." + className; } TypeElement typeElement = (TypeElement) enclosingElement; return getBinaryNameImpl(typeElement, typeElement.getSimpleName() + "$" + className); } /** * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code annotationMirror}. */ private ImmutableSet getValueFieldOfClasses(AnnotationMirror annotationMirror) { return getAnnotationValue(annotationMirror, "value") .accept( new SimpleAnnotationValueVisitor8, Void>() { @Override public ImmutableSet visitType(TypeMirror typeMirror, Void v) { // TODO(ronshapiro): class literals may not always be declared types, i.e. int.class, // int[].class return ImmutableSet.of(MoreTypes.asDeclared(typeMirror)); } @Override public ImmutableSet visitArray( List values, Void v) { return values .stream() .flatMap(value -> value.accept(this, null).stream()) .collect(toImmutableSet()); } }, null); } private void log(String msg) { if (processingEnv.getOptions().containsKey("debug")) { processingEnv.getMessager().printMessage(Kind.NOTE, msg); } } private void error(String msg, Element element, AnnotationMirror annotation) { processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation); } private void fatalError(String msg) { processingEnv.getMessager().printMessage(Kind.ERROR, "FATAL ERROR: " + msg); } } ServicesFiles.java000066400000000000000000000057371365703632600355740ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/auto/service/processor/* * Copyright 2008 Google LLC * * 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.google.auto.service.processor; import static com.google.common.base.Charsets.UTF_8; import com.google.common.io.Closer; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.util.Collection; import java.util.HashSet; import java.util.Set; /** * A helper class for reading and writing Services files. */ final class ServicesFiles { public static final String SERVICES_PATH = "META-INF/services"; private ServicesFiles() { } /** * Returns an absolute path to a service file given the class * name of the service. * * @param serviceName not {@code null} * @return SERVICES_PATH + serviceName */ static String getPath(String serviceName) { return SERVICES_PATH + "/" + serviceName; } /** * Reads the set of service classes from a service file. * * @param input not {@code null}. Closed after use. * @return a not {@code null Set} of service class names. * @throws IOException */ static Set readServiceFile(InputStream input) throws IOException { HashSet serviceClasses = new HashSet(); Closer closer = Closer.create(); try { // TODO(gak): use CharStreams BufferedReader r = closer.register(new BufferedReader(new InputStreamReader(input, UTF_8))); String line; while ((line = r.readLine()) != null) { int commentStart = line.indexOf('#'); if (commentStart >= 0) { line = line.substring(0, commentStart); } line = line.trim(); if (!line.isEmpty()) { serviceClasses.add(line); } } return serviceClasses; } catch (Throwable t) { throw closer.rethrow(t); } finally { closer.close(); } } /** * Writes the set of service class names to a service file. * * @param output not {@code null}. Not closed after use. * @param services a not {@code null Collection} of service class names. * @throws IOException */ static void writeServiceFile(Collection services, OutputStream output) throws IOException { BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8)); for (String service : services) { writer.write(service); writer.newLine(); } writer.flush(); } }package-info.java000066400000000000000000000013641365703632600353420ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/java/com/google/auto/service/processor/* * Copyright 2013 Google LLC * * 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. */ /** * This package contains the annotation processor that implements the * {@link com.google.auto.service.AutoService} API. */ package com.google.auto.service.processor;auto-auto-service-1.0-rc7/service/processor/src/main/resources/000077500000000000000000000000001365703632600246365ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/resources/META-INF/000077500000000000000000000000001365703632600257765ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/resources/META-INF/gradle/000077500000000000000000000000001365703632600272345ustar00rootroot00000000000000incremental.annotation.processors000066400000000000000000000001031365703632600357450ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/resources/META-INF/gradlecom.google.auto.service.processor.AutoServiceProcessor,AGGREGATING auto-auto-service-1.0-rc7/service/processor/src/main/resources/META-INF/services/000077500000000000000000000000001365703632600276215ustar00rootroot00000000000000javax.annotation.processing.Processor000066400000000000000000000000671365703632600371030ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/main/resources/META-INF/servicescom.google.auto.service.processor.AutoServiceProcessor auto-auto-service-1.0-rc7/service/processor/src/test/000077500000000000000000000000001365703632600226575ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/000077500000000000000000000000001365703632600236005ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/com/000077500000000000000000000000001365703632600243565ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/com/google/000077500000000000000000000000001365703632600256325ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/com/google/auto/000077500000000000000000000000001365703632600266025ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/com/google/auto/service/000077500000000000000000000000001365703632600302425ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/com/google/auto/service/processor/000077500000000000000000000000001365703632600322615ustar00rootroot00000000000000AutoServiceProcessorTest.java000066400000000000000000000050751365703632600400450ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/java/com/google/auto/service/processor/* * Copyright 2008 Google LLC * * 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.google.auto.service.processor; import static com.google.auto.service.processor.AutoServiceProcessor.MISSING_SERVICES_ERROR; import static com.google.testing.compile.JavaSourcesSubject.assertThat; import com.google.testing.compile.JavaFileObjects; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests the {@link AutoServiceProcessor}. */ @RunWith(JUnit4.class) public class AutoServiceProcessorTest { @Test public void autoService() { assertThat( JavaFileObjects.forResource("test/SomeService.java"), JavaFileObjects.forResource("test/SomeServiceProvider1.java"), JavaFileObjects.forResource("test/SomeServiceProvider2.java"), JavaFileObjects.forResource("test/Enclosing.java"), JavaFileObjects.forResource("test/AnotherService.java"), JavaFileObjects.forResource("test/AnotherServiceProvider.java")) .processedWith(new AutoServiceProcessor()) .compilesWithoutError() .and().generatesFiles( JavaFileObjects.forResource("META-INF/services/test.SomeService"), JavaFileObjects.forResource("META-INF/services/test.AnotherService")); } @Test public void multiService() { assertThat( JavaFileObjects.forResource("test/SomeService.java"), JavaFileObjects.forResource("test/AnotherService.java"), JavaFileObjects.forResource("test/MultiServiceProvider.java")) .processedWith(new AutoServiceProcessor()) .compilesWithoutError() .and().generatesFiles( JavaFileObjects.forResource("META-INF/services/test.SomeServiceMulti"), JavaFileObjects.forResource("META-INF/services/test.AnotherServiceMulti")); } @Test public void badMultiService() { assertThat(JavaFileObjects.forResource("test/NoServices.java")) .processedWith(new AutoServiceProcessor()) .failsToCompile() .withErrorContaining(MISSING_SERVICES_ERROR); } } auto-auto-service-1.0-rc7/service/processor/src/test/resources/000077500000000000000000000000001365703632600246715ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/resources/META-INF/000077500000000000000000000000001365703632600260315ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/resources/META-INF/services/000077500000000000000000000000001365703632600276545ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/resources/META-INF/services/test.AnotherService000066400000000000000000000000341365703632600334730ustar00rootroot00000000000000test.AnotherServiceProvider test.AnotherServiceMulti000066400000000000000000000000321365703632600344250ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/resources/META-INF/servicestest.MultiServiceProvider auto-auto-service-1.0-rc7/service/processor/src/test/resources/META-INF/services/test.SomeService000066400000000000000000000001351365703632600330000ustar00rootroot00000000000000test.Enclosing$NestedSomeServiceProvider test.SomeServiceProvider1 test.SomeServiceProvider2 test.SomeServiceMulti000066400000000000000000000000321365703632600337300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/resources/META-INF/servicestest.MultiServiceProvider auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/000077500000000000000000000000001365703632600256505ustar00rootroot00000000000000auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/AnotherService.java000066400000000000000000000011711365703632600314340ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; interface AnotherService { }auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/AnotherServiceProvider.java000066400000000000000000000013561365703632600331540ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; import com.google.auto.service.AutoService; @AutoService(AnotherService.class) public class AnotherServiceProvider implements AnotherService { }auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/Enclosing.java000066400000000000000000000014211365703632600304320ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; import com.google.auto.service.AutoService; public class Enclosing { @AutoService(SomeService.class) public static class NestedSomeServiceProvider implements SomeService { } }auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/MultiServiceProvider.java000066400000000000000000000014161365703632600326430ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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 test; import com.google.auto.service.AutoService; @AutoService({SomeService.class, AnotherService.class}) public class MultiServiceProvider implements SomeService, AnotherService {} auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/NoServices.java000066400000000000000000000012661365703632600306000ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; import com.google.auto.service.AutoService; @AutoService({}) public class NoServices {} auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/SomeService.java000066400000000000000000000011661365703632600307430ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; interface SomeService { }auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/SomeServiceProvider1.java000066400000000000000000000013461365703632600325370ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; import com.google.auto.service.AutoService; @AutoService(SomeService.class) public class SomeServiceProvider1 implements SomeService { }auto-auto-service-1.0-rc7/service/processor/src/test/resources/test/SomeServiceProvider2.java000066400000000000000000000013461365703632600325400ustar00rootroot00000000000000/* * Copyright 2008 Google LLC * * 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 test; import com.google.auto.service.AutoService; @AutoService(SomeService.class) public class SomeServiceProvider2 implements SomeService { }auto-auto-service-1.0-rc7/util/000077500000000000000000000000001365703632600164075ustar00rootroot00000000000000auto-auto-service-1.0-rc7/util/generate-latest-docs.sh000077500000000000000000000016301365703632600227600ustar00rootroot00000000000000# see http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ for details if [ "$TRAVIS_REPO_SLUG" == "google/auto" ] && \ [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \ [ "$TRAVIS_PULL_REQUEST" == "false" ] && \ [ "$TRAVIS_BRANCH" == "master" ]; then echo -e "Publishing javadoc...\n" mvn -f build-pom.xml javadoc:aggregate TARGET="$(pwd)/target" cd $HOME git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/google/auto gh-pages > /dev/null cd gh-pages git config --global user.email "travis@travis-ci.org" git config --global user.name "travis-ci" git rm -rf api/latest mv ${TARGET}/site/apidocs api/latest git add -A -f api/latest git commit -m "Latest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages" git push -fq origin gh-pages > /dev/null echo -e "Published Javadoc to gh-pages.\n" fi auto-auto-service-1.0-rc7/util/mvn-deploy.sh000077500000000000000000000007131365703632600210410ustar00rootroot00000000000000#!/bin/bash if [ $# -lt 1 ]; then echo "usage $0 [ ...]" exit 1; fi key=${1} shift params=${@} #validate key keystatus=$(gpg --list-keys | grep ${key} | awk '{print $1}') if [ "${keystatus}" != "pub" ]; then echo "Could not find public key with label ${key}" echo -n "Available keys from: " gpg --list-keys | grep --invert-match '^sub' exit 1 fi mvn ${params} clean site:jar -P sonatype-oss-release -Dgpg.keyname=${key} deploy auto-auto-service-1.0-rc7/util/publish-snapshot-on-commit.sh000077500000000000000000000006571365703632600241610ustar00rootroot00000000000000# see https://coderwall.com/p/9b_lfq if [ "$TRAVIS_REPO_SLUG" == "google/auto" ] && \ [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \ [ "$TRAVIS_PULL_REQUEST" == "false" ] && \ [ "$TRAVIS_BRANCH" == "master" ]; then echo -e "Publishing maven snapshot...\n" mvn -f build-pom.xml clean source:jar deploy --settings="util/settings.xml" -DskipTests=true -Dmaven.javadoc.skip=true echo -e "Published maven snapshot" fi auto-auto-service-1.0-rc7/util/settings.xml000066400000000000000000000003351365703632600207720ustar00rootroot00000000000000 sonatype-nexus-snapshots ${env.CI_DEPLOY_USERNAME} ${env.CI_DEPLOY_PASSWORD} auto-auto-service-1.0-rc7/value/000077500000000000000000000000001365703632600165465ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/CHANGES.md000066400000000000000000000247211365703632600201460ustar00rootroot00000000000000# AutoValue Changes **This document is obsolete.** For details of changes in releases since 1.5, see the [releases page](https://github.com/google/auto/releases) for the Auto project. ## 1.4 → 1.5 ### Functional changes * A workaround for older Eclipse versions has been removed. If you need to use an Eclipse version older than 4.5, you will need to stay on AutoValue 1.4. * The [retention](https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Retention.html) of the `@AutoValue` annotation has changed from `SOURCE` to `CLASS`. This means that it is possible for code-analysis tools to tell whether a class is an `@AutoValue`. AutoValue itself uses this to enforce the check that one `@AutoValue` class cannot extend another, even if the classes are compiled separately. * It is now an error if `@Memoized` is applied to a method not inside an `@AutoValue` class. * Type annotations are now handled more consistently. If `@Nullable` is a type annotation, a property of type `@Nullable Integer` will have that type used everywhere in the generated code. Associated bugs with nested type annotations, like `Outer.@Inner`, have been fixed. ### Bugs fixed since 1.4.1 * `@Memoized` methods can now throw checked exceptions. Previously this failed because the exceptions were not copied into the `throws` clause of the generated override, so the call to `super.foo()` did not compile. * The generated `hashCode()` method uses `h = (int) (h ^ longProperty)` rather than `h ^= longProperty` to avoid warnings about loss of precision. * Annotations are not copied from an abstract method to its implementation if they are not visible from the latter. This can happen if the `@AutoValue` inherits the abstract method from a class or interface in a different package. ## 1.3 → 1.4 *This is the last AutoValue version that compiles and runs on Java 6.* Future versions will require at least Java 8 to run. We will continue to generate code that is compatible with Java 7, so AutoValue can be used with `javac -source 7 -target 7 -bootclasspath `, but using the `javac` from jdk8 or later. ### Functional changes * Builder setters now reject a null parameter immediately unless the corresponding property is `@Nullable`. Previously this check happened at `build()` time, and in some cases didn't happen at all. This is the change that is most likely to affect existing code. * Added `@Memoized`. A `@Memoized` method will be overridden in the generated `AutoValue_Foo` class to save the value returned the first time it was called and reuse that every other time. * Generalized support for property builders. Now, in addition to being able to say `immutableListBuilder()` for a property of type `ImmutableList`, you can say `fooBuilder()` for a property of an arbitrary type that has a builder following certain conventions. In particular, you can do this if the type of `foo()` is itself an `@AutoValue` class with a builder. The default value of `foo()`, if `fooBuilder()` is never called, is `fooBuilder().build()`. * If a property `foo()` or `getFoo()` has a builder method `fooBuilder()` then the property can not now be `@Nullable`. An `ImmutableList`, for example, starts off empty, not null, so `@Nullable` was misleading. * When an `@AutoValue` class `Foo` has a builder, the generated `AutoValue_Foo.Builder` has a constructor `AutoValue_Foo.Builder(Foo)`. That constructor was never documented and is now private. If you want to make a `Foo.Builder` from a `Foo`, `Foo` should have an abstract method `Builder toBuilder()`. This change was necessary so that generalized property-builder support could know whether or not the built class needs to be convertible back into its builder. That's only necessary if there is a `toBuilder()`. * The Extension API is now a committed API, meaning we no longer warn that it is likely to change incompatibly. A [guide](https://github.com/google/auto/blob/master/value/userguide/extensions.md) gives tips on writing extensions. * Extensions can now return null rather than generated code. In that case the extension does not generate a class in the AutoValue hierarchy, but it can still do other things like error checking or generating side files. * Access modifiers like `protected` are copied from builder methods to their implementations, instead of the implementations always being public. Change by @torquestomp. * AutoAnnotation now precomputes parts of the `hashCode` that are constant because they come from defaulted methods. This avoids warnings about integer overflow from tools that check that. * If a property is called `oAuth()`, its setter can be called `setOAuth(x)`. Previously it had to be `setoAuth(x)`, which is still allowed. ## Bugs fixed * AutoAnnotation now correctly handles types like `Class[]`. Previously it would try to create a generic array, which Java doesn't allow. Change by @lukesandberg. * We guard against spurious exceptions due to a JDK bug in reading resources from jars. (#365) * We don't propagate an exception if a corrupt jar is found in extension loading. * AutoValue is ready for Java 9, where public classes are not necessarily accessible, and javax.annotation.Generated is not necessarily present. * AutoValue now works correctly even if the version of AutoValue in the `-classpath` is older than the one in the `-processorpath`. * Builders now behave correctly when there is a non-optional property called `missing`. Previously a variable-hiding problem meant that we didn't detect when it wasn't set. * If `@AutoValue class Foo` has a builder, we always generated two constructors, `Builder()` and `Builder(Foo)`, but we only used the second one if `Foo` had a `toBuilder()` method. Now we only generate that constructor if it is needed. That avoids warnings about unused code. * `@AutoAnnotation` now works when the annotation and the factory method are in the default (unnamed) package. ## 1.2 → 1.3 ### Functional changes * Support for TYPE_USE `@Nullable`. This is https://github.com/google/auto/pull/293 by @brychcy. * Restructured the code in AutoValueProcessor for handling extensions, to get rid of warnings about abstract methods when those methods are going to be implemented by an extension, and to fix a bug where extensions would not work right if there was a toBuilder() method. Some of the code in this change is based on https://github.com/google/auto/pull/299 by @rharter. * Added support for "optional getters", where a getter in an AutoValue Builder can have type `Optional` and it will return `Optional.of(x)` where `x` is the value that has been set in the Builder, or `Optional.empty()` if no value has been set. * In AutoValue builders, added support for setting a property of type `Optional` via a setter with an argument of type `T`. * Added logic to AutoValue to detect the confusing case where you think you are using JavaBeans conventions (like getFoo()) but you aren't because at least one method isn't. * Added a README.md describing EscapeVelocity. ### Bugs fixed * Allow an `@AutoValue.Builder` to extend a parent builder using the `>` idiom. * AutoAnnotation now factors in package names when detecting overloads. Previously it treated all annotations with the same SimpleName as being overload attempts. * Removed an inaccurate javadoc reference, which referred to an artifact from an earlier draft version of the Extensions API. This is https://github.com/google/auto/pull/322 by @lucastsa. ## 1.1 → 1.2 ### Functional changes * A **provisional** extension API has been introduced. This **will change** in a later release. If you want to use it regardless, see the [AutoValueExtension] class. * Properties of primitive array type (e.g. `byte[]`) are no longer cloned when read. If your `@AutoValue` class includes an array property, by default it will get a compiler warning, which can be suppressed with `@SuppressWarnings("mutable")`. * An `@AutoValue.Builder` type can now define both the setter and builder methods like so: ``` ... abstract void setStrings(ImmutableList); abstract ImmutableList.Builder stringsBuilder(); ... ``` At runtime, if `stringsBuilder()...` is called then it is an error to call `setStrings(...)` afterwards. * The classes in the autovalue jar are now shaded with a `$` so they never appear in IDE autocompletion. * AutoValue now uses its own implementation of a subset of Apache Velocity, so there will no longer be problems with interference between the Velocity that was bundled with AutoValue and other versions that might be present. ### Bugs fixed * Explicit check for nested `@AutoValue` classes being private, or not being static. Otherwise the compiler errors could be hard to understand, especially in IDEs. * An Eclipse bug that could occasionally lead to exceptions in the IDE has been fixed (GitHub issue #200). * Fixed a bug where AutoValue generated incorrect code if a method with a type parameter was inherited by a class that supplies a concrete type for that parameter. For example `StringIterator implements Iterator`, where the type of `next()` is String, not `T`. * In `AutoValueProcessor`, fixed an exception that happened if the same abstract method was inherited from more than one parent (Github Issue #267). * AutoValue now works correctly in an environment where `@javax.annotation.Generated` does not exist. * Properties marked `@Nullable` now get `@Nullable` on the corresponding constructor parameters in the generated class. ## 1.0 → 1.1 ### Functional changes * Adds builders to AutoValue. Builders are nested classes annotated with `@AutoValue.Builder`. * Annotates constructor parameters with `@Nullable` if the corresponding property methods are `@Nullable`. * Changes Maven shading so org.apache.commons is shaded. * Copies a `@GwtCompatible` annotation from the `@AutoValue` class to its implementation subclass. ### Bugs fixed * Works around a bug in the Eclipse compiler that meant that annotations would be incorrectly copied from `@AutoValue` methods to their implementations. ## 1.0 (Initial Release) * Allows automatic generation of value type implementations See [the AutoValue User's Guide](userguide/index.md) [AutoValueExtension]: src/main/java/com/google/auto/value/extension/AutoValueExtension.java auto-auto-service-1.0-rc7/value/README.md000066400000000000000000000021321365703632600200230ustar00rootroot00000000000000# AutoValue *Generated immutable value classes for Java 7+*
***Kevin Bourrillion, Éamonn McManus***
**Google, Inc.** **Value classes** are extremely common in Java projects. These are classes for which you want to treat any two instances with suitably equal field values as interchangeable. That's right: we're talking about those classes where you wind up implementing `equals`, `hashCode` and `toString` in a bloated, repetitive, formulaic yet error-prone fashion. Writing these methods the first time is not too bad, with the aid of a few helper methods and IDE templates. But once written they continue to burden reviewers, editors and future readers. Their wide expanses of boilerplate sharply decrease the signal-to-noise ratio of your code... and they love to harbor hard-to-spot bugs. AutoValue provides an easier way to create immutable value classes, with a lot less code and less room for error, while **not restricting your freedom** to code almost any aspect of your class exactly the way you want it. For more information, consult the [detailed documentation](userguide/index.md) auto-auto-service-1.0-rc7/value/annotations/000077500000000000000000000000001365703632600211035ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/annotations/pom.xml000066400000000000000000000052441365703632600224250ustar00rootroot00000000000000 4.0.0 com.google.auto.value auto-value-parent HEAD-SNAPSHOT com.google.auto.value auto-value-annotations HEAD-SNAPSHOT AutoValue Annotations Immutable value-type code generation for Java 1.6+. https://github.com/google/auto/tree/master/value 1.7 http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD ../src/main/java org.apache.maven.plugins maven-compiler-plugin com/google/auto/value/* com/google/auto/value/extension/memoized/* com/google/auto/value/extension/serializable/* org.apache.maven.plugins maven-jar-plugin org.apache.maven.plugins maven-invoker-plugin disable-java8-doclint [1.8,) -Xdoclint:none auto-auto-service-1.0-rc7/value/pom.xml000066400000000000000000000137431365703632600200730ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.google.auto.value auto-value-parent HEAD-SNAPSHOT AutoValue Parent Immutable value-type code generation for Java 7+. pom https://github.com/google/auto/tree/master/value UTF-8 1.8 28.1-jre 1.0.1 http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD GitHub Issues http://github.com/google/auto/issues Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt Google LLC http://www.google.com annotations processor src/it/functional src/it/gwtserializer com.google.guava guava ${guava.version} com.google.guava guava-gwt ${guava.version} com.squareup javapoet 1.11.1 com.google.code.findbugs jsr305 3.0.2 com.google.guava guava-testlib ${guava.version} com.google.testing.compile compile-testing 0.18 com.google.truth truth ${truth.version} com.google.truth.extensions truth-java8-extension ${truth.version} junit junit 4.13 org.apache.velocity velocity 1.7 org.apache.maven.plugins maven-compiler-plugin 3.7.0 ${java.version} ${java.version} -Xlint:all true true org.codehaus.plexus plexus-java 0.9.4 org.apache.maven.plugins maven-jar-plugin 3.0.2 org.apache.maven.plugins maven-invoker-plugin 3.0.1 true ${project.build.directory}/it */pom.xml true integration-test install run org.immutables.tools maven-shade-plugin 4 auto-auto-service-1.0-rc7/value/processor/000077500000000000000000000000001365703632600205655ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/processor/pom.xml000066400000000000000000000164221365703632600221070ustar00rootroot00000000000000 4.0.0 com.google.auto.value auto-value-parent HEAD-SNAPSHOT com.google.auto.value auto-value HEAD-SNAPSHOT AutoValue Processor Immutable value-type code generation for Java 1.6+. https://github.com/google/auto/tree/master/value http://github.com/google/auto scm:git:git://github.com/google/auto.git scm:git:ssh://git@github.com/google/auto.git HEAD com.google.auto auto-common 0.10 com.google.auto.service auto-service 1.0-rc6 provided com.google.errorprone error_prone_annotations 2.3.3 provided com.google.escapevelocity escapevelocity 0.9.1 net.ltgt.gradle.incap incap 0.2 net.ltgt.gradle.incap incap-processor 0.2 provided com.google.guava guava com.squareup javapoet com.google.auto.value auto-value-annotations ${project.version} test org.apache.velocity velocity test com.google.guava guava-testlib test junit junit test com.google.truth truth test com.google.truth.extensions truth-java8-extension test com.google.testing.compile compile-testing test org.mockito mockito-core 3.1.0 test ../src/main/java ../src/test/java ../src/main/java **/*.vm org.apache.maven.plugins maven-compiler-plugin com/google/auto/value/processor/**/*.java com/google/auto/value/extension/memoized/processor/**/*.java com/google/auto/value/extension/serializable/processor/**/*.java com/google/auto/value/extension/serializable/serializer/**/*.java org.apache.maven.plugins maven-jar-plugin org.apache.maven.plugins maven-invoker-plugin org.immutables.tools maven-shade-plugin package shade true com.google.code.findbugs:jsr305 org.objectweb autovalue.shaded.org.objectweb$ com.google autovalue.shaded.com.google$ com.google.auto.value.** com.squareup.javapoet autovalue.shaded.com.squareup.javapoet$ net.ltgt.gradle.incap autovalue.shaded.net.ltgt.gradle.incap$ org.checkerframework autovalue.shaded.org.checkerframework$ disable-java8-doclint [1.8,) -Xdoclint:none auto-auto-service-1.0-rc7/value/src/000077500000000000000000000000001365703632600173355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/000077500000000000000000000000001365703632600177515ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/000077500000000000000000000000001365703632600221135ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/invoker.properties000066400000000000000000000001171365703632600257050ustar00rootroot00000000000000invoker.goals = clean verify invoker.profiles.1 = invoker.profiles.2 = eclipseauto-auto-service-1.0-rc7/value/src/it/functional/pom.xml000066400000000000000000000144741365703632600234420ustar00rootroot00000000000000 4.0.0 com.google.auto.value auto-value-parent HEAD-SNAPSHOT ../../../pom.xml https://github.com/google/auto/tree/master/value com.google.auto.value.it.functional functional HEAD-SNAPSHOT Auto-Value Functional Integration Test this-matches-nothing com.google.auto.value auto-value-annotations ${project.version} com.google.auto.value auto-value ${project.version} com.google.auto.service auto-service 1.0-rc6 com.google.guava guava com.google.code.findbugs jsr305 provided com.google.gwt gwt-user 2.8.2 junit junit test com.google.guava guava-testlib test com.google.truth truth test com.google.truth.extensions truth-java8-extension test com.google.testing.compile compile-testing test org.mockito mockito-core 3.1.0 test org.eclipse.jdt ecj 3.20.0 com.google.escapevelocity escapevelocity 0.9.1 org.apache.maven.plugins maven-jar-plugin 3.0.2 org.apache.maven.plugins maven-compiler-plugin 3.7.0 org.codehaus.plexus plexus-java 0.9.4 ${java.specification.version} ${java.specification.version} -Xlint:all -encoding utf8 true true ${exclude.tests} org.apache.maven.plugins maven-deploy-plugin 2.7 true eclipse org.apache.maven.plugins maven-compiler-plugin 3.7.0 org.codehaus.plexus plexus-java 0.9.4 ${java.specification.version} ${java.specification.version} -Xlint:all -encoding utf8 true true ${exclude.tests} exclude-java8-tests (,1.7] **/AutoValueJava8Test.java auto-auto-service-1.0-rc7/value/src/it/functional/src/000077500000000000000000000000001365703632600227025ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/000077500000000000000000000000001365703632600236265ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/000077500000000000000000000000001365703632600245475ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/PackagelessNestedValueType.java000066400000000000000000000017571365703632600326500ustar00rootroot00000000000000/* * Copyright 2012 Google LLC * * 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. */ import com.google.auto.value.AutoValue; import java.util.Map; /** @author emcmanus@google.com (Éamonn McManus) */ public class PackagelessNestedValueType { @AutoValue public abstract static class Nested { abstract Map numberNames(); public static Nested create(Map numberNames) { return new AutoValue_PackagelessNestedValueType_Nested(numberNames); } } } auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/PackagelessValueType.java000066400000000000000000000035541365703632600315020ustar00rootroot00000000000000/* * Copyright 2012 Google LLC * * 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. */ import com.google.auto.value.AutoValue; import java.util.Map; import javax.annotation.Nullable; /** * Simple package-less value type for tests. * * @author emcmanus@google.com (Éamonn McManus) */ @AutoValue public abstract class PackagelessValueType { // The getters here are formatted as an illustration of what getters typically look in real // classes. In particular they have doc comments. /** @return A string that is a nullable string. */ @Nullable public abstract String string(); /** @return An integer that is an integer. */ public abstract int integer(); /** @return A non-null map where the keys are strings and the values are longs. */ public abstract Map map(); public static PackagelessValueType create( @Nullable String string, int integer, Map map) { // The subclass AutoValue_PackagelessValueType is created by the annotation processor that is // triggered by the presence of the @AutoValue annotation. It has a constructor for each // of the abstract getter methods here, in order. The constructor stashes the values here // in private final fields, and each method is implemented to return the value of the // corresponding field. return new AutoValue_PackagelessValueType(string, integer, map); } } auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/com/000077500000000000000000000000001365703632600253255ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/com/google/000077500000000000000000000000001365703632600266015ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/com/google/auto/000077500000000000000000000000001365703632600275515ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/com/google/auto/value/000077500000000000000000000000001365703632600306655ustar00rootroot00000000000000NestedValueType.java000066400000000000000000000017201365703632600345320ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/com/google/auto/value/* * Copyright 2013 Google LLC * * 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.google.auto.value; import java.util.Map; /** @author emcmanus@google.com (Éamonn McManus) */ public class NestedValueType { @AutoValue public abstract static class Nested { abstract Map numberNames(); public static Nested create(Map numberNames) { return new AutoValue_NestedValueType_Nested(numberNames); } } } SimpleValueType.java000066400000000000000000000035021365703632600345410ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/main/java/com/google/auto/value/* * Copyright 2012 Google LLC * * 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.google.auto.value; import java.util.Map; import javax.annotation.Nullable; /** * Simple value type for tests. * * @author emcmanus@google.com (Éamonn McManus) */ @AutoValue public abstract class SimpleValueType { // The getters here are formatted as an illustration of what getters typically look in real // classes. In particular they have doc comments. /** Returns a string that is a nullable string. */ @Nullable public abstract String string(); /** Returns an integer that is an integer. */ public abstract int integer(); /** Returns a non-null map where the keys are strings and the values are longs. */ public abstract Map map(); public static SimpleValueType create( @Nullable String string, int integer, Map map) { // The subclass AutoValue_SimpleValueType is created by the annotation processor that is // triggered by the presence of the @AutoValue annotation. It has a constructor for each // of the abstract getter methods here, in order. The constructor stashes the values here // in private final fields, and each method is implemented to return the value of the // corresponding field. return new AutoValue_SimpleValueType(string, integer, map); } } auto-auto-service-1.0-rc7/value/src/it/functional/src/test/000077500000000000000000000000001365703632600236615ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/000077500000000000000000000000001365703632600246025ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/PackagelessValueTypeTest.java000066400000000000000000000043711365703632600323730ustar00rootroot00000000000000/* * Copyright 2012 Google LLC * * 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. */ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import com.google.common.collect.ImmutableMap; import com.google.common.testing.NullPointerTester; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class PackagelessValueTypeTest { @Test public void testPackagelessValueType() { final String happy = "happy"; final int testInt = 23; final Map testMap = ImmutableMap.of("happy", 23L); PackagelessValueType simple = PackagelessValueType.create(happy, testInt, testMap); assertSame(happy, simple.string()); assertEquals(testInt, simple.integer()); assertSame(testMap, simple.map()); assertEquals( "PackagelessValueType{string=happy, integer=23, map={happy=23}}", simple.toString()); int expectedHashCode = 1; expectedHashCode = (expectedHashCode * 1000003) ^ happy.hashCode(); expectedHashCode = (expectedHashCode * 1000003) ^ ((Object) testInt).hashCode(); expectedHashCode = (expectedHashCode * 1000003) ^ testMap.hashCode(); assertEquals(expectedHashCode, simple.hashCode()); } @Test public void testNestedValueType() { ImmutableMap numberNames = ImmutableMap.of(1, "un", 2, "deux"); PackagelessNestedValueType.Nested nested = PackagelessNestedValueType.Nested.create(numberNames); assertEquals(numberNames, nested.numberNames()); } @Test public void testNull() { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(PackagelessValueType.class); } } auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/000077500000000000000000000000001365703632600253605ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/000077500000000000000000000000001365703632600266345ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/000077500000000000000000000000001365703632600276045ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/000077500000000000000000000000001365703632600307205ustar00rootroot00000000000000AutoAnnotationDefaultsTest.java000066400000000000000000000077451365703632600370140ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2014 Google LLC * * 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.google.auto.value; import static org.junit.Assert.assertTrue; import com.google.auto.value.enums.MyEnum; import com.google.common.testing.EqualsTester; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Method; import java.util.Arrays; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoAnnotationDefaultsTest { @Retention(RetentionPolicy.RUNTIME) @interface EverythingWithDefaults { byte aByte() default 1; short aShort() default 2; int anInt() default 3; long aLong() default Long.MAX_VALUE; float aFloat() default Float.MAX_VALUE; double aDouble() default -Double.MAX_VALUE; char aChar() default '#'; boolean aBoolean() default true; String aString() default "maybe\nmaybe not\n"; String spaces() default " ( x ) , ( y )"; // ensure the formatter doesn't eat spaces MyEnum anEnum() default MyEnum.TWO; byte[] bytes() default {-1, 0, 1}; short[] shorts() default {-2, 0, 2}; int[] ints() default {}; long[] longs() default {-Long.MAX_VALUE}; float[] floats() default { -Float.MIN_VALUE, Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NaN, }; double[] doubles() default { -Double.MIN_VALUE, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN, }; char[] chars() default {'f', '\n', '\uffef'}; boolean[] booleans() default {false, true}; String[] strings() default {"", "\uffef\n"}; MyEnum[] enums() default {MyEnum.ONE, MyEnum.TWO}; } @AutoAnnotation static EverythingWithDefaults newEverythingWithDefaults() { return new AutoAnnotation_AutoAnnotationDefaultsTest_newEverythingWithDefaults(); } @Test public void testDefaults() throws Exception { @EverythingWithDefaults class Annotated {} EverythingWithDefaults expected = Annotated.class.getAnnotation(EverythingWithDefaults.class); EverythingWithDefaults actual = newEverythingWithDefaults(); // Iterate over the annotation members to see if any differ. We could just compare expected and // actual but if the comparison failed it could be hard to see exactly what differed. StringBuilder differencesBuilder = new StringBuilder(); for (Method member : EverythingWithDefaults.class.getDeclaredMethods()) { String name = member.getName(); Object expectedValue = member.invoke(expected); Object actualValue = member.invoke(actual); if (!equal(expectedValue, actualValue)) { differencesBuilder .append("For ") .append(name) .append(" expected <") .append(string(expectedValue)) .append("> but was <") .append(string(actualValue)) .append(">\n"); } } String differences = differencesBuilder.toString(); assertTrue(differences, differences.isEmpty()); // All the members were the same. Check that the equals and hashCode results say so too. new EqualsTester().addEqualityGroup(expected, actual).testEquals(); } private static boolean equal(Object x, Object y) { return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); } private static String string(Object x) { String s = Arrays.deepToString(new Object[] {x}); return s.substring(1, s.length() - 1); } } AutoAnnotationTest.java000066400000000000000000000451441365703632600353170ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2014 Google LLC * * 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.google.auto.value; import static org.junit.Assert.assertEquals; import com.google.auto.value.annotations.Empty; import com.google.auto.value.annotations.GwtArrays; import com.google.auto.value.annotations.StringValues; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.primitives.Ints; import com.google.common.testing.EqualsTester; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoAnnotationTest { @AutoAnnotation private static StringValues newStringValues(String[] value) { return new AutoAnnotation_AutoAnnotationTest_newStringValues(value); } @Empty @StringValues("oops") static class AnnotatedClass {} @Test public void testSimple() { StringValues expectedStringValues = AnnotatedClass.class.getAnnotation(StringValues.class); StringValues actualStringValues = newStringValues(new String[] {"oops"}); StringValues otherStringValues = newStringValues(new String[] {}); new EqualsTester() .addEqualityGroup(expectedStringValues, actualStringValues) .addEqualityGroup(otherStringValues) .testEquals(); } @Test public void testArraysAreCloned() { String[] array = {"Jekyll"}; StringValues stringValues = newStringValues(array); array[0] = "Hyde"; assertEquals("Jekyll", stringValues.value()[0]); stringValues.value()[0] = "Hyde"; assertEquals("Jekyll", stringValues.value()[0]); } @Test public void testGwtArraysAreCloned() { String[] strings = {"Jekyll"}; int[] ints = {2, 3, 5}; GwtArrays arrays = newGwtArrays(strings, ints); assertEquals(ImmutableList.of("Jekyll"), ImmutableList.copyOf(arrays.strings())); assertEquals(ImmutableList.of(2, 3, 5), Ints.asList(arrays.ints())); strings[0] = "Hyde"; ints[0] = -1; assertEquals(ImmutableList.of("Jekyll"), ImmutableList.copyOf(arrays.strings())); assertEquals(ImmutableList.of(2, 3, 5), Ints.asList(arrays.ints())); } @AutoAnnotation private static GwtArrays newGwtArrays(String[] strings, int[] ints) { return new AutoAnnotation_AutoAnnotationTest_newGwtArrays(strings, ints); } @AutoAnnotation private static StringValues newStringValuesVarArgs(String... value) { return new AutoAnnotation_AutoAnnotationTest_newStringValuesVarArgs(value); } @Test public void testSimpleVarArgs() { StringValues expectedStringValues = AnnotatedClass.class.getAnnotation(StringValues.class); StringValues actualStringValues = newStringValuesVarArgs("oops"); StringValues otherStringValues = newStringValuesVarArgs(new String[] {}); new EqualsTester() .addEqualityGroup(expectedStringValues, actualStringValues) .addEqualityGroup(otherStringValues) .testEquals(); } @AutoAnnotation private static Empty newEmpty() { return new AutoAnnotation_AutoAnnotationTest_newEmpty(); } @Test public void testEmpty() { Empty expectedEmpty = AnnotatedClass.class.getAnnotation(Empty.class); Empty actualEmpty = newEmpty(); new EqualsTester().addEqualityGroup(expectedEmpty, actualEmpty).testEquals(); } @Retention(RetentionPolicy.RUNTIME) @interface Everything { byte aByte(); short aShort(); int anInt(); long aLong(); float aFloat(); double aDouble(); char aChar(); boolean aBoolean(); String aString(); RetentionPolicy anEnum(); StringValues anAnnotation(); byte[] bytes(); short[] shorts(); int[] ints(); long[] longs(); float[] floats(); double[] doubles(); char[] chars(); boolean[] booleans(); String[] strings(); RetentionPolicy[] enums(); StringValues[] annotations(); } @AutoAnnotation static Everything newEverything( byte aByte, short aShort, int anInt, long aLong, float aFloat, double aDouble, char aChar, boolean aBoolean, String aString, RetentionPolicy anEnum, StringValues anAnnotation, byte[] bytes, short[] shorts, int[] ints, long[] longs, float[] floats, double[] doubles, char[] chars, boolean[] booleans, String[] strings, RetentionPolicy[] enums, StringValues[] annotations) { return new AutoAnnotation_AutoAnnotationTest_newEverything( aByte, aShort, anInt, aLong, aFloat, aDouble, aChar, aBoolean, aString, anEnum, anAnnotation, bytes, shorts, ints, longs, floats, doubles, chars, booleans, strings, enums, annotations); } @AutoAnnotation static Everything newEverythingCollections( byte aByte, short aShort, int anInt, long aLong, float aFloat, double aDouble, char aChar, boolean aBoolean, String aString, RetentionPolicy anEnum, StringValues anAnnotation, Collection bytes, List shorts, ArrayList ints, Set longs, SortedSet floats, TreeSet doubles, LinkedHashSet chars, ImmutableCollection booleans, ImmutableList strings, ImmutableSet enums, Set annotations) { return new AutoAnnotation_AutoAnnotationTest_newEverythingCollections( aByte, aShort, anInt, aLong, aFloat, aDouble, aChar, aBoolean, aString, anEnum, anAnnotation, bytes, shorts, ints, longs, floats, doubles, chars, booleans, strings, enums, annotations); } @Everything( aByte = 1, aShort = 2, anInt = 3, aLong = -4, aFloat = Float.NaN, aDouble = Double.NaN, aChar = '#', aBoolean = true, aString = "maybe\nmaybe not\n", anEnum = RetentionPolicy.RUNTIME, anAnnotation = @StringValues("whatever"), bytes = {5, 6}, shorts = {}, ints = {7}, longs = {8, 9}, floats = {10, 11}, doubles = {Double.NEGATIVE_INFINITY, -12.0, Double.POSITIVE_INFINITY}, chars = {'?', '!', '\n'}, booleans = {false, true, false}, strings = {"ver", "vers", "vert", "verre", "vair"}, enums = {RetentionPolicy.CLASS, RetentionPolicy.RUNTIME}, annotations = {@StringValues({}), @StringValues({"foo", "bar"})}) private static class AnnotatedWithEverything {} // Get an instance of @Everything via reflection on the class AnnotatedWithEverything, // fabricate an instance using newEverything that is supposed to be equal to it, and // fabricate another instance using newEverything that is supposed to be different. private static final Everything EVERYTHING_FROM_REFLECTION = AnnotatedWithEverything.class.getAnnotation(Everything.class); private static final Everything EVERYTHING_FROM_AUTO = newEverything( (byte) 1, (short) 2, 3, -4, Float.NaN, Double.NaN, '#', true, "maybe\nmaybe not\n", RetentionPolicy.RUNTIME, newStringValues(new String[] {"whatever"}), new byte[] {5, 6}, new short[] {}, new int[] {7}, new long[] {8, 9}, new float[] {10, 11}, new double[] {Double.NEGATIVE_INFINITY, -12.0, Double.POSITIVE_INFINITY}, new char[] {'?', '!', '\n'}, new boolean[] {false, true, false}, new String[] {"ver", "vers", "vert", "verre", "vair"}, new RetentionPolicy[] {RetentionPolicy.CLASS, RetentionPolicy.RUNTIME}, new StringValues[] { newStringValues(new String[] {}), newStringValues(new String[] {"foo", "bar"}), }); private static final Everything EVERYTHING_FROM_AUTO_COLLECTIONS = newEverythingCollections( (byte) 1, (short) 2, 3, -4, Float.NaN, Double.NaN, '#', true, "maybe\nmaybe not\n", RetentionPolicy.RUNTIME, newStringValues(new String[] {"whatever"}), Arrays.asList((byte) 5, (byte) 6), Collections.emptyList(), new ArrayList(Collections.singleton(7)), ImmutableSet.of(8L, 9L), ImmutableSortedSet.of(10f, 11f), new TreeSet( ImmutableList.of(Double.NEGATIVE_INFINITY, -12.0, Double.POSITIVE_INFINITY)), new LinkedHashSet(ImmutableList.of('?', '!', '\n')), ImmutableList.of(false, true, false), ImmutableList.of("ver", "vers", "vert", "verre", "vair"), ImmutableSet.of(RetentionPolicy.CLASS, RetentionPolicy.RUNTIME), ImmutableSet.of( newStringValues(new String[] {}), newStringValues(new String[] {"foo", "bar"}))); private static final Everything EVERYTHING_ELSE_FROM_AUTO = newEverything( (byte) 0, (short) 0, 0, 0, 0, 0, '0', false, "", RetentionPolicy.SOURCE, newStringValues(new String[] {""}), new byte[0], new short[0], new int[0], new long[0], new float[0], new double[0], new char[0], new boolean[0], new String[0], new RetentionPolicy[0], new StringValues[0]); private static final Everything EVERYTHING_ELSE_FROM_AUTO_COLLECTIONS = newEverythingCollections( (byte) 0, (short) 0, 0, 0, 0, 0, '0', false, "", RetentionPolicy.SOURCE, newStringValues(new String[] {""}), ImmutableList.of(), Collections.emptyList(), new ArrayList(), Collections.emptySet(), ImmutableSortedSet.of(), new TreeSet(), new LinkedHashSet(), ImmutableSet.of(), ImmutableList.of(), ImmutableSet.of(), Collections.emptySet()); @Test public void testEqualsAndHashCode() { new EqualsTester() .addEqualityGroup( EVERYTHING_FROM_REFLECTION, EVERYTHING_FROM_AUTO, EVERYTHING_FROM_AUTO_COLLECTIONS) .addEqualityGroup(EVERYTHING_ELSE_FROM_AUTO, EVERYTHING_ELSE_FROM_AUTO_COLLECTIONS) .testEquals(); } public static class IntList extends ArrayList { IntList(Collection c) { super(c); } } @Retention(RetentionPolicy.RUNTIME) @interface IntArray { int[] ints(); } @IntArray(ints = {1, 2, 3}) private static class AnnotatedWithIntArray {} @AutoAnnotation static IntArray newIntArray(IntList ints) { return new AutoAnnotation_AutoAnnotationTest_newIntArray(ints); } /** * Test that we can represent a primitive array member with a parameter whose type is a collection * of the corresponding wrapper type, even if the wrapper type is not explicitly a type parameter. * Specifically, if the member is an {@code int[]} then obviously we can represent it as a {@code * List}, but here we test that we can also represent it as an {@code IntList}, which is * only a {@code List} by virtue of inheritance. This is a separate test rather than just * putting an {@code IntList} parameter into {@link #newEverythingCollections} because we want to * check that we are still able to detect the primitive wrapper type even though it's hidden in * this way. We need to generate a helper method for every primitive wrapper. */ @Test public void testDerivedPrimitiveCollection() { IntList intList = new IntList(ImmutableList.of(1, 2, 3)); IntArray actual = newIntArray(intList); IntArray expected = AnnotatedWithIntArray.class.getAnnotation(IntArray.class); assertEquals(expected, actual); } @Test public void testToString() { String expected = "@com.google.auto.value.AutoAnnotationTest.Everything(" + "aByte=1, aShort=2, anInt=3, aLong=-4, aFloat=NaN, aDouble=NaN, aChar='#', " + "aBoolean=true, aString=\"maybe\\nmaybe not\\n\", anEnum=RUNTIME, " + "anAnnotation=@com.google.auto.value.annotations.StringValues([\"whatever\"]), " + "bytes=[5, 6], shorts=[], ints=[7], longs=[8, 9], floats=[10.0, 11.0], " + "doubles=[-Infinity, -12.0, Infinity], " + "chars=['?', '!', '\\n'], " + "booleans=[false, true, false], " + "strings=[\"ver\", \"vers\", \"vert\", \"verre\", \"vair\"], " + "enums=[CLASS, RUNTIME], " + "annotations=[" + "@com.google.auto.value.annotations.StringValues([]), " + "@com.google.auto.value.annotations.StringValues([\"foo\", \"bar\"])" + "]" + ")"; assertEquals(expected, EVERYTHING_FROM_AUTO.toString()); assertEquals(expected, EVERYTHING_FROM_AUTO_COLLECTIONS.toString()); } @Test public void testStringQuoting() { StringValues instance = newStringValues( new String[] { "", "\r\n", "hello, world", "Éamonn", "\007\uffef", }); String expected = "@com.google.auto.value.annotations.StringValues(" + "[\"\", \"\\r\\n\", \"hello, world\", \"Éamonn\", \"\\007\\uffef\"])"; assertEquals(expected, instance.toString()); } @Retention(RetentionPolicy.RUNTIME) @interface AnnotationsAnnotation { Class[] value(); } @AnnotationsAnnotation(AnnotationsAnnotation.class) static class AnnotatedWithAnnotationsAnnotation {} @AutoAnnotation static AnnotationsAnnotation newAnnotationsAnnotation(List> value) { return new AutoAnnotation_AutoAnnotationTest_newAnnotationsAnnotation(value); } @Test public void testGenericArray() { AnnotationsAnnotation generated = newAnnotationsAnnotation( ImmutableList.>of(AnnotationsAnnotation.class)); AnnotationsAnnotation fromReflect = AnnotatedWithAnnotationsAnnotation.class.getAnnotation(AnnotationsAnnotation.class); assertEquals(fromReflect, generated); } @Retention(RetentionPolicy.RUNTIME) @interface ClassesAnnotation { Class[] value(); } @ClassesAnnotation(AnnotationsAnnotation.class) static class AnnotatedWithClassesAnnotation {} @AutoAnnotation static ClassesAnnotation newClassesAnnotation(List> value) { return new AutoAnnotation_AutoAnnotationTest_newClassesAnnotation(value); } @Test public void testWildcardArray() { ClassesAnnotation generated = newClassesAnnotation(Arrays.>asList(AnnotationsAnnotation.class)); ClassesAnnotation fromReflect = AnnotatedWithClassesAnnotation.class.getAnnotation(ClassesAnnotation.class); assertEquals(fromReflect, generated); } @Retention(RetentionPolicy.RUNTIME) @interface IntegersAnnotation { int one() default Integer.MAX_VALUE; int two() default Integer.MAX_VALUE; int three(); } @IntegersAnnotation(three = 23) static class AnnotatedWithIntegersAnnotation {} @AutoAnnotation static IntegersAnnotation newIntegersAnnotation(int three) { return new AutoAnnotation_AutoAnnotationTest_newIntegersAnnotation(three); } @Test public void testConstantOverflowInHashCode() { IntegersAnnotation generated = newIntegersAnnotation(23); IntegersAnnotation fromReflect = AnnotatedWithIntegersAnnotation.class.getAnnotation(IntegersAnnotation.class); new EqualsTester().addEqualityGroup(generated, fromReflect).testEquals(); } @Retention(RetentionPolicy.RUNTIME) @interface EverythingWithDefaults { byte aByte() default 5; short aShort() default 17; int anInt() default 23; long aLong() default 1729; float aFloat() default 5; double aDouble() default 17; char aChar() default 'x'; boolean aBoolean() default true; String aString() default "whatever"; RetentionPolicy anEnum() default RetentionPolicy.CLASS; // We don't yet support defaulting annotation values. // StringValues anAnnotation() default @StringValues({"foo", "bar"}); byte[] bytes() default {1, 2}; short[] shorts() default {3, 4}; int[] ints() default {5, 6}; long[] longs() default {7, 8}; float[] floats() default {9, 10}; double[] doubles() default {11, 12}; char[] chars() default {'D', 'E'}; boolean[] booleans() default {true, false}; String[] strings() default {"vrai", "faux"}; RetentionPolicy[] enums() default {RetentionPolicy.SOURCE, RetentionPolicy.CLASS}; // We don't yet support defaulting annotation values. // StringValues[] annotations() default { // @StringValues({"foo", "bar"}), @StringValues({"baz", "buh"}) // }; } @EverythingWithDefaults static class AnnotatedWithEverythingWithDefaults {} @AutoAnnotation static EverythingWithDefaults newEverythingWithDefaults() { return new AutoAnnotation_AutoAnnotationTest_newEverythingWithDefaults(); } @Test public void testDefaultedValues() { EverythingWithDefaults generated = newEverythingWithDefaults(); EverythingWithDefaults fromReflect = AnnotatedWithEverythingWithDefaults.class.getAnnotation(EverythingWithDefaults.class); new EqualsTester().addEqualityGroup(generated, fromReflect).testEquals(); } } AutoOneOfJava8Test.java000066400000000000000000000045201365703632600350760ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2018 Google LLC * * 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.google.auto.value; import static com.google.common.truth.Truth.assertThat; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Method; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for Java8-specific {@code @AutoOneOf} behaviour. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoOneOfJava8Test { @AutoOneOf(EqualsNullable.Kind.class) public abstract static class EqualsNullable { @Target(ElementType.TYPE_USE) @Retention(RetentionPolicy.RUNTIME) public @interface Nullable {} public enum Kind { THING } public abstract Kind kind(); public abstract String thing(); public static EqualsNullable ofThing(String thing) { return AutoOneOf_AutoOneOfJava8Test_EqualsNullable.thing(thing); } @Override public abstract boolean equals(@Nullable Object x); @Override public abstract int hashCode(); } /** * Tests that a type annotation on the parameter of {@code equals(Object)} is copied into the * implementation class. */ @Test public void equalsNullable() throws ReflectiveOperationException { EqualsNullable x = EqualsNullable.ofThing("foo"); Class c = x.getClass(); Method equals = c.getMethod("equals", Object.class); assertThat(equals.getDeclaringClass()).isNotSameInstanceAs(EqualsNullable.class); AnnotatedType parameterType = equals.getAnnotatedParameterTypes()[0]; assertThat(parameterType.isAnnotationPresent(EqualsNullable.Nullable.class)).isTrue(); } } AutoOneOfTest.java000066400000000000000000000425401365703632600342100ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2018 Google LLC * * 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.google.auto.value; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.testing.EqualsTester; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.ExecutionException; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoOneOfTest { @AutoValue public abstract static class Dog { public abstract String name(); public static Dog create(String name) { return new AutoValue_AutoOneOfTest_Dog(name); } public void bark() {} } @AutoValue public abstract static class Cat { public static Cat create() { return new AutoValue_AutoOneOfTest_Cat(); } public void meow() {} } @AutoValue public abstract static class TigerShark { public static TigerShark create() { return new AutoValue_AutoOneOfTest_TigerShark(); } public void chomp() {} } @AutoOneOf(Pet.Kind.class) public abstract static class Pet { public static Pet create(Dog dog) { return AutoOneOf_AutoOneOfTest_Pet.dog(dog); } public static Pet create(Cat cat) { return AutoOneOf_AutoOneOfTest_Pet.cat(cat); } public static Pet create(TigerShark shark) { return AutoOneOf_AutoOneOfTest_Pet.tigerShark(shark); } public abstract Dog dog(); public abstract Cat cat(); public abstract TigerShark tigerShark(); public enum Kind { DOG, CAT, TIGER_SHARK } public abstract Kind getKind(); } @Test public void equality() { Dog marvin1 = Dog.create("Marvin"); Pet petMarvin1 = Pet.create(marvin1); Dog marvin2 = Dog.create("Marvin"); Pet petMarvin2 = Pet.create(marvin2); Dog isis = Dog.create("Isis"); Pet petIsis = Pet.create(isis); Cat cat = Cat.create(); new EqualsTester() .addEqualityGroup(marvin1, marvin2) .addEqualityGroup(petMarvin1, petMarvin2) .addEqualityGroup(petIsis) .addEqualityGroup(cat) .addEqualityGroup("foo") .testEquals(); } @Test public void getCorrectType() { Dog marvin = Dog.create("Marvin"); Pet petMarvin = Pet.create(marvin); assertThat(petMarvin.dog()).isSameInstanceAs(marvin); } @Test public void getWrongType() { Cat cat = Cat.create(); Pet petCat = Pet.create(cat); try { petCat.tigerShark(); fail(); } catch (UnsupportedOperationException e) { assertThat(e).hasMessageThat().containsMatch("(?i:cat)"); } } @Test public void string() { Dog marvin = Dog.create("Marvin"); Pet petMarvin = Pet.create(marvin); assertThat(petMarvin.toString()).isEqualTo("Pet{dog=Dog{name=Marvin}}"); } @Test public void getKind() { Dog marvin = Dog.create("Marvin"); Pet petMarvin = Pet.create(marvin); Cat cat = Cat.create(); Pet petCat = Pet.create(cat); TigerShark shark = TigerShark.create(); Pet petShark = Pet.create(shark); assertThat(petMarvin.getKind()).isEqualTo(Pet.Kind.DOG); assertThat(petCat.getKind()).isEqualTo(Pet.Kind.CAT); assertThat(petShark.getKind()).isEqualTo(Pet.Kind.TIGER_SHARK); } @Test public void cannotBeNull() { try { Pet.create((Dog) null); fail(); } catch (NullPointerException expected) { } } // Package-private case. @AutoOneOf(IntegerOrString.Kind.class) abstract static class IntegerOrString { enum Kind { INTEGER, STRING } abstract Kind getKind(); abstract int integer(); abstract String string(); static IntegerOrString of(int x) { return AutoOneOf_AutoOneOfTest_IntegerOrString.integer(x); } static IntegerOrString of(String x) { return AutoOneOf_AutoOneOfTest_IntegerOrString.string(x); } } @Test public void packagePrivate() { IntegerOrString integer = IntegerOrString.of(23); IntegerOrString string = IntegerOrString.of("23"); assertThat(integer.getKind()).isEqualTo(IntegerOrString.Kind.INTEGER); assertThat(string.getKind()).isEqualTo(IntegerOrString.Kind.STRING); assertThat(integer.integer()).isEqualTo(23); assertThat(string.string()).isEqualTo("23"); assertThat(integer).isNotEqualTo(string); try { integer.string(); fail(); } catch (UnsupportedOperationException e) { assertThat(e).hasMessageThat().containsMatch("(?i:integer)"); } } @AutoOneOf(Pet.Kind.class) public abstract static class PetWithGet { public abstract Dog getDog(); public abstract Cat getCat(); public abstract TigerShark getTigerShark(); public static PetWithGet create(Dog dog) { return AutoOneOf_AutoOneOfTest_PetWithGet.dog(dog); } public static PetWithGet create(Cat cat) { return AutoOneOf_AutoOneOfTest_PetWithGet.cat(cat); } public static PetWithGet create(TigerShark shark) { return AutoOneOf_AutoOneOfTest_PetWithGet.tigerShark(shark); } public abstract Pet.Kind getKind(); } @Test public void getPrefix() { Dog marvin = Dog.create("Marvin"); PetWithGet petMarvin = PetWithGet.create(marvin); assertThat(petMarvin.toString()).isEqualTo("PetWithGet{dog=Dog{name=Marvin}}"); } @AutoOneOf(Primitive.Kind.class) public abstract static class Primitive { public enum Kind { A_BYTE, A_SHORT, AN_INT, A_LONG, A_FLOAT, A_DOUBLE, A_CHAR, A_BOOLEAN } public abstract Kind getKind(); public abstract byte aByte(); public abstract short aShort(); public abstract int anInt(); public abstract long aLong(); public abstract float aFloat(); public abstract double aDouble(); public abstract char aChar(); public abstract boolean aBoolean(); public static Primitive of(byte x) { return AutoOneOf_AutoOneOfTest_Primitive.aByte(x); } public static Primitive of(short x) { return AutoOneOf_AutoOneOfTest_Primitive.aShort(x); } public static Primitive of(int x) { return AutoOneOf_AutoOneOfTest_Primitive.anInt(x); } public static Primitive of(long x) { return AutoOneOf_AutoOneOfTest_Primitive.aLong(x); } public static Primitive of(float x) { return AutoOneOf_AutoOneOfTest_Primitive.aFloat(x); } public static Primitive of(double x) { return AutoOneOf_AutoOneOfTest_Primitive.aDouble(x); } public static Primitive of(char x) { return AutoOneOf_AutoOneOfTest_Primitive.aChar(x); } public static Primitive of(boolean x) { return AutoOneOf_AutoOneOfTest_Primitive.aBoolean(x); } } @Test public void primitive() { Primitive primitive = Primitive.of(17); assertThat(primitive.anInt()).isEqualTo(17); assertThat(primitive.toString()).isEqualTo("Primitive{anInt=17}"); } @AutoOneOf(OneOfOne.Kind.class) public abstract static class OneOfOne { public enum Kind { DOG } public abstract Dog getDog(); public static OneOfOne create(Dog dog) { return AutoOneOf_AutoOneOfTest_OneOfOne.dog(dog); } public abstract Kind getKind(); } @Test public void oneOfOne() { Dog marvin = Dog.create("Marvin"); OneOfOne oneOfMarvin = OneOfOne.create(marvin); assertThat(oneOfMarvin.toString()).isEqualTo("OneOfOne{dog=Dog{name=Marvin}}"); assertThat(oneOfMarvin.getKind()).isEqualTo(OneOfOne.Kind.DOG); } // We allow this for consistency, even though it's obviously pretty useless. // The generated code might be rubbish, but it compiles. No concrete implementation is generated // so there isn't really anything to test beyond that it compiles. @AutoOneOf(OneOfNone.Kind.class) public abstract static class OneOfNone { public enum Kind {} public abstract Kind getKind(); } // Testing generics. Typically generics will be a bit messy because the @AutoOneOf class must // have type parameters for every property that needs them, even though any given property // might not use all the type parameters. @AutoOneOf(TaskResult.Kind.class) public abstract static class TaskResult { public enum Kind { VALUE, EXCEPTION } public abstract Kind getKind(); public abstract V value(); public abstract Throwable exception(); public V get() throws ExecutionException { switch (getKind()) { case VALUE: return value(); case EXCEPTION: throw new ExecutionException(exception()); } throw new AssertionError(getKind()); } static TaskResult value(V value) { return AutoOneOf_AutoOneOfTest_TaskResult.value(value); } static TaskResult exception(Throwable exception) { return AutoOneOf_AutoOneOfTest_TaskResult.exception(exception); } } @Test public void taskResultValue() throws Exception { TaskResult result = TaskResult.value("foo"); assertThat(result.get()).isEqualTo("foo"); } @Test public void taskResultException() { Exception exception = new IllegalArgumentException("oops"); TaskResult result = TaskResult.exception(exception); try { result.get(); fail(); } catch (ExecutionException e) { assertThat(e).hasCauseThat().isEqualTo(exception); } } @AutoOneOf(CustomToString.Kind.class) public abstract static class CustomToString { public enum Kind { ACE } public abstract Kind getKind(); public abstract String ace(); public static CustomToString ace(String ace) { return AutoOneOf_AutoOneOfTest_CustomToString.ace(ace); } @Override public String toString() { return "blim"; } } // If you have an explicit toString() method, we won't override it. @Test public void customToString() { CustomToString x = CustomToString.ace("ceg"); assertThat(x.toString()).isEqualTo("blim"); } @AutoOneOf(AbstractToString.Kind.class) public abstract static class AbstractToString { public enum Kind { ACE } public abstract Kind getKind(); public abstract String ace(); public static AbstractToString ace(String ace) { return AutoOneOf_AutoOneOfTest_AbstractToString.ace(ace); } @Override public abstract String toString(); } // If you have an explicit abstract toString() method, we will implement it. @Test public void abstractToString() { AbstractToString x = AbstractToString.ace("ceg"); assertThat(x.toString()).isEqualTo("AbstractToString{ace=ceg}"); } // "package" is a reserved word. You probably don't want to have a property with that name, // but if you insist, you can get one by using getFoo()-style methods. We leak our renaming // scheme here (package0) and for users that that bothers they can just avoid having properties // that are reserved words. @AutoOneOf(LetterOrPackage.Kind.class) public abstract static class LetterOrPackage { public enum Kind { LETTER, PACKAGE } public abstract Kind getKind(); public abstract String getLetter(); public abstract String getPackage(); public static LetterOrPackage ofLetter(String letter) { return AutoOneOf_AutoOneOfTest_LetterOrPackage.letter(letter); } public static LetterOrPackage ofPackage(String pkg) { return AutoOneOf_AutoOneOfTest_LetterOrPackage.package0(pkg); } } @Test public void reservedWordProperty() { LetterOrPackage pkg = LetterOrPackage.ofPackage("pacquet"); assertThat(pkg.toString()).isEqualTo("LetterOrPackage{package=pacquet}"); } @AutoOneOf(ArrayValue.Kind.class) public abstract static class ArrayValue { public enum Kind { STRING, INTS } public abstract Kind getKind(); public abstract String string(); @SuppressWarnings("mutable") public abstract int[] ints(); public static ArrayValue ofString(String string) { return AutoOneOf_AutoOneOfTest_ArrayValue.string(string); } public static ArrayValue ofInts(int[] ints) { return AutoOneOf_AutoOneOfTest_ArrayValue.ints(ints); } } @Test public void arrayValues() { ArrayValue string = ArrayValue.ofString("foo"); ArrayValue ints1 = ArrayValue.ofInts(new int[] {17, 23}); ArrayValue ints2 = ArrayValue.ofInts(new int[] {17, 23}); new EqualsTester() .addEqualityGroup(string) .addEqualityGroup(ints1, ints2) .testEquals(); } @Retention(RetentionPolicy.RUNTIME) public @interface CopyTest { int value(); } @AutoOneOf(AnnotationNotCopied.Kind.class) @CopyTest(23) public abstract static class AnnotationNotCopied { public enum Kind { ACE } public abstract Kind getKind(); public abstract String ace(); public static AnnotationNotCopied ace(String ace) { return AutoOneOf_AutoOneOfTest_AnnotationNotCopied.ace(ace); } } @Test public void classAnnotationsNotCopiedByDefault() { assertThat(AnnotationNotCopied.class.isAnnotationPresent(CopyTest.class)).isTrue(); AnnotationNotCopied ace = AnnotationNotCopied.ace("ace"); assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isFalse(); } @AutoOneOf(AnnotationCopied.Kind.class) @CopyTest(23) @AutoValue.CopyAnnotations public abstract static class AnnotationCopied { public enum Kind { ACE } public abstract Kind getKind(); public abstract String ace(); public static AnnotationCopied ace(String ace) { return AutoOneOf_AutoOneOfTest_AnnotationCopied.ace(ace); } } @Test public void classAnnotationsCopiedIfCopyAnnotations() { assertThat(AnnotationCopied.class.isAnnotationPresent(CopyTest.class)).isTrue(); AnnotationCopied ace = AnnotationCopied.ace("ace"); assertThat(ace.getClass().isAnnotationPresent(CopyTest.class)).isTrue(); assertThat(ace.getClass().getAnnotation(CopyTest.class).value()).isEqualTo(23); } @AutoOneOf(MaybeEmpty.Kind.class) public abstract static class MaybeEmpty implements Serializable { public enum Kind { EMPTY, STRING, } public abstract Kind getKind(); public abstract void empty(); public abstract String string(); public static MaybeEmpty ofEmpty() { return AutoOneOf_AutoOneOfTest_MaybeEmpty.empty(); } public static MaybeEmpty ofString(String s) { return AutoOneOf_AutoOneOfTest_MaybeEmpty.string(s); } } @Test public void voidPropertyIsSingleton() { MaybeEmpty empty1 = MaybeEmpty.ofEmpty(); MaybeEmpty empty2 = MaybeEmpty.ofEmpty(); assertThat(empty1).isSameInstanceAs(empty2); } @Test public void voidPropertyRemainsSingletonWhenDeserialized() throws Exception { MaybeEmpty empty1 = MaybeEmpty.ofEmpty(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); // We're still compiling this with -source 6, so we can't use try-with-resources. ObjectOutputStream dos = new ObjectOutputStream(baos); dos.writeObject(empty1); dos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); MaybeEmpty empty2 = (MaybeEmpty) ois.readObject(); assertThat(empty2).isSameInstanceAs(empty1); } @Test public void voidPropertyToString() { MaybeEmpty empty = MaybeEmpty.ofEmpty(); assertThat(empty.toString()).isEqualTo("MaybeEmpty{empty}"); } @Test public void voidPropertyHashCodeIsIdentity() { MaybeEmpty empty = MaybeEmpty.ofEmpty(); assertThat(empty.hashCode()).isEqualTo(System.identityHashCode(empty)); } @Test public void voidPropertyGetterDoesNothing() { MaybeEmpty empty = MaybeEmpty.ofEmpty(); empty.empty(); } @Test public void voidPropertyNotEqualToNonVoid() { MaybeEmpty empty = MaybeEmpty.ofEmpty(); MaybeEmpty notEmpty = MaybeEmpty.ofString("foo"); assertThat(empty).isNotEqualTo(notEmpty); assertThat(notEmpty).isNotEqualTo(empty); } @Test public void voidPropertyWrongType() { MaybeEmpty notEmpty = MaybeEmpty.ofString("foo"); try { notEmpty.empty(); fail(); } catch (UnsupportedOperationException e) { assertThat(e).hasMessageThat().containsMatch("(?i:string)"); } } @AutoOneOf(OneOfArray.Kind.class) public abstract static class OneOfArray { public enum Kind { INTS } public abstract Kind getKind(); @SuppressWarnings("mutable") public abstract int[] ints(); public static OneOfArray ofInts(int[] s) { return AutoOneOf_AutoOneOfTest_OneOfArray.ints(s); } } @Test public void arrayToString() { OneOfArray oneOfArray = OneOfArray.ofInts(new int[] {1, 2}); assertThat(oneOfArray.toString()).isEqualTo("OneOfArray{ints=[1, 2]}"); } } AutoValueJava8Test.java000066400000000000000000000667131365703632600351600ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2012 Google LLC * * 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.google.auto.value; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth8.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.testing.EqualsTester; import com.google.testing.compile.Compilation; import com.google.testing.compile.Compiler; import com.google.testing.compile.JavaFileObjects; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.TypeVariable; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.ElementFilter; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for constructs new in Java 8, such as type annotations. * * @author Till Brychcy * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoValueJava8Test { private static boolean javacHandlesTypeAnnotationsCorrectly; // This is appalling. Some versions of javac do not correctly report annotations on type uses in // certain cases, for example on type variables or arrays. Since some of the tests here are for // exactly that, we compile a test program with a test annotation processor to see whether we // might be in the presence of such a javac, and if so we skip the tests that would fail because // of the bug. This isn't completely sound because we can't be entirely sure that the javac that // Compiler.javac() finds is the same as the javac that was used to build this test (and therefore // run AutoValueProcessor), but it's better than just ignoring the tests outright. @BeforeClass public static void setUpClass() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "Test", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Retention;", "import java.lang.annotation.RetentionPolicy;", "import java.lang.annotation.Target;", "public abstract class Test {", " @Retention(RetentionPolicy.RUNTIME)", " @Target(ElementType.TYPE_USE)", " public @interface Nullable {}", "", " public abstract @Nullable T t();", "}"); Compilation compilation = Compiler.javac().withProcessors(new BugTestProcessor()).compile(javaFileObject); if (compilation.errors().isEmpty()) { javacHandlesTypeAnnotationsCorrectly = true; } else { assertThat(compilation).hadErrorCount(1); assertThat(compilation).hadErrorContaining(JAVAC_HAS_BUG_ERROR); } } private static final String JAVAC_HAS_BUG_ERROR = "javac has the type-annotation bug"; @SupportedAnnotationTypes("*") @SupportedSourceVersion(SourceVersion.RELEASE_8) private static class BugTestProcessor extends AbstractProcessor { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { test(); } return false; } private void test() { TypeElement test = processingEnv.getElementUtils().getTypeElement("Test"); List methods = ElementFilter.methodsIn(test.getEnclosedElements()); ExecutableElement t = Iterables.getOnlyElement(methods); assertThat(t.getSimpleName().toString()).isEqualTo("t"); List typeAnnotations = t.getReturnType().getAnnotationMirrors(); if (typeAnnotations.isEmpty()) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, JAVAC_HAS_BUG_ERROR); return; } AnnotationMirror typeAnnotation = Iterables.getOnlyElement(typeAnnotations); assertThat(typeAnnotation.getAnnotationType().toString()).contains("Nullable"); } } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) public @interface Nullable {} @AutoValue abstract static class NullableProperties { abstract @Nullable String nullableString(); abstract int randomInt(); static NullableProperties create(@Nullable String nullableString, int randomInt) { return new AutoValue_AutoValueJava8Test_NullableProperties(nullableString, randomInt); } } @Test public void testNullablePropertiesCanBeNull() { NullableProperties instance = NullableProperties.create(null, 23); assertThat(instance.nullableString()).isNull(); assertThat(instance.randomInt()).isEqualTo(23); assertThat(instance.toString()) .isEqualTo("NullableProperties{nullableString=null, randomInt=23}"); } @AutoAnnotation static Nullable nullable() { return new AutoAnnotation_AutoValueJava8Test_nullable(); } @Test public void testNullablePropertyImplementationIsNullable() throws NoSuchMethodException { Method method = AutoValue_AutoValueJava8Test_NullableProperties.class.getDeclaredMethod("nullableString"); assertThat(method.getAnnotatedReturnType().getAnnotations()) .asList() .contains(nullable()); } @Test public void testNullablePropertyConstructorParameterIsNullable() throws NoSuchMethodException { Constructor constructor = AutoValue_AutoValueJava8Test_NullableProperties.class.getDeclaredConstructor( String.class, int.class); try { assertThat(constructor.getAnnotatedParameterTypes()[0].getAnnotations()) .asList() .contains(nullable()); } catch (AssertionError e) { if (javacHandlesTypeAnnotationsCorrectly) { throw e; } } } @AutoValue abstract static class NullablePropertiesNotCopied { @AutoValue.CopyAnnotations(exclude = Nullable.class) abstract @Nullable String nullableString(); abstract int randomInt(); NullablePropertiesNotCopied create(String notNullableAfterAll, int randomInt) { return new AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied( notNullableAfterAll, randomInt); } } @Test public void testExcludedNullablePropertyImplementation() throws NoSuchMethodException { Method method = AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class .getDeclaredMethod("nullableString"); assertThat(method.getAnnotatedReturnType().getAnnotations()) .asList() .doesNotContain(nullable()); } @Test public void testExcludedNullablePropertyConstructorParameter() throws NoSuchMethodException { Constructor constructor = AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class.getDeclaredConstructor( String.class, int.class); try { assertThat(constructor.getAnnotatedParameterTypes()[0].getAnnotations()) .asList() .doesNotContain(nullable()); } catch (AssertionError e) { if (javacHandlesTypeAnnotationsCorrectly) { throw e; } } } @AutoValue abstract static class NullableNonNullable { abstract @Nullable String nullableString(); abstract @Nullable String otherNullableString(); abstract String nonNullableString(); static NullableNonNullable create( String nullableString, String otherNullableString, String nonNullableString) { return new AutoValue_AutoValueJava8Test_NullableNonNullable( nullableString, otherNullableString, nonNullableString); } } @Test public void testEqualsWithNullable() throws Exception { NullableNonNullable everythingNull = NullableNonNullable.create(null, null, "nonNullableString"); NullableNonNullable somethingNull = NullableNonNullable.create(null, "otherNullableString", "nonNullableString"); NullableNonNullable nothingNull = NullableNonNullable.create("nullableString", "otherNullableString", "nonNullableString"); NullableNonNullable nothingNullAgain = NullableNonNullable.create("nullableString", "otherNullableString", "nonNullableString"); new EqualsTester() .addEqualityGroup(everythingNull) .addEqualityGroup(somethingNull) .addEqualityGroup(nothingNull, nothingNullAgain) .testEquals(); } public static class Nested {} @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE) public @interface OtherTypeAnnotation {} @AutoAnnotation public static OtherTypeAnnotation otherTypeAnnotation() { return new AutoAnnotation_AutoValueJava8Test_otherTypeAnnotation(); } @AutoValue abstract static class NestedNullableProperties { abstract @Nullable @OtherTypeAnnotation Nested nullableThing(); abstract int randomInt(); static Builder builder() { return new AutoValue_AutoValueJava8Test_NestedNullableProperties.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setNullableThing(@Nullable @OtherTypeAnnotation Nested thing); abstract Builder setRandomInt(int x); abstract NestedNullableProperties build(); } } @Test public void testNestedNullablePropertiesCanBeNull() { NestedNullableProperties instance = NestedNullableProperties.builder().setRandomInt(23).build(); assertThat(instance.nullableThing()).isNull(); assertThat(instance.randomInt()).isEqualTo(23); assertThat(instance.toString()) .isEqualTo("NestedNullableProperties{nullableThing=null, randomInt=23}"); } @Test public void testNestedNullablePropertiesAreCopied() throws Exception { try { Method generatedGetter = AutoValue_AutoValueJava8Test_NestedNullableProperties.class.getDeclaredMethod( "nullableThing"); Annotation[] getterAnnotations = generatedGetter.getAnnotatedReturnType().getAnnotations(); assertThat(getterAnnotations).asList().containsAtLeast(nullable(), otherTypeAnnotation()); Method generatedSetter = AutoValue_AutoValueJava8Test_NestedNullableProperties.Builder.class.getDeclaredMethod( "setNullableThing", Nested.class); Annotation[] setterAnnotations = generatedSetter.getAnnotatedParameterTypes()[0].getAnnotations(); assertThat(setterAnnotations).asList().containsAtLeast(nullable(), otherTypeAnnotation()); } catch (AssertionError e) { if (javacHandlesTypeAnnotationsCorrectly) { throw e; } } } @AutoValue @SuppressWarnings("AutoValueImmutableFields") abstract static class PrimitiveArrays { @SuppressWarnings("mutable") abstract boolean[] booleans(); @SuppressWarnings("mutable") abstract int @Nullable [] ints(); static PrimitiveArrays create(boolean[] booleans, int[] ints) { // Real code would likely clone these parameters, but here we want to check that the // generated constructor rejects a null value for booleans. return new AutoValue_AutoValueJava8Test_PrimitiveArrays(booleans, ints); } } @Test public void testPrimitiveArrays() { PrimitiveArrays object0 = PrimitiveArrays.create(new boolean[0], new int[0]); boolean[] booleans = {false, true, true, false}; int[] ints = {6, 28, 496, 8128, 33550336}; PrimitiveArrays object1 = PrimitiveArrays.create(booleans.clone(), ints.clone()); PrimitiveArrays object2 = PrimitiveArrays.create(booleans.clone(), ints.clone()); new EqualsTester().addEqualityGroup(object1, object2).addEqualityGroup(object0).testEquals(); // EqualsTester also exercises hashCode(). We clone the arrays above to ensure that using the // default Object.hashCode() will fail. String expectedString = "PrimitiveArrays{booleans=" + Arrays.toString(booleans) + ", " + "ints=" + Arrays.toString(ints) + "}"; assertThat(object1.toString()).isEqualTo(expectedString); assertThat(object1.ints()).isSameInstanceAs(object1.ints()); } @Test public void testNullablePrimitiveArrays() { assumeTrue(javacHandlesTypeAnnotationsCorrectly); PrimitiveArrays object0 = PrimitiveArrays.create(new boolean[0], null); boolean[] booleans = {false, true, true, false}; PrimitiveArrays object1 = PrimitiveArrays.create(booleans.clone(), null); PrimitiveArrays object2 = PrimitiveArrays.create(booleans.clone(), null); new EqualsTester().addEqualityGroup(object1, object2).addEqualityGroup(object0).testEquals(); String expectedString = "PrimitiveArrays{booleans=" + Arrays.toString(booleans) + ", " + "ints=null}"; assertThat(object1.toString()).isEqualTo(expectedString); assertThat(object1.booleans()).isSameInstanceAs(object1.booleans()); assertThat(object1.booleans()).isEqualTo(booleans); object1.booleans()[0] ^= true; assertThat(object1.booleans()).isNotEqualTo(booleans); } @Test public void testNotNullablePrimitiveArrays() { try { PrimitiveArrays.create(null, new int[0]); fail("Construction with null value for non-@Nullable array should have failed"); } catch (NullPointerException e) { assertThat(e.getMessage()).contains("booleans"); } } @AutoValue public abstract static class NullablePropertyWithBuilder { public abstract String notNullable(); public abstract @Nullable String nullable(); public static Builder builder() { return new AutoValue_AutoValueJava8Test_NullablePropertyWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder notNullable(String s); Builder nullable(@Nullable String s); NullablePropertyWithBuilder build(); } } @Test public void testOmitNullableWithBuilder() { NullablePropertyWithBuilder instance1 = NullablePropertyWithBuilder.builder().notNullable("hello").build(); assertThat(instance1.notNullable()).isEqualTo("hello"); assertThat(instance1.nullable()).isNull(); NullablePropertyWithBuilder instance2 = NullablePropertyWithBuilder.builder().notNullable("hello").nullable(null).build(); assertThat(instance2.notNullable()).isEqualTo("hello"); assertThat(instance2.nullable()).isNull(); assertThat(instance1).isEqualTo(instance2); NullablePropertyWithBuilder instance3 = NullablePropertyWithBuilder.builder().notNullable("hello").nullable("world").build(); assertThat(instance3.notNullable()).isEqualTo("hello"); assertThat(instance3.nullable()).isEqualTo("world"); try { NullablePropertyWithBuilder.builder().build(); fail("Expected IllegalStateException for unset non-@Nullable property"); } catch (IllegalStateException e) { assertThat(e.getMessage()).contains("notNullable"); } } @AutoValue public abstract static class OptionalPropertyWithNullableBuilder { public abstract String notOptional(); public abstract Optional optional(); public static Builder builder() { return new AutoValue_AutoValueJava8Test_OptionalPropertyWithNullableBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder notOptional(String s); Builder optional(@Nullable String s); OptionalPropertyWithNullableBuilder build(); } } @Test public void testOmitOptionalWithNullableBuilder() { OptionalPropertyWithNullableBuilder instance1 = OptionalPropertyWithNullableBuilder.builder().notOptional("hello").build(); assertThat(instance1.notOptional()).isEqualTo("hello"); assertThat(instance1.optional()).isEmpty(); OptionalPropertyWithNullableBuilder instance2 = OptionalPropertyWithNullableBuilder.builder().notOptional("hello").optional(null).build(); assertThat(instance2.notOptional()).isEqualTo("hello"); assertThat(instance2.optional()).isEmpty(); assertThat(instance1).isEqualTo(instance2); OptionalPropertyWithNullableBuilder instance3 = OptionalPropertyWithNullableBuilder.builder() .notOptional("hello") .optional("world") .build(); assertThat(instance3.notOptional()).isEqualTo("hello"); assertThat(instance3.optional()).hasValue("world"); try { OptionalPropertyWithNullableBuilder.builder().build(); fail("Expected IllegalStateException for unset non-Optional property"); } catch (IllegalStateException expected) { } } @AutoValue @SuppressWarnings("AutoValueImmutableFields") public abstract static class BuilderWithUnprefixedGetters> { public abstract ImmutableList list(); public abstract @Nullable T t(); @SuppressWarnings("mutable") public abstract int[] ints(); public abstract int noGetter(); public static > Builder builder() { return new AutoValue_AutoValueJava8Test_BuilderWithUnprefixedGetters.Builder(); } @AutoValue.Builder public interface Builder> { Builder setList(ImmutableList list); Builder setT(T t); Builder setInts(int[] ints); Builder setNoGetter(int x); ImmutableList list(); T t(); int[] ints(); BuilderWithUnprefixedGetters build(); } } @Test public void testBuilderWithUnprefixedGetter() { assumeTrue(javacHandlesTypeAnnotationsCorrectly); ImmutableList names = ImmutableList.of("fred", "jim"); int[] ints = {6, 28, 496, 8128, 33550336}; int noGetter = -1; BuilderWithUnprefixedGetters.Builder builder = BuilderWithUnprefixedGetters.builder(); assertThat(builder.t()).isNull(); try { builder.list(); fail("Attempt to retrieve unset list property should have failed"); } catch (IllegalStateException e) { assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); } try { builder.ints(); fail("Attempt to retrieve unset ints property should have failed"); } catch (IllegalStateException e) { assertThat(e).hasMessageThat().isEqualTo("Property \"ints\" has not been set"); } builder.setList(names); assertThat(builder.list()).isSameInstanceAs(names); builder.setInts(ints); assertThat(builder.ints()).isEqualTo(ints); // The array is not cloned by the getter, so the client can modify it (but shouldn't). ints[0] = 0; assertThat(builder.ints()[0]).isEqualTo(0); ints[0] = 6; BuilderWithUnprefixedGetters instance = builder.setNoGetter(noGetter).build(); assertThat(instance.list()).isSameInstanceAs(names); assertThat(instance.t()).isNull(); assertThat(instance.ints()).isEqualTo(ints); assertThat(instance.noGetter()).isEqualTo(noGetter); } @AutoValue @SuppressWarnings("AutoValueImmutableFields") public abstract static class BuilderWithPrefixedGetters> { public abstract ImmutableList getList(); public abstract T getT(); @SuppressWarnings("mutable") public abstract int @Nullable [] getInts(); public abstract int getNoGetter(); public static > Builder builder() { return new AutoValue_AutoValueJava8Test_BuilderWithPrefixedGetters.Builder(); } @AutoValue.Builder public abstract static class Builder> { public abstract Builder setList(ImmutableList list); public abstract Builder setT(T t); public abstract Builder setInts(int[] ints); public abstract Builder setNoGetter(int x); abstract ImmutableList getList(); abstract T getT(); abstract int[] getInts(); public abstract BuilderWithPrefixedGetters build(); } } @Test public void testBuilderWithPrefixedGetter() { assumeTrue(javacHandlesTypeAnnotationsCorrectly); ImmutableList names = ImmutableList.of("fred", "jim"); String name = "sheila"; int noGetter = -1; BuilderWithPrefixedGetters.Builder builder = BuilderWithPrefixedGetters.builder(); assertThat(builder.getInts()).isNull(); try { builder.getList(); fail("Attempt to retrieve unset list property should have failed"); } catch (IllegalStateException e) { assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); } builder.setList(names); assertThat(builder.getList()).isSameInstanceAs(names); builder.setT(name); assertThat(builder.getInts()).isNull(); BuilderWithPrefixedGetters instance = builder.setNoGetter(noGetter).build(); assertThat(instance.getList()).isSameInstanceAs(names); assertThat(instance.getT()).isEqualTo(name); assertThat(instance.getInts()).isNull(); assertThat(instance.getNoGetter()).isEqualTo(noGetter); } // This class tests the case where an annotation is both a method annotation and a type // annotation. If we weren't careful, we might emit it twice in the generated code. @AutoValue abstract static class FunkyNullable { @Target({ElementType.METHOD, ElementType.TYPE_USE}) @interface Nullable {} abstract @Nullable String foo(); abstract Optional bar(); static Builder builder() { return new AutoValue_AutoValueJava8Test_FunkyNullable.Builder(); } @AutoValue.Builder interface Builder { Builder setFoo(@Nullable String foo); Builder setBar(@Nullable String bar); FunkyNullable build(); } } @Test public void testFunkyNullable() { FunkyNullable explicitNull = FunkyNullable.builder().setFoo(null).setBar(null).build(); FunkyNullable implicitNull = FunkyNullable.builder().build(); assertThat(explicitNull).isEqualTo(implicitNull); } @AutoValue abstract static class EqualsNullable { @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @interface Nullable {} abstract String foo(); static EqualsNullable create(String foo) { return new AutoValue_AutoValueJava8Test_EqualsNullable(foo); } @Override public abstract boolean equals(@Nullable Object x); @Override public abstract int hashCode(); } /** * Tests that a type annotation on the parameter of {@code equals(Object)} is copied into the * implementation class. */ @Test public void testEqualsNullable() throws ReflectiveOperationException { EqualsNullable x = EqualsNullable.create("foo"); Class implClass = x.getClass(); Method equals = implClass.getDeclaredMethod("equals", Object.class); AnnotatedType[] parameterTypes = equals.getAnnotatedParameterTypes(); assertThat(parameterTypes[0].isAnnotationPresent(EqualsNullable.Nullable.class)).isTrue(); } @AutoValue abstract static class AnnotatedTypeParameter<@Nullable T> { abstract @Nullable T thing(); static <@Nullable T> AnnotatedTypeParameter create(T thing) { return new AutoValue_AutoValueJava8Test_AnnotatedTypeParameter(thing); } } /** * Tests that an annotation on a type parameter of an {@code @AutoValue} class is copied to the * implementation class. */ @Test public void testTypeAnnotationCopiedToImplementation() { @Nullable String nullableString = "blibby"; AnnotatedTypeParameter<@Nullable String> x = AnnotatedTypeParameter.create(nullableString); Class c = x.getClass(); assertThat(c.getTypeParameters()).hasLength(1); TypeVariable typeParameter = c.getTypeParameters()[0]; assertWithMessage(typeParameter.toString()) .that(typeParameter.getAnnotations()) .asList() .contains(nullable()); } @AutoValue abstract static class AnnotatedTypeParameterWithBuilder<@Nullable T> { abstract @Nullable T thing(); static <@Nullable T> Builder builder() { return new AutoValue_AutoValueJava8Test_AnnotatedTypeParameterWithBuilder.Builder(); } @AutoValue.Builder abstract static class Builder<@Nullable T> { abstract Builder setThing(T thing); abstract AnnotatedTypeParameterWithBuilder build(); } } /** * Tests that an annotation on a type parameter of an {@code @AutoValue} builder is copied to the * implementation class. */ @Test public void testTypeAnnotationOnBuilderCopiedToImplementation() { AnnotatedTypeParameterWithBuilder.Builder<@Nullable String> builder = AnnotatedTypeParameterWithBuilder.builder(); Class c = builder.getClass(); assertThat(c.getTypeParameters()).hasLength(1); TypeVariable typeParameter = c.getTypeParameters()[0]; assertWithMessage(typeParameter.toString()) .that(typeParameter.getAnnotations()) .asList() .contains(nullable()); } // b/127701294 @AutoValue abstract static class OptionalOptional { abstract Optional> maybeJustMaybe(); static Builder builder() { return new AutoValue_AutoValueJava8Test_OptionalOptional.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder maybeJustMaybe(Optional maybe); abstract OptionalOptional build(); } } @Test public void testOptionalOptional_empty() { OptionalOptional empty = OptionalOptional.builder().build(); assertThat(empty.maybeJustMaybe()).isEmpty(); } @Test public void testOptionalOptional_ofEmpty() { OptionalOptional ofEmpty = OptionalOptional.builder().maybeJustMaybe(Optional.empty()).build(); assertThat(ofEmpty.maybeJustMaybe()).hasValue(Optional.empty()); } @Test public void testOptionalOptional_ofSomething() { OptionalOptional ofSomething = OptionalOptional.builder().maybeJustMaybe(Optional.of("foo")).build(); assertThat(ofSomething.maybeJustMaybe()).hasValue(Optional.of("foo")); } @AutoValue abstract static class OptionalExtends { abstract Optional> predicate(); static Builder builder() { return new AutoValue_AutoValueJava8Test_OptionalExtends.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setPredicate(Predicate predicate); abstract OptionalExtends build(); } } @Test public void testOptionalExtends() { Predicate predicate = n -> n.toString().equals("0"); OptionalExtends t = OptionalExtends.builder().setPredicate(predicate).build(); assertThat(t.predicate()).hasValue(predicate); } } AutoValueNotEclipseTest.java000066400000000000000000000045361365703632600362470ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2019 Google LLC * * 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.google.auto.value; import static com.google.common.truth.Truth8.assertThat; import java.util.Optional; import javax.annotation.Nullable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Like {@link AutoValueTest}, but with code that doesn't build with at least some versions of * Eclipse, and should therefore not be included in {@link CompileWithEclipseTest}. (The latter is * not currently present in the open-source build.) */ @RunWith(JUnit4.class) public class AutoValueNotEclipseTest { // This produced the following error with JDT 4.6: // Internal compiler error: java.lang.Exception: java.lang.IllegalArgumentException: element // public abstract B setOptional(T) is not a member of the containing type // com.google.auto.value.AutoValueTest.ConcreteOptional.Builder nor any of its superclasses at // org.eclipse.jdt.internal.compiler.apt.dispatch.RoundDispatcher.handleProcessor(RoundDispatcher.java:169) interface AbstractOptional { Optional optional(); interface Builder> { B setOptional(@Nullable T t); } } @AutoValue abstract static class ConcreteOptional implements AbstractOptional { static Builder builder() { return new AutoValue_AutoValueNotEclipseTest_ConcreteOptional.Builder(); } @AutoValue.Builder interface Builder extends AbstractOptional.Builder { ConcreteOptional build(); } } @Test public void genericOptionalOfNullable() { ConcreteOptional empty = ConcreteOptional.builder().build(); assertThat(empty.optional()).isEmpty(); ConcreteOptional notEmpty = ConcreteOptional.builder().setOptional("foo").build(); assertThat(notEmpty.optional()).hasValue("foo"); } } AutoValueTest.java000066400000000000000000003227671365703632600342720ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2012 Google LLC * * 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.google.auto.value; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.base.MoreObjects; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedMap; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableTable; import com.google.common.testing.EqualsTester; import com.google.common.testing.SerializableTester; import java.io.ObjectStreamClass; import java.io.Serializable; import java.lang.annotation.Annotation; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.math.BigInteger; import java.util.AbstractList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.NavigableSet; import java.util.NoSuchElementException; import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; import javax.annotation.Nullable; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoValueTest { private static boolean omitIdentifiers; @BeforeClass public static void initOmitIdentifiers() { omitIdentifiers = System.getProperty("OmitIdentifiers") != null; } @AutoValue abstract static class Simple { public abstract String publicString(); protected abstract int protectedInt(); abstract Map packageMap(); public static Simple create(String s, int i, Map m) { return new AutoValue_AutoValueTest_Simple(s, i, m); } } @Test public void testSimple() throws Exception { Simple instance1a = Simple.create("example", 23, ImmutableMap.of("twenty-three", 23L)); Simple instance1b = Simple.create("example", 23, ImmutableMap.of("twenty-three", 23L)); Simple instance2 = Simple.create("", 0, ImmutableMap.of()); assertEquals("example", instance1a.publicString()); assertEquals(23, instance1a.protectedInt()); assertEquals(ImmutableMap.of("twenty-three", 23L), instance1a.packageMap()); MoreObjects.ToStringHelper toStringHelper = MoreObjects.toStringHelper(Simple.class); toStringHelper.add("publicString", "example"); toStringHelper.add("protectedInt", 23); toStringHelper.add("packageMap", ImmutableMap.of("twenty-three", 23L)); String expectedString = omitIdentifiers ? "{example, 23, {twenty-three=23}}" : toStringHelper.toString(); assertThat(instance1a.toString()).isEqualTo(expectedString); new EqualsTester() .addEqualityGroup(instance1a, instance1b) .addEqualityGroup(instance2) .testEquals(); } @AutoValue abstract static class Empty { public static Empty create() { return new AutoValue_AutoValueTest_Empty(); } } @Test public void testEmpty() throws Exception { Empty instance = Empty.create(); String expectedString = omitIdentifiers ? "{}" : "Empty{}"; assertThat(instance.toString()).isEqualTo(expectedString); assertEquals(instance, instance); assertEquals(instance, Empty.create()); } @AutoValue abstract static class SimpleWithGetters { abstract int getFoo(); abstract boolean isBar(); abstract boolean getOtherBar(); abstract String getPackage(); // package is a reserved word abstract String getPackage0(); abstract String getHTMLPage(); static SimpleWithGetters create( int foo, boolean bar, boolean otherBar, String pkg, String pkg0, String htmlPage) { return new AutoValue_AutoValueTest_SimpleWithGetters(foo, bar, otherBar, pkg, pkg0, htmlPage); } } @Test public void testGetters() { SimpleWithGetters instance = SimpleWithGetters.create(23, true, false, "foo", "bar", ""); String expectedString = omitIdentifiers ? "{23, true, false, foo, bar, }" : "SimpleWithGetters{" + "foo=23, bar=true, otherBar=false, package=foo, package0=bar, HTMLPage=}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoValue abstract static class NotAllGetters { abstract int getFoo(); abstract boolean bar(); static NotAllGetters create(int foo, boolean bar) { return new AutoValue_AutoValueTest_NotAllGetters(foo, bar); } } @Test public void testNotGetters() { NotAllGetters instance = NotAllGetters.create(23, true); String expectedString = omitIdentifiers ? "{23, true}" : "NotAllGetters{getFoo=23, bar=true}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoValue abstract static class StrangeGetters { abstract int get1st(); abstract int get_1st(); // by default we'll use _1st where identifiers are needed, so foil that. @AutoValue.Builder abstract static class Builder { abstract Builder set1st(int x); abstract Builder set_1st(int x); abstract StrangeGetters build(); } static Builder builder() { return new AutoValue_AutoValueTest_StrangeGetters.Builder(); } } @Test public void testStrangeGetters() { StrangeGetters instance = StrangeGetters.builder().set1st(17).set_1st(23).build(); String expectedString = omitIdentifiers ? "{17, 23}" : "StrangeGetters{1st=17, _1st=23}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoValue abstract static class GettersAndConcreteNonGetters { abstract int getFoo(); @SuppressWarnings("mutable") abstract byte[] getBytes(); boolean hasNoBytes() { return getBytes().length == 0; } static GettersAndConcreteNonGetters create(int foo, byte[] bytes) { return new AutoValue_AutoValueTest_GettersAndConcreteNonGetters(foo, bytes); } } @Test public void testGettersAndConcreteNonGetters() { GettersAndConcreteNonGetters instance = GettersAndConcreteNonGetters.create(23, new byte[] {1}); assertFalse(instance.hasNoBytes()); String expectedString = omitIdentifiers ? "{23, [1]}" : "GettersAndConcreteNonGetters{foo=23, bytes=[1]}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoValue abstract static class ClassProperty { abstract Class theClass(); static ClassProperty create(Class theClass) { return new AutoValue_AutoValueTest_ClassProperty(theClass); } } @Test public void testClassProperty() { ClassProperty instance = ClassProperty.create(Thread.class); assertThat(instance.theClass()).isEqualTo(Thread.class); try { ClassProperty.create(null); fail(); } catch (NullPointerException expected) { } } @AutoValue abstract static class ClassPropertyWithBuilder { abstract Class numberClass(); static Builder builder() { return new AutoValue_AutoValueTest_ClassPropertyWithBuilder.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setNumberClass(Class x); abstract ClassPropertyWithBuilder build(); } } @Test public void testClassPropertyWithBuilder() { ClassPropertyWithBuilder instance = ClassPropertyWithBuilder.builder().setNumberClass(Integer.class).build(); assertThat(instance.numberClass()).isEqualTo(Integer.class); try { ClassPropertyWithBuilder.builder().build(); fail(); } catch (IllegalStateException expected) { } try { ClassPropertyWithBuilder.builder().setNumberClass(null); fail(); } catch (NullPointerException expected) { } } @AutoValue public abstract static class Serialize implements Serializable { public abstract int integer(); public abstract String string(); public abstract BigInteger bigInteger(); public static Serialize create(int integer, String string, BigInteger bigInteger) { return new AutoValue_AutoValueTest_Serialize(integer, string, bigInteger); } } @Test public void testSerialize() throws Exception { Serialize instance = Serialize.create(23, "23", BigInteger.valueOf(23)); assertEquals(instance, SerializableTester.reserialize(instance)); } @AutoValue public abstract static class SerializeWithVersionUID implements Serializable { private static final long serialVersionUID = 4294967297L; public abstract int integer(); public abstract String string(); public static SerializeWithVersionUID create(int integer, String string) { return new AutoValue_AutoValueTest_SerializeWithVersionUID(integer, string); } } @Test public void testSerializeWithVersionUID() throws Exception { SerializeWithVersionUID instance = SerializeWithVersionUID.create(23, "23"); assertEquals(instance, SerializableTester.reserialize(instance)); long serialVersionUID = ObjectStreamClass.lookup(AutoValue_AutoValueTest_SerializeWithVersionUID.class) .getSerialVersionUID(); assertEquals(4294967297L, serialVersionUID); } @AutoValue abstract static class LongProperty { public abstract long longProperty(); public static LongProperty create(long longProperty) { return new AutoValue_AutoValueTest_LongProperty(longProperty); } } @Test public void testLongHashCode() { long longValue = 0x1234567887654321L; LongProperty longProperty = LongProperty.create(longValue); assertEquals(singlePropertyHash(longValue), longProperty.hashCode()); } @AutoValue abstract static class IntProperty { public abstract int intProperty(); public static IntProperty create(int intProperty) { return new AutoValue_AutoValueTest_IntProperty(intProperty); } } @Test public void testIntHashCode() { int intValue = 0x12345678; IntProperty intProperty = IntProperty.create(intValue); assertEquals(singlePropertyHash(intValue), intProperty.hashCode()); } @AutoValue abstract static class ShortProperty { public abstract short shortProperty(); public static ShortProperty create(short shortProperty) { return new AutoValue_AutoValueTest_ShortProperty(shortProperty); } } @Test public void testShortHashCode() { short shortValue = 0x1234; ShortProperty shortProperty = ShortProperty.create(shortValue); assertEquals(singlePropertyHash(shortValue), shortProperty.hashCode()); } @AutoValue abstract static class ByteProperty { public abstract byte byteProperty(); public static ByteProperty create(byte byteProperty) { return new AutoValue_AutoValueTest_ByteProperty(byteProperty); } } @Test public void testByteHashCode() { byte byteValue = 123; ByteProperty byteProperty = ByteProperty.create(byteValue); assertEquals(singlePropertyHash(byteValue), byteProperty.hashCode()); } @AutoValue abstract static class CharProperty { public abstract char charProperty(); public static CharProperty create(char charProperty) { return new AutoValue_AutoValueTest_CharProperty(charProperty); } } @Test public void testCharHashCode() { char charValue = 123; CharProperty charProperty = CharProperty.create(charValue); assertEquals(singlePropertyHash(charValue), charProperty.hashCode()); } @AutoValue abstract static class BooleanProperty { public abstract boolean booleanProperty(); public static BooleanProperty create(boolean booleanProperty) { return new AutoValue_AutoValueTest_BooleanProperty(booleanProperty); } } @Test public void testBooleanHashCode() { for (boolean booleanValue : new boolean[] {false, true}) { BooleanProperty booleanProperty = BooleanProperty.create(booleanValue); assertEquals(singlePropertyHash(booleanValue), booleanProperty.hashCode()); } } @AutoValue abstract static class FloatProperty { public abstract float floatProperty(); public static FloatProperty create(float floatProperty) { return new AutoValue_AutoValueTest_FloatProperty(floatProperty); } } @Test public void testFloatHashCode() { float floatValue = 123456f; FloatProperty floatProperty = FloatProperty.create(floatValue); assertEquals(singlePropertyHash(floatValue), floatProperty.hashCode()); } @AutoValue abstract static class DoubleProperty { public abstract double doubleProperty(); public static DoubleProperty create(double doubleProperty) { return new AutoValue_AutoValueTest_DoubleProperty(doubleProperty); } } @Test public void testDoubleHashCode() { double doubleValue = 1234567890123456d; DoubleProperty doubleProperty = DoubleProperty.create(doubleValue); assertEquals(singlePropertyHash(doubleValue), doubleProperty.hashCode()); } @Test public void testFloatingEquality() { FloatProperty floatZero = FloatProperty.create(0.0f); FloatProperty floatMinusZero = FloatProperty.create(-0.0f); FloatProperty floatNaN = FloatProperty.create(Float.NaN); DoubleProperty doubleZero = DoubleProperty.create(0.0); DoubleProperty doubleMinusZero = DoubleProperty.create(-0.0); DoubleProperty doubleNaN = DoubleProperty.create(Double.NaN); new EqualsTester() .addEqualityGroup(floatZero) .addEqualityGroup(floatMinusZero) .addEqualityGroup(floatNaN) .addEqualityGroup(doubleZero) .addEqualityGroup(doubleMinusZero) .addEqualityGroup(doubleNaN) .testEquals(); } private static int singlePropertyHash(Object property) { return 1000003 ^ property.hashCode(); } abstract static class Super { public abstract Object superObject(); public abstract boolean superBoolean(); // The above two are out of alphabetical order to test EclipseHack. } @AutoValue public abstract static class Sub extends Super { public abstract int subInt(); public static Sub create(Object superObject, boolean superBoolean, int subInt) { return new AutoValue_AutoValueTest_Sub(superObject, superBoolean, subInt); } } // The @AutoValue class can inherit abstract methods from its superclass. @Test public void testSuperclass() throws Exception { Sub instance = Sub.create("blim", true, 1729); assertEquals("blim", instance.superObject()); assertTrue(instance.superBoolean()); assertEquals(1729, instance.subInt()); assertEquals(instance, instance); assertEqualsNullIsFalse(instance); } abstract static class NonPublicSuper { abstract Object superObject(); } // The properties in this subclass are not in alphabetical order, which enables us to test that // everything works correctly when Eclipse sorts them into the order // [superObject, subInt, subString], since it sorts per class. @AutoValue abstract static class NonPublicSub extends NonPublicSuper { abstract String subString(); abstract int subInt(); static NonPublicSub create(Object superObject, String subString, int subInt) { return new AutoValue_AutoValueTest_NonPublicSub(superObject, subString, subInt); } } @Test public void testNonPublicInheritedGetters() throws Exception { NonPublicSub instance = NonPublicSub.create("blim", "blam", 1729); assertEquals("blim", instance.superObject()); assertEquals("blam", instance.subString()); assertEquals(1729, instance.subInt()); assertEquals(instance, instance); assertEqualsNullIsFalse(instance); } @SuppressWarnings("ObjectEqualsNull") private void assertEqualsNullIsFalse(Object instance) { assertFalse(instance.equals(null)); } @AutoValue abstract static class NullableProperties { @Nullable abstract String nullableString(); abstract int randomInt(); static NullableProperties create(@Nullable String nullableString, int randomInt) { return new AutoValue_AutoValueTest_NullableProperties(nullableString, randomInt); } } @Test public void testNullablePropertiesCanBeNull() { NullableProperties instance = NullableProperties.create(null, 23); assertNull(instance.nullableString()); assertThat(instance.randomInt()).isEqualTo(23); String expectedString = omitIdentifiers ? "{null, 23}" : "NullableProperties{nullableString=null, randomInt=23}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoAnnotation static Nullable nullable() { return new AutoAnnotation_AutoValueTest_nullable(); } @Test public void testNullablePropertyConstructorParameterIsNullable() throws NoSuchMethodException { Constructor constructor = AutoValue_AutoValueTest_NullableProperties.class.getDeclaredConstructor( String.class, int.class); assertThat(constructor.getParameterAnnotations()[0]).asList().contains(nullable()); } @AutoValue abstract static class AlternativeNullableProperties { @interface Nullable {} @AlternativeNullableProperties.Nullable abstract String nullableString(); abstract int randomInt(); static AlternativeNullableProperties create(@Nullable String nullableString, int randomInt) { return new AutoValue_AutoValueTest_AlternativeNullableProperties(nullableString, randomInt); } } @Test public void testNullableCanBeFromElsewhere() throws Exception { AlternativeNullableProperties instance = AlternativeNullableProperties.create(null, 23); assertNull(instance.nullableString()); assertThat(instance.randomInt()).isEqualTo(23); String expectedString = omitIdentifiers ? "{null, 23}" : "AlternativeNullableProperties{nullableString=null, randomInt=23}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoValue abstract static class NonNullableProperties { abstract String nonNullableString(); abstract int randomInt(); static NonNullableProperties create(String nonNullableString, int randomInt) { return new AutoValue_AutoValueTest_NonNullableProperties(nonNullableString, randomInt); } } @Test public void testNonNullablePropertiesCannotBeNull() throws Exception { try { NonNullableProperties.create(null, 23); fail("Object creation succeeded but should not have"); } catch (NullPointerException expected) { } NonNullableProperties instance = NonNullableProperties.create("nonnull", 23); assertEquals("nonnull", instance.nonNullableString()); assertEquals(23, instance.randomInt()); } @AutoValue abstract static class NullableListProperties { @Nullable abstract ImmutableList nullableStringList(); static NullableListProperties create(@Nullable ImmutableList nullableStringList) { return new AutoValue_AutoValueTest_NullableListProperties(nullableStringList); } } @Test public void testNullableListPropertiesCanBeNonNull() { NullableListProperties instance = NullableListProperties.create(ImmutableList.of("foo", "bar")); assertEquals(ImmutableList.of("foo", "bar"), instance.nullableStringList()); } @Test public void testNullableListPropertiesCanBeNull() { NullableListProperties instance = NullableListProperties.create(null); assertNull(instance.nullableStringList()); } @AutoValue abstract static class NullableListPropertiesWithBuilder { @Nullable abstract ImmutableList nullableStringList(); static Builder builder() { return new AutoValue_AutoValueTest_NullableListPropertiesWithBuilder.Builder(); } @AutoValue.Builder interface Builder { Builder nullableStringList(List nullableStringList); NullableListPropertiesWithBuilder build(); } } @Test public void testNullableListPropertiesWithBuilderCanBeNonNull() { NullableListPropertiesWithBuilder instance = NullableListPropertiesWithBuilder.builder() .nullableStringList(ImmutableList.of("foo", "bar")) .build(); assertEquals(ImmutableList.of("foo", "bar"), instance.nullableStringList()); } @Test public void testNullableListPropertiesWithBuilderCanBeUnset() { NullableListPropertiesWithBuilder instance = NullableListPropertiesWithBuilder.builder().build(); assertNull(instance.nullableStringList()); } @Test public void testNullableListPropertiesWithBuilderCanBeNull() { NullableListPropertiesWithBuilder instance = NullableListPropertiesWithBuilder.builder().nullableStringList(null).build(); assertNull(instance.nullableStringList()); } static class Nested { @AutoValue abstract static class Doubly { @Nullable abstract String nullableString(); abstract int randomInt(); static Doubly create(String nullableString, int randomInt) { return new AutoValue_AutoValueTest_Nested_Doubly(nullableString, randomInt); } } } @Test public void testDoublyNestedClass() throws Exception { Nested.Doubly instance = Nested.Doubly.create(null, 23); assertNull(instance.nullableString()); assertThat(instance.randomInt()).isEqualTo(23); String expectedString = omitIdentifiers ? "{null, 23}" : "Doubly{nullableString=null, randomInt=23}"; assertThat(instance.toString()).isEqualTo(expectedString); } static interface NestedInInterface { @AutoValue abstract class Doubly { abstract String string(); abstract Map map(); static Doubly create(String string, Map map) { return new AutoValue_AutoValueTest_NestedInInterface_Doubly(string, map); } } } @Test public void testClassNestedInInterface() throws Exception { Map map = ImmutableMap.of("vingt-et-un", 21); NestedInInterface.Doubly instance = NestedInInterface.Doubly.create("foo", map); assertEquals("foo", instance.string()); assertEquals(map, instance.map()); } @AutoValue abstract static class NullableNonNullable { @Nullable abstract String nullableString(); @Nullable abstract String otherNullableString(); abstract String nonNullableString(); static NullableNonNullable create( String nullableString, String otherNullableString, String nonNullableString) { return new AutoValue_AutoValueTest_NullableNonNullable( nullableString, otherNullableString, nonNullableString); } } @Test public void testEqualsWithNullable() throws Exception { NullableNonNullable everythingNull = NullableNonNullable.create(null, null, "nonNullableString"); NullableNonNullable somethingNull = NullableNonNullable.create(null, "otherNullableString", "nonNullableString"); NullableNonNullable nothingNull = NullableNonNullable.create("nullableString", "otherNullableString", "nonNullableString"); NullableNonNullable nothingNullAgain = NullableNonNullable.create("nullableString", "otherNullableString", "nonNullableString"); new EqualsTester() .addEqualityGroup(everythingNull) .addEqualityGroup(somethingNull) .addEqualityGroup(nothingNull, nothingNullAgain) .testEquals(); } @AutoValue abstract static class GenericProperties { abstract Map simpleMap(); abstract Map> hairyMap(); static GenericProperties create( Map simpleMap, Map> hairyMap) { return new AutoValue_AutoValueTest_GenericProperties(simpleMap, hairyMap); } } @Test public void testGenericProperties() throws Exception { GenericProperties instance1 = GenericProperties.create( ImmutableMap.of("twenty-three", 23), ImmutableMap.of("very", (Map) ImmutableMap.of("hairy", 17))); GenericProperties instance2 = GenericProperties.create( ImmutableMap.of("seventeen", 17), ImmutableMap.of("very", (Map) ImmutableMap.of("hairy", 23))); new EqualsTester().addEqualityGroup(instance1).addEqualityGroup(instance2).testEquals(); assertEquals( ImmutableMap.of("very", (Map) ImmutableMap.of("hairy", 23)), instance2.hairyMap()); } @AutoValue abstract static class GenericClass { abstract K key(); abstract Map map(); static GenericClass create(K key, Map map) { return new AutoValue_AutoValueTest_GenericClass(key, map); } } @Test public void testGenericClass() throws Exception { GenericClass instance = GenericClass.create("whatever", ImmutableMap.of("no", false)); assertEquals(instance, instance); assertEquals("whatever", instance.key()); assertEquals(ImmutableMap.of("no", false), instance.map()); } @AutoValue abstract static class GenericClassSimpleBounds { abstract K key(); abstract Map map(); static GenericClassSimpleBounds create( K key, Map map) { return new AutoValue_AutoValueTest_GenericClassSimpleBounds(key, map); } } @Test public void testGenericClassWithSimpleBounds() throws Exception { GenericClassSimpleBounds instance = GenericClassSimpleBounds.create(23, ImmutableMap.of(17, 23)); assertEquals(instance, instance); assertEquals(23, (int) instance.key()); assertEquals(ImmutableMap.of(17, 23), instance.map()); } @AutoValue abstract static class GenericClassHairyBounds & Comparable, V> { abstract K key(); abstract Map map(); static & Comparable, V> GenericClassHairyBounds create( K key, Map map) { return new AutoValue_AutoValueTest_GenericClassHairyBounds(key, map); } } @Test public void testGenericClassWithHairyBounds() throws Exception { class ComparableList extends ArrayList implements Comparable> { @Override public int compareTo(ComparableList list) { throw new UnsupportedOperationException(); } } ComparableList emptyList = new ComparableList(); GenericClassHairyBounds, String> instance = GenericClassHairyBounds.create(emptyList, ImmutableMap.of(emptyList, "23")); assertEquals(instance, instance); assertEquals(emptyList, instance.key()); assertEquals(ImmutableMap.of(emptyList, "23"), instance.map()); } interface Mergeable> { M merge(M other); } @AutoValue abstract static class Delta> { abstract M meta(); static > Delta create(M meta) { return new AutoValue_AutoValueTest_Delta(meta); } } @Test public void testRecursiveGeneric() { class MergeableImpl implements Mergeable { @Override public MergeableImpl merge(MergeableImpl other) { return this; } } MergeableImpl mergeable = new MergeableImpl(); Delta instance = Delta.create(mergeable); assertSame(mergeable, instance.meta()); } static class NodeType {} abstract static class NodeExpressionClass { abstract NodeType getType(); } @AutoValue abstract static class NotNodeExpression extends NodeExpressionClass { static NotNodeExpression create() { return new AutoValue_AutoValueTest_NotNodeExpression(new NodeType()); } } interface NodeExpressionInterface { NodeType getType(); } @AutoValue abstract static class NotNodeExpression2 implements NodeExpressionInterface { static NotNodeExpression2 create() { return new AutoValue_AutoValueTest_NotNodeExpression2(new NodeType()); } } @Test public void testConcreteWithGenericParent() { NotNodeExpression instance = NotNodeExpression.create(); assertThat(instance.getType()).isInstanceOf(NodeType.class); NotNodeExpression2 instance2 = NotNodeExpression2.create(); assertThat(instance2.getType()).isInstanceOf(NodeType.class); } @AutoValue abstract static class ExplicitToString { abstract String string(); static ExplicitToString create(String string) { return new AutoValue_AutoValueTest_ExplicitToString(string); } @Override public String toString() { return "Bazinga{" + string() + "}"; } } // We should not generate a toString() method if there already is a non-default one. @Test public void testExplicitToString() throws Exception { ExplicitToString instance = ExplicitToString.create("foo"); assertEquals("Bazinga{foo}", instance.toString()); } abstract static class NonAutoExplicitToString { abstract String string(); @Override public String toString() { return "Bazinga{" + string() + "}"; } } @AutoValue abstract static class InheritedExplicitToString extends NonAutoExplicitToString { static InheritedExplicitToString create(String string) { return new AutoValue_AutoValueTest_InheritedExplicitToString(string); } } // We should not generate a toString() method if we already inherit a non-default one. @Test public void testInheritedExplicitToString() throws Exception { InheritedExplicitToString instance = InheritedExplicitToString.create("foo"); assertEquals("Bazinga{foo}", instance.toString()); } @AutoValue abstract static class AbstractToString { abstract String string(); static AbstractToString create(String string) { return new AutoValue_AutoValueTest_AbstractToString(string); } @Override public abstract String toString(); } // We should generate a toString() method if the parent class has an abstract one. // That allows users to cancel a toString() from a parent class if they want. @Test public void testAbstractToString() throws Exception { AbstractToString instance = AbstractToString.create("foo"); String expectedString = omitIdentifiers ? "{foo}" : "AbstractToString{string=foo}"; assertThat(instance.toString()).isEqualTo(expectedString); } abstract static class NonAutoAbstractToString { abstract String string(); @Override public abstract String toString(); } @AutoValue abstract static class SubAbstractToString extends NonAutoAbstractToString { static SubAbstractToString create(String string) { return new AutoValue_AutoValueTest_SubAbstractToString(string); } } // We should generate a toString() method if the parent class inherits an abstract one. @Test public void testInheritedAbstractToString() throws Exception { SubAbstractToString instance = SubAbstractToString.create("foo"); String expectedString = omitIdentifiers ? "{foo}" : "SubAbstractToString{string=foo}"; assertThat(instance.toString()).isEqualTo(expectedString); } @AutoValue abstract static class ExplicitHashCode { abstract String string(); static ExplicitHashCode create(String string) { return new AutoValue_AutoValueTest_ExplicitHashCode(string); } @Override public int hashCode() { return 1234; } } @Test public void testExplicitHashCode() throws Exception { ExplicitHashCode instance = ExplicitHashCode.create("foo"); assertEquals(1234, instance.hashCode()); } @AutoValue @SuppressWarnings("EqualsHashCode") abstract static class ExplicitEquals { int equalsCount; static ExplicitEquals create() { return new AutoValue_AutoValueTest_ExplicitEquals(); } @Override public boolean equals(Object o) { equalsCount++; return super.equals(o); } } @SuppressWarnings("SelfEquals") @Test public void testExplicitEquals() throws Exception { ExplicitEquals instance = ExplicitEquals.create(); assertEquals(0, instance.equalsCount); assertTrue(instance.equals(instance)); assertEquals(1, instance.equalsCount); Method equals = instance.getClass().getMethod("equals", Object.class); assertNotSame(ExplicitEquals.class, instance.getClass()); assertSame(ExplicitEquals.class, equals.getDeclaringClass()); } @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation { String value(); } @AutoValue abstract static class PrimitiveArrays { @SuppressWarnings("mutable") abstract boolean[] booleans(); @SuppressWarnings("mutable") @Nullable abstract int[] ints(); static PrimitiveArrays create(boolean[] booleans, int[] ints) { // Real code would likely clone these parameters, but here we want to check that the // generated constructor rejects a null value for booleans. return new AutoValue_AutoValueTest_PrimitiveArrays(booleans, ints); } } @Test public void testPrimitiveArrays() { PrimitiveArrays object0 = PrimitiveArrays.create(new boolean[0], new int[0]); boolean[] booleans = {false, true, true, false}; int[] ints = {6, 28, 496, 8128, 33550336}; PrimitiveArrays object1 = PrimitiveArrays.create(booleans.clone(), ints.clone()); PrimitiveArrays object2 = PrimitiveArrays.create(booleans.clone(), ints.clone()); new EqualsTester().addEqualityGroup(object1, object2).addEqualityGroup(object0).testEquals(); // EqualsTester also exercises hashCode(). We clone the arrays above to ensure that using the // default Object.hashCode() will fail. String expectedString = omitIdentifiers ? ("{" + Arrays.toString(booleans) + ", " + Arrays.toString(ints) + "}") : ("PrimitiveArrays{booleans=" + Arrays.toString(booleans) + ", " + "ints=" + Arrays.toString(ints) + "}"); assertThat(object1.toString()).isEqualTo(expectedString); assertThat(object1.ints()).isSameInstanceAs(object1.ints()); } @Test public void testNullablePrimitiveArrays() { PrimitiveArrays object0 = PrimitiveArrays.create(new boolean[0], null); boolean[] booleans = {false, true, true, false}; PrimitiveArrays object1 = PrimitiveArrays.create(booleans.clone(), null); PrimitiveArrays object2 = PrimitiveArrays.create(booleans.clone(), null); new EqualsTester().addEqualityGroup(object1, object2).addEqualityGroup(object0).testEquals(); String expectedString = omitIdentifiers ? ("{" + Arrays.toString(booleans) + ", null}") : ("PrimitiveArrays{booleans=" + Arrays.toString(booleans) + ", " + "ints=null}"); assertThat(object1.toString()).isEqualTo(expectedString); assertThat(object1.booleans()).isSameInstanceAs(object1.booleans()); assertThat(object1.booleans()).isEqualTo(booleans); object1.booleans()[0] ^= true; assertThat(object1.booleans()).isNotEqualTo(booleans); } @Test public void testNotNullablePrimitiveArrays() { try { PrimitiveArrays.create(null, new int[0]); fail("Construction with null value for non-@Nullable array should have failed"); } catch (NullPointerException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().contains("booleans"); } } } // If users are mad enough to define their own Arrays class and have some properties of that // class and others of primitive array type, then we can't import java.util.Arrays. // This is unlikely. @AutoValue abstract static class AmbiguousArrays { static class Arrays {} abstract Arrays arrays(); @SuppressWarnings("mutable") abstract int[] ints(); static AmbiguousArrays create(Arrays arrays, int[] ints) { return new AutoValue_AutoValueTest_AmbiguousArrays(arrays, ints); } } @Test public void testAmbiguousArrays() { // If this test compiles at all then we presumably don't have the import problem above. AmbiguousArrays object1 = AmbiguousArrays.create(new AmbiguousArrays.Arrays(), new int[0]); assertNotNull(object1.arrays()); assertEquals(0, object1.ints().length); } static final class HashCodeObserver { int hashCodeCount; @Override public boolean equals(Object obj) { return obj instanceof HashCodeObserver; } @Override public int hashCode() { hashCodeCount++; return 23; } } @AutoValue abstract static class MaybeCachedHashCode { abstract HashCodeObserver hashCodeObserver(); abstract int randomInt(); static MaybeCachedHashCode create(HashCodeObserver hashCodeObserver, int randomInt) { return new AutoValue_AutoValueTest_MaybeCachedHashCode(hashCodeObserver, randomInt); } } @Test public void testHashCodeNotCached() { HashCodeObserver observer = new HashCodeObserver(); MaybeCachedHashCode maybeCached = MaybeCachedHashCode.create(observer, 17); int hash1 = maybeCached.hashCode(); int hash2 = maybeCached.hashCode(); assertEquals(hash1, hash2); assertEquals(2, observer.hashCodeCount); } @AutoValue abstract static class Version implements Comparable { abstract int major(); abstract int minor(); static Version create(int major, int minor) { return new AutoValue_AutoValueTest_Version(major, minor); } @Override public int compareTo(Version that) { return ComparisonChain.start() .compare(this.major(), that.major()) .compare(this.minor(), that.minor()) .result(); } } @Test public void testComparisonChain() { assertEquals(Version.create(1, 2), Version.create(1, 2)); Version[] versions = {Version.create(1, 2), Version.create(1, 3), Version.create(2, 1)}; for (int i = 0; i < versions.length; i++) { for (int j = 0; j < versions.length; j++) { int actual = Integer.signum(versions[i].compareTo(versions[j])); int expected = Integer.signum(i - j); assertEquals(expected, actual); } } } abstract static class LukesBase { interface LukesVisitor { T visit(LukesSub s); } abstract T accept(LukesVisitor visitor); @AutoValue abstract static class LukesSub extends LukesBase { static LukesSub create() { return new AutoValue_AutoValueTest_LukesBase_LukesSub(); } @Override T accept(LukesVisitor visitor) { return visitor.visit(this); } } } @Test public void testVisitor() { LukesBase.LukesVisitor visitor = new LukesBase.LukesVisitor() { @Override public String visit(LukesBase.LukesSub s) { return s.toString(); } }; LukesBase.LukesSub sub = LukesBase.LukesSub.create(); assertEquals(sub.toString(), sub.accept(visitor)); } @AutoValue public abstract static class ComplexInheritance extends AbstractBase implements A, B { public static ComplexInheritance create(String name) { return new AutoValue_AutoValueTest_ComplexInheritance(name); } abstract String name(); } static class AbstractBase implements Base { @Override public int answer() { return 42; } } interface A extends Base {} interface B extends Base {} interface Base { int answer(); } @Test public void testComplexInheritance() { ComplexInheritance complex = ComplexInheritance.create("fred"); assertEquals("fred", complex.name()); assertEquals(42, complex.answer()); } // This tests the case where we inherit abstract methods on more than one path. AbstractList // extends AbstractCollection, which implements Collection; and AbstractList also implements List, // which extends Collection. So the class here inherits the methods of Collection on more than // one path. In an earlier version of the logic for handling inheritance, this confused us into // thinking that the methods from Collection were still abstract and therefore candidates for // implementation, even though we inherit concrete implementations of them from AbstractList. @AutoValue public static class MoreComplexInheritance extends AbstractList { @Override public String get(int index) { throw new NoSuchElementException(String.valueOf(index)); } @Override public int size() { return 0; } public static MoreComplexInheritance create() { return new AutoValue_AutoValueTest_MoreComplexInheritance(); } } @Test public void testMoreComplexInheritance() { MoreComplexInheritance instance1 = MoreComplexInheritance.create(); MoreComplexInheritance instance2 = MoreComplexInheritance.create(); assertThat(instance1).isEqualTo(instance2); assertThat(instance1).isNotSameInstanceAs(instance2); } // Test that we are not misled by the privateness of an ancestor into thinking that its methods // are invisible to descendants. public abstract static class PublicGrandparent { public abstract String foo(); } private static class PrivateParent extends PublicGrandparent { @Override public String foo() { return "foo"; } } @AutoValue static class EffectiveVisibility extends PrivateParent { static EffectiveVisibility create() { return new AutoValue_AutoValueTest_EffectiveVisibility(); } } @Test public void testEffectiveVisibility() { EffectiveVisibility instance1 = EffectiveVisibility.create(); EffectiveVisibility instance2 = EffectiveVisibility.create(); assertThat(instance1).isEqualTo(instance2); assertThat(instance1).isNotSameInstanceAs(instance2); } @AutoValue public abstract static class InheritTwice implements A, B { public static InheritTwice create(int answer) { return new AutoValue_AutoValueTest_InheritTwice(answer); } } @Test public void testInheritTwice() { InheritTwice inheritTwice = InheritTwice.create(42); assertEquals(42, inheritTwice.answer()); } @AutoValue public abstract static class Optional { public abstract com.google.common.base.Optional getOptional(); public static Optional create(com.google.common.base.Optional opt) { return new AutoValue_AutoValueTest_Optional(opt); } } @Test public void testAmbiguityFromAutoValueType() { Optional autoOptional = Optional.create(com.google.common.base.Optional.absent()); assertEquals(com.google.common.base.Optional.absent(), autoOptional.getOptional()); } static class BaseWithNestedType { static class Optional {} } @AutoValue public abstract static class InheritsNestedType extends BaseWithNestedType { public abstract com.google.common.base.Optional getOptional(); public static InheritsNestedType create(com.google.common.base.Optional opt) { return new AutoValue_AutoValueTest_InheritsNestedType(opt); } } @Test public void testAmbiguityFromInheritedType() { InheritsNestedType inheritsNestedType = InheritsNestedType.create(com.google.common.base.Optional.absent()); assertEquals(com.google.common.base.Optional.absent(), inheritsNestedType.getOptional()); } abstract static class AbstractParent { abstract int foo(); } @AutoValue abstract static class AbstractChild extends AbstractParent { // The main point of this test is to ensure that we don't try to copy this @Override into the // generated implementation alongside the @Override that we put on all implementation methods. @Override abstract int foo(); static AbstractChild create(int foo) { return new AutoValue_AutoValueTest_AbstractChild(foo); } } @Test public void testOverrideNotDuplicated() { AbstractChild instance = AbstractChild.create(23); assertEquals(23, instance.foo()); } @AutoValue public abstract static class BasicWithBuilder { public abstract int foo(); public static Builder builder() { return new AutoValue_AutoValueTest_BasicWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder foo(int foo); BasicWithBuilder build(); } } @Test public void testBasicWithBuilder() { BasicWithBuilder x = BasicWithBuilder.builder().foo(23).build(); assertEquals(23, x.foo()); try { BasicWithBuilder.builder().build(); fail("Expected exception for missing property"); } catch (IllegalStateException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().contains("foo"); } } } @Test public void testBasicWithBuilderHasOnlyOneConstructor() throws Exception { Class builderClass = AutoValue_AutoValueTest_BasicWithBuilder.Builder.class; Constructor[] constructors = builderClass.getDeclaredConstructors(); assertThat(constructors).hasLength(1); Constructor constructor = constructors[0]; assertThat(constructor.getParameterTypes()).isEmpty(); } @AutoValue public abstract static class EmptyWithBuilder { public static Builder builder() { return new AutoValue_AutoValueTest_EmptyWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { EmptyWithBuilder build(); } } @Test public void testEmptyWithBuilder() { EmptyWithBuilder x = EmptyWithBuilder.builder().build(); EmptyWithBuilder y = EmptyWithBuilder.builder().build(); assertEquals(x, y); } @AutoValue public abstract static class TwoPropertiesWithBuilderClass { public abstract String string(); public abstract int integer(); public static Builder builder() { return new AutoValue_AutoValueTest_TwoPropertiesWithBuilderClass.Builder(); } public static Builder builder(String string) { return new AutoValue_AutoValueTest_TwoPropertiesWithBuilderClass.Builder().string(string); } @AutoValue.Builder public abstract static class Builder { public abstract Builder string(String x); public abstract Builder integer(int x); public abstract TwoPropertiesWithBuilderClass build(); } } @Test public void testTwoPropertiesWithBuilderClass() { TwoPropertiesWithBuilderClass a1 = TwoPropertiesWithBuilderClass.builder().string("23").integer(17).build(); TwoPropertiesWithBuilderClass a2 = TwoPropertiesWithBuilderClass.builder("23").integer(17).build(); TwoPropertiesWithBuilderClass a3 = TwoPropertiesWithBuilderClass.builder().integer(17).string("23").build(); TwoPropertiesWithBuilderClass b = TwoPropertiesWithBuilderClass.builder().string("17").integer(17).build(); new EqualsTester().addEqualityGroup(a1, a2, a3).addEqualityGroup(b).testEquals(); try { TwoPropertiesWithBuilderClass.builder().string(null); fail("Did not get expected exception"); } catch (NullPointerException expected) { } } @AutoValue public abstract static class NullablePropertyWithBuilder { public abstract String notNullable(); @Nullable public abstract String nullable(); public static Builder builder() { return new AutoValue_AutoValueTest_NullablePropertyWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder notNullable(String s); Builder nullable(@Nullable String s); NullablePropertyWithBuilder build(); } } @Test public void testOmitNullableWithBuilder() { NullablePropertyWithBuilder instance1 = NullablePropertyWithBuilder.builder().notNullable("hello").build(); assertThat(instance1.notNullable()).isEqualTo("hello"); assertThat(instance1.nullable()).isNull(); NullablePropertyWithBuilder instance2 = NullablePropertyWithBuilder.builder().notNullable("hello").nullable(null).build(); assertThat(instance2.notNullable()).isEqualTo("hello"); assertThat(instance2.nullable()).isNull(); assertThat(instance1).isEqualTo(instance2); NullablePropertyWithBuilder instance3 = NullablePropertyWithBuilder.builder().notNullable("hello").nullable("world").build(); assertThat(instance3.notNullable()).isEqualTo("hello"); assertThat(instance3.nullable()).isEqualTo("world"); try { NullablePropertyWithBuilder.builder().build(); fail("Expected IllegalStateException for unset non-@Nullable property"); } catch (IllegalStateException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().contains("notNullable"); } } } @AutoValue public abstract static class PrimitiveAndBoxed { public abstract int anInt(); @Nullable public abstract Integer aNullableInteger(); public abstract Integer aNonNullableInteger(); public abstract Builder toBuilder(); public static Builder builder() { return new AutoValue_AutoValueTest_PrimitiveAndBoxed.Builder(); } @AutoValue.Builder public interface Builder { Builder setAnInt(Integer x); Builder setANullableInteger(int x); Builder setANonNullableInteger(int x); PrimitiveAndBoxed build(); } } @Test public void testPrimitiveAndBoxed() { PrimitiveAndBoxed instance1 = PrimitiveAndBoxed.builder().setAnInt(17).setANonNullableInteger(23).build(); assertThat(instance1.anInt()).isEqualTo(17); assertThat(instance1.aNullableInteger()).isNull(); assertThat(instance1.aNonNullableInteger()).isEqualTo(23); PrimitiveAndBoxed instance2 = instance1.toBuilder().setANullableInteger(5).build(); assertThat(instance2.aNullableInteger()).isEqualTo(5); try { instance1.toBuilder().setAnInt(null); fail(); } catch (NullPointerException expected) { } } @AutoValue public abstract static class OptionalPropertiesWithBuilder { public abstract com.google.common.base.Optional optionalString(); public abstract com.google.common.base.Optional optionalInteger(); public static Builder builder() { return new AutoValue_AutoValueTest_OptionalPropertiesWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder setOptionalString(com.google.common.base.Optional s); Builder setOptionalString(String s); Builder setOptionalInteger(com.google.common.base.Optional i); Builder setOptionalInteger(int i); OptionalPropertiesWithBuilder build(); } } @Test public void testOmitOptionalWithBuilder() { OptionalPropertiesWithBuilder omitted = OptionalPropertiesWithBuilder.builder().build(); assertThat(omitted.optionalString()).isAbsent(); assertThat(omitted.optionalInteger()).isAbsent(); OptionalPropertiesWithBuilder supplied = OptionalPropertiesWithBuilder.builder() .setOptionalString(com.google.common.base.Optional.of("foo")) .build(); assertThat(supplied.optionalString()).hasValue("foo"); assertThat(omitted.optionalInteger()).isAbsent(); OptionalPropertiesWithBuilder suppliedDirectly = OptionalPropertiesWithBuilder.builder() .setOptionalString("foo") .setOptionalInteger(23) .build(); assertThat(suppliedDirectly.optionalString()).hasValue("foo"); assertThat(suppliedDirectly.optionalInteger()).hasValue(23); } @AutoValue public abstract static class OptionalPropertyWithNullableBuilder { public abstract String notOptional(); public abstract com.google.common.base.Optional optional(); public static Builder builder() { return new AutoValue_AutoValueTest_OptionalPropertyWithNullableBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder notOptional(String s); Builder optional(@Nullable String s); OptionalPropertyWithNullableBuilder build(); } } @Test public void testOmitOptionalWithNullableBuilder() { OptionalPropertyWithNullableBuilder instance1 = OptionalPropertyWithNullableBuilder.builder().notOptional("hello").build(); assertThat(instance1.notOptional()).isEqualTo("hello"); assertThat(instance1.optional()).isAbsent(); OptionalPropertyWithNullableBuilder instance2 = OptionalPropertyWithNullableBuilder.builder().notOptional("hello").optional(null).build(); assertThat(instance2.notOptional()).isEqualTo("hello"); assertThat(instance2.optional()).isAbsent(); assertThat(instance1).isEqualTo(instance2); OptionalPropertyWithNullableBuilder instance3 = OptionalPropertyWithNullableBuilder.builder() .notOptional("hello") .optional("world") .build(); assertThat(instance3.notOptional()).isEqualTo("hello"); assertThat(instance3.optional()).hasValue("world"); try { OptionalPropertyWithNullableBuilder.builder().build(); fail("Expected IllegalStateException for unset non-Optional property"); } catch (IllegalStateException expected) { } } @AutoValue public abstract static class NullableOptionalPropertiesWithBuilder { @Nullable public abstract com.google.common.base.Optional optionalString(); public static Builder builder() { return new AutoValue_AutoValueTest_NullableOptionalPropertiesWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder setOptionalString(com.google.common.base.Optional s); NullableOptionalPropertiesWithBuilder build(); } } @Test public void testOmitNullableOptionalWithBuilder() { NullableOptionalPropertiesWithBuilder omitted = NullableOptionalPropertiesWithBuilder.builder().build(); assertThat(omitted.optionalString()).isNull(); NullableOptionalPropertiesWithBuilder supplied = NullableOptionalPropertiesWithBuilder.builder() .setOptionalString(com.google.common.base.Optional.of("foo")) .build(); assertThat(supplied.optionalString()).hasValue("foo"); } @AutoValue public abstract static class OptionalPropertiesWithBuilderSimpleSetter { public abstract com.google.common.base.Optional optionalString(); public static Builder builder() { return new AutoValue_AutoValueTest_OptionalPropertiesWithBuilderSimpleSetter.Builder(); } @AutoValue.Builder public interface Builder { Builder setOptionalString(String s); OptionalPropertiesWithBuilderSimpleSetter build(); } } @Test public void testOptionalPropertySimpleSetter() { OptionalPropertiesWithBuilderSimpleSetter omitted = OptionalPropertiesWithBuilderSimpleSetter.builder().build(); assertThat(omitted.optionalString()).isAbsent(); OptionalPropertiesWithBuilderSimpleSetter supplied = OptionalPropertiesWithBuilderSimpleSetter.builder().setOptionalString("foo").build(); assertThat(supplied.optionalString()).hasValue("foo"); } @AutoValue public abstract static class PropertyWithOptionalGetter { public abstract String getString(); public abstract int getInt(); public static Builder builder() { return new AutoValue_AutoValueTest_PropertyWithOptionalGetter.Builder(); } @AutoValue.Builder public interface Builder { Builder setString(String s); com.google.common.base.Optional getString(); Builder setInt(int x); com.google.common.base.Optional getInt(); PropertyWithOptionalGetter build(); } } @Test public void testOptionalGetter() { PropertyWithOptionalGetter.Builder omitted = PropertyWithOptionalGetter.builder(); assertThat(omitted.getString()).isAbsent(); assertThat(omitted.getInt()).isAbsent(); PropertyWithOptionalGetter.Builder supplied = PropertyWithOptionalGetter.builder().setString("foo").setInt(23); assertThat(supplied.getString()).hasValue("foo"); assertThat(supplied.getInt()).hasValue(23); } @AutoValue public abstract static class PropertyNamedMissing { public abstract String missing(); public static Builder builder() { return new AutoValue_AutoValueTest_PropertyNamedMissing.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setMissing(String x); public abstract PropertyNamedMissing build(); } } // https://github.com/google/auto/issues/412 @Test public void testPropertyNamedMissing() { try { PropertyNamedMissing.builder().build(); fail(); } catch (IllegalStateException expected) { } PropertyNamedMissing x = PropertyNamedMissing.builder().setMissing("foo").build(); assertThat(x.missing()).isEqualTo("foo"); } @AutoValue public abstract static class GenericsWithBuilder, U extends T> { public abstract List list(); public abstract U u(); public static , U extends T> Builder builder() { return new AutoValue_AutoValueTest_GenericsWithBuilder.Builder(); } public abstract Builder toBuilderGenerated(); @AutoValue.Builder public interface Builder, U extends T> { Builder list(List list); Builder u(U u); GenericsWithBuilder build(); } } @Test public void testBuilderGenerics() { List integers = ImmutableList.of(1, 2, 3); GenericsWithBuilder instance = GenericsWithBuilder.builder().list(integers).u(23).build(); assertEquals(integers, instance.list()); assertEquals((Integer) 23, instance.u()); GenericsWithBuilder instance2 = instance.toBuilderGenerated().build(); assertEquals(instance, instance2); assertNotSame(instance, instance2); GenericsWithBuilder instance3 = instance.toBuilderGenerated().u(17).build(); assertEquals(integers, instance3.list()); assertEquals((Integer) 17, instance3.u()); } @AutoValue public abstract static class BuilderWithSet> { public abstract List list(); public abstract T t(); public static > Builder builder() { return new AutoValue_AutoValueTest_BuilderWithSet.Builder(); } @AutoValue.Builder public interface Builder> { Builder setList(List list); Builder setT(T t); BuilderWithSet build(); } } @Test public void testBuilderWithSet() { List integers = ImmutableList.of(1, 2, 3); BuilderWithSet instance = BuilderWithSet.builder().setList(integers).setT(23).build(); assertEquals(integers, instance.list()); assertEquals((Integer) 23, instance.t()); } @AutoValue public abstract static class BuilderWithSetAndGet { public abstract List getAList(); public abstract int getAnInt(); public static Builder builder() { return new AutoValue_AutoValueTest_BuilderWithSetAndGet.Builder(); } public abstract Builder toBuilder(); @AutoValue.Builder public interface Builder { Builder setAList(List list); Builder setAnInt(int i); BuilderWithSetAndGet build(); } } @Test public void testBuilderWithSetAndGet() { List integers = ImmutableList.of(1, 2, 3); BuilderWithSetAndGet instance = BuilderWithSetAndGet.builder().setAList(integers).setAnInt(23).build(); assertEquals(integers, instance.getAList()); assertEquals(23, instance.getAnInt()); BuilderWithSetAndGet instance2 = instance.toBuilder().build(); assertEquals(instance, instance2); assertNotSame(instance, instance2); BuilderWithSetAndGet instance3 = instance.toBuilder().setAnInt(17).build(); assertEquals(integers, instance3.getAList()); assertEquals(17, instance3.getAnInt()); } @AutoValue public abstract static class BuilderWithUnprefixedGetters> { public abstract ImmutableList list(); @Nullable public abstract T t(); @SuppressWarnings("mutable") public abstract int[] ints(); public abstract int noGetter(); public abstract String oAuth(); public abstract String oBrien(); public static > Builder builder() { return new AutoValue_AutoValueTest_BuilderWithUnprefixedGetters.Builder(); } @AutoValue.Builder public interface Builder> { Builder setList(ImmutableList list); Builder setT(T t); Builder setInts(int[] ints); Builder setNoGetter(int x); Builder setoAuth(String x); // this ugly spelling is for compatibility Builder setOBrien(String x); ImmutableList list(); T t(); int[] ints(); String oAuth(); String oBrien(); BuilderWithUnprefixedGetters build(); } } @Test public void testBuilderWithUnprefixedGetter() { ImmutableList names = ImmutableList.of("fred", "jim"); int[] ints = {6, 28, 496, 8128, 33550336}; int noGetter = -1; BuilderWithUnprefixedGetters.Builder builder = BuilderWithUnprefixedGetters.builder(); assertNull(builder.t()); try { builder.list(); fail("Attempt to retrieve unset list property should have failed"); } catch (IllegalStateException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); } } try { builder.ints(); fail("Attempt to retrieve unset ints property should have failed"); } catch (IllegalStateException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().isEqualTo("Property \"ints\" has not been set"); } } builder.setList(names); assertThat(builder.list()).isSameInstanceAs(names); builder.setInts(ints); assertThat(builder.ints()).isEqualTo(ints); builder.setoAuth("OAuth"); assertThat(builder.oAuth()).isEqualTo("OAuth"); builder.setOBrien("Flann"); assertThat(builder.oBrien()).isEqualTo("Flann"); // The array is not cloned by the getter, so the client can modify it (but shouldn't). ints[0] = 0; assertThat(builder.ints()[0]).isEqualTo(0); ints[0] = 6; BuilderWithUnprefixedGetters instance = builder.setNoGetter(noGetter).build(); assertThat(instance.list()).isSameInstanceAs(names); assertThat(instance.t()).isNull(); assertThat(instance.ints()).isEqualTo(ints); assertThat(instance.noGetter()).isEqualTo(noGetter); assertThat(instance.oAuth()).isEqualTo("OAuth"); assertThat(instance.oBrien()).isEqualTo("Flann"); } @AutoValue public abstract static class BuilderWithPrefixedGetters> { public abstract ImmutableList getList(); public abstract T getT(); @SuppressWarnings("mutable") @Nullable public abstract int[] getInts(); public abstract String getOAuth(); public abstract int getNoGetter(); public static > Builder builder() { return new AutoValue_AutoValueTest_BuilderWithPrefixedGetters.Builder(); } @AutoValue.Builder public abstract static class Builder> { public abstract Builder setList(ImmutableList list); public abstract Builder setT(T t); public abstract Builder setInts(int[] ints); public abstract Builder setNoGetter(int x); public abstract Builder setOAuth(String x); abstract ImmutableList getList(); abstract T getT(); abstract int[] getInts(); public abstract BuilderWithPrefixedGetters build(); } } @Test public void testBuilderWithPrefixedGetter() { ImmutableList names = ImmutableList.of("fred", "jim"); String name = "sheila"; int noGetter = -1; BuilderWithPrefixedGetters.Builder builder = BuilderWithPrefixedGetters.builder(); assertThat(builder.getInts()).isNull(); try { builder.getList(); fail("Attempt to retrieve unset list property should have failed"); } catch (IllegalStateException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().isEqualTo("Property \"list\" has not been set"); } } builder.setList(names); assertThat(builder.getList()).isSameInstanceAs(names); builder.setT(name); assertThat(builder.getInts()).isNull(); builder.setOAuth("OAuth"); BuilderWithPrefixedGetters instance = builder.setNoGetter(noGetter).build(); assertThat(instance.getList()).isSameInstanceAs(names); assertThat(instance.getT()).isEqualTo(name); assertThat(instance.getInts()).isNull(); assertThat(instance.getNoGetter()).isEqualTo(noGetter); assertThat(instance.getOAuth()).isEqualTo("OAuth"); } @AutoValue public abstract static class BuilderWithPropertyBuilders> { public abstract ImmutableList getFoos(); public abstract ImmutableSet getStrings(); public abstract BuilderWithPropertyBuilders.Builder toBuilder(); public static > Builder builder() { return new AutoValue_AutoValueTest_BuilderWithPropertyBuilders.Builder(); } @AutoValue.Builder public abstract static class Builder> { public abstract ImmutableList getFoos(); public Builder addFoos(Iterable foos) { foosBuilder().addAll(foos); return this; } abstract ImmutableList.Builder foosBuilder(); public Builder addToTs(FooT element) { foosBuilder().add(element); return this; } abstract Builder setStrings(ImmutableList strings); abstract ImmutableSet.Builder stringsBuilder(); public Builder addToStrings(String element) { stringsBuilder().add(element); return this; } public abstract BuilderWithPropertyBuilders build(); } } @Test public void testBuilderWithPropertyBuilders() { ImmutableList numbers = ImmutableList.of(1, 1, 2, 6, 24); ImmutableSet names = ImmutableSet.of("one", "two", "six", "twenty-four"); BuilderWithPropertyBuilders a = BuilderWithPropertyBuilders.builder() .addFoos(numbers) .addToStrings("one") .addToStrings("two") .addToStrings("six") .addToStrings("twenty-four") .build(); assertEquals(numbers, a.getFoos()); assertEquals(names, a.getStrings()); BuilderWithPropertyBuilders.Builder bBuilder = BuilderWithPropertyBuilders.builder(); bBuilder.stringsBuilder().addAll(names); bBuilder.foosBuilder().addAll(numbers); assertEquals(numbers, bBuilder.getFoos()); BuilderWithPropertyBuilders b = bBuilder.build(); assertEquals(a, b); BuilderWithPropertyBuilders.Builder cBuilder = a.toBuilder(); cBuilder.addToStrings("one hundred and twenty"); cBuilder.addToTs(120); BuilderWithPropertyBuilders c = cBuilder.build(); assertEquals( ImmutableSet.of("one", "two", "six", "twenty-four", "one hundred and twenty"), c.getStrings()); assertEquals(ImmutableList.of(1, 1, 2, 6, 24, 120), c.getFoos()); BuilderWithPropertyBuilders.Builder dBuilder = a.toBuilder(); dBuilder.addFoos(ImmutableList.of(120, 720)); BuilderWithPropertyBuilders d = dBuilder.build(); assertEquals(ImmutableList.of(1, 1, 2, 6, 24, 120, 720), d.getFoos()); assertEquals(names, d.getStrings()); BuilderWithPropertyBuilders empty = BuilderWithPropertyBuilders.builder().build(); assertEquals(ImmutableList.of(), empty.getFoos()); assertEquals(ImmutableSet.of(), empty.getStrings()); try { BuilderWithPropertyBuilders.builder().setStrings(null).build(); fail("Did not get expected exception"); } catch (RuntimeException expected) { // We don't specify whether you get the exception on setStrings(null) or on build(), nor // which exception it is exactly. } } interface ImmutableListOf { ImmutableList list(); } @AutoValue abstract static class PropertyBuilderInheritsType implements ImmutableListOf { static Builder builder() { return new AutoValue_AutoValueTest_PropertyBuilderInheritsType.Builder(); } @AutoValue.Builder abstract static class Builder { abstract ImmutableList.Builder listBuilder(); abstract PropertyBuilderInheritsType build(); } } @Test public void propertyBuilderInheritsType() { PropertyBuilderInheritsType.Builder builder = PropertyBuilderInheritsType.builder(); builder.listBuilder().add("foo", "bar"); PropertyBuilderInheritsType x = builder.build(); assertThat(x.list()).containsExactly("foo", "bar").inOrder(); } @AutoValue public abstract static class BuilderWithExoticPropertyBuilders< K extends Number, V extends Comparable> { public abstract ImmutableMap map(); public abstract ImmutableTable table(); public static > Builder builder() { return new AutoValue_AutoValueTest_BuilderWithExoticPropertyBuilders.Builder(); } @AutoValue.Builder public abstract static class Builder> { public Builder putAll(Map map) { mapBuilder().putAll(map); return this; } public abstract ImmutableMap.Builder mapBuilder(); public Builder putAll(ImmutableTable table) { tableBuilder().putAll(table); return this; } public abstract ImmutableTable.Builder tableBuilder(); public abstract BuilderWithExoticPropertyBuilders build(); } } @Test public void testBuilderWithExoticPropertyBuilders() { ImmutableMap map = ImmutableMap.of("one", 1); ImmutableTable table = ImmutableTable.of("one", 1, -1); BuilderWithExoticPropertyBuilders a = BuilderWithExoticPropertyBuilders.builder() .putAll(map) .putAll(table) .build(); assertEquals(map, a.map()); assertEquals(table, a.table()); BuilderWithExoticPropertyBuilders.Builder bBuilder = BuilderWithExoticPropertyBuilders.builder(); bBuilder.mapBuilder().putAll(map); bBuilder.tableBuilder().putAll(table); BuilderWithExoticPropertyBuilders b = bBuilder.build(); assertEquals(a, b); BuilderWithExoticPropertyBuilders empty = BuilderWithExoticPropertyBuilders.builder().build(); assertEquals(ImmutableMap.of(), empty.map()); assertEquals(ImmutableTable.of(), empty.table()); } @AutoValue public abstract static class BuilderWithCopyingSetters { public abstract ImmutableSet things(); public abstract ImmutableList numbers(); public abstract ImmutableMap map(); public static Builder builder(T value) { return new AutoValue_AutoValueTest_BuilderWithCopyingSetters.Builder() .setNumbers(ImmutableSet.of(17, 23.0)) .setMap(Collections.singletonMap("foo", value)); } @AutoValue.Builder public interface Builder { Builder setThings(ImmutableSet things); Builder setThings(Iterable things); Builder setThings(T... things); Builder setNumbers(Collection strings); Builder setMap(Map map); BuilderWithCopyingSetters build(); } } @Test public void testBuilderWithCopyingSetters() { BuilderWithCopyingSetters.Builder builder = BuilderWithCopyingSetters.builder(23); BuilderWithCopyingSetters a = builder.setThings(ImmutableSet.of(1, 2)).build(); assertThat(a.things()).containsExactly(1, 2); assertThat(a.numbers()).containsExactly(17, 23.0).inOrder(); assertThat(a.map()).containsExactly("foo", 23); BuilderWithCopyingSetters b = builder.setThings(Arrays.asList(1, 2)).build(); assertThat(b).isEqualTo(a); BuilderWithCopyingSetters c = builder.setThings(1, 2).build(); assertThat(c).isEqualTo(a); } @AutoValue public abstract static class BuilderWithImmutableSorted> { public abstract ImmutableSortedSet sortedSet(); public abstract ImmutableSortedMap sortedMap(); public static > Builder builder() { return new AutoValue_AutoValueTest_BuilderWithImmutableSorted.Builder() .setSortedSet(new TreeSet()) .setSortedMap(new TreeMap()); } @AutoValue.Builder public interface Builder> { @SuppressWarnings("unchecked") Builder setSortedSet(T... x); Builder setSortedSet(NavigableSet x); ImmutableSortedSet.Builder sortedSetBuilder(); Builder setSortedMap(SortedMap x); Builder setSortedMap(NavigableMap x); ImmutableSortedMap.Builder sortedMapBuilder(); BuilderWithImmutableSorted build(); } } @Test public void testBuilderWithImmutableSorted_Varargs() { BuilderWithImmutableSorted x = BuilderWithImmutableSorted.builder().setSortedSet("foo", "bar", "baz").build(); assertThat(x.sortedSet()).containsExactly("bar", "baz", "foo").inOrder(); } @Test public void testBuilderWithImmutableSorted_SetSet() { BuilderWithImmutableSorted x = BuilderWithImmutableSorted.builder() .setSortedSet(new TreeSet(String.CASE_INSENSITIVE_ORDER)) .build(); assertThat(x.sortedSet().comparator()).isEqualTo(String.CASE_INSENSITIVE_ORDER); } @Test public void testBuilderWithImmutableSorted_SetMap() { BuilderWithImmutableSorted x = BuilderWithImmutableSorted.builder() .setSortedMap(new TreeMap(String.CASE_INSENSITIVE_ORDER)) .build(); assertThat(x.sortedMap().comparator()).isEqualTo(String.CASE_INSENSITIVE_ORDER); } @Test public void testBuilderWithImmutableSorted_SetCollectionBuilder() { BuilderWithImmutableSorted.Builder builder = BuilderWithImmutableSorted.builder(); builder.sortedSetBuilder().add("is", "ea", "id"); BuilderWithImmutableSorted x = builder.build(); assertThat(x.sortedSet()).containsExactly("ea", "id", "is").inOrder(); } @Test public void testBuilderWithImmutableSorted_MapCollectionBuilder() { BuilderWithImmutableSorted.Builder builder = BuilderWithImmutableSorted.builder(); builder.sortedMapBuilder().put("two", 2).put("one", 1); BuilderWithImmutableSorted x = builder.build(); assertThat(x.sortedMap()).containsExactly("one", 1, "two", 2).inOrder(); } @AutoValue public abstract static class BuilderWithCollectionBuilderAndSetter { public abstract ImmutableList things(); public static Builder builder() { return new AutoValue_AutoValueTest_BuilderWithCollectionBuilderAndSetter.Builder(); } @AutoValue.Builder public interface Builder { Builder setThings(List things); ImmutableList things(); ImmutableList.Builder thingsBuilder(); BuilderWithCollectionBuilderAndSetter build(); } } @Test public void testBuilderAndSetterDefaultsEmpty() { BuilderWithCollectionBuilderAndSetter.Builder builder = BuilderWithCollectionBuilderAndSetter.builder(); assertThat(builder.things()).isEmpty(); assertThat(builder.build().things()).isEmpty(); } @Test public void testBuilderAndSetterUsingBuilder() { BuilderWithCollectionBuilderAndSetter.Builder builder = BuilderWithCollectionBuilderAndSetter.builder(); builder.thingsBuilder().add(17, 23); BuilderWithCollectionBuilderAndSetter x = builder.build(); assertThat(x.things()).isEqualTo(ImmutableList.of(17, 23)); } @Test public void testBuilderAndSetterUsingSetter() { ImmutableList things = ImmutableList.of(17, 23); BuilderWithCollectionBuilderAndSetter.Builder builder = BuilderWithCollectionBuilderAndSetter.builder().setThings(things); assertThat(builder.things()).isSameInstanceAs(things); assertThat(builder.build().things()).isSameInstanceAs(things); List moreThings = Arrays.asList(5, 17, 23); BuilderWithCollectionBuilderAndSetter.Builder builder2 = BuilderWithCollectionBuilderAndSetter.builder().setThings(moreThings); assertThat(builder2.things()).isEqualTo(moreThings); assertThat(builder2.build().things()).isEqualTo(moreThings); } @Test public void testBuilderAndSetterUsingSetterThenBuilder() { BuilderWithCollectionBuilderAndSetter.Builder builder = BuilderWithCollectionBuilderAndSetter.builder(); builder.setThings(ImmutableList.of(5)); builder.thingsBuilder().add(17, 23); List expectedThings = ImmutableList.of(5, 17, 23); assertThat(builder.things()).isEqualTo(expectedThings); assertThat(builder.build().things()).isEqualTo(expectedThings); } @Test public void testBuilderAndSetterCannotSetAfterBuilder() { BuilderWithCollectionBuilderAndSetter.Builder builder = BuilderWithCollectionBuilderAndSetter.builder(); builder.setThings(ImmutableList.of(5)); builder.thingsBuilder().add(17, 23); try { builder.setThings(ImmutableList.of(1729)); fail("Setting list after retrieving builder should provoke an exception"); } catch (IllegalStateException e) { if (omitIdentifiers) { assertThat(e).hasMessageThat().isNull(); } else { assertThat(e).hasMessageThat().isEqualTo("Cannot set things after calling thingsBuilder()"); } } } abstract static class AbstractParentWithBuilder { abstract String foo(); abstract static class Builder> { abstract B foo(String s); } } @AutoValue abstract static class ChildWithBuilder extends AbstractParentWithBuilder { abstract String bar(); static Builder builder() { return new AutoValue_AutoValueTest_ChildWithBuilder.Builder(); } @AutoValue.Builder abstract static class Builder extends AbstractParentWithBuilder.Builder { abstract Builder bar(String s); abstract ChildWithBuilder build(); } } @Test public void testInheritedBuilder() { ChildWithBuilder x = ChildWithBuilder.builder().foo("foo").bar("bar").build(); assertThat(x.foo()).isEqualTo("foo"); assertThat(x.bar()).isEqualTo("bar"); } @Retention(RetentionPolicy.RUNTIME) @interface GwtCompatible { boolean funky() default false; } @AutoValue @GwtCompatible(funky = true) abstract static class GwtCompatibleTest { abstract int foo(); static GwtCompatibleTest create(int foo) { return new AutoValue_AutoValueTest_GwtCompatibleTest(foo); } } @AutoValue @GwtCompatible abstract static class GwtCompatibleTestNoArgs { abstract String bar(); static GwtCompatibleTestNoArgs create(String bar) { return new AutoValue_AutoValueTest_GwtCompatibleTestNoArgs(bar); } } @Test public void testGwtCompatibleInherited() { GwtCompatibleTest test = GwtCompatibleTest.create(23); GwtCompatible gwtCompatible = test.getClass().getAnnotation(GwtCompatible.class); assertNotNull(gwtCompatible); assertTrue(gwtCompatible.funky()); GwtCompatibleTestNoArgs testNoArgs = GwtCompatibleTestNoArgs.create("23"); GwtCompatible gwtCompatibleNoArgs = testNoArgs.getClass().getAnnotation(GwtCompatible.class); assertNotNull(gwtCompatibleNoArgs); assertFalse(gwtCompatibleNoArgs.funky()); } @interface NestedAnnotation { int anInt(); Class[] aClassArray(); } @Retention(RetentionPolicy.RUNTIME) @interface HairyAnnotation { String aString(); Class aClass(); RetentionPolicy anEnum(); NestedAnnotation anAnnotation(); } @Retention(RetentionPolicy.RUNTIME) @interface CopiedAnnotation {} @Retention(RetentionPolicy.RUNTIME) @interface ExcludedAnnotation {} @Retention(RetentionPolicy.RUNTIME) @Inherited @interface InheritedAnnotation {} @CopiedAnnotation @ExcludedAnnotation @InheritedAnnotation @AutoValue @AutoValue.CopyAnnotations(exclude = {ExcludedAnnotation.class}) abstract static class CopyAnnotation { @HairyAnnotation( aString = "hello", aClass = Integer.class, anEnum = RetentionPolicy.RUNTIME, anAnnotation = @NestedAnnotation( anInt = 73, aClassArray = {String.class, Object.class})) abstract String field1(); @CopiedAnnotation @ExcludedAnnotation @InheritedAnnotation @AutoValue.CopyAnnotations(exclude = {ExcludedAnnotation.class}) abstract String field2(); static CopyAnnotation create() { return new AutoValue_AutoValueTest_CopyAnnotation("field1", "field2"); } } @Test public void testCopyClassAnnotations() throws Exception { CopyAnnotation x = CopyAnnotation.create(); Class c = x.getClass(); assertNotSame(CopyAnnotation.class, c); // Sanity check: if these don't appear on CopyAnnotation, it makes no sense to assert that they // don't appear on the AutoValue_ subclass. { List> annotationsOnSuperclass = new ArrayList>(); for (Annotation annotation : CopyAnnotation.class.getDeclaredAnnotations()) { annotationsOnSuperclass.add(annotation.annotationType()); } assertThat(annotationsOnSuperclass) .containsAtLeast( CopiedAnnotation.class, ExcludedAnnotation.class, InheritedAnnotation.class); } { List> annotationsOnSubclass = new ArrayList>(); for (Annotation annotation : c.getDeclaredAnnotations()) { annotationsOnSubclass.add(annotation.annotationType()); } assertThat(annotationsOnSubclass).containsExactly(CopiedAnnotation.class); } } @Test public void testCopyMethodAnnotations() throws Exception { CopyAnnotation x = CopyAnnotation.create(); Class c = x.getClass(); assertNotSame(CopyAnnotation.class, c); Method methodInSubclass = c.getDeclaredMethod("field2"); Method methodInSuperclass = CopyAnnotation.class.getDeclaredMethod("field2"); // Sanity check: if these don't appear on CopyAnnotation, it makes no sense to assert that they // don't appear on the AutoValue_ subclass. assertThat(methodInSuperclass.isAnnotationPresent(CopiedAnnotation.class)).isTrue(); assertThat(methodInSuperclass.isAnnotationPresent(ExcludedAnnotation.class)).isTrue(); assertThat(methodInSuperclass.isAnnotationPresent(InheritedAnnotation.class)).isTrue(); assertThat(methodInSubclass.isAnnotationPresent(CopiedAnnotation.class)).isTrue(); assertThat(methodInSubclass.isAnnotationPresent(ExcludedAnnotation.class)).isFalse(); assertThat(methodInSubclass.isAnnotationPresent(InheritedAnnotation.class)).isTrue(); } @Test public void testCopyMethodAnnotationsByDefault() throws Exception { CopyAnnotation x = CopyAnnotation.create(); Class c = x.getClass(); assertNotSame(CopyAnnotation.class, c); Method methodInSubclass = c.getDeclaredMethod("field1"); Method methodInSuperclass = CopyAnnotation.class.getDeclaredMethod("field1"); assertNotSame(methodInSuperclass, methodInSubclass); HairyAnnotation annotationInSubclass = methodInSubclass.getAnnotation(HairyAnnotation.class); HairyAnnotation annotationInSuperclass = methodInSuperclass.getAnnotation(HairyAnnotation.class); assertEquals(annotationInSuperclass, annotationInSubclass); } @AutoValue abstract static class HProperty { public abstract Object h(); public static HProperty create(Object h) { return new AutoValue_AutoValueTest_HProperty(h); } } @Test public void testHProperty() throws Exception { // Checks that we can have a property called `h`. The generated hashCode() method has // a local variable of that name and can cause the error `int cannot be dereferenced` HProperty.create(new Object()); } interface Parent1 { int something(); } interface Parent2 { int something(); } @AutoValue abstract static class InheritSameMethodTwice implements Parent1, Parent2 { static InheritSameMethodTwice create(int something) { return new AutoValue_AutoValueTest_InheritSameMethodTwice(something); } } @Test public void testInheritSameMethodTwice() { InheritSameMethodTwice x = InheritSameMethodTwice.create(23); assertThat(x.something()).isEqualTo(23); } // Make sure we behave correctly when we inherit the same method definition from more than // one parent interface. We expect methods to appear in the order they are seen, with parents // preceding children, the superclass of a class preceding interfaces that class implements, // and an interface mentioned earlier in the "implements" clause preceding one mentioned later. // https://github.com/google/auto/issues/372 interface OneTwoThreeFour { String one(); String two(); boolean three(); long four(); } interface TwoFour { String two(); long four(); } @AutoValue abstract static class OneTwoThreeFourImpl implements OneTwoThreeFour, TwoFour { static OneTwoThreeFourImpl create(String one, String two, boolean three, long four) { return new AutoValue_AutoValueTest_OneTwoThreeFourImpl(one, two, three, four); } } @Test public void testOneTwoThreeFour() { OneTwoThreeFour x = OneTwoThreeFourImpl.create("one", "two", false, 4); String expectedString = omitIdentifiers ? "{one, two, false, 4}" : "OneTwoThreeFourImpl{one=one, two=two, three=false, four=4}"; assertThat(x.toString()).isEqualTo(expectedString); } @AutoValue abstract static class OuterWithBuilder { abstract String foo(); abstract InnerWithBuilder inner(); abstract Builder toBuilder(); static Builder builder() { return new AutoValue_AutoValueTest_OuterWithBuilder.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder foo(String x); abstract Builder inner(InnerWithBuilder x); abstract InnerWithBuilder.Builder innerBuilder(); abstract OuterWithBuilder build(); } } @AutoValue abstract static class InnerWithBuilder { abstract int bar(); abstract Builder toBuilder(); static Builder builder() { return new AutoValue_AutoValueTest_InnerWithBuilder.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setBar(int x); abstract InnerWithBuilder build(); } } @Test public void testBuilderWithinBuilder() { OuterWithBuilder x = OuterWithBuilder.builder() .inner(InnerWithBuilder.builder().setBar(23).build()) .foo("yes") .build(); String expectedStringX = omitIdentifiers ? "{yes, {23}}" : "OuterWithBuilder{foo=yes, inner=InnerWithBuilder{bar=23}}"; assertThat(x.toString()).isEqualTo(expectedStringX); OuterWithBuilder.Builder xBuilder = x.toBuilder(); xBuilder.innerBuilder().setBar(17); OuterWithBuilder y = xBuilder.build(); String expectedStringY = omitIdentifiers ? "{yes, {17}}" : "OuterWithBuilder{foo=yes, inner=InnerWithBuilder{bar=17}}"; assertThat(y.toString()).isEqualTo(expectedStringY); } public static class MyMap extends HashMap { public MyMap() {} public MyMap(Map map) { super(map); } } public static class MyMapBuilder extends LinkedHashMap { public MyMapBuilder() {} public MyMapBuilder(Map map) { super(map); } public MyMap build() { return new MyMap(this); } } @AutoValue abstract static class BuildMyMap { abstract MyMap map(); abstract Builder toBuilder(); static Builder builder() { return new AutoValue_AutoValueTest_BuildMyMap.Builder(); } @AutoValue.Builder abstract static class Builder { abstract MyMapBuilder mapBuilder(); abstract BuildMyMap build(); } } @Test public void testMyMapBuilder() { BuildMyMap.Builder builder = BuildMyMap.builder(); MyMapBuilder mapBuilder = builder.mapBuilder(); mapBuilder.put("23", 23); BuildMyMap built = builder.build(); assertThat(built.map()).containsExactly("23", 23); BuildMyMap.Builder builder2 = built.toBuilder(); MyMapBuilder mapBuilder2 = builder2.mapBuilder(); mapBuilder2.put("17", 17); BuildMyMap built2 = builder2.build(); assertThat(built2.map()).containsExactly("23", 23, "17", 17); } public static class MyStringMap extends MyMap { public MyStringMap() {} public MyStringMap(Map map) { super(map); } public MyStringMapBuilder toBuilder() { return new MyStringMapBuilder(this); } } public static class MyStringMapBuilder extends MyMapBuilder { public MyStringMapBuilder() {} public MyStringMapBuilder(Map map) { super(map); } @Override public MyStringMap build() { return new MyStringMap(this); } } @AutoValue abstract static class BuildMyStringMap { abstract MyStringMap map(); abstract Builder toBuilder(); static Builder builder() { return new AutoValue_AutoValueTest_BuildMyStringMap.Builder(); } @AutoValue.Builder abstract static class Builder { abstract MyStringMapBuilder mapBuilder(); abstract BuildMyStringMap build(); } } @Test public void testMyStringMapBuilder() { BuildMyStringMap.Builder builder = BuildMyStringMap.builder(); MyStringMapBuilder mapBuilder = builder.mapBuilder(); mapBuilder.put("23", 23); BuildMyStringMap built = builder.build(); assertThat(built.map()).containsExactly("23", 23); BuildMyStringMap.Builder builder2 = built.toBuilder(); MyStringMapBuilder mapBuilder2 = builder2.mapBuilder(); mapBuilder2.put("17", 17); BuildMyStringMap built2 = builder2.build(); assertThat(built2.map()).containsExactly("17", 17, "23", 23); } @AutoValue abstract static class BuilderOfManyAccessLevels { public abstract int publicGetterProtectedBuilderGetterPackageProtectedSetterInt(); protected abstract int protectedGetterPackageProtectedBuilderGetterPublicSetterInt(); abstract int packageProtectedGetterPublicBuilderGetterProtectedSetterInt(); @AutoValue.Builder public abstract static class Builder { protected abstract int publicGetterProtectedBuilderGetterPackageProtectedSetterInt(); abstract int protectedGetterPackageProtectedBuilderGetterPublicSetterInt(); public abstract int packageProtectedGetterPublicBuilderGetterProtectedSetterInt(); abstract Builder setPublicGetterProtectedBuilderGetterPackageProtectedSetterInt(int x); public abstract Builder setProtectedGetterPackageProtectedBuilderGetterPublicSetterInt(int x); protected abstract Builder setPackageProtectedGetterPublicBuilderGetterProtectedSetterInt( int x); public abstract BuilderOfManyAccessLevels build(); } } @Test public void testBuilderOfManyAccessLevels_accessLevels() throws NoSuchMethodException { Class builderClass = AutoValue_AutoValueTest_BuilderOfManyAccessLevels.Builder.class; testMethodAccess( Access.PROTECTED, builderClass, "publicGetterProtectedBuilderGetterPackageProtectedSetterInt"); testMethodAccess( Access.PACKAGE, builderClass, "protectedGetterPackageProtectedBuilderGetterPublicSetterInt"); testMethodAccess( Access.PUBLIC, builderClass, "packageProtectedGetterPublicBuilderGetterProtectedSetterInt"); testMethodAccess( Access.PACKAGE, builderClass, "setPublicGetterProtectedBuilderGetterPackageProtectedSetterInt", int.class); testMethodAccess( Access.PUBLIC, builderClass, "setProtectedGetterPackageProtectedBuilderGetterPublicSetterInt", int.class); testMethodAccess( Access.PROTECTED, builderClass, "setPackageProtectedGetterPublicBuilderGetterProtectedSetterInt", int.class); } private enum Access { PRIVATE, PACKAGE, PROTECTED, PUBLIC } private static final ImmutableMap MODIFIER_BITS_TO_ACCESS = ImmutableMap.of( Modifier.PUBLIC, Access.PUBLIC, Modifier.PROTECTED, Access.PROTECTED, Modifier.PRIVATE, Access.PRIVATE, 0, Access.PACKAGE); private static void testMethodAccess( Access expectedAccess, Class clazz, String methodName, Class... parameterTypes) throws NoSuchMethodException { Method method = clazz.getDeclaredMethod(methodName, parameterTypes); int modBits = method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE); Access actualAccess = MODIFIER_BITS_TO_ACCESS.get(modBits); assertWithMessage("Wrong access for %s", methodName) .that(actualAccess) .isEqualTo(expectedAccess); } static class VersionId {} static class ItemVersionId extends VersionId {} interface VersionedPersistent { VersionId getVersionId(); } interface Item extends VersionedPersistent { @Override ItemVersionId getVersionId(); } @AutoValue abstract static class FakeItem implements Item { static Builder builder() { return new AutoValue_AutoValueTest_FakeItem.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setVersionId(ItemVersionId x); abstract FakeItem build(); } } @Test public void testParentInterfaceOverridesGrandparent() { ItemVersionId version = new ItemVersionId(); FakeItem fakeItem = FakeItem.builder().setVersionId(version).build(); assertThat(fakeItem.getVersionId()).isSameInstanceAs(version); } /** Fake ApkVersionCode class. */ public static class ApkVersionCode {} /** * Illustrates a potential problem that showed up while generalizing builders. If our imports are * not accurate we may end up importing ImmutableList.Builder, which won't work because the * generated Builder subclass of ReleaseInfoBuilder will supersede it. Normally we wouldn't import * ImmutableList.Builder because the nested Builder class in the {@code @AutoValue} class would * prevent us trying. But in this case the nested class is called ReleaseInfoBuilder so we might * import anyway if we're not careful. This is one reason why we moved away from importing nested * classes to only importing top-level classes. */ @AutoValue public abstract static class ReleaseInfo { public static ReleaseInfoBuilder newBuilder() { return new AutoValue_AutoValueTest_ReleaseInfo.Builder(); } public abstract ImmutableList apkVersionCodes(); ReleaseInfo() {} /** Notice that this is called ReleaseInfoBuilder and not Builder. */ @AutoValue.Builder public abstract static class ReleaseInfoBuilder { public ReleaseInfoBuilder addApkVersionCode(ApkVersionCode code) { apkVersionCodesBuilder().add(code); return this; } abstract ImmutableList.Builder apkVersionCodesBuilder(); public abstract ReleaseInfo build(); } } @Test public void testUnusualBuilderName() { ApkVersionCode apkVersionCode = new ApkVersionCode(); ReleaseInfo x = ReleaseInfo.newBuilder().addApkVersionCode(apkVersionCode).build(); assertThat(x.apkVersionCodes()).containsExactly(apkVersionCode); } @AutoValue public abstract static class OuterWithDefaultableInner { public abstract ImmutableList names(); public abstract DefaultableInner inner(); public static Builder builder() { return new AutoValue_AutoValueTest_OuterWithDefaultableInner.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract ImmutableList names(); public abstract ImmutableList.Builder namesBuilder(); public abstract DefaultableInner inner(); public abstract DefaultableInner.Builder innerBuilder(); public abstract OuterWithDefaultableInner build(); } } @AutoValue public abstract static class DefaultableInner { public abstract int bar(); public static Builder builder() { return new AutoValue_AutoValueTest_DefaultableInner.Builder().setBar(23); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setBar(int x); public abstract DefaultableInner build(); } } @Test public void testOuterWithDefaultableInner_Defaults() { DefaultableInner defaultInner = DefaultableInner.builder().build(); OuterWithDefaultableInner x = OuterWithDefaultableInner.builder().build(); assertThat(x.names()).isEmpty(); assertThat(x.inner()).isEqualTo(defaultInner); } @Test public void testOuterWithDefaultableInner_Getters() { DefaultableInner defaultInner = DefaultableInner.builder().build(); OuterWithDefaultableInner.Builder builder = OuterWithDefaultableInner.builder(); assertThat(builder.names()).isEmpty(); assertThat(builder.inner()).isEqualTo(defaultInner); OuterWithDefaultableInner x1 = builder.build(); assertThat(x1.names()).isEmpty(); assertThat(x1.inner()).isEqualTo(defaultInner); builder.namesBuilder().add("Fred"); builder.innerBuilder().setBar(17); OuterWithDefaultableInner x2 = builder.build(); assertThat(x2.names()).containsExactly("Fred"); assertThat(x2.inner().bar()).isEqualTo(17); } @AutoValue public abstract static class OuterWithNonDefaultableInner { public abstract int foo(); public abstract NonDefaultableInner inner(); public static Builder builder() { return new AutoValue_AutoValueTest_OuterWithNonDefaultableInner.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setFoo(int x); public abstract NonDefaultableInner.Builder innerBuilder(); public abstract OuterWithNonDefaultableInner build(); } } @AutoValue public abstract static class NonDefaultableInner { public abstract E bar(); public static Builder builder() { return new AutoValue_AutoValueTest_NonDefaultableInner.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setBar(E x); public abstract NonDefaultableInner build(); } } @Test public void testOuterWithNonDefaultableInner() { OuterWithNonDefaultableInner.Builder builder = OuterWithNonDefaultableInner.builder(); builder.setFoo(23); try { builder.build(); fail("Did not get expected exception for unbuilt inner instance"); } catch (IllegalStateException expected) { } } @SuppressWarnings("JavaLangClash") @AutoValue public abstract static class RedeclareJavaLangClasses { // If you really really want to do this, we have you covered. public static class Object {} public static class String {} public abstract Object alienObject(); public abstract String alienString(); public static Builder builder() { return new AutoValue_AutoValueTest_RedeclareJavaLangClasses.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setAlienObject(Object x); public abstract Builder setAlienString(String x); public abstract RedeclareJavaLangClasses build(); } } @Test public void testRedeclareJavaLangClasses() { RedeclareJavaLangClasses x = RedeclareJavaLangClasses.builder() .setAlienObject(new RedeclareJavaLangClasses.Object()) .setAlienString(new RedeclareJavaLangClasses.String()) .build(); assertThat(x).isNotNull(); } // b/28382293 @AutoValue abstract static class GenericExtends { abstract ImmutableSet metrics(); static Builder builder() { return new AutoValue_AutoValueTest_GenericExtends.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setMetrics(ImmutableSet metrics); abstract GenericExtends build(); } } @Test public void testGenericExtends() { ImmutableSet ints = ImmutableSet.of(1, 2, 3); GenericExtends g = GenericExtends.builder().setMetrics(ints).build(); assertThat(g.metrics()).isEqualTo(ints); } abstract static class Parent { abstract List getList(); } @AutoValue abstract static class Child extends Parent { static Builder builder() { return new AutoValue_AutoValueTest_Child.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setList(List list); abstract Child build(); } } @Test public void nonGenericExtendsGeneric() { List list = ImmutableList.of("foo", "bar", "baz"); Child child = Child.builder().setList(list).build(); assertThat(child.getList()).containsExactlyElementsIn(list).inOrder(); } abstract static class AbstractGenericParentWithBuilder { abstract T foo(); abstract static class Builder> { abstract B foo(T s); } } @AutoValue abstract static class ChildOfAbstractGenericParentWithBuilder extends AbstractGenericParentWithBuilder { static Builder builder() { return new AutoValue_AutoValueTest_ChildOfAbstractGenericParentWithBuilder.Builder(); } @AutoValue.Builder abstract static class Builder extends AbstractGenericParentWithBuilder.Builder> { abstract ChildOfAbstractGenericParentWithBuilder build(); } } @Test public void genericExtendsGeneric() { ChildOfAbstractGenericParentWithBuilder child = ChildOfAbstractGenericParentWithBuilder.builder().foo("foo").build(); assertThat(child.foo()).isEqualTo("foo"); } @SuppressWarnings("ClassCanBeStatic") static class OuterWithTypeParam { class InnerWithTypeParam {} class InnerWithoutTypeParam {} static class Nested {} } @AutoValue abstract static class Nesty { abstract OuterWithTypeParam.InnerWithTypeParam innerWithTypeParam(); abstract OuterWithTypeParam.InnerWithoutTypeParam innerWithoutTypeParam(); abstract OuterWithTypeParam.Nested nested(); static Builder builder() { return new AutoValue_AutoValueTest_Nesty.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setInnerWithTypeParam( OuterWithTypeParam.InnerWithTypeParam x); abstract Builder setInnerWithoutTypeParam(OuterWithTypeParam.InnerWithoutTypeParam x); abstract Builder setNested(OuterWithTypeParam.Nested x); abstract Nesty build(); } } @Test public void outerWithTypeParam() throws ReflectiveOperationException { @SuppressWarnings("UseDiamond") // Currently we compile this with -source 6 in the Eclipse test. OuterWithTypeParam outer = new OuterWithTypeParam(); Nesty nesty = Nesty.builder() .setInnerWithTypeParam(outer.new InnerWithTypeParam()) .setInnerWithoutTypeParam(outer.new InnerWithoutTypeParam()) .setNested(new OuterWithTypeParam.Nested()) .build(); Type originalReturnType = Nesty.class.getDeclaredMethod("innerWithTypeParam").getGenericReturnType(); Type generatedReturnType = nesty.getClass().getDeclaredMethod("innerWithTypeParam").getGenericReturnType(); assertThat(generatedReturnType).isEqualTo(originalReturnType); Type generatedBuilderParamType = Nesty.builder() .getClass() .getDeclaredMethod("setInnerWithTypeParam", OuterWithTypeParam.InnerWithTypeParam.class) .getGenericParameterTypes()[0]; assertThat(generatedBuilderParamType).isEqualTo(originalReturnType); } } CompileWithEclipseTest.java000066400000000000000000000134571365703632600361070ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2014 Google LLC * * 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.google.auto.value; import static com.google.common.base.StandardSystemProperty.JAVA_HOME; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import com.google.auto.value.processor.AutoAnnotationProcessor; import com.google.auto.value.processor.AutoOneOfProcessor; import com.google.auto.value.processor.AutoValueProcessor; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.processing.Processor; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.StandardLocation; import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests that we can compile our AutoValue tests using the Eclipse batch compiler. Since the tests * exercise many AutoValue subtleties, the ability to compile them all is a good indication of * Eclipse support. */ @RunWith(JUnit4.class) public class CompileWithEclipseTest { private static final String SOURCE_ROOT = System.getProperty("basedir"); @BeforeClass public static void setSourceRoot() { assertWithMessage("basedir property must be set - test must be run from Maven") .that(SOURCE_ROOT).isNotNull(); } public @Rule TemporaryFolder tmp = new TemporaryFolder(); private static final ImmutableSet IGNORED_TEST_FILES = ImmutableSet.of("AutoValueNotEclipseTest.java", "CompileWithEclipseTest.java"); private static final Predicate JAVA_FILE = f -> f.getName().endsWith(".java") && !IGNORED_TEST_FILES.contains(f.getName()); private static final Predicate JAVA8_TEST = f -> f.getName().equals("AutoValueJava8Test.java") || f.getName().equals("AutoOneOfJava8Test.java") || f.getName().equals("EmptyExtension.java"); @Test public void compileWithEclipseJava6() throws Exception { compileWithEclipse("6", JAVA_FILE.and(JAVA8_TEST.negate())); } @Test public void compileWithEclipseJava8() throws Exception { compileWithEclipse("8", JAVA_FILE); } private void compileWithEclipse(String version, Predicate predicate) throws IOException { File sourceRootFile = new File(SOURCE_ROOT); File javaDir = new File(sourceRootFile, "src/main/java"); File javatestsDir = new File(sourceRootFile, "src/test/java"); Set sources = new ImmutableSet.Builder() .addAll(filesUnderDirectory(javaDir, predicate)) .addAll(filesUnderDirectory(javatestsDir, predicate)) .build(); assertThat(sources).isNotEmpty(); JavaCompiler compiler = new EclipseCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); // This hack is only needed in a Google-internal Java 8 environment where symbolic links make it // hard for ecj to find the boot class path. Elsewhere it is unnecessary but harmless. Notably, // on Java 9+ there is no rt.jar. There, fileManager.getLocation(PLATFORM_CLASS_PATH) returns // null, because the relevant classes are in modules inside // fileManager.getLocation(SYSTEM_MODULES). File rtJar = new File(JAVA_HOME.value() + "/lib/rt.jar"); if (rtJar.exists()) { List bootClassPath = ImmutableList.builder() .add(rtJar) .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH)) .build(); fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath); } Iterable sourceFileObjects = fileManager.getJavaFileObjectsFromFiles(sources); String outputDir = tmp.getRoot().toString(); ImmutableList options = ImmutableList.of("-d", outputDir, "-s", outputDir, "-source", version, "-target", version); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, sourceFileObjects); // Explicitly supply an empty list of extensions for AutoValueProcessor, because otherwise this // test will pick up a test one and get confused. AutoValueProcessor autoValueProcessor = new AutoValueProcessor(ImmutableList.of()); ImmutableList processors = ImmutableList.of( autoValueProcessor, new AutoOneOfProcessor(), new AutoAnnotationProcessor()); task.setProcessors(processors); assertWithMessage("Compilation should succeed").that(task.call()).isTrue(); } private static ImmutableSet filesUnderDirectory(File dir, Predicate predicate) throws IOException { assertWithMessage(dir.toString()).that(dir.isDirectory()).isTrue(); try (Stream paths = Files.walk(dir.toPath())) { return paths.map(Path::toFile).filter(predicate).collect(toImmutableSet()); } } } SimpleValueTypeTest.java000066400000000000000000000043231365703632600354360ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/* * Copyright 2012 Google LLC * * 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.google.auto.value; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import com.google.common.collect.ImmutableMap; import com.google.common.testing.NullPointerTester; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class SimpleValueTypeTest { @Test public void testSimpleValueType() { final String happy = "happy"; final int testInt = 23; final Map testMap = ImmutableMap.of("happy", 23L); SimpleValueType simple = SimpleValueType.create(happy, testInt, testMap); assertSame(happy, simple.string()); assertEquals(testInt, simple.integer()); assertSame(testMap, simple.map()); assertEquals("SimpleValueType{string=happy, integer=23, map={happy=23}}", simple.toString()); int expectedHashCode = 1; expectedHashCode = (expectedHashCode * 1000003) ^ happy.hashCode(); expectedHashCode = (expectedHashCode * 1000003) ^ ((Object) testInt).hashCode(); expectedHashCode = (expectedHashCode * 1000003) ^ testMap.hashCode(); assertEquals(expectedHashCode, simple.hashCode()); } @Test public void testNestedValueType() { ImmutableMap numberNames = ImmutableMap.of(1, "un", 2, "deux"); NestedValueType.Nested nested = NestedValueType.Nested.create(numberNames); assertEquals(numberNames, nested.numberNames()); } @Test public void testNull() { NullPointerTester tester = new NullPointerTester(); tester.testAllPublicStaticMethods(SimpleValueType.class); } } auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/annotations/000077500000000000000000000000001365703632600332555ustar00rootroot00000000000000Empty.java000066400000000000000000000016721365703632600351450ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/annotations/* * Copyright 2014 Google LLC * * 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.google.auto.value.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * An annotation with no members, for tests, and runtime retention so it can be accessed through * reflection. * * @author emcmanus@google.com (Éamonn McManus) */ @Retention(RetentionPolicy.RUNTIME) public @interface Empty {} GwtArrays.java000066400000000000000000000016331365703632600357670ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/annotations/* * Copyright 2015 Google LLC * * 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.google.auto.value.annotations; import com.google.common.annotations.GwtCompatible; /** * An annotation that is marked {@code @GwtCompatible} and that contains an array member. * * @author emcmanus@google.com (Éamonn McManus) */ @GwtCompatible public @interface GwtArrays { String[] strings(); int[] ints(); } StringValues.java000066400000000000000000000020041365703632600364630ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/annotations/* * Copyright 2014 Google LLC * * 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.google.auto.value.annotations; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * An annotation with one member, an array of strings with the default name {@code value}, and * runtime retention so it can be accessed through reflection. * * @author emcmanus@google.com (Éamonn McManus) */ @Retention(RetentionPolicy.RUNTIME) public @interface StringValues { String[] value(); } auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/enums/000077500000000000000000000000001365703632600320475ustar00rootroot00000000000000MyEnum.java000066400000000000000000000013371365703632600340510ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/enums/* * Copyright 2014 Google LLC * * 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.google.auto.value.enums; /** @author emcmanus@google.com (Éamonn McManus) */ public enum MyEnum { ONE, TWO, BUCKLE_MY_SHOE } auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/000077500000000000000000000000001365703632600315215ustar00rootroot00000000000000CustomFieldSerializerTest.java000066400000000000000000000206611365703632600374220ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2014 Google LLC * * 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.google.auto.value.gwt; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import com.google.auto.value.AutoValue; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamWriter; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Tests that the generated GWT serializer for GwtValueType serializes fields in the expected way. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class CustomFieldSerializerTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); } @AutoValue @GwtCompatible(serializable = true) abstract static class ValueType implements Serializable { abstract String string(); abstract int integer(); @Nullable abstract ValueType other(); abstract List others(); static ValueType create(String string, int integer, @Nullable ValueType other) { return create(string, integer, other, Collections.emptyList()); } static ValueType create( String string, int integer, @Nullable ValueType other, List others) { return new AutoValue_CustomFieldSerializerTest_ValueType(string, integer, other, others); } } private static final ValueType SIMPLE = ValueType.create("anotherstring", 1729, null); private static final ValueType CONS = ValueType.create("whatever", 1296, SIMPLE); private static final ValueType WITH_LIST = ValueType.create("blim", 11881376, SIMPLE, ImmutableList.of(SIMPLE, CONS)); @Mock SerializationStreamWriter streamWriter; @Test public void testCustomFieldSerializer() throws SerializationException { AutoValue_CustomFieldSerializerTest_ValueType withList = (AutoValue_CustomFieldSerializerTest_ValueType) WITH_LIST; AutoValue_CustomFieldSerializerTest_ValueType_CustomFieldSerializer.serialize( streamWriter, withList); verify(streamWriter).writeString("blim"); verify(streamWriter).writeInt(11881376); verify(streamWriter).writeObject(SIMPLE); verify(streamWriter).writeObject(ImmutableList.of(SIMPLE, CONS)); verifyNoMoreInteractions(streamWriter); } @AutoValue @GwtCompatible(serializable = true) abstract static class ValueTypeWithGetters implements Serializable { abstract String getPackage(); abstract boolean isDefault(); static ValueTypeWithGetters create(String pkg, boolean dflt) { return new AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters(pkg, dflt); } } @Test public void testCustomFieldSerializerWithGetters() throws SerializationException { AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters instance = (AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters) ValueTypeWithGetters.create("package", true); AutoValue_CustomFieldSerializerTest_ValueTypeWithGetters_CustomFieldSerializer.serialize( streamWriter, instance); verify(streamWriter).writeString("package"); verify(streamWriter).writeBoolean(true); verifyNoMoreInteractions(streamWriter); } @AutoValue @GwtCompatible(serializable = true) abstract static class GenericValueType, V extends K> implements Serializable { abstract Map map(); static , V extends K> GenericValueType create(Map map) { return new AutoValue_CustomFieldSerializerTest_GenericValueType(map); } } @Test public void testCustomFieldSerializerGeneric() throws SerializationException { Map map = ImmutableMap.of(2, 2); AutoValue_CustomFieldSerializerTest_GenericValueType instance = (AutoValue_CustomFieldSerializerTest_GenericValueType) GenericValueType.create(map); AutoValue_CustomFieldSerializerTest_GenericValueType_CustomFieldSerializer.serialize( streamWriter, instance); verify(streamWriter).writeObject(map); verifyNoMoreInteractions(streamWriter); } @AutoValue @GwtCompatible(serializable = true) abstract static class ValueTypeWithBuilder implements Serializable { abstract String string(); abstract ImmutableList strings(); static Builder builder() { return new AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder.Builder(); } @AutoValue.Builder interface Builder { Builder string(String x); Builder strings(ImmutableList x); ValueTypeWithBuilder build(); } } @Test public void testCustomFieldSerializerWithBuilder() throws SerializationException { AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder instance = (AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder) ValueTypeWithBuilder.builder().string("s").strings(ImmutableList.of("a", "b")).build(); AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilder_CustomFieldSerializer.serialize( streamWriter, instance); verify(streamWriter).writeString("s"); verify(streamWriter).writeObject(ImmutableList.of("a", "b")); verifyNoMoreInteractions(streamWriter); } @AutoValue @GwtCompatible(serializable = true) abstract static class ValueTypeWithBuilderAndGetters implements Serializable { abstract String getPackage(); abstract boolean isDefault(); static Builder builder() { return new AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters.Builder(); } @AutoValue.Builder interface Builder { Builder setPackage(String x); Builder setDefault(boolean x); ValueTypeWithBuilderAndGetters build(); } } @Test public void testCustomFieldSerializerWithBuilderAndGetters() throws SerializationException { AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters instance = (AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters) ValueTypeWithBuilderAndGetters.builder().setPackage("s").setDefault(false).build(); AutoValue_CustomFieldSerializerTest_ValueTypeWithBuilderAndGetters_CustomFieldSerializer .serialize(streamWriter, instance); verify(streamWriter).writeString("s"); verify(streamWriter).writeBoolean(false); verifyNoMoreInteractions(streamWriter); } @AutoValue @GwtCompatible(serializable = true) abstract static class GenericValueTypeWithBuilder, V extends K> implements Serializable { abstract Map map(); static , V extends K> Builder builder() { return new AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder.Builder(); } @AutoValue.Builder interface Builder, V extends K> { Builder map(Map map); GenericValueTypeWithBuilder build(); } } @Test public void testCustomFieldSerializerGenericWithBuilder() throws SerializationException { Map map = ImmutableMap.of(2, 2); AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder instance = (AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder) GenericValueTypeWithBuilder.builder().map(map).build(); AutoValue_CustomFieldSerializerTest_GenericValueTypeWithBuilder_CustomFieldSerializer.serialize( streamWriter, instance); verify(streamWriter).writeObject(map); verifyNoMoreInteractions(streamWriter); } } EmptyExtension.java000066400000000000000000000120241365703632600352770ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2018 Google LLC * * 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.google.auto.value.gwt; import static java.util.stream.Collectors.joining; import com.google.auto.service.AutoService; import com.google.auto.value.extension.AutoValueExtension; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.escapevelocity.Template; import java.io.IOException; import java.io.StringReader; import java.util.List; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; /** * An AutoValue extension that generates a subclass that does nothing useful. */ @AutoService(AutoValueExtension.class) public class EmptyExtension extends AutoValueExtension { // TODO(emcmanus): it is way too difficult to write a trivial extension. Problems we have here: // (1) We have to generate a constructor that calls the superclass constructor, which means // declaring the appropriate constructor parameters and then forwarding them to a super // call. // (2) We have to avoid generating variable names that are keywords (we append $ here // to avoid that). // (3) We have to concoct appropriate type parameter strings, for example // final class AutoValue_Foo, V> extends $AutoValue_Foo. // These problems show up with the template approach here, but also using JavaPoet as the // Memoize extension does. private static final ImmutableList TEMPLATE_LINES = ImmutableList.of( "package $package;", "\n", "#if ($isFinal) final #end class ${className}${formalTypes}" + " extends ${classToExtend}${actualTypes} {\n", " ${className}(", " #foreach ($property in $propertyTypes.keySet())", " $propertyTypes[$property] ${property}$ #if ($foreach.hasNext) , #end", " #end", " ) {", " super(", " #foreach ($property in $propertyTypes.keySet())", " ${property}$ #if ($foreach.hasNext) , #end", " #end", " );", " }", "}"); @Override public boolean applicable(Context context) { return true; } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { String templateString = Joiner.on('\n').join(TEMPLATE_LINES); StringReader templateReader = new StringReader(templateString); Template template; try { template = Template.parseFrom(templateReader); } catch (IOException e) { throw new RuntimeException(e); } TypeElement autoValueClass = context.autoValueClass(); ImmutableMap vars = ImmutableMap.builder() .put("package", context.packageName()) .put("className", className) .put("classToExtend", classToExtend) .put("isFinal", isFinal) .put("propertyTypes", context.propertyTypes()) .put("formalTypes", formalTypeParametersString(autoValueClass)) .put("actualTypes", actualTypeParametersString(autoValueClass)) .build(); return template.evaluate(vars); } private static String actualTypeParametersString(TypeElement type) { List typeParameters = type.getTypeParameters(); if (typeParameters.isEmpty()) { return ""; } return typeParameters .stream() .map(e -> e.getSimpleName().toString()) .collect(joining(", ", "<", ">")); } private static String formalTypeParametersString(TypeElement type) { List typeParameters = type.getTypeParameters(); if (typeParameters.isEmpty()) { return ""; } StringBuilder sb = new StringBuilder("<"); String sep = ""; for (TypeParameterElement typeParameter : typeParameters) { sb.append(sep); sep = ", "; appendTypeParameterWithBounds(typeParameter, sb); } return sb.append(">").toString(); } private static void appendTypeParameterWithBounds( TypeParameterElement typeParameter, StringBuilder sb) { sb.append(typeParameter.getSimpleName()); String sep = " extends "; for (TypeMirror bound : typeParameter.getBounds()) { if (!bound.toString().equals("java.lang.Object")) { sb.append(sep); sep = " & "; sb.append(bound); } } } } GwtCompilationTest.java000066400000000000000000000356031365703632600361140ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2014 Google LLC * * 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.google.auto.value.gwt; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.auto.value.processor.AutoValueProcessor; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.lang.model.SourceVersion; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Test of generated source for GWT serialization classes. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class GwtCompilationTest { private static final JavaFileObject GWT_COMPATIBLE = JavaFileObjects.forSourceLines( "com.google.annotations.GwtCompatible", "package com.google.annotations;", "", "public @interface GwtCompatible {", " boolean serializable() default false;", "}"); /** * Test where the serialized properties don't include generics, so no {@code @SuppressWarnings} * annotation is needed. We explicitly check that one is not included anyway, because Eclipse for * example can be configured to complain about unnecessary warning suppression. */ @Test public void testBasic() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.annotations.GwtCompatible;", "", "@AutoValue", "@GwtCompatible(serializable = true)", "public abstract class Baz {", " public abstract int buh();", "", " public static Baz create(int buh) {", " return new AutoValue_Baz(buh);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz_CustomFieldSerializer", "package foo.bar;", "", "import com.google.gwt.user.client.rpc.CustomFieldSerializer;", "import com.google.gwt.user.client.rpc.SerializationException;", "import com.google.gwt.user.client.rpc.SerializationStreamReader;", "import com.google.gwt.user.client.rpc.SerializationStreamWriter;", "import " + generatedAnnotationType() + ";", "", "@Generated(\"com.google.auto.value.processor.AutoValueProcessor\")", "public final class AutoValue_Baz_CustomFieldSerializer" + " extends CustomFieldSerializer {", "", " public static AutoValue_Baz instantiate(", " SerializationStreamReader streamReader) throws SerializationException {", " int buh = streamReader.readInt();", " return new AutoValue_Baz(buh);", " }", "", " public static void serialize(", " SerializationStreamWriter streamWriter,", " AutoValue_Baz instance) throws SerializationException {", " streamWriter.writeInt(instance.buh());", " }", "", " public static void deserialize(", " @SuppressWarnings(\"unused\") SerializationStreamReader streamReader,", " @SuppressWarnings(\"unused\") AutoValue_Baz instance) {", " }", "", " @SuppressWarnings(\"unused\") private int dummy_3f8e1b04;", "", " @Override", " public void deserializeInstance(", " SerializationStreamReader streamReader,", " AutoValue_Baz instance) {", " deserialize(streamReader, instance);", " }", "", " @Override", " public boolean hasCustomInstantiateInstance() {", " return true;", " }", "", " @Override", " public AutoValue_Baz instantiateInstance(", " SerializationStreamReader streamReader) throws SerializationException {", " return instantiate(streamReader);", " }", "", " @Override", " public void serializeInstance(", " SerializationStreamWriter streamWriter,", " AutoValue_Baz instance) throws SerializationException {", " serialize(streamWriter, instance);", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject, GWT_COMPATIBLE); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz_CustomFieldSerializer") .hasSourceEquivalentTo(expectedOutput); } /** * Test where the serialized properties don't include generics, so a {@code @SuppressWarnings} * annotation is needed. */ @Test public void testSuppressWarnings() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.annotations.GwtCompatible;", "", "import java.util.List;", "", "@AutoValue", "@GwtCompatible(serializable = true)", "public abstract class Baz {", " public abstract List buh();", "", " public static Baz create(List buh) {", " return new AutoValue_Baz(buh);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz_CustomFieldSerializer", "package foo.bar;", "", "import com.google.gwt.user.client.rpc.CustomFieldSerializer;", "import com.google.gwt.user.client.rpc.SerializationException;", "import com.google.gwt.user.client.rpc.SerializationStreamReader;", "import com.google.gwt.user.client.rpc.SerializationStreamWriter;", "import java.util.List;", "import " + generatedAnnotationType() + ";", "", "@Generated(\"com.google.auto.value.processor.AutoValueProcessor\")", "public final class AutoValue_Baz_CustomFieldSerializer" + " extends CustomFieldSerializer {", "", " public static AutoValue_Baz instantiate(", " SerializationStreamReader streamReader) throws SerializationException {", " @SuppressWarnings(\"unchecked\")", " List buh = (List) streamReader.readObject();", " return new AutoValue_Baz(buh);", " }", "", " public static void serialize(", " SerializationStreamWriter streamWriter,", " AutoValue_Baz instance) throws SerializationException {", " streamWriter.writeObject(instance.buh());", " }", "", " public static void deserialize(", " @SuppressWarnings(\"unused\") SerializationStreamReader streamReader,", " @SuppressWarnings(\"unused\") AutoValue_Baz instance) {", " }", "", " @SuppressWarnings(\"unused\") private int dummy_949e312e;", "", " @Override", " public void deserializeInstance(", " SerializationStreamReader streamReader,", " AutoValue_Baz instance) {", " deserialize(streamReader, instance);", " }", "", " @Override", " public boolean hasCustomInstantiateInstance() {", " return true;", " }", "", " @Override", " public AutoValue_Baz instantiateInstance(", " SerializationStreamReader streamReader) throws SerializationException {", " return instantiate(streamReader);", " }", "", " @Override", " public void serializeInstance(", " SerializationStreamWriter streamWriter,", " AutoValue_Baz instance) throws SerializationException {", " serialize(streamWriter, instance);", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject, GWT_COMPATIBLE); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz_CustomFieldSerializer") .hasSourceEquivalentTo(expectedOutput); } /** * Test builders and classes that are generic (as opposed to just containing properties with * generics). */ @Test public void testBuildersAndGenerics() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.annotations.GwtCompatible;", "import com.google.common.collect.ImmutableMap;", "import java.util.Map;", "", "@AutoValue", "@GwtCompatible(serializable = true)", "public abstract class Baz, V extends K> {", " public abstract Map map();", " public abstract ImmutableMap immutableMap();", "", " public static , V extends K> Builder builder() {", " return new AutoValue_Baz.Builder();", " }", "", " @AutoValue.Builder", " public interface Builder, V extends K> {", " Builder map(Map map);", " ImmutableMap.Builder immutableMapBuilder();", " Baz build();", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz_CustomFieldSerializer", "package foo.bar;", "", "import com.google.common.collect.ImmutableMap;", "import com.google.gwt.user.client.rpc.CustomFieldSerializer;", "import com.google.gwt.user.client.rpc.SerializationException;", "import com.google.gwt.user.client.rpc.SerializationStreamReader;", "import com.google.gwt.user.client.rpc.SerializationStreamWriter;", "import java.util.Map;", "import " + generatedAnnotationType() + ";", "", "@Generated(\"com.google.auto.value.processor.AutoValueProcessor\")", "public final class AutoValue_Baz_CustomFieldSerializer" + ", V extends K>" + " extends CustomFieldSerializer> {", "", " public static , V extends K> AutoValue_Baz" + " instantiate(", " SerializationStreamReader streamReader) throws SerializationException {", " @SuppressWarnings(\"unchecked\")", " Map map = (Map) streamReader.readObject();", " @SuppressWarnings(\"unchecked\")", " ImmutableMap immutableMap = (ImmutableMap) streamReader.readObject();", " AutoValue_Baz.Builder builder$ = new AutoValue_Baz.Builder();", " builder$.map(map);", " builder$.immutableMapBuilder().putAll(immutableMap);", " return (AutoValue_Baz) builder$.build();", " }", "", " public static , V extends K> void serialize(", " SerializationStreamWriter streamWriter,", " AutoValue_Baz instance) throws SerializationException {", " streamWriter.writeObject(instance.map());", " streamWriter.writeObject(instance.immutableMap());", " }", "", " public static , V extends K> void deserialize(", " @SuppressWarnings(\"unused\") SerializationStreamReader streamReader,", " @SuppressWarnings(\"unused\") AutoValue_Baz instance) {", " }", "", " @SuppressWarnings(\"unused\")", " private int dummy_2865d9ec;", "", " @Override", " public void deserializeInstance(", " SerializationStreamReader streamReader,", " AutoValue_Baz instance) {", " deserialize(streamReader, instance);", " }", "", " @Override", " public boolean hasCustomInstantiateInstance() {", " return true;", " }", "", " @Override", " public AutoValue_Baz instantiateInstance(", " SerializationStreamReader streamReader) throws SerializationException {", " return instantiate(streamReader);", " }", "", " @Override", " public void serializeInstance(", " SerializationStreamWriter streamWriter,", " AutoValue_Baz instance) throws SerializationException {", " serialize(streamWriter, instance);", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject, GWT_COMPATIBLE); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz_CustomFieldSerializer") .hasSourceEquivalentTo(expectedOutput); } private String generatedAnnotationType() { return isJavaxAnnotationProcessingGeneratedAvailable() ? "javax.annotation.processing.Generated" : "javax.annotation.Generated"; } private boolean isJavaxAnnotationProcessingGeneratedAvailable() { return SourceVersion.latestSupported().compareTo(SourceVersion.RELEASE_8) > 0; } } GwtValueType.java000066400000000000000000000030611365703632600347050ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2014 Google LLC * * 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.google.auto.value.gwt; import com.google.auto.value.AutoValue; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; import java.util.Collections; import java.util.List; import javax.annotation.Nullable; /** * A class that is serializable with both Java and GWT serialization. * * @author emcmanus@google.com (Éamonn McManus) */ @AutoValue @GwtCompatible(serializable = true) abstract class GwtValueType implements Serializable { abstract String string(); abstract int integer(); @Nullable abstract GwtValueType other(); abstract List others(); static GwtValueType create(String string, int integer, @Nullable GwtValueType other) { return create(string, integer, other, Collections.emptyList()); } static GwtValueType create( String string, int integer, @Nullable GwtValueType other, List others) { return new AutoValue_GwtValueType(string, integer, other, others); } } GwtValueTypeWithBuilder.java000066400000000000000000000036071365703632600370560ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2015 Google LLC * * 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.google.auto.value.gwt; import com.google.auto.value.AutoValue; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableList; import java.io.Serializable; import java.util.List; import javax.annotation.Nullable; /** * A class that is serializable with both Java and GWT serialization. * * @author emcmanus@google.com (Éamonn McManus) */ @AutoValue @GwtCompatible(serializable = true) abstract class GwtValueTypeWithBuilder implements Serializable { abstract String string(); abstract int integer(); @Nullable abstract GwtValueTypeWithBuilder other(); abstract List> others(); abstract ImmutableList list(); abstract ImmutableList otherList(); abstract ImmutableList listWithBuilder(); static Builder builder() { return new AutoValue_GwtValueTypeWithBuilder.Builder(); } @AutoValue.Builder interface Builder { Builder string(String x); Builder integer(int x); Builder other(@Nullable GwtValueTypeWithBuilder x); Builder others(List> x); Builder list(ImmutableList x); Builder otherList(List x); ImmutableList.Builder listWithBuilderBuilder(); GwtValueTypeWithBuilder build(); } } NonSerializableGwtValueType.java000066400000000000000000000022651365703632600377140ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2014 Google LLC * * 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.google.auto.value.gwt; import com.google.auto.value.AutoValue; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; /** * A class that is serializable with native Java serialization but not with GWT serialization. * * @author emcmanus@google.com (Éamonn McManus) */ @AutoValue @GwtCompatible abstract class NonSerializableGwtValueType implements Serializable { abstract String string(); abstract int integer(); static NonSerializableGwtValueType create(String string, int integer) { return new AutoValue_NonSerializableGwtValueType(string, integer); } } SerialSignatureTest.java000066400000000000000000000047051365703632600362540ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/functional/src/test/java/com/google/auto/value/gwt/* * Copyright 2014 Google LLC * * 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.google.auto.value.gwt; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import com.google.auto.value.AutoValue; import com.google.common.annotations.GwtCompatible; import java.lang.reflect.Field; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests that the generated serializer class for a GWT-serializable class contains a dummy field to * influence the signature, and that different classes have different signatures. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class SerialSignatureTest { @AutoValue @GwtCompatible(serializable = true) abstract static class One { abstract int foo(); static One create(int foo) { return new AutoValue_SerialSignatureTest_One(foo); } } @AutoValue @GwtCompatible(serializable = true) abstract static class Two { abstract int foo(); static Two create(int foo) { return new AutoValue_SerialSignatureTest_Two(foo); } } @Test public void testSerialSignatures() { Class serializerOne = AutoValue_SerialSignatureTest_One_CustomFieldSerializer.class; Class serializerTwo = AutoValue_SerialSignatureTest_Two_CustomFieldSerializer.class; String fieldNameOne = dummySignatureFieldName(serializerOne); String fieldNameTwo = dummySignatureFieldName(serializerTwo); assertFalse(fieldNameOne.equals(fieldNameTwo)); } private static String dummySignatureFieldName(Class c) { String name = null; for (Field f : c.getDeclaredFields()) { if (f.getName().startsWith("dummy_")) { assertNull("More than one field begins with dummy_: " + name + ", " + f.getName(), name); } name = f.getName(); } assertNotNull("No field begins with dummy_", name); return name; } } auto-auto-service-1.0-rc7/value/src/it/gwtserializer/000077500000000000000000000000001365703632600226445ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/invoker.properties000066400000000000000000000000351365703632600264350ustar00rootroot00000000000000invoker.goals = clean verify auto-auto-service-1.0-rc7/value/src/it/gwtserializer/pom.xml000066400000000000000000000117611365703632600241670ustar00rootroot00000000000000 4.0.0 com.google.auto.value auto-value-parent HEAD-SNAPSHOT ../../../pom.xml https://github.com/google/auto/tree/master/value com.google.auto.value.it.gwtserializer gwtserializer HEAD-SNAPSHOT Auto-Value GWT-RPC Serialization Integration Test com.google.gwt gwt 2.8.2 pom import com.google.auto.value auto-value-annotations ${project.version} com.google.auto.value auto-value ${project.version} true com.google.gwt gwt-user test com.google.gwt gwt-dev test com.google.guava guava test com.google.guava guava-gwt test junit junit test src/test/java ${project.build.directory}/generated-test-sources/test-annotations org.apache.maven.plugins maven-compiler-plugin 3.7.0 org.codehaus.plexus plexus-java 0.9.4 1.7 1.7 -Xlint:all true true org.apache.maven.plugins maven-surefire-plugin true org.apache.maven.plugins maven-resources-plugin 3.0.2 default-testResources process-test-classes net.ltgt.gwt.maven gwt-maven-plugin 1.0-rc-6 test org.apache.maven.plugins maven-deploy-plugin 2.7 true auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/000077500000000000000000000000001365703632600234335ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/000077500000000000000000000000001365703632600244125ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/000077500000000000000000000000001365703632600253335ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/000077500000000000000000000000001365703632600261115ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/000077500000000000000000000000001365703632600273655ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/auto/000077500000000000000000000000001365703632600303355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/auto/value/000077500000000000000000000000001365703632600314515ustar00rootroot00000000000000GwtSerializerSuite.gwt.xml000066400000000000000000000014211365703632600365170ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/auto/value GwtSerializerSuite.java000066400000000000000000000016541365703632600360500ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/auto/value/* * Copyright 2015 Google LLC * * 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.google.auto.value; import com.google.auto.value.client.GwtSerializerTest; import com.google.gwt.junit.tools.GWTTestSuite; import junit.framework.Test; public class GwtSerializerSuite { public static Test suite() { GWTTestSuite suite = new GWTTestSuite(); suite.addTestSuite(GwtSerializerTest.class); return suite; } } auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/auto/value/client/000077500000000000000000000000001365703632600327275ustar00rootroot00000000000000GwtSerializerTest.java000066400000000000000000000166671365703632600371660ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/it/gwtserializer/src/test/java/com/google/auto/value/client/* * Copyright 2015 Google LLC * * 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.google.auto.value.client; import com.google.auto.value.AutoValue; import com.google.common.annotations.GwtCompatible; import com.google.common.annotations.GwtIncompatible; import com.google.gwt.core.client.GWT; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; import com.google.gwt.user.server.rpc.RemoteServiceServlet; public class GwtSerializerTest extends GWTTestCase { @RemoteServiceRelativePath("test") public interface TestService extends RemoteService { Simple echo(Simple simple); SimpleWithBuilder echo(SimpleWithBuilder simple); Nested echo(Nested nested); NestedWithBuilder echo(NestedWithBuilder nested); Generics echo(Generics generics); GenericsWithBuilder echo(GenericsWithBuilder generics); } interface TestServiceAsync { void echo(Simple simple, AsyncCallback callback); void echo(SimpleWithBuilder simple, AsyncCallback callback); void echo(Nested nested, AsyncCallback callback); void echo(NestedWithBuilder nested, AsyncCallback callback); void echo(Generics generics, AsyncCallback> callback); void echo( GenericsWithBuilder generics, AsyncCallback> callback); } class AssertEqualsCallback implements AsyncCallback { private final T expected; AssertEqualsCallback(T expected) { this.expected = expected; } @Override public void onSuccess(T actual) { assertEquals(expected, actual); finishTest(); } @Override public void onFailure(Throwable caught) { fail(); } } @GwtIncompatible("RemoteServiceServlet") @SuppressWarnings("serial") public static class TestServiceImpl extends RemoteServiceServlet implements TestService { @Override public Simple echo(Simple simple) { return Simple.create(simple.message()); } @Override public SimpleWithBuilder echo(SimpleWithBuilder simple) { return SimpleWithBuilder.builder().message(simple.message()).build(); } @Override public Nested echo(Nested nested) { return Nested.create(nested.message(), echo(nested.simple())); } @Override public NestedWithBuilder echo(NestedWithBuilder nested) { return NestedWithBuilder.builder() .message(nested.message()) .simple(echo(nested.simple())) .build(); } @Override public Generics echo(Generics generics) { return Generics.create(echo(generics.simple())); } @Override public GenericsWithBuilder echo( GenericsWithBuilder generics) { return GenericsWithBuilder.builder() .simple(echo(generics.simple())) .build(); } } @AutoValue @GwtCompatible(serializable = true) abstract static class Simple { public abstract String message(); public static Simple create(String message) { return new AutoValue_GwtSerializerTest_Simple(message); } } @AutoValue @GwtCompatible(serializable = true) abstract static class SimpleWithBuilder { public abstract String message(); public static Builder builder() { return new AutoValue_GwtSerializerTest_SimpleWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder message(String message); SimpleWithBuilder build(); } } @AutoValue @GwtCompatible(serializable = true) abstract static class Nested { public abstract String message(); public abstract Simple simple(); public static Nested create(String message, Simple simple) { return new AutoValue_GwtSerializerTest_Nested(message, simple); } } @AutoValue @GwtCompatible(serializable = true) abstract static class NestedWithBuilder { public abstract String message(); public abstract SimpleWithBuilder simple(); public static Builder builder() { return new AutoValue_GwtSerializerTest_NestedWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder message(String message); Builder simple(SimpleWithBuilder simple); NestedWithBuilder build(); } } @AutoValue @GwtCompatible(serializable = true) abstract static class Generics { public abstract T simple(); public static Generics create(T simple) { return new AutoValue_GwtSerializerTest_Generics(simple); } } @AutoValue @GwtCompatible(serializable = true) abstract static class GenericsWithBuilder { public abstract T simple(); public static Builder builder() { return new AutoValue_GwtSerializerTest_GenericsWithBuilder.Builder(); } @AutoValue.Builder public interface Builder { Builder simple(T simple); GenericsWithBuilder build(); } } private TestServiceAsync testService; @Override public String getModuleName() { return "com.google.auto.value.GwtSerializerSuite"; } @Override public void gwtSetUp() { testService = GWT.create(TestService.class); } public void testSimple() { delayTestFinish(2000); Simple simple = Simple.create("able"); testService.echo(simple, new AssertEqualsCallback(simple)); } public void testSimpleWithBuilder() { delayTestFinish(2000); SimpleWithBuilder simple = SimpleWithBuilder.builder().message("able").build(); testService.echo(simple, new AssertEqualsCallback(simple)); } public void testNested() { delayTestFinish(2000); Nested nested = Nested.create("able", Simple.create("baker")); testService.echo(nested, new AssertEqualsCallback(nested)); } public void testNestedWithBuilder() { delayTestFinish(2000); NestedWithBuilder nested = NestedWithBuilder.builder() .message("able") .simple(SimpleWithBuilder.builder().message("baker").build()) .build(); testService.echo(nested, new AssertEqualsCallback(nested)); } public void testGenerics() { delayTestFinish(2000); Generics generics = Generics.create(Simple.create("able")); testService.echo(generics, new AssertEqualsCallback>(generics)); } public void testGenericsWithBuilder() { delayTestFinish(2000); GenericsWithBuilder generics = GenericsWithBuilder.builder() .simple(SimpleWithBuilder.builder().message("able").build()) .build(); testService.echo( generics, new AssertEqualsCallback>(generics)); } } auto-auto-service-1.0-rc7/value/src/main/000077500000000000000000000000001365703632600202615ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/000077500000000000000000000000001365703632600212025ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/000077500000000000000000000000001365703632600217605ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/000077500000000000000000000000001365703632600232345ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/000077500000000000000000000000001365703632600242045ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/000077500000000000000000000000001365703632600253205ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/AutoAnnotation.java000066400000000000000000000067641365703632600311430ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; /** * Annotation that causes an implementation of an annotation interface to be generated. The * annotation is applied to a method whose return type is an annotation interface. The method can * then create and return an instance of the generated class that conforms to the specification of * {@link Annotation}, in particular as regards {@link Annotation#equals equals} and {@link * Annotation#hashCode hashCode}. These instances behave essentially the same as instances returned * by {@link AnnotatedElement#getAnnotation}. * *

For example, suppose you have an annotation like this: * *

 * package com.google.inject.name;
 *
 * public @interface Named {
 *   String value();
 * }
* *

You could write a method like this to construct implementations of the interface: * *

 * package com.example;
 *
 * public class Names {
 *   @AutoAnnotation public static Named named(String value) {
 *     return new AutoAnnotation_Names_named(value);
 *   }
 * }
* *

Because the annotated method is called {@code Names.named}, the generated class is called * {@code AutoAnnotation_Names_named} in the same package. If the annotated method were in a nested * class, for example {@code Outer.Names.named}, then the generated class would be called {@code * AutoAnnotation_Outer_Names_named}. The generated class is package-private and it is not expected * that it will be referenced outside the {@code @AutoAnnotation} method. * *

The names and types of the parameters in the annotated method must be the same as the names * and types of the annotation elements, except that elements which have default values can be * omitted. The parameters do not need to be in any particular order. * *

The annotated method does not need to be public. It can have any visibility, including * private. This means the method can be a private implementation called by a public method with a * different API, for example using a builder. * *

It is a compile-time error if more than one method with the same name in the same class is * annotated {@code @AutoAnnotation}. * *

The constructor of the generated class has the same parameters as the {@code @AutoAnnotation} * method. It will throw {@code NullPointerException} if any parameter is null. In order to * guarantee that the constructed object is immutable, the constructor will clone each array * parameter corresponding to an array-valued annotation member, and the implementation of each such * member will also return a clone of the array. * * @author emcmanus@google.com (Éamonn McManus) */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface AutoAnnotation {} auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/AutoOneOf.java000066400000000000000000000046051365703632600300270ustar00rootroot00000000000000/* * Copyright 2018 Google LLC * * 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.google.auto.value; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Specifies that the annotated class is a one-of class, also known as a tagged union. An * {@code @AutoOneOf} class is very similar to an {@link AutoValue @AutoValue} class, in that its * abstract methods define a set of properties. But unlike {@code @AutoValue}, only one of those * properties is defined in any given instance. * *

{@code @AutoOneOf(StringOrInteger.Kind.class)
 * public abstract class StringOrInteger {
 *   public enum Kind {STRING, INTEGER}
 *
 *   public abstract Kind getKind();
 *
 *   public abstract String string();
 *   public abstract int integer();
 *
 *   public static StringOrInteger ofString(String s) {
 *     return AutoOneOf_StringOrInteger.string(s);
 *   }
 *
 *   public static StringOrInteger ofInteger(int i) {
 *     return AutoOneOf_StringOrInteger.integer(i);
 *   }
 * }
 *
 * String client(StringOrInteger stringOrInteger) {
 *   switch (stringOrInteger.getKind()) {
 *     case STRING:
 *       return "the string '" + stringOrInteger.string() + "'";
 *     case INTEGER:
 *       return "the integer " + stringOrInteger.integer();
 *   }
 *   throw new AssertionError();
 * }}
* *

{@code @AutoOneOf} is explained in more detail in the user guide. * * @author Chris Nokleberg * @author Éamonn McManus */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface AutoOneOf { /** Specifies an enum that has one entry per variant in the one-of. */ Class> value(); } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/AutoValue.java000066400000000000000000000133151365703632600300730ustar00rootroot00000000000000/* * Copyright 2012 Google LLC * * 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.google.auto.value; import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Specifies that AutoValue should * generate an implementation class for the annotated abstract class, implementing the standard * {@link Object} methods like {@link Object#equals equals} to have conventional value semantics. A * simple example: * *

 *
 *   {@code @}AutoValue
 *   abstract class Person {
 *     static Person create(String name, int id) {
 *       return new AutoValue_Person(name, id);
 *     }
 *
 *     abstract String name();
 *     abstract int id();
 *   }
* * @see AutoValue User's Guide * @author Éamonn McManus * @author Kevin Bourrillion */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface AutoValue { /** * Specifies that AutoValue should generate an implementation of the annotated class or interface, * to serve as a builder for the value-type class it is nested within. As a simple example, * here is an alternative way to write the {@code Person} class mentioned in the {@link AutoValue} * example: * *
   *
   *   {@code @}AutoValue
   *   abstract class Person {
   *     static Builder builder() {
   *       return new AutoValue_Person.Builder();
   *     }
   *
   *     abstract String name();
   *     abstract int id();
   *
   *     {@code @}AutoValue.Builder
   *     interface Builder {
   *       Builder name(String x);
   *       Builder id(int x);
   *       Person build();
   *     }
   *   }
* * @author Éamonn McManus */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.TYPE) public @interface Builder {} /** * Specifies that AutoValue should copy any annotations from the annotated element to the * generated class. This annotation supports classes and methods. * *

The following annotations are excluded: * *

    *
  1. AutoValue and its nested annotations; *
  2. any annotation appearing in the {@link AutoValue.CopyAnnotations#exclude} field; *
  3. any class annotation which is itself annotated with the {@link * java.lang.annotation.Inherited} meta-annotation. *
* *

For historical reasons, annotations are always copied from an {@code @AutoValue} property * method to its implementation, unless {@code @CopyAnnotations} is present and explicitly * {@linkplain CopyAnnotations#exclude excludes} that annotation. But annotations are not copied * from the {@code @AutoValue} class itself to its implementation unless {@code @CopyAnnotations} * is present. * *

If you want to copy annotations from your {@literal @}AutoValue-annotated class's methods to * the generated fields in the AutoValue_... implementation, annotate your method * with {@literal @}AutoValue.CopyAnnotations. For example, if Example.java is:


   *   {@code @}Immutable
   *   {@code @}AutoValue
   *   abstract class Example {
   *     {@code @}CopyAnnotations
   *     {@code @}SuppressWarnings("Immutable") // justification ...
   *     abstract Object getObject();
   *     // other details ...
   *   }
* *

Then AutoValue will generate the following AutoValue_Example.java:

   *
   *   final class AutoValue_Example extends Example {
   *     {@code @}SuppressWarnings("Immutable")
   *     private final Object object;
   *
   *     {@code @}SuppressWarnings("Immutable")
   *     {@code @}Override
   *     Object getObject() {
   *       return object;
   *     }
   *
   *     // other details ...
   *   }
* *

When the type of an {@code @AutoValue} property method has annotations, those are * part of the type, so by default they are copied to the implementation of the method. But if * a type annotation is mentioned in {@code exclude} then it is not copied. * *

For example, suppose {@code @Confidential} is a * {@link java.lang.annotation.ElementType#TYPE_USE TYPE_USE} annotation: * *

   *
   *   {@code @}AutoValue
   *   abstract class Person {
   *     static Person create({@code @}Confidential String name, int id) {
   *       return new AutoValue_Person(name, id);
   *     }
   *
   *     abstract {@code @}Confidential String name();
   *     abstract int id();
   *   }
* * Then the implementation of the {@code name()} method will also have return type * {@code @Confidential String}. But if {@code name()} were written like this... * *
   *
   *     {@code @AutoValue.CopyAnnotations(exclude = Confidential.class)}
   *     abstract {@code @}Confidential String name();
* *

...then the implementation of {@code name()} would have return type {@code String} without * the annotation. * * @author Carmi Grushko */ @Retention(RetentionPolicy.CLASS) @Target({ElementType.TYPE, ElementType.METHOD}) public @interface CopyAnnotations { Class[] exclude() default {}; } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/000077500000000000000000000000001365703632600273345ustar00rootroot00000000000000AutoValueExtension.java000066400000000000000000000533111365703632600337250ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/* * Copyright 2015 Google LLC * * 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.google.auto.value.extension; import com.google.common.collect.ImmutableSet; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.SupportedOptions; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; /** * An AutoValueExtension allows for extra functionality to be created during the generation of an * AutoValue class. * *

Extensions are discovered at compile time using the {@link java.util.ServiceLoader} APIs, * allowing them to run without any additional annotations. To be found by {@code ServiceLoader}, an * extension class must be public with a public no-arg constructor, and its fully-qualified name * must appear in a file called {@code * META-INF/services/com.google.auto.value.extension.AutoValueExtension} in a jar that is on the * compiler's {@code -classpath} or {@code -processorpath}. * *

When the AutoValue processor runs for a class {@code Foo}, it will ask each Extension whether * it is {@linkplain #applicable applicable}. Suppose two Extensions reply that they are. Then * the processor will generate the AutoValue logic in a direct subclass of {@code Foo}, and it * will ask the first Extension to generate a subclass of that, and the second Extension to generate * a subclass of the subclass. So we might have this hierarchy: * *

 * @AutoValue abstract class Foo {...}                          // the hand-written class
 * abstract class $$AutoValue_Foo extends Foo {...}             // generated by AutoValue processor
 * abstract class $AutoValue_Foo extends $$AutoValue_Foo {...}  // generated by first Extension
 * final class AutoValue_Foo extends $AutoValue_Foo {...}       // generated by second Extension
 * 
* *

(The exact naming scheme illustrated here is not fixed and should not be relied on.) * *

If an Extension needs its generated class to be the final class in the inheritance hierarchy, * its {@link #mustBeFinal(Context)} method returns true. Only one Extension can return true for a * given context. Only generated classes that will be the final class in the inheritance hierarchy * can be declared final. All others should be declared abstract. * *

The first generated class in the hierarchy will always be the one generated by the AutoValue * processor and the last one will always be the one generated by the Extension that {@code * mustBeFinal}, if any. Other than that, the order of the classes in the hierarchy is unspecified. * The last class in the hierarchy is {@code AutoValue_Foo} and that is the one that the * {@code Foo} class will reference, for example with {@code new AutoValue_Foo(...)}. * *

Each Extension must also be sure to generate a constructor with arguments corresponding to all * properties in {@link com.google.auto.value.extension.AutoValueExtension.Context#propertyTypes()}, * in order, and to call the superclass constructor with the same arguments. This constructor must * have at least package visibility. * *

Because the class generated by the AutoValue processor is at the top of the generated * hierarchy, Extensions can override its methods, for example {@code hashCode()}, * {@code toString()}, or the implementations of the various {@code bar()} property methods. */ public abstract class AutoValueExtension { /** The context of the generation cycle. */ public interface Context { /** * Returns the processing environment of this generation cycle. This can be used, among other * things, to produce compilation warnings or errors, using {@link * ProcessingEnvironment#getMessager()}. */ ProcessingEnvironment processingEnvironment(); /** Returns the package name of the classes to be generated. */ String packageName(); /** * Returns the annotated class that this generation cycle is based on. * *

Given {@code @AutoValue public class Foo {...}}, this will be {@code Foo}. */ TypeElement autoValueClass(); /** * The fully-qualified name of the last class in the {@code AutoValue} hierarchy. For an * {@code @AutoValue} class {@code foo.bar.Baz}, this will be {@code foo.bar.AutoValue_Baz}. * The class may be generated by an extension, which will be the current extension if the * {@code isFinal} parameter to {@link AutoValueExtension#generateClass} is true and the * returned string is not {@code null}. * *

For compatibility reasons, this method has a default implementation that throws an * exception. The AutoValue processor supplies an implementation that behaves as documented. */ default String finalAutoValueClassName() { throw new UnsupportedOperationException(); } /** * Returns the ordered collection of properties to be generated by AutoValue. Each key is a * property name, and the corresponding value is the getter method for that property. For * example, if property {@code bar} is defined by {@code abstract String getBar()} then this map * will have an entry mapping {@code "bar"} to the {@code ExecutableElement} for {@code * getBar()}. * *

To determine the type of a property, it is best to use {@link #propertyTypes()} rather * than looking at the return type of the {@link ExecutableElement} in this map. The reason is * that the final type of the property might be different because of type variables. For * example, if you have... * *

     *   interface Parent {
     *     T bar();
     *   }
     *
     *  {@code @AutoValue abstract class Foo implements Parent {...}}
     * 
* * ...then the type of the {@code bar} property in {@code Foo} is actually {@code String}, but * the {@code ExecutableElement} will be the the method in {@code Parent}, whose return type is * {@code T}. */ Map properties(); /** * Returns the properties to be generated by AutoValue, with their types. Each key is a property * name, and the corresponding value is the type of that property. The order of the map entries * is the same as the order of the {@code @AutoValue} properties. * *

For example, if property {@code bar} is defined by {@code abstract String getBar()} then * this map will have an entry mapping {@code "bar"} to the {@code TypeMirror} for {@code * String}. * *

For compatibility reasons, this method has a default implementation that throws an * exception. The AutoValue processor supplies an implementation that behaves as documented. */ default Map propertyTypes() { throw new UnsupportedOperationException(); } /** * Returns the complete set of abstract methods defined in or inherited by the * {@code @AutoValue} class. This includes all methods that define properties (like {@code * abstract String getBar()}), any abstract {@code toBuilder()} method, and any other abstract * method even if it has been consumed by this or another Extension. */ Set abstractMethods(); /** * Returns a representation of the {@code Builder} associated with the {@code @AutoValue} class, * if there is one. * *

This method returns {@link Optional#empty()} if called from within the {@link #applicable} * method. If an Extension needs {@code Builder} information to decide whether it is applicable, * it should return {@code true} from the {@link #applicable} method and then return {@code * null} from the {@link #generateClass} method if it does not need to generate a class after * all. * *

The default implementation of this method returns {@link Optional#empty()} for * compatibility with extensions which may have implemented this interface themselves. */ default Optional builder() { return Optional.empty(); } } /** * Represents a {@code Builder} associated with an {@code @AutoValue} class. */ public interface BuilderContext { /** * Returns the {@code @AutoValue.Builder} interface or abstract class that this object * represents. */ TypeElement builderType(); /** * Returns abstract no-argument methods in the {@code @AutoValue} class that return the builder * type. * *

Consider a class like this: *

     *   {@code @AutoValue} abstract class Foo {
     *     abstract String bar();
     *
     *     abstract Builder toBuilder();
     *
     *     ...
     *     {@code @AutoValue.Builder}
     *     abstract static class Builder {...}
     *   }
     * 
* *

Here {@code toBuilderMethods()} will return a set containing the method * {@code Foo.toBuilder()}. */ Set toBuilderMethods(); /** * Returns static no-argument methods in the {@code @AutoValue} class that return the builder * type. * *

Consider a class like this: *

     *   {@code @AutoValue} abstract class Foo {
     *     abstract String bar();
     *
     *     static Builder builder() {
     *       return new AutoValue_Foo.Builder()
     *           .setBar("default bar");
     *     }
     *
     *     {@code @AutoValue.Builder}
     *     abstract class Builder {
     *       abstract Builder setBar(String x);
     *       abstract Foo build();
     *     }
     *   }
     * 
* *

Here {@code builderMethods()} will return a set containing the method * {@code Foo.builder()}. Generated code should usually call this method in preference to * constructing {@code AutoValue_Foo.Builder()} directly, because this method can establish * default values for properties, as it does here. */ Set builderMethods(); /** * Returns the method {@code build()} in the builder class, if it exists and returns the * {@code @AutoValue} type. This is the method that generated code for * {@code @AutoValue class Foo} should call in order to get an instance of {@code Foo} from its * builder. The returned method is called {@code build()}; if the builder uses some other name * then extensions have no good way to guess how they should build. * *

A common convention is for {@code build()} to be a concrete method in the * {@code @AutoValue.Builder} class, which calls an abstract method {@code autoBuild()} that is * implemented in the generated subclass. The {@code build()} method can then do validation, * defaulting, and so on. */ Optional buildMethod(); /** * Returns the abstract build method. If the {@code @AutoValue} class is {@code Foo}, this is an * abstract no-argument method in the builder class that returns {@code Foo}. This might be * called {@code build()}, or, following a common convention, it might be called * {@code autoBuild()} and used in the implementation of a {@code build()} method that is * defined in the builder class. * *

Extensions should call the {@code build()} method in preference to this one. But they * should override this one if they want to customize build-time behaviour. */ ExecutableElement autoBuildMethod(); /** * Returns a map from property names to the corresponding setters. A property may have more than * one setter. For example, an {@code ImmutableList} might be set by * {@code setFoo(ImmutableList)} and {@code setFoo(String[])}. */ Map> setters(); /** * Returns a map from property names to property builders. For example, if there is a property * {@code foo} defined by {@code abstract ImmutableList foo();} or * {@code abstract ImmutableList getFoo();} in the {@code @AutoValue} class, * then there can potentially be a builder defined by * {@code abstract ImmutableList.Builder fooBuilder();} in the * {@code @AutoValue.Builder} class. This map would then map {@code "foo"} to the * {@link ExecutableElement} representing {@code fooBuilder()}. */ Map propertyBuilders(); } /** * Indicates to an annotation processor environment supporting incremental annotation processing * (currently a feature specific to Gradle starting with version 4.8) the incremental type of an * Extension. * *

The constants for this enum are ordered by increasing performance (but also constraints). * * @see Gradle * documentation of its incremental annotation processing */ public enum IncrementalExtensionType { /** * The incrementality of this extension is unknown, or it is neither aggregating nor isolating. */ UNKNOWN, /** * This extension is aggregating, meaning that it may generate outputs based on several * annotated input classes and it respects the constraints imposed on aggregating processors. * It is unusual for AutoValue extensions to be aggregating. * * @see Gradle * definition of aggregating processors */ AGGREGATING, /** * This extension is isolating, meaning roughly that its output depends on the * {@code @AutoValue} class and its dependencies, but not on other {@code @AutoValue} classes * that might be compiled at the same time. The constraints that an isolating extension must * respect are the same as those that Gradle imposes on an isolating annotation processor. * * @see Gradle * definition of isolating processors */ ISOLATING } /** * Determines the incremental type of this Extension. * *

The {@link ProcessingEnvironment} can be used, among other things, to obtain the processor * options, using {@link ProcessingEnvironment#getOptions()}. * *

The actual incremental type of the AutoValue processor as a whole will be the loosest * incremental types of the Extensions present in the annotation processor path. The default * returned value is {@link IncrementalExtensionType#UNKNOWN}, which will disable incremental * annotation processing entirely. */ public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) { return IncrementalExtensionType.UNKNOWN; } /** * Analogous to {@link Processor#getSupportedOptions()}, here to allow extensions to report their * own. * *

By default, if the extension class is annotated with {@link SupportedOptions}, this will * return a set with the strings in the annotation. If the class is not so annotated, an empty set * is returned. * * @return the set of options recognized by this extension or an empty set if none * @see SupportedOptions */ public Set getSupportedOptions() { SupportedOptions so = this.getClass().getAnnotation(SupportedOptions.class); if (so == null) { return ImmutableSet.of(); } else { return ImmutableSet.copyOf(so.value()); } } /** * Determines whether this Extension applies to the given context. If an Extension returns {@code * false} for a given class, it will not be called again during the processing of that class. An * Extension can return {@code true} and still choose not to generate any code for the class, by * returning {@code null} from {@link #generateClass}. That is often a more flexible approach. * * @param context The Context of the code generation for this class. */ public boolean applicable(Context context) { return false; } /** * Denotes that the class generated by this Extension must be the final class in the inheritance * hierarchy. Only one Extension may be the final class, so this should be used sparingly. * * @param context the Context of the code generation for this class. */ public boolean mustBeFinal(Context context) { return false; } /** * Returns a possibly empty set of property names that this Extension intends to implement. This * will prevent AutoValue from generating an implementation, and remove the supplied properties * from builders, constructors, {@code toString}, {@code equals}, and {@code hashCode}. The * default set returned by this method is empty. * *

Each returned string must be one of the property names in {@link Context#properties()}. * *

Returning a property name from this method is equivalent to returning the property's getter * method from {@link #consumeMethods}. * *

For example, Android's {@code Parcelable} interface includes a method * {@code int describeContents()}. Since this is an abstract method with no parameters, by default * AutoValue will consider that it defines an {@code int} property called {@code * describeContents}. If an {@code @AutoValue} class implements {@code Parcelable} and does not * provide an implementation of this method, by default its implementation will include {@code * describeContents} in builders, constructors, and so on. But an {@code AutoValueExtension} that * understands {@code Parcelable} can instead provide a useful implementation and return a set * containing {@code "describeContents"}. Then {@code describeContents} will be omitted from * builders and the rest. * * @param context the Context of the code generation for this class. */ public Set consumeProperties(Context context) { return ImmutableSet.of(); } /** * Returns a possible empty set of abstract methods that this Extension intends to implement. This * will prevent AutoValue from generating an implementation, in cases where it would have, and it * will also avoid warnings about abstract methods that AutoValue doesn't expect. The default set * returned by this method is empty. * *

Each returned method must be one of the abstract methods in {@link * Context#abstractMethods()}. * *

For example, Android's {@code Parcelable} interface includes a method {@code void writeToParcel(Parcel, int)}. Normally AutoValue would not know * what to do with that abstract method. But an {@code AutoValueExtension} that understands {@code * Parcelable} can provide a useful implementation and return the {@code writeToParcel} method * here. That will prevent a warning about the method from AutoValue. * * @param context the Context of the code generation for this class. */ public Set consumeMethods(Context context) { return ImmutableSet.of(); } /** * Returns the generated source code of the class named {@code className} to extend {@code * classToExtend}, or {@code null} if this extension does not generate a class in the hierarchy. * If there is a generated class, it should be final if {@code isFinal} is true; otherwise it * should be abstract. The returned string should be a complete Java class definition of the class * {@code className} in the package {@link Context#packageName() context.packageName()}. * *

The returned string will typically look like this: * *

{@code
   * package ;
   * ...
   *  class  extends  {
   *   // Constructor
   *   () {
   *     super();
   *     ...
   *   }
   *   ...
   * }}
* *

Here, {@code } is {@link Context#packageName()}; {@code } is the * keyword {@code final} if {@code isFinal} is true or {@code abstract} otherwise; and {@code * } and {@code } are the values of this method's parameters of the same * name. The {@code } and {@code } are typically * derived from {@link Context#propertyTypes()}. * * @param context The {@link Context} of the code generation for this class. * @param className The simple name of the resulting class. The returned code will be written to a * file named accordingly. * @param classToExtend The simple name of the direct parent of the generated class. This could be * the AutoValue generated class, or a class generated as the result of another Extension. * @param isFinal True if this class is the last class in the chain, meaning it should be marked * as final. Otherwise it should be marked as abstract. * @return The source code of the generated class, or {@code null} if this extension does not * generate a class in the hierarchy. */ public abstract String generateClass( Context context, String className, String classToExtend, boolean isFinal); } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/memoized/000077500000000000000000000000001365703632600311455ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/memoized/Memoized.java000066400000000000000000000053641365703632600335710ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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.google.auto.value.extension.memoized; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.CLASS; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; /** * Annotates methods in {@link com.google.auto.value.AutoValue @AutoValue} classes for which the * generated subclass will memoize the * returned value. * *

Methods annotated with {@code @Memoized} cannot: * *

    *
  • be {@code abstract} (except for {@link #hashCode()} and {@link #toString()}), {@code * private}, {@code final}, or {@code static} *
  • return {@code void} *
  • have any parameters *
* *

If you want to memoize {@link #hashCode()} or {@link #toString()}, you can redeclare them, * keeping them {@code abstract}, and annotate them with {@code @Memoize}. * *

If a {@code @Memoized} method is annotated with an annotation whose simple name is {@code * Nullable}, then {@code null} values will also be memoized. Otherwise, if the method returns * {@code null}, the overriding method will throw a {@link NullPointerException}. * *

The overriding method uses double-checked locking to * ensure that the annotated method is called at most once. * *

Example

* *
 *   {@code @AutoValue}
 *   abstract class Value {
 *     abstract String stringProperty();
 *
 *     {@code @Memoized}
 *     String derivedProperty() {
 *       return someCalculationOn(stringProperty());
 *     }
 *   }
 *
 *   {@code @Generated}
 *   class AutoValue_Value {
 *     // …
 *
 *     private volatile String derivedProperty;
 *
 *     {@code Override}
 *     String derivedProperty() {
 *       if (derivedProperty == null) {
 *         synchronized (this) {
 *           if (derivedProperty == null) {
 *             derivedProperty = super.derivedProperty();
 *           }
 *         }
 *       }
 *       return derivedProperty;
 *     }
 *   }
*/ @Documented @Retention(CLASS) @Target(METHOD) public @interface Memoized {} auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/memoized/processor/000077500000000000000000000000001365703632600331645ustar00rootroot00000000000000ClassNames.java000066400000000000000000000015111365703632600357770ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/memoized/processor/* * Copyright 2017 Google LLC * * 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.google.auto.value.extension.memoized.processor; /** Names of classes that are referenced in the processor/extension. */ final class ClassNames { static final String MEMOIZED_NAME = "com.google.auto.value.extension.memoized.Memoized"; } MemoizeExtension.java000066400000000000000000000622201365703632600372540ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/memoized/processor/* * Copyright 2016 Google LLC * * 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.google.auto.value.extension.memoized.processor; import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec; import static com.google.auto.common.MoreElements.getPackage; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.value.extension.memoized.processor.ClassNames.MEMOIZED_NAME; import static com.google.auto.value.extension.memoized.processor.MemoizedValidator.getAnnotationMirror; import static com.google.common.base.Predicates.equalTo; import static com.google.common.base.Predicates.not; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Sets.union; import static com.squareup.javapoet.MethodSpec.constructorBuilder; import static com.squareup.javapoet.MethodSpec.methodBuilder; import static com.squareup.javapoet.TypeSpec.classBuilder; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import static javax.lang.model.element.Modifier.ABSTRACT; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import static javax.lang.model.element.Modifier.TRANSIENT; import static javax.lang.model.element.Modifier.VOLATILE; import static javax.lang.model.type.TypeKind.VOID; import static javax.lang.model.util.ElementFilter.methodsIn; import static javax.tools.Diagnostic.Kind.ERROR; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.common.Visibility; import com.google.auto.service.AutoService; import com.google.auto.value.extension.AutoValueExtension; import com.google.common.base.Equivalence.Wrapper; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.errorprone.annotations.FormatMethod; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import java.lang.annotation.Inherited; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; /** * An extension that implements the {@link com.google.auto.value.extension.memoized.Memoized} * contract. */ @AutoService(AutoValueExtension.class) public final class MemoizeExtension extends AutoValueExtension { private static final ImmutableSet DO_NOT_PULL_DOWN_ANNOTATIONS = ImmutableSet.of(Override.class.getCanonicalName(), MEMOIZED_NAME); // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private static final String AUTO_VALUE_PACKAGE_NAME = "com.google.auto.value."; private static final String AUTO_VALUE_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoValue"; private static final String COPY_ANNOTATIONS_NAME = AUTO_VALUE_NAME + ".CopyAnnotations"; private static final ClassName LAZY_INIT = ClassName.get("com.google.errorprone.annotations.concurrent", "LazyInit"); private static final AnnotationSpec SUPPRESS_WARNINGS = AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "$S", "Immutable").build(); @Override public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) { return IncrementalExtensionType.ISOLATING; } @Override public boolean applicable(Context context) { return !memoizedMethods(context).isEmpty(); } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { return new Generator(context, className, classToExtend, isFinal).generate(); } private static ImmutableSet memoizedMethods(Context context) { ImmutableSet.Builder memoizedMethods = ImmutableSet.builder(); for (ExecutableElement method : methodsIn(context.autoValueClass().getEnclosedElements())) { if (getAnnotationMirror(method, MEMOIZED_NAME).isPresent()) { memoizedMethods.add(method); } } return memoizedMethods.build(); } static final class Generator { private final Context context; private final String className; private final String classToExtend; private final boolean isFinal; private final Elements elements; private final Types types; private final SourceVersion sourceVersion; private final Messager messager; private final Optional lazyInitAnnotation; private boolean hasErrors; Generator(Context context, String className, String classToExtend, boolean isFinal) { this.context = context; this.className = className; this.classToExtend = classToExtend; this.isFinal = isFinal; this.elements = context.processingEnvironment().getElementUtils(); this.types = context.processingEnvironment().getTypeUtils(); this.sourceVersion = context.processingEnvironment().getSourceVersion(); this.messager = context.processingEnvironment().getMessager(); this.lazyInitAnnotation = getLazyInitAnnotation(elements); } String generate() { TypeSpec.Builder generated = classBuilder(className) .superclass(superType()) .addAnnotations(copiedClassAnnotations(context.autoValueClass())) .addTypeVariables(typeVariableNames()) .addModifiers(isFinal ? FINAL : ABSTRACT) .addMethod(constructor()); generatedAnnotationSpec(elements, sourceVersion, MemoizeExtension.class) .ifPresent(generated::addAnnotation); for (ExecutableElement method : memoizedMethods(context)) { MethodOverrider methodOverrider = new MethodOverrider(method); generated.addFields(methodOverrider.fields()); generated.addMethod(methodOverrider.method()); } if (isHashCodeMemoized() && !isEqualsFinal()) { generated.addMethod(equalsWithHashCodeCheck()); } if (hasErrors) { return null; } return JavaFile.builder(context.packageName(), generated.build()).build().toString(); } private TypeName superType() { ClassName superType = ClassName.get(context.packageName(), classToExtend); ImmutableList typeVariableNames = typeVariableNames(); return typeVariableNames.isEmpty() ? superType : ParameterizedTypeName.get(superType, typeVariableNames.toArray(new TypeName[] {})); } private ImmutableList typeVariableNames() { ImmutableList.Builder typeVariableNamesBuilder = ImmutableList.builder(); for (TypeParameterElement typeParameter : context.autoValueClass().getTypeParameters()) { typeVariableNamesBuilder.add(TypeVariableName.get(typeParameter)); } return typeVariableNamesBuilder.build(); } private MethodSpec constructor() { MethodSpec.Builder constructor = constructorBuilder(); for (Map.Entry property : context.propertyTypes().entrySet()) { constructor.addParameter(annotatedType(property.getValue()), property.getKey() + "$"); } List namesWithDollars = new ArrayList(); for (String property : context.properties().keySet()) { namesWithDollars.add(property + "$"); } constructor.addStatement("super($L)", Joiner.on(", ").join(namesWithDollars)); return constructor.build(); } private boolean isHashCodeMemoized() { return memoizedMethods(context).stream() .anyMatch(method -> method.getSimpleName().contentEquals("hashCode")); } private boolean isEqualsFinal() { TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType(); ExecutableElement equals = MoreElements.getLocalAndInheritedMethods(context.autoValueClass(), types, elements) .stream() .filter(method -> method.getSimpleName().contentEquals("equals")) .filter(method -> method.getParameters().size() == 1) .filter( method -> types.isSameType(getOnlyElement(method.getParameters()).asType(), objectType)) .findFirst() .get(); return equals.getModifiers().contains(FINAL); } private MethodSpec equalsWithHashCodeCheck() { return methodBuilder("equals") .addModifiers(PUBLIC) .returns(TypeName.BOOLEAN) .addAnnotation(Override.class) .addParameter(TypeName.OBJECT, "that") .beginControlFlow("if (this == that)") .addStatement("return true") .endControlFlow() .addStatement( "return that instanceof $N " + "&& this.hashCode() == that.hashCode() " + "&& super.equals(that)", className) .build(); } /** * True if the given class name is in the com.google.auto.value package or a subpackage. False * if the class name contains {@code Test}, since many AutoValue tests under * com.google.auto.value define their own annotations. */ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private boolean isInAutoValuePackage(String className) { return className.startsWith(AUTO_VALUE_PACKAGE_NAME) && !className.contains("Test"); } /** * Returns the fully-qualified name of an annotation-mirror, e.g. * "com.google.auto.value.AutoValue". */ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private static String getAnnotationFqName(AnnotationMirror annotation) { return ((QualifiedNameable) annotation.getAnnotationType().asElement()) .getQualifiedName() .toString(); } // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private boolean annotationVisibleFrom(AnnotationMirror annotation, Element from) { Element annotationElement = annotation.getAnnotationType().asElement(); Visibility visibility = Visibility.effectiveVisibilityOfElement(annotationElement); switch (visibility) { case PUBLIC: return true; case PROTECTED: // If the annotation is protected, it must be inside another class, call it C. If our // @AutoValue class is Foo then, for the annotation to be visible, either Foo must be in // the same package as C or Foo must be a subclass of C. If the annotation is visible from // Foo then it is also visible from our generated subclass AutoValue_Foo. // The protected case only applies to method annotations. An annotation on the // AutoValue_Foo class itself can't be protected, even if AutoValue_Foo ultimately // inherits from the class that defines the annotation. The JLS says "Access is permitted // only within the body of a subclass": // https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.2.1 // AutoValue_Foo is a top-level class, so an annotation on it cannot be in the body of a // subclass of anything. return getPackage(annotationElement).equals(getPackage(from)) || types.isSubtype(from.asType(), annotationElement.getEnclosingElement().asType()); case DEFAULT: return getPackage(annotationElement).equals(getPackage(from)); default: return false; } } /** Implements the semantics of {@code AutoValue.CopyAnnotations}; see its javadoc. */ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private ImmutableList annotationsToCopy( Element autoValueType, Element typeOrMethod, Set excludedAnnotations) { ImmutableList.Builder result = ImmutableList.builder(); for (AnnotationMirror annotation : typeOrMethod.getAnnotationMirrors()) { String annotationFqName = getAnnotationFqName(annotation); // To be included, the annotation should not be in com.google.auto.value, // and it should not be in the excludedAnnotations set. if (!isInAutoValuePackage(annotationFqName) && !excludedAnnotations.contains(annotationFqName) && annotationVisibleFrom(annotation, autoValueType)) { result.add(annotation); } } return result.build(); } /** Implements the semantics of {@code AutoValue.CopyAnnotations}; see its javadoc. */ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private ImmutableList copyAnnotations( Element autoValueType, Element typeOrMethod, Set excludedAnnotations) { ImmutableList annotationsToCopy = annotationsToCopy(autoValueType, typeOrMethod, excludedAnnotations); return annotationsToCopy.stream().map(AnnotationSpec::get).collect(toImmutableList()); } // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private static boolean hasAnnotationMirror(Element element, String annotationName) { return getAnnotationMirror(element, annotationName).isPresent(); } /** * Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of * {@code TypeMirror} where each type is an annotation type. */ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private ImmutableSet getExcludedAnnotationTypes(Element element) { Optional maybeAnnotation = getAnnotationMirror(element, COPY_ANNOTATIONS_NAME); if (!maybeAnnotation.isPresent()) { return ImmutableSet.of(); } @SuppressWarnings("unchecked") List excludedClasses = (List) getAnnotationValue(maybeAnnotation.get(), "exclude").getValue(); return excludedClasses.stream() .map( annotationValue -> MoreTypes.equivalence().wrap((TypeMirror) annotationValue.getValue())) // TODO(b/122509249): Move TypeMirrorSet to common package instead of doing this. .distinct() .map(Wrapper::get) .collect(toImmutableSet()); } /** * Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of * strings that are fully-qualified class names. */ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private Set getExcludedAnnotationClassNames(Element element) { return getExcludedAnnotationTypes(element).stream() .map(MoreTypes::asTypeElement) .map(typeElement -> typeElement.getQualifiedName().toString()) .collect(toSet()); } // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common. private static Set getAnnotationsMarkedWithInherited(Element element) { return element.getAnnotationMirrors().stream() .filter(a -> isAnnotationPresent(a.getAnnotationType().asElement(), Inherited.class)) .map(Generator::getAnnotationFqName) .collect(toSet()); } private ImmutableList copiedClassAnnotations(TypeElement type) { // Only copy annotations from a class if it has @AutoValue.CopyAnnotations. if (hasAnnotationMirror(type, COPY_ANNOTATIONS_NAME)) { Set excludedAnnotations = union(getExcludedAnnotationClassNames(type), getAnnotationsMarkedWithInherited(type)); return copyAnnotations(type, type, excludedAnnotations); } else { return ImmutableList.of(); } } /** * Determines the required fields and overriding method for a {@link * com.google.auto.value.extension.memoized.Memoized @Memoized} method. */ private final class MethodOverrider { private final ExecutableElement method; private final MethodSpec.Builder override; private final FieldSpec cacheField; private final ImmutableList.Builder fields = ImmutableList.builder(); MethodOverrider(ExecutableElement method) { this.method = method; validate(); cacheField = buildCacheField( annotatedType(method.getReturnType()), method.getSimpleName().toString()); fields.add(cacheField); override = methodBuilder(method.getSimpleName().toString()) .addAnnotation(Override.class) .returns(cacheField.type) .addExceptions( method.getThrownTypes().stream().map(TypeName::get).collect(toList())) .addModifiers(filter(method.getModifiers(), not(equalTo(ABSTRACT)))); for (AnnotationMirror annotation : method.getAnnotationMirrors()) { AnnotationSpec annotationSpec = AnnotationSpec.get(annotation); if (pullDownMethodAnnotation(annotation)) { override.addAnnotation(annotationSpec); } } InitializationStrategy checkStrategy = strategy(); fields.addAll(checkStrategy.additionalFields()); override .beginControlFlow("if ($L)", checkStrategy.checkMemoized()) .beginControlFlow("synchronized (this)") .beginControlFlow("if ($L)", checkStrategy.checkMemoized()) .addStatement("$N = super.$L()", cacheField, method.getSimpleName()) .addCode(checkStrategy.setMemoized()) .endControlFlow() .endControlFlow() .endControlFlow() .addStatement("return $N", cacheField); } /** The fields that should be added to the subclass. */ Iterable fields() { return fields.build(); } /** The overriding method that should be added to the subclass. */ MethodSpec method() { return override.build(); } private void validate() { if (method.getReturnType().getKind().equals(VOID)) { printMessage(ERROR, "@Memoized methods cannot be void"); } if (!method.getParameters().isEmpty()) { printMessage(ERROR, "@Memoized methods cannot have parameters"); } checkIllegalModifier(PRIVATE); checkIllegalModifier(FINAL); checkIllegalModifier(STATIC); if (!overridesObjectMethod("hashCode") && !overridesObjectMethod("toString")) { checkIllegalModifier(ABSTRACT); } } private void checkIllegalModifier(Modifier modifier) { if (method.getModifiers().contains(modifier)) { printMessage(ERROR, "@Memoized methods cannot be %s", modifier.toString()); } } @FormatMethod private void printMessage(Kind kind, String format, Object... args) { if (kind.equals(ERROR)) { hasErrors = true; } messager.printMessage(kind, String.format(format, args), method); } private boolean overridesObjectMethod(String methodName) { return elements.overrides(method, objectMethod(methodName), context.autoValueClass()); } private ExecutableElement objectMethod(final String methodName) { TypeElement object = elements.getTypeElement(Object.class.getName()); for (ExecutableElement method : methodsIn(object.getEnclosedElements())) { if (method.getSimpleName().contentEquals(methodName)) { return method; } } throw new IllegalArgumentException( String.format("No method in Object named \"%s\"", methodName)); } private boolean pullDownMethodAnnotation(AnnotationMirror annotation) { return !DO_NOT_PULL_DOWN_ANNOTATIONS.contains( MoreElements.asType(annotation.getAnnotationType().asElement()) .getQualifiedName() .toString()); } /** * Builds a {@link FieldSpec} for use in property caching. Field will be {@code private * transient volatile} and have the given type and name. If the @LazyInit annotation is * available it is added as well. */ private FieldSpec buildCacheField(TypeName type, String name) { FieldSpec.Builder builder = FieldSpec.builder(type, name, PRIVATE, TRANSIENT, VOLATILE); if (lazyInitAnnotation.isPresent()) { builder.addAnnotation(lazyInitAnnotation.get()); builder.addAnnotation(SUPPRESS_WARNINGS); } return builder.build(); } InitializationStrategy strategy() { if (method.getReturnType().getKind().isPrimitive()) { return new CheckBooleanField(); } if (containsNullable(method.getAnnotationMirrors()) || containsNullable(method.getReturnType().getAnnotationMirrors())) { return new CheckBooleanField(); } return new NullMeansUninitialized(); } private abstract class InitializationStrategy { abstract Iterable additionalFields(); abstract CodeBlock checkMemoized(); abstract CodeBlock setMemoized(); } private final class NullMeansUninitialized extends InitializationStrategy { @Override Iterable additionalFields() { return ImmutableList.of(); } @Override CodeBlock checkMemoized() { return CodeBlock.of("$N == null", cacheField); } @Override CodeBlock setMemoized() { return CodeBlock.builder() .beginControlFlow("if ($N == null)", cacheField) .addStatement( "throw new NullPointerException($S)", method.getSimpleName() + "() cannot return null") .endControlFlow() .build(); } } private final class CheckBooleanField extends InitializationStrategy { private final FieldSpec field = buildCacheField(TypeName.BOOLEAN, method.getSimpleName() + "$Memoized"); @Override Iterable additionalFields() { return ImmutableList.of(field); } @Override CodeBlock checkMemoized() { return CodeBlock.of("!$N", field); } @Override CodeBlock setMemoized() { return CodeBlock.builder().addStatement("$N = true", field).build(); } } } } /** Returns the errorprone {@code @LazyInit} annotation if it is found on the classpath. */ private static Optional getLazyInitAnnotation(Elements elements) { if (elements.getTypeElement(LAZY_INIT.toString()) == null) { return Optional.empty(); } return Optional.of(AnnotationSpec.builder(LAZY_INIT).build()); } /** True if one of the given annotations is {@code @Nullable} in any package. */ private static boolean containsNullable(List annotations) { return annotations.stream() .map(a -> a.getAnnotationType().asElement().getSimpleName()) .anyMatch(n -> n.contentEquals("Nullable")); } /** Translate a {@link TypeMirror} into a {@link TypeName}, including type annotations. */ private static TypeName annotatedType(TypeMirror type) { List annotations = type.getAnnotationMirrors().stream() .map(AnnotationSpec::get) .collect(toList()); return TypeName.get(type).annotated(annotations); } } MemoizedValidator.java000066400000000000000000000066431365703632600374000ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/memoized/processor/* * Copyright 2017 Google LLC * * 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.google.auto.value.extension.memoized.processor; import static com.google.auto.value.extension.memoized.processor.ClassNames.MEMOIZED_NAME; import static javax.lang.model.util.ElementFilter.methodsIn; import static javax.tools.Diagnostic.Kind.ERROR; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import java.util.Optional; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Messager; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; /** * An annotation {@link Processor} that reports errors for {@link Memoized @Memoized} methods that * are not inside {@code AutoValue}-annotated classes. */ @AutoService(Processor.class) @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) @SupportedAnnotationTypes(MEMOIZED_NAME) public final class MemoizedValidator extends AbstractProcessor { @Override public final boolean process(Set annotations, RoundEnvironment roundEnv) { Messager messager = processingEnv.getMessager(); TypeElement memoized = processingEnv.getElementUtils().getTypeElement(MEMOIZED_NAME); for (ExecutableElement method : methodsIn(roundEnv.getElementsAnnotatedWith(memoized))) { if (!isAutoValue(method.getEnclosingElement())) { messager.printMessage( ERROR, "@Memoized methods must be declared only in @AutoValue classes", method, getAnnotationMirror(method, MEMOIZED_NAME).get()); } } return false; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } private static boolean isAutoValue(Element element) { return element .getAnnotationMirrors() .stream() .map(annotation -> MoreTypes.asTypeElement(annotation.getAnnotationType())) .anyMatch(type -> type.getQualifiedName().contentEquals("com.google.auto.value.AutoValue")); } static Optional getAnnotationMirror(Element element, String annotationName) { for (AnnotationMirror annotation : element.getAnnotationMirrors()) { TypeElement annotationElement = MoreTypes.asTypeElement(annotation.getAnnotationType()); if (annotationElement.getQualifiedName().contentEquals(annotationName)) { return Optional.of(annotation); } } return Optional.empty(); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/000077500000000000000000000000001365703632600320025ustar00rootroot00000000000000SerializableAutoValue.java000066400000000000000000000022111365703632600370160ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotates {@link com.google.auto.value.AutoValue @AutoValue} classes that implement {@link * java.io.Serializable}. A serializable subclass is generated for classes with normally * un-serializable fields like {@link java.util.Optional}. */ @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface SerializableAutoValue {} processor/000077500000000000000000000000001365703632600337425ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializableClassNames.java000066400000000000000000000016011365703632600366340ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/processor/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.processor; /** Names of classes that are referenced in /processor. */ final class ClassNames { static final String SERIALIZABLE_AUTO_VALUE_NAME = "com.google.auto.value.extension.serializable.SerializableAutoValue"; private ClassNames() {} } PropertyMirror.java000066400000000000000000000027411365703632600376300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/processor/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.processor; import javax.lang.model.type.TypeMirror; /** * A POJO containing information about an AutoValue's property. * *

For example, given this AutoValue property: abstract T getX(); * *

    *
  1. The type would be T. *
  2. The name would be x. *
  3. The method would be getX. */ final class PropertyMirror { private final TypeMirror type; private final String name; private final String method; PropertyMirror(TypeMirror type, String name, String method) { this.type = type; this.name = name; this.method = method; } /** Gets the AutoValue property's type. */ TypeMirror getType() { return type; } /** Gets the AutoValue property's name. */ String getName() { return name; } /** Gets the AutoValue property accessor method. */ String getMethod() { return method; } } SerializableAutoValueExtension.java000066400000000000000000000312111365703632600427340ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/processor/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.processor; import static com.google.auto.value.extension.serializable.processor.ClassNames.SERIALIZABLE_AUTO_VALUE_NAME; import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.common.collect.ImmutableMap.toImmutableMap; import static java.util.stream.Collectors.joining; import com.google.auto.common.GeneratedAnnotationSpecs; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.auto.value.extension.AutoValueExtension; import com.google.auto.value.extension.serializable.serializer.SerializerFactoryLoader; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import java.io.Serializable; import java.util.List; import java.util.Optional; import java.util.function.Function; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; /** * An AutoValue extension that enables classes with unserializable fields to be serializable. * *

    For this extension to work: * *

      *
    • The AutoValue class must implement {@link Serializable}. *
    • Unserializable fields in the AutoValue class must be supported by a {@link * com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension}. */ @AutoService(AutoValueExtension.class) public final class SerializableAutoValueExtension extends AutoValueExtension { @Override public boolean applicable(Context context) { return hasSerializableInterface(context) && hasSerializableAutoValueAnnotation(context); } @Override public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) { return IncrementalExtensionType.ISOLATING; } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { return new Generator(context, className, classToExtend, isFinal).generate(); } private static final class Generator { private final Context context; private final String className; private final String classToExtend; private final boolean isFinal; private final ImmutableList propertyMirrors; private final ImmutableList typeVariableNames; private final ProxyGenerator proxyGenerator; Generator(Context context, String className, String classToExtend, boolean isFinal) { this.context = context; this.className = className; this.classToExtend = classToExtend; this.isFinal = isFinal; this.propertyMirrors = context.propertyTypes().entrySet().stream() .map( entry -> new PropertyMirror( /* type= */ entry.getValue(), /* name= */ entry.getKey(), /* method= */ context .properties() .get(entry.getKey()) .getSimpleName() .toString())) .collect(toImmutableList()); this.typeVariableNames = context.autoValueClass().getTypeParameters().stream() .map(TypeVariableName::get) .collect(toImmutableList()); TypeName classTypeName = getClassTypeName(ClassName.get(context.packageName(), className), typeVariableNames); this.proxyGenerator = new ProxyGenerator( classTypeName, typeVariableNames, propertyMirrors, buildSerializersMap()); } private String generate() { ClassName superclass = ClassName.get(context.packageName(), classToExtend); Optional generatedAnnotationSpec = GeneratedAnnotationSpecs.generatedAnnotationSpec( context.processingEnvironment().getElementUtils(), context.processingEnvironment().getSourceVersion(), SerializableAutoValueExtension.class); TypeSpec.Builder subclass = TypeSpec.classBuilder(className) .superclass(getClassTypeName(superclass, typeVariableNames)) .addTypeVariables(typeVariableNames) .addModifiers(isFinal ? Modifier.FINAL : Modifier.ABSTRACT) .addMethod(constructor()) .addMethod(writeReplace()) .addType(proxyGenerator.generate()); generatedAnnotationSpec.ifPresent(subclass::addAnnotation); return JavaFile.builder(context.packageName(), subclass.build()).build().toString(); } /** Creates a constructor that calls super with all the AutoValue fields. */ private MethodSpec constructor() { MethodSpec.Builder constructor = MethodSpec.constructorBuilder() .addStatement( "super($L)", propertyMirrors.stream().map(PropertyMirror::getName).collect(joining(", "))); for (PropertyMirror propertyMirror : propertyMirrors) { constructor.addParameter(TypeName.get(propertyMirror.getType()), propertyMirror.getName()); } return constructor.build(); } /** * Creates an implementation of writeReplace that delegates serialization to its inner Proxy * class. */ private MethodSpec writeReplace() { ImmutableList properties = propertyMirrors.stream() .map(propertyMirror -> CodeBlock.of("$L()", propertyMirror.getMethod())) .collect(toImmutableList()); return MethodSpec.methodBuilder("writeReplace") .returns(Object.class) .addStatement( "return new $T($L)", getClassTypeName( ClassName.get( context.packageName(), className, SerializableAutoValueExtension.ProxyGenerator.PROXY_CLASS_NAME), typeVariableNames), CodeBlock.join(properties, ", ")) .build(); } private ImmutableMap, Serializer> buildSerializersMap() { SerializerFactory factory = SerializerFactoryLoader.getFactory(context.processingEnvironment()); return propertyMirrors.stream() .map(PropertyMirror::getType) .map(MoreTypes.equivalence()::wrap) .distinct() .collect( toImmutableMap( Function.identity(), equivalence -> factory.getSerializer(equivalence.get()))); } /** Adds type parameters to the given {@link ClassName}, if available. */ private static TypeName getClassTypeName( ClassName className, List typeVariableNames) { return typeVariableNames.isEmpty() ? className : ParameterizedTypeName.get(className, typeVariableNames.toArray(new TypeName[] {})); } } /** A generator of nested serializable Proxy classes. */ private static final class ProxyGenerator { private static final String PROXY_CLASS_NAME = "Proxy$"; private final TypeName outerClassTypeName; private final ImmutableList typeVariableNames; private final ImmutableList propertyMirrors; private final ImmutableMap, Serializer> serializersMap; ProxyGenerator( TypeName outerClassTypeName, ImmutableList typeVariableNames, ImmutableList propertyMirrors, ImmutableMap, Serializer> serializersMap) { this.outerClassTypeName = outerClassTypeName; this.typeVariableNames = typeVariableNames; this.propertyMirrors = propertyMirrors; this.serializersMap = serializersMap; } private TypeSpec generate() { TypeSpec.Builder proxy = TypeSpec.classBuilder(PROXY_CLASS_NAME) .addModifiers(Modifier.STATIC) .addTypeVariables(typeVariableNames) .addSuperinterface(Serializable.class) .addField(serialVersionUid()) .addFields(properties()) .addMethod(constructor()) .addMethod(readResolve()); return proxy.build(); } private static FieldSpec serialVersionUid() { return FieldSpec.builder( long.class, "serialVersionUID", Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .initializer("0") .build(); } /** Maps each AutoValue property to a serializable type. */ private List properties() { return propertyMirrors.stream() .map( propertyMirror -> FieldSpec.builder( TypeName.get( serializersMap .get(MoreTypes.equivalence().wrap(propertyMirror.getType())) .proxyFieldType()), propertyMirror.getName(), Modifier.PRIVATE) .build()) .collect(toImmutableList()); } /** Creates a constructor that converts the AutoValue's properties to serializable values. */ private MethodSpec constructor() { MethodSpec.Builder constructor = MethodSpec.constructorBuilder(); for (PropertyMirror propertyMirror : propertyMirrors) { Serializer serializer = serializersMap.get(MoreTypes.equivalence().wrap(propertyMirror.getType())); String name = propertyMirror.getName(); constructor.addParameter(TypeName.get(propertyMirror.getType()), name); constructor.addStatement( CodeBlock.of("this.$L = $L", name, serializer.toProxy(CodeBlock.of(name)))); } return constructor.build(); } /** * Creates an implementation of {@code readResolve} that returns the serializable values in the * Proxy object back to their original types. */ private MethodSpec readResolve() { return MethodSpec.methodBuilder("readResolve") .returns(Object.class) .addException(Exception.class) .addStatement( "return new $T($L)", outerClassTypeName, CodeBlock.join( propertyMirrors.stream().map(this::resolve).collect(toImmutableList()), ", ")) .build(); } /** Maps a serializable type back to its original AutoValue property. */ private CodeBlock resolve(PropertyMirror propertyMirror) { return serializersMap .get(MoreTypes.equivalence().wrap(propertyMirror.getType())) .fromProxy(CodeBlock.of(propertyMirror.getName())); } } private static boolean hasSerializableInterface(Context context) { final TypeMirror serializableTypeMirror = context .processingEnvironment() .getElementUtils() .getTypeElement(Serializable.class.getCanonicalName()) .asType(); return context .processingEnvironment() .getTypeUtils() .isAssignable(context.autoValueClass().asType(), serializableTypeMirror); } private static boolean hasSerializableAutoValueAnnotation(Context context) { return context.autoValueClass().getAnnotationMirrors().stream() .map(AnnotationMirror::getAnnotationType) .map(MoreTypes::asTypeElement) .map(TypeElement::getQualifiedName) .anyMatch(name -> name.contentEquals(SERIALIZABLE_AUTO_VALUE_NAME)); } } serializer/000077500000000000000000000000001365703632600340745ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializableSerializerFactoryLoader.java000066400000000000000000000044611365703632600415340ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer; import com.google.auto.value.extension.serializable.serializer.impl.SerializerFactoryImpl; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.google.auto.value.processor.SimpleServiceLoader; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import javax.annotation.processing.ProcessingEnvironment; import javax.tools.Diagnostic; /** * Builds a {@link SerializerFactory} populated with discovered {@link SerializerExtension} * instances. */ public final class SerializerFactoryLoader { /** * Returns a {@link SerializerFactory} with {@link SerializerExtension} instances provided by the * {@link java.util.ServiceLoader}. */ public static SerializerFactory getFactory(ProcessingEnvironment processingEnv) { return new SerializerFactoryImpl(loadExtensions(processingEnv), processingEnv); } private static ImmutableList loadExtensions( ProcessingEnvironment processingEnv) { try { return ImmutableList.copyOf( SimpleServiceLoader.load( SerializerExtension.class, SerializerFactoryLoader.class.getClassLoader())); } catch (Throwable t) { processingEnv .getMessager() .printMessage( Diagnostic.Kind.ERROR, "An exception occurred while looking for SerializerExtensions. No extensions will" + " function.\n" + Throwables.getStackTraceAsString(t)); return ImmutableList.of(); } } private SerializerFactoryLoader() {} } impl/000077500000000000000000000000001365703632600350355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializerIdentitySerializerFactory.java000066400000000000000000000032731365703632600430600ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.squareup.javapoet.CodeBlock; import javax.lang.model.type.TypeMirror; /** Creates identity {@link Serializer} instances. */ public final class IdentitySerializerFactory { /** Returns a {@link Serializer} that leaves the type as is. */ public static Serializer getSerializer(TypeMirror typeMirror) { return new IdentitySerializer(typeMirror); } private static class IdentitySerializer implements Serializer { private final TypeMirror typeMirror; IdentitySerializer(TypeMirror typeMirror) { this.typeMirror = typeMirror; } @Override public TypeMirror proxyFieldType() { return typeMirror; } @Override public CodeBlock toProxy(CodeBlock expression) { return expression; } @Override public CodeBlock fromProxy(CodeBlock expression) { return expression; } @Override public boolean isIdentity() { return true; } } private IdentitySerializerFactory() {} } ImmutableListSerializerExtension.java000066400000000000000000000106031365703632600444020ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.google.auto.value.extension.serializable.serializer.runtime.FunctionWithExceptions; import com.google.common.collect.ImmutableList; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; /** * A {@link SerializerExtension} that deserializes objects inside an {@link ImmutableList}. * *

      Enables unserializable objects inside an ImmutableList to be serializable. */ @AutoService(SerializerExtension.class) public final class ImmutableListSerializerExtension implements SerializerExtension { public ImmutableListSerializerExtension() {} @Override public Optional getSerializer( TypeMirror typeMirror, SerializerFactory factory, ProcessingEnvironment processingEnv) { if (!isImmutableList(typeMirror)) { return Optional.empty(); } // Extract the T of ImmutableList. TypeMirror containedType = getContainedType(typeMirror); Serializer containedTypeSerializer = factory.getSerializer(containedType); // We don't need this serializer if the T of ImmutableList is serializable. if (containedTypeSerializer.isIdentity()) { return Optional.empty(); } return Optional.of(new ImmutableListSerializer(containedTypeSerializer, processingEnv)); } private static class ImmutableListSerializer implements Serializer { private final Serializer containedTypeSerializer; private final ProcessingEnvironment processingEnv; ImmutableListSerializer( Serializer containedTypeSerializer, ProcessingEnvironment processingEnv) { this.containedTypeSerializer = containedTypeSerializer; this.processingEnv = processingEnv; } @Override public TypeMirror proxyFieldType() { TypeElement immutableListTypeElement = processingEnv.getElementUtils().getTypeElement(ImmutableList.class.getCanonicalName()); TypeMirror containedProxyType = containedTypeSerializer.proxyFieldType(); return processingEnv .getTypeUtils() .getDeclaredType(immutableListTypeElement, containedProxyType); } @Override public CodeBlock toProxy(CodeBlock expression) { CodeBlock element = CodeBlock.of("value$$"); return CodeBlock.of( "$L.stream().map($T.wrapper($L -> $L)).collect($T.toImmutableList())", expression, FunctionWithExceptions.class, element, containedTypeSerializer.toProxy(element), ImmutableList.class); } @Override public CodeBlock fromProxy(CodeBlock expression) { CodeBlock element = CodeBlock.of("value$$"); return CodeBlock.of( "$L.stream().map($T.wrapper($L -> $L)).collect($T.toImmutableList())", expression, FunctionWithExceptions.class, element, containedTypeSerializer.fromProxy(element), ImmutableList.class); } } private static boolean isImmutableList(TypeMirror type) { if (type.getKind() != TypeKind.DECLARED) { return false; } return MoreTypes.asTypeElement(type) .getQualifiedName() .contentEquals("com.google.common.collect.ImmutableList"); } private static TypeMirror getContainedType(TypeMirror type) { return MoreTypes.asDeclared(type).getTypeArguments().get(0); } } ImmutableMapSerializerExtension.java000066400000000000000000000142161365703632600442100ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.google.auto.value.extension.serializable.serializer.runtime.FunctionWithExceptions; import com.google.common.collect.ImmutableMap; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import java.util.function.Function; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; /** * A {@link SerializerExtension} that deserializes objects inside an {@link ImmutableMap}. * *

      Enables unserializable objects inside an ImmutableMap to be serializable. */ @AutoService(SerializerExtension.class) public final class ImmutableMapSerializerExtension implements SerializerExtension { public ImmutableMapSerializerExtension() {} @Override public Optional getSerializer( TypeMirror typeMirror, SerializerFactory factory, ProcessingEnvironment processingEnv) { if (!isImmutableMap(typeMirror)) { return Optional.empty(); } // Extract the K, V of ImmutableMap. TypeMirror keyType = getKeyType(typeMirror); TypeMirror valueType = getValueType(typeMirror); Serializer keyTypeSerializer = factory.getSerializer(keyType); Serializer valueTypeSerializer = factory.getSerializer(valueType); // We don't need this serializer if the K and V of ImmutableMap are serializable. if (keyTypeSerializer.isIdentity() && valueTypeSerializer.isIdentity()) { return Optional.empty(); } return Optional.of( new ImmutableMapSerializer( keyType, valueType, keyTypeSerializer, valueTypeSerializer, processingEnv)); } private static class ImmutableMapSerializer implements Serializer { private final TypeMirror keyType; private final TypeMirror valueType; private final TypeMirror keyProxyType; private final TypeMirror valueProxyType; private final Serializer keyTypeSerializer; private final Serializer valueTypeSerializer; private final ProcessingEnvironment processingEnv; ImmutableMapSerializer( TypeMirror keyType, TypeMirror valueType, Serializer keyTypeSerializer, Serializer valueTypeSerializer, ProcessingEnvironment processingEnv) { this.keyType = keyType; this.valueType = valueType; this.keyProxyType = keyTypeSerializer.proxyFieldType(); this.valueProxyType = valueTypeSerializer.proxyFieldType(); this.keyTypeSerializer = keyTypeSerializer; this.valueTypeSerializer = valueTypeSerializer; this.processingEnv = processingEnv; } @Override public TypeMirror proxyFieldType() { TypeElement immutableMapTypeElement = processingEnv.getElementUtils().getTypeElement(ImmutableMap.class.getCanonicalName()); return processingEnv .getTypeUtils() .getDeclaredType(immutableMapTypeElement, keyProxyType, valueProxyType); } @Override public CodeBlock toProxy(CodeBlock expression) { return CodeBlock.of( "$L.entrySet().stream().collect($T.toImmutableMap($L, $L))", expression, ImmutableMap.class, generateKeyMapFunction(keyType, keyProxyType, keyTypeSerializer::toProxy), generateValueMapFunction(valueType, valueProxyType, valueTypeSerializer::toProxy)); } @Override public CodeBlock fromProxy(CodeBlock expression) { return CodeBlock.of( "$L.entrySet().stream().collect($T.toImmutableMap($L, $L))", expression, ImmutableMap.class, generateKeyMapFunction(keyProxyType, keyType, keyTypeSerializer::fromProxy), generateValueMapFunction(valueProxyType, valueType, valueTypeSerializer::fromProxy)); } private static CodeBlock generateKeyMapFunction( TypeMirror originalType, TypeMirror transformedType, Function proxyMap) { CodeBlock element = CodeBlock.of("element$$"); return CodeBlock.of( "value$$ -> $T.<$T, $T>wrapper($L -> $L).apply(value$$.getKey())", FunctionWithExceptions.class, originalType, transformedType, element, proxyMap.apply(element)); } private static CodeBlock generateValueMapFunction( TypeMirror originalType, TypeMirror transformedType, Function proxyMap) { CodeBlock element = CodeBlock.of("element$$"); return CodeBlock.of( "value$$ -> $T.<$T, $T>wrapper($L -> $L).apply(value$$.getValue())", FunctionWithExceptions.class, originalType, transformedType, element, proxyMap.apply(element)); } } private static boolean isImmutableMap(TypeMirror type) { if (type.getKind() != TypeKind.DECLARED) { return false; } return MoreTypes.asTypeElement(type) .getQualifiedName() .contentEquals("com.google.common.collect.ImmutableMap"); } private static TypeMirror getKeyType(TypeMirror type) { return MoreTypes.asDeclared(type).getTypeArguments().get(0); } private static TypeMirror getValueType(TypeMirror type) { return MoreTypes.asDeclared(type).getTypeArguments().get(1); } } OptionalSerializerExtension.java000066400000000000000000000073211365703632600434170ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; /** * A {@link SerializerExtension} that enables {@link Optional} types to be serialized. * *

      The type argument {@code T} of {@code Optional} is queried against the {@link * SerializerFactory}. */ @AutoService(SerializerExtension.class) public final class OptionalSerializerExtension implements SerializerExtension { public OptionalSerializerExtension() {} /** Creates a {@link Serializer} that supports {@link Optional} types. */ @Override public Optional getSerializer( TypeMirror typeMirror, SerializerFactory factory, ProcessingEnvironment processingEnv) { if (!isOptional(typeMirror)) { return Optional.empty(); } // Extract the T of Optional. TypeMirror containedType = getContainedType(typeMirror); Serializer containedTypeSerializer = factory.getSerializer(containedType); return Optional.of(new OptionalSerializer(containedTypeSerializer)); } private static class OptionalSerializer implements Serializer { private final Serializer containedTypeSerializer; OptionalSerializer(Serializer containedTypeSerializer) { this.containedTypeSerializer = containedTypeSerializer; } @Override public TypeMirror proxyFieldType() { // If this is an Optional then the proxy field type is String. // If this is an Optional, and the proxy field type for Foo is Bar, then the proxy field // type for Optional is Bar. return containedTypeSerializer.proxyFieldType(); } @Override public CodeBlock toProxy(CodeBlock expression) { return CodeBlock.of( "$L.isPresent() ? $L : null", expression, containedTypeSerializer.toProxy(CodeBlock.of("$L.get()", expression))); } @Override public CodeBlock fromProxy(CodeBlock expression) { return CodeBlock.of( "$T.ofNullable($L == null ? null : $L)", Optional.class, expression, containedTypeSerializer.fromProxy(expression)); } } /** Checks if the given type is an {@link Optional}. */ private static boolean isOptional(TypeMirror type) { if (type.getKind() != TypeKind.DECLARED) { return false; } return MoreTypes.asTypeElement(type).getQualifiedName().contentEquals("java.util.Optional"); } /** * Gets the given type's first type argument. * *

      Returns the {@code T} in {@code Optional}. */ private static TypeMirror getContainedType(TypeMirror type) { return MoreTypes.asDeclared(type).getTypeArguments().get(0); } } SerializerFactoryImpl.java000066400000000000000000000035451365703632600421720ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.google.common.collect.ImmutableList; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.TypeMirror; /** A concrete implementation of {@link SerializerFactory}. */ public final class SerializerFactoryImpl implements SerializerFactory { private final ImmutableList extensions; private final ProcessingEnvironment env; public SerializerFactoryImpl( ImmutableList extensions, ProcessingEnvironment env) { this.extensions = extensions; this.env = env; } @Override public Serializer getSerializer(TypeMirror typeMirror) { for (SerializerExtension extension : extensions) { Optional serializer = extension.getSerializer(typeMirror, this, env); if (serializer.isPresent()) { return serializer.get(); } } return IdentitySerializerFactory.getSerializer(typeMirror); } } interfaces/000077500000000000000000000000001365703632600362175ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializerSerializer.java000066400000000000000000000026521365703632600412000ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.interfaces; import com.squareup.javapoet.CodeBlock; import javax.lang.model.type.TypeMirror; /** * A Serializer, at compile time, generates code to map an unserializable type to a serializable * type. It also generates the reverse code to re-create the original type. */ public interface Serializer { /** The proxy type the original unserializable type will be mapped to. */ TypeMirror proxyFieldType(); /** Creates an expression that converts the original type to the proxy type. */ CodeBlock toProxy(CodeBlock expression); /** Creates an expression that converts the proxy type back to the original type. */ CodeBlock fromProxy(CodeBlock expression); /** Returns true if this is an identity {@link Serializer}. */ default boolean isIdentity() { return false; } } SerializerExtension.java000066400000000000000000000045551365703632600431010ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.interfaces; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.type.TypeMirror; /** * A SerializerExtension allows unserializable types to be serialized by SerializableAutoValue. * *

      Extensions are discovered at compile time using the {@link java.util.ServiceLoader} APIs, * allowing them to run without any additional annotations. To be found by {@code ServiceLoader}, an * extension class must be public with a public no-arg constructor, and its fully-qualified name * must appear in a file called {@code * META-INF/services/com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension} * in a jar that is on the compiler's {@code -classpath} or {@code -processorpath}. * *

      When SerializableAutoValue maps each field in an AutoValue to a serializable proxy object, it * asks each SerializerExtension whether it can generate code to make the given type serializable. A * SerializerExtension replies that it can by returning a non-empty {@link Serializer}. * *

      A SerializerExtension is also provided with a SerializerFactory, which it can use to query * nested types. */ public interface SerializerExtension { /** * Returns a {@link Serializer} if this {@link SerializerExtension} applies to the given {@code * type}. Otherwise, {@code Optional.empty} is returned. * * @param type the type being serialized * @param factory a {@link SerializerFactory} that can be used to serialize nested types * @param processingEnv the processing environment provided by the annotation processing framework */ Optional getSerializer( TypeMirror type, SerializerFactory factory, ProcessingEnvironment processingEnv); } SerializerFactory.java000066400000000000000000000020201365703632600425150ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.interfaces; import javax.lang.model.type.TypeMirror; /** * A factory that returns a {@link Serializer} for any given {@link TypeMirror}. * *

      Defaults to an identity serializer if no SerializerExtensions are suitable. */ public interface SerializerFactory { /** Returns a {@link Serializer} for the given {@link TypeMirror}. */ Serializer getSerializer(TypeMirror type); } runtime/000077500000000000000000000000001365703632600355575ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializerFunctionWithExceptions.java000066400000000000000000000024551365703632600431130ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/serializer/runtime/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.runtime; import java.util.function.Function; /** A utility for lambdas that throw exceptions. */ public final class FunctionWithExceptions { /** Creates a wrapper for lambdas that converts checked exceptions to runtime exceptions. */ public static Function wrapper(FunctionWithException fe) { return arg -> { try { return fe.apply(arg); } catch (Exception e) { throw new RuntimeException(e); } }; } /** A function that can throw an exception. */ @FunctionalInterface public interface FunctionWithException { O apply(I i) throws Exception; } private FunctionWithExceptions() {} } userguide/000077500000000000000000000000001365703632600337175ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializableindex.md000066400000000000000000000062131365703632600353520ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/userguide# Serializable AutoValue Extension An [`AutoValue`] extension that enables `@AutoValue` classes with un-serializable properties to be serializable. ## Usage To use the [`SerializableAutoValueExtension`] with your `AutoValue` class, the `AutoValue` class must: 1. Implement `java.io.Serializable`. 2. Be annotated with `@SerializableAutoValue`. ## Example The following `AutoValue` class is un-serializable: ```java @AutoValue public abstract class Foo implements Serializable { public static Foo create(Optional x) { return new AutoValue_Foo(x); } // java.util.Optional is not serializable. abstract Optional x; } ``` This is because `java.util.Optional` is un-serializable. We can make `Foo` serializable by using the [`SerializableAutoValueExtension`]. ```java @SerializableAutoValue // This annotation activates the extension. @AutoValue public abstract class Foo implements Serializable { ... } ``` ## Details For the example class `Foo` above, `SerializableAutoValueExtension` will generate the following code: ```java @Generated("SerializableAutoValueExtension") final class AutoValue_Foo extends $AutoValue_Foo { // Instead of serializing AutoValue_Foo, we delegate serialization to a // proxy object. Object writeReplace() throws ObjectStreamException { return new Proxy$(this.x); } // When serializing, AutoValue_Foo's values are written to Proxy$. // When de-serializing, Proxy$'s values used to create a new instance of // AutoValue_Foo static class Proxy$ implements Serializable { private String x; // During serialization, un-wrap the Optional field. Proxy$(Optional x) { this.x = x.orElse(null); } // During de-serialization, re-create AutoValue_Foo. Object readResolve() throws ObjectStreamException { return new AutoValue_Foo(Optional.ofNullable(x)); } } } ``` `SerializableAutoValueExtension` delegates the serialization of `Foo` to a proxy object `Proxy$` where `Foo`'s data is unwrapped. ## Supported Types `SerializableAutoValueExtension` currently supports the following types: * `java.util.Optional` * `com.google.common.collect.ImmutableList` * Enables `ImmutableList`, where `T` is an un-serializable but supported type, to be serializable. * `com.google.common.collect.ImmutableMap` * Enables `ImmutableMap`, where `K` and/or `V` are un-serializable but supported types, to be serializable. ### Extensions `SerializableAutoValueExtension` can be extended to support additional un-serializable types with [SerializerExtensions]. [`AutoValue`]: https://github.com/google/auto/tree/master/value [`SerializableAutoValue`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/SerializableAutoValue.java [`SerializableAutoValueExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/extension/SerializableAutoValueExtension.java [SerializerExtensions]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/userguide/serializer-extension.md serializer-extension.md000066400000000000000000000164531365703632600404350ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/extension/serializable/userguide# Serializer Extensions [`SerializableAutoValueExtension`] can be extended to support additional un-serializable types. Extensions are created by implementing [`SerializerExtension`]. [TOC] ## Using SerializerExtensions To use an extension that's not bundled with [`SerializableAutoValueExtension`], include it in your `AutoValue` class's dependencies. ## Writing a SerializerExtension To add serialization support to a new type, write a class that extends [`SerializerExtension`]. Put that class on the `processorpath` along with `SerializerExtension`. See [`AutoService`] for Java extension loading information. ### How SerializerExtension Works `SerializableAutoValueExtension` iterates through each property of the `@AutoValue` class and tries to look up a `SerializerExtension` for the property's type. If a `SerializerExtension` is available, a [`Serializer`] is returned. The `Serializer` does three things: 1. From the original property's type, determine what a suitable serializable type is. 2. Generate an expression that maps the original property's value to the proxy value. 3. Generate an expression that maps a proxy value back to the original property's value. ### Example We have a `Foo` AutoValue class that we would like to serialize: ```java @SerializableAutoValue @AutoValue public abstract class Foo implements Serializable { public static Foo create(Bar bar) { return new AutoValue_Foo(bar); } // Bar is unserializable. abstract Bar bar(); } // An un-serializable class. public final class Bar { public int x; public Bar(int x) { this.x = x; } } ``` Unfortunately, `Bar` is un-serializable, which makes the entire class un-serializable. We can extend `SerializableAutoValue` to support `Bar` by implementing a `SerializerExtension` for `Bar`. ```java // Use AutoService to make BarSerializerExtension discoverable by java.util.ServiceLoader. @AutoService(SerializerExtension.class) public final class BarSerializerExtension implements SerializerExtension { // Service providers must have a public constructor with no formal parameters. public BarSerializerExtension() {} // This method is called for each property in an AutoValue. // When this extension can handle a type (i.e. Bar), we return a Serializer. called on all SerializerExtensions. @Override public Optional getSerializer( TypeMirror type, SerializerFactory factory, ProcessingEnvironment env) { // BarSerializerExtension only handles Bar types. if (!isBar(type)) { return Optional.empty(); } return Optional.of(new BarSerializer(env)); } // Our implementation of how Bar should be serialized + de-serialized. private static class BarSerializer implements Serializer { private final ProcessingEnvironment env; BarSerializer(ProcessingEnvironment env) { this.env = env; } // One way to serialize Bar is to just serialize Bar.x. // We can do that by mapping Bar to an int. @Override public TypeMirror proxyFieldType() { return Types.getPrimitiveType(TypeKind.INT); } // We need to map Bar to the type we declared in {@link #proxyFieldType}. // Note that {@code expression} is a variable of type Bar. @Override public CodeBlock toProxy(CodeBlock expression) { return CodeBlock.of("$L.x", expression); } // We need to map the integer back to a Bar. @Override public CodeBlock fromProxy(CodeBlock expression) { return CodeBlock.of("new $T($L)", Bar.class, expression); } } } ``` `BarSerializerExtension` enables AutoValue classes with `Bar` properties to be serialized. For our example class `Foo`, it would help `SerializableAutoValue` generate the following code: ```java @Generated("SerializableAutoValueExtension") final class AutoValue_Foo extends $AutoValue_Foo { Object writeReplace() throws ObjectStreamException { return new Proxy$(this.x); } static class Proxy$ implements Serializable { // The type is generated by {@code BarSerializer#proxyFieldType}. private int bar; Proxy$(Bar bar) { // The assignment expression is generated by {@code BarSerializer#toProxy}. this.bar = bar.x; } Object readResolve() throws ObjectStreamException { // The reverse mapping expression is generated by {@code BarSerializer#fromProxy}. return new AutoValue_Foo(new Bar(bar)); } } } ``` ### Type Parameters Objects with type parameters are also supported by `SerializerExtension`. For example: ```java // A potentially un-serializable class, depending on the actual type of T. public final class Baz implements Serializable { public T x; public Baz(int x) { this.x = x; } } ``` `Baz`'s type argument `T` may not be serializable, but we could create a `SerializerExtension` that supports `Baz` by asking for a `SerializerExtension` for `T`. ```java @AutoService(SerializerExtension.class) public final class BazSerializerExtension implements SerializerExtension { public BazSerializerExtension() {} @Override public Optional getSerializer( TypeMirror type, SerializerFactory factory, ProcessingEnvironment env) { if (!isBaz(type)) { return Optional.empty(); } // Extract the T of Baz. TypeMirror containedType = getContainedType(type); // Look up a serializer for the contained type T. Serializer containedTypeSerializer = factory.getSerializer(containedType); // If the serializer for the contained type T is an identity function, it // means the contained type is either serializable or unsupported. // Either way, nothing needs to be done. Baz can be serialized as is. if (containedTypeSerializer.isIdentity()) { return Optional.empty(); } // Make Baz serializable by using the contained type T serializer. return Optional.of(new BazSerializer(containedTypeSerializer)); } private static class BazSerializer implements Serializer { private Serializer serializer; BazSerializer(Serializer serialize) { this.serializer = serializer; } @Override public TypeMirror proxyFieldType() { // Since the contained type "T" is Baz's only field, we map Baz to "T"'s // proxy type. return serializer.proxyFieldType(); } @Override public CodeBlock toProxy(CodeBlock expression) { return serializer.toProxy(expression); } @Override public CodeBlock fromProxy(CodeBlock expression) { return serializer.fromProxy(expression); } } } ``` This implementation uses `SerializerFactory` to find a `Serializer` for `T`. If a `Serializer` is available, we use it to map `Baz` to a serializable type. If no `Serializer` is available, we can do nothing and let `Baz` be serialized as-is. [AutoService]: https://github.com/google/auto/tree/master/service [`SerializableAutoValueExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/extension/SerializableAutoValueExtension.java [`SerializerExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerExtension.java [`Serializer`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/Serializer.java auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/000077500000000000000000000000001365703632600273375ustar00rootroot00000000000000AbortProcessingException.java000066400000000000000000000021251365703632600351060ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; /** * Exception thrown when annotation processing should be aborted for the current class. Processing * can continue on other classes. Throwing this exception does not cause a compiler error, so either * one should explicitly be emitted or it should be clear that the compiler will be producing its * own error for other reasons. * * @author emcmanus@google.com (Éamonn McManus) */ @SuppressWarnings("serial") class AbortProcessingException extends RuntimeException {} auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/AnnotationOutput.java000066400000000000000000000212671365703632600335450ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import java.util.List; import java.util.Map; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.tools.Diagnostic; /** * Handling of default values for annotation members. * * @author emcmanus@google.com (Éamonn McManus) */ final class AnnotationOutput { private AnnotationOutput() {} // There are no instances of this class. /** * Visitor that produces a string representation of an annotation value, suitable for inclusion in * a Java source file as an annotation member or as the initializer of a variable of the * appropriate type. The syntax for the two is the same except for annotation members that are * themselves annotations. Within an annotation, an annotation member can be written as * {@code @NestedAnnotation(...)}, while in an initializer it must be written as an object, for * example the construction of an {@code @AutoAnnotation} class. That's why we have this abstract * class and two concrete subclasses. */ private abstract static class SourceFormVisitor extends SimpleAnnotationValueVisitor8 { @Override protected Void defaultAction(Object value, StringBuilder sb) { sb.append(value); return null; } @Override public Void visitArray(List values, StringBuilder sb) { sb.append('{'); String sep = ""; for (AnnotationValue value : values) { sb.append(sep); visit(value, sb); sep = ", "; } sb.append('}'); return null; } @Override public Void visitChar(char c, StringBuilder sb) { appendQuoted(sb, c); return null; } @Override public Void visitLong(long i, StringBuilder sb) { sb.append(i).append('L'); return null; } @Override public Void visitDouble(double d, StringBuilder sb) { if (Double.isNaN(d)) { sb.append("Double.NaN"); } else if (d == Double.POSITIVE_INFINITY) { sb.append("Double.POSITIVE_INFINITY"); } else if (d == Double.NEGATIVE_INFINITY) { sb.append("Double.NEGATIVE_INFINITY"); } else { sb.append(d); } return null; } @Override public Void visitFloat(float f, StringBuilder sb) { if (Float.isNaN(f)) { sb.append("Float.NaN"); } else if (f == Float.POSITIVE_INFINITY) { sb.append("Float.POSITIVE_INFINITY"); } else if (f == Float.NEGATIVE_INFINITY) { sb.append("Float.NEGATIVE_INFINITY"); } else { sb.append(f).append('F'); } return null; } @Override public Void visitEnumConstant(VariableElement c, StringBuilder sb) { sb.append(TypeEncoder.encode(c.asType())).append('.').append(c.getSimpleName()); return null; } @Override public Void visitString(String s, StringBuilder sb) { appendQuoted(sb, s); return null; } @Override public Void visitType(TypeMirror classConstant, StringBuilder sb) { sb.append(TypeEncoder.encode(classConstant)).append(".class"); return null; } } private static class InitializerSourceFormVisitor extends SourceFormVisitor { private final ProcessingEnvironment processingEnv; private final String memberName; private final Element context; InitializerSourceFormVisitor( ProcessingEnvironment processingEnv, String memberName, Element context) { this.processingEnv = processingEnv; this.memberName = memberName; this.context = context; } @Override public Void visitAnnotation(AnnotationMirror a, StringBuilder sb) { processingEnv .getMessager() .printMessage( Diagnostic.Kind.ERROR, "@AutoAnnotation cannot yet supply a default value for annotation-valued member '" + memberName + "'", context); sb.append("null"); return null; } } private static class AnnotationSourceFormVisitor extends SourceFormVisitor { @Override public Void visitArray(List values, StringBuilder sb) { if (values.size() == 1) { // We can shorten @Foo(a = {23}) to @Foo(a = 23). For the specific case where `a` is // actually `value`, we'll already have shortened that in visitAnnotation, so effectively we // go from @Foo(value = {23}) to @Foo({23}) to @Foo(23). visit(values.get(0), sb); return null; } return super.visitArray(values, sb); } @Override public Void visitAnnotation(AnnotationMirror a, StringBuilder sb) { sb.append('@').append(TypeEncoder.encode(a.getAnnotationType())); ImmutableMap map = ImmutableMap.copyOf(a.getElementValues()); if (!map.isEmpty()) { sb.append('('); Optional shortForm = shortForm(map); if (shortForm.isPresent()) { this.visit(shortForm.get(), sb); } else { String sep = ""; for (Map.Entry entry : map.entrySet()) { sb.append(sep).append(entry.getKey().getSimpleName()).append(" = "); sep = ", "; this.visit(entry.getValue(), sb); } } sb.append(')'); } return null; } // We can shorten @Annot(value = 23) to @Annot(23). private static Optional shortForm( Map values) { if (values.size() == 1 && Iterables.getOnlyElement(values.keySet()).getSimpleName().contentEquals("value")) { return Optional.of(Iterables.getOnlyElement(values.values())); } return Optional.empty(); } } /** * Returns a string representation of the given annotation value, suitable for inclusion in a Java * source file as the initializer of a variable of the appropriate type. */ static String sourceFormForInitializer( AnnotationValue annotationValue, ProcessingEnvironment processingEnv, String memberName, Element context) { SourceFormVisitor visitor = new InitializerSourceFormVisitor(processingEnv, memberName, context); StringBuilder sb = new StringBuilder(); visitor.visit(annotationValue, sb); return sb.toString(); } /** * Returns a string representation of the given annotation mirror, suitable for inclusion in a * Java source file to reproduce the annotation in source form. */ static String sourceFormForAnnotation(AnnotationMirror annotationMirror) { StringBuilder sb = new StringBuilder(); new AnnotationSourceFormVisitor().visitAnnotation(annotationMirror, sb); return sb.toString(); } private static StringBuilder appendQuoted(StringBuilder sb, String s) { sb.append('"'); for (int i = 0; i < s.length(); i++) { appendEscaped(sb, s.charAt(i)); } return sb.append('"'); } private static StringBuilder appendQuoted(StringBuilder sb, char c) { sb.append('\''); appendEscaped(sb, c); return sb.append('\''); } private static void appendEscaped(StringBuilder sb, char c) { switch (c) { case '\\': case '"': case '\'': sb.append('\\').append(c); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; default: if (c < 0x20) { sb.append(String.format("\\%03o", (int) c)); } else if (c < 0x7f || Character.isLetter(c)) { sb.append(c); } else { sb.append(String.format("\\u%04x", (int) c)); } break; } } } AutoAnnotationProcessor.java000066400000000000000000000540661365703632600350010ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation; import static com.google.auto.value.processor.ClassNames.AUTO_ANNOTATION_NAME; import com.google.auto.common.MoreElements; import com.google.auto.common.SuperficialValidation; import com.google.auto.service.AutoService; import com.google.common.base.Preconditions; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Primitives; import com.google.errorprone.annotations.FormatMethod; import java.io.IOException; import java.io.Writer; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; /** * Javac annotation processor (compiler plugin) to generate annotation implementations. User code * never references this class. * * @author emcmanus@google.com (Éamonn McManus) */ @AutoService(Processor.class) @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) @SupportedAnnotationTypes(AUTO_ANNOTATION_NAME) public class AutoAnnotationProcessor extends AbstractProcessor { public AutoAnnotationProcessor() {} @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** * Issue a compilation error. This method does not throw an exception, since we want to continue * processing and perhaps report other errors. */ @FormatMethod private void reportError(Element e, String msg, Object... msgParams) { String formattedMessage = String.format(msg, msgParams); processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, formattedMessage, e); } /** * Issue a compilation error and return an exception that, when thrown, will cause the processing * of this class to be abandoned. This does not prevent the processing of other classes. */ @FormatMethod private AbortProcessingException abortWithError(Element e, String msg, Object... msgParams) { reportError(e, msg, msgParams); return new AbortProcessingException(); } private Elements elementUtils; private Types typeUtils; @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { elementUtils = processingEnv.getElementUtils(); typeUtils = processingEnv.getTypeUtils(); boolean claimed = (annotations.size() == 1 && annotations .iterator() .next() .getQualifiedName() .contentEquals(AUTO_ANNOTATION_NAME)); if (claimed) { process(roundEnv); return true; } else { return false; } } private void process(RoundEnvironment roundEnv) { TypeElement autoAnnotation = elementUtils.getTypeElement(AUTO_ANNOTATION_NAME); Collection annotatedElements = roundEnv.getElementsAnnotatedWith(autoAnnotation); List methods = ElementFilter.methodsIn(annotatedElements); if (!SuperficialValidation.validateElements(methods) || methodsAreOverloaded(methods)) { return; } for (ExecutableElement method : methods) { try { processMethod(method); } catch (AbortProcessingException e) { // We abandoned this type, but continue with the next. } catch (RuntimeException e) { String trace = Throwables.getStackTraceAsString(e); reportError(method, "@AutoAnnotation processor threw an exception: %s", trace); throw e; } } } private void processMethod(ExecutableElement method) { if (!method.getModifiers().contains(Modifier.STATIC)) { throw abortWithError(method, "@AutoAnnotation method must be static"); } TypeElement annotationElement = getAnnotationReturnType(method); Set> wrapperTypesUsedInCollections = wrapperTypesUsedInCollections(method); ImmutableMap memberMethods = getMemberMethods(annotationElement); TypeElement methodClass = (TypeElement) method.getEnclosingElement(); String pkg = TypeSimplifier.packageNameOf(methodClass); ImmutableMap defaultValues = getDefaultValues(annotationElement); ImmutableMap members = getMembers(method, memberMethods); ImmutableMap parameters = getParameters(annotationElement, method, members); validateParameters(annotationElement, method, members, parameters, defaultValues); String generatedClassName = generatedClassName(method); AutoAnnotationTemplateVars vars = new AutoAnnotationTemplateVars(); vars.annotationFullName = annotationElement.toString(); vars.annotationName = TypeEncoder.encode(annotationElement.asType()); vars.className = generatedClassName; vars.generated = getGeneratedTypeName(); vars.members = members; vars.params = parameters; vars.pkg = pkg; vars.wrapperTypesUsedInCollections = wrapperTypesUsedInCollections; vars.gwtCompatible = isGwtCompatible(annotationElement); ImmutableMap invariableHashes = invariableHashes(members, parameters.keySet()); vars.invariableHashSum = 0; for (int h : invariableHashes.values()) { vars.invariableHashSum += h; } vars.invariableHashes = invariableHashes.keySet(); String text = vars.toText(); text = TypeEncoder.decode(text, processingEnv, pkg, annotationElement.asType()); text = Reformatter.fixup(text); String fullName = fullyQualifiedName(pkg, generatedClassName); writeSourceFile(fullName, text, methodClass); } private String getGeneratedTypeName() { return generatedAnnotation(elementUtils, processingEnv.getSourceVersion()) .map(generatedAnnotation -> TypeEncoder.encode(generatedAnnotation.asType())) .orElse(""); } /** * Returns the hashCode of the given AnnotationValue, if that hashCode is guaranteed to be always * the same. The hashCode of a String or primitive type never changes. The hashCode of a Class or * an enum constant does potentially change in different runs of the same program. The hashCode of * an array doesn't change if the hashCodes of its elements don't. Although we could have a * similar rule for nested annotation values, we currently don't. */ private static Optional invariableHash(AnnotationValue annotationValue) { Object value = annotationValue.getValue(); if (value instanceof String || Primitives.isWrapperType(value.getClass())) { return Optional.of(value.hashCode()); } else if (value instanceof List) { @SuppressWarnings("unchecked") // by specification List list = (List) value; return invariableHash(list); } else { return Optional.empty(); } } private static Optional invariableHash( List annotationValues) { int h = 1; for (AnnotationValue annotationValue : annotationValues) { Optional maybeHash = invariableHash(annotationValue); if (!maybeHash.isPresent()) { return Optional.empty(); } h = h * 31 + maybeHash.get(); } return Optional.of(h); } /** * Returns a map from the names of members with invariable hashCodes to the values of those * hashCodes. */ private static ImmutableMap invariableHashes( ImmutableMap members, ImmutableSet parameters) { ImmutableMap.Builder builder = ImmutableMap.builder(); for (String element : members.keySet()) { if (!parameters.contains(element)) { Member member = members.get(element); AnnotationValue annotationValue = member.method.getDefaultValue(); Optional invariableHash = invariableHash(annotationValue); if (invariableHash.isPresent()) { builder.put(element, (element.hashCode() * 127) ^ invariableHash.get()); } } } return builder.build(); } private boolean methodsAreOverloaded(List methods) { boolean overloaded = false; Set classNames = new HashSet(); for (ExecutableElement method : methods) { String qualifiedClassName = fullyQualifiedName( MoreElements.getPackage(method).getQualifiedName().toString(), generatedClassName(method)); if (!classNames.add(qualifiedClassName)) { overloaded = true; reportError(method, "@AutoAnnotation methods cannot be overloaded"); } } return overloaded; } private String generatedClassName(ExecutableElement method) { TypeElement type = (TypeElement) method.getEnclosingElement(); String name = type.getSimpleName().toString(); while (type.getEnclosingElement() instanceof TypeElement) { type = (TypeElement) type.getEnclosingElement(); name = type.getSimpleName() + "_" + name; } return "AutoAnnotation_" + name + "_" + method.getSimpleName(); } private TypeElement getAnnotationReturnType(ExecutableElement method) { TypeMirror returnTypeMirror = method.getReturnType(); if (returnTypeMirror.getKind() == TypeKind.DECLARED) { Element returnTypeElement = typeUtils.asElement(method.getReturnType()); if (returnTypeElement.getKind() == ElementKind.ANNOTATION_TYPE) { return (TypeElement) returnTypeElement; } } throw abortWithError( method, "Return type of @AutoAnnotation method must be an annotation type, not %s", returnTypeMirror); } private ImmutableMap getMemberMethods(TypeElement annotationElement) { ImmutableMap.Builder members = ImmutableMap.builder(); for (ExecutableElement member : ElementFilter.methodsIn(annotationElement.getEnclosedElements())) { String name = member.getSimpleName().toString(); members.put(name, member); } return members.build(); } private ImmutableMap getMembers( Element context, ImmutableMap memberMethods) { ImmutableMap.Builder members = ImmutableMap.builder(); for (Map.Entry entry : memberMethods.entrySet()) { ExecutableElement memberMethod = entry.getValue(); String name = memberMethod.getSimpleName().toString(); members.put(name, new Member(processingEnv, context, memberMethod)); } return members.build(); } private ImmutableMap getDefaultValues(TypeElement annotationElement) { ImmutableMap.Builder defaultValues = ImmutableMap.builder(); for (ExecutableElement member : ElementFilter.methodsIn(annotationElement.getEnclosedElements())) { String name = member.getSimpleName().toString(); AnnotationValue defaultValue = member.getDefaultValue(); if (defaultValue != null) { defaultValues.put(name, defaultValue); } } return defaultValues.build(); } private ImmutableMap getParameters( TypeElement annotationElement, ExecutableElement method, Map members) { ImmutableMap.Builder parameters = ImmutableMap.builder(); boolean error = false; for (VariableElement parameter : method.getParameters()) { String name = parameter.getSimpleName().toString(); Member member = members.get(name); if (member == null) { reportError( parameter, "@AutoAnnotation method parameter '%s' must have the same name as a member of %s", name, annotationElement); error = true; } else { TypeMirror parameterType = parameter.asType(); TypeMirror memberType = member.getTypeMirror(); if (compatibleTypes(parameterType, memberType)) { parameters.put(name, new Parameter(parameterType)); } else { reportError( parameter, "@AutoAnnotation method parameter '%s' has type %s but %s.%s has type %s", name, parameterType, annotationElement, name, memberType); error = true; } } } if (error) { throw new AbortProcessingException(); } return parameters.build(); } private void validateParameters( TypeElement annotationElement, ExecutableElement method, ImmutableMap members, ImmutableMap parameters, ImmutableMap defaultValues) { boolean error = false; for (String memberName : members.keySet()) { if (!parameters.containsKey(memberName) && !defaultValues.containsKey(memberName)) { reportError( method, "@AutoAnnotation method needs a parameter with name '%s' and type %s" + " corresponding to %s.%s, which has no default value", memberName, members.get(memberName).getType(), annotationElement, memberName); error = true; } } if (error) { throw new AbortProcessingException(); } } /** * Returns true if {@code parameterType} can be used to provide the value of an annotation member * of type {@code memberType}. They must either be the same type, or the member type must be an * array and the parameter type must be a collection of a compatible type. */ private boolean compatibleTypes(TypeMirror parameterType, TypeMirror memberType) { if (typeUtils.isAssignable(parameterType, memberType)) { // parameterType assignable to memberType, which in the restricted world of annotations // means they are the same type, or maybe memberType is an annotation type and parameterType // is a subtype of that annotation interface (why would you do that?). return true; } // They're not the same, but we could still consider them compatible if for example // parameterType is List and memberType is int[]. We accept any type that is assignable // to Collection (in this example). if (memberType.getKind() != TypeKind.ARRAY) { return false; } TypeMirror arrayElementType = ((ArrayType) memberType).getComponentType(); TypeMirror wrappedArrayElementType = arrayElementType.getKind().isPrimitive() ? typeUtils.boxedClass((PrimitiveType) arrayElementType).asType() : arrayElementType; TypeElement javaUtilCollection = elementUtils.getTypeElement(Collection.class.getCanonicalName()); DeclaredType collectionOfElement = typeUtils.getDeclaredType(javaUtilCollection, wrappedArrayElementType); return typeUtils.isAssignable(parameterType, collectionOfElement); } /** * Returns the wrapper types ({@code Integer.class} etc) that are used in collection parameters * like {@code List}. This is needed because we will emit a helper method for each such * type, for example to convert {@code Collection} into {@code int[]}. */ private Set> wrapperTypesUsedInCollections(ExecutableElement method) { TypeElement javaUtilCollection = elementUtils.getTypeElement(Collection.class.getName()); ImmutableSet.Builder> usedInCollections = ImmutableSet.builder(); for (Class wrapper : Primitives.allWrapperTypes()) { DeclaredType collectionOfWrapper = typeUtils.getDeclaredType(javaUtilCollection, getTypeMirror(wrapper)); for (VariableElement parameter : method.getParameters()) { if (typeUtils.isAssignable(parameter.asType(), collectionOfWrapper)) { usedInCollections.add(wrapper); break; } } } return usedInCollections.build(); } private TypeMirror getTypeMirror(Class c) { return elementUtils.getTypeElement(c.getName()).asType(); } private static boolean isGwtCompatible(TypeElement annotationElement) { return annotationElement .getAnnotationMirrors() .stream() .map(mirror -> mirror.getAnnotationType().asElement()) .anyMatch(element -> element.getSimpleName().contentEquals("GwtCompatible")); } private static String fullyQualifiedName(String pkg, String cls) { return pkg.isEmpty() ? cls : pkg + "." + cls; } private void writeSourceFile(String className, String text, TypeElement originatingType) { try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(className, originatingType); try (Writer writer = sourceFile.openWriter()) { writer.write(text); } } catch (IOException e) { // This should really be an error, but we make it a warning in the hope of resisting Eclipse // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599. If that bug manifests, we may get // invoked more than once for the same file, so ignoring the ability to overwrite it is the // right thing to do. If we are unable to write for some other reason, we should get a compile // error later because user code will have a reference to the code we were supposed to // generate (new AutoValue_Foo() or whatever) and that reference will be undefined. processingEnv .getMessager() .printMessage( Diagnostic.Kind.WARNING, "Could not write generated class " + className + ": " + e); } } public static class Member { private final ProcessingEnvironment processingEnv; private final Element context; private final ExecutableElement method; Member(ProcessingEnvironment processingEnv, Element context, ExecutableElement method) { this.processingEnv = processingEnv; this.context = context; this.method = method; } @Override public String toString() { return method.getSimpleName().toString(); } public String getType() { return TypeEncoder.encode(getTypeMirror()); } public String getComponentType() { Preconditions.checkState(getTypeMirror().getKind() == TypeKind.ARRAY); ArrayType arrayType = (ArrayType) getTypeMirror(); return TypeEncoder.encode(arrayType.getComponentType()); } public TypeMirror getTypeMirror() { return method.getReturnType(); } public TypeKind getKind() { return getTypeMirror().getKind(); } // Used as part of the hashCode() computation. // See https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Annotation.html#hashCode-- public int getNameHash() { return 127 * toString().hashCode(); } public boolean isArrayOfClassWithBounds() { if (getTypeMirror().getKind() != TypeKind.ARRAY) { return false; } TypeMirror componentType = ((ArrayType) getTypeMirror()).getComponentType(); if (componentType.getKind() != TypeKind.DECLARED) { return false; } DeclaredType declared = (DeclaredType) componentType; if (!((TypeElement) processingEnv.getTypeUtils().asElement(componentType)) .getQualifiedName() .contentEquals("java.lang.Class")) { return false; } if (declared.getTypeArguments().size() != 1) { return false; } TypeMirror parameter = declared.getTypeArguments().get(0); if (parameter.getKind() != TypeKind.WILDCARD) { return true; // for Class } WildcardType wildcard = (WildcardType) parameter; // In theory, we should check if getExtendsBound() != Object, since '?' is equivalent to // '? extends Object', but, experimentally, neither javac or ecj will sets getExtendsBound() // to 'Object', so there isn't a point in checking. return wildcard.getSuperBound() != null || wildcard.getExtendsBound() != null; } public String getDefaultValue() { AnnotationValue defaultValue = method.getDefaultValue(); if (defaultValue == null) { return null; } else { return AnnotationOutput.sourceFormForInitializer( defaultValue, processingEnv, method.getSimpleName().toString(), context); } } } public static class Parameter { private final String typeName; private final TypeKind kind; Parameter(TypeMirror type) { this.typeName = TypeEncoder.encode(type); this.kind = type.getKind(); } public String getType() { return typeName; } public TypeKind getKind() { return kind; } } } AutoAnnotationTemplateVars.java000066400000000000000000000053161365703632600354230ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import com.google.escapevelocity.Template; import java.util.Map; import java.util.Set; /** * The variables to substitute into the autoannotation.vm template. * * @author emcmanus@google.com (Éamonn McManus) */ @SuppressWarnings("unused") // the fields in this class are only read via reflection class AutoAnnotationTemplateVars extends TemplateVars { /** The members of the annotation being implemented. */ Map members; /** * The parameters in the {@code @AutoAnnotation} method, which are also the constructor parameters * in the generated class. */ Map params; /** The encoded form of the {@code Generated} class, or empty if it is not available. */ String generated; /** * The package of the class containing the {@code @AutoAnnotation} annotation, which is also the * package where the annotation implementation will be generated. */ String pkg; /** The simple name of the generated class, like {@code AutoAnnotation_Foo_bar}. */ String className; /** The name of the annotation interface as it can be referenced in the generated code. */ String annotationName; /** The fully-qualified name of the annotation interface. */ String annotationFullName; /** * The wrapper types (like {@code Integer.class}) that are referenced in collection parameters * (like {@code List}). */ Set> wrapperTypesUsedInCollections; /** * True if this annotation is marked {@code @GwtCompatible}. That means that we can't use {@code * clone()} to make a copy of an array. */ Boolean gwtCompatible; /** * The names of members that are defaulted (not mentioned) in this {@code @AutoAnnotation}, and * whose hash codes are invariable. */ Set invariableHashes; /** The sum of the hash code contributions from the members in {@link #invariableHashes}. */ Integer invariableHashSum; private static final Template TEMPLATE = parsedTemplateForResource("autoannotation.vm"); @Override Template parsedTemplate() { return TEMPLATE; } } AutoOneOfProcessor.java000066400000000000000000000302231365703632600336620ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.auto.value.processor.ClassNames.AUTO_ONE_OF_NAME; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; import com.google.auto.common.AnnotationMirrors; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.util.LinkedHashSet; import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.processing.Processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; /** * Javac annotation processor (compiler plugin) for {@linkplain com.google.auto.value.AutoOneOf * one-of} types; user code never references this class. * * @author Éamonn McManus * @see AutoValue User's Guide */ @AutoService(Processor.class) @SupportedAnnotationTypes(AUTO_ONE_OF_NAME) @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor { public AutoOneOfProcessor() { super(AUTO_ONE_OF_NAME); } @Override boolean propertiesCanBeVoid() { return true; } @Override void processType(TypeElement autoOneOfType) { if (autoOneOfType.getKind() != ElementKind.CLASS) { errorReporter() .abortWithError("@" + AUTO_ONE_OF_NAME + " only applies to classes", autoOneOfType); } checkModifiersIfNested(autoOneOfType); DeclaredType kindMirror = mirrorForKindType(autoOneOfType); // We are going to classify the methods of the @AutoOneOf class into several categories. // This covers the methods in the class itself and the ones it inherits from supertypes. // First, the only concrete (non-abstract) methods we are interested in are overrides of // Object methods (equals, hashCode, toString), which signal that we should not generate // an implementation of those methods. // Then, each abstract method is one of the following: // (1) A property getter, like "abstract String foo()" or "abstract String getFoo()". // (2) A kind getter, which is a method that returns the enum in @AutoOneOf. For // example if we have @AutoOneOf(PetKind.class), this would be a method that returns // PetKind. // If there are abstract methods that don't fit any of the categories above, that is an error // which we signal explicitly to avoid confusion. ImmutableSet methods = getLocalAndInheritedMethods( autoOneOfType, processingEnv.getTypeUtils(), processingEnv.getElementUtils()); ImmutableSet abstractMethods = abstractMethodsIn(methods); ExecutableElement kindGetter = findKindGetterOrAbort(autoOneOfType, kindMirror, abstractMethods); Set otherMethods = new LinkedHashSet<>(abstractMethods); otherMethods.remove(kindGetter); ImmutableMap propertyMethodsAndTypes = propertyMethodsIn(otherMethods, autoOneOfType); ImmutableBiMap properties = propertyNameToMethodMap(propertyMethodsAndTypes.keySet()); validateMethods(autoOneOfType, abstractMethods, propertyMethodsAndTypes.keySet(), kindGetter); ImmutableMap propertyToKind = propertyToKindMap(kindMirror, properties.keySet()); String subclass = generatedClassName(autoOneOfType, "AutoOneOf_"); AutoOneOfTemplateVars vars = new AutoOneOfTemplateVars(); vars.generatedClass = TypeSimplifier.simpleNameOf(subclass); vars.propertyToKind = propertyToKind; defineSharedVarsForType(autoOneOfType, methods, vars); defineVarsForType(autoOneOfType, vars, propertyMethodsAndTypes, kindGetter); String text = vars.toText(); text = TypeEncoder.decode(text, processingEnv, vars.pkg, autoOneOfType.asType()); text = Reformatter.fixup(text); writeSourceFile(subclass, text, autoOneOfType); } private DeclaredType mirrorForKindType(TypeElement autoOneOfType) { Optional oneOfAnnotation = getAnnotationMirror(autoOneOfType, AUTO_ONE_OF_NAME); if (!oneOfAnnotation.isPresent()) { // This shouldn't happen unless the compilation environment is buggy, // but it has happened in the past and can crash the compiler. errorReporter() .abortWithError( "annotation processor for @AutoOneOf was invoked with a type" + " that does not have that annotation; this is probably a compiler bug", autoOneOfType); } AnnotationValue kindValue = AnnotationMirrors.getAnnotationValue(oneOfAnnotation.get(), "value"); Object value = kindValue.getValue(); if (value instanceof TypeMirror && ((TypeMirror) value).getKind().equals(TypeKind.DECLARED)) { return MoreTypes.asDeclared((TypeMirror) value); } else { // This is presumably because the referenced type doesn't exist. throw new MissingTypeException(); } } private ImmutableMap propertyToKindMap( DeclaredType kindMirror, ImmutableSet propertyNames) { // We require a one-to-one correspondence between the property names and the enum constants. // We must have transformName(propertyName) = transformName(constantName) for each one. // So we build two maps, transformName(propertyName) → propertyName and // transformName(constantName) → constant. The key sets of the two maps must match, and we // can then join them to make propertyName → constantName. TypeElement kindElement = MoreElements.asType(kindMirror.asElement()); Map transformedPropertyNames = propertyNames.stream().collect(toMap(this::transformName, s -> s)); Map transformedEnumConstants = kindElement .getEnclosedElements() .stream() .filter(e -> e.getKind().equals(ElementKind.ENUM_CONSTANT)) .collect(toMap(e -> transformName(e.getSimpleName().toString()), e -> e)); if (transformedPropertyNames.keySet().equals(transformedEnumConstants.keySet())) { ImmutableMap.Builder mapBuilder = ImmutableMap.builder(); for (String transformed : transformedPropertyNames.keySet()) { mapBuilder.put( transformedPropertyNames.get(transformed), transformedEnumConstants.get(transformed).getSimpleName().toString()); } return mapBuilder.build(); } // The names don't match. Emit errors for the differences. // Properties that have no enum constant transformedPropertyNames.forEach( (transformed, property) -> { if (!transformedEnumConstants.containsKey(transformed)) { errorReporter() .reportError( "Enum has no constant with name corresponding to property '" + property + "'", kindElement); } }); // Enum constants that have no property transformedEnumConstants.forEach( (transformed, constant) -> { if (!transformedPropertyNames.containsKey(transformed)) { errorReporter() .reportError( "Name of enum constant '" + constant.getSimpleName() + "' does not correspond to any property name", constant); } }); throw new AbortProcessingException(); } private String transformName(String s) { return s.toLowerCase(Locale.ROOT).replace("_", ""); } private ExecutableElement findKindGetterOrAbort( TypeElement autoOneOfType, TypeMirror kindMirror, ImmutableSet abstractMethods) { Set kindGetters = abstractMethods .stream() .filter(e -> sameType(kindMirror, e.getReturnType())) .filter(e -> e.getParameters().isEmpty()) .collect(toSet()); switch (kindGetters.size()) { case 0: errorReporter() .reportError( autoOneOfType + " must have a no-arg abstract method returning " + kindMirror, autoOneOfType); break; case 1: return Iterables.getOnlyElement(kindGetters); default: for (ExecutableElement getter : kindGetters) { errorReporter() .reportError("More than one abstract method returns " + kindMirror, getter); } } throw new AbortProcessingException(); } private void validateMethods( TypeElement type, ImmutableSet abstractMethods, ImmutableSet propertyMethods, ExecutableElement kindGetter) { for (ExecutableElement method : abstractMethods) { if (propertyMethods.contains(method)) { checkReturnType(type, method); } else if (!method.equals(kindGetter) && objectMethodToOverride(method) == ObjectMethod.NONE) { // This could reasonably be an error, were it not for an Eclipse bug in // ElementUtils.override that sometimes fails to recognize that one method overrides // another, and therefore leaves us with both an abstract method and the subclass method // that overrides it. This shows up in AutoValueTest.LukesBase for example. // The compilation will fail anyway because the generated concrete classes won't // implement this alien method. errorReporter() .reportWarning( "Abstract methods in @AutoOneOf classes must have no parameters", method); } } errorReporter().abortIfAnyError(); } private void defineVarsForType( TypeElement type, AutoOneOfTemplateVars vars, ImmutableMap propertyMethodsAndTypes, ExecutableElement kindGetter) { vars.props = propertySet( propertyMethodsAndTypes, ImmutableListMultimap.of(), ImmutableListMultimap.of()); vars.kindGetter = kindGetter.getSimpleName().toString(); vars.kindType = TypeEncoder.encode(kindGetter.getReturnType()); TypeElement javaIoSerializable = elementUtils().getTypeElement("java.io.Serializable"); vars.serializable = javaIoSerializable != null // just in case && typeUtils().isAssignable(type.asType(), javaIoSerializable.asType()); } @Override Optional nullableAnnotationForMethod(ExecutableElement propertyMethod) { if (nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()).isPresent()) { errorReporter().reportError("@AutoOneOf properties cannot be @Nullable", propertyMethod); } return Optional.empty(); } private static boolean sameType(TypeMirror t1, TypeMirror t2) { return MoreTypes.equivalence().equivalent(t1, t2); } } AutoOneOfTemplateVars.java000066400000000000000000000035571365703632600343240ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import com.google.common.collect.ImmutableSet; import com.google.escapevelocity.Template; import java.util.Map; /** * The variables to substitute into the autooneof.vm template. * * @author emcmanus@google.com (Éamonn McManus) */ @SuppressWarnings("unused") // the fields in this class are only read via reflection class AutoOneOfTemplateVars extends AutoValueOrOneOfTemplateVars { /** * The properties defined by the parent class's abstract methods. The elements of this set are in * the same order as the original abstract method declarations in the AutoOneOf class. */ ImmutableSet props; /** The simple name of the generated class. */ String generatedClass; /** The encoded name of the "kind" enum class. */ String kindType; /** The name of the method that gets the kind of the current {@code @AutoOneOf} instance. */ String kindGetter; /** Maps property names like {@code dog} to enum constants like {@code DOG}. */ Map propertyToKind; /** True if this {@code @AutoOneOf} class is Serializable. */ Boolean serializable; private static final Template TEMPLATE = parsedTemplateForResource("autooneof.vm"); @Override Template parsedTemplate() { return TEMPLATE; } } AutoValueBuilderProcessor.java000066400000000000000000000064561365703632600352520ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.hasAnnotationMirror; import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_BUILDER_NAME; import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_NAME; import com.google.auto.common.SuperficialValidation; import com.google.auto.service.AutoService; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; /** * Annotation processor that checks that the type that {@code AutoValue.Builder} is applied to is * nested inside an {@code @AutoValue} class. The actual code generation for builders is done in * {@link AutoValueProcessor}. * * @author Éamonn McManus */ @AutoService(Processor.class) @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING) @SupportedAnnotationTypes(AUTO_VALUE_BUILDER_NAME) public class AutoValueBuilderProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { TypeElement autoValueBuilder = processingEnv.getElementUtils().getTypeElement(AUTO_VALUE_BUILDER_NAME); Set builderTypes = roundEnv.getElementsAnnotatedWith(autoValueBuilder); if (!SuperficialValidation.validateElements(builderTypes)) { return false; } for (Element annotatedType : builderTypes) { // Double-check that the annotation is there. Sometimes the compiler gets confused in case of // erroneous source code. SuperficialValidation should protect us against this but it doesn't // cost anything to check again. if (hasAnnotationMirror(annotatedType, AUTO_VALUE_BUILDER_NAME)) { validate( annotatedType, "@AutoValue.Builder can only be applied to a class or interface inside an" + " @AutoValue class"); } } return false; } private void validate(Element annotatedType, String errorMessage) { Element container = annotatedType.getEnclosingElement(); if (!hasAnnotationMirror(container, AUTO_VALUE_NAME)) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, errorMessage, annotatedType); } } } AutoValueOrOneOfProcessor.java000066400000000000000000001357271365703632600351770ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation; import static com.google.auto.common.MoreElements.getPackage; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_PACKAGE_NAME; import static com.google.auto.value.processor.ClassNames.COPY_ANNOTATIONS_NAME; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Sets.union; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toCollection; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.common.Visibility; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.io.Serializable; import java.io.Writer; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.function.Predicate; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.Name; import javax.lang.model.element.QualifiedNameable; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleAnnotationValueVisitor8; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; /** * Shared code between AutoValueProcessor and AutoOneOfProcessor. * * @author emcmanus@google.com (Éamonn McManus) */ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor { private final String annotationClassName; /** * Qualified names of {@code @AutoValue} or {@code AutoOneOf} classes that we attempted to process * but had to abandon because we needed other types that they referenced and those other types * were missing. */ private final List deferredTypeNames = new ArrayList<>(); AutoValueOrOneOfProcessor(String annotationClassName) { this.annotationClassName = annotationClassName; } /** The annotation we are processing, {@code AutoValue} or {@code AutoOneOf}. */ private TypeElement annotationType; /** The simple name of {@link #annotationType}. */ private String simpleAnnotationName; private ErrorReporter errorReporter; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); errorReporter = new ErrorReporter(processingEnv); } final ErrorReporter errorReporter() { return errorReporter; } final Types typeUtils() { return processingEnv.getTypeUtils(); } final Elements elementUtils() { return processingEnv.getElementUtils(); } @Override public final SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } /** * A property of an {@code @AutoValue} or {@code @AutoOneOf} class, defined by one of its abstract * methods. An instance of this class is made available to the Velocity template engine for each * property. The public methods of this class define JavaBeans-style properties that are * accessible from templates. For example {@link #getType()} means we can write {@code $p.type} * for a Velocity variable {@code $p} that is a {@code Property}. */ public static class Property { private final String name; private final String identifier; private final ExecutableElement method; private final String type; private final ImmutableList fieldAnnotations; private final ImmutableList methodAnnotations; private final Optional nullableAnnotation; private final Optionalish optional; Property( String name, String identifier, ExecutableElement method, String type, ImmutableList fieldAnnotations, ImmutableList methodAnnotations, Optional nullableAnnotation) { this.name = name; this.identifier = identifier; this.method = method; this.type = type; this.fieldAnnotations = fieldAnnotations; this.methodAnnotations = methodAnnotations; this.nullableAnnotation = nullableAnnotation; TypeMirror propertyType = method.getReturnType(); this.optional = Optionalish.createIfOptional(propertyType); } /** * Returns the name of the property as it should be used when declaring identifiers (fields and * parameters). If the original getter method was {@code foo()} then this will be {@code foo}. * If it was {@code getFoo()} then it will be {@code foo}. If it was {@code getPackage()} then * it will be something like {@code package0}, since {@code package} is a reserved word. */ @Override public String toString() { return identifier; } /** * Returns the name of the property as it should be used in strings visible to users. This is * usually the same as {@code toString()}, except that if we had to use an identifier like * "package0" because "package" is a reserved word, the name here will be the original * "package". */ public String getName() { return name; } /** * Returns the name of the getter method for this property as defined by the {@code @AutoValue} * class. For property {@code foo}, this will be {@code foo} or {@code getFoo} or {@code isFoo}. */ public String getGetter() { return method.getSimpleName().toString(); } public TypeMirror getTypeMirror() { return method.getReturnType(); } public String getType() { return type; } public TypeKind getKind() { return method.getReturnType().getKind(); } /** * Returns the annotations (in string form) that should be applied to the property's field * declaration. */ public List getFieldAnnotations() { return fieldAnnotations; } /** * Returns the annotations (in string form) that should be applied to the property's method * implementation. */ public List getMethodAnnotations() { return methodAnnotations; } /** * Returns an {@link Optionalish} representing the kind of Optional that this property's type * is, or null if the type is not an Optional of any kind. */ public Optionalish getOptional() { return optional; } /** * Returns the string to use as a method annotation to indicate the nullability of this * property. It is either the empty string, if the property is not nullable, or an annotation * string with a trailing space, such as {@code "@`javax.annotation.Nullable` "}, where the * {@code ``} is the encoding used by {@link TypeEncoder}. If the property is nullable by virtue * of its type rather than its method being {@code @Nullable}, this method returns the * empty string, because the {@code @Nullable} will appear when the type is spelled out. In this * case, {@link #nullableAnnotation} is present but empty. */ public final String getNullableAnnotation() { return nullableAnnotation.orElse(""); } public boolean isNullable() { return nullableAnnotation.isPresent(); } public String getAccess() { return SimpleMethod.access(method); } @Override public boolean equals(Object obj) { return obj instanceof Property && ((Property) obj).method.equals(method); } @Override public int hashCode() { return method.hashCode(); } } @Override public final boolean process(Set annotations, RoundEnvironment roundEnv) { annotationType = elementUtils().getTypeElement(annotationClassName); if (annotationType == null) { // This should not happen. If the annotation type is not found, how did the processor get // triggered? processingEnv .getMessager() .printMessage( Diagnostic.Kind.ERROR, "Did not process @" + annotationClassName + " because the annotation class was not found"); return false; } simpleAnnotationName = annotationType.getSimpleName().toString(); List deferredTypes = deferredTypeNames .stream() .map(name -> elementUtils().getTypeElement(name)) .collect(toList()); if (roundEnv.processingOver()) { // This means that the previous round didn't generate any new sources, so we can't have found // any new instances of @AutoValue; and we can't have any new types that are the reason a type // was in deferredTypes. for (TypeElement type : deferredTypes) { errorReporter.reportError( "Did not generate @" + simpleAnnotationName + " class for " + type.getQualifiedName() + " because it references undefined types", type); } return false; } Collection annotatedElements = roundEnv.getElementsAnnotatedWith(annotationType); List types = new ImmutableList.Builder() .addAll(deferredTypes) .addAll(ElementFilter.typesIn(annotatedElements)) .build(); deferredTypeNames.clear(); for (TypeElement type : types) { try { processType(type); } catch (AbortProcessingException e) { // We abandoned this type; continue with the next. } catch (MissingTypeException e) { // We abandoned this type, but only because we needed another type that it references and // that other type was missing. It is possible that the missing type will be generated by // further annotation processing, so we will try again on the next round (perhaps failing // again and adding it back to the list). We save the name of the @AutoValue type rather // than its TypeElement because it is not guaranteed that it will be represented by // the same TypeElement on the next round. deferredTypeNames.add(type.getQualifiedName().toString()); } catch (RuntimeException e) { String trace = Throwables.getStackTraceAsString(e); errorReporter.reportError( "@" + simpleAnnotationName + " processor threw an exception: " + trace, type); throw e; } } return false; // never claim annotation, because who knows what other processors want? } /** * Analyzes a single {@code @AutoValue} or {@code @AutoOneOf} class, and outputs the corresponding * implementation class or classes. * * @param type the class with the {@code @AutoValue} or {@code @AutoOneOf} annotation. */ abstract void processType(TypeElement type); /** * Returns the appropriate {@code @Nullable} annotation to put on the implementation of the given * property method, and indicates whether the property is in fact nullable. The annotation in * question is on the method, not its return type. If instead the return type is * {@code @Nullable}, this method returns {@code Optional.of("")}, to indicate that the property * is nullable but the method isn't. The {@code @Nullable} annotation will instead appear * when the return type of the method is spelled out in the implementation. */ abstract Optional nullableAnnotationForMethod(ExecutableElement propertyMethod); /** * Returns the ordered set of {@link Property} definitions for the given {@code @AutoValue} or * {@code AutoOneOf} type. * * @param annotatedPropertyMethods a map from property methods to the method annotations that * should go on the implementation of those methods. These annotations are method annotations * specifically. Type annotations do not appear because they are considered part of the return * type and will appear when that is spelled out. Annotations that are excluded by {@code * AutoValue.CopyAnnotations} also do not appear here. */ final ImmutableSet propertySet( ImmutableMap propertyMethodsAndTypes, ImmutableListMultimap annotatedPropertyFields, ImmutableListMultimap annotatedPropertyMethods) { ImmutableBiMap methodToPropertyName = propertyNameToMethodMap(propertyMethodsAndTypes.keySet()).inverse(); Map methodToIdentifier = new LinkedHashMap<>(methodToPropertyName); fixReservedIdentifiers(methodToIdentifier); ImmutableSet.Builder props = ImmutableSet.builder(); propertyMethodsAndTypes.forEach( (propertyMethod, returnType) -> { String propertyType = TypeEncoder.encodeWithAnnotations( returnType, getExcludedAnnotationTypes(propertyMethod)); String propertyName = methodToPropertyName.get(propertyMethod); String identifier = methodToIdentifier.get(propertyMethod); ImmutableList fieldAnnotations = annotationStrings(annotatedPropertyFields.get(propertyMethod)); ImmutableList methodAnnotationMirrors = annotatedPropertyMethods.get(propertyMethod); ImmutableList methodAnnotations = annotationStrings(methodAnnotationMirrors); Optional nullableAnnotation = nullableAnnotationForMethod(propertyMethod); Property p = new Property( propertyName, identifier, propertyMethod, propertyType, fieldAnnotations, methodAnnotations, nullableAnnotation); props.add(p); if (p.isNullable() && returnType.getKind().isPrimitive()) { errorReporter().reportError("Primitive types cannot be @Nullable", propertyMethod); } }); return props.build(); } /** Defines the template variables that are shared by AutoValue and AutoOneOf. */ final void defineSharedVarsForType( TypeElement type, ImmutableSet methods, AutoValueOrOneOfTemplateVars vars) { vars.pkg = TypeSimplifier.packageNameOf(type); vars.origClass = TypeSimplifier.classNameOf(type); vars.simpleClassName = TypeSimplifier.simpleNameOf(vars.origClass); vars.generated = generatedAnnotation(elementUtils(), processingEnv.getSourceVersion()) .map(annotation -> TypeEncoder.encode(annotation.asType())) .orElse(""); vars.formalTypes = TypeEncoder.formalTypeParametersString(type); vars.actualTypes = TypeSimplifier.actualTypeParametersString(type); vars.wildcardTypes = wildcardTypeParametersString(type); vars.annotations = copiedClassAnnotations(type); Map methodsToGenerate = determineObjectMethodsToGenerate(methods); vars.toString = methodsToGenerate.containsKey(ObjectMethod.TO_STRING); vars.equals = methodsToGenerate.containsKey(ObjectMethod.EQUALS); vars.hashCode = methodsToGenerate.containsKey(ObjectMethod.HASH_CODE); vars.equalsParameterType = equalsParameterType(methodsToGenerate); } /** Returns the spelling to be used in the generated code for the given list of annotations. */ static ImmutableList annotationStrings(List annotations) { // TODO(b/68008628): use ImmutableList.toImmutableList() when that works. return ImmutableList.copyOf( annotations.stream().map(AnnotationOutput::sourceFormForAnnotation).collect(toList())); } /** * Returns the name of the generated {@code @AutoValue} or {@code @AutoOneOf} class, for example * {@code AutoOneOf_TaskResult} or {@code $$AutoValue_SimpleMethod}. * * @param type the name of the type bearing the {@code @AutoValue} or {@code @AutoOneOf} * annotation. * @param prefix the prefix to use in the generated class. This may start with one or more dollar * signs, for an {@code @AutoValue} implementation where there are AutoValue extensions. */ static String generatedClassName(TypeElement type, String prefix) { String name = type.getSimpleName().toString(); while (type.getEnclosingElement() instanceof TypeElement) { type = (TypeElement) type.getEnclosingElement(); name = type.getSimpleName() + "_" + name; } String pkg = TypeSimplifier.packageNameOf(type); String dot = pkg.isEmpty() ? "" : "."; return pkg + dot + prefix + name; } private static boolean isJavaLangObject(TypeElement type) { return type.getSuperclass().getKind() == TypeKind.NONE && type.getKind() == ElementKind.CLASS; } enum ObjectMethod { NONE, TO_STRING, EQUALS, HASH_CODE } /** * Determines which of the three public non-final methods from {@code java.lang.Object}, if any, * is overridden by the given method. */ static ObjectMethod objectMethodToOverride(ExecutableElement method) { String name = method.getSimpleName().toString(); switch (method.getParameters().size()) { case 0: if (name.equals("toString")) { return ObjectMethod.TO_STRING; } else if (name.equals("hashCode")) { return ObjectMethod.HASH_CODE; } break; case 1: if (name.equals("equals")) { TypeMirror param = getOnlyElement(method.getParameters()).asType(); if (param.getKind().equals(TypeKind.DECLARED)) { TypeElement paramType = MoreTypes.asTypeElement(param); if (paramType.getQualifiedName().contentEquals("java.lang.Object")) { return ObjectMethod.EQUALS; } } } break; default: // No relevant Object methods have more than one parameter. } return ObjectMethod.NONE; } /** Returns a bi-map between property names and the corresponding abstract property methods. */ final ImmutableBiMap propertyNameToMethodMap( Set propertyMethods) { Map map = new LinkedHashMap<>(); Set reportedDups = new HashSet<>(); boolean allPrefixed = gettersAllPrefixed(propertyMethods); for (ExecutableElement method : propertyMethods) { String methodName = method.getSimpleName().toString(); String name = allPrefixed ? nameWithoutPrefix(methodName) : methodName; ExecutableElement old = map.put(name, method); if (old != null) { String message = "More than one @" + simpleAnnotationName + " property called " + name; errorReporter.reportError(message, method); if (reportedDups.add(name)) { errorReporter.reportError(message, old); } } } return ImmutableBiMap.copyOf(map); } private static boolean gettersAllPrefixed(Set methods) { return prefixedGettersIn(methods).size() == methods.size(); } /** * Returns an appropriate annotation spelling to indicate the nullability of an element. If the * return value is a non-empty Optional, that indicates that the element is nullable, and the * string should be used to annotate it. If the return value is an empty Optional, the element is * not nullable. The return value can be {@code Optional.of("")}, which indicates that the element * is nullable but that the nullability comes from a type annotation. In this case, the annotation * will appear when the type is written, and must not be specified again. If the Optional contains * a present non-empty string then that string will end with a space. * * @param element the element that might be {@code @Nullable}, either a method or a parameter. * @param elementType the relevant type of the element: the return type for a method, or the * parameter type for a parameter. */ static Optional nullableAnnotationFor(Element element, TypeMirror elementType) { List typeAnnotations = elementType.getAnnotationMirrors(); if (nullableAnnotationIndex(typeAnnotations).isPresent()) { return Optional.of(""); } List elementAnnotations = element.getAnnotationMirrors(); OptionalInt nullableAnnotationIndex = nullableAnnotationIndex(elementAnnotations); if (nullableAnnotationIndex.isPresent()) { ImmutableList annotations = annotationStrings(elementAnnotations); return Optional.of(annotations.get(nullableAnnotationIndex.getAsInt()) + " "); } else { return Optional.empty(); } } private static OptionalInt nullableAnnotationIndex(List annotations) { for (int i = 0; i < annotations.size(); i++) { if (isNullable(annotations.get(i))) { return OptionalInt.of(i); } } return OptionalInt.empty(); } private static boolean isNullable(AnnotationMirror annotation) { return annotation.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable"); } /** * Returns the subset of the given zero-arg methods whose names begin with {@code get}. Also * includes {@code isFoo} methods if they return {@code boolean}. This corresponds to JavaBeans * conventions. */ static ImmutableSet prefixedGettersIn(Iterable methods) { ImmutableSet.Builder getters = ImmutableSet.builder(); for (ExecutableElement method : methods) { String name = method.getSimpleName().toString(); // Note that getfoo() (without a capital) is still a getter. boolean get = name.startsWith("get") && !name.equals("get"); boolean is = name.startsWith("is") && !name.equals("is") && method.getReturnType().getKind() == TypeKind.BOOLEAN; if (get || is) { getters.add(method); } } return getters.build(); } /** * Returns the name of the property defined by the given getter. A getter called {@code getFoo()} * or {@code isFoo()} defines a property called {@code foo}. For consistency with JavaBeans, a * getter called {@code getHTMLPage()} defines a property called {@code HTMLPage}. The * rule is: the name of the property is the part after {@code get} or {@code is}, with the * first letter lowercased unless the first two letters are uppercase. This works well for * the {@code HTMLPage} example, but in these more enlightened times we use {@code HtmlPage} * anyway, so the special behaviour is not useful, and of course it behaves poorly with examples * like {@code OAuth}. */ private static String nameWithoutPrefix(String name) { if (name.startsWith("get")) { name = name.substring(3); } else { assert name.startsWith("is"); name = name.substring(2); } return PropertyNames.decapitalizeLikeJavaBeans(name); } /** * Checks that, if the given {@code @AutoValue} or {@code @AutoOneOf} class is nested, it is * static and not private. This check is not necessary for correctness, since the generated code * would not compile if the check fails, but it produces better error messages for the user. */ final void checkModifiersIfNested(TypeElement type) { ElementKind enclosingKind = type.getEnclosingElement().getKind(); if (enclosingKind.isClass() || enclosingKind.isInterface()) { if (type.getModifiers().contains(Modifier.PRIVATE)) { errorReporter.abortWithError( "@" + simpleAnnotationName + " class must not be private", type); } else if (Visibility.effectiveVisibilityOfElement(type).equals(Visibility.PRIVATE)) { // The previous case, where the class itself is private, is much commoner so it deserves // its own error message, even though it would be caught by the test here too. errorReporter.abortWithError( "@" + simpleAnnotationName + " class must not be nested in a private class", type); } if (!type.getModifiers().contains(Modifier.STATIC)) { errorReporter.abortWithError( "Nested @" + simpleAnnotationName + " class must be static", type); } } // In principle type.getEnclosingElement() could be an ExecutableElement (for a class // declared inside a method), but since RoundEnvironment.getElementsAnnotatedWith doesn't // return such classes we won't see them here. } /** * Modifies the values of the given map to avoid reserved words. If we have a getter called {@code * getPackage()} then we can't use the identifier {@code package} to represent its value since * that's a reserved word. */ static void fixReservedIdentifiers(Map methodToIdentifier) { for (Map.Entry entry : methodToIdentifier.entrySet()) { String name = entry.getValue(); if (SourceVersion.isKeyword(name) || !Character.isJavaIdentifierStart(name.codePointAt(0))) { entry.setValue(disambiguate(name, methodToIdentifier.values())); } } } private static String disambiguate(String name, Collection existingNames) { if (!Character.isJavaIdentifierStart(name.codePointAt(0))) { // You've defined a getter called get1st(). What were you thinking? name = "_" + name; if (!existingNames.contains(name)) { return name; } } for (int i = 0; ; i++) { String candidate = name + i; if (!existingNames.contains(candidate)) { return candidate; } } } /** * Given a list of all methods defined in or inherited by a class, returns a map indicating which * of equals, hashCode, and toString should be generated. Each value in the map is the method that * will be overridden by the generated method, which might be a method in {@code Object} or an * abstract method in the {@code @AutoValue} class or an ancestor. */ private static Map determineObjectMethodsToGenerate( Set methods) { Map methodsToGenerate = new EnumMap<>(ObjectMethod.class); for (ExecutableElement method : methods) { ObjectMethod override = objectMethodToOverride(method); boolean canGenerate = method.getModifiers().contains(Modifier.ABSTRACT) || isJavaLangObject((TypeElement) method.getEnclosingElement()); if (!override.equals(ObjectMethod.NONE) && canGenerate) { methodsToGenerate.put(override, method); } } return methodsToGenerate; } /** * Returns the encoded parameter type of the {@code equals(Object)} method that is to be * generated, or an empty string if the method is not being generated. The parameter type includes * any type annotations, for example {@code @Nullable}. */ static String equalsParameterType(Map methodsToGenerate) { ExecutableElement equals = methodsToGenerate.get(ObjectMethod.EQUALS); if (equals == null) { return ""; // this will not be referenced because no equals method will be generated } TypeMirror parameterType = equals.getParameters().get(0).asType(); return TypeEncoder.encodeWithAnnotations(parameterType); } /** * Returns the subset of all abstract methods in the given set of methods. A given method * signature is only mentioned once, even if it is inherited on more than one path. */ static ImmutableSet abstractMethodsIn( ImmutableSet methods) { Set noArgMethods = new HashSet<>(); ImmutableSet.Builder abstracts = ImmutableSet.builder(); for (ExecutableElement method : methods) { if (method.getModifiers().contains(Modifier.ABSTRACT)) { boolean hasArgs = !method.getParameters().isEmpty(); if (hasArgs || noArgMethods.add(method.getSimpleName())) { // If an abstract method with the same signature is inherited on more than one path, // we only add it once. At the moment we only do this check for no-arg methods. All // methods that AutoValue will implement are either no-arg methods or equals(Object). // The former is covered by this check and the latter will lead to vars.equals being // set to true, regardless of how many times it appears. So the only case that is // covered imperfectly here is that of a method that is inherited on more than one path // and that will be consumed by an extension. We could check parameters as well, but that // can be a bit tricky if any of the parameters are generic. abstracts.add(method); } } } return abstracts.build(); } /** * Returns the subset of property methods in the given set of abstract methods, with their actual * return types. A property method has no arguments, is not void, and is not {@code hashCode()} or * {@code toString()}. */ ImmutableMap propertyMethodsIn( Set abstractMethods, TypeElement autoValueOrOneOfType) { DeclaredType declaredType = MoreTypes.asDeclared(autoValueOrOneOfType.asType()); ImmutableSet.Builder properties = ImmutableSet.builder(); for (ExecutableElement method : abstractMethods) { if (method.getParameters().isEmpty() && (method.getReturnType().getKind() != TypeKind.VOID || propertiesCanBeVoid()) && objectMethodToOverride(method) == ObjectMethod.NONE) { properties.add(method); } } return new EclipseHack(processingEnv).methodReturnTypes(properties.build(), declaredType); } /** True if void properties are allowed. */ boolean propertiesCanBeVoid() { return false; } /** * Checks that the return type of the given property method is allowed. Currently, this means that * it cannot be an array, unless it is a primitive array. */ final void checkReturnType(TypeElement autoValueClass, ExecutableElement getter) { TypeMirror type = getter.getReturnType(); if (type.getKind() == TypeKind.ARRAY) { TypeMirror componentType = ((ArrayType) type).getComponentType(); if (componentType.getKind().isPrimitive()) { warnAboutPrimitiveArrays(autoValueClass, getter); } else { errorReporter.reportError( "An @" + simpleAnnotationName + " class cannot define an array-valued property unless it is a primitive array", getter); } } } private void warnAboutPrimitiveArrays(TypeElement autoValueClass, ExecutableElement getter) { boolean suppressed = false; Optional maybeAnnotation = getAnnotationMirror(getter, "java.lang.SuppressWarnings"); if (maybeAnnotation.isPresent()) { AnnotationValue listValue = getAnnotationValue(maybeAnnotation.get(), "value"); suppressed = listValue.accept(new ContainsMutableVisitor(), null); } if (!suppressed) { // If the primitive-array property method is defined directly inside the @AutoValue class, // then our error message should point directly to it. But if it is inherited, we don't // want to try to make the error message point to the inherited definition, since that would // be confusing (there is nothing wrong with the definition itself), and won't work if the // inherited class is not being recompiled. Instead, in this case we point to the @AutoValue // class itself, and we include extra text in the error message that shows the full name of // the inherited method. String warning = "An @" + simpleAnnotationName + " property that is a primitive array returns the original array, which can" + " therefore be modified by the caller. If this is OK, you can suppress this warning" + " with @SuppressWarnings(\"mutable\"). Otherwise, you should replace the property" + " with an immutable type, perhaps a simple wrapper around the original array."; boolean sameClass = getter.getEnclosingElement().equals(autoValueClass); if (sameClass) { errorReporter.reportWarning(warning, getter); } else { errorReporter.reportWarning( warning + " Method: " + getter.getEnclosingElement() + "." + getter, autoValueClass); } } } // Detects whether the visited AnnotationValue is an array that contains the string "mutable". // The simpler approach using Element.getAnnotation(SuppressWarnings.class) doesn't work if // the annotation has an undefined reference, like @SuppressWarnings(UNDEFINED). // TODO(emcmanus): replace with a method from auto-common when that is available. private static class ContainsMutableVisitor extends SimpleAnnotationValueVisitor8 { @Override public Boolean visitArray(List list, Void p) { return list.stream().map(av -> av.getValue()).anyMatch("mutable"::equals); } } /** * Returns a string like {@code "1234L"} if {@code type instanceof Serializable} and defines * {@code serialVersionUID = 1234L}; otherwise {@code ""}. */ final String getSerialVersionUID(TypeElement type) { TypeMirror serializable = elementUtils().getTypeElement(Serializable.class.getName()).asType(); if (typeUtils().isAssignable(type.asType(), serializable)) { List fields = ElementFilter.fieldsIn(type.getEnclosedElements()); for (VariableElement field : fields) { if (field.getSimpleName().contentEquals("serialVersionUID")) { Object value = field.getConstantValue(); if (field.getModifiers().containsAll(Arrays.asList(Modifier.STATIC, Modifier.FINAL)) && field.asType().getKind() == TypeKind.LONG && value != null) { return value + "L"; } else { errorReporter.reportError( "serialVersionUID must be a static final long compile-time constant", field); break; } } } } return ""; } /** Implements the semantics of {@code AutoValue.CopyAnnotations}; see its javadoc. */ ImmutableList annotationsToCopy( Element autoValueType, Element typeOrMethod, Set excludedAnnotations) { ImmutableList.Builder result = ImmutableList.builder(); for (AnnotationMirror annotation : typeOrMethod.getAnnotationMirrors()) { String annotationFqName = getAnnotationFqName(annotation); // To be included, the annotation should not be in com.google.auto.value, // and it should not be in the excludedAnnotations set. if (!isInAutoValuePackage(annotationFqName) && !excludedAnnotations.contains(annotationFqName) && annotationVisibleFrom(annotation, autoValueType)) { result.add(annotation); } } return result.build(); } /** * True if the given class name is in the com.google.auto.value package or a subpackage. False if * the class name contains {@code Test}, since many AutoValue tests under com.google.auto.value * define their own annotations. */ private boolean isInAutoValuePackage(String className) { return className.startsWith(AUTO_VALUE_PACKAGE_NAME) && !className.contains("Test"); } private ImmutableList copiedClassAnnotations(TypeElement type) { // Only copy annotations from a class if it has @AutoValue.CopyAnnotations. if (hasAnnotationMirror(type, COPY_ANNOTATIONS_NAME)) { Set excludedAnnotations = union(getExcludedAnnotationClassNames(type), getAnnotationsMarkedWithInherited(type)); return copyAnnotations(type, type, excludedAnnotations); } else { return ImmutableList.of(); } } /** Implements the semantics of {@code AutoValue.CopyAnnotations}; see its javadoc. */ private ImmutableList copyAnnotations( Element autoValueType, Element typeOrMethod, Set excludedAnnotations) { ImmutableList annotationsToCopy = annotationsToCopy(autoValueType, typeOrMethod, excludedAnnotations); return annotationStrings(annotationsToCopy); } /** * Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of * {@code TypeMirror} where each type is an annotation type. */ private Set getExcludedAnnotationTypes(Element element) { Optional maybeAnnotation = getAnnotationMirror(element, COPY_ANNOTATIONS_NAME); if (!maybeAnnotation.isPresent()) { return ImmutableSet.of(); } @SuppressWarnings("unchecked") List excludedClasses = (List) getAnnotationValue(maybeAnnotation.get(), "exclude").getValue(); return excludedClasses .stream() .map(annotationValue -> (DeclaredType) annotationValue.getValue()) .collect(toCollection(TypeMirrorSet::new)); } /** * Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of * strings that are fully-qualified class names. */ private Set getExcludedAnnotationClassNames(Element element) { return getExcludedAnnotationTypes(element) .stream() .map(MoreTypes::asTypeElement) .map(typeElement -> typeElement.getQualifiedName().toString()) .collect(toSet()); } private static Set getAnnotationsMarkedWithInherited(Element element) { return element .getAnnotationMirrors() .stream() .filter(a -> isAnnotationPresent(a.getAnnotationType().asElement(), Inherited.class)) .map(a -> getAnnotationFqName(a)) .collect(toSet()); } /** * Returns the fully-qualified name of an annotation-mirror, e.g. * "com.google.auto.value.AutoValue". */ private static String getAnnotationFqName(AnnotationMirror annotation) { return ((QualifiedNameable) annotation.getAnnotationType().asElement()) .getQualifiedName() .toString(); } final ImmutableListMultimap propertyMethodAnnotationMap( TypeElement type, ImmutableSet propertyMethods) { ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (ExecutableElement propertyMethod : propertyMethods) { builder.putAll(propertyMethod, propertyMethodAnnotations(type, propertyMethod)); } return builder.build(); } private ImmutableList propertyMethodAnnotations( TypeElement type, ExecutableElement method) { ImmutableSet excludedAnnotations = ImmutableSet.builder() .addAll(getExcludedAnnotationClassNames(method)) .add(Override.class.getCanonicalName()) .build(); // We need to exclude type annotations from the ones being output on the method, since // they will be output as part of the method's return type. Set returnTypeAnnotations = getReturnTypeAnnotations(method, a -> true); Set excluded = union(excludedAnnotations, returnTypeAnnotations); return annotationsToCopy(type, method, excluded); } final ImmutableListMultimap propertyFieldAnnotationMap( TypeElement type, ImmutableSet propertyMethods) { ImmutableListMultimap.Builder builder = ImmutableListMultimap.builder(); for (ExecutableElement propertyMethod : propertyMethods) { builder.putAll(propertyMethod, propertyFieldAnnotations(type, propertyMethod)); } return builder.build(); } private ImmutableList propertyFieldAnnotations( TypeElement type, ExecutableElement method) { if (!hasAnnotationMirror(method, COPY_ANNOTATIONS_NAME)) { return ImmutableList.of(); } ImmutableSet excludedAnnotations = ImmutableSet.builder() .addAll(getExcludedAnnotationClassNames(method)) .add(Override.class.getCanonicalName()) .build(); // We need to exclude type annotations from the ones being output on the method, since // they will be output as part of the field's type. Set returnTypeAnnotations = getReturnTypeAnnotations(method, this::annotationAppliesToFields); Set nonFieldAnnotations = method .getAnnotationMirrors() .stream() .map(a -> a.getAnnotationType().asElement()) .map(MoreElements::asType) .filter(a -> !annotationAppliesToFields(a)) .map(e -> e.getQualifiedName().toString()) .collect(toSet()); Set excluded = ImmutableSet.builder() .addAll(excludedAnnotations) .addAll(returnTypeAnnotations) .addAll(nonFieldAnnotations) .build(); return annotationsToCopy(type, method, excluded); } private Set getReturnTypeAnnotations( ExecutableElement method, Predicate typeFilter) { return method .getReturnType() .getAnnotationMirrors() .stream() .map(a -> a.getAnnotationType().asElement()) .map(MoreElements::asType) .filter(typeFilter) .map(e -> e.getQualifiedName().toString()) .collect(toSet()); } private boolean annotationAppliesToFields(TypeElement annotation) { Target target = annotation.getAnnotation(Target.class); return target == null || Arrays.asList(target.value()).contains(ElementType.FIELD); } private boolean annotationVisibleFrom(AnnotationMirror annotation, Element from) { Element annotationElement = annotation.getAnnotationType().asElement(); Visibility visibility = Visibility.effectiveVisibilityOfElement(annotationElement); switch (visibility) { case PUBLIC: return true; case PROTECTED: // If the annotation is protected, it must be inside another class, call it C. If our // @AutoValue class is Foo then, for the annotation to be visible, either Foo must be in the // same package as C or Foo must be a subclass of C. If the annotation is visible from Foo // then it is also visible from our generated subclass AutoValue_Foo. // The protected case only applies to method annotations. An annotation on the AutoValue_Foo // class itself can't be protected, even if AutoValue_Foo ultimately inherits from the // class that defines the annotation. The JLS says "Access is permitted only within the // body of a subclass": // https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.2.1 // AutoValue_Foo is a top-level class, so an annotation on it cannot be in the body of a // subclass of anything. return getPackage(annotationElement).equals(getPackage(from)) || typeUtils() .isSubtype(from.asType(), annotationElement.getEnclosingElement().asType()); case DEFAULT: return getPackage(annotationElement).equals(getPackage(from)); default: return false; } } /** * Returns the {@code @AutoValue} or {@code @AutoOneOf} type parameters, with a ? for every type. * If we have {@code @AutoValue abstract class Foo} then this method will * return just {@code }. */ private static String wildcardTypeParametersString(TypeElement type) { List typeParameters = type.getTypeParameters(); if (typeParameters.isEmpty()) { return ""; } else { return typeParameters.stream().map(e -> "?").collect(joining(", ", "<", ">")); } } // TODO(emcmanus,ronshapiro): move to auto-common static Optional getAnnotationMirror(Element element, String annotationName) { for (AnnotationMirror annotation : element.getAnnotationMirrors()) { TypeElement annotationElement = MoreTypes.asTypeElement(annotation.getAnnotationType()); if (annotationElement.getQualifiedName().contentEquals(annotationName)) { return Optional.of(annotation); } } return Optional.empty(); } static boolean hasAnnotationMirror(Element element, String annotationName) { return getAnnotationMirror(element, annotationName).isPresent(); } final void writeSourceFile(String className, String text, TypeElement originatingType) { try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(className, originatingType); try (Writer writer = sourceFile.openWriter()) { writer.write(text); } } catch (IOException e) { // This should really be an error, but we make it a warning in the hope of resisting Eclipse // bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=367599. If that bug manifests, we may get // invoked more than once for the same file, so ignoring the ability to overwrite it is the // right thing to do. If we are unable to write for some other reason, we should get a compile // error later because user code will have a reference to the code we were supposed to // generate (new AutoValue_Foo() or whatever) and that reference will be undefined. errorReporter.reportWarning( "Could not write generated class " + className + ": " + e, originatingType); } } } AutoValueOrOneOfTemplateVars.java000066400000000000000000000055241365703632600356160ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import com.google.common.collect.ImmutableList; /** * The variables to substitute into the autovalue.vm or autooneof.vm template. * * @author emcmanus@google.com (Éamonn McManus) */ @SuppressWarnings("unused") // the fields in this class are only read via reflection abstract class AutoValueOrOneOfTemplateVars extends TemplateVars { /** Whether to generate an equals(Object) method. */ Boolean equals; /** Whether to generate a hashCode() method. */ Boolean hashCode; /** Whether to generate a toString() method. */ Boolean toString; /** * A string representing the parameter type declaration of the equals(Object) method, including * any annotations. If {@link #equals} is false, this field is ignored (but it must still be * non-null). */ String equalsParameterType; /** The encoding of the {@code Generated} class. Empty if the class is not available. */ String generated; /** The package of the class with the {@code @AutoValue} annotation and its generated subclass. */ String pkg; /** * The name of the class with the {@code @AutoValue} annotation, including containing classes but * not including the package name. */ String origClass; /** The simple name of the class with the {@code @AutoValue} annotation. */ String simpleClassName; /** * The full spelling of any annotation to add to this class, or an empty list if there are none. A * non-empty value might look something like {@code * "@com.google.common.annotations.GwtCompatible(serializable = true)"}. */ ImmutableList annotations; /** * The formal generic signature of the class with the {@code @AutoValue} or {@code AutoOneOf} * annotation and any generated subclass. This is empty, or contains type variables with optional * bounds, for example {@code }. */ String formalTypes; /** * The generic signature used by any generated subclass for its superclass reference. This is * empty, or contains only type variables with no bounds, for example {@code }. */ String actualTypes; /** * The generic signature in {@link #actualTypes} where every variable has been replaced by a * wildcard, for example {@code }. */ String wildcardTypes; } AutoValueProcessor.java000066400000000000000000000534411365703632600337370ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2012 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_NAME; import static com.google.common.collect.Sets.difference; import static com.google.common.collect.Sets.intersection; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import com.google.auto.service.AutoService; import com.google.auto.value.extension.AutoValueExtension; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.ServiceConfigurationError; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; /** * Javac annotation processor (compiler plugin) for value types; user code never references this * class. * * @see AutoValue User's Guide * @author Éamonn McManus */ @AutoService(Processor.class) @SupportedAnnotationTypes(AUTO_VALUE_NAME) @IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.DYNAMIC) public class AutoValueProcessor extends AutoValueOrOneOfProcessor { private static final String OMIT_IDENTIFIERS_OPTION = "com.google.auto.value.OmitIdentifiers"; // We moved MemoizeExtension to a different package, which had an unexpected effect: // now if an old version of AutoValue is in the class path, ServiceLoader can pick up both the // old and the new versions of MemoizeExtension. So we exclude the old version if we see it. // The new version will be bundled with this processor so we should always find it. private static final String OLD_MEMOIZE_EXTENSION = "com.google.auto.value.extension.memoized.MemoizeExtension"; public AutoValueProcessor() { this(AutoValueProcessor.class.getClassLoader()); } @VisibleForTesting AutoValueProcessor(ClassLoader loaderForExtensions) { super(AUTO_VALUE_NAME); this.extensions = null; this.loaderForExtensions = loaderForExtensions; } @VisibleForTesting public AutoValueProcessor(Iterable extensions) { super(AUTO_VALUE_NAME); this.extensions = ImmutableList.copyOf(extensions); this.loaderForExtensions = null; } // Depending on how this AutoValueProcessor was constructed, we might already have a list of // extensions when init() is run, or, if `extensions` is null, we have a ClassLoader that will be // used to get the list using the ServiceLoader API. private ImmutableList extensions; private final ClassLoader loaderForExtensions; @VisibleForTesting static ImmutableList extensionsFromLoader(ClassLoader loader) { return ImmutableList.copyOf( Iterables.filter( SimpleServiceLoader.load(AutoValueExtension.class, loader), ext -> !ext.getClass().getName().equals(OLD_MEMOIZE_EXTENSION))); } @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); if (extensions == null) { try { extensions = extensionsFromLoader(loaderForExtensions); } catch (RuntimeException | Error e) { StringBuilder warning = new StringBuilder(); warning.append( "An exception occurred while looking for AutoValue extensions." + " No extensions will function."); if (e instanceof ServiceConfigurationError) { warning.append(" This may be due to a corrupt jar file in the compiler's classpath."); } warning.append("\n").append(Throwables.getStackTraceAsString(e)); errorReporter().reportWarning(warning.toString(), null); extensions = ImmutableList.of(); } } } @Override public Set getSupportedOptions() { ImmutableSet.Builder builder = ImmutableSet.builder(); AutoValueExtension.IncrementalExtensionType incrementalType = extensions.stream() .map(e -> e.incrementalType(processingEnv)) .min(Comparator.naturalOrder()) .orElse(AutoValueExtension.IncrementalExtensionType.ISOLATING); builder.add(OMIT_IDENTIFIERS_OPTION).addAll(optionsFor(incrementalType)); for (AutoValueExtension extension : extensions) { builder.addAll(extension.getSupportedOptions()); } return builder.build(); } private static ImmutableSet optionsFor( AutoValueExtension.IncrementalExtensionType incrementalType) { switch (incrementalType) { case ISOLATING: return ImmutableSet.of(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption()); case AGGREGATING: return ImmutableSet.of(IncrementalAnnotationProcessorType.AGGREGATING.getProcessorOption()); case UNKNOWN: return ImmutableSet.of(); } throw new AssertionError(incrementalType); } static String generatedSubclassName(TypeElement type, int depth) { return generatedClassName(type, Strings.repeat("$", depth) + "AutoValue_"); } @Override void processType(TypeElement type) { if (!hasAnnotationMirror(type, AUTO_VALUE_NAME)) { // This shouldn't happen unless the compilation environment is buggy, // but it has happened in the past and can crash the compiler. errorReporter() .abortWithError( "annotation processor for @AutoValue was invoked with a type" + " that does not have that annotation; this is probably a compiler bug", type); } if (type.getKind() != ElementKind.CLASS) { errorReporter().abortWithError("@AutoValue only applies to classes", type); } if (ancestorIsAutoValue(type)) { errorReporter().abortWithError("One @AutoValue class may not extend another", type); } if (implementsAnnotation(type)) { errorReporter() .abortWithError( "@AutoValue may not be used to implement an annotation" + " interface; try using @AutoAnnotation instead", type); } checkModifiersIfNested(type); // We are going to classify the methods of the @AutoValue class into several categories. // This covers the methods in the class itself and the ones it inherits from supertypes. // First, the only concrete (non-abstract) methods we are interested in are overrides of // Object methods (equals, hashCode, toString), which signal that we should not generate // an implementation of those methods. // Then, each abstract method is one of the following: // (1) A property getter, like "abstract String foo()" or "abstract String getFoo()". // (2) A toBuilder() method, which is any abstract no-arg method returning the Builder for // this @AutoValue class. // (3) An abstract method that will be consumed by an extension, such as // Parcelable.describeContents() or Parcelable.writeToParcel(Parcel, int). // The describeContents() example shows a quirk here: initially we will identify it as a // property, which means that we need to reconstruct the list of properties after allowing // extensions to consume abstract methods. // If there are abstract methods that don't fit any of the categories above, that is an error // which we signal explicitly to avoid confusion. ImmutableSet methods = getLocalAndInheritedMethods( type, processingEnv.getTypeUtils(), processingEnv.getElementUtils()); ImmutableSet abstractMethods = abstractMethodsIn(methods); BuilderSpec builderSpec = new BuilderSpec(type, processingEnv, errorReporter()); Optional builder = builderSpec.getBuilder(); ImmutableSet toBuilderMethods; if (builder.isPresent()) { toBuilderMethods = builder.get().toBuilderMethods(typeUtils(), abstractMethods); } else { toBuilderMethods = ImmutableSet.of(); } ImmutableMap propertyMethodsAndTypes = propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods), type); ImmutableMap properties = propertyNameToMethodMap(propertyMethodsAndTypes.keySet()); ExtensionContext context = new ExtensionContext( processingEnv, type, properties, propertyMethodsAndTypes, abstractMethods); ImmutableList applicableExtensions = applicableExtensions(type, context); ImmutableSet consumedMethods = methodsConsumedByExtensions( type, applicableExtensions, context, abstractMethods, properties); if (!consumedMethods.isEmpty()) { ImmutableSet allAbstractMethods = abstractMethods; abstractMethods = immutableSetDifference(abstractMethods, consumedMethods); toBuilderMethods = immutableSetDifference(toBuilderMethods, consumedMethods); propertyMethodsAndTypes = propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods), type); properties = propertyNameToMethodMap(propertyMethodsAndTypes.keySet()); context = new ExtensionContext( processingEnv, type, properties, propertyMethodsAndTypes, allAbstractMethods); } ImmutableSet propertyMethods = propertyMethodsAndTypes.keySet(); boolean extensionsPresent = !applicableExtensions.isEmpty(); validateMethods(type, abstractMethods, toBuilderMethods, propertyMethods, extensionsPresent); String finalSubclass = generatedSubclassName(type, 0); AutoValueTemplateVars vars = new AutoValueTemplateVars(); vars.finalSubclass = TypeSimplifier.simpleNameOf(finalSubclass); vars.types = processingEnv.getTypeUtils(); vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION); defineSharedVarsForType(type, methods, vars); defineVarsForType(type, vars, toBuilderMethods, propertyMethodsAndTypes, builder); // If we've encountered problems then we might end up invoking extensions with inconsistent // state. Anyway we probably don't want to generate code which is likely to provoke further // compile errors to add to the ones we've already seen. errorReporter().abortIfAnyError(); GwtCompatibility gwtCompatibility = new GwtCompatibility(type); vars.gwtCompatibleAnnotation = gwtCompatibility.gwtCompatibleAnnotationString(); builder.ifPresent(context::setBuilderContext); int subclassDepth = writeExtensions(type, context, applicableExtensions); String subclass = generatedSubclassName(type, subclassDepth); vars.subclass = TypeSimplifier.simpleNameOf(subclass); vars.isFinal = (subclassDepth == 0); vars.modifiers = vars.isFinal ? "final " : "abstract "; String text = vars.toText(); text = TypeEncoder.decode(text, processingEnv, vars.pkg, type.asType()); text = Reformatter.fixup(text); writeSourceFile(subclass, text, type); GwtSerialization gwtSerialization = new GwtSerialization(gwtCompatibility, processingEnv, type); gwtSerialization.maybeWriteGwtSerializer(vars); } // Invokes each of the given extensions to generate its subclass, and returns the number of // hierarchy classes that extensions generated. This number is then the number of $ characters // that should precede the name of the AutoValue implementation class. // Assume the @AutoValue class is com.example.Foo.Bar. Then if there are no // extensions the returned value will be 0, so the AutoValue implementation will be // com.example.AutoValue_Foo_Bar. If there is one extension, it will be asked to // generate AutoValue_Foo_Bar with parent $AutoValue_Foo_Bar. If it does so (returns // non-null) then the returned value will be 1, so the AutoValue implementation will be // com.example.$AutoValue_Foo_Bar. Otherwise, the returned value will still be 0. Likewise, // if there is a second extension and both extensions return non-null, the first one will // generate AutoValue_Foo_Bar with parent $AutoValue_Foo_Bar, the second will generate // $AutoValue_Foo_Bar with parent $$AutoValue_Foo_Bar, and the returned value will be 2 for // com.example.$$AutoValue_Foo_Bar. private int writeExtensions( TypeElement type, ExtensionContext context, ImmutableList applicableExtensions) { int writtenSoFar = 0; for (AutoValueExtension extension : applicableExtensions) { String parentFqName = generatedSubclassName(type, writtenSoFar + 1); String parentSimpleName = TypeSimplifier.simpleNameOf(parentFqName); String classFqName = generatedSubclassName(type, writtenSoFar); String classSimpleName = TypeSimplifier.simpleNameOf(classFqName); boolean isFinal = (writtenSoFar == 0); String source = extension.generateClass(context, classSimpleName, parentSimpleName, isFinal); if (source != null) { source = Reformatter.fixup(source); writeSourceFile(classFqName, source, type); writtenSoFar++; } } return writtenSoFar; } private ImmutableList applicableExtensions( TypeElement type, ExtensionContext context) { List applicableExtensions = new ArrayList<>(); List finalExtensions = new ArrayList<>(); for (AutoValueExtension extension : extensions) { if (extension.applicable(context)) { if (extension.mustBeFinal(context)) { finalExtensions.add(extension); } else { applicableExtensions.add(extension); } } } switch (finalExtensions.size()) { case 0: break; case 1: applicableExtensions.add(0, finalExtensions.get(0)); break; default: errorReporter() .reportError( "More than one extension wants to generate the final class: " + finalExtensions.stream().map(this::extensionName).collect(joining(", ")), type); break; } return ImmutableList.copyOf(applicableExtensions); } private ImmutableSet methodsConsumedByExtensions( TypeElement type, ImmutableList applicableExtensions, ExtensionContext context, ImmutableSet abstractMethods, ImmutableMap properties) { Set consumed = new HashSet<>(); for (AutoValueExtension extension : applicableExtensions) { Set consumedHere = new HashSet<>(); for (String consumedProperty : extension.consumeProperties(context)) { ExecutableElement propertyMethod = properties.get(consumedProperty); if (propertyMethod == null) { errorReporter() .reportError( "Extension " + extensionName(extension) + " wants to consume a property that does not exist: " + consumedProperty, type); } else { consumedHere.add(propertyMethod); } } for (ExecutableElement consumedMethod : extension.consumeMethods(context)) { if (!abstractMethods.contains(consumedMethod)) { errorReporter() .reportError( "Extension " + extensionName(extension) + " wants to consume a method that is not one of the abstract methods in this" + " class: " + consumedMethod, type); } else { consumedHere.add(consumedMethod); } } for (ExecutableElement repeat : intersection(consumed, consumedHere)) { errorReporter() .reportError( "Extension " + extensionName(extension) + " wants to consume a method that was already consumed by another extension", repeat); } consumed.addAll(consumedHere); } return ImmutableSet.copyOf(consumed); } private void validateMethods( TypeElement type, ImmutableSet abstractMethods, ImmutableSet toBuilderMethods, ImmutableSet propertyMethods, boolean extensionsPresent) { for (ExecutableElement method : abstractMethods) { if (propertyMethods.contains(method)) { checkReturnType(type, method); } else if (!toBuilderMethods.contains(method) && objectMethodToOverride(method) == ObjectMethod.NONE) { // This could reasonably be an error, were it not for an Eclipse bug in // ElementUtils.override that sometimes fails to recognize that one method overrides // another, and therefore leaves us with both an abstract method and the subclass method // that overrides it. This shows up in AutoValueTest.LukesBase for example. String message = "Abstract method is neither a property getter nor a Builder converter"; if (extensionsPresent) { message += ", and no extension consumed it"; } errorReporter().reportWarning(message, method); } } errorReporter().abortIfAnyError(); } private String extensionName(AutoValueExtension extension) { return extension.getClass().getName(); } private void defineVarsForType( TypeElement type, AutoValueTemplateVars vars, ImmutableSet toBuilderMethods, ImmutableMap propertyMethodsAndTypes, Optional builder) { ImmutableSet propertyMethods = propertyMethodsAndTypes.keySet(); // We can't use ImmutableList.toImmutableList() for obscure Google-internal reasons. vars.toBuilderMethods = ImmutableList.copyOf(toBuilderMethods.stream().map(SimpleMethod::new).collect(toList())); ImmutableListMultimap annotatedPropertyFields = propertyFieldAnnotationMap(type, propertyMethods); ImmutableListMultimap annotatedPropertyMethods = propertyMethodAnnotationMap(type, propertyMethods); vars.props = propertySet(propertyMethodsAndTypes, annotatedPropertyFields, annotatedPropertyMethods); vars.serialVersionUID = getSerialVersionUID(type); // Check for @AutoValue.Builder and add appropriate variables if it is present. if (builder.isPresent()) { ImmutableBiMap methodToPropertyName = propertyNameToMethodMap(propertyMethods).inverse(); builder.get().defineVars(vars, methodToPropertyName); } } @Override Optional nullableAnnotationForMethod(ExecutableElement propertyMethod) { return nullableAnnotationFor(propertyMethod, propertyMethod.getReturnType()); } static ImmutableSet prefixedGettersIn(Iterable methods) { ImmutableSet.Builder getters = ImmutableSet.builder(); for (ExecutableElement method : methods) { String name = method.getSimpleName().toString(); // TODO(emcmanus): decide whether getfoo() (without a capital) is a getter. Currently it is. boolean get = name.startsWith("get") && !name.equals("get"); boolean is = name.startsWith("is") && !name.equals("is") && method.getReturnType().getKind() == TypeKind.BOOLEAN; if (get || is) { getters.add(method); } } return getters.build(); } private boolean ancestorIsAutoValue(TypeElement type) { while (true) { TypeMirror parentMirror = type.getSuperclass(); if (parentMirror.getKind() == TypeKind.NONE) { return false; } TypeElement parentElement = (TypeElement) typeUtils().asElement(parentMirror); if (hasAnnotationMirror(parentElement, AUTO_VALUE_NAME)) { return true; } type = parentElement; } } private boolean implementsAnnotation(TypeElement type) { return typeUtils().isAssignable(type.asType(), getTypeMirror(Annotation.class)); } private TypeMirror getTypeMirror(Class c) { return processingEnv.getElementUtils().getTypeElement(c.getName()).asType(); } private static ImmutableSet immutableSetDifference(ImmutableSet a, ImmutableSet b) { if (Collections.disjoint(a, b)) { return a; } else { return ImmutableSet.copyOf(difference(a, b)); } } } AutoValueTemplateVars.java000066400000000000000000000137561365703632600343740ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2012 Google LLC * * 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.google.auto.value.processor; import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.escapevelocity.Template; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.util.Types; /** * The variables to substitute into the autovalue.vm template. * * @author emcmanus@google.com (Éamonn McManus) */ @SuppressWarnings("unused") // the fields in this class are only read via reflection class AutoValueTemplateVars extends AutoValueOrOneOfTemplateVars { /** * The properties defined by the parent class's abstract methods. The elements of this set are in * the same order as the original abstract method declarations in the AutoValue class. */ ImmutableSet props; /** * Whether to include identifiers in strings in the generated code. If false, exception messages * will not mention properties by name, and {@code toString()} will include neither property names * nor the name of the {@code @AutoValue} class. */ Boolean identifiers; /** The type utilities returned by {@link ProcessingEnvironment#getTypeUtils()}. */ Types types; /** * The encoding of the {@code @GwtCompatible} annotation to add to this class, or an empty string * if there is none. A non-empty value will look something like {@code * "@`com.google.common.annotations.GwtCompatible`(serializable = true)"}, where the {@code ``} * represent the encoding used by {@link TypeEncoder}. */ String gwtCompatibleAnnotation; /** The text of the serialVersionUID constant, or empty if there is none. */ String serialVersionUID; /** The simple name of the generated subclass. */ String subclass; /** * The simple name of the final generated subclass. For {@code @AutoValue public static class Foo * {}} this should always be "AutoValue_Foo". */ String finalSubclass; /** * True if the generated class should be final (there are no extensions that will generate * subclasses) */ Boolean isFinal = false; /** * The modifiers (for example {@code final} or {@code abstract}) for the generated subclass, * followed by a space if they are not empty. */ String modifiers; /** * The name of the builder type as it should appear in source code, or empty if there is no * builder type. If class {@code Address} contains {@code @AutoValue.Builder} class Builder then * this will typically be {@code "Address.Builder"}. */ String builderTypeName = ""; /** * The formal generic signature of the {@code AutoValue.Builder} class. This is empty, or contains * type variables with optional bounds, for example {@code }. */ String builderFormalTypes = ""; /** * The generic signature used by the generated builder subclass for its superclass reference. This * is empty, or contains only type variables with no bounds, for example {@code }. */ String builderActualTypes = ""; /** True if the builder being implemented is an interface, false if it is an abstract class. */ Boolean builderIsInterface = false; /** The builder's build method, often {@code "build"}. */ Optional buildMethod = Optional.empty(); /** * A multimap from property names (like foo) to the corresponding setters. The same property may * be set by more than one setter. For example, an ImmutableList might be set by {@code * setFoo(ImmutableList)} and {@code setFoo(String[])}. */ ImmutableMultimap builderSetters = ImmutableMultimap.of(); /** * A map from property names to information about the associated property builder. A property * called foo (defined by a method foo() or getFoo()) can have a property builder called * fooBuilder(). The type of foo must be a type that has an associated builder following certain * conventions. Guava immutable types such as ImmutableList follow those conventions, as do many * {@code @AutoValue} types. */ ImmutableMap builderPropertyBuilders = ImmutableMap.of(); /** * Properties that are required to be set. A property must be set explicitly except in the * following cases: * *

        *
      • it is {@code @Nullable} (in which case it defaults to null); *
      • it is {@code Optional} (in which case it defaults to empty); *
      • it has a property-builder method (in which case it defaults to empty). *
      */ ImmutableSet builderRequiredProperties = ImmutableSet.of(); /** * A map from property names to information about the associated property getter. A property * called foo (defined by a method foo() or getFoo()) can have a property getter method with the * same name (foo() or getFoo()) and either the same return type or an Optional (or OptionalInt, * etc) wrapping it. */ ImmutableMap builderGetters = ImmutableMap.of(); /** Any {@code toBuilder()} methods, that is methods that return the builder type. */ ImmutableList toBuilderMethods; private static final Template TEMPLATE = parsedTemplateForResource("autovalue.vm"); @Override Template parsedTemplate() { return TEMPLATE; } } BuilderMethodClassifier.java000066400000000000000000000743471365703632600346760ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.nullableAnnotationFor; import static com.google.common.collect.Sets.difference; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.value.processor.BuilderSpec.PropertySetter; import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder; import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.Multimap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; /** * Classifies methods inside builder types, based on their names and parameter and return types. * * @author Éamonn McManus */ class BuilderMethodClassifier { private static final Equivalence TYPE_EQUIVALENCE = MoreTypes.equivalence(); private final ErrorReporter errorReporter; private final Types typeUtils; private final Elements elementUtils; private final TypeElement autoValueClass; private final TypeElement builderType; private final ImmutableBiMap getterToPropertyName; private final ImmutableMap getterToPropertyType; private final ImmutableMap getterNameToGetter; private final Set buildMethods = new LinkedHashSet<>(); private final Map builderGetters = new LinkedHashMap<>(); private final Map propertyNameToPropertyBuilder = new LinkedHashMap<>(); private final Multimap propertyNameToPrefixedSetters = LinkedListMultimap.create(); private final Multimap propertyNameToUnprefixedSetters = LinkedListMultimap.create(); private final EclipseHack eclipseHack; private boolean settersPrefixed; private BuilderMethodClassifier( ErrorReporter errorReporter, ProcessingEnvironment processingEnv, TypeElement autoValueClass, TypeElement builderType, ImmutableBiMap getterToPropertyName, ImmutableMap getterToPropertyType) { this.errorReporter = errorReporter; this.typeUtils = processingEnv.getTypeUtils(); this.elementUtils = processingEnv.getElementUtils(); this.autoValueClass = autoValueClass; this.builderType = builderType; this.getterToPropertyName = getterToPropertyName; this.getterToPropertyType = getterToPropertyType; ImmutableMap.Builder getterToPropertyNameBuilder = ImmutableMap.builder(); for (ExecutableElement getter : getterToPropertyName.keySet()) { getterToPropertyNameBuilder.put(getter.getSimpleName().toString(), getter); } this.getterNameToGetter = getterToPropertyNameBuilder.build(); this.eclipseHack = new EclipseHack(processingEnv); } /** * Classifies the given methods from a builder type and its ancestors. * * @param methods the abstract methods in {@code builderType} and its ancestors. * @param errorReporter where to report errors. * @param processingEnv the ProcessingEnvironment for annotation processing. * @param autoValueClass the {@code AutoValue} class containing the builder. * @param builderType the builder class or interface within {@code autoValueClass}. * @param getterToPropertyName a map from getter methods to the properties they get. * @param getterToPropertyType a map from getter methods to their return types. The return types * here use type parameters from the builder class (if any) rather than from the {@code * AutoValue} class, even though the getter methods are in the latter. * @param autoValueHasToBuilder true if the containing {@code @AutoValue} class has a {@code * toBuilder()} method. * @return an {@code Optional} that contains the results of the classification if it was * successful or nothing if it was not. */ static Optional classify( Iterable methods, ErrorReporter errorReporter, ProcessingEnvironment processingEnv, TypeElement autoValueClass, TypeElement builderType, ImmutableBiMap getterToPropertyName, ImmutableMap getterToPropertyType, boolean autoValueHasToBuilder) { BuilderMethodClassifier classifier = new BuilderMethodClassifier( errorReporter, processingEnv, autoValueClass, builderType, getterToPropertyName, getterToPropertyType); if (classifier.classifyMethods(methods, autoValueHasToBuilder)) { return Optional.of(classifier); } else { return Optional.empty(); } } /** * Returns a multimap from the name of a property to the methods that set it. If the property is * defined by an abstract method in the {@code @AutoValue} class called {@code foo()} or {@code * getFoo()} then the name of the property is {@code foo} and there will be an entry in the map * where the key is {@code "foo"} and the value describes a method in the builder called * {@code foo} or {@code setFoo}. */ ImmutableMultimap propertyNameToSetters() { return ImmutableMultimap.copyOf( settersPrefixed ? propertyNameToPrefixedSetters : propertyNameToUnprefixedSetters); } Map propertyNameToPropertyBuilder() { return propertyNameToPropertyBuilder; } /** * Returns the set of properties that have getters in the builder. If a property is defined by an * abstract method in the {@code @AutoValue} class called {@code foo()} or {@code getFoo()} then * the name of the property is {@code foo}, If the builder also has a method of the same name * ({@code foo()} or {@code getFoo()}) then the set returned here will contain {@code foo}. */ ImmutableMap builderGetters() { return ImmutableMap.copyOf(builderGetters); } /** * Returns the methods that were identified as {@code build()} methods. These are methods that * have no parameters and return the {@code @AutoValue} type, conventionally called {@code * build()}. */ Set buildMethods() { return ImmutableSet.copyOf(buildMethods); } /** Classifies the given methods and sets the state of this object based on what is found. */ private boolean classifyMethods( Iterable methods, boolean autoValueHasToBuilder) { int startErrorCount = errorReporter.errorCount(); for (ExecutableElement method : methods) { classifyMethod(method); } if (errorReporter.errorCount() > startErrorCount) { return false; } Multimap propertyNameToSetter; if (propertyNameToPrefixedSetters.isEmpty()) { propertyNameToSetter = propertyNameToUnprefixedSetters; this.settersPrefixed = false; } else if (propertyNameToUnprefixedSetters.isEmpty()) { propertyNameToSetter = propertyNameToPrefixedSetters; this.settersPrefixed = true; } else { errorReporter.reportError( "If any setter methods use the setFoo convention then all must", propertyNameToUnprefixedSetters.values().iterator().next().getSetter()); return false; } getterToPropertyName.forEach( (getter, property) -> { TypeMirror propertyType = getterToPropertyType.get(getter); boolean hasSetter = propertyNameToSetter.containsKey(property); PropertyBuilder propertyBuilder = propertyNameToPropertyBuilder.get(property); boolean hasBuilder = propertyBuilder != null; if (hasBuilder) { // If property bar of type Bar has a barBuilder() that returns BarBuilder, then it must // be possible to make a BarBuilder from a Bar if either (1) the @AutoValue class has a // toBuilder() or (2) there is also a setBar(Bar). Making BarBuilder from Bar is // possible if Bar either has a toBuilder() method or BarBuilder has an addAll or putAll // method that accepts a Bar argument. boolean canMakeBarBuilder = (propertyBuilder.getBuiltToBuilder() != null || propertyBuilder.getCopyAll() != null); boolean needToMakeBarBuilder = (autoValueHasToBuilder || hasSetter); if (needToMakeBarBuilder && !canMakeBarBuilder) { String error = String.format( "Property builder method returns %1$s but there is no way to make that type" + " from %2$s: %2$s does not have a non-static toBuilder() method that" + " returns %1$s, and %1$s does not have a method addAll or" + " putAll that accepts an argument of type %2$s", propertyBuilder.getBuilderTypeMirror(), propertyType); errorReporter.reportError(error, propertyBuilder.getPropertyBuilderMethod()); } } else if (!hasSetter) { // We have neither barBuilder() nor setBar(Bar), so we should complain. String setterName = settersPrefixed ? prefixWithSet(property) : property; String error = String.format( "Expected a method with this signature: %s%s %s(%s), or a %sBuilder() method", builderType, typeParamsString(), setterName, propertyType, property); errorReporter.reportError(error, builderType); } }); return errorReporter.errorCount() == startErrorCount; } /** Classifies a method and update the state of this object based on what is found. */ private void classifyMethod(ExecutableElement method) { switch (method.getParameters().size()) { case 0: classifyMethodNoArgs(method); break; case 1: classifyMethodOneArg(method); break; default: errorReporter.reportError("Builder methods must have 0 or 1 parameters", method); } } /** * Classifies a method given that it has no arguments. Currently a method with no arguments can be * a {@code build()} method, meaning that its return type must be the {@code @AutoValue} class; it * can be a getter, with the same signature as one of the property getters in the * {@code @AutoValue} class; or it can be a property builder, like {@code * ImmutableList.Builder foosBuilder()} for the property defined by {@code * ImmutableList foos()} or {@code getFoos()}. */ private void classifyMethodNoArgs(ExecutableElement method) { String methodName = method.getSimpleName().toString(); TypeMirror returnType = builderMethodReturnType(method); ExecutableElement getter = getterNameToGetter.get(methodName); if (getter != null) { classifyGetter(method, getter); return; } if (methodName.endsWith("Builder")) { String property = methodName.substring(0, methodName.length() - "Builder".length()); if (getterToPropertyName.containsValue(property)) { PropertyBuilderClassifier propertyBuilderClassifier = new PropertyBuilderClassifier( errorReporter, typeUtils, elementUtils, this, getterToPropertyName, getterToPropertyType, eclipseHack); Optional propertyBuilder = propertyBuilderClassifier.makePropertyBuilder(method, property); if (propertyBuilder.isPresent()) { propertyNameToPropertyBuilder.put(property, propertyBuilder.get()); } return; } } if (TYPE_EQUIVALENCE.equivalent(returnType, autoValueClass.asType())) { buildMethods.add(method); } else { String error = String.format( "Method without arguments should be a build method returning %1$s%2$s," + " or a getter method with the same name and type as a getter method of %1$s," + " or fooBuilder() where foo() or getFoo() is a getter method of %1$s", autoValueClass, typeParamsString()); errorReporter.reportError(error, method); } } private void classifyGetter(ExecutableElement builderGetter, ExecutableElement originalGetter) { String propertyName = getterToPropertyName.get(originalGetter); TypeMirror originalGetterType = getterToPropertyType.get(originalGetter); TypeMirror builderGetterType = builderMethodReturnType(builderGetter); String builderGetterTypeString = TypeEncoder.encodeWithAnnotations(builderGetterType); if (TYPE_EQUIVALENCE.equivalent(builderGetterType, originalGetterType)) { builderGetters.put( propertyName, new BuilderSpec.PropertyGetter(builderGetter, builderGetterTypeString, null)); return; } Optionalish optional = Optionalish.createIfOptional(builderGetterType); if (optional != null) { TypeMirror containedType = optional.getContainedType(typeUtils); // If the original method is int getFoo() then we allow Optional here. // boxedOriginalType is Integer, and containedType is also Integer. // We don't need any special code for OptionalInt because containedType will be int then. TypeMirror boxedOriginalType = originalGetterType.getKind().isPrimitive() ? typeUtils.boxedClass(MoreTypes.asPrimitiveType(originalGetterType)).asType() : null; if (TYPE_EQUIVALENCE.equivalent(containedType, originalGetterType) || TYPE_EQUIVALENCE.equivalent(containedType, boxedOriginalType)) { builderGetters.put( propertyName, new BuilderSpec.PropertyGetter(builderGetter, builderGetterTypeString, optional)); return; } } String error = String.format( "Method matches a property of %1$s but has return type %2$s instead of %3$s " + "or an Optional wrapping of %3$s", autoValueClass, builderGetterType, originalGetterType); errorReporter.reportError(error, builderGetter); } /** * Classifies a method given that it has one argument. Currently, a method with one argument can * only be a setter, meaning that it must look like {@code foo(T)} or {@code setFoo(T)}, where the * {@code AutoValue} class has a property called {@code foo} of type {@code T}. */ private void classifyMethodOneArg(ExecutableElement method) { String methodName = method.getSimpleName().toString(); Map propertyNameToGetter = getterToPropertyName.inverse(); String propertyName = null; ExecutableElement valueGetter = propertyNameToGetter.get(methodName); Multimap propertyNameToSetters = null; if (valueGetter != null) { propertyNameToSetters = propertyNameToUnprefixedSetters; propertyName = methodName; } else if (valueGetter == null && methodName.startsWith("set") && methodName.length() > 3) { propertyNameToSetters = propertyNameToPrefixedSetters; propertyName = PropertyNames.decapitalizeLikeJavaBeans(methodName.substring(3)); valueGetter = propertyNameToGetter.get(propertyName); if (valueGetter == null) { // If our property is defined by a getter called getOAuth() then it is called "OAuth" // because of Introspector.decapitalize. Therefore we want Introspector.decapitalize to // be used for the setter too, so that you can write setOAuth(x). Meanwhile if the property // is defined by a getter called oAuth() then it is called "oAuth", but you would still // expect to be able to set it using setOAuth(x). Hence the second try using a decapitalize // method without the quirky two-leading-capitals rule. propertyName = PropertyNames.decapitalizeNormally(methodName.substring(3)); valueGetter = propertyNameToGetter.get(propertyName); } } if (valueGetter == null || propertyNameToSetters == null) { // The second disjunct isn't needed but convinces control-flow checkers that // propertyNameToSetters can't be null when we call put on it below. errorReporter.reportError( "Method does not correspond to a property of " + autoValueClass, method); checkForFailedJavaBean(method); return; } Optional> function = getSetterFunction(valueGetter, method); if (function.isPresent()) { DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType()); ExecutableType methodMirror = MoreTypes.asExecutable(typeUtils.asMemberOf(builderTypeMirror, method)); if (TYPE_EQUIVALENCE.equivalent(methodMirror.getReturnType(), builderType.asType())) { TypeMirror parameterType = Iterables.getOnlyElement(methodMirror.getParameterTypes()); propertyNameToSetters.put( propertyName, new PropertySetter(method, parameterType, function.get())); } else { errorReporter.reportError( "Setter methods must return " + builderType + typeParamsString(), method); } } } // A frequent source of problems is where the JavaBeans conventions have been followed for // most but not all getters. Then AutoValue considers that they haven't been followed at all, // so you might have a property called getFoo where you thought it was called just foo, and // you might not understand why your setter called setFoo is rejected (it would have to be called // setGetFoo). private void checkForFailedJavaBean(ExecutableElement rejectedSetter) { ImmutableSet allGetters = getterToPropertyName.keySet(); ImmutableSet prefixedGetters = AutoValueProcessor.prefixedGettersIn(allGetters); if (prefixedGetters.size() < allGetters.size() && prefixedGetters.size() >= allGetters.size() / 2) { String note = "This might be because you are using the getFoo() convention" + " for some but not all methods. These methods don't follow the convention: " + difference(allGetters, prefixedGetters); errorReporter.reportNote(note, rejectedSetter); } } /** * Returns an {@code Optional} describing how to convert a value from the setter's parameter type * to the getter's return type, or {@code Optional.empty()} if the conversion isn't possible. An * error will have been reported in the latter case. We can convert if they are already the same * type, when the returned function will be the identity; or if the setter type can be copied * using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned * function will be something like {@code s -> "Optional.of(" + s + ")"}. */ private Optional> getSetterFunction( ExecutableElement valueGetter, ExecutableElement setter) { VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); boolean nullableParameter = nullableAnnotationFor(parameterElement, parameterElement.asType()).isPresent(); TypeMirror targetType = getterToPropertyType.get(valueGetter); ExecutableType finalSetter = MoreTypes.asExecutable( typeUtils.asMemberOf(MoreTypes.asDeclared(builderType.asType()), setter)); TypeMirror parameterType = finalSetter.getParameterTypes().get(0); // Two types are assignable to each other if they are the same type, or if one is primitive and // the other is the corresponding boxed type. There might be other cases where this is true, but // we're likely to want to accept those too. if (typeUtils.isAssignable(parameterType, targetType) && typeUtils.isAssignable(targetType, parameterType)) { if (nullableParameter) { boolean nullableProperty = nullableAnnotationFor(valueGetter, valueGetter.getReturnType()).isPresent(); if (!nullableProperty) { String error = String.format( "Parameter of setter method is @Nullable but property method %s.%s() is not", autoValueClass, valueGetter.getSimpleName()); errorReporter.reportError(error, setter); return Optional.empty(); } } return Optional.of(s -> s); } // Parameter type is not equal to property type, but might be convertible with copyOf. ImmutableList copyOfMethods = copyOfMethods(targetType, nullableParameter); if (!copyOfMethods.isEmpty()) { return getConvertingSetterFunction(copyOfMethods, valueGetter, setter, parameterType); } String error = String.format( "Parameter type %s of setter method should be %s to match getter %s.%s", parameterType, targetType, autoValueClass, valueGetter.getSimpleName()); errorReporter.reportError(error, setter); return Optional.empty(); } /** * Returns an {@code Optional} describing how to convert a value from the setter's parameter type * to the getter's return type using one of the given methods, or {@code Optional.empty()} if the * conversion isn't possible. An error will have been reported in the latter case. */ private Optional> getConvertingSetterFunction( ImmutableList copyOfMethods, ExecutableElement valueGetter, ExecutableElement setter, TypeMirror parameterType) { DeclaredType targetType = MoreTypes.asDeclared(getterToPropertyType.get(valueGetter)); for (ExecutableElement copyOfMethod : copyOfMethods) { Optional> function = getConvertingSetterFunction(copyOfMethod, targetType, parameterType); if (function.isPresent()) { return function; } } String targetTypeSimpleName = targetType.asElement().getSimpleName().toString(); String error = String.format( "Parameter type %s of setter method should be %s to match getter %s.%s," + " or it should be a type that can be passed to %s.%s to produce %s", parameterType, targetType, autoValueClass, valueGetter.getSimpleName(), targetTypeSimpleName, copyOfMethods.get(0).getSimpleName(), targetType); errorReporter.reportError(error, setter); return Optional.empty(); } /** * Returns an {@code Optional} containing a function to use {@code copyOfMethod} to copy the * {@code parameterType} to the {@code targetType}, or {@code Optional.empty()} if the method * can't be used. For example, we might have a property of type {@code ImmutableSet} and our * setter has a parameter of type {@code Set}. Can we use {@code ImmutableSet * ImmutableSet.copyOf(Collection)} to set the property? What about {@code * ImmutableSet ImmutableSet.copyOf(E[])}? * *

      The example here is deliberately complicated, in that it has a type parameter of its own, * presumably because the {@code @AutoValue} class is {@code Foo}. One subtle point is that the * builder will then be {@code Builder} where this {@code T} is a different type * variable. However, we've used {@link TypeVariables} to ensure that the {@code T} in {@code * ImmutableSet} is actually the one from {@code Builder} instead of the original one from * {@code Foo}.} * * @param copyOfMethod the candidate method to do the copy, {@code * ImmutableSet.copyOf(Collection)} or {@code ImmutableSet.copyOf(E[])} in the * examples. * @param targetType the type of the property to be set, {@code ImmutableSet} in the example. * @param parameterType the type of the setter parameter, {@code Set} in the example. * @return a function that maps a string parameter to a method call using that parameter. For * example it might map {@code foo} to {@code ImmutableList.copyOf(foo)}. */ private Optional> getConvertingSetterFunction( ExecutableElement copyOfMethod, DeclaredType targetType, TypeMirror parameterType) { // We have a parameter type, for example Set, and we want to know if it can be // passed to the given copyOf method, which might for example be one of these methods from // ImmutableSet: // public static ImmutableSet copyOf(Collection elements) // public static ImmutableSet copyOf(E[] elements) // Additionally, if it can indeed be passed to the method, we want to know whether the result // (here ImmutableSet) is compatible with the property to be set. // We can't use Types.asMemberOf to do the substitution for us, because the methods in question // are static. So even if our target type is ImmutableSet, if we ask what the type of // copyOf is in ImmutableSet it will still tell us Optional (T). // Instead, we do the variable substitutions ourselves. if (TypeVariables.canAssignStaticMethodResult( copyOfMethod, parameterType, targetType, typeUtils)) { String method = TypeEncoder.encodeRaw(targetType) + "." + copyOfMethod.getSimpleName(); return Optional.of(s -> method + "(" + s + ")"); } return Optional.empty(); } /** * Returns {@code copyOf} methods from the given type. These are static methods with a single * parameter, called {@code copyOf} or {@code copyOfSorted} for Guava collection types, and called * {@code of} or {@code ofNullable} for {@code Optional}. All of Guava's concrete immutable * collection types have at least one such method, but we will also accept other classes with an * appropriate {@code copyOf} method, such as {@link java.util.EnumSet}. */ private ImmutableList copyOfMethods( TypeMirror targetType, boolean nullableParameter) { if (!targetType.getKind().equals(TypeKind.DECLARED)) { return ImmutableList.of(); } ImmutableSet copyOfNames; Optionalish optionalish = Optionalish.createIfOptional(targetType); if (optionalish == null) { copyOfNames = ImmutableSet.of("copyOfSorted", "copyOf"); } else { copyOfNames = ImmutableSet.of(nullableParameter ? optionalish.ofNullable() : "of"); } TypeElement targetTypeElement = MoreElements.asType(typeUtils.asElement(targetType)); ImmutableList.Builder copyOfMethods = ImmutableList.builder(); for (String copyOfName : copyOfNames) { for (ExecutableElement method : ElementFilter.methodsIn(targetTypeElement.getEnclosedElements())) { if (method.getSimpleName().contentEquals(copyOfName) && method.getParameters().size() == 1 && method.getModifiers().contains(Modifier.STATIC)) { copyOfMethods.add(method); } } } return copyOfMethods.build(); } /** * Returns the return type of the given method from the builder. This should be the final type of * the method when any bound type variables are substituted. Consider this example: * *

      {@code
         * abstract static class ParentBuilder {
         *   B setFoo(String s);
         * }
         * abstract static class ChildBuilder extends ParentBuilder {
         *   ...
         * }
         * }
      * * If the builder is {@code ChildBuilder} then the return type of {@code setFoo} is also {@code * ChildBuilder}, and not {@code B} as its {@code getReturnType()} method would claim. * *

      If the caller is in a version of Eclipse with this bug then the {@code * asMemberOf} call will fail if the method is inherited from an interface. We work around that * for methods in the {@code @AutoValue} class using {@link EclipseHack#methodReturnTypes} but we * don't try to do so here because it should be much less likely. You might need to change {@code * ParentBuilder} from an interface to an abstract class to make it work, but you'll often need to * do that anyway. */ TypeMirror builderMethodReturnType(ExecutableElement builderMethod) { DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType()); TypeMirror methodMirror; try { methodMirror = typeUtils.asMemberOf(builderTypeMirror, builderMethod); } catch (IllegalArgumentException e) { // Presumably we've hit the Eclipse bug cited. return builderMethod.getReturnType(); } return MoreTypes.asExecutable(methodMirror).getReturnType(); } private static String prefixWithSet(String propertyName) { // This is not internationalizationally correct, but it corresponds to what // Introspector.decapitalize does. return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1); } private String typeParamsString() { return TypeSimplifier.actualTypeParametersString(autoValueClass); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java000066400000000000000000000505261365703632600324130ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.hasAnnotationMirror; import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.nullableAnnotationFor; import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_BUILDER_NAME; import static com.google.common.collect.Sets.immutableEnumSet; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import static javax.lang.model.util.ElementFilter.methodsIn; import static javax.lang.model.util.ElementFilter.typesIn; import com.google.auto.common.MoreTypes; import com.google.auto.value.extension.AutoValueExtension; import com.google.auto.value.processor.AutoValueOrOneOfProcessor.Property; import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; 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.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Types; /** * Support for AutoValue builders. * * @author Éamonn McManus */ class BuilderSpec { private final TypeElement autoValueClass; private final ProcessingEnvironment processingEnv; private final ErrorReporter errorReporter; BuilderSpec( TypeElement autoValueClass, ProcessingEnvironment processingEnv, ErrorReporter errorReporter) { this.autoValueClass = autoValueClass; this.processingEnv = processingEnv; this.errorReporter = errorReporter; } private static final ImmutableSet CLASS_OR_INTERFACE = immutableEnumSet(ElementKind.CLASS, ElementKind.INTERFACE); /** * Determines if the {@code @AutoValue} class for this instance has a correct nested * {@code @AutoValue.Builder} class or interface and return a representation of it in an {@code * Optional} if so. */ Optional getBuilder() { Optional builderTypeElement = Optional.empty(); for (TypeElement containedClass : typesIn(autoValueClass.getEnclosedElements())) { if (hasAnnotationMirror(containedClass, AUTO_VALUE_BUILDER_NAME)) { if (!CLASS_OR_INTERFACE.contains(containedClass.getKind())) { errorReporter.reportError( "@AutoValue.Builder can only apply to a class or an interface", containedClass); } else if (!containedClass.getModifiers().contains(Modifier.STATIC)) { errorReporter.reportError( "@AutoValue.Builder cannot be applied to a non-static class", containedClass); } else if (builderTypeElement.isPresent()) { errorReporter.reportError( autoValueClass + " already has a Builder: " + builderTypeElement.get(), containedClass); } else { builderTypeElement = Optional.of(containedClass); } } } if (builderTypeElement.isPresent()) { return builderFrom(builderTypeElement.get()); } else { return Optional.empty(); } } /** Representation of an {@code AutoValue.Builder} class or interface. */ class Builder implements AutoValueExtension.BuilderContext { private final TypeElement builderTypeElement; private ImmutableSet toBuilderMethods; private ExecutableElement buildMethod; private BuilderMethodClassifier classifier; Builder(TypeElement builderTypeElement) { this.builderTypeElement = builderTypeElement; } @Override public TypeElement builderType() { return builderTypeElement; } @Override public Set builderMethods() { return methodsIn(autoValueClass.getEnclosedElements()).stream() .filter( m -> m.getParameters().isEmpty() && m.getModifiers().contains(Modifier.STATIC) && !m.getModifiers().contains(Modifier.PRIVATE) && erasedTypeIs(m.getReturnType(), builderTypeElement)) .collect(toSet()); } @Override public Optional buildMethod() { return methodsIn(builderTypeElement.getEnclosedElements()).stream() .filter( m -> m.getSimpleName().contentEquals("build") && !m.getModifiers().contains(Modifier.PRIVATE) && !m.getModifiers().contains(Modifier.STATIC) && m.getParameters().isEmpty() && erasedTypeIs(m.getReturnType(), autoValueClass)) .findFirst(); } @Override public ExecutableElement autoBuildMethod() { return buildMethod; } @Override public Map> setters() { return Maps.transformValues( classifier.propertyNameToSetters().asMap(), propertySetters -> propertySetters.stream().map(PropertySetter::getSetter).collect(toSet())); } @Override public Map propertyBuilders() { return Maps.transformValues( classifier.propertyNameToPropertyBuilder(), PropertyBuilder::getPropertyBuilderMethod); } private boolean erasedTypeIs(TypeMirror type, TypeElement baseType) { return type.getKind().equals(TypeKind.DECLARED) && MoreTypes.asDeclared(type).asElement().equals(baseType); } @Override public Set toBuilderMethods() { return toBuilderMethods; } /** * Finds any methods in the set that return the builder type. If the builder has type parameters * {@code }, then the return type of the method must be {@code Builder} with the * same parameter names. We enforce elsewhere that the names and bounds of the builder * parameters must be the same as those of the @AutoValue class. Here's a correct example: * *

           * {@code @AutoValue abstract class Foo {
           *   abstract int someProperty();
           *
           *   abstract Builder toBuilder();
           *
           *   interface Builder {...}
           * }}
           * 
      * *

      We currently impose that there cannot be more than one such method. */ ImmutableSet toBuilderMethods( Types typeUtils, Set abstractMethods) { List builderTypeParamNames = builderTypeElement.getTypeParameters().stream() .map(e -> e.getSimpleName().toString()) .collect(toList()); ImmutableSet.Builder methods = ImmutableSet.builder(); for (ExecutableElement method : abstractMethods) { if (builderTypeElement.equals(typeUtils.asElement(method.getReturnType()))) { methods.add(method); DeclaredType returnType = MoreTypes.asDeclared(method.getReturnType()); List typeArguments = returnType.getTypeArguments().stream() .filter(t -> t.getKind().equals(TypeKind.TYPEVAR)) .map(t -> typeUtils.asElement(t).getSimpleName().toString()) .collect(toList()); if (!builderTypeParamNames.equals(typeArguments)) { errorReporter.reportError( "Builder converter method should return " + builderTypeElement + TypeSimplifier.actualTypeParametersString(builderTypeElement), method); } } } ImmutableSet builderMethods = methods.build(); if (builderMethods.size() > 1) { errorReporter.reportError( "There can be at most one builder converter method", builderMethods.iterator().next()); } this.toBuilderMethods = builderMethods; return builderMethods; } void defineVars( AutoValueTemplateVars vars, ImmutableBiMap getterToPropertyName) { Iterable builderMethods = abstractMethods(builderTypeElement); boolean autoValueHasToBuilder = !toBuilderMethods.isEmpty(); ImmutableMap getterToPropertyType = TypeVariables.rewriteReturnTypes( processingEnv.getElementUtils(), processingEnv.getTypeUtils(), getterToPropertyName.keySet(), autoValueClass, builderTypeElement); Optional optionalClassifier = BuilderMethodClassifier.classify( builderMethods, errorReporter, processingEnv, autoValueClass, builderTypeElement, getterToPropertyName, getterToPropertyType, autoValueHasToBuilder); if (!optionalClassifier.isPresent()) { return; } for (ExecutableElement method : ElementFilter.methodsIn(builderTypeElement.getEnclosedElements())) { if (method.getSimpleName().contentEquals("builder") && method.getModifiers().contains(Modifier.STATIC) && method.getAnnotationMirrors().isEmpty()) { // For now we ignore methods with annotations, because for example we do want to allow // Jackson's @JsonCreator. errorReporter.reportWarning( "Static builder() method should be in the containing class", method); } } this.classifier = optionalClassifier.get(); Set buildMethods = classifier.buildMethods(); if (buildMethods.size() != 1) { Set errorElements = buildMethods.isEmpty() ? ImmutableSet.of(builderTypeElement) : buildMethods; for (Element buildMethod : errorElements) { errorReporter.reportError( "Builder must have a single no-argument method returning " + autoValueClass + typeParamsString(), buildMethod); } return; } this.buildMethod = Iterables.getOnlyElement(buildMethods); vars.builderIsInterface = builderTypeElement.getKind() == ElementKind.INTERFACE; vars.builderTypeName = TypeSimplifier.classNameOf(builderTypeElement); vars.builderFormalTypes = TypeEncoder.formalTypeParametersString(builderTypeElement); vars.builderActualTypes = TypeSimplifier.actualTypeParametersString(builderTypeElement); vars.buildMethod = Optional.of(new SimpleMethod(buildMethod)); vars.builderGetters = classifier.builderGetters(); vars.builderSetters = classifier.propertyNameToSetters(); vars.builderPropertyBuilders = ImmutableMap.copyOf(classifier.propertyNameToPropertyBuilder()); Set required = new LinkedHashSet<>(vars.props); for (Property property : vars.props) { if (property.isNullable() || property.getOptional() != null || vars.builderPropertyBuilders.containsKey(property.getName())) { required.remove(property); } } vars.builderRequiredProperties = ImmutableSet.copyOf(required); } } /** * Information about a builder property getter, referenced from the autovalue.vm template. A * property called foo (defined by a method {@code T foo()} or {@code T getFoo()}) can have a * getter method in the builder with the same name ({@code foo()} or {@code getFoo()}) and a * return type of either {@code T} or {@code Optional}. The {@code Optional} form can be * used to tell whether the property has been set. Here, {@code Optional} can be either {@code * java.util.Optional} or {@code com.google.common.base.Optional}. If {@code T} is {@code int}, * {@code long}, or {@code double}, then instead of {@code Optional} we can have {@code * OptionalInt} etc. If {@code T} is a primitive type (including these ones but also the other * five) then {@code Optional} can be the corresponding boxed type. */ public static class PropertyGetter { private final String access; private final String type; private final Optionalish optional; /** * Makes a new {@code PropertyGetter} instance. * * @param method the source method which this getter is implementing. * @param type the type that the getter returns. This is written to take imports into account, * so it might be {@code List} for example. It is either identical to the type of * the corresponding getter in the {@code @AutoValue} class, or it is an optional wrapper, * like {@code Optional>}. * @param optional a representation of the {@code Optional} type that the getter returns, if * this is an optional getter, or null otherwise. An optional getter is one that returns * {@code Optional} rather than {@code T}, as explained above. */ PropertyGetter(ExecutableElement method, String type, Optionalish optional) { this.access = SimpleMethod.access(method); this.type = type; this.optional = optional; } public String getAccess() { return access; } public String getType() { return type; } public Optionalish getOptional() { return optional; } } /** * Information about a property setter, referenced from the autovalue.vm template. A property * called foo (defined by a method {@code T foo()} or {@code T getFoo()}) can have a setter method * {@code foo(T)} or {@code setFoo(T)} that returns the builder type. Additionally, it can have a * setter with a type that can be copied to {@code T} through a {@code copyOf} method; for example * a property {@code foo} of type {@code ImmutableSet} can be set with a method {@code * setFoo(Collection foos)}. And, if {@code T} is {@code Optional}, it can have a setter * with a type that can be copied to {@code T} through {@code Optional.of}. */ public static class PropertySetter { private final ExecutableElement setter; private final String access; private final String name; private final String parameterTypeString; private final boolean primitiveParameter; private final String nullableAnnotation; private final Function copyFunction; PropertySetter( ExecutableElement setter, TypeMirror parameterType, Function copyFunction) { this.setter = setter; this.copyFunction = copyFunction; this.access = SimpleMethod.access(setter); this.name = setter.getSimpleName().toString(); primitiveParameter = parameterType.getKind().isPrimitive(); this.parameterTypeString = parameterTypeString(setter, parameterType); VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters()); Optional maybeNullable = nullableAnnotationFor(parameterElement, parameterType); this.nullableAnnotation = maybeNullable.orElse(""); } ExecutableElement getSetter() { return setter; } private static String parameterTypeString(ExecutableElement setter, TypeMirror parameterType) { if (setter.isVarArgs()) { TypeMirror componentType = MoreTypes.asArray(parameterType).getComponentType(); // This is a bit ugly. It's OK to annotate just the component type, because if it is // say `@Nullable String` then we will end up with `@Nullable String...`. Unlike the // normal array case, we can't have the situation where the array itself is annotated; // you can write `String @Nullable []` to mean that, but you can't write // `String @Nullable ...`. return TypeEncoder.encodeWithAnnotations(componentType) + "..."; } else { return TypeEncoder.encodeWithAnnotations(parameterType); } } public String getAccess() { return access; } public String getName() { return name; } public String getParameterType() { return parameterTypeString; } public boolean getPrimitiveParameter() { return primitiveParameter; } public String getNullableAnnotation() { return nullableAnnotation; } public String copy(AutoValueProcessor.Property property) { String copy = copyFunction.apply(property.toString()); // Add a null guard only in cases where we are using copyOf and the property is @Nullable. if (property.isNullable() && !copy.equals(property.toString())) { copy = String.format("(%s == null ? null : %s)", property, copy); } return copy; } } /** * Returns a representation of the given {@code @AutoValue.Builder} class or interface. If the * class or interface has abstract methods that could not be part of any builder, emits error * messages and returns Optional.empty(). */ private Optional builderFrom(TypeElement builderTypeElement) { // We require the builder to have the same type parameters as the @AutoValue class, meaning the // same names and bounds. In principle the type parameters could have different names, but that // would be confusing, and our code would reject it anyway because it wouldn't consider that // the return type of Foo build() was really the same as the declaration of Foo. This // check produces a better error message in that case and similar ones. if (!sameTypeParameters(autoValueClass, builderTypeElement)) { errorReporter.reportError( "Type parameters of " + builderTypeElement + " must have same names and bounds as " + "type parameters of " + autoValueClass, builderTypeElement); return Optional.empty(); } return Optional.of(new Builder(builderTypeElement)); } private static boolean sameTypeParameters(TypeElement a, TypeElement b) { int nTypeParameters = a.getTypeParameters().size(); if (nTypeParameters != b.getTypeParameters().size()) { return false; } for (int i = 0; i < nTypeParameters; i++) { TypeParameterElement aParam = a.getTypeParameters().get(i); TypeParameterElement bParam = b.getTypeParameters().get(i); if (!aParam.getSimpleName().equals(bParam.getSimpleName())) { return false; } Set autoValueBounds = new TypeMirrorSet(aParam.getBounds()); Set builderBounds = new TypeMirrorSet(bParam.getBounds()); if (!autoValueBounds.equals(builderBounds)) { return false; } } return true; } // Return a set of all abstract methods in the given TypeElement or inherited from ancestors. private Set abstractMethods(TypeElement typeElement) { Set methods = getLocalAndInheritedMethods( typeElement, processingEnv.getTypeUtils(), processingEnv.getElementUtils()); ImmutableSet.Builder abstractMethods = ImmutableSet.builder(); for (ExecutableElement method : methods) { if (method.getModifiers().contains(Modifier.ABSTRACT)) { abstractMethods.add(method); } } return abstractMethods.build(); } private String typeParamsString() { return TypeSimplifier.actualTypeParametersString(autoValueClass); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/ClassNames.java000066400000000000000000000024111365703632600322310ustar00rootroot00000000000000/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; /** * Names of classes that are referenced in the processors. * * @author emcmanus@google.com (Éamonn McManus) */ final class ClassNames { private ClassNames() {} static final String AUTO_VALUE_PACKAGE_NAME = "com.google.auto.value."; static final String AUTO_ANNOTATION_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoAnnotation"; static final String AUTO_ONE_OF_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoOneOf"; static final String AUTO_VALUE_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoValue"; static final String AUTO_VALUE_BUILDER_NAME = AUTO_VALUE_NAME + ".Builder"; static final String COPY_ANNOTATIONS_NAME = AUTO_VALUE_NAME + ".CopyAnnotations"; } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/EclipseHack.java000066400000000000000000000206171365703632600323630ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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.google.auto.value.processor; import static java.util.stream.Collectors.toList; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; /** * Hacks needed to work around various bugs and incompatibilities in Eclipse's implementation of * annotation processing. * * @author Éamonn McManus */ class EclipseHack { private final Elements elementUtils; private final Types typeUtils; EclipseHack(ProcessingEnvironment processingEnv) { this(processingEnv.getElementUtils(), processingEnv.getTypeUtils()); } EclipseHack(Elements elementUtils, Types typeUtils) { this.elementUtils = elementUtils; this.typeUtils = typeUtils; } /** * Returns the enclosing type of {@code type}, if {@code type} is an inner class. Otherwise * returns a {@code NoType}. This is what {@link DeclaredType#getEnclosingType()} is supposed to * do. However, some versions of Eclipse have a bug where, for example, asking for the enclosing * type of {@code PrimitiveIterator.OfInt} will return {@code PrimitiveIterator} rather * than plain {@code PrimitiveIterator}, as if {@code OfInt} were an inner class rather than a * static one. This would lead us to write a reference to {@code OfInt} as {@code * PrimitiveIterator.OfInt}, which would obviously break. We attempt to avert this by * detecting that: * *

        *
      • there is an enclosing type that is a {@code DeclaredType}, which should mean that {@code * type} is an inner class; *
      • we are in the Eclipse compiler; *
      • the type arguments of the purported enclosing type are all type variables with the same * names as the corresponding type parameters. *
      * *

      If all these conditions are met, we assume we're hitting the Eclipse bug, and we return no * enclosing type instead. That does mean that in the unlikely event where we really do have an * inner class of an instantiation of the outer class with type arguments that happen to be type * variables with the same names as the corresponding parameters, we will do the wrong thing on * Eclipse. But doing the wrong thing in that case is better than doing the wrong thing in the * usual case. */ static TypeMirror getEnclosingType(DeclaredType type) { TypeMirror enclosing = type.getEnclosingType(); if (!enclosing.getKind().equals(TypeKind.DECLARED) || !enclosing.getClass().getName().contains("eclipse")) { // If the class representing the enclosing type comes from the Eclipse compiler, it will be // something like org.eclipse.jdt.internal.compiler.apt.model.DeclaredTypeImpl. If we're not // in the Eclipse compiler then we don't expect to see "eclipse" in the name of this // implementation class. return enclosing; } DeclaredType declared = MoreTypes.asDeclared(enclosing); List arguments = declared.getTypeArguments(); if (!arguments.isEmpty()) { boolean allVariables = arguments.stream().allMatch(t -> t.getKind().equals(TypeKind.TYPEVAR)); if (allVariables) { List argumentNames = arguments.stream() .map(t -> MoreTypes.asTypeVariable(t).asElement().getSimpleName()) .collect(toList()); TypeElement enclosingElement = MoreTypes.asTypeElement(declared); List parameterNames = enclosingElement.getTypeParameters().stream() .map(Element::getSimpleName) .collect(toList()); if (argumentNames.equals(parameterNames)) { // We're going to return a NoType. We don't have a Types to hand so we can't call // Types.getNoType(). Instead, just keep going through outer types until we get to // the outside, which will be a NoType. while (enclosing.getKind().equals(TypeKind.DECLARED)) { enclosing = MoreTypes.asDeclared(enclosing).getEnclosingType(); } return enclosing; } } } return declared; } TypeMirror methodReturnType(ExecutableElement method, DeclaredType in) { try { TypeMirror methodMirror = typeUtils.asMemberOf(in, method); return MoreTypes.asExecutable(methodMirror).getReturnType(); } catch (IllegalArgumentException e) { return methodReturnTypes(ImmutableSet.of(method), in).get(method); } } /** * Returns a map containing the real return types of the given methods, knowing that they appear * in the given type. This means that if the given type is say {@code StringIterator implements * Iterator} then we want the {@code next()} method to map to String, rather than the * {@code T} that it returns as inherited from {@code Iterator}. This method is in EclipseHack * because if it weren't for this * Eclipse bug it would be trivial. Unfortunately, versions of Eclipse up to at least 4.5 have * a bug where the {@link Types#asMemberOf} method throws IllegalArgumentException if given a * method that is inherited from an interface. Fortunately, Eclipse's implementation of {@link * Elements#getAllMembers} does the type substitution that {@code asMemberOf} would have done. But * javac's implementation doesn't. So we try the way that would work if Eclipse weren't buggy, and * only if we get IllegalArgumentException do we use {@code getAllMembers}. */ ImmutableMap methodReturnTypes( Set methods, DeclaredType in) { ImmutableMap.Builder map = ImmutableMap.builder(); Map noArgMethods = null; for (ExecutableElement method : methods) { TypeMirror returnType = null; try { TypeMirror methodMirror = typeUtils.asMemberOf(in, method); returnType = MoreTypes.asExecutable(methodMirror).getReturnType(); } catch (IllegalArgumentException e) { if (method.getParameters().isEmpty()) { if (noArgMethods == null) { noArgMethods = noArgMethodsIn(in); } returnType = noArgMethods.get(method.getSimpleName()).getReturnType(); } } if (returnType == null) { returnType = method.getReturnType(); } map.put(method, returnType); } return map.build(); } /** * Constructs a map from name to method of the no-argument methods in the given type. We need this * because an ExecutableElement returned by {@link Elements#getAllMembers} will not compare equal * to the original ExecutableElement if {@code getAllMembers} substituted type parameters, as it * does in Eclipse. */ private Map noArgMethodsIn(DeclaredType in) { TypeElement autoValueType = MoreElements.asType(typeUtils.asElement(in)); List allMethods = ElementFilter.methodsIn(elementUtils.getAllMembers(autoValueType)); Map map = new LinkedHashMap(); for (ExecutableElement method : allMethods) { if (method.getParameters().isEmpty()) { map.put(method.getSimpleName(), method); } } return map; } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/ErrorReporter.java000066400000000000000000000053531365703632600330240ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.tools.Diagnostic; /** * Handle error reporting for an annotation processor. * * @author Éamonn McManus */ class ErrorReporter { private final Messager messager; private int errorCount; ErrorReporter(ProcessingEnvironment processingEnv) { this.messager = processingEnv.getMessager(); } /** * Issue a compilation note. * * @param msg the text of the note * @param e the element to which it pertains */ void reportNote(String msg, Element e) { messager.printMessage(Diagnostic.Kind.NOTE, msg, e); } /** * Issue a compilation warning. * * @param msg the text of the warning * @param e the element to which it pertains */ void reportWarning(String msg, Element e) { messager.printMessage(Diagnostic.Kind.WARNING, msg, e); } /** * Issue a compilation error. This method does not throw an exception, since we want to continue * processing and perhaps report other errors. It is a good idea to introduce a test case in * CompilationTest for any new call to reportError(...) to ensure that we continue correctly after * an error. * * @param msg the text of the warning * @param e the element to which it pertains */ void reportError(String msg, Element e) { messager.printMessage(Diagnostic.Kind.ERROR, msg, e); errorCount++; } /** * Issue a compilation error and abandon the processing of this class. This does not prevent the * processing of other classes. * * @param msg the text of the error * @param e the element to which it pertains */ void abortWithError(String msg, Element e) { reportError(msg, e); throw new AbortProcessingException(); } /** The number of errors that have been output by calls to {@link #reportError}. */ int errorCount() { return errorCount; } /** Abandon the processing of this class if any errors have been output. */ void abortIfAnyError() { if (errorCount > 0) { throw new AbortProcessingException(); } } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/ExtensionContext.java000066400000000000000000000060621365703632600335270ustar00rootroot00000000000000/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; import com.google.auto.value.extension.AutoValueExtension; import com.google.auto.value.extension.AutoValueExtension.BuilderContext; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; class ExtensionContext implements AutoValueExtension.Context { private final ProcessingEnvironment processingEnvironment; private final TypeElement autoValueClass; private final ImmutableMap properties; private final ImmutableMap propertyTypes; private final ImmutableSet abstractMethods; private Optional builderContext = Optional.empty(); ExtensionContext( ProcessingEnvironment processingEnvironment, TypeElement autoValueClass, ImmutableMap properties, ImmutableMap propertyMethodsAndTypes, ImmutableSet abstractMethods) { this.processingEnvironment = processingEnvironment; this.autoValueClass = autoValueClass; this.properties = properties; this.propertyTypes = ImmutableMap.copyOf(Maps.transformValues(properties, propertyMethodsAndTypes::get)); this.abstractMethods = abstractMethods; } void setBuilderContext(BuilderContext builderContext) { this.builderContext = Optional.of(builderContext); } @Override public ProcessingEnvironment processingEnvironment() { return processingEnvironment; } @Override public String packageName() { return TypeSimplifier.packageNameOf(autoValueClass); } @Override public TypeElement autoValueClass() { return autoValueClass; } @Override public String finalAutoValueClassName() { return AutoValueProcessor.generatedSubclassName(autoValueClass, 0); } @Override public Map properties() { return properties; } @Override public Map propertyTypes() { return propertyTypes; } @Override public Set abstractMethods() { return abstractMethods; } @Override public Optional builder() { return builderContext; } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/GwtCompatibility.java000066400000000000000000000054431365703632600335030ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static java.util.stream.Collectors.joining; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Name; import javax.lang.model.element.TypeElement; class GwtCompatibility { private final Optional gwtCompatibleAnnotation; GwtCompatibility(TypeElement type) { Optional gwtCompatibleAnnotation = Optional.empty(); List annotations = type.getAnnotationMirrors(); for (AnnotationMirror annotation : annotations) { Name name = annotation.getAnnotationType().asElement().getSimpleName(); if (name.contentEquals("GwtCompatible")) { gwtCompatibleAnnotation = Optional.of(annotation); } } this.gwtCompatibleAnnotation = gwtCompatibleAnnotation; } Optional gwtCompatibleAnnotation() { return gwtCompatibleAnnotation; } // Get rid of the misconceived // in the return type of getElementValues(). static Map getElementValues(AnnotationMirror annotation) { return Collections.unmodifiableMap( annotation.getElementValues()); } String gwtCompatibleAnnotationString() { if (gwtCompatibleAnnotation.isPresent()) { AnnotationMirror annotation = gwtCompatibleAnnotation.get(); TypeElement annotationElement = (TypeElement) annotation.getAnnotationType().asElement(); String annotationArguments; if (annotation.getElementValues().isEmpty()) { annotationArguments = ""; } else { annotationArguments = getElementValues(annotation) .entrySet() .stream() .map(e -> e.getKey().getSimpleName() + " = " + e.getValue()) .collect(joining(", ", "(", ")")); } return "@" + annotationElement.getQualifiedName() + annotationArguments; } else { return ""; } } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java000066400000000000000000000241451365703632600335070ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.toList; import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multimap; import com.google.escapevelocity.Template; import java.io.IOException; import java.io.Writer; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.zip.CRC32; import javax.annotation.processing.ProcessingEnvironment; 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.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; /** * Generates GWT serialization code for {@code @AutoValue} classes also marked * {@code @GwtCompatible(serializable = true)}. * * @author Éamonn McManus */ class GwtSerialization { private final GwtCompatibility gwtCompatibility; private final ProcessingEnvironment processingEnv; private final TypeElement type; GwtSerialization( GwtCompatibility gwtCompatibility, ProcessingEnvironment processingEnv, TypeElement type) { this.gwtCompatibility = gwtCompatibility; this.processingEnv = processingEnv; this.type = type; } private boolean shouldWriteGwtSerializer() { Optional optionalGwtCompatible = gwtCompatibility.gwtCompatibleAnnotation(); if (optionalGwtCompatible.isPresent()) { AnnotationMirror gwtCompatible = optionalGwtCompatible.get(); for (Map.Entry entry : GwtCompatibility.getElementValues(gwtCompatible).entrySet()) { if (entry.getKey().getSimpleName().contentEquals("serializable") && entry.getValue().getValue().equals(true)) { return true; } } } return false; } /** * Writes the GWT serializer for the given type, if appropriate. An {@code @AutoValue} class gets * a GWT serializer if it is annotated with {@code @GwtCompatible(serializable = true)}, where the * {@code @GwtCompatible} annotation can come from any package. * *

      If the type is com.example.Foo then the generated AutoValue subclass is * com.example.AutoValue_Foo and the GWT serializer is * com.example.AutoValue_Foo_CustomFieldSerializer. * * @param autoVars the template variables defined for this type. */ void maybeWriteGwtSerializer(AutoValueTemplateVars autoVars) { if (shouldWriteGwtSerializer()) { GwtTemplateVars vars = new GwtTemplateVars(); vars.pkg = autoVars.pkg; vars.subclass = autoVars.finalSubclass; vars.formalTypes = autoVars.formalTypes; vars.actualTypes = autoVars.actualTypes; vars.useBuilder = !autoVars.builderTypeName.isEmpty(); vars.builderSetters = autoVars.builderSetters; vars.builderPropertyBuilders = autoVars.builderPropertyBuilders; vars.generated = autoVars.generated; String className = (vars.pkg.isEmpty() ? "" : vars.pkg + ".") + vars.subclass + "_CustomFieldSerializer"; vars.serializerClass = TypeSimplifier.simpleNameOf(className); vars.props = autoVars.props.stream().map(Property::new).collect(toList()); vars.classHashString = computeClassHash(autoVars.props, vars.pkg); String text = vars.toText(); text = TypeEncoder.decode(text, processingEnv, vars.pkg, type.asType()); writeSourceFile(className, text, type); } } public static class Property { private final AutoValueProcessor.Property property; private final boolean isCastingUnchecked; Property(AutoValueProcessor.Property property) { this.property = property; this.isCastingUnchecked = TypeSimplifier.isCastingUnchecked(property.getTypeMirror()); } @Override public String toString() { return property.toString(); } public String getGetter() { return property.getGetter(); } public String getType() { return property.getType(); } public String getName() { return property.getName(); } /** * Returns the suffix in serializer method names for values of the given type. For example, if * the type is "int" then the returned value will be "Int" because the serializer methods are * called readInt and writeInt. There are methods for all primitive types and String; every * other type uses readObject and writeObject. */ public String getGwtType() { TypeMirror typeMirror = property.getTypeMirror(); String type = typeMirror.toString(); if (property.getKind().isPrimitive()) { return Character.toUpperCase(type.charAt(0)) + type.substring(1); } else if (type.equals("java.lang.String")) { return "String"; } else { return "Object"; } } /** * Returns a string to be inserted before the call to the readFoo() call so that the expression * can be assigned to the given type. For primitive types and String, the readInt() etc methods * already return the right type so the string is empty. For other types, the string is a cast * like "(Foo) ". */ public String getGwtCast() { if (property.getKind().isPrimitive() || getType().equals("String")) { return ""; } else { return "(" + getType() + ") "; } } public boolean isCastingUnchecked() { return isCastingUnchecked; } } @SuppressWarnings("unused") // some fields are only read through reflection static class GwtTemplateVars extends TemplateVars { /** The properties defined by the parent class's abstract methods. */ List props; /** * The package of the class with the {@code @AutoValue} annotation and its generated subclass. */ String pkg; /** The simple name of the generated subclass. */ String subclass; /** * The formal generic signature of the class with the {@code @AutoValue} annotation and its * generated subclass. This is empty, or contains type variables with optional bounds, for * example {@code }. */ String formalTypes; /** * The generic signature used by the generated subclass for its superclass reference. This is * empty, or contains only type variables with no bounds, for example {@code }. */ String actualTypes; /** True if the {@code @AutoValue} class is constructed using a generated builder. */ Boolean useBuilder; /** * A multimap from property names (like foo) to the corresponding setter methods (foo or * setFoo). */ Multimap builderSetters; /** * A map from property names to information about the associated property builder. A property * called foo (defined by a method foo() or getFoo()) can have a property builder called * fooBuilder(). The type of foo must be a type that has an associated builder following certain * conventions. Guava immutable types such as ImmutableList follow those conventions, as do many * {@code @AutoValue} types. */ ImmutableMap builderPropertyBuilders = ImmutableMap.of(); /** The simple name of the generated GWT serializer class. */ String serializerClass; /** * The encoding of the {@code Generated} class. Empty if no {@code Generated} class is * available. */ String generated; /** A string that should change if any salient details of the serialized class change. */ String classHashString; private static final Template TEMPLATE = parsedTemplateForResource("gwtserializer.vm"); @Override Template parsedTemplate() { return TEMPLATE; } } private void writeSourceFile(String className, String text, TypeElement originatingType) { try { JavaFileObject sourceFile = processingEnv.getFiler().createSourceFile(className, originatingType); try (Writer writer = sourceFile.openWriter()) { writer.write(text); } } catch (IOException e) { processingEnv .getMessager() .printMessage( Diagnostic.Kind.WARNING, "Could not write generated class " + className + ": " + e); // A warning rather than an error for the reason explained in // AutoValueOrOneOfProcessor.writeSourceFile. } } // Compute a hash that is guaranteed to change if the names, types, or order of the fields // change. We use TypeEncoder so that we can get a defined string for types, since // TypeMirror.toString() isn't guaranteed to remain the same. private String computeClassHash(Iterable props, String pkg) { CRC32 crc = new CRC32(); String encodedType = TypeEncoder.encode(type.asType()) + ":"; String decodedType = TypeEncoder.decode(encodedType, processingEnv, "", null); if (!decodedType.startsWith(pkg)) { // This is for compatibility with the way an earlier version did things. Preserving hash // codes probably isn't vital, since client and server should be in sync. decodedType = pkg + "." + decodedType; } crc.update(decodedType.getBytes(UTF_8)); for (AutoValueProcessor.Property prop : props) { String encodedProp = prop + ":" + TypeEncoder.encode(prop.getTypeMirror()) + ";"; String decodedProp = TypeEncoder.decode(encodedProp, processingEnv, pkg, null); crc.update(decodedProp.getBytes(UTF_8)); } return String.format("%08x", crc.getValue()); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/JavaScanner.java000066400000000000000000000076001365703632600324000ustar00rootroot00000000000000/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; /** * A simplistic Java scanner. This scanner returns a sequence of tokens that can be used to * reconstruct the source code. Since the source code is coming from a string, the scanner in fact * just returns token boundaries rather than the tokens themselves. * *

      We are not dealing with arbitrary user code so we can assume there are no exotic things like * tabs or Unicode escapes that resolve into quotes. The purpose of the scanner here is to return a * sequence of offsets that split the string up in a way that allows us to work with spaces without * having to worry whether they are inside strings or comments. The particular properties we use are * that every string and character literal and every comment is a single token; every newline plus * all following indentation is a single token; and every other string of consecutive spaces outside * a comment or literal is a single token. That means that we can safely compress a token that * starts with a space into a single space, without falsely removing indentation or changing the * contents of strings. * *

      In addition to real Java syntax, this scanner recognizes tokens of the form {@code `text`}, * which are used in the templates to wrap fully-qualified type names, so that they can be extracted * and replaced by imported names if possible. * * @author Éamonn McManus */ class JavaScanner { private final String s; JavaScanner(String s) { this.s = s.endsWith("\n") ? s : (s + '\n'); // This allows us to avoid checking for the end of the string in most cases. } /** * Returns the string being scanned, which is either the original input string or that string plus * a newline. */ String string() { return s; } /** Returns the position at which this token ends and the next token begins. */ int tokenEnd(int start) { if (start >= s.length()) { return s.length(); } switch (s.charAt(start)) { case ' ': case '\n': return spaceEnd(start); case '/': if (s.charAt(start + 1) == '*') { return blockCommentEnd(start); } else if (s.charAt(start + 1) == '/') { return lineCommentEnd(start); } else { return start + 1; } case '\'': case '"': case '`': return quoteEnd(start); default: // Every other character is considered to be its own token. return start + 1; } } private int spaceEnd(int start) { assert s.charAt(start) == ' ' || s.charAt(start) == '\n'; int i; for (i = start + 1; i < s.length() && s.charAt(i) == ' '; i++) {} return i; } private int blockCommentEnd(int start) { assert s.charAt(start) == '/' && s.charAt(start + 1) == '*'; int i; for (i = start + 2; s.charAt(i) != '*' || s.charAt(i + 1) != '/'; i++) {} return i + 2; } private int lineCommentEnd(int start) { assert s.charAt(start) == '/' && s.charAt(start + 1) == '/'; int end = s.indexOf('\n', start + 2); assert end > 0; return end; } private int quoteEnd(int start) { char quote = s.charAt(start); assert quote == '\'' || quote == '"' || quote == '`'; int i; for (i = start + 1; s.charAt(i) != quote; i++) { if (s.charAt(i) == '\\') { i++; } } return i + 1; } } MissingTypeException.java000066400000000000000000000021761365703632600342630ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; /** * Exception thrown in the specific case where processing of a class was abandoned because it * required types that the class references to be present and they were not. This case is handled * specially because it is possible that those types might be generated later during annotation * processing, so we should reattempt the processing of the class in a later annotation processing * round. * * @author Éamonn McManus */ @SuppressWarnings("serial") class MissingTypeException extends RuntimeException {} auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/Optionalish.java000066400000000000000000000111431365703632600324730ustar00rootroot00000000000000/* * Copyright 2016 Google LLC * * 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.google.auto.value.processor; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.base.Verify; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.List; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; /** * A wrapper for properties of Optional-like classes. This can be com.google.common.base.Optional, * or any of Optional, OptionalDouble, OptionalInt, OptionalLong in java.util. * * @author emcmanus@google.com (Éamonn McManus) */ public class Optionalish { private static final ImmutableSet OPTIONAL_CLASS_NAMES = ImmutableSet.of( "com.".concat("google.common.base.Optional"), // subterfuge to foil shading "java.util.Optional", "java.util.OptionalDouble", "java.util.OptionalInt", "java.util.OptionalLong"); private final DeclaredType optionalType; private final String className; private Optionalish(DeclaredType optionalType) { this.optionalType = optionalType; this.className = MoreElements.asType(optionalType.asElement()).getQualifiedName().toString(); } /** * Returns an instance wrapping the given TypeMirror, or null if it is not any kind of Optional. * * @param type the TypeMirror for the original optional type, for example {@code * Optional}. */ static Optionalish createIfOptional(TypeMirror type) { if (isOptional(type)) { return new Optionalish(MoreTypes.asDeclared(type)); } else { return null; } } static boolean isOptional(TypeMirror type) { if (type.getKind() != TypeKind.DECLARED) { return false; } DeclaredType declaredType = MoreTypes.asDeclared(type); TypeElement typeElement = MoreElements.asType(declaredType.asElement()); return OPTIONAL_CLASS_NAMES.contains(typeElement.getQualifiedName().toString()) && typeElement.getTypeParameters().size() == declaredType.getTypeArguments().size(); } /** * Returns a string representing the raw type of this Optional. This will typically be just {@code * "Optional"}, but it might be {@code "OptionalInt"} or {@code "java.util.Optional"} for example. */ public String getRawType() { return TypeEncoder.encodeRaw(optionalType); } /** * Returns a string representing the method call to obtain the empty version of this Optional. * This will be something like {@code "Optional.empty()"} or possibly {@code * "java.util.Optional.empty()"}. It does not have a final semicolon. * *

      This method is public so that it can be referenced as {@code p.optional.empty} from * templates. */ public String getEmpty() { String empty = className.startsWith("java.util.") ? ".empty()" : ".absent()"; return TypeEncoder.encodeRaw(optionalType) + empty; } TypeMirror getContainedType(Types typeUtils) { List typeArguments = optionalType.getTypeArguments(); switch (typeArguments.size()) { case 1: return typeArguments.get(0); case 0: return getContainedPrimitiveType(typeUtils); default: throw new AssertionError("Wrong number of type arguments: " + optionalType); } } String ofNullable() { return className.equals("java.util.Optional") ? "ofNullable" : "fromNullable"; } private static final ImmutableMap PRIMITIVE_TYPE_KINDS = ImmutableMap.of( "OptionalDouble", TypeKind.DOUBLE, "OptionalInt", TypeKind.INT, "OptionalLong", TypeKind.LONG); private TypeMirror getContainedPrimitiveType(Types typeUtils) { String simpleName = optionalType.asElement().getSimpleName().toString(); TypeKind typeKind = PRIMITIVE_TYPE_KINDS.get(simpleName); Verify.verifyNotNull(typeKind, "Could not get contained type of %s", optionalType); return typeUtils.getPrimitiveType(typeKind); } } PropertyBuilderClassifier.java000066400000000000000000000444211365703632600352700ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2016 Google LLC * * 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.google.auto.value.processor; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ElementKind; 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.type.ExecutableType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; /** * Classifies methods inside builder types that return builders for properties. For example, if * {@code @AutoValue} class Foo has a method {@code ImmutableList bar()} then Foo.Builder * can have a method {@code ImmutableList.Builder barBuilder()}. This class checks that a * method like {@code barBuilder()} follows the rules, and if so constructs a {@link * PropertyBuilder} instance with information about {@code barBuilder}. * * @author Éamonn McManus */ class PropertyBuilderClassifier { private final ErrorReporter errorReporter; private final Types typeUtils; private final Elements elementUtils; private final BuilderMethodClassifier builderMethodClassifier; private final ImmutableBiMap getterToPropertyName; private final ImmutableMap getterToPropertyType; private final EclipseHack eclipseHack; PropertyBuilderClassifier( ErrorReporter errorReporter, Types typeUtils, Elements elementUtils, BuilderMethodClassifier builderMethodClassifier, ImmutableBiMap getterToPropertyName, ImmutableMap getterToPropertyType, EclipseHack eclipseHack) { this.errorReporter = errorReporter; this.typeUtils = typeUtils; this.elementUtils = elementUtils; this.builderMethodClassifier = builderMethodClassifier; this.getterToPropertyName = getterToPropertyName; this.getterToPropertyType = getterToPropertyType; this.eclipseHack = eclipseHack; } /** * Information about a property builder, referenced from the autovalue.vm template. A property * called bar (defined by a method bar() or getBar()) can have a property builder called * barBuilder(). For example, if {@code bar()} returns {@code ImmutableSet} then {@code * barBuilder()} might return {@code ImmutableSet.Builder}. */ public static class PropertyBuilder { private final ExecutableElement propertyBuilderMethod; private final String name; private final String builderType; private final TypeMirror builderTypeMirror; private final String initializer; private final String beforeInitDefault; private final String initDefault; private final String builtToBuilder; private final String copyAll; PropertyBuilder( ExecutableElement propertyBuilderMethod, String builderType, TypeMirror builderTypeMirror, String initializer, String beforeInitDefault, String initDefault, String builtToBuilder, String copyAll) { this.propertyBuilderMethod = propertyBuilderMethod; this.name = propertyBuilderMethod.getSimpleName() + "$"; this.builderType = builderType; this.builderTypeMirror = builderTypeMirror; this.initializer = initializer; this.beforeInitDefault = beforeInitDefault; this.initDefault = initDefault; this.builtToBuilder = builtToBuilder; this.copyAll = copyAll; } /** The property builder method, for example {@code barBuilder()}. */ public ExecutableElement getPropertyBuilderMethod() { return propertyBuilderMethod; } public String getAccess() { return SimpleMethod.access(propertyBuilderMethod); } /** The name of the field to hold this builder. */ public String getName() { return name; } /** The type of the builder, for example {@code ImmutableSet.Builder}. */ public String getBuilderType() { return builderType; } TypeMirror getBuilderTypeMirror() { return builderTypeMirror; } /** An initializer for the builder field, for example {@code ImmutableSet.builder()}. */ public String getInitializer() { return initializer; } /** * An empty string, or a complete statement to be included before the expression returned by * {@link #getInitDefault()}. */ public String getBeforeInitDefault() { return beforeInitDefault; } /** * An expression to return a default instance of the type that this builder builds. For example, * if this is an {@code ImmutableList} then the method {@code ImmutableList.of()} will * correctly return an empty {@code ImmutableList}, assuming the appropriate context for * type inference. The expression here can assume that the statement from {@link * #getBeforeInitDefault} has preceded it. */ public String getInitDefault() { return initDefault; } /** * A method to convert the built type back into a builder. Unfortunately Guava collections don't * have this (you can't say {@code myImmutableMap.toBuilder()}), but for other types such as * {@code @AutoValue} types this is {@code toBuilder()}. */ public String getBuiltToBuilder() { return builtToBuilder; } /** * The method to copy another collection into this builder. It is {@code addAll} for * one-dimensional collections like {@code ImmutableList} and {@code ImmutableSet}, and it is * {@code putAll} for two-dimensional collections like {@code ImmutableMap} and {@code * ImmutableTable}. */ public String getCopyAll() { return copyAll; } } // Our @AutoValue class `Foo` has a property `Bar bar()` or `Bar getBar()` and we've encountered // a builder method like `BarBuilder barBuilder()`. Here `BarBuilder` can have any name (its name // doesn't have to be the name of `Bar` with `Builder` stuck on the end), but `barBuilder()` does // have to be the name of the property with `Builder` stuck on the end. The requirements for the // `BarBuilder` type are: // (1) It must have an instance method called `build()` that returns `Bar`. If the type of // `bar()` is `Bar` then the type of `build()` must be `Bar`. // (2) `BarBuilder` must have a public no-arg constructor, or `Bar` must have a static method // `naturalOrder(), `builder()`, or `newBuilder()` that returns `BarBuilder`. The // `naturalOrder()` case is specifically for ImmutableSortedSet and ImmutableSortedMap. // (3) If `Foo` has a `toBuilder()` method, or if we have both `barBuilder()` and `setBar(Bar)` // methods, then `Bar` must have an instance method `BarBuilder toBuilder()`, or `BarBuilder` // must have an `addAll` or `putAll` method that accepts an argument of type `Bar`. // // This method outputs an error and returns Optional.empty() if the barBuilder() method has a // problem. Optional makePropertyBuilder(ExecutableElement method, String property) { TypeMirror barBuilderTypeMirror = builderMethodClassifier.builderMethodReturnType(method); if (barBuilderTypeMirror.getKind() != TypeKind.DECLARED) { errorReporter.reportError( "Method looks like a property builder, but its return type is not a class or interface", method); return Optional.empty(); } DeclaredType barBuilderDeclaredType = MoreTypes.asDeclared(barBuilderTypeMirror); TypeElement barBuilderTypeElement = MoreTypes.asTypeElement(barBuilderTypeMirror); Map barBuilderNoArgMethods = noArgMethodsOf(barBuilderTypeElement); ExecutableElement barGetter = getterToPropertyName.inverse().get(property); TypeMirror barTypeMirror = getterToPropertyType.get(barGetter); if (barTypeMirror.getKind() != TypeKind.DECLARED) { errorReporter.reportError( "Method looks like a property builder, but the type of property " + property + " is not a class or interface", method); return Optional.empty(); } if (isNullable(barGetter)) { errorReporter.reportError( "Property " + property + " has a property builder so it cannot be @Nullable", barGetter); } TypeElement barTypeElement = MoreTypes.asTypeElement(barTypeMirror); Map barNoArgMethods = noArgMethodsOf(barTypeElement); // Condition (1), must have build() method returning Bar. ExecutableElement build = barBuilderNoArgMethods.get("build"); if (build == null || build.getModifiers().contains(Modifier.STATIC)) { errorReporter.reportError( "Method looks like a property builder, but it returns " + barBuilderTypeElement + " which does not have a non-static build() method", method); return Optional.empty(); } // We've determined that `BarBuilder` has a method `build()`. But it must return `Bar`. // And if the type of `bar()` is Bar then `BarBuilder.build()` must return Bar. TypeMirror buildType = eclipseHack.methodReturnType(build, barBuilderDeclaredType); if (!MoreTypes.equivalence().equivalent(barTypeMirror, buildType)) { errorReporter.reportError( "Property builder for " + property + " has type " + barBuilderTypeElement + " whose build() method returns " + buildType + " instead of " + barTypeMirror, method); return Optional.empty(); } Optional maybeBuilderMaker = builderMaker(barNoArgMethods, barBuilderTypeElement); if (!maybeBuilderMaker.isPresent()) { errorReporter.reportError( "Method looks like a property builder, but its type " + barBuilderTypeElement + " does not have a public constructor and " + barTypeElement + " does not have a static builder() or newBuilder() method that returns " + barBuilderTypeElement, method); return Optional.empty(); } ExecutableElement builderMaker = maybeBuilderMaker.get(); String barBuilderType = TypeEncoder.encodeWithAnnotations(barBuilderTypeMirror); String rawBarType = TypeEncoder.encodeRaw(barTypeMirror); String initializer = (builderMaker.getKind() == ElementKind.CONSTRUCTOR) ? "new " + barBuilderType + "()" : rawBarType + "." + builderMaker.getSimpleName() + "()"; String builtToBuilder = null; String copyAll = null; ExecutableElement toBuilder = barNoArgMethods.get("toBuilder"); if (toBuilder != null && !toBuilder.getModifiers().contains(Modifier.STATIC) && typeUtils.isAssignable( typeUtils.erasure(toBuilder.getReturnType()), typeUtils.erasure(barBuilderTypeMirror))) { builtToBuilder = toBuilder.getSimpleName().toString(); } else { Optional maybeCopyAll = addAllPutAll(barBuilderTypeElement, barBuilderDeclaredType, barTypeMirror); if (maybeCopyAll.isPresent()) { copyAll = maybeCopyAll.get().getSimpleName().toString(); } } ExecutableElement barOf = barNoArgMethods.get("of"); boolean hasOf = (barOf != null && barOf.getModifiers().contains(Modifier.STATIC)); // An expression (initDefault) to make a default one of these, plus optionally a statement // (beforeInitDefault) that prepares the expression. For a collection, beforeInitDefault is // empty and initDefault is (e.g.) `ImmutableList.of()`. For a nested value type, // beforeInitDefault is (e.g.) // `NestedAutoValueType.Builder foo$builder = NestedAutoValueType.builder();` // and initDefault is `foo$builder.build();`. The reason for the separate statement is to // exploit type inference rather than having to write `NestedAutoValueType.build();`. String beforeInitDefault; String initDefault; if (hasOf) { beforeInitDefault = ""; initDefault = rawBarType + ".of()"; } else { String localBuilder = property + "$builder"; beforeInitDefault = barBuilderType + " " + localBuilder + " = " + initializer + ";"; initDefault = localBuilder + ".build()"; } PropertyBuilder propertyBuilder = new PropertyBuilder( method, barBuilderType, barBuilderTypeMirror, initializer, beforeInitDefault, initDefault, builtToBuilder, copyAll); return Optional.of(propertyBuilder); } private static final ImmutableSet BUILDER_METHOD_NAMES = ImmutableSet.of("naturalOrder", "builder", "newBuilder"); // (2) `BarBuilder must have a public no-arg constructor, or `Bar` must have a visible static // method `naturalOrder(), `builder()`, or `newBuilder()` that returns `BarBuilder`. private Optional builderMaker( Map barNoArgMethods, TypeElement barBuilderTypeElement) { for (String builderMethodName : BUILDER_METHOD_NAMES) { ExecutableElement method = barNoArgMethods.get(builderMethodName); if (method != null && method.getModifiers().contains(Modifier.STATIC) && typeUtils.isSameType( typeUtils.erasure(method.getReturnType()), typeUtils.erasure(barBuilderTypeElement.asType()))) { // TODO(emcmanus): check visibility. We don't want to require public for @AutoValue // builders. By not checking visibility we risk accepting something as a builder maker // and then failing when the generated code tries to call Bar.builder(). But the risk // seems small. return Optional.of(method); } } return ElementFilter.constructorsIn(barBuilderTypeElement.getEnclosedElements()) .stream() .filter(c -> c.getParameters().isEmpty()) .filter(c -> c.getModifiers().contains(Modifier.PUBLIC)) .findFirst(); } private Map noArgMethodsOf(TypeElement type) { // Can't easily use ImmutableMap here because getAllMembers could return more than one method // with the same name. Map methods = new LinkedHashMap<>(); for (ExecutableElement method : ElementFilter.methodsIn(elementUtils.getAllMembers(type))) { if (method.getParameters().isEmpty() && !isStaticInterfaceMethodNotIn(method, type)) { methods.put(method.getSimpleName().toString(), method); } } return methods; } // Work around an Eclipse compiler bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=547185 // The result of Elements.getAllMembers includes static methods declared in superinterfaces. // That's wrong because those aren't inherited. So this method checks whether the given method is // a static interface method not in the given type. private static boolean isStaticInterfaceMethodNotIn(ExecutableElement method, TypeElement type) { return method.getModifiers().contains(Modifier.STATIC) && !method.getEnclosingElement().equals(type) && method.getEnclosingElement().getKind().equals(ElementKind.INTERFACE); } private static final ImmutableSet ADD_ALL_PUT_ALL = ImmutableSet.of("addAll", "putAll"); // We have `Bar bar()` and `Foo.Builder toBuilder()` in the @AutoValue type Foo, and we have // `BarBuilder barBuilder()` in Foo.Builder. That means that we need to be able to make a // `BarBuilder` from a `Bar` as part of the implementation of `Foo.toBuilder()`. We can do that // if `Bar` has a method `BarBuilder toBuilder()`, but what if it doesn't? For example, Guava's // `ImmutableList` doesn't have a method `ImmutableList.Builder toBuilder()`. So we also allow it // to work if `BarBuilder` has a method `addAll(T)` or `putAll(T)`, where `Bar` is assignable to // `T`. `ImmutableList.Builder` does have a method `addAll(Iterable)` and // `ImmutableList` is assignable to `Iterable`, so that works. private Optional addAllPutAll( TypeElement barBuilderTypeElement, DeclaredType barBuilderDeclaredType, TypeMirror barTypeMirror) { return MoreElements.getLocalAndInheritedMethods(barBuilderTypeElement, typeUtils, elementUtils) .stream() .filter( method -> ADD_ALL_PUT_ALL.contains(method.getSimpleName().toString()) && method.getParameters().size() == 1) .filter( method -> { ExecutableType methodMirror = MoreTypes.asExecutable(typeUtils.asMemberOf(barBuilderDeclaredType, method)); return typeUtils.isAssignable(barTypeMirror, methodMirror.getParameterTypes().get(0)); }) .findFirst(); } private static boolean isNullable(ExecutableElement getter) { List> annotationLists = ImmutableList.of( getter.getAnnotationMirrors(), getter.getReturnType().getAnnotationMirrors()); for (List annotations : annotationLists) { for (AnnotationMirror annotation : annotations) { if (annotation.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) { return true; } } } return false; } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/PropertyNames.java000066400000000000000000000043301365703632600330120ustar00rootroot00000000000000/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import com.google.common.base.Strings; /** Helper methods to create property names. */ class PropertyNames { /** * Returns the {@code propertyName} with its first character in lower case, but leaves it intact * if it starts with two capitals. * *

      For consistency with JavaBeans, a getter called {@code getHTMLPage()} defines a property * called {@code HTMLPage}. The * rule is: the name of the property is the part after {@code get} or {@code * is}, with the first letter lowercased unless the first two letters are uppercase. This * works well for the {@code HTMLPage} example, but in these more enlightened times we use {@code * HtmlPage} anyway, so the special behaviour is not useful, and of course it behaves poorly with * examples like {@code OAuth}. Nevertheless, we preserve it for compatibility. */ static String decapitalizeLikeJavaBeans(String propertyName) { if (propertyName != null && propertyName.length() >= 2 && Character.isUpperCase(propertyName.charAt(0)) && Character.isUpperCase(propertyName.charAt(1))) { return propertyName; } return decapitalizeNormally(propertyName); } /** * Returns the {@code propertyName} with its first character in lower case. */ static String decapitalizeNormally(String propertyName) { if (Strings.isNullOrEmpty(propertyName)) { return propertyName; } return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/Reformatter.java000066400000000000000000000066401365703632600325020ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; /** * Postprocessor that runs over the output of the template engine in order to make it look nicer. * Mostly, this involves removing surplus horizontal and vertical space. * * @author emcmanus@google.com (Éamonn McManus) */ class Reformatter { static String fixup(String s) { StringBuilder out = new StringBuilder(); JavaScanner scanner = new JavaScanner(s); s = scanner.string(); int len = s.length(); for (int start = 0, previous = 0, braces = 0, parens = 0, end = 0; start < len; previous = start, start = end) { end = scanner.tokenEnd(start); // The tokenized string always ends with \n so we can usually look at s.charAt(end) without // worrying about going past the end of the string. switch (s.charAt(start)) { case '(': parens++; break; case ')': parens--; break; case '{': braces++; break; case '}': braces--; break; case ' ': // This token is a string of consecutive spaces that is not at the start of a line. // Consecutive spaces at the start of a line are attached to the previous newline, and // we don't expect the first line to start with spaces. So we are going to compress this // into just one space, and we are going to delete it entirely if it follows '(' or // precedes a newline or one of the punctuation characters here. if (s.charAt(previous) != '(' && "\n.,;)".indexOf(s.charAt(end)) < 0) { out.append(' '); } continue; case '\n': // This token is a newline plus any following spaces (the indentation of the next line). // If it is followed by something other than a newline then we will output it. Otherwise, // it is part of a sequence of newlines but it is not the last one. If this is a context // where we delete blank lines, or if this is not the first new line in the sequence, or // if we are at the start of the file, we will delete this one. Otherwise we will output a // single newline with no following indentation. Contexts where we delete blank lines are // inside parentheses or inside more than one set of braces. if (end < len && s.charAt(end) != '\n') { if (out.length() == 0) { // Omit newlines at the very start of the file. start++; } break; // Output the newline and its following indentation. } if (parens == 0 && braces < 2 && s.charAt(previous) != '\n' && out.length() > 0) { out.append('\n'); } continue; default: break; } out.append(s, start, end); } return out.toString(); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/SimpleMethod.java000066400000000000000000000040131365703632600325720ustar00rootroot00000000000000/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import java.util.Set; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; /** * A method on an {@code @AutoValue} or {@code AutoOneOf} class that has no specific attached * information, such as a {@code toBuilder()} method, or a {@code build()} method, where only the * name and access type is needed in context. * *

      It implements JavaBean-style getters which means it can be referenced from templates, for * example {@code $method.access}. This template access means that the class and its getters must be * public. */ public final class SimpleMethod { private final String access; private final String name; SimpleMethod(ExecutableElement method) { this.access = access(method); this.name = method.getSimpleName().toString(); } public String getAccess() { return access; } public String getName() { return name; } /** * Returns an appropriate string to be used in code for the access specification of the given * method. This will be {@code public} or {@code protected} followed by a space, or the empty * string for default access. */ static String access(ExecutableElement method) { Set mods = method.getModifiers(); if (mods.contains(Modifier.PUBLIC)) { return "public "; } else if (mods.contains(Modifier.PROTECTED)) { return "protected "; } else { return ""; } } } SimpleServiceLoader.java000066400000000000000000000101141365703632600340210ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/* * Copyright 2019 Google LLC * * 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.google.auto.value.processor; import static java.util.stream.Collectors.toList; import com.google.common.collect.ImmutableList; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.ServiceConfigurationError; /** * A replacement for {@link java.util.ServiceLoader} that avoids certain long-standing bugs. This * simpler implementation does not bother with lazy loading but returns all the service * implementations in one list. It makes sure that {@link URLConnection#setUseCaches} is called to * turn off jar caching, since that tends to lead to problems in versions before JDK 9. * * @see Issue #718 * @see JDK-8156014 */ public final class SimpleServiceLoader { private SimpleServiceLoader() {} public static ImmutableList load(Class service, ClassLoader loader) { String resourceName = "META-INF/services/" + service.getName(); List resourceUrls; try { resourceUrls = Collections.list(loader.getResources(resourceName)); } catch (IOException e) { throw new ServiceConfigurationError("Could not look up " + resourceName, e); } ImmutableList.Builder providers = ImmutableList.builder(); for (URL resourceUrl : resourceUrls) { try { providers.addAll(providersFromUrl(resourceUrl, service, loader)); } catch (IOException e) { throw new ServiceConfigurationError("Could not read " + resourceUrl, e); } } return providers.build(); } private static ImmutableList providersFromUrl( URL resourceUrl, Class service, ClassLoader loader) throws IOException { ImmutableList.Builder providers = ImmutableList.builder(); URLConnection urlConnection = resourceUrl.openConnection(); urlConnection.setUseCaches(false); try (InputStream in = urlConnection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { for (String line : reader.lines().collect(toList())) { Optional maybeClassName = parseClassName(line); if (maybeClassName.isPresent()) { String className = maybeClassName.get(); Class c; try { c = Class.forName(className, false, loader); } catch (ClassNotFoundException e) { throw new ServiceConfigurationError("Could not load " + className, e); } if (!service.isAssignableFrom(c)) { throw new ServiceConfigurationError( "Class " + className + " is not assignable to " + service.getName()); } try { Object provider = c.getConstructor().newInstance(); providers.add(service.cast(provider)); } catch (ReflectiveOperationException e) { throw new ServiceConfigurationError("Could not construct " + className, e); } } } return providers.build(); } } private static Optional parseClassName(String line) { int hash = line.indexOf('#'); if (hash >= 0) { line = line.substring(0, hash); } line = line.trim(); return line.isEmpty() ? Optional.empty() : Optional.of(line); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/TemplateVars.java000066400000000000000000000222051365703632600326120ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.escapevelocity.Template; import java.io.File; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.TreeMap; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * A template and a set of variables to be substituted into that template. A concrete subclass of * this class defines a set of fields that are template variables, and an implementation of the * {@link #parsedTemplate()} method which is the template to substitute them into. Once the values * of the fields have been assigned, the {@link #toText()} method returns the result of substituting * them into the template. * *

      The subclass may be a direct subclass of this class or a more distant descendant. Every field * in the starting class and its ancestors up to this class will be included. Fields cannot be * static unless they are also final. They cannot be private, though they can be package-private if * the class is in the same package as this class. They cannot be primitive or null, so that there * is a clear indication when a field has not been set. * * @author Éamonn McManus */ abstract class TemplateVars { abstract Template parsedTemplate(); private final ImmutableList fields; TemplateVars() { this.fields = getFields(getClass()); } private static ImmutableList getFields(Class c) { ImmutableList.Builder fieldsBuilder = ImmutableList.builder(); while (c != TemplateVars.class) { addFields(fieldsBuilder, c.getDeclaredFields()); c = c.getSuperclass(); } return fieldsBuilder.build(); } private static void addFields( ImmutableList.Builder fieldsBuilder, Field[] declaredFields) { for (Field field : declaredFields) { if (field.isSynthetic() || isStaticFinal(field)) { continue; } if (Modifier.isPrivate(field.getModifiers())) { throw new IllegalArgumentException("Field cannot be private: " + field); } if (Modifier.isStatic(field.getModifiers())) { throw new IllegalArgumentException("Field cannot be static unless also final: " + field); } if (field.getType().isPrimitive()) { throw new IllegalArgumentException("Field cannot be primitive: " + field); } fieldsBuilder.add(field); } } /** * Returns the result of substituting the variables defined by the fields of this class (a * concrete subclass of TemplateVars) into the template returned by {@link #parsedTemplate()}. */ String toText() { Map vars = toVars(); return parsedTemplate().evaluate(vars); } private Map toVars() { Map vars = new TreeMap<>(); for (Field field : fields) { Object value = fieldValue(field, this); if (value == null) { throw new IllegalArgumentException("Field cannot be null (was it set?): " + field); } Object old = vars.put(field.getName(), value); if (old != null) { throw new IllegalArgumentException("Two fields called " + field.getName() + "?!"); } } return ImmutableMap.copyOf(vars); } static Template parsedTemplateForResource(String resourceName) { try { return Template.parseFrom(resourceName, TemplateVars::readerFromResource); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } catch (IOException | NullPointerException | IllegalStateException e) { // https://github.com/google/auto/pull/439 says that we can get NullPointerException. // https://github.com/google/auto/issues/715 says that we can get IllegalStateException return retryParseAfterException(resourceName, e); } } private static Template retryParseAfterException(String resourceName, Exception exception) { try { return Template.parseFrom(resourceName, TemplateVars::readerFromUrl); } catch (IOException t) { // Chain the original exception so we can see both problems. Throwables.getRootCause(exception).initCause(t); throw new AssertionError(exception); } } private static Reader readerFromResource(String resourceName) { InputStream in = TemplateVars.class.getResourceAsStream(resourceName); if (in == null) { throw new IllegalArgumentException("Could not find resource: " + resourceName); } return new InputStreamReader(in, StandardCharsets.UTF_8); } // This is an ugly workaround for https://bugs.openjdk.java.net/browse/JDK-6947916, as // reported in https://github.com/google/auto/issues/365. // The issue is that sometimes the InputStream returned by JarURLCollection.getInputStream() // can be closed prematurely, which leads to an IOException saying "Stream closed". // We catch all IOExceptions, and fall back on logic that opens the jar file directly and // loads the resource from it. Since that doesn't use JarURLConnection, it shouldn't be // susceptible to the same bug. We only use this as fallback logic rather than doing it always, // because jars are memory-mapped by URLClassLoader, so loading a resource in the usual way // through the getResourceAsStream should be a lot more efficient than reopening the jar. private static Reader readerFromUrl(String resourceName) throws IOException { URL resourceUrl = TemplateVars.class.getResource(resourceName); if (resourceUrl == null) { // This is unlikely, since getResourceAsStream has already succeeded for the same resource. throw new IllegalArgumentException("Could not find resource: " + resourceName); } InputStream in; try { if (resourceUrl.getProtocol().equalsIgnoreCase("file")) { in = inputStreamFromFile(resourceUrl); } else if (resourceUrl.getProtocol().equalsIgnoreCase("jar")) { in = inputStreamFromJar(resourceUrl); } else { throw new AssertionError("Template fallback logic fails for: " + resourceUrl); } } catch (URISyntaxException e) { throw new IOException(e); } return new InputStreamReader(in, StandardCharsets.UTF_8); } private static InputStream inputStreamFromJar(URL resourceUrl) throws URISyntaxException, IOException { // Jar URLs look like this: jar:file:/path/to/file.jar!/entry/within/jar // So take apart the URL to open the jar /path/to/file.jar and read the entry // entry/within/jar from it. String resourceUrlString = resourceUrl.toString().substring("jar:".length()); int bang = resourceUrlString.lastIndexOf('!'); String entryName = resourceUrlString.substring(bang + 1); if (entryName.startsWith("/")) { entryName = entryName.substring(1); } URI jarUri = new URI(resourceUrlString.substring(0, bang)); JarFile jar = new JarFile(new File(jarUri)); JarEntry entry = jar.getJarEntry(entryName); InputStream in = jar.getInputStream(entry); // We have to be careful not to close the JarFile before the stream has been read, because // that would also close the stream. So we defer closing the JarFile until the stream is closed. return new FilterInputStream(in) { @Override public void close() throws IOException { super.close(); jar.close(); } }; } // We don't really expect this case to arise, since the bug we're working around concerns jars // not individual files. However, when running the test for this workaround from Maven, we do // have files. That does mean the test is basically useless there, but Google's internal build // system does run it using a jar, so we do have coverage. private static InputStream inputStreamFromFile(URL resourceUrl) throws IOException, URISyntaxException { File resourceFile = new File(resourceUrl.toURI()); return new FileInputStream(resourceFile); } private static Object fieldValue(Field field, Object container) { try { return field.get(container); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } private static boolean isStaticFinal(Field field) { int modifiers = field.getModifiers(); return Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java000066400000000000000000000524111365703632600324260ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.value.processor; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static java.util.stream.Collectors.toList; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.OptionalInt; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; 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.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; /** * Encodes types so they can later be decoded to incorporate imports. * *

      The idea is that types that appear in generated source code use {@link #encode}, which will * spell out a type like {@code java.util.List}, except that wherever a * class name appears it is replaced by a special token. So the spelling might actually be {@code * `java.util.List`}. Then once the entire class has been generated, * {@code #decode} scans for these tokens to determine what classes need to be imported, and * replaces the tokens with the correct spelling given the imports. So here, {@code java.util.List} * would be imported, and the final spelling would be {@code List} (knowing that * {@code Number} is implicitly imported). The special token {@code `import`} marks where the * imports should be, and {@link #decode} replaces it with the correct list of imports. * *

      The funky syntax for type annotations on qualified type names requires an adjustment to this * scheme. {@code `«java.util.Map`} stands for {@code java.util.} and {@code `»java.util.Map`} * stands for {@code Map}. If {@code java.util.Map} is imported, then {@code `«java.util.Map`} will * eventually be empty, but if {@code java.util.Map} is not imported (perhaps because there is * another {@code Map} in scope) then {@code `«java.util.Map`} will be {@code java.util.}. The end * result is that the code can contain {@code `«java.util.Map`@`javax.annotation.Nullable` * `»java.util.Map`}. That might decode to {@code @Nullable Map} or to {@code java.util.@Nullable * Map} or even to {@code java.util.@javax.annotation.Nullable Map}. * * @author emcmanus@google.com (Éamonn McManus) */ final class TypeEncoder { private TypeEncoder() {} // There are no instances of this class. private static final EncodingTypeVisitor ENCODING_TYPE_VISITOR = new EncodingTypeVisitor(); private static final RawEncodingTypeVisitor RAW_ENCODING_TYPE_VISITOR = new RawEncodingTypeVisitor(); /** * Returns the encoding for the given type, where class names are marked by special tokens. The * encoding for {@code int} will be {@code int}, but the encoding for {@code * java.util.List} will be {@code `java.util.List`<`java.lang.Integer`>}. */ static String encode(TypeMirror type) { StringBuilder sb = new StringBuilder(); return type.accept(ENCODING_TYPE_VISITOR, sb).toString(); } /** * Like {@link #encode}, except that only the raw type is encoded. So if the given type is {@code * java.util.List} the result will be {@code `java.util.List`}. */ static String encodeRaw(TypeMirror type) { StringBuilder sb = new StringBuilder(); return type.accept(RAW_ENCODING_TYPE_VISITOR, sb).toString(); } /** * Encodes the given type and its type annotations. The class comment for {@link TypeEncoder} * covers the details of annotation encoding. */ static String encodeWithAnnotations(TypeMirror type) { return encodeWithAnnotations(type, ImmutableSet.of()); } /** * Encodes the given type and its type annotations. The class comment for {@link TypeEncoder} * covers the details of annotation encoding. * * @param excludedAnnotationTypes annotations not to include in the encoding. For example, if * {@code com.example.Nullable} is in this set then the encoding will not include this * {@code @Nullable} annotation. */ static String encodeWithAnnotations(TypeMirror type, Set excludedAnnotationTypes) { StringBuilder sb = new StringBuilder(); return new AnnotatedEncodingTypeVisitor(excludedAnnotationTypes).visit2(type, sb).toString(); } /** * Decodes the given string, respelling class names appropriately. The text is scanned for tokens * like {@code `java.util.Locale`} or {@code `«java.util.Locale`} to determine which classes are * referenced. An appropriate set of imports is computed based on the set of those types. If the * special token {@code `import`} appears in {@code text} then it will be replaced by this set of * import statements. Then all of the tokens are replaced by the class names they represent, * spelled appropriately given the import statements. * * @param text the text to be decoded. * @param packageName the package of the generated class. Other classes in the same package do not * need to be imported. * @param baseType a class or interface that the generated class inherits from. Nested classes in * that type do not need to be imported, and if another class has the same name as one of * those nested classes then it will need to be qualified. */ static String decode( String text, ProcessingEnvironment processingEnv, String packageName, TypeMirror baseType) { return decode( text, processingEnv.getElementUtils(), processingEnv.getTypeUtils(), packageName, baseType); } static String decode( String text, Elements elementUtils, Types typeUtils, String pkg, TypeMirror baseType) { TypeRewriter typeRewriter = new TypeRewriter(text, elementUtils, typeUtils, pkg, baseType); return typeRewriter.rewrite(); } private static String className(DeclaredType declaredType) { return MoreElements.asType(declaredType.asElement()).getQualifiedName().toString(); } /** * Returns the formal type parameters of the given type. If we have {@code @AutoValue abstract * class Foo} then this method will return an encoding of {@code } for {@code Foo}. Likewise it will return an encoding of the angle-bracket part of: *
      * {@code Foo}
      * {@code Foo}
      * {@code Foo>}
      * {@code Foo>}. * *

      The encoding is simply that classes in the "extends" part are marked, so the examples will * actually look something like this:
      * {@code <`bar.baz.SomeClass`>}
      * {@code }
      * {@code >}
      * {@code >}. */ static String formalTypeParametersString(TypeElement type) { List typeParameters = type.getTypeParameters(); if (typeParameters.isEmpty()) { return ""; } else { StringBuilder sb = new StringBuilder("<"); String sep = ""; for (TypeParameterElement typeParameter : typeParameters) { sb.append(sep); sep = ", "; appendTypeParameterWithBounds(typeParameter, sb); } return sb.append(">").toString(); } } private static void appendTypeParameterWithBounds( TypeParameterElement typeParameter, StringBuilder sb) { appendAnnotations(typeParameter.getAnnotationMirrors(), sb); sb.append(typeParameter.getSimpleName()); String sep = " extends "; for (TypeMirror bound : typeParameter.getBounds()) { if (!isUnannotatedJavaLangObject(bound)) { sb.append(sep); sep = " & "; sb.append(encodeWithAnnotations(bound)); } } } // We can omit "extends Object" from a type bound, but not "extends @NullableType Object". private static boolean isUnannotatedJavaLangObject(TypeMirror type) { return type.getKind().equals(TypeKind.DECLARED) && type.getAnnotationMirrors().isEmpty() && MoreTypes.asTypeElement(type).getQualifiedName().contentEquals("java.lang.Object"); } private static void appendAnnotations( List annotationMirrors, StringBuilder sb) { for (AnnotationMirror annotationMirror : annotationMirrors) { sb.append(AnnotationOutput.sourceFormForAnnotation(annotationMirror)).append(" "); } } /** * Converts a type into a string, using standard Java syntax, except that every class name is * wrapped in backquotes, like {@code `java.util.List`}. */ private static class EncodingTypeVisitor extends SimpleTypeVisitor8 { /** * Equivalent to {@code visit(type, sb)} or {@code type.accept(sb)}, except that it fixes a bug * with javac versions up to JDK 8, whereby if the type is a {@code DeclaredType} then the * visitor is called with a version of the type where any annotations have been lost. We can't * override {@code visit} because it is final. */ StringBuilder visit2(TypeMirror type, StringBuilder sb) { if (type.getKind().equals(TypeKind.DECLARED)) { // There's no point in using MoreTypes.asDeclared here, and in fact we can't, because it // uses a visitor, so it would trigger the bug we're working around. return visitDeclared((DeclaredType) type, sb); } else { return visit(type, sb); } } @Override protected StringBuilder defaultAction(TypeMirror type, StringBuilder sb) { return sb.append(type); } @Override public StringBuilder visitArray(ArrayType type, StringBuilder sb) { return visit2(type.getComponentType(), sb).append("[]"); } @Override public StringBuilder visitDeclared(DeclaredType type, StringBuilder sb) { appendTypeName(type, sb); appendTypeArguments(type, sb); return sb; } void appendTypeName(DeclaredType type, StringBuilder sb) { TypeMirror enclosing = EclipseHack.getEnclosingType(type); if (enclosing.getKind().equals(TypeKind.DECLARED)) { // We might have something like com.example.Outer.Inner. We need to encode // com.example.Outer first, producing `com.example.Outer`<`java.lang.Double`>. // Then we can simply add .Inner after that. If Inner has its own type arguments, we'll // add them with appendTypeArguments below. Of course, it's more usual for the outer class // not to have type arguments, but we'll still follow this path if the nested class is an // inner (not static) class. visit2(enclosing, sb); sb.append(".").append(type.asElement().getSimpleName()); } else { sb.append('`').append(className(type)).append('`'); } } void appendTypeArguments(DeclaredType type, StringBuilder sb) { List arguments = type.getTypeArguments(); if (!arguments.isEmpty()) { sb.append("<"); String sep = ""; for (TypeMirror argument : arguments) { sb.append(sep); sep = ", "; visit2(argument, sb); } sb.append(">"); } } @Override public StringBuilder visitWildcard(WildcardType type, StringBuilder sb) { sb.append("?"); TypeMirror extendsBound = type.getExtendsBound(); TypeMirror superBound = type.getSuperBound(); if (superBound != null) { sb.append(" super "); visit2(superBound, sb); } else if (extendsBound != null) { sb.append(" extends "); visit2(extendsBound, sb); } return sb; } @Override public StringBuilder visitError(ErrorType t, StringBuilder p) { throw new MissingTypeException(); } } /** Like {@link EncodingTypeVisitor} except that type parameters are omitted from the result. */ private static class RawEncodingTypeVisitor extends EncodingTypeVisitor { @Override void appendTypeArguments(DeclaredType type, StringBuilder sb) {} } /** * Like {@link EncodingTypeVisitor} except that annotations on the visited type are also included * in the resultant string. Class names in those annotations are also encoded using the {@code * `java.util.List`} form. */ private static class AnnotatedEncodingTypeVisitor extends EncodingTypeVisitor { private final Set excludedAnnotationTypes; AnnotatedEncodingTypeVisitor(Set excludedAnnotationTypes) { this.excludedAnnotationTypes = excludedAnnotationTypes; } private void appendAnnotationsWithExclusions( List annotations, StringBuilder sb) { // Optimization for the very common cases where there are no annotations or there are no // exclusions. if (annotations.isEmpty() || excludedAnnotationTypes.isEmpty()) { appendAnnotations(annotations, sb); return; } List includedAnnotations = annotations.stream() .filter(a -> !excludedAnnotationTypes.contains(a.getAnnotationType())) .collect(toList()); appendAnnotations(includedAnnotations, sb); } @Override public StringBuilder visitPrimitive(PrimitiveType type, StringBuilder sb) { appendAnnotationsWithExclusions(type.getAnnotationMirrors(), sb); // We can't just append type.toString(), because that will also have the annotation, but // without encoding. return sb.append(type.getKind().toString().toLowerCase()); } @Override public StringBuilder visitTypeVariable(TypeVariable type, StringBuilder sb) { appendAnnotationsWithExclusions(type.getAnnotationMirrors(), sb); return sb.append(type.asElement().getSimpleName()); } /** * {@inheritDoc} The result respects the Java syntax, whereby {@code Foo @Bar []} is an * annotation on the array type itself, while {@code @Bar Foo[]} would be an annotation on the * component type. */ @Override public StringBuilder visitArray(ArrayType type, StringBuilder sb) { visit2(type.getComponentType(), sb); List annotationMirrors = type.getAnnotationMirrors(); if (!annotationMirrors.isEmpty()) { sb.append(" "); appendAnnotationsWithExclusions(annotationMirrors, sb); } return sb.append("[]"); } @Override public StringBuilder visitDeclared(DeclaredType type, StringBuilder sb) { List annotationMirrors = type.getAnnotationMirrors(); if (annotationMirrors.isEmpty()) { super.visitDeclared(type, sb); } else { TypeMirror enclosing = EclipseHack.getEnclosingType(type); if (enclosing.getKind().equals(TypeKind.DECLARED)) { // We have something like com.example.Outer.@Annot Inner. // We'll recursively encode com.example.Outer first, // which if it is also annotated might result in a mouthful like // `«com.example.Outer`@`org.annots.Nullable``»com.example.Outer`<`java.lang.Double`> . // That annotation will have been added by a recursive call to this method. // Then we'll add the annotation on the .Inner class, which we know is there because // annotationMirrors is not empty. That means we'll append .@`org.annots.Annot` Inner . visit2(enclosing, sb); sb.append("."); appendAnnotationsWithExclusions(annotationMirrors, sb); sb.append(type.asElement().getSimpleName()); } else { // This isn't an inner class, so we have the simpler (but still complicated) case of // needing to place the annotation correctly in cases like java.util.@Nullable Map . // See the class doc comment for an explanation of « and » here. String className = className(type); sb.append("`«").append(className).append("`"); appendAnnotationsWithExclusions(annotationMirrors, sb); sb.append("`»").append(className).append("`"); } appendTypeArguments(type, sb); } return sb; } } private static class TypeRewriter { private final String text; private final int textLength; private final JavaScanner scanner; private final Elements elementUtils; private final Types typeUtils; private final String packageName; private final TypeMirror baseType; TypeRewriter( String text, Elements elementUtils, Types typeUtils, String pkg, TypeMirror baseType) { this.text = text; this.textLength = text.length(); this.scanner = new JavaScanner(text); this.elementUtils = elementUtils; this.typeUtils = typeUtils; this.packageName = pkg; this.baseType = baseType; } String rewrite() { // Scan the text to determine what classes are referenced. Set referencedClasses = findReferencedClasses(); // Make a type simplifier based on these referenced types. TypeSimplifier typeSimplifier = new TypeSimplifier(elementUtils, typeUtils, packageName, referencedClasses, baseType); StringBuilder output = new StringBuilder(); int copyStart; // Replace the `import` token with the import statements, if it is present. OptionalInt importMarker = findImportMarker(); if (importMarker.isPresent()) { output.append(text, 0, importMarker.getAsInt()); for (String toImport : typeSimplifier.typesToImport()) { output.append("import ").append(toImport).append(";\n"); } copyStart = scanner.tokenEnd(importMarker.getAsInt()); } else { copyStart = 0; } // Replace each of the classname tokens with the appropriate spelling of the classname. int token; for (token = copyStart; token < textLength; token = scanner.tokenEnd(token)) { if (text.charAt(token) == '`') { output.append(text, copyStart, token); decode(output, typeSimplifier, token); copyStart = scanner.tokenEnd(token); } } output.append(text, copyStart, textLength); return output.toString(); } private Set findReferencedClasses() { Set classes = new TypeMirrorSet(); for (int token = 0; token < textLength; token = scanner.tokenEnd(token)) { if (text.charAt(token) == '`' && !text.startsWith("`import`", token)) { String className = classNameAt(token); classes.add(classForName(className)); } } return classes; } private DeclaredType classForName(String className) { TypeElement typeElement = elementUtils.getTypeElement(className); checkState(typeElement != null, "Could not find referenced class %s", className); return MoreTypes.asDeclared(typeElement.asType()); } private void decode(StringBuilder output, TypeSimplifier typeSimplifier, int token) { String className = classNameAt(token); DeclaredType type = classForName(className); String simplified = typeSimplifier.simplifiedClassName(type); int dot; switch (text.charAt(token + 1)) { case '«': // If this is `«java.util.Map` then we want "java.util." here. // That's because this is the first part of something like "java.util.@Nullable Map" // or "java.util.Map.@Nullable Entry". // If there's no dot, then we want nothing here, for "@Nullable Map". dot = simplified.lastIndexOf('.'); output.append(simplified.substring(0, dot + 1)); // correct even if dot == -1 break; case '»': dot = simplified.lastIndexOf('.'); output.append(simplified.substring(dot + 1)); // correct even if dot == -1 break; default: output.append(simplified); break; } } private OptionalInt findImportMarker() { for (int token = 0; token < textLength; token = scanner.tokenEnd(token)) { if (text.startsWith("`import`", token)) { return OptionalInt.of(token); } } return OptionalInt.empty(); } private String classNameAt(int token) { checkArgument(text.charAt(token) == '`'); int end = scanner.tokenEnd(token) - 1; // points to the closing ` int t = token + 1; char c = text.charAt(t); if (c == '«' || c == '»') { t++; } return text.substring(t, end); } } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/TypeMirrorSet.java000066400000000000000000000054031365703632600327740ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import com.google.auto.common.MoreTypes; import com.google.common.base.Equivalence; import com.google.common.collect.ImmutableList; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set; import javax.lang.model.type.TypeMirror; /** * A set of TypeMirror objects. * * @author emcmanus@google.com (Éamonn McManus) */ class TypeMirrorSet extends AbstractSet { private final Set> wrappers = new LinkedHashSet<>(); TypeMirrorSet() {} TypeMirrorSet(Collection types) { addAll(types); } static TypeMirrorSet of(TypeMirror... types) { return new TypeMirrorSet(ImmutableList.copyOf(types)); } private Equivalence.Wrapper wrap(TypeMirror typeMirror) { return MoreTypes.equivalence().wrap(typeMirror); } @Override public boolean add(TypeMirror typeMirror) { return wrappers.add(wrap(typeMirror)); } @Override public Iterator iterator() { final Iterator> iterator = wrappers.iterator(); return new Iterator() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public TypeMirror next() { return iterator.next().get(); } @Override public void remove() { iterator.remove(); } }; } @Override public int size() { return wrappers.size(); } @Override public boolean contains(Object o) { if (o instanceof TypeMirror) { return wrappers.contains(wrap((TypeMirror) o)); } else { return false; } } @Override public boolean remove(Object o) { if (o instanceof TypeMirror) { return wrappers.remove(wrap((TypeMirror) o)); } else { return false; } } @Override public boolean equals(Object o) { if (o instanceof TypeMirrorSet) { TypeMirrorSet that = (TypeMirrorSet) o; return wrappers.equals(that.wrappers); } else { return false; } } @Override public int hashCode() { return wrappers.hashCode(); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java000066400000000000000000000376411365703632600331620ustar00rootroot00000000000000/* * Copyright 2012 Google LLC * * 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.google.auto.value.processor; import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toCollection; import static javax.lang.model.element.Modifier.PRIVATE; import com.google.auto.common.MoreElements; import com.google.common.collect.ImmutableSortedSet; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.lang.model.element.Name; import javax.lang.model.element.NestingKind; import javax.lang.model.element.QualifiedNameable; 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.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.TypeVisitor; import javax.lang.model.type.WildcardType; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; /** * Takes a set of types and a package and determines which of those types can be imported, and how * to spell any of the types in the set given those imports. * * @author emcmanus@google.com (Éamonn McManus) */ final class TypeSimplifier { /** * The spelling that should be used to refer to a given class, and an indication of whether it * should be imported. */ private static class Spelling { final String spelling; final boolean importIt; Spelling(String spelling, boolean importIt) { this.spelling = spelling; this.importIt = importIt; } } private final Map imports; /** * Makes a new simplifier for the given package and set of types. * * @param elementUtils the result of {@code ProcessingEnvironment.getElementUtils()} for the * current annotation processing environment. * @param typeUtils the result of {@code ProcessingEnvironment.getTypeUtils()} for the current * annotation processing environment. * @param packageName the name of the package from which classes will be referenced. Classes that * are in the same package do not need to be imported. * @param types the types that will be referenced. * @param base a base class that the class containing the references will extend. This is needed * because nested classes in that class or one of its ancestors are in scope in the generated * subclass, so a reference to another class with the same name as one of them is ambiguous. * @throws MissingTypeException if one of the input types contains an error (typically, is * undefined). */ TypeSimplifier( Elements elementUtils, Types typeUtils, String packageName, Set types, TypeMirror base) { Set typesPlusBase = new TypeMirrorSet(types); if (base != null) { typesPlusBase.add(base); } Set topLevelTypes = topLevelTypes(typeUtils, typesPlusBase); Set defined = nonPrivateDeclaredTypes(typeUtils, base); this.imports = findImports(elementUtils, typeUtils, packageName, topLevelTypes, defined); } /** * Returns the set of types to import. We import every type that is neither in java.lang nor in * the package containing the AutoValue class, provided that the result refers to the type * unambiguously. For example, if there is a property of type java.util.Map.Entry then we will * import java.util.Map.Entry and refer to the property as Entry. We could also import just * java.util.Map in this case and refer to Map.Entry, but currently we never do that. */ ImmutableSortedSet typesToImport() { ImmutableSortedSet.Builder typesToImport = ImmutableSortedSet.naturalOrder(); for (Map.Entry entry : imports.entrySet()) { if (entry.getValue().importIt) { typesToImport.add(entry.getKey()); } } return typesToImport.build(); } String simplifiedClassName(DeclaredType type) { TypeElement typeElement = MoreElements.asType(type.asElement()); TypeElement top = topLevelType(typeElement); // We always want to write a class name starting from the outermost class. For example, // if the type is java.util.Map.Entry then we will import java.util.Map and write Map.Entry. String topString = top.getQualifiedName().toString(); if (imports.containsKey(topString)) { String suffix = typeElement.getQualifiedName().toString().substring(topString.length()); return imports.get(topString).spelling + suffix; } else { return typeElement.getQualifiedName().toString(); } } // The actual type parameters of the given type. // If we have @AutoValue abstract class Foo then the subclass will be // final class AutoValue_Foo extends Foo. // is the formal type parameter list and // is the actual type parameter list, which is what this method returns. static String actualTypeParametersString(TypeElement type) { List typeParameters = type.getTypeParameters(); if (typeParameters.isEmpty()) { return ""; } else { return typeParameters .stream() .map(e -> e.getSimpleName().toString()) .collect(joining(", ", "<", ">")); } } /** Returns the name of the given type, including any enclosing types but not the package. */ static String classNameOf(TypeElement type) { String name = type.getQualifiedName().toString(); String pkgName = packageNameOf(type); return pkgName.isEmpty() ? name : name.substring(pkgName.length() + 1); } private static TypeElement topLevelType(TypeElement type) { while (type.getNestingKind() != NestingKind.TOP_LEVEL) { type = MoreElements.asType(type.getEnclosingElement()); } return type; } /** * Returns the name of the package that the given type is in. If the type is in the default * (unnamed) package then the name is the empty string. */ static String packageNameOf(TypeElement type) { return MoreElements.getPackage(type).getQualifiedName().toString(); } static String simpleNameOf(String s) { if (s.contains(".")) { return s.substring(s.lastIndexOf('.') + 1); } else { return s; } } /** * Given a set of referenced types, works out which of them should be imported and what the * resulting spelling of each one is. * *

      This method operates on a {@code Set} rather than just a {@code Set} * because it is not strictly possible to determine what part of a fully-qualified type name is * the package and what part is the top-level class. For example, {@code java.util.Map.Entry} is a * class called {@code Map.Entry} in a package called {@code java.util} assuming Java conventions * are being followed, but it could theoretically also be a class called {@code Entry} in a * package called {@code java.util.Map}. Since we are operating as part of the compiler, our goal * should be complete correctness, and the only way to achieve that is to operate on the real * representations of types. * * @param codePackageName The name of the package where the class containing these references is * defined. Other classes within the same package do not need to be imported. * @param referenced The complete set of declared types (classes and interfaces) that will be * referenced in the generated code. * @param defined The complete set of declared types (classes and interfaces) that are defined * within the scope of the generated class (i.e. nested somewhere in its superclass chain, or * in its interface set) * @return a map where the keys are fully-qualified types and the corresponding values indicate * whether the type should be imported, and how the type should be spelled in the source code. */ private static Map findImports( Elements elementUtils, Types typeUtils, String codePackageName, Set referenced, Set defined) { Map imports = new HashMap<>(); Set typesInScope = new TypeMirrorSet(); typesInScope.addAll(referenced); typesInScope.addAll(defined); Set ambiguous = ambiguousNames(typeUtils, typesInScope); for (TypeMirror type : referenced) { TypeElement typeElement = (TypeElement) typeUtils.asElement(type); String fullName = typeElement.getQualifiedName().toString(); String simpleName = typeElement.getSimpleName().toString(); String pkg = packageNameOf(typeElement); boolean importIt; String spelling; if (ambiguous.contains(simpleName)) { importIt = false; spelling = fullName; } else if (pkg.equals("java.lang")) { importIt = false; spelling = javaLangSpelling(elementUtils, codePackageName, typeElement); } else if (pkg.equals(codePackageName)) { importIt = false; spelling = fullName.substring(pkg.isEmpty() ? 0 : pkg.length() + 1); } else { importIt = true; spelling = simpleName; } imports.put(fullName, new Spelling(spelling, importIt)); } return imports; } /** * Handles the tricky case where the class being referred to is in {@code java.lang}, but the * package of the referring code contains another class of the same name. For example, if the * current package is {@code foo.bar} and there is a {@code foo.bar.Compiler}, then we will refer * to {@code java.lang.Compiler} by its full name. The plain name {@code Compiler} would reference * {@code foo.bar.Compiler} in this case. We need to write {@code java.lang.Compiler} even if the * other {@code Compiler} class is not being considered here, so the {@link #ambiguousNames} logic * is not enough. We have to look to see if the class exists. */ private static String javaLangSpelling( Elements elementUtils, String codePackageName, TypeElement typeElement) { // If this is java.lang.Thread.State or the like, we have to look for a clash with Thread. TypeElement topLevelType = topLevelType(typeElement); TypeElement clash = elementUtils.getTypeElement(codePackageName + "." + topLevelType.getSimpleName()); String fullName = typeElement.getQualifiedName().toString(); return (clash == null) ? fullName.substring("java.lang.".length()) : fullName; } /** * Finds the top-level types for all the declared types (classes and interfaces) in the given * {@code Set}. * *

      The returned set contains only top-level types. If we reference {@code java.util.Map.Entry} * then the returned set will contain {@code java.util.Map}. This is because we want to write * {@code Map.Entry} everywhere rather than {@code Entry}. */ private static Set topLevelTypes(Types typeUtil, Set types) { return types .stream() .map(typeMirror -> MoreElements.asType(typeUtil.asElement(typeMirror))) .map(typeElement -> topLevelType(typeElement).asType()) .collect(toCollection(TypeMirrorSet::new)); } /** * Finds all types that are declared with non private visibility by the given {@code TypeMirror}, * any class in its superclass chain, or any interface it implements. */ private static Set nonPrivateDeclaredTypes(Types typeUtils, TypeMirror type) { if (type == null) { return new TypeMirrorSet(); } else { Set declared = new TypeMirrorSet(); declared.add(type); List nestedTypes = ElementFilter.typesIn(typeUtils.asElement(type).getEnclosedElements()); for (TypeElement nestedType : nestedTypes) { if (!nestedType.getModifiers().contains(PRIVATE)) { declared.add(nestedType.asType()); } } for (TypeMirror supertype : typeUtils.directSupertypes(type)) { declared.addAll(nonPrivateDeclaredTypes(typeUtils, supertype)); } return declared; } } private static Set ambiguousNames(Types typeUtils, Set types) { Set ambiguous = new HashSet<>(); Map simpleNamesToQualifiedNames = new HashMap<>(); for (TypeMirror type : types) { if (type.getKind() == TypeKind.ERROR) { throw new MissingTypeException(); } String simpleName = typeUtils.asElement(type).getSimpleName().toString(); /* * Compare by qualified names, because in Eclipse JDT, if Java 8 type annotations are used, * the same (unannotated) type may appear multiple times in the Set. * TODO(emcmanus): investigate further, because this might cause problems elsewhere. */ Name qualifiedName = ((QualifiedNameable) typeUtils.asElement(type)).getQualifiedName(); Name previous = simpleNamesToQualifiedNames.put(simpleName, qualifiedName); if (previous != null && !previous.equals(qualifiedName)) { ambiguous.add(simpleName); } } return ambiguous; } /** * Returns true if casting to the given type will elicit an unchecked warning from the compiler. * Only generic types such as {@code List} produce such warnings. There will be no warning * if the type's only generic parameters are simple wildcards, as in {@code Map}. */ static boolean isCastingUnchecked(TypeMirror type) { return CASTING_UNCHECKED_VISITOR.visit(type, null); } private static final TypeVisitor CASTING_UNCHECKED_VISITOR = new SimpleTypeVisitor8(false) { @Override public Boolean visitUnknown(TypeMirror t, Void p) { // We don't know whether casting is unchecked for this mysterious type but assume it is, // so we will insert a possibly unnecessary @SuppressWarnings("unchecked"). return true; } @Override public Boolean visitArray(ArrayType t, Void p) { return visit(t.getComponentType(), p); } @Override public Boolean visitDeclared(DeclaredType t, Void p) { return t.getTypeArguments().stream().anyMatch(TypeSimplifier::uncheckedTypeArgument); } @Override public Boolean visitTypeVariable(TypeVariable t, Void p) { return true; } }; // If a type has a type argument, then casting to the type is unchecked, except if the argument // is or . The same applies to all type arguments, so casting to Map // does not produce an unchecked warning for example. private static boolean uncheckedTypeArgument(TypeMirror arg) { if (arg.getKind() == TypeKind.WILDCARD) { WildcardType wildcard = (WildcardType) arg; if (wildcard.getExtendsBound() == null || isJavaLangObject(wildcard.getExtendsBound())) { // This is , unless there's a super bound, in which case it is and // is erased. return (wildcard.getSuperBound() != null); } } return true; } private static boolean isJavaLangObject(TypeMirror type) { if (type.getKind() != TypeKind.DECLARED) { return false; } DeclaredType declaredType = (DeclaredType) type; TypeElement typeElement = (TypeElement) declaredType.asElement(); return typeElement.getQualifiedName().contentEquals("java.lang.Object"); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/TypeVariables.java000066400000000000000000000265001365703632600327570ustar00rootroot00000000000000/* * Copyright 2019 Google LLC * * 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.google.auto.value.processor; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.base.Equivalence; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import javax.lang.model.element.ExecutableElement; 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.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.type.WildcardType; import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor8; import javax.lang.model.util.Types; /** * Methods for handling type variables. */ final class TypeVariables { private TypeVariables() {} /** * Returns a map from methods to return types, where the return types are not necessarily the * original return types of the methods. Consider this example: * *

         * @AutoValue class {@code Foo} {
         *   abstract T getFoo();
         *
         *   @AutoValue.Builder
         *   abstract class {@code Builder} {
         *     abstract Builder setFoo(T t);
         *     abstract {@code Foo} build();
         *   }
         * }
         * 
      * * We want to be able to check that the parameter type of {@code setFoo} is the same as the * return type of {@code getFoo}. But in fact it isn't, because the {@code T} of {@code Foo} * is not the same as the {@code T} of {@code Foo.Builder}. So we create a parallel * {@code Foo} where the {@code T} is the one from {@code Foo.Builder}. That way the * types do correspond. This method then returns the return types of the given methods as they * appear in that parallel class, meaning the type given for {@code getFoo()} is the {@code T} of * {@code Foo.Builder}. * *

      We do the rewrite this way around (applying the type parameter from {@code Foo.Builder} to * {@code Foo}) because if we hit one of the historical Eclipse bugs with {@link Types#asMemberOf} * then {@link EclipseHack#methodReturnType} can use fallback logic, which only works for methods * with no arguments. * * @param methods the methods whose return types are to be rewritten. * @param sourceType the class containing those methods ({@code Foo} in the example). * @param targetType the class to translate the methods into ({@code Foo.Builder}) in the * example. */ static ImmutableMap rewriteReturnTypes( Elements elementUtils, Types typeUtils, Collection methods, TypeElement sourceType, TypeElement targetType) { List sourceTypeParameters = sourceType.getTypeParameters(); List targetTypeParameters = targetType.getTypeParameters(); Preconditions.checkArgument( sourceTypeParameters.toString().equals(targetTypeParameters.toString()), "%s != %s", sourceTypeParameters, targetTypeParameters); // What we're doing is only valid if the type parameters are "the same". The check here even // requires the names to be the same. The logic would still work without that, but we impose // that requirement elsewhere and it means we can check in this simple way. EclipseHack eclipseHack = new EclipseHack(elementUtils, typeUtils); TypeMirror[] targetTypeParameterMirrors = new TypeMirror[targetTypeParameters.size()]; for (int i = 0; i < targetTypeParameters.size(); i++) { targetTypeParameterMirrors[i] = targetTypeParameters.get(i).asType(); } DeclaredType parallelSource = typeUtils.getDeclaredType(sourceType, targetTypeParameterMirrors); return methods.stream() .collect( ImmutableMap.toImmutableMap( m -> m, m -> eclipseHack.methodReturnType(m, parallelSource))); } /** * Tests whether a given parameter can be given to a static method like * {@code ImmutableMap.copyOf} to produce a value that can be assigned to the given target type. * *

      For example, suppose we have this method in {@code ImmutableMap}:
      * {@code static ImmutableMap copyOf(Map)}
      * and we want to know if we can do this: * *

         * {@code ImmutableMap actualParameter = ...;}
         * {@code ImmutableMap target = ImmutableMap.copyOf(actualParameter);}
         * 
      * * We will infer {@code K=String}, {@code V=Number} based on the target type, and then rewrite the * formal parameter type from
      * {@code Map} to
      * {@code Map}. Then we can check whether * {@code actualParameter} is assignable to that. * *

      The logic makes some simplifying assumptions, which are met for the {@code copyOf} and * {@code of} methods that we use this for. The method must be static, it must have exactly one * parameter, and it must have type parameters without bounds that are the same as the type * parameters of its return type. We can see that these assumptions are met for the * {@code ImmutableMap.copyOf} example above. */ static boolean canAssignStaticMethodResult( ExecutableElement method, TypeMirror actualParameterType, TypeMirror targetType, Types typeUtils) { if (!targetType.getKind().equals(TypeKind.DECLARED) || !method.getModifiers().contains(Modifier.STATIC) || method.getParameters().size() != 1) { return false; } List typeParameters = method.getTypeParameters(); List targetTypeArguments = MoreTypes.asDeclared(targetType).getTypeArguments(); if (typeParameters.size() != targetTypeArguments.size()) { return false; } Map, TypeMirror> typeVariables = new LinkedHashMap<>(); for (int i = 0; i < typeParameters.size(); i++) { TypeVariable v = MoreTypes.asTypeVariable(typeParameters.get(i).asType()); typeVariables.put(MoreTypes.equivalence().wrap(v), targetTypeArguments.get(i)); } TypeMirror formalParameterType = method.getParameters().get(0).asType(); SubstitutionVisitor substitutionVisitor = new SubstitutionVisitor(typeVariables, typeUtils); TypeMirror substitutedParameterType = substitutionVisitor.visit(formalParameterType, null); if (substitutedParameterType.getKind().equals(TypeKind.WILDCARD)) { // If the target type is Optional then T Optional.of(T) will give us // ? extends Foo here, and typeUtils.isAssignable will return false. But we can in fact // give a Foo as an argument, so we just replace ? extends Foo with Foo. WildcardType wildcard = MoreTypes.asWildcard(substitutedParameterType); if (wildcard.getExtendsBound() != null) { substitutedParameterType = wildcard.getExtendsBound(); } } return typeUtils.isAssignable(actualParameterType, substitutedParameterType); } /** * Rewrites types such that references to type variables in the given map are replaced by the * values of those variables. */ private static class SubstitutionVisitor extends SimpleTypeVisitor8 { private final Map, TypeMirror> typeVariables; private final Types typeUtils; SubstitutionVisitor( Map, TypeMirror> typeVariables, Types typeUtils) { this.typeVariables = ImmutableMap.copyOf(typeVariables); this.typeUtils = typeUtils; } @Override protected TypeMirror defaultAction(TypeMirror t, Void p) { return t; } @Override public TypeMirror visitTypeVariable(TypeVariable t, Void p) { TypeMirror substituted = typeVariables.get(MoreTypes.equivalence().wrap(t)); return (substituted == null) ? t : substituted; } @Override public TypeMirror visitDeclared(DeclaredType t, Void p) { List typeArguments = t.getTypeArguments(); TypeMirror[] substitutedTypeArguments = new TypeMirror[typeArguments.size()]; for (int i = 0; i < typeArguments.size(); i++) { substitutedTypeArguments[i] = visit(typeArguments.get(i)); } return typeUtils.getDeclaredType( MoreElements.asType(t.asElement()), substitutedTypeArguments); } @Override public TypeMirror visitWildcard(WildcardType t, Void p) { TypeMirror ext = visitOrNull(t.getExtendsBound()); if (ext != null && ext.getKind().equals(TypeKind.WILDCARD)) { // An example of where this happens is if we have this method in ImmutableSet: // static ImmutableSet copyOf(Collection) // and we want to know if we can do this (where T is parameter of the enclosing class): // ImmutableSet actualParameter = ... // ImmutableSet target = ImmutableSet.copyOf(actualParameter); // We will infer E= and rewrite the formal parameter type to // Collection, which we must simplify to Collection. return ext; } return typeUtils.getWildcardType(ext, visitOrNull(t.getSuperBound())); } @Override public TypeMirror visitArray(ArrayType t, Void p) { TypeMirror comp = visit(t.getComponentType()); if (comp.getKind().equals(TypeKind.WILDCARD)) { // An example of where this happens is if we have this method in ImmutableSet: // static ImmutableSet copyOf(E[]) // and we want to know if we can do this: // String[] actualParameter = ... // ImmutableSet target = ImmutableSet.copyOf(actualParameter); // We will infer E= and rewrite the formal parameter type to // [], which we must simplify to T[]. // TODO: what if getExtendsBound() returns null? comp = MoreTypes.asWildcard(comp).getExtendsBound(); } return typeUtils.getArrayType(comp); } // We'd like to override visitIntersectionType here for completeness, but Types has no method // to fabricate a new IntersectionType. Anyway, currently IntersectionType can only occur as // the result of TypeVariable.getUpperBound(), but the TypeMirror we're starting from is not // that, and if we encounter a TypeVariable during our visit we don't visit its upper bound. private TypeMirror visitOrNull(TypeMirror t) { if (t == null) { return null; } else { return visit(t); } } } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/autoannotation.vm000066400000000000000000000233271365703632600327550ustar00rootroot00000000000000## Copyright 2014 Google LLC ## ## 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. ## Template for each generated AutoAnnotation_Foo_bar class. ## This template uses the Apache Velocity Template Language (VTL). ## The variables ($pkg, $props, and so on) are defined by the fields of AutoAnnotationTemplateVars. ## ## Comments, like this one, begin with ##. The comment text extends up to and including the newline ## character at the end of the line. So comments also serve to join a line to the next one. ## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there. ## That does mean that we sometimes need an extra blank line after such a directive. ## ## Post-processing will remove unwanted spaces and blank lines, but will not join two lines. ## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to ## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not. #macro (cloneArray $a) #if ($gwtCompatible) `java.util.Arrays`.copyOf($a, ${a}.length) #else ${a}.clone() #end #end #if (!$pkg.empty) package $pkg; #end ## The following line will be replaced by the required imports during post-processing. `import` #if (!$generated.empty) @${generated}("com.google.auto.value.processor.AutoAnnotationProcessor") #else // Generated by com.google.auto.value.processor.AutoAnnotationProcessor #end final class $className implements $annotationName { ## Fields #foreach ($m in $members) #if ($params.containsKey($m.toString())) private final $m.type $m; #else private static final $m.type $m = $m.defaultValue; #end #end ## Constructor $className( #foreach ($p in $params.keySet()) $params[$p].type $members[$p] #if ($foreach.hasNext) , #end #end ) { #foreach ($p in $params.keySet()) #if (!$members[$p].kind.primitive) if ($p == null) { throw new NullPointerException("Null $p"); } #end #if ($members[$p].kind == "ARRAY") #if ($params[$p].kind == "ARRAY") this.$p = #cloneArray(${p}); #elseif ($members[$p].typeMirror.componentType.kind.primitive) this.$p = ${members[$p].typeMirror.componentType}ArrayFromCollection($p); #elseif ($members[$p].arrayOfClassWithBounds) @SuppressWarnings({"unchecked", "rawtypes"}) ${members[$p].componentType}[] ${p}$ = ${p}.toArray(new Class[0]); this.$p = ${p}$; #else this.$p = ${p}.toArray(new ${members[$p].componentType}[0]); #end #else this.$p = $p; #end #end } ## annotationType method (defined by the Annotation interface) @Override public Class annotationType() { return ${annotationName}.class; } ## Member getters #foreach ($m in $members) @Override public ${m.type} ${m}() { #if ($m.kind == "ARRAY") return #cloneArray(${m}); #else return ${m}; #end } #end ## toString #macro (appendMemberString $m) #if ($m.typeMirror.toString() == "java.lang.String") #set ($appendQuotedStringMethod = "true") appendQuoted(sb, $m) ## #elseif ($m.typeMirror.toString() == "char") #set ($appendQuotedCharMethod = "true") appendQuoted(sb, $m) ## #elseif ($m.typeMirror.toString() == "java.lang.String[]") #set ($appendQuotedStringArrayMethod = "true") appendQuoted(sb, $m) ## #elseif ($m.typeMirror.toString() == "char[]") #set ($appendQuotedCharArrayMethod = "true") appendQuoted(sb, $m) ## #elseif ($m.kind == "ARRAY") sb.append(`java.util.Arrays`.toString($m)) ## #else sb.append($m) ## #end #end @Override public String toString() { StringBuilder sb = new StringBuilder("@$annotationFullName("); #foreach ($p in $params.keySet()) #if ($params.size() > 1 || $params.keySet().iterator().next() != "value") sb.append("$p="); #end #appendMemberString($members[$p]); #if ($foreach.hasNext) sb.append(", "); #end #end return sb.append(')').toString(); } ## equals #macro (memberEqualsThatExpression $m) #if ($m.kind == "FLOAT") Float.floatToIntBits($m) == Float.floatToIntBits(that.${m}()) ## #elseif ($m.kind == "DOUBLE") Double.doubleToLongBits($m) == Double.doubleToLongBits(that.${m}()) ## #elseif ($m.kind.primitive) $m == that.${m}() ## #elseif ($m.kind == "ARRAY") #if ($params.containsKey($m.toString())) `java.util.Arrays`.equals($m, (that instanceof $className) ? (($className) that).$m : that.${m}()) ## #else ## default value, so if |that| is also a $className then it has the same constant value that instanceof $className || `java.util.Arrays`.equals($m, that.${m}()) #end #else ${m}.equals(that.${m}()) ## #end #end @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof $annotationName) { #if ($members.isEmpty()) return true; #else $annotationName that = ($annotationName) o; return ## #foreach ($m in $members) (#memberEqualsThatExpression ($m))## #if ($foreach.hasNext) && ## #end #end ; #end } return false; } ## hashCode #macro (memberHashCodeExpression $m) #if ($m.kind == "LONG") (int) (($m >>> 32) ^ $m) ## #elseif ($m.kind == "FLOAT") Float.floatToIntBits($m) ## #elseif ($m.kind == "DOUBLE") (int) ((Double.doubleToLongBits($m) >>> 32) ^ Double.doubleToLongBits($m)) ## #elseif ($m.kind == "BOOLEAN") $m ? 1231 : 1237 ## #elseif ($m.kind.primitive) $m ## #elseif ($m.kind == "ARRAY") `java.util.Arrays`.hashCode($m) ## #else ${m}.hashCode() ## #end #end ## The hashCode is the sum of two parts, an invariable part and a variable part. The invariable part ## comes from defaulted members (ones that don't appear in the @AutoAnnotation method parameters) ## whose values have hash codes that never change. (That doesn't include Class constants, for ## example.) We precompute the invariable part, as an optimization but also in order to avoid ## falling afoul of constant-overflow checks in the compiler. @Override public int hashCode() { return ## If the invariable part is 0, we avoid outputting `return 0 + ...` just because it generates ## unnecessary byte code. But if there are no members then we must say `return 0;` here. ## We must write $members.isEmpty() because $members is a Map and Velocity interprets ## $members.empty as meaning $members["empty"] in that case. #if ($invariableHashSum != 0 || $members.isEmpty()) $invariableHashSum // $invariableHashSum is the contribution from default members $invariableHashes #end #foreach ($m in $members) #if (!$invariableHashes.contains($m.toString())) + ($m.nameHash ^ (#memberHashCodeExpression($m))) // $m.nameHash is 127 * "${m}".hashCode() #end #end ; } ## support functions #foreach ($w in $wrapperTypesUsedInCollections) #set ($prim = $w.getField("TYPE").get("")) private static ${prim}[] ${prim}ArrayFromCollection(`java.util.Collection`<${w.simpleName}> c) { ${prim}[] a = new ${prim}[c.size()]; int i = 0; for (${prim} x : c) { a[i++] = x; } return a; } #end #if ($appendQuotedStringArrayMethod) #set ($appendQuotedStringMethod = "true") private static void appendQuoted(StringBuilder sb, String[] strings) { sb.append('['); String sep = ""; for (String s : strings) { sb.append(sep); sep = ", "; appendQuoted(sb, s); } sb.append(']'); } #end #if ($appendQuotedCharArrayMethod) #set ($appendQuotedCharMethod = "true") private static void appendQuoted(StringBuilder sb, char[] chars) { sb.append('['); String sep = ""; for (char c : chars) { sb.append(sep); sep = ", "; appendQuoted(sb, c); } sb.append(']'); } #end #if ($appendQuotedStringMethod) #set ($appendEscapedMethod = "true") private static void appendQuoted(StringBuilder sb, String s) { sb.append('"'); for (int i = 0; i < s.length(); i++) { appendEscaped(sb, s.charAt(i)); } sb.append('"'); } #end #if ($appendQuotedCharMethod) #set ($appendEscapedMethod = "true") private static void appendQuoted(StringBuilder sb, char c) { sb.append('\''); appendEscaped(sb, c); sb.append('\''); } #end #if ($appendEscapedMethod) private static void appendEscaped(StringBuilder sb, char c) { switch (c) { case '\\': case '"': case '\'': sb.append('\\').append(c); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; default: if (c < 0x20) { sb.append('\\'); appendWithZeroPadding(sb, Integer.toOctalString(c), 3); } else if (c < 0x7f || Character.isLetter(c)) { sb.append(c); } else { sb.append("\\u"); appendWithZeroPadding(sb, Integer.toHexString(c), 4); } break; } } ## We use this rather than String.format because that doesn't exist on GWT. private static void appendWithZeroPadding(StringBuilder sb, String s, int width) { for (int i = width - s.length(); i > 0; i--) { sb.append('0'); } sb.append(s); } #end } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/autooneof.vm000066400000000000000000000126161365703632600317100ustar00rootroot00000000000000## Copyright 2018 Google LLC ## ## 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. ## Template for each generated AutoOneOf_Foo class. ## This template uses the Apache Velocity Template Language (VTL). ## The variables ($pkg, $props, and so on) are defined by the fields of AutoOneOfTemplateVars. ## ## Comments, like this one, begin with ##. The comment text extends up to and including the newline ## character at the end of the line. So comments also serve to join a line to the next one. ## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there. ## That does mean that we sometimes need an extra blank line after such a directive. ## ## Post-processing will remove unwanted spaces and blank lines, but will not join two lines. ## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to ## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not. ## Get #equalsThatExpression($p) and #hashCodeExpression($p). #parse("equalshashcode.vm") #if (!$pkg.empty) package $pkg; #end ## The following line will be replaced by the required imports during post-processing. `import` #if ($generated.empty) // Generated by com.google.auto.value.processor.AutoOneOfProcessor #else @${generated}("com.google.auto.value.processor.AutoOneOfProcessor") #end final class $generatedClass { private ${generatedClass}() {} // There are no instances of this type. ## Factory methods. #foreach ($p in $props) #if ($p.type == "void") #if ($wildcardTypes == "") static $origClass $p() { return Impl_${p}.INSTANCE; } #else @SuppressWarnings("unchecked") // type parameters are unused in void instances static $formalTypes $origClass$actualTypes $p() { return ($origClass$actualTypes) Impl_${p}.INSTANCE; } #end #else ## If the @AutoOneOf type is TaskResult, then we might have here: ## static TaskResult value(V value) { ## return new Impl_value(value); ## } ## The parameter type might be something else (Throwable for example), but we will still ## want TaskResult. static $formalTypes $origClass$actualTypes $p($p.type $p) { #if (!$p.kind.primitive) if ($p == null) { throw new NullPointerException(); } #end return new Impl_$p$actualTypes($p); } #end #end #foreach ($a in $annotations) $a #end // Parent class that each implementation will inherit from. private abstract static class Parent_$formalTypes extends $origClass$actualTypes { #foreach ($p in $props) @Override $p.access $p.type ${p.getter}() { throw new UnsupportedOperationException(${kindGetter}().toString()); } #end } #foreach ($p in $props) #foreach ($a in $annotations) $a #end // Implementation when the contained property is "${p}". private static final class Impl_$p$formalTypes extends Parent_$actualTypes { #if ($p.type == "void") // There is only one instance of this class. static final Impl_$p$wildcardTypes INSTANCE = new ## #if ($wildcardTypes == "") Impl_$p() #else Impl_$p<>() #end; private Impl_$p() {} @Override public void ${p.getter}() {} #if ($serializable) private Object readResolve() { return INSTANCE; } #end #if ($toString) @Override public String toString() { return "${simpleClassName}{$p.name}"; } #end ## The implementations of equals and hashCode are equivalent to the ones ## we inherit from Object. We only need to define them if they're redeclared ## as abstract in an ancestor class. But currently we define them always. #if ($equals) @Override public boolean equals($equalsParameterType x) { return x == this; } #end #if ($hashCode) @Override public int hashCode() { return System.identityHashCode(this); } #end #else private final $p.type $p; Impl_$p($p.type $p) { this.$p = $p; } @Override public $p.type ${p.getter}() { return $p; } #if ($toString) @Override public String toString() { return "${simpleClassName}{$p.name=" ## + #if ($p.kind == "ARRAY") `java.util.Arrays`.toString(this.$p) #else this.$p #end + "}"; } #end #if ($equals) @Override public boolean equals($equalsParameterType x) { if (x instanceof $origClass) { $origClass$wildcardTypes that = ($origClass$wildcardTypes) x; return this.${kindGetter}() == that.${kindGetter}() && #equalsThatExpression($p "Impl_$p"); } else { return false; } } #end #if ($hashCode) @Override public int hashCode() { return #hashCodeExpression($p); } #end #end @Override public $kindType ${kindGetter}() { return ${kindType}.$propertyToKind[$p.name]; } } #end } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/autovalue.vm000066400000000000000000000245531365703632600317210ustar00rootroot00000000000000## Copyright 2014 Google LLC ## ## 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. ## Template for each generated AutoValue_Foo class. ## This template uses the Apache Velocity Template Language (VTL). ## The variables ($pkg, $props, and so on) are defined by the fields of AutoValueTemplateVars. ## ## Comments, like this one, begin with ##. The comment text extends up to and including the newline ## character at the end of the line. So comments also serve to join a line to the next one. ## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there. ## That does mean that we sometimes need an extra blank line after such a directive. ## ## Post-processing will remove unwanted spaces and blank lines, but will not join two lines. ## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to ## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not. ## Get #equalsThatExpression($p) and #hashCodeExpression($p). #parse("equalshashcode.vm") #if (!$pkg.empty) package $pkg; #end ## The following line will be replaced by the required imports during post-processing. `import` ${gwtCompatibleAnnotation} #foreach ($a in $annotations) $a #end #if (!$generated.empty) @${generated}("com.google.auto.value.processor.AutoValueProcessor") #else // Generated by com.google.auto.value.processor.AutoValueProcessor #end ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes { ## Fields #foreach ($p in $props) #foreach ($a in ${p.fieldAnnotations}) ${a}## #end private final $p.type $p; #end ## Constructor #if ($isFinal && $builderTypeName != "") private ## #end $subclass( #foreach ($p in $props) ${p.nullableAnnotation}$p.type $p #if ($foreach.hasNext) , #end #end ) { #foreach ($p in $props) #if (!$p.kind.primitive && !$p.nullable && ($builderTypeName == "" || !$isFinal)) ## We don't need a null check if the type is primitive or @Nullable. We also don't need it ## if there is a builder, since the build() method will check for us. However, if there is a ## builder but there are also extensions (!$isFinal) then we can't omit the null check because ## the constructor is called from the extension code. #if ($identifiers) if ($p == null) { throw new NullPointerException("Null $p.name"); } #else ## Just throw NullPointerException with no message if it's null. ## The Object cast has no effect on the code but silences an ErrorProne warning. ((`java.lang.Object`) ${p}).getClass(); #end #end this.$p = $p; #end } ## Property getters #foreach ($p in $props) #foreach ($a in ${p.methodAnnotations}) ${a}## #end @Override ${p.access}${p.type} ${p.getter}() { return $p; } #end #if ($toString) @Override public `java.lang.String` toString() { return "#if ($identifiers)$simpleClassName#end{" #foreach ($p in $props) #if ($identifiers)+ "$p.name=" ## #end+ #if ($p.kind == "ARRAY") `java.util.Arrays`.toString($p) #else $p #end #if ($foreach.hasNext) + ", " #end #end + "}"; } #end #if ($equals) @Override public boolean equals($equalsParameterType o) { if (o == this) { return true; } if (o instanceof $origClass) { #if ($props.empty) return true; #else $origClass$wildcardTypes that = ($origClass$wildcardTypes) o; return ## #foreach ($p in $props) #equalsThatExpression ($p $subclass)## #if ($foreach.hasNext) && ## #end #end ; #end } return false; } #end #if ($hashCode) @Override public int hashCode() { int h$ = 1; #foreach ($p in $props) h$ *= 1000003; h$ ^= #hashCodeExpression($p); #end return h$; } #end #if (!$serialVersionUID.empty) private static final long serialVersionUID = $serialVersionUID; #end #if ($builderTypeName != "") #foreach ($m in $toBuilderMethods) @Override ${m.access}${builderTypeName}${builderActualTypes} ${m.name}() { return new Builder${builderActualTypes}(this); } #end static #if ($isFinal) final #end class Builder${builderFormalTypes} ## #if ($builderIsInterface) implements #else extends #end ${builderTypeName}${builderActualTypes} { #foreach ($p in $props) #if ($p.kind.primitive) private $types.boxedClass($p.typeMirror).simpleName $p; #else #if ($builderPropertyBuilders[$p.name]) ## If you have ImmutableList.Builder stringsBuilder() then we define two fields: ## private ImmutableList.Builder stringsBuilder$; ## private ImmutableList strings; private ${builderPropertyBuilders[$p.name].builderType} ## ${builderPropertyBuilders[$p.name].name}; #end private $p.type $p #if ($p.optional && !$p.nullable) = $p.optional.empty #end ; #end #end Builder() { } #if (!$toBuilderMethods.empty) private Builder(${origClass}${actualTypes} source) { #foreach ($p in $props) this.$p = source.${p.getter}(); #end } #end #foreach ($p in $props) ## The following is either null or an instance of PropertyBuilderClassifier.PropertyBuilder #set ($propertyBuilder = $builderPropertyBuilders[$p.name]) ## Setter and/or property builder #foreach ($setter in $builderSetters[$p.name]) @Override ${setter.access}${builderTypeName}${builderActualTypes} ## ${setter.name}(${setter.nullableAnnotation}$setter.parameterType $p) { ## Omit null check for primitive, or @Nullable, or if we are going to be calling a copy method ## such as Optional.of, which will have its own null check if appropriate. #if (!$setter.primitiveParameter && !$p.nullable && ${setter.copy($p)} == $p) #if ($identifiers) if ($p == null) { throw new NullPointerException("Null $p.name"); } #else ## Just throw NullPointerException with no message if it's null. ## The Object cast has no effect on the code but silences an ErrorProne warning. ((`java.lang.Object`) ${p}).getClass(); #end #end #if ($propertyBuilder) if (${propertyBuilder.name} != null) { throw new IllegalStateException(#if ($identifiers)"Cannot set $p after calling ${p.name}Builder()"#end); } #end this.$p = ${setter.copy($p)}; return this; } #end #if ($propertyBuilder) @Override ${propertyBuilder.access}$propertyBuilder.builderType ${p.name}Builder() { if (${propertyBuilder.name} == null) { ## This is the first time someone has asked for the builder. If the property it sets already ## has a value (because it came from a toBuilder() call on the AutoValue class, or because ## there is also a setter for this property) then we copy that value into the builder. ## Otherwise the builder starts out empty. ## If we have neither a setter nor a toBuilder() method, then the builder always starts ## off empty. #if ($builderSetters[$p.name].empty && $toBuilderMethods.empty) ${propertyBuilder.name} = ${propertyBuilder.initializer}; #else if ($p == null) { ${propertyBuilder.name} = ${propertyBuilder.initializer}; } else { #if (${propertyBuilder.builtToBuilder}) ${propertyBuilder.name} = ${p}.${propertyBuilder.builtToBuilder}(); #else ${propertyBuilder.name} = ${propertyBuilder.initializer}; ${propertyBuilder.name}.${propertyBuilder.copyAll}($p); #end $p = null; } #end } return $propertyBuilder.name; } #end ## Getter #if ($builderGetters[$p.name]) @Override ${p.nullableAnnotation}${builderGetters[$p.name].access}$builderGetters[$p.name].type ${p.getter}() { #if ($builderGetters[$p.name].optional) if ($p == null) { return $builderGetters[$p.name].optional.empty; } else { return ${builderGetters[$p.name].optional.rawType}.of($p); } #else #if ($builderRequiredProperties.contains($p)) if ($p == null) { throw new IllegalStateException(#if ($identifiers)"Property \"$p.name\" has not been set"#end); } #end #if ($propertyBuilder) if (${propertyBuilder.name} != null) { return ${propertyBuilder.name}.build(); } if ($p == null) { ${propertyBuilder.beforeInitDefault} $p = ${propertyBuilder.initDefault}; } #end return $p; #end } #end #end @Override ${buildMethod.get().access}${origClass}${actualTypes} ${buildMethod.get().name}() { #foreach ($p in $props) #set ($propertyBuilder = $builderPropertyBuilders[$p.name]) #if ($propertyBuilder) if (${propertyBuilder.name} != null) { this.$p = ${propertyBuilder.name}.build(); } else if (this.$p == null) { ${propertyBuilder.beforeInitDefault} this.$p = ${propertyBuilder.initDefault}; } #end #end #if (!$builderRequiredProperties.empty) #if ($identifiers) ## build a friendly message showing all missing properties `java.lang.String` missing = ""; #foreach ($p in $builderRequiredProperties) if (this.$p == null) { missing += " $p.name"; } #end if (!missing.isEmpty()) { throw new IllegalStateException("Missing required properties:" + missing); } #else ## just throw an exception if anything is missing if (#foreach ($p in $builderRequiredProperties)## this.$p == null## #if ($foreach.hasNext) || #end #end) { throw new IllegalStateException(); } #end #end return new ${finalSubclass}${actualTypes}( #foreach ($p in $props) this.$p #if ($foreach.hasNext) , #end #end ); } } #end } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/equalshashcode.vm000066400000000000000000000074501365703632600327020ustar00rootroot00000000000000## Copyright 2018 Google LLC ## ## 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. ## Shared definitions for @AutoFoo templates. This is included in those templates using ## the #parse directive. ## ## This template uses the Apache Velocity Template Language (VTL). ## The variables ($pkg, $props, and so on) are defined by the fields of AutoValueTemplateVars. ## ## Comments, like this one, begin with ##. The comment text extends up to and including the newline ## character at the end of the line. So comments also serve to join a line to the next one. ## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there. ## That does mean that we sometimes need an extra blank line after such a directive. ## ## Post-processing will remove unwanted spaces and blank lines, but will not join two lines. ## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to ## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not. ## In the following two macros, $p is an object of type AutoValueProcessor.Property ## or AutoOneOfProcessor.Property. $p.kind means the getKind() method of those classes, ## and likewise for $p.getter and $p.nullable (isNullable()). ## Expands to an expression appropriate for comparing the $p property in `this` against ## the $p property in `that`. If we're generating code for `AutoValue_Baz` then ## `that` is of type `Baz`. ## As an example, if $p is the `foo` property and $p.kind is FLOAT, ## this becomes `Float.floatToIntBits(this.foo) == Float.floatToIntBits(that.foo())` ## or `...that.getFoo()...`. ## The expression should be surrounded by parentheses if otherwise there might be precedence ## problems when it is followed by &&. ## A reminder that trailing ## here serves to delete the newline, which we don't want in the output. #macro (equalsThatExpression $p $subclass) #if ($p.kind == "FLOAT") Float.floatToIntBits(this.$p) == Float.floatToIntBits(that.${p.getter}()) ## #elseif ($p.kind == "DOUBLE") Double.doubleToLongBits(this.$p) == Double.doubleToLongBits(that.${p.getter}()) ## #elseif ($p.kind.primitive) this.$p == that.${p.getter}() ## #elseif ($p.kind == "ARRAY") `java.util.Arrays`.equals(this.$p, ## (that instanceof $subclass) ? (($subclass) that).$p : that.${p.getter}()) ## #elseif ($p.nullable) (this.$p == null ? that.${p.getter}() == null : this.${p}.equals(that.${p.getter}())) ## #else this.${p}.equals(that.${p.getter}()) ## #end #end ## Expands to an expression to compute the hashCode of the $p property. ## For example, if $p is the `foo` property and $p.kind is FLOAT, ## this becomes `Float.floatToIntBits(this.foo)`. ## A reminder that trailing ## here serves to delete the newline, which we don't want in the output. #macro (hashCodeExpression $p) #if ($p.kind == "LONG") (int) (($p >>> 32) ^ $p) ## #elseif ($p.kind == "FLOAT") Float.floatToIntBits($p) ## #elseif ($p.kind == "DOUBLE") (int) ((Double.doubleToLongBits($p) >>> 32) ^ Double.doubleToLongBits($p)) ## #elseif ($p.kind == "BOOLEAN") $p ? 1231 : 1237 ## #elseif ($p.kind.primitive) $p ## #elseif ($p.kind == "ARRAY") `java.util.Arrays`.hashCode($p) ## #elseif ($p.nullable) ($p == null) ? 0 : ${p}.hashCode() ## #else ${p}.hashCode() ## #end #end auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/gwtserializer.vm000066400000000000000000000107251365703632600326030ustar00rootroot00000000000000## Copyright 2014 Google LLC ## ## 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. ## Template for each generated AutoValue_Foo_CustomFieldSerializer class. ## This template uses the Apache Velocity Template Language (VTL). ## The variables ($pkg, $props, and so on) are defined by the fields of ## GwtSerialization.GwtTemplateVars. ## ## Comments, like this one, begin with ##. The comment text extends up to and including the newline ## character at the end of the line. So comments also serve to join a line to the next one. ## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there. ## That does mean that we sometimes need an extra blank line after such a directive. ## ## A post-processing step will remove unwanted spaces and blank lines, but will not join two lines. ## TODO(emcmanus): perform the post-processing. #if (!$pkg.empty) package $pkg; #end ## The following line will be replaced by the required imports during post-processing. `import` #if (!$generated.empty) @${generated}("com.google.auto.value.processor.AutoValueProcessor") #else // Generated by com.google.auto.value.processor.AutoValueProcessor #end public final class $serializerClass$formalTypes extends `com.google.gwt.user.client.rpc.CustomFieldSerializer`<$subclass$actualTypes> { public static $formalTypes $subclass$actualTypes instantiate( `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader) throws `com.google.gwt.user.client.rpc.SerializationException` { #foreach ($p in $props) #if ($p.castingUnchecked) @SuppressWarnings("unchecked") #end $p.type $p = ${p.gwtCast}streamReader.read${p.gwtType}(); #end #if ($useBuilder) ${subclass}.Builder${actualTypes} builder$ = new ${subclass}.Builder${actualTypes}(); #foreach ($p in $props) #if ($builderSetters[$p.name].empty) #set ($propertyBuilder = $builderPropertyBuilders[$p.name]) builder$.${propertyBuilder.propertyBuilderMethod}.${propertyBuilder.copyAll}($p); #else builder$.${builderSetters[$p.name].iterator().next().name}($p); #end #end return (${subclass}${actualTypes}) builder$.build(); #else return new ${subclass}$actualTypes( #foreach ($p in $props) $p #if ($foreach.hasNext) , #end #end); #end } public static $formalTypes void serialize( `com.google.gwt.user.client.rpc.SerializationStreamWriter` streamWriter, $subclass$actualTypes instance) throws `com.google.gwt.user.client.rpc.SerializationException` { #foreach ($p in $props) streamWriter.write${p.gwtType}(instance.${p.getter}()); #end } public static $formalTypes void deserialize( @SuppressWarnings("unused") `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader, @SuppressWarnings("unused") $subclass$actualTypes instance) { // instantiate already did all the work. } // This dummy field is a hash of the fields in ${subclass}. It will change if they do, including // if their order changes. This is because GWT identity for a class that has a serializer is // based on the fields of the serializer rather than the class itself. @SuppressWarnings("unused") private int dummy_$classHashString; @Override public void deserializeInstance( `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader, $subclass$actualTypes instance) { deserialize(streamReader, instance); } @Override public boolean hasCustomInstantiateInstance() { return true; } @Override public $subclass$actualTypes instantiateInstance( `com.google.gwt.user.client.rpc.SerializationStreamReader` streamReader) throws `com.google.gwt.user.client.rpc.SerializationException` { return instantiate(streamReader); } @Override public void serializeInstance( `com.google.gwt.user.client.rpc.SerializationStreamWriter` streamWriter, $subclass$actualTypes instance) throws `com.google.gwt.user.client.rpc.SerializationException` { serialize(streamWriter, instance); } } auto-auto-service-1.0-rc7/value/src/main/java/com/google/auto/value/processor/package-info.java000066400000000000000000000013571365703632600325340ustar00rootroot00000000000000/* * Copyright 2013 Google LLC * * 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. */ /** * This package contains the annotation processor that implements the {@link * com.google.auto.value.AutoValue} API. */ package com.google.auto.value.processor; auto-auto-service-1.0-rc7/value/src/test/000077500000000000000000000000001365703632600203145ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/000077500000000000000000000000001365703632600212355ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/000077500000000000000000000000001365703632600220135ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/000077500000000000000000000000001365703632600232675ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/000077500000000000000000000000001365703632600242375ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/000077500000000000000000000000001365703632600253535ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/000077500000000000000000000000001365703632600273675ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/memoized/000077500000000000000000000000001365703632600312005ustar00rootroot00000000000000MemoizedMethodSubject.java000066400000000000000000000040511365703632600362160ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/memoized/* * Copyright 2016 Google LLC * * 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.google.auto.value.extension.memoized; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.auto.value.extension.memoized.processor.MemoizeExtension; import com.google.auto.value.processor.AutoValueProcessor; import com.google.common.collect.ImmutableList; import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; final class MemoizedMethodSubject extends Subject { private final String actual; MemoizedMethodSubject(FailureMetadata failureMetadata, String actual) { super(failureMetadata, actual); this.actual = actual; } void hasError(String error) { JavaFileObject file = JavaFileObjects.forSourceLines( "Value", "import com.google.auto.value.AutoValue;", "import com.google.auto.value.extension.memoized.Memoized;", "", "@AutoValue abstract class Value {", " abstract String string();", actual, "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new MemoizeExtension()))) .compile(file); assertThat(compilation) .hadErrorContaining(error) .inFile(file) .onLineContaining(actual); } } MemoizedMethodSubjectFactory.java000066400000000000000000000024261365703632600375520ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/memoized/* * Copyright 2016 Google LLC * * 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.google.auto.value.extension.memoized; import static com.google.common.truth.Truth.assertAbout; import com.google.common.truth.FailureMetadata; import com.google.common.truth.Subject; final class MemoizedMethodSubjectFactory implements Subject.Factory { static MemoizedMethodSubjectFactory memoizeMethod() { return new MemoizedMethodSubjectFactory(); } static MemoizedMethodSubject assertThatMemoizeMethod(String method) { return assertAbout(memoizeMethod()).that(method); } @Override public MemoizedMethodSubject createSubject(FailureMetadata failureMetadata, String that) { return new MemoizedMethodSubject(failureMetadata, that); } } MemoizedTest.java000066400000000000000000000341631365703632600344040ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/memoized/* * Copyright 2016 Google LLC * * 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.google.auto.value.extension.memoized; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.auto.value.AutoValue; import com.google.auto.value.AutoValue.CopyAnnotations; import com.google.auto.value.extension.memoized.MemoizedTest.HashCodeEqualsOptimization.EqualsCounter; import com.google.common.collect.ImmutableList; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class MemoizedTest { private Value value; private ListValue listValue; @AutoValue abstract static class ValueWithKeywordName { abstract boolean getNative(); abstract boolean getNative0(); @Memoized boolean getMemoizedNative() { return getNative(); } @Memoized boolean getMemoizedNative0() { return getNative0(); } } @AutoValue @CopyAnnotations @javax.annotation.Nullable abstract static class ValueWithCopyAnnotations { abstract boolean getNative(); @Memoized boolean getMemoizedNative() { return getNative(); } } @AutoValue @javax.annotation.Nullable abstract static class ValueWithoutCopyAnnotations { abstract boolean getNative(); @Memoized boolean getMemoizedNative() { return getNative(); } } @AutoValue abstract static class Value { private int primitiveCount; private int notNullableCount; private int nullableCount; private int returnsNullCount; private int nullableWithTypeAnnotationCount; private int returnsNullWithTypeAnnotationCount; private int notNullableButReturnsNullCount; private int throwsExceptionCount; @javax.annotation.Nullable abstract String string(); abstract @org.checkerframework.checker.nullness.qual.Nullable String stringWithTypeAnnotation(); abstract HashCodeAndToStringCounter counter(); @Memoized int primitive() { return ++primitiveCount; } @Memoized String notNullable() { notNullableCount++; return "derived " + string() + " " + notNullableCount; } @Memoized @javax.annotation.Nullable String nullable() { nullableCount++; return "nullable derived " + string() + " " + nullableCount; } @Memoized @javax.annotation.Nullable String returnsNull() { returnsNullCount++; return null; } @Memoized @org.checkerframework.checker.nullness.qual.Nullable String nullableWithTypeAnnotation() { nullableWithTypeAnnotationCount++; return "nullable derived " + stringWithTypeAnnotation() + " " + nullableWithTypeAnnotationCount; } @Memoized @org.checkerframework.checker.nullness.qual.Nullable String returnsNullWithTypeAnnotation() { returnsNullWithTypeAnnotationCount++; return null; } @Memoized String notNullableButReturnsNull() { notNullableButReturnsNullCount++; return null; } @Memoized String throwsException() throws SomeCheckedException { throwsExceptionCount++; throw new SomeCheckedException(); } @Override @Memoized public abstract int hashCode(); @Override @Memoized public abstract String toString(); } static final class SomeCheckedException extends Exception {} @AutoValue abstract static class ListValue { abstract T value(); abstract K otherValue(); @Memoized ImmutableList myTypedList() { return ImmutableList.of(value()); } } static class HashCodeAndToStringCounter { int hashCodeCount; int toStringCount; @Override public int hashCode() { return ++hashCodeCount; } @Override public String toString() { return "a string" + ++toStringCount; } } @AutoValue abstract static class HashCodeEqualsOptimization { int overrideHashCode; int hashCodeCount; abstract EqualsCounter equalsCounter(); @Memoized @Override public int hashCode() { hashCodeCount++; return overrideHashCode; } static class EqualsCounter { int equalsCount; @Override public int hashCode() { return 0; } @Override public boolean equals(Object obj) { equalsCount++; return true; } } } @AutoValue abstract static class HashCodeEqualsOptimizationOffWhenEqualsIsFinal { int hashCodeCount; @Override @Memoized public int hashCode() { hashCodeCount++; return 1; } @Override public final boolean equals(Object that) { return that instanceof HashCodeEqualsOptimizationOffWhenEqualsIsFinal; } } @Before public void setUp() { value = new AutoValue_MemoizedTest_Value( "string", "stringWithTypeAnnotation", new HashCodeAndToStringCounter()); listValue = new AutoValue_MemoizedTest_ListValue(0, "hello"); } @Test public void listValueList() { assertThat(listValue.myTypedList()).containsExactly(listValue.value()); } @Test public void listValueString() { assertThat(listValue.otherValue()).isEqualTo("hello"); } @Test public void primitive() { assertThat(value.primitive()).isEqualTo(1); assertThat(value.primitive()).isEqualTo(1); assertThat(value.primitiveCount).isEqualTo(1); } @Test public void notNullable() { assertThat(value.notNullable()).isEqualTo("derived string 1"); assertThat(value.notNullable()).isSameInstanceAs(value.notNullable()); assertThat(value.notNullableCount).isEqualTo(1); } @Test public void nullable() { assertThat(value.nullable()).isEqualTo("nullable derived string 1"); assertThat(value.nullable()).isSameInstanceAs(value.nullable()); assertThat(value.nullableCount).isEqualTo(1); } @Test public void nullableWithTypeAnnotation() { assertThat(value.nullableWithTypeAnnotation()) .isEqualTo("nullable derived stringWithTypeAnnotation 1"); assertThat(value.nullableWithTypeAnnotation()) .isSameInstanceAs(value.nullableWithTypeAnnotation()); assertThat(value.nullableWithTypeAnnotationCount).isEqualTo(1); } @Test public void returnsNull() { assertThat(value.returnsNull()).isNull(); assertThat(value.returnsNull()).isNull(); assertThat(value.returnsNullCount).isEqualTo(1); } @Test public void returnsNullWithTypeAnnotation() { assertThat(value.returnsNullWithTypeAnnotation()).isNull(); assertThat(value.returnsNullWithTypeAnnotation()).isNull(); assertThat(value.returnsNullWithTypeAnnotationCount).isEqualTo(1); } @Test public void notNullableButReturnsNull() { try { value.notNullableButReturnsNull(); fail(); } catch (NullPointerException expected) { assertThat(expected) .hasMessageThat() .isEqualTo("notNullableButReturnsNull() cannot return null"); } assertThat(value.notNullableButReturnsNullCount).isEqualTo(1); } @Test public void methodThrows() { // The exception is thrown. try { value.throwsException(); fail(); } catch (SomeCheckedException expected1) { // The exception is not memoized. try { value.throwsException(); fail(); } catch (SomeCheckedException expected2) { assertThat(expected2).isNotSameInstanceAs(expected1); } } assertThat(value.throwsExceptionCount).isEqualTo(2); } @Test public void testHashCode() { assertThat(value.hashCode()).isEqualTo(value.hashCode()); assertThat(value.counter().hashCodeCount).isEqualTo(1); } @Test public void testToString() { assertThat(value.toString()).isEqualTo(value.toString()); assertThat(value.counter().toStringCount).isEqualTo(1); } @Test public void keywords() { ValueWithKeywordName value = new AutoValue_MemoizedTest_ValueWithKeywordName(true, false); assertThat(value.getNative()).isTrue(); assertThat(value.getMemoizedNative()).isTrue(); assertThat(value.getNative0()).isFalse(); assertThat(value.getMemoizedNative0()).isFalse(); } @Test public void copyAnnotations() { ValueWithCopyAnnotations valueWithCopyAnnotations = new AutoValue_MemoizedTest_ValueWithCopyAnnotations(true); ValueWithoutCopyAnnotations valueWithoutCopyAnnotations = new AutoValue_MemoizedTest_ValueWithoutCopyAnnotations(true); assertThat( valueWithCopyAnnotations .getClass() .isAnnotationPresent(javax.annotation.Nullable.class)) .isTrue(); assertThat( valueWithoutCopyAnnotations .getClass() .isAnnotationPresent(javax.annotation.Nullable.class)) .isFalse(); } @Test public void nullableHasAnnotation() throws ReflectiveOperationException { Method nullable = AutoValue_MemoizedTest_Value.class.getDeclaredMethod("nullable"); assertThat(nullable.isAnnotationPresent(javax.annotation.Nullable.class)).isTrue(); } @Test public void nullableWithTypeAnnotationHasAnnotation() throws ReflectiveOperationException { Method nullable = AutoValue_MemoizedTest_Value.class.getDeclaredMethod("nullableWithTypeAnnotation"); AnnotatedType returnType = nullable.getAnnotatedReturnType(); assertThat(returnType.isAnnotationPresent( org.checkerframework.checker.nullness.qual.Nullable.class)) .isTrue(); } @Test public void nullableConstructorParameter() throws ReflectiveOperationException { // Constructor parameters are potentially: // [0] @javax.annotation.Nullable String string, // [1] @org.checkerframework.checker.nullness.qual.Nullable String stringWithTypeAnnotation, // [2] HashCodeAndToStringCounter counter // We don't currently copy @javax.annotation.Nullable because it is not a TYPE_USE annotation. Constructor constructor = AutoValue_MemoizedTest_Value.class.getDeclaredConstructor( String.class, String.class, HashCodeAndToStringCounter.class); AnnotatedType paramType = constructor.getAnnotatedParameterTypes()[1]; assertThat(paramType.isAnnotationPresent( org.checkerframework.checker.nullness.qual.Nullable.class)) .isTrue(); } @Test public void hashCodeEqualsOptimization() { HashCodeEqualsOptimization first = new AutoValue_MemoizedTest_HashCodeEqualsOptimization(new EqualsCounter()); HashCodeEqualsOptimization second = new AutoValue_MemoizedTest_HashCodeEqualsOptimization(new EqualsCounter()); first.overrideHashCode = 2; second.overrideHashCode = 2; assertThat(first.equals(second)).isTrue(); assertThat(first.equalsCounter().equalsCount).isEqualTo(1); HashCodeEqualsOptimization otherwiseEqualsButDifferentHashCode = new AutoValue_MemoizedTest_HashCodeEqualsOptimization(new EqualsCounter()); otherwiseEqualsButDifferentHashCode.overrideHashCode = 4; assertThat(otherwiseEqualsButDifferentHashCode.equals(first)).isFalse(); assertThat(otherwiseEqualsButDifferentHashCode.equalsCounter().equalsCount).isEqualTo(0); } @Test public void hashCodeEqualsOptimization_otherTypes() { HashCodeEqualsOptimization optimizedEquals = new AutoValue_MemoizedTest_HashCodeEqualsOptimization(new EqualsCounter()); assertThat(optimizedEquals.equals(new Object())).isFalse(); assertThat(optimizedEquals.equals(null)).isFalse(); assertThat(optimizedEquals.equalsCounter().equalsCount).isEqualTo(0); assertThat(optimizedEquals.hashCodeCount).isEqualTo(0); } @Test public void hashCodeEqualsOptimization_hashCodeIgnoredForSameInstance() { HashCodeEqualsOptimization optimizedEquals = new AutoValue_MemoizedTest_HashCodeEqualsOptimization(new EqualsCounter()); assertThat(optimizedEquals.equals(optimizedEquals)).isTrue(); assertThat(optimizedEquals.equalsCounter().equalsCount).isEqualTo(0); assertThat(optimizedEquals.hashCodeCount).isEqualTo(0); } @Test public void hashCodeEqualsOptimization_offWhenEqualsIsFinal() { HashCodeEqualsOptimizationOffWhenEqualsIsFinal memoizedHashCodeAndFinalEqualsMethod = new AutoValue_MemoizedTest_HashCodeEqualsOptimizationOffWhenEqualsIsFinal(); HashCodeEqualsOptimizationOffWhenEqualsIsFinal second = new AutoValue_MemoizedTest_HashCodeEqualsOptimizationOffWhenEqualsIsFinal(); assertThat(memoizedHashCodeAndFinalEqualsMethod.equals(second)).isTrue(); assertThat(memoizedHashCodeAndFinalEqualsMethod.hashCodeCount).isEqualTo(0); memoizedHashCodeAndFinalEqualsMethod.hashCode(); memoizedHashCodeAndFinalEqualsMethod.hashCode(); assertThat(memoizedHashCodeAndFinalEqualsMethod.hashCodeCount).isEqualTo(1); } interface TypeEdgeIterable {} interface ResourceUri {} interface TypePath {} abstract static class AbstractTypePath { abstract TypeEdgeIterable edges(); } @AutoValue abstract static class ResourceUriPath extends AbstractTypePath { static ResourceUriPath create( TypeEdgeIterable edges) { return new AutoValue_MemoizedTest_ResourceUriPath<>(edges); } @Memoized TypePath toServiceName() { return new TypePath() {}; } } @Test public void methodTypeFromTypeVariableSubsitution() { ResourceUriPath path = ResourceUriPath.create(new TypeEdgeIterable() {}); assertThat(path.edges()).isNotNull(); } } MemoizedValidationTest.java000066400000000000000000000057771365703632600364300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/memoized/* * Copyright 2016 Google LLC * * 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.google.auto.value.extension.memoized; import static com.google.auto.value.extension.memoized.MemoizedMethodSubjectFactory.assertThatMemoizeMethod; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.auto.value.extension.memoized.processor.MemoizedValidator; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class MemoizedValidationTest { @Test public void privateMethod() { assertThatMemoizeMethod("@Memoized private String method() { return \"\"; }") .hasError("@Memoized methods cannot be private"); } @Test public void staticMethod() { assertThatMemoizeMethod("@Memoized static String method() { return \"\"; }") .hasError("@Memoized methods cannot be static"); } @Test public void finalMethod() { assertThatMemoizeMethod("@Memoized final String method() { return \"\"; }") .hasError("@Memoized methods cannot be final"); } @Test public void abstractMethod() { assertThatMemoizeMethod("@Memoized abstract String method();") .hasError("@Memoized methods cannot be abstract"); } @Test public void voidMethod() { assertThatMemoizeMethod("@Memoized void method() {}") .hasError("@Memoized methods cannot be void"); } @Test public void parameters() { assertThatMemoizeMethod("@Memoized String method(Object param) { return \"\"; }") .hasError("@Memoized methods cannot have parameters"); } @Test public void notInAutoValueClass() { JavaFileObject source = JavaFileObjects.forSourceLines( "test.EnclosingClass", "package test;", "", "import com.google.auto.value.extension.memoized.Memoized;", "", "abstract class EnclosingClass {", " @Memoized", " String string() {", " return \"\";", " }", "}"); Compilation compilation = javac().withProcessors(new MemoizedValidator()).compile(source); assertThat(compilation).failed(); assertThat(compilation) .hadErrorContaining("@Memoized methods must be declared only in @AutoValue classes") .inFile(source) .onLine(6); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/000077500000000000000000000000001365703632600320355ustar00rootroot00000000000000processor/000077500000000000000000000000001365703632600337755ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializableSerializableAutoValueExtensionTest.java000066400000000000000000000325771365703632600436470ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/processor/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.processor; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import static org.junit.Assert.assertThrows; import com.google.auto.value.AutoValue; import com.google.auto.value.extension.serializable.SerializableAutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.testing.SerializableTester; import java.io.ByteArrayOutputStream; import java.io.NotSerializableException; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Optional; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class SerializableAutoValueExtensionTest { private static final String A = "a"; private static final int B = 1; private static final String C = "c"; private static final int D = 2; @SerializableAutoValue @AutoValue abstract static class DummySerializableAutoValue implements Serializable { // Primitive fields abstract String a(); abstract int b(); // Optional fields abstract Optional optionalC(); abstract Optional optionalD(); static DummySerializableAutoValue.Builder builder() { return new AutoValue_SerializableAutoValueExtensionTest_DummySerializableAutoValue.Builder(); } @AutoValue.Builder abstract static class Builder { abstract DummySerializableAutoValue.Builder setA(String value); abstract DummySerializableAutoValue.Builder setB(int value); abstract DummySerializableAutoValue.Builder setOptionalC(String value); abstract DummySerializableAutoValue.Builder setOptionalD(int value); abstract DummySerializableAutoValue build(); } } @Test public void allFieldsAreSet_noEmpty() { DummySerializableAutoValue autoValue = DummySerializableAutoValue.builder() .setA(A) .setB(B) .setOptionalC(C) .setOptionalD(D) .build(); assertThat(autoValue.a()).isEqualTo(A); assertThat(autoValue.b()).isEqualTo(B); assertThat(autoValue.optionalC()).hasValue(C); assertThat(autoValue.optionalD()).hasValue(D); } @Test public void allFieldsAreSet_withMixedEmpty() { DummySerializableAutoValue autoValue = DummySerializableAutoValue.builder().setA(A).setB(B).setOptionalC(C).build(); assertThat(autoValue.a()).isEqualTo(A); assertThat(autoValue.b()).isEqualTo(B); assertThat(autoValue.optionalC()).hasValue(C); assertThat(autoValue.optionalD()).isEmpty(); } @Test public void allFieldsAreSet_withEmpty() { DummySerializableAutoValue autoValue = DummySerializableAutoValue.builder().setA(A).setB(B).build(); assertThat(autoValue.a()).isEqualTo(A); assertThat(autoValue.b()).isEqualTo(B); assertThat(autoValue.optionalC()).isEmpty(); assertThat(autoValue.optionalD()).isEmpty(); } @Test public void allFieldsAreSerialized_noEmpty() { DummySerializableAutoValue autoValue = DummySerializableAutoValue.builder() .setA(A) .setB(B) .setOptionalC(C) .setOptionalD(D) .build(); DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @Test public void allFieldsAreSerialized_withEmpty() { DummySerializableAutoValue autoValue = DummySerializableAutoValue.builder().setA(A).setB(B).build(); DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @Test public void allFieldsAreSerialized_withMixedEmpty() { DummySerializableAutoValue autoValue = DummySerializableAutoValue.builder().setA(A).setB(B).setOptionalC(C).build(); DummySerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @SerializableAutoValue @AutoValue abstract static class PrefixSerializableAutoValue implements Serializable { // Primitive fields abstract String getA(); abstract boolean isB(); // Optional fields abstract Optional getC(); abstract Optional getD(); static PrefixSerializableAutoValue.Builder builder() { return new AutoValue_SerializableAutoValueExtensionTest_PrefixSerializableAutoValue.Builder(); } @AutoValue.Builder abstract static class Builder { abstract PrefixSerializableAutoValue.Builder a(String value); abstract PrefixSerializableAutoValue.Builder b(boolean value); abstract PrefixSerializableAutoValue.Builder c(String value); abstract PrefixSerializableAutoValue.Builder d(boolean value); abstract PrefixSerializableAutoValue build(); } } @Test public void allPrefixFieldsAreSerialized_noEmpty() { PrefixSerializableAutoValue autoValue = PrefixSerializableAutoValue.builder().a("A").b(true).c("C").d(false).build(); PrefixSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @Test public void allPrefixFieldsAreSerialized_WithEmpty() { PrefixSerializableAutoValue autoValue = PrefixSerializableAutoValue.builder().a("A").b(true).build(); PrefixSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @SerializableAutoValue @AutoValue abstract static class NotSerializable { static NotSerializable create() { return new AutoValue_SerializableAutoValueExtensionTest_NotSerializable(Optional.of("A")); } abstract Optional optionalA(); } @Test public void missingImplementsSerializableThrowsException() throws Exception { NotSerializable autoValue = NotSerializable.create(); ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream so = new ObjectOutputStream(bo); assertThrows(NotSerializableException.class, () -> so.writeObject(autoValue)); } @AutoValue abstract static class NotSerializableNoAnnotation implements Serializable { static NotSerializableNoAnnotation create() { return new AutoValue_SerializableAutoValueExtensionTest_NotSerializableNoAnnotation( Optional.of("A")); } abstract Optional optionalA(); } @Test public void missingSerializableAutoValueAnnotationThrowsException() throws Exception { NotSerializableNoAnnotation autoValue = NotSerializableNoAnnotation.create(); ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream so = new ObjectOutputStream(bo); assertThrows(NotSerializableException.class, () -> so.writeObject(autoValue)); } @SerializableAutoValue @AutoValue // Technically all type parameters should extend serializable, but for the purposes of testing, // only one type parameter is bounded. abstract static class HasTypeParameters implements Serializable { abstract T a(); abstract Optional optionalB(); static Builder builder() { return new AutoValue_SerializableAutoValueExtensionTest_HasTypeParameters.Builder<>(); } @AutoValue.Builder abstract static class Builder { abstract Builder setA(T value); abstract Builder setOptionalB(S value); abstract HasTypeParameters build(); } } @Test public void typeParameterizedFieldsAreSet_noEmpty() { HasTypeParameters autoValue = HasTypeParameters.builder().setA(A).setOptionalB(B).build(); assertThat(autoValue.a()).isEqualTo(A); assertThat(autoValue.optionalB()).hasValue(B); } @Test public void typeParameterizedFieldsAreSet_withEmpty() { HasTypeParameters autoValue = HasTypeParameters.builder().setA(A).build(); assertThat(autoValue.a()).isEqualTo(A); assertThat(autoValue.optionalB()).isEmpty(); } @Test public void typeParameterizedFieldsAreSerializable_noEmpty() { HasTypeParameters autoValue = HasTypeParameters.builder().setA(A).setOptionalB(B).build(); HasTypeParameters actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @Test public void typeParameterizedFieldsAreSerializable_withEmpty() { HasTypeParameters autoValue = HasTypeParameters.builder().setA(A).build(); HasTypeParameters actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @SerializableAutoValue @AutoValue abstract static class ImmutableListSerializableAutoValue implements Serializable { abstract ImmutableList> payload(); static ImmutableListSerializableAutoValue.Builder builder() { return new AutoValue_SerializableAutoValueExtensionTest_ImmutableListSerializableAutoValue .Builder(); } @AutoValue.Builder abstract static class Builder { abstract ImmutableListSerializableAutoValue.Builder setPayload( ImmutableList> payload); abstract ImmutableListSerializableAutoValue build(); } } @Test public void immutableList_emptyListSerialized() { ImmutableListSerializableAutoValue autoValue = ImmutableListSerializableAutoValue.builder().setPayload(ImmutableList.of()).build(); ImmutableListSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @Test public void immutableList_allFieldsSetAndSerialized() { ImmutableListSerializableAutoValue autoValue = ImmutableListSerializableAutoValue.builder() .setPayload(ImmutableList.of(Optional.of("a1"), Optional.of("a2"))) .build(); ImmutableListSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @SerializableAutoValue @AutoValue abstract static class ImmutableMapSerializableAutoValue implements Serializable { abstract ImmutableMap, String> a(); abstract ImmutableMap> b(); static ImmutableMapSerializableAutoValue.Builder builder() { return new AutoValue_SerializableAutoValueExtensionTest_ImmutableMapSerializableAutoValue .Builder(); } @AutoValue.Builder abstract static class Builder { abstract ImmutableMapSerializableAutoValue.Builder setA( ImmutableMap, String> a); abstract ImmutableMapSerializableAutoValue.Builder setB( ImmutableMap> b); abstract ImmutableMapSerializableAutoValue build(); } } @Test public void immutableMap_emptyMapSerialized() { ImmutableMapSerializableAutoValue autoValue = ImmutableMapSerializableAutoValue.builder() .setA(ImmutableMap.of()) .setB(ImmutableMap.of()) .build(); ImmutableMapSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @Test public void immutableMap_allFieldsSetAndSerialized() { ImmutableMapSerializableAutoValue autoValue = ImmutableMapSerializableAutoValue.builder() .setA(ImmutableMap.of(Optional.of("key"), "value")) .setB(ImmutableMap.of("key", Optional.of("value"))) .build(); ImmutableMapSerializableAutoValue actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } @SerializableAutoValue @AutoValue abstract static class MultiplePropertiesSameType implements Serializable { abstract String a(); abstract String b(); static MultiplePropertiesSameType.Builder builder() { return new AutoValue_SerializableAutoValueExtensionTest_MultiplePropertiesSameType.Builder(); } @AutoValue.Builder abstract static class Builder { abstract MultiplePropertiesSameType.Builder setA(String value); abstract MultiplePropertiesSameType.Builder setB(String value); abstract MultiplePropertiesSameType build(); } } @Test public void multiplePropertiesSameType_allFieldsSerialized() { MultiplePropertiesSameType autoValue = MultiplePropertiesSameType.builder().setA("A").setB("B").build(); MultiplePropertiesSameType actualAutoValue = SerializableTester.reserialize(autoValue); assertThat(actualAutoValue).isEqualTo(autoValue); } } serializer/000077500000000000000000000000001365703632600341275ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializableSerializerFactoryLoaderTest.java000066400000000000000000000030351365703632600424230ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer; import static com.google.common.truth.Truth.assertThat; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.google.auto.value.extension.serializable.serializer.utils.CompilationAbstractTest; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class SerializerFactoryLoaderTest extends CompilationAbstractTest { @Test public void getFactory_extensionsLoaded() throws Exception { SerializerFactory factory = SerializerFactoryLoader.getFactory(mockProcessingEnvironment); Serializer actualSerializer = factory.getSerializer(typeMirrorOf(String.class)); assertThat(actualSerializer.getClass().getName()) .contains("TestStringSerializerFactory$TestStringSerializer"); } } impl/000077500000000000000000000000001365703632600350705ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializerIdentitySerializerFactoryTest.java000066400000000000000000000045241365703632600437530ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import static com.google.common.truth.Truth.assertThat; import com.google.auto.value.extension.serializable.serializer.utils.CompilationAbstractTest; import com.squareup.javapoet.CodeBlock; import javax.lang.model.type.TypeMirror; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class IdentitySerializerFactoryTest extends CompilationAbstractTest { @Test public void proxyFieldType_isUnchanged() throws Exception { TypeMirror typeMirror = typeMirrorOf(String.class); TypeMirror actualTypeMirror = IdentitySerializerFactory.getSerializer(typeMirror).proxyFieldType(); assertThat(actualTypeMirror).isSameInstanceAs(typeMirror); } @Test public void toProxy_isUnchanged() throws Exception { TypeMirror typeMirror = typeMirrorOf(String.class); CodeBlock inputExpression = CodeBlock.of("x"); CodeBlock outputExpression = IdentitySerializerFactory.getSerializer(typeMirror).toProxy(inputExpression); assertThat(outputExpression).isSameInstanceAs(inputExpression); } @Test public void fromProxy_isUnchanged() throws Exception { TypeMirror typeMirror = typeMirrorOf(String.class); CodeBlock inputExpression = CodeBlock.of("x"); CodeBlock outputExpression = IdentitySerializerFactory.getSerializer(typeMirror).fromProxy(inputExpression); assertThat(outputExpression).isSameInstanceAs(inputExpression); } @Test public void isIdentity() throws Exception { TypeMirror typeMirror = typeMirrorOf(String.class); boolean actualIsIdentity = IdentitySerializerFactory.getSerializer(typeMirror).isIdentity(); assertThat(actualIsIdentity).isTrue(); } } ImmutableListSerializerExtensionTest.java000066400000000000000000000111101365703632600452670ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.utils.CompilationAbstractTest; import com.google.auto.value.extension.serializable.serializer.utils.FakeSerializerFactory; import com.google.common.collect.ImmutableList; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import javax.lang.model.type.TypeMirror; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class ImmutableListSerializerExtensionTest extends CompilationAbstractTest { private static final String FUNCTION_WITH_EXCEPTIONS = "com.google.auto.value.extension.serializable.serializer.runtime.FunctionWithExceptions"; private static final String IMMUTABLE_LIST = "com.google.common.collect.ImmutableList"; private ImmutableListSerializerExtension extension; private FakeSerializerFactory fakeSerializerFactory; @Before public void setUpExtension() { extension = new ImmutableListSerializerExtension(); fakeSerializerFactory = new FakeSerializerFactory(); fakeSerializerFactory.setReturnIdentitySerializer(false); } @Test public void getSerializer_nonImmutableList_emptyReturned() { TypeMirror typeMirror = typeMirrorOf(String.class); Optional actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment); assertThat(actualSerializer).isEmpty(); } @Test public void getSerializer_immutableListWithSerializableContainedType_emptyReturned() { fakeSerializerFactory.setReturnIdentitySerializer(true); TypeMirror typeMirror = typeMirrorOf(ImmutableList.class); Optional actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment); assertThat(actualSerializer).isEmpty(); } @Test public void getSerializer_immutableList_serializerReturned() { TypeMirror typeMirror = typeMirrorOf(ImmutableList.class); Serializer actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); assertThat(actualSerializer.getClass().getName()) .contains("ImmutableListSerializerExtension$ImmutableListSerializer"); } @Test public void proxyFieldType() { TypeMirror typeMirror = declaredTypeOf(ImmutableList.class, Integer.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); TypeMirror actualTypeMirror = serializer.proxyFieldType(); assertThat(typeUtils.isSameType(actualTypeMirror, typeMirror)).isTrue(); } @Test public void toProxy() { TypeMirror typeMirror = declaredTypeOf(ImmutableList.class, Integer.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); CodeBlock actualCodeBlock = serializer.toProxy(CodeBlock.of("x")); assertThat(actualCodeBlock.toString()) .isEqualTo( String.format( "x.stream().map(%s.wrapper(value$ -> value$)).collect(%s.toImmutableList())", FUNCTION_WITH_EXCEPTIONS, IMMUTABLE_LIST)); } @Test public void fromProxy() { TypeMirror typeMirror = declaredTypeOf(ImmutableList.class, Integer.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); CodeBlock actualCodeBlock = serializer.fromProxy(CodeBlock.of("x")); assertThat(actualCodeBlock.toString()) .isEqualTo( String.format( "x.stream().map(%s.wrapper(value$ -> value$)).collect(%s.toImmutableList())", FUNCTION_WITH_EXCEPTIONS, IMMUTABLE_LIST)); } } ImmutableMapSerializerExtensionTest.java000066400000000000000000000125611365703632600451040ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.utils.CompilationAbstractTest; import com.google.auto.value.extension.serializable.serializer.utils.FakeSerializerFactory; import com.google.common.collect.ImmutableMap; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import javax.lang.model.type.TypeMirror; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class ImmutableMapSerializerExtensionTest extends CompilationAbstractTest { private static final String FUNCTION_WITH_EXCEPTIONS = "com.google.auto.value.extension.serializable.serializer.runtime.FunctionWithExceptions"; private static final String IMMUTABLE_MAP = "com.google.common.collect.ImmutableMap"; private static final String INTEGER = "java.lang.Integer"; private static final String STRING = "java.lang.String"; private ImmutableMapSerializerExtension extension; private FakeSerializerFactory fakeSerializerFactory; @Before public void setUpExtension() { extension = new ImmutableMapSerializerExtension(); fakeSerializerFactory = new FakeSerializerFactory(); fakeSerializerFactory.setReturnIdentitySerializer(false); } @Test public void getSerializer_nonImmutableMap_emptyReturned() { TypeMirror typeMirror = typeMirrorOf(String.class); Optional actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment); assertThat(actualSerializer).isEmpty(); } @Test public void getSerializer_immutableMapWithSerializableContainedTypes_emptyReturned() { fakeSerializerFactory.setReturnIdentitySerializer(true); TypeMirror typeMirror = typeMirrorOf(ImmutableMap.class); Optional actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment); assertThat(actualSerializer).isEmpty(); } @Test public void getSerializer_immutableMap_serializerReturned() { TypeMirror typeMirror = typeMirrorOf(ImmutableMap.class); Serializer actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); assertThat(actualSerializer.getClass().getName()) .contains("ImmutableMapSerializerExtension$ImmutableMapSerializer"); } @Test public void proxyFieldType() { TypeMirror typeMirror = declaredTypeOf(ImmutableMap.class, Integer.class, String.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); TypeMirror actualTypeMirror = serializer.proxyFieldType(); assertThat(typeUtils.isSameType(actualTypeMirror, typeMirror)).isTrue(); } @Test public void toProxy() { TypeMirror typeMirror = declaredTypeOf(ImmutableMap.class, Integer.class, String.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); CodeBlock actualCodeBlock = serializer.toProxy(CodeBlock.of("x")); assertThat(actualCodeBlock.toString()) .isEqualTo( String.format( "x.entrySet().stream().collect(%s.toImmutableMap(value$ -> %s.<%s," + " %s>wrapper(element$ -> element$).apply(value$.getKey()), value$ -> %s.<%s," + " %s>wrapper(element$ -> element$).apply(value$.getValue())))", IMMUTABLE_MAP, FUNCTION_WITH_EXCEPTIONS, INTEGER, INTEGER, FUNCTION_WITH_EXCEPTIONS, STRING, STRING)); } @Test public void fromProxy() { TypeMirror typeMirror = declaredTypeOf(ImmutableMap.class, Integer.class, String.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); CodeBlock actualCodeBlock = serializer.fromProxy(CodeBlock.of("x")); assertThat(actualCodeBlock.toString()) .isEqualTo( String.format( "x.entrySet().stream().collect(%s.toImmutableMap(value$ -> %s.<%s," + " %s>wrapper(element$ -> element$).apply(value$.getKey()), value$ -> %s.<%s," + " %s>wrapper(element$ -> element$).apply(value$.getValue())))", IMMUTABLE_MAP, FUNCTION_WITH_EXCEPTIONS, INTEGER, INTEGER, FUNCTION_WITH_EXCEPTIONS, STRING, STRING)); } } OptionalSerializerExtensionTest.java000066400000000000000000000067661365703632600443260ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.utils.CompilationAbstractTest; import com.google.auto.value.extension.serializable.serializer.utils.FakeSerializerFactory; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import javax.lang.model.type.TypeMirror; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class OptionalSerializerExtensionTest extends CompilationAbstractTest { private OptionalSerializerExtension extension; private FakeSerializerFactory fakeSerializerFactory; @Before public void setUpExtension() { extension = new OptionalSerializerExtension(); fakeSerializerFactory = new FakeSerializerFactory(); } @Test public void getSerializer_nonOptional_emptyReturned() { TypeMirror typeMirror = typeMirrorOf(String.class); Optional actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment); assertThat(actualSerializer).isEmpty(); } @Test public void getSerializer_optional_serializerReturned() { TypeMirror typeMirror = typeMirrorOf(Optional.class); Serializer actualSerializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); assertThat(actualSerializer.getClass().getName()) .contains("OptionalSerializerExtension$OptionalSerializer"); } @Test public void proxyFieldType() { TypeMirror typeMirror = declaredTypeOf(Optional.class, Integer.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); TypeMirror actualTypeMirror = serializer.proxyFieldType(); assertThat(actualTypeMirror).isEqualTo(typeMirrorOf(Integer.class)); } @Test public void toProxy() { TypeMirror typeMirror = declaredTypeOf(Optional.class, Integer.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); CodeBlock actualCodeBlock = serializer.toProxy(CodeBlock.of("x")); assertThat(actualCodeBlock.toString()).isEqualTo("x.isPresent() ? x.get() : null"); } @Test public void fromProxy() { TypeMirror typeMirror = declaredTypeOf(Optional.class, Integer.class); Serializer serializer = extension.getSerializer(typeMirror, fakeSerializerFactory, mockProcessingEnvironment).get(); CodeBlock actualCodeBlock = serializer.fromProxy(CodeBlock.of("x")); assertThat(actualCodeBlock.toString()) .isEqualTo("java.util.Optional.ofNullable(x == null ? null : x)"); } } SerializerFactoryImplTest.java000066400000000000000000000041341365703632600430600ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/impl/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.impl; import static com.google.common.truth.Truth.assertThat; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.utils.CompilationAbstractTest; import com.google.auto.value.extension.serializable.serializer.utils.TestStringSerializerFactory; import com.google.common.collect.ImmutableList; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class SerializerFactoryImplTest extends CompilationAbstractTest { @Test public void getSerializer_emptyFactories_identitySerializerReturned() throws Exception { SerializerFactoryImpl factory = new SerializerFactoryImpl(ImmutableList.of(), mockProcessingEnvironment); Serializer actualSerializer = factory.getSerializer(typeMirrorOf(String.class)); assertThat(actualSerializer.getClass().getName()) .contains("IdentitySerializerFactory$IdentitySerializer"); } @Test public void getSerializer_factoriesProvided_factoryReturned() throws Exception { SerializerFactoryImpl factory = new SerializerFactoryImpl( ImmutableList.of(new TestStringSerializerFactory()), mockProcessingEnvironment); Serializer actualSerializer = factory.getSerializer(typeMirrorOf(String.class)); assertThat(actualSerializer.getClass().getName()) .contains("TestStringSerializerFactory$TestStringSerializer"); } } utils/000077500000000000000000000000001365703632600352675ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializerCompilationAbstractTest.java000066400000000000000000000055631365703632600427450ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.utils; import static org.mockito.Mockito.when; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.testing.compile.CompilationRule; import java.util.Arrays; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.junit.Before; import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @RunWith(JUnit4.class) public abstract class CompilationAbstractTest { @Rule public final CompilationRule compilationRule = new CompilationRule(); @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @Mock protected ProcessingEnvironment mockProcessingEnvironment; @Mock protected Messager mockMessager; protected Types typeUtils; protected Elements elementUtils; @Before public final void setUp() { typeUtils = compilationRule.getTypes(); elementUtils = compilationRule.getElements(); when(mockProcessingEnvironment.getTypeUtils()).thenReturn(typeUtils); when(mockProcessingEnvironment.getElementUtils()).thenReturn(elementUtils); when(mockProcessingEnvironment.getMessager()).thenReturn(mockMessager); } protected TypeElement typeElementOf(Class c) { return elementUtils.getTypeElement(c.getCanonicalName()); } protected TypeMirror typeMirrorOf(Class c) { return typeElementOf(c).asType(); } protected DeclaredType declaredTypeOf(Class enclosingClass, Class containedClass) { return typeUtils.getDeclaredType(typeElementOf(enclosingClass), typeMirrorOf(containedClass)); } protected DeclaredType declaredTypeOf(Class enclosingClass, Class... classArgs) { return typeUtils.getDeclaredType( typeElementOf(enclosingClass), Iterables.toArray( Arrays.stream(classArgs) .map(this::typeMirrorOf) .collect(ImmutableList.toImmutableList()), TypeMirror.class)); } } FakeSerializerFactory.java000066400000000000000000000044471365703632600423730ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.utils; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.squareup.javapoet.CodeBlock; import javax.lang.model.type.TypeMirror; /** A fake {@link SerializerFactory} that returns an identity serializer used for tests. */ public final class FakeSerializerFactory implements SerializerFactory { private boolean isIdentity = true; public FakeSerializerFactory() {} /** * Set if this factory should return a serializer that is considered an identity serializer. * *

      The underlying fake serializer implementation will always be an identity serializer. This * only changes the {@link Serializer#isIdentity} return value. */ public void setReturnIdentitySerializer(boolean isIdentity) { this.isIdentity = isIdentity; } @Override public Serializer getSerializer(TypeMirror type) { return new FakeIdentitySerializer(type, isIdentity); } private static class FakeIdentitySerializer implements Serializer { private final TypeMirror typeMirror; private final boolean isIdentity; FakeIdentitySerializer(TypeMirror typeMirror, boolean isIdentity) { this.typeMirror = typeMirror; this.isIdentity = isIdentity; } @Override public TypeMirror proxyFieldType() { return typeMirror; } @Override public CodeBlock toProxy(CodeBlock expression) { return expression; } @Override public CodeBlock fromProxy(CodeBlock expression) { return expression; } @Override public boolean isIdentity() { return isIdentity; } } } TestStringSerializerFactory.java000066400000000000000000000052361365703632600436300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/* * Copyright 2020 Google LLC * * 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.google.auto.value.extension.serializable.serializer.utils; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.auto.service.AutoService; import com.google.auto.value.extension.serializable.serializer.interfaces.Serializer; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension; import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory; import com.squareup.javapoet.CodeBlock; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; /** A test implementation of {@link SerializerExtension} that overwrites a string field's value. */ @AutoService(SerializerExtension.class) public final class TestStringSerializerFactory implements SerializerExtension { public TestStringSerializerFactory() {} @Override public Optional getSerializer( TypeMirror typeMirror, SerializerFactory factory, ProcessingEnvironment processingEnv) { if (typeMirror.getKind() != TypeKind.DECLARED) { return Optional.empty(); } DeclaredType declaredType = MoreTypes.asDeclared(typeMirror); TypeElement typeElement = MoreElements.asType(declaredType.asElement()); if (typeElement.getQualifiedName().contentEquals("java.lang.String")) { return Optional.of(new TestStringSerializer(typeMirror)); } return Optional.empty(); } private static class TestStringSerializer implements Serializer { private final TypeMirror typeMirror; TestStringSerializer(TypeMirror typeMirror) { this.typeMirror = typeMirror; } @Override public TypeMirror proxyFieldType() { return typeMirror; } @Override public CodeBlock toProxy(CodeBlock expression) { return CodeBlock.of("$S", "test"); } @Override public CodeBlock fromProxy(CodeBlock expression) { return CodeBlock.of("$S", "test"); } } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/000077500000000000000000000000001365703632600273725ustar00rootroot00000000000000AutoAnnotationCompilationTest.java000066400000000000000000000463511365703632600361710ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth8.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoAnnotationCompilationTest { @Test public void testSimple() { JavaFileObject myAnnotationJavaFile = JavaFileObjects.forSourceLines( "com.example.annotations.MyAnnotation", "package com.example.annotations;", "", "import com.example.enums.MyEnum;", "", "public @interface MyAnnotation {", " MyEnum value();", " int defaultedValue() default 23;", "}"); int invariableHash = ("defaultedValue".hashCode() * 127) ^ 23; JavaFileObject myEnumJavaFile = JavaFileObjects.forSourceLines( "com.example.enums.MyEnum", "package com.example.enums;", "", "public enum MyEnum {", " ONE", "}"); JavaFileObject annotationFactoryJavaFile = JavaFileObjects.forSourceLines( "com.example.factories.AnnotationFactory", "package com.example.factories;", "", "import com.google.auto.value.AutoAnnotation;", "import com.example.annotations.MyAnnotation;", "import com.example.enums.MyEnum;", "", "public class AnnotationFactory {", " @AutoAnnotation", " public static MyAnnotation newMyAnnotation(MyEnum value) {", " return new AutoAnnotation_AnnotationFactory_newMyAnnotation(value);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "com.example.factories.AutoAnnotation_AnnotationFactory_newMyAnnotation", "package com.example.factories;", "", "import com.example.annotations.MyAnnotation;", "import com.example.enums.MyEnum;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", "final class AutoAnnotation_AnnotationFactory_newMyAnnotation", " implements MyAnnotation {", " private final MyEnum value;", " private static final int defaultedValue = 23;", "", " AutoAnnotation_AnnotationFactory_newMyAnnotation(MyEnum value) {", " if (value == null) {", " throw new NullPointerException(\"Null value\");", " }", " this.value = value;", " }", "", " @Override public Class annotationType() {", " return MyAnnotation.class;", " }", "", " @Override public MyEnum value() {", " return value;", " }", "", " @Override public int defaultedValue() {", " return defaultedValue;", " }", "", " @Override public String toString() {", " StringBuilder sb = new StringBuilder(\"@com.example.annotations.MyAnnotation(\");", " sb.append(value);", " return sb.append(')').toString();", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof MyAnnotation) {", " MyAnnotation that = (MyAnnotation) o;", " return (value.equals(that.value()))", " && (defaultedValue == that.defaultedValue());", " }", " return false;", " }", "", " @Override public int hashCode() {", " return ", " " + invariableHash, " + (" + 127 * "value".hashCode() + " ^ (value.hashCode()))", " ;", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(annotationFactoryJavaFile, myAnnotationJavaFile, myEnumJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile( "com.example.factories.AutoAnnotation_AnnotationFactory_newMyAnnotation") .hasSourceEquivalentTo(expectedOutput); } @Test public void testEmptyPackage() { JavaFileObject myAnnotationJavaFile = JavaFileObjects.forSourceLines( "MyAnnotation", // "public @interface MyAnnotation {}"); JavaFileObject annotationFactoryJavaFile = JavaFileObjects.forSourceLines( "AnnotationFactory", "import com.google.auto.value.AutoAnnotation;", "", "public class AnnotationFactory {", " @AutoAnnotation", " public static MyAnnotation newMyAnnotation() {", " return new AutoAnnotation_AnnotationFactory_newMyAnnotation();", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "AutoAnnotation_AnnotationFactory_newMyAnnotation", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", "final class AutoAnnotation_AnnotationFactory_newMyAnnotation", " implements MyAnnotation {", " AutoAnnotation_AnnotationFactory_newMyAnnotation() {", " }", "", " @Override public Class annotationType() {", " return MyAnnotation.class;", " }", "", " @Override public String toString() {", " StringBuilder sb = new StringBuilder(\"@MyAnnotation(\");", " return sb.append(')').toString();", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof MyAnnotation) {", " return true;", " }", " return false;", " }", "", " @Override public int hashCode() {", " return 0;", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(annotationFactoryJavaFile, myAnnotationJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("AutoAnnotation_AnnotationFactory_newMyAnnotation") .hasSourceEquivalentTo(expectedOutput); } @Test public void testGwtSimple() { JavaFileObject myAnnotationJavaFile = JavaFileObjects.forSourceLines( "com.example.annotations.MyAnnotation", "package com.example.annotations;", "", "import com.google.common.annotations.GwtCompatible;", "", "@GwtCompatible", "public @interface MyAnnotation {", " int[] value();", "}"); JavaFileObject gwtCompatibleJavaFile = JavaFileObjects.forSourceLines( "com.google.common.annotations.GwtCompatible", "package com.google.common.annotations;", "", "public @interface GwtCompatible {}"); JavaFileObject annotationFactoryJavaFile = JavaFileObjects.forSourceLines( "com.example.factories.AnnotationFactory", "package com.example.factories;", "", "import com.google.auto.value.AutoAnnotation;", "import com.example.annotations.MyAnnotation;", "", "public class AnnotationFactory {", " @AutoAnnotation", " public static MyAnnotation newMyAnnotation(int[] value) {", " return new AutoAnnotation_AnnotationFactory_newMyAnnotation(value);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "com.example.factories.AutoAnnotation_AnnotationFactory_newMyAnnotation", "package com.example.factories;", "", "import com.example.annotations.MyAnnotation;", "import java.util.Arrays;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation" + " {", " private final int[] value;", "", " AutoAnnotation_AnnotationFactory_newMyAnnotation(int[] value) {", " if (value == null) {", " throw new NullPointerException(\"Null value\");", " }", " this.value = Arrays.copyOf(value, value.length);", " }", "", " @Override public Class annotationType() {", " return MyAnnotation.class;", " }", "", " @Override public int[] value() {", " return Arrays.copyOf(value, value.length);", " }", "", " @Override public String toString() {", " StringBuilder sb = new StringBuilder(\"@com.example.annotations.MyAnnotation(\");", " sb.append(Arrays.toString(value));", " return sb.append(')').toString();", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof MyAnnotation) {", " MyAnnotation that = (MyAnnotation) o;", " return (Arrays.equals(value,", " (that instanceof AutoAnnotation_AnnotationFactory_newMyAnnotation)", " ? ((AutoAnnotation_AnnotationFactory_newMyAnnotation) that).value", " : that.value()));", " }", " return false;", " }", "", " @Override public int hashCode() {", " return ", " + (" + 127 * "value".hashCode() + " ^ (Arrays.hashCode(value)));", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(annotationFactoryJavaFile, myAnnotationJavaFile, gwtCompatibleJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile( "com.example.factories.AutoAnnotation_AnnotationFactory_newMyAnnotation") .hasSourceEquivalentTo(expectedOutput); } @Test public void testCollectionsForArrays() { JavaFileObject myAnnotationJavaFile = JavaFileObjects.forSourceLines( "com.example.annotations.MyAnnotation", "package com.example.annotations;", "", "import com.example.enums.MyEnum;", "", "public @interface MyAnnotation {", " int[] value();", " MyEnum[] enums() default {};", "}"); JavaFileObject myEnumJavaFile = JavaFileObjects.forSourceLines( "com.example.enums.MyEnum", "package com.example.enums;", "", "public enum MyEnum {", " ONE", "}"); JavaFileObject annotationFactoryJavaFile = JavaFileObjects.forSourceLines( "com.example.factories.AnnotationFactory", "package com.example.factories;", "", "import com.google.auto.value.AutoAnnotation;", "import com.example.annotations.MyAnnotation;", "import com.example.enums.MyEnum;", "", "import java.util.List;", "import java.util.Set;", "", "public class AnnotationFactory {", " @AutoAnnotation", " public static MyAnnotation newMyAnnotation(", " List value, Set enums) {", " return new AutoAnnotation_AnnotationFactory_newMyAnnotation(value, enums);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "com.example.factories.AutoAnnotation_AnnotationFactory_newMyAnnotation", "package com.example.factories;", "", "import com.example.annotations.MyAnnotation;", "import com.example.enums.MyEnum;", "import java.util.Arrays;", "import java.util.Collection;", "import java.util.List;", "import java.util.Set;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")", "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation" + " {", " private final int[] value;", " private final MyEnum[] enums;", "", " AutoAnnotation_AnnotationFactory_newMyAnnotation(", " List value,", " Set enums) {", " if (value == null) {", " throw new NullPointerException(\"Null value\");", " }", " this.value = intArrayFromCollection(value);", " if (enums == null) {", " throw new NullPointerException(\"Null enums\");", " }", " this.enums = enums.toArray(new MyEnum[0];", " }", "", " @Override public Class annotationType() {", " return MyAnnotation.class;", " }", "", " @Override public int[] value() {", " return value.clone();", " }", "", " @Override public MyEnum[] enums() {", " return enums.clone();", " }", "", " @Override public String toString() {", " StringBuilder sb = new StringBuilder(\"@com.example.annotations.MyAnnotation(\");", " sb.append(\"value=\");", " sb.append(Arrays.toString(value));", " sb.append(\", \");", " sb.append(\"enums=\");", " sb.append(Arrays.toString(enums));", " return sb.append(')').toString();", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof MyAnnotation) {", " MyAnnotation that = (MyAnnotation) o;", " return (Arrays.equals(value,", " (that instanceof AutoAnnotation_AnnotationFactory_newMyAnnotation)", " ? ((AutoAnnotation_AnnotationFactory_newMyAnnotation) that).value", " : that.value()))", " && (Arrays.equals(enums,", " (that instanceof AutoAnnotation_AnnotationFactory_newMyAnnotation)", " ? ((AutoAnnotation_AnnotationFactory_newMyAnnotation) that).enums", " : that.enums()))", " }", " return false;", " }", "", " @Override public int hashCode() {", " return ", " + (" + 127 * "value".hashCode() + " ^ (Arrays.hashCode(value)))", " + (" + 127 * "enums".hashCode() + " ^ (Arrays.hashCode(enums)));", " }", "", " private static int[] intArrayFromCollection(Collection c) {", " int[] a = new int[c.size()];", " int i = 0;", " for (int x : c) {", " a[i++] = x;", " }", " return a;", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(annotationFactoryJavaFile, myEnumJavaFile, myAnnotationJavaFile); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile( "com.example.factories.AutoAnnotation_AnnotationFactory_newMyAnnotation") .hasSourceEquivalentTo(expectedOutput); } @Test public void testMissingClass() { // Test that referring to an undefined annotation does not trigger @AutoAnnotation processing. // The class Erroneous references an undefined annotation @NotAutoAnnotation. If we didn't have // any special treatment of undefined types then we could run into a compiler bug where // AutoAnnotationProcessor would think that a method annotated with @NotAutoAnnotation was in // fact annotated with @AutoAnnotation. As it is, we do get an error about @NotAutoAnnotation // being undefined, and we do not get an error complaining that this supposed @AutoAnnotation // method is not static. We do need to have @AutoAnnotation appear somewhere so that the // processor will run. JavaFileObject erroneousJavaFileObject = JavaFileObjects.forSourceLines( "com.example.annotations.Erroneous", "package com.example.annotations;", "", "import com.google.auto.value.AutoAnnotation;", "", "public class Erroneous {", " @interface Empty {}", " @AutoAnnotation static Empty newEmpty() {}", " @NotAutoAnnotation Empty notNewEmpty() {}", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(erroneousJavaFileObject); assertThat(compilation) .hadErrorContaining("NotAutoAnnotation") .inFile(erroneousJavaFileObject) .onLineContaining("@NotAutoAnnotation"); assertThat( compilation.errors().stream() .map(diag -> diag.getMessage(null)) .filter(m -> m.contains("static"))) .isEmpty(); } } AutoAnnotationErrorsTest.java000066400000000000000000000350651365703632600351670ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for compilation errors with the AutoAnnotation processor. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoAnnotationErrorsTest { private static final JavaFileObject TEST_ANNOTATION = JavaFileObjects.forSourceLines( "com.example.TestAnnotation", "package com.example;", "", "public @interface TestAnnotation {", " int value();", "}"); @Test public void testCorrect() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation).succeededWithoutWarnings(); } @Test public void testNotStatic() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation TestAnnotation newTestAnnotation(int value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("must be static") .inFile(testSource) .onLineContaining("TestAnnotation newTestAnnotation(int value)"); } @Test public void testDoesNotReturnAnnotation() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static String newString(int value) {", " return new AutoAnnotation_Test_newString(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("must be an annotation type, not java.lang.String") .inFile(testSource) .onLineContaining("static String newString(int value)"); } @Test public void testOverload() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "", " @AutoAnnotation static TestAnnotation newTestAnnotation(Integer value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("@AutoAnnotation methods cannot be overloaded") .inFile(testSource) .onLineContaining("newTestAnnotation(Integer value)"); } // Overload detection used to detect all @AutoAnnotation methods that resulted in // annotation class of the same SimpleName as being an overload. // This verifies that implementations in different packages work correctly. @Test public void testSameNameDifferentPackagesDoesNotTriggerOverload() { JavaFileObject fooTestSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); JavaFileObject barTestSource = JavaFileObjects.forSourceLines( "com.bar.Test", "package com.bar;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(int value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(fooTestSource, barTestSource, TEST_ANNOTATION); assertThat(compilation).succeededWithoutWarnings(); } @Test public void testWrongName() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(int fred) {", " return new AutoAnnotation_Test_newTestAnnotation(fred);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("method parameter 'fred' must have the same name") .inFile(testSource) .onLineContaining("newTestAnnotation(int fred)"); } @Test public void testWrongType() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(String value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining( "method parameter 'value' has type java.lang.String " + "but com.example.TestAnnotation.value has type int") .inFile(testSource) .onLineContaining("newTestAnnotation(String value)"); } @Test public void testWrongTypeCollection() { JavaFileObject testAnnotation = JavaFileObjects.forSourceLines( "com.example.TestAnnotation", "package com.example;", "", "public @interface TestAnnotation {", " int[] value();", "}"); String[] wrongTypes = { "java.util.List", "java.util.List", "java.util.Set", "java.util.Map", "java.util.concurrent.Callable", "java.util.List", }; for (String wrongType : wrongTypes) { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(" + wrongType + " value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(testSource, testAnnotation); assertThat(compilation) .hadErrorContaining( "method parameter 'value' has type " + wrongType + " but com.example.TestAnnotation.value has type int[]") .inFile(testSource) .onLineContaining("TestAnnotation newTestAnnotation("); } } @Test public void testExtraParameters() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(int value, int other) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining( "method parameter 'other' must have the same name as a member of " + "com.example.TestAnnotation") .inFile(testSource) .onLineContaining("newTestAnnotation(int value, int other)"); } @Test public void testMissingParameters() { JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation() {", " return new AutoAnnotation_Test_newTestAnnotation();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(TEST_ANNOTATION, testSource); assertThat(compilation) .hadErrorContaining("method needs a parameter with name 'value' and type int") .inFile(testSource) .onLineContaining("TestAnnotation newTestAnnotation()"); } @Test public void testAnnotationValuedDefaultsNotSupportedYet() { JavaFileObject annotationSource = JavaFileObjects.forSourceLines( "com.example.TestAnnotation", "package com.example;", "", "public @interface TestAnnotation {", " String value();", " Override optionalAnnotation() default @Override;", "}"); JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(String value) {", " return new AutoAnnotation_Test_newTestAnnotation(value);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(annotationSource, testSource); assertThat(compilation) .hadErrorContaining( "@AutoAnnotation cannot yet supply a default value for annotation-valued member " + "'optionalAnnotation'") .inFile(testSource) .onLineContaining("TestAnnotation newTestAnnotation(String value)"); } @Test public void testAnnotationMemberNameConflictWithGeneratedLocal() { JavaFileObject annotationSource = JavaFileObjects.forSourceLines( "com.example.TestAnnotation", "package com.example;", "", "import java.lang.annotation.Annotation;", "", "public @interface TestAnnotation {", " Class[] value();", " int value$();", "}"); JavaFileObject testSource = JavaFileObjects.forSourceLines( "com.foo.Test", "package com.foo;", "", "import java.lang.annotation.Annotation;", "import java.util.Collection;", "", "import com.example.TestAnnotation;", "import com.google.auto.value.AutoAnnotation;", "", "class Test {", " @AutoAnnotation static TestAnnotation newTestAnnotation(", " Collection> value, int value$) {", " return new AutoAnnotation_Test_newTestAnnotation(value, value$);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoAnnotationProcessor()) .compile(annotationSource, testSource); assertThat(compilation) .hadErrorContaining("variable value$ is already defined in constructor"); } } AutoOneOfCompilationTest.java000066400000000000000000000426041365703632600350620ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.common.truth.Expect; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoOneOfCompilationTest { @Rule public final Expect expect = Expect.create(); @Test public void success() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.TaskResult", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "import java.io.Serializable;", "", "@AutoOneOf(TaskResult.Kind.class)", "public abstract class TaskResult {", " public enum Kind {VALUE, EXCEPTION, EMPTY}", " public abstract Kind getKind();", "", " public abstract V value();", " public abstract Throwable exception();", " public abstract void empty();", "", " public static TaskResult value(V value) {", " return AutoOneOf_TaskResult.value(value);", " }", "", " public static TaskResult exception(T exception) {", " return AutoOneOf_TaskResult.exception(exception);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoOneOf_TaskResult", "package foo.bar;", "", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"com.google.auto.value.processor.AutoOneOfProcessor\")", "final class AutoOneOf_TaskResult {", " private AutoOneOf_TaskResult() {} // There are no instances of this type.", "", " static TaskResult value(V value) {", " if (value == null) {", " throw new NullPointerException();", " }", " return new Impl_value(value);", " }", "", " static TaskResult exception(Throwable exception) {", " if (exception == null) {", " throw new NullPointerException();", " }", " return new Impl_exception(exception);", " }", "", " @SuppressWarnings(\"unchecked\") // type parameters are unused in void instances", " static TaskResult empty() {", " return (TaskResult) Impl_empty.INSTANCE;", " }", "", " // Parent class that each implementation will inherit from.", " private abstract static class Parent_ " + "extends TaskResult {", " @Override", " public V value() {", " throw new UnsupportedOperationException(getKind().toString());", " }", "", " @Override", " public Throwable exception() {", " throw new UnsupportedOperationException(getKind().toString());", " }", "", " @Override", " public void empty() {", " throw new UnsupportedOperationException(getKind().toString());", " }", " }", "", " // Implementation when the contained property is \"value\".", " private static final class Impl_value " + "extends Parent_ {", " private final V value;", "", " Impl_value(V value) {", " this.value = value;", " }", "", " @Override", " public V value() {", " return value;", " }", "", " @Override", " public String toString() {", " return \"TaskResult{value=\" + this.value + \"}\";", " }", "", " @Override", " public boolean equals(Object x) {", " if (x instanceof TaskResult) {", " TaskResult that = (TaskResult) x;", " return this.getKind() == that.getKind()", " && this.value.equals(that.value());", " } else {", " return false;", " }", " }", "", " @Override", " public int hashCode() {", " return value.hashCode();", " }", "", " @Override", " public TaskResult.Kind getKind() {", " return TaskResult.Kind.VALUE;", " }", " }", "", " // Implementation when the contained property is \"exception\".", " private static final class Impl_exception " + "extends Parent_ {", " private final Throwable exception;", "", " Impl_exception(Throwable exception) {", " this.exception = exception;", " }", "", " @Override", " public Throwable exception() {", " return exception;", " }", "", " @Override", " public String toString() {", " return \"TaskResult{exception=\" + this.exception + \"}\";", " }", "", " @Override", " public boolean equals(Object x) {", " if (x instanceof TaskResult) {", " TaskResult that = (TaskResult) x;", " return this.getKind() == that.getKind()", " && this.exception.equals(that.exception());", " } else {", " return false;", " }", " }", "", " @Override", " public int hashCode() {", " return exception.hashCode();", " }", "", " @Override", " public TaskResult.Kind getKind() {", " return TaskResult.Kind.EXCEPTION;", " }", " }", "", " // Implementation when the contained property is \"empty\".", " private static final class Impl_empty " + "extends Parent_ {", " static final Impl_empty INSTANCE = new Impl_empty<>();", "", " private Impl_empty() {}", "", " @Override", " public void empty() {}", "", " @Override", " public String toString() {", " return \"TaskResult{empty}\";", " }", "", " @Override", " public boolean equals(Object x) {", " return x == this;", " }", "", " @Override", " public int hashCode() {", " return System.identityHashCode(this);", " }", "", " @Override", " public TaskResult.Kind getKind() {", " return TaskResult.Kind.EMPTY;", " }", " }"); Compilation compilation = javac() .withProcessors(new AutoOneOfProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoOneOf_TaskResult") .hasSourceEquivalentTo(expectedOutput); } @Test public void voidInstanceWithoutGenericTypeParameters() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Nothing", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "import java.io.Serializable;", "", "@AutoOneOf(Nothing.Kind.class)", "abstract class Nothing {", "", " enum Kind {NOTHING}", "", " abstract Kind kind();", "", " abstract void nothing();", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.Nothing", "package foo.bar;", "", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"com.google.auto.value.processor.AutoOneOfProcessor\")", "final class AutoOneOf_Nothing {", " private AutoOneOf_Nothing() {} // There are no instances of this type.", "", " static Nothing nothing() {", " return Impl_nothing.INSTANCE;", " }", "", " // Parent class that each implementation will inherit from.", " private abstract static class Parent_ extends Nothing {", " @Override", " void nothing() {", " throw new UnsupportedOperationException(kind().toString());", " }", " }", "", " // Implementation when the contained property is \"nothing\".", " private static final class Impl_nothing extends Parent_ {", " // There is only one instance of this class.", " static final Impl_nothing INSTANCE = new Impl_nothing();", "", " private Impl_nothing() {}", "", " @Override", " public void nothing() {}", "", " @Override", " public String toString() {", " return \"Nothing{nothing}\";", " }", "", " @Override", " public boolean equals(Object x) {", " return x == this;", " }", "", " @Override", " public int hashCode() {", " return System.identityHashCode(this);", " }", "", " @Override", " public Nothing.Kind kind() {", " return Nothing.Kind.NOTHING;", " }", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoOneOfProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoOneOf_Nothing") .hasSourceEquivalentTo(expectedOutput); } @Test public void noKindGetter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Pet", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "", "@AutoOneOf(Pet.Kind.class)", "public abstract class Pet {", " public enum Kind {DOG, CAT}", " public abstract String dog();", " public abstract String cat();", "}"); Compilation compilation = javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "foo.bar.Pet must have a no-arg abstract method returning foo.bar.Pet.Kind") .inFile(javaFileObject) .onLineContaining("class Pet"); } @Test public void kindGetterHasParam() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Pet", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "", "@AutoOneOf(Pet.Kind.class)", "public abstract class Pet {", " public enum Kind {DOG, CAT}", " public abstract Kind getKind(String wut);", " public abstract String dog();", " public abstract String cat();", "}"); Compilation compilation = javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "foo.bar.Pet must have a no-arg abstract method returning foo.bar.Pet.Kind") .inFile(javaFileObject) .onLineContaining("class Pet"); } @Test public void twoKindGetters() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Pet", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "", "@AutoOneOf(Pet.Kind.class)", "public abstract class Pet {", " public enum Kind {DOG, CAT}", " public abstract Kind getKind();", " public abstract Kind alsoGetKind();", " public abstract String dog();", " public abstract String cat();", "}"); Compilation compilation = javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("More than one abstract method returns foo.bar.Pet.Kind") .inFile(javaFileObject) .onLineContaining("getKind"); assertThat(compilation) .hadErrorContaining("More than one abstract method returns foo.bar.Pet.Kind") .inFile(javaFileObject) .onLineContaining("alsoGetKind"); } @Test public void enumMissingCase() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Pet", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "", "@AutoOneOf(Pet.Kind.class)", "public abstract class Pet {", " public enum Kind {DOG}", " public abstract Kind getKind();", " public abstract String dog();", " public abstract String cat();", "}"); Compilation compilation = javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Enum has no constant with name corresponding to property 'cat'") .inFile(javaFileObject) .onLineContaining("enum Kind"); } @Test public void enumExtraCase() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Pet", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "", "@AutoOneOf(Pet.Kind.class)", "public abstract class Pet {", " public enum Kind {", " DOG,", " CAT,", " GERBIL,", " }", " public abstract Kind getKind();", " public abstract String dog();", " public abstract String cat();", "}"); Compilation compilation = javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Name of enum constant 'GERBIL' does not correspond to any property name") .inFile(javaFileObject) .onLineContaining("GERBIL"); } @Test public void cantBeNullable() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Pet", "package foo.bar;", "", "import com.google.auto.value.AutoOneOf;", "", "@AutoOneOf(Pet.Kind.class)", "public abstract class Pet {", " @interface Nullable {}", "", " public enum Kind {", " DOG,", " CAT,", " }", " public abstract Kind getKind();", " public abstract @Nullable String dog();", " public abstract String cat();", "}"); Compilation compilation = javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("@AutoOneOf properties cannot be @Nullable") .inFile(javaFileObject) .onLineContaining("@Nullable String dog()"); } } AutoValueCompilationTest.java000066400000000000000000003556201365703632600351350ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.CompilationSubject.compilations; import static com.google.testing.compile.Compiler.javac; import static java.util.stream.Collectors.joining; import com.google.common.collect.ImmutableSet; import com.google.common.truth.Expect; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import java.io.IOException; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; import javax.lang.model.util.ElementFilter; import javax.tools.JavaFileObject; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class AutoValueCompilationTest { @Rule public final Expect expect = Expect.create(); @Test public void simpleSuccess() { // Positive test case that ensures we generate the expected code for at least one case. // Most AutoValue code-generation tests are functional, meaning that we check that the generated // code does the right thing rather than checking what it looks like, but this test is a sanity // check that we are not generating correct but weird code. JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract long buh();", "", " public static Baz create(long buh) {", " return new AutoValue_Baz(buh);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz", "package foo.bar;", "", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoValueProcessor.class.getName() + "\")", "final class AutoValue_Baz extends Baz {", " private final long buh;", "", " AutoValue_Baz(long buh) {", " this.buh = buh;", " }", "", " @Override public long buh() {", " return buh;", " }", "", " @Override public String toString() {", " return \"Baz{\"", " + \"buh=\" + buh", " + \"}\";", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Baz) {", " Baz that = (Baz) o;", " return this.buh == that.buh();", " }", " return false;", " }", "", " @Override public int hashCode() {", " int h$ = 1;", " h$ *= 1000003;", " h$ ^= (int) ((buh >>> 32) ^ buh);", " return h$;", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedOutput); } @Test public void importTwoWays() { // Test that referring to the same class in two different ways does not confuse the import logic // into thinking it is two different classes and that therefore it can't import. The code here // is nonsensical but successfully reproduces a real problem, which is that a TypeMirror that is // extracted using Elements.getTypeElement(name).asType() does not compare equal to one that is // extracted from ExecutableElement.getReturnType(), even though Types.isSameType considers them // equal. So unless we are careful, the java.util.Arrays that we import explicitly to use its // methods will appear different from the java.util.Arrays that is the return type of the // arrays() method here. JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "import java.util.Arrays;", "", "@AutoValue", "public abstract class Baz {", " @SuppressWarnings(\"mutable\")", " public abstract int[] ints();", " public abstract Arrays arrays();", "", " public static Baz create(int[] ints, Arrays arrays) {", " return new AutoValue_Baz(ints, arrays);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz", "package foo.bar;", "", "import java.util.Arrays;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"" + AutoValueProcessor.class.getName() + "\")", "final class AutoValue_Baz extends Baz {", " private final int[] ints;", " private final Arrays arrays;", "", " AutoValue_Baz(int[] ints, Arrays arrays) {", " if (ints == null) {", " throw new NullPointerException(\"Null ints\");", " }", " this.ints = ints;", " if (arrays == null) {", " throw new NullPointerException(\"Null arrays\");", " }", " this.arrays = arrays;", " }", "", " @SuppressWarnings(\"mutable\")", " @Override public int[] ints() {", " return ints;", " }", "", " @Override public Arrays arrays() {", " return arrays;", " }", "", " @Override public String toString() {", " return \"Baz{\"", " + \"ints=\" + Arrays.toString(ints) + \", \"", " + \"arrays=\" + arrays", " + \"}\";", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Baz) {", " Baz that = (Baz) o;", " return Arrays.equals(this.ints, (that instanceof AutoValue_Baz) " + "? ((AutoValue_Baz) that).ints : that.ints())", " && this.arrays.equals(that.arrays());", " }", " return false;", " }", "", " @Override public int hashCode() {", " int h$ = 1;", " h$ *= 1000003;", " h$ ^= Arrays.hashCode(ints);", " h$ *= 1000003;", " h$ ^= arrays.hashCode();", " return h$;", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedOutput); } @Test public void testNoWarningsFromGenerics() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue", "public abstract class Baz {", " public abstract T t();", " public abstract U u();", " public static Baz create(T t, U u) {", " return new AutoValue_Baz(t, u);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); } @Test public void testNestedParameterizedTypesWithTypeAnnotations() { JavaFileObject annotFileObject = JavaFileObjects.forSourceLines( "foo.bar.Annot", "package foo.bar;", "", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Target;", "", "@Target(ElementType.TYPE_USE)", "public @interface Annot {", " int value();", "}"); JavaFileObject outerFileObject = JavaFileObjects.forSourceLines( "foo.baz.OuterWithTypeParam", "package foo.baz;", "", "public class OuterWithTypeParam {", " public class InnerWithTypeParam {}", "}"); JavaFileObject nestyFileObject = JavaFileObjects.forSourceLines( "com.example.Nesty", "package com.example;", "", "import com.google.auto.value.AutoValue;", "import foo.bar.Annot;", "import foo.baz.OuterWithTypeParam;", "", "@AutoValue", "abstract class Nesty {", " abstract @Annot(1) OuterWithTypeParam<@Annot(2) Double>", " .@Annot(3) InnerWithTypeParam<@Annot(4) String> inner();", "", " static Nesty of(", " @Annot(1) OuterWithTypeParam<@Annot(2) Double>", " .@Annot(3) InnerWithTypeParam<@Annot(4) String> inner) {", " return new AutoValue_Nesty(inner);", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "com.example.AutoValue_Nesty", "package com.example;", "", "import foo.bar.Annot;", "import foo.baz.OuterWithTypeParam;", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"com.google.auto.value.processor.AutoValueProcessor\")", "final class AutoValue_Nesty extends Nesty {", " private final @Annot(1) OuterWithTypeParam<@Annot(2) Double>" + ".@Annot(3) InnerWithTypeParam<@Annot(4) String> inner;", "", " AutoValue_Nesty(", " @Annot(1) OuterWithTypeParam<@Annot(2) Double>" + ".@Annot(3) InnerWithTypeParam<@Annot(4) String> inner) {", " if (inner == null) {", " throw new NullPointerException(\"Null inner\");", " }", " this.inner = inner;", " }", "", " @Override", " @Annot(1) OuterWithTypeParam<@Annot(2) Double>" + ".@Annot(3) InnerWithTypeParam<@Annot(4) String> inner() {", " return inner;", " }", "", " @Override", " public String toString() {", " return \"Nesty{\"", " + \"inner=\" + inner", " + \"}\";", " }", "", " @Override", " public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Nesty) {", " Nesty that = (Nesty) o;", " return this.inner.equals(that.inner());", " }", " return false;", " }", "", " @Override", " public int hashCode() {", " int h$ = 1;", " h$ *= 1000003;", " h$ ^= inner.hashCode();", " return h$;", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(annotFileObject, outerFileObject, nestyFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("com.example.AutoValue_Nesty") .hasSourceEquivalentTo(expectedOutput); } // Tests that type annotations are correctly copied from the bounds of type parameters in the // @AutoValue class to the bounds of the corresponding parameters in the generated class. For // example, if we have `@AutoValue abstract class Foo`, then the // generated class should be `class AutoValue_Foo extends Foo`. // Some buggy versions of javac do not report type annotations correctly in this context. // AutoValue can't copy them if it can't see them, so we make a special annotation processor to // detect if we are in the presence of this bug and if so we don't fail. @Test public void testTypeParametersWithAnnotationsOnBounds() { @SupportedAnnotationTypes("*") class CompilerBugProcessor extends AbstractProcessor { boolean checkedAnnotationsOnTypeBounds; boolean reportsAnnotationsOnTypeBounds; @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { TypeElement test = processingEnv.getElementUtils().getTypeElement("com.example.Test"); TypeParameterElement t = test.getTypeParameters().get(0); this.checkedAnnotationsOnTypeBounds = true; this.reportsAnnotationsOnTypeBounds = !t.getBounds().get(0).getAnnotationMirrors().isEmpty(); } return false; } } CompilerBugProcessor compilerBugProcessor = new CompilerBugProcessor(); JavaFileObject nullableTypeFileObject = JavaFileObjects.forSourceLines( "foo.bar.NullableType", "package foo.bar;", "", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Target;", "", "@Target(ElementType.TYPE_USE)", "public @interface NullableType {}"); JavaFileObject autoValueFileObject = JavaFileObjects.forSourceLines( "com.example.Test", "package com.example;", "", "import com.google.auto.value.AutoValue;", "import foo.bar.NullableType;", "", "@AutoValue", "abstract class Test {}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), compilerBugProcessor) .withOptions("-Xlint:-processing", "-implicit:none") .compile(nullableTypeFileObject, autoValueFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilerBugProcessor.checkedAnnotationsOnTypeBounds).isTrue(); if (compilerBugProcessor.reportsAnnotationsOnTypeBounds) { assertThat(compilation) .generatedSourceFile("com.example.AutoValue_Test") .contentsAsUtf8String() .contains( "class AutoValue_Test" + " extends Test {"); } } // In the following few tests, see AutoValueProcessor.validateMethods for why unrecognized // abstract methods provoke only a warning rather than an error. Compilation will fail anyway // because the generated class is not abstract and does not implement the unrecognized methods. @Test public void testAbstractVoid() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue", "public abstract class Baz {", " public abstract void foo();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation).failed(); assertThat(compilation) .hadWarningContaining( "Abstract method is neither a property getter nor a Builder converter") .inFile(javaFileObject) .onLineContaining("void foo()"); } @Test public void testAbstractWithParams() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue", "public abstract class Baz {", " public abstract int foo(int bar);", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation).failed(); assertThat(compilation) .hadWarningContaining( "Abstract method is neither a property getter nor a Builder converter") .inFile(javaFileObject) .onLineContaining("int foo(int bar)"); } @Test public void testPrimitiveArrayWarning() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue", "public abstract class Baz {", " public abstract byte[] bytes();", " public static Baz create(byte[] bytes) {", " return new AutoValue_Baz(bytes);", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation).succeeded(); assertThat(compilation) .hadWarningContaining( "An @AutoValue property that is a primitive array returns the original array") .inFile(javaFileObject) .onLineContaining("byte[] bytes()"); } @Test public void testPrimitiveArrayWarningFromParent() { // If the array-valued property is defined by an ancestor then we shouldn't try to attach // the warning to the method that defined it, but rather to the @AutoValue class itself. JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "public abstract class Baz {", " public abstract byte[] bytes();", "", " @AutoValue", " public abstract static class BazChild extends Baz {", " public static BazChild create(byte[] bytes) {", " return new AutoValue_Baz_BazChild(bytes);", " }", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation).succeeded(); assertThat(compilation) .hadWarningContainingMatch( "An @AutoValue property that is a primitive array returns the original array" + ".*foo\\.bar\\.Baz\\.bytes") .inFile(javaFileObject) .onLineContaining("BazChild extends Baz"); } @Test public void testPrimitiveArrayWarningSuppressed() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue", "public abstract class Baz {", " @SuppressWarnings(\"mutable\")", " public abstract byte[] bytes();", " public static Baz create(byte[] bytes) {", " return new AutoValue_Baz(bytes);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); } @Test public void autoValueMustBeStatic() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "public class Baz {", " @AutoValue", " public abstract class NotStatic {", " public abstract String buh();", " public NotStatic create(String buh) {", " return new AutoValue_Baz_NotStatic(buh);", " }", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Nested @AutoValue class must be static") .inFile(javaFileObject) .onLineContaining("abstract class NotStatic"); } @Test public void autoValueMustBeNotBePrivate() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "public class Baz {", " @AutoValue", " private abstract static class Private {", " public abstract String buh();", " public Private create(String buh) {", " return new AutoValue_Baz_Private(buh);", " }", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("@AutoValue class must not be private") .inFile(javaFileObject) .onLineContaining("class Private"); } @Test public void autoValueMustBeNotBeNestedInPrivate() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "public class Baz {", " private static class Private {", " @AutoValue", " abstract static class Nested {", " public abstract String buh();", " public Nested create(String buh) {", " return new AutoValue_Baz_Private_Nested(buh);", " }", " }", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("@AutoValue class must not be nested in a private class") .inFile(javaFileObject) .onLineContaining("class Nested"); } @Test public void noMultidimensionalPrimitiveArrays() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract int[][] ints();", "", " public static Baz create(int[][] ints) {", " return new AutoValue_Baz(ints);", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "@AutoValue class cannot define an array-valued property " + "unless it is a primitive array") .inFile(javaFileObject) .onLineContaining("int[][] ints()"); } @Test public void noObjectArrays() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract String[] strings();", "", " public static Baz create(String[] strings) {", " return new AutoValue_Baz(strings);", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "@AutoValue class cannot define an array-valued property " + "unless it is a primitive array") .inFile(javaFileObject) .onLineContaining("String[] strings()"); } @Test public void annotationOnInterface() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public interface Baz {}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("AutoValue only applies to classes") .inFile(javaFileObject) .onLineContaining("interface Baz"); } @Test public void annotationOnEnum() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public enum Baz {}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("AutoValue only applies to classes") .inFile(javaFileObject) .onLineContaining("enum Baz"); } @Test public void extendAutoValue() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Outer", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "public class Outer {", " @AutoValue", " static abstract class Parent {", " static Parent create(int randomProperty) {", " return new AutoValue_Outer_Parent(randomProperty);", " }", "", " abstract int randomProperty();", " }", "", " @AutoValue", " static abstract class Child extends Parent {", " static Child create(int randomProperty) {", " return new AutoValue_Outer_Child(randomProperty);", " }", "", " abstract int randomProperty();", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("may not extend") .inFile(javaFileObject) .onLineContaining("Child extends Parent"); } @Test public void bogusSerialVersionUID() { String[] mistakes = { "final long serialVersionUID = 1234L", // not static "static long serialVersionUID = 1234L", // not final "static final Long serialVersionUID = 1234L", // not long "static final long serialVersionUID = (Long) 1234L", // not a compile-time constant }; for (String mistake : mistakes) { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz implements java.io.Serializable {", " " + mistake + ";", "", " public abstract int foo();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); expect .about(compilations()) .that(compilation) .hadErrorContaining("serialVersionUID must be a static final long compile-time constant") .inFile(javaFileObject) .onLineContaining(mistake); } } @Test public void nonExistentSuperclass() { // The main purpose of this test is to check that AutoValueProcessor doesn't crash the // compiler in this case. JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Existent extends NonExistent {", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("NonExistent") .inFile(javaFileObject) .onLineContaining("NonExistent"); } @Test public void cannotImplementAnnotation() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.RetentionImpl", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import java.lang.annotation.Retention;", "import java.lang.annotation.RetentionPolicy;", "", "@AutoValue", "public abstract class RetentionImpl implements Retention {", " public static Retention create(RetentionPolicy policy) {", " return new AutoValue_RetentionImpl(policy);", " }", "", " @Override public Class annotationType() {", " return Retention.class;", " }", "", " @Override public boolean equals(Object o) {", " return (o instanceof Retention && value().equals((Retention) o).value());", " }", "", " @Override public int hashCode() {", " return (\"value\".hashCode() * 127) ^ value().hashCode();", " }", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("may not be used to implement an annotation interface") .inFile(javaFileObject) .onLineContaining("RetentionImpl implements Retention"); } @Test public void missingPropertyType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract MissingType missingType();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("MissingType") .inFile(javaFileObject) .onLineContaining("MissingType"); } @Test public void missingGenericPropertyType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract MissingType missingType();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("MissingType") .inFile(javaFileObject) .onLineContaining("MissingType"); } @Test public void missingComplexGenericPropertyType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "import java.util.Map;", "import java.util.Set;", "", "@AutoValue", "public abstract class Baz {", " public abstract Map, MissingType> missingType();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("MissingType") .inFile(javaFileObject) .onLineContaining("MissingType"); } @Test public void missingSuperclassGenericParameter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz> {", " public abstract int foo();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("MissingType") .inFile(javaFileObject) .onLineContaining("MissingType"); } @Test public void nullablePrimitive() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " @interface Nullable {}", " public abstract @Nullable int foo();", "}"); Compilation compilation = javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Primitive types cannot be @Nullable") .inFile(javaFileObject) .onLineContaining("@Nullable int"); } @Test public void correctBuilder() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.base.Optional;", "import com.google.common.collect.ImmutableList;", "", "import java.util.ArrayList;", "import java.util.List;", "import javax.annotation.Nullable;", "", "@AutoValue", "public abstract class Baz {", " public abstract int anInt();", " @SuppressWarnings(\"mutable\")", " public abstract byte[] aByteArray();", " @SuppressWarnings(\"mutable\")", " @Nullable public abstract int[] aNullableIntArray();", " public abstract List aList();", " public abstract ImmutableList anImmutableList();", " public abstract Optional anOptionalString();", " public abstract NestedAutoValue aNestedAutoValue();", "", " public abstract Builder toBuilder();", "", " @AutoValue.Builder", " public abstract static class Builder {", " public abstract Builder anInt(int x);", " public abstract Builder aByteArray(byte[] x);", " public abstract Builder aNullableIntArray(@Nullable int[] x);", " public abstract Builder aList(List x);", " public abstract Builder anImmutableList(List x);", " public abstract ImmutableList.Builder anImmutableListBuilder();", " public abstract Builder anOptionalString(Optional s);", " public abstract Builder anOptionalString(String s);", " public abstract NestedAutoValue.Builder aNestedAutoValueBuilder();", "", " public Builder aList(ArrayList x) {", // ArrayList should not be imported in the generated class. " return aList((List) x);", " }", "", " public abstract Optional anInt();", " public abstract List aList();", " public abstract ImmutableList anImmutableList();", "", " public abstract Baz build();", " }", "", " public static Builder builder() {", " return AutoValue_Baz.builder();", " }", "}"); JavaFileObject nestedJavaFileObject = JavaFileObjects.forSourceLines( "foo.bar.NestedAutoValue", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class NestedAutoValue {", " public abstract T t();", "", " public abstract Builder toBuilder();", "", " @AutoValue.Builder", " public abstract static class Builder {", " public abstract Builder t(T t);", " public abstract NestedAutoValue build();", " }", "", " public static Builder builder() {", " return AutoValue_NestedAutoValue.builder();", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz", "package foo.bar;", "", "import com.google.common.base.Optional;", "import com.google.common.collect.ImmutableList;", "import java.util.Arrays;", "import java.util.List;", sorted( GeneratedImport.importGeneratedAnnotationType(), "import javax.annotation.Nullable;"), "", "@Generated(\"" + AutoValueProcessor.class.getName() + "\")", "final class AutoValue_Baz extends Baz {", " private final int anInt;", " private final byte[] aByteArray;", " private final int[] aNullableIntArray;", " private final List aList;", " private final ImmutableList anImmutableList;", " private final Optional anOptionalString;", " private final NestedAutoValue aNestedAutoValue;", "", " private AutoValue_Baz(", " int anInt,", " byte[] aByteArray,", " @Nullable int[] aNullableIntArray,", " List aList,", " ImmutableList anImmutableList,", " Optional anOptionalString,", " NestedAutoValue aNestedAutoValue) {", " this.anInt = anInt;", " this.aByteArray = aByteArray;", " this.aNullableIntArray = aNullableIntArray;", " this.aList = aList;", " this.anImmutableList = anImmutableList;", " this.anOptionalString = anOptionalString;", " this.aNestedAutoValue = aNestedAutoValue;", " }", "", " @Override public int anInt() {", " return anInt;", " }", "", " @SuppressWarnings(\"mutable\")", " @Override public byte[] aByteArray() {", " return aByteArray;", " }", "", " @SuppressWarnings(\"mutable\")", " @Nullable", " @Override public int[] aNullableIntArray() {", " return aNullableIntArray;", " }", "", " @Override public List aList() {", " return aList;", " }", "", " @Override public ImmutableList anImmutableList() {", " return anImmutableList;", " }", "", " @Override public Optional anOptionalString() {", " return anOptionalString;", " }", "", " @Override public NestedAutoValue aNestedAutoValue() {", " return aNestedAutoValue;", " }", "", " @Override public String toString() {", " return \"Baz{\"", " + \"anInt=\" + anInt + \", \"", " + \"aByteArray=\" + Arrays.toString(aByteArray) + \", \"", " + \"aNullableIntArray=\" + Arrays.toString(aNullableIntArray) + \", \"", " + \"aList=\" + aList + \", \"", " + \"anImmutableList=\" + anImmutableList + \", \"", " + \"anOptionalString=\" + anOptionalString + \", \"", " + \"aNestedAutoValue=\" + aNestedAutoValue", " + \"}\";", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Baz) {", " Baz that = (Baz) o;", " return this.anInt == that.anInt()", " && Arrays.equals(this.aByteArray, " + "(that instanceof AutoValue_Baz) " + "? ((AutoValue_Baz) that).aByteArray : that.aByteArray())", " && Arrays.equals(this.aNullableIntArray, " + "(that instanceof AutoValue_Baz) " + "? ((AutoValue_Baz) that).aNullableIntArray : that.aNullableIntArray())", " && this.aList.equals(that.aList())", " && this.anImmutableList.equals(that.anImmutableList())", " && this.anOptionalString.equals(that.anOptionalString())", " && this.aNestedAutoValue.equals(that.aNestedAutoValue());", " }", " return false;", " }", "", " @Override public int hashCode() {", " int h$ = 1;", " h$ *= 1000003;", " h$ ^= anInt;", " h$ *= 1000003;", " h$ ^= Arrays.hashCode(aByteArray);", " h$ *= 1000003;", " h$ ^= Arrays.hashCode(aNullableIntArray);", " h$ *= 1000003;", " h$ ^= aList.hashCode();", " h$ *= 1000003;", " h$ ^= anImmutableList.hashCode();", " h$ *= 1000003;", " h$ ^= anOptionalString.hashCode();", " h$ *= 1000003;", " h$ ^= aNestedAutoValue.hashCode();", " return h$;", " }", "", " @Override public Baz.Builder toBuilder() {", " return new Builder(this);", " }", "", " static final class Builder extends Baz.Builder {", " private Integer anInt;", " private byte[] aByteArray;", " private int[] aNullableIntArray;", " private List aList;", " private ImmutableList.Builder anImmutableListBuilder$;", " private ImmutableList anImmutableList;", " private Optional anOptionalString = Optional.absent();", " private NestedAutoValue.Builder aNestedAutoValueBuilder$;", " private NestedAutoValue aNestedAutoValue;", "", " Builder() {", " }", "", " private Builder(Baz source) {", " this.anInt = source.anInt();", " this.aByteArray = source.aByteArray();", " this.aNullableIntArray = source.aNullableIntArray();", " this.aList = source.aList();", " this.anImmutableList = source.anImmutableList();", " this.anOptionalString = source.anOptionalString();", " this.aNestedAutoValue = source.aNestedAutoValue();", " }", "", " @Override", " public Baz.Builder anInt(int anInt) {", " this.anInt = anInt;", " return this;", " }", "", " @Override", " public Optional anInt() {", " if (anInt == null) {", " return Optional.absent();", " } else {", " return Optional.of(anInt);", " }", " }", "", " @Override", " public Baz.Builder aByteArray(byte[] aByteArray) {", " if (aByteArray == null) {", " throw new NullPointerException(\"Null aByteArray\");", " }", " this.aByteArray = aByteArray;", " return this;", " }", "", " @Override", " public Baz.Builder aNullableIntArray(@Nullable int[] aNullableIntArray) {", " this.aNullableIntArray = aNullableIntArray;", " return this;", " }", "", " @Override", " public Baz.Builder aList(List aList) {", " if (aList == null) {", " throw new NullPointerException(\"Null aList\");", " }", " this.aList = aList;", " return this;", " }", "", " @Override", " public List aList() {", " if (aList == null) {", " throw new IllegalStateException(\"Property \\\"aList\\\" has not been set\");", " }", " return aList;", " }", "", " @Override", " public Baz.Builder anImmutableList(List anImmutableList) {", " if (anImmutableListBuilder$ != null) {", " throw new IllegalStateException(" + "\"Cannot set anImmutableList after calling anImmutableListBuilder()\");", " }", " this.anImmutableList = ImmutableList.copyOf(anImmutableList);", " return this;", " }", "", " @Override", " public ImmutableList.Builder anImmutableListBuilder() {", " if (anImmutableListBuilder$ == null) {", " if (anImmutableList == null) {", " anImmutableListBuilder$ = ImmutableList.builder();", " } else {", " anImmutableListBuilder$ = ImmutableList.builder();", " anImmutableListBuilder$.addAll(anImmutableList);", " anImmutableList = null;", " }", " }", " return anImmutableListBuilder$;", " }", "", " @Override", " public ImmutableList anImmutableList() {", " if (anImmutableListBuilder$ != null) {", " return anImmutableListBuilder$.build();", " }", " if (anImmutableList == null) {", " anImmutableList = ImmutableList.of();", " }", " return anImmutableList;", " }", "", " @Override", " public Baz.Builder anOptionalString(Optional anOptionalString) {", " if (anOptionalString == null) {", " throw new NullPointerException(\"Null anOptionalString\");", " }", " this.anOptionalString = anOptionalString;", " return this;", " }", "", " @Override", " public Baz.Builder anOptionalString(String anOptionalString) {", " this.anOptionalString = Optional.of(anOptionalString);", " return this;", " }", "", " @Override", " public NestedAutoValue.Builder aNestedAutoValueBuilder() {", " if (aNestedAutoValueBuilder$ == null) {", " if (aNestedAutoValue == null) {", " aNestedAutoValueBuilder$ = NestedAutoValue.builder();", " } else {", " aNestedAutoValueBuilder$ = aNestedAutoValue.toBuilder();", " aNestedAutoValue = null;", " }", " }", " return aNestedAutoValueBuilder$;", " }", "", " @Override", " public Baz build() {", " if (anImmutableListBuilder$ != null) {", " this.anImmutableList = anImmutableListBuilder$.build();", " } else if (this.anImmutableList == null) {", " this.anImmutableList = ImmutableList.of();", " }", " if (aNestedAutoValueBuilder$ != null) {", " this.aNestedAutoValue = aNestedAutoValueBuilder$.build();", " } else if (this.aNestedAutoValue == null) {", " NestedAutoValue.Builder aNestedAutoValue$builder = " + "NestedAutoValue.builder();", " this.aNestedAutoValue = aNestedAutoValue$builder.build();", " }", " String missing = \"\";", " if (this.anInt == null) {", " missing += \" anInt\";", " }", " if (this.aByteArray == null) {", " missing += \" aByteArray\";", " }", " if (this.aList == null) {", " missing += \" aList\";", " }", " if (!missing.isEmpty()) {", " throw new IllegalStateException(\"Missing required properties:\" + missing);", " }", " return new AutoValue_Baz(", " this.anInt,", " this.aByteArray,", " this.aNullableIntArray,", " this.aList,", " this.anImmutableList,", " this.anOptionalString,", " this.aNestedAutoValue);", " }", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(javaFileObject, nestedJavaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedOutput); } @Test public void autoValueBuilderOnTopLevelClass() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Builder", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue.Builder", "public interface Builder {", " Builder foo(int x);", " Object build();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("can only be applied to a class or interface inside") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderNotInsideAutoValue() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "public abstract class Baz {", " abstract int foo();", "", " static Builder builder() {", " return new AutoValue_Baz.Builder();", " }", "", " @AutoValue.Builder", " public interface Builder {", " Builder foo(int x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("can only be applied to a class or interface inside") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderNotStatic() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Example", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "class Example {", " @AutoValue", " abstract static class Baz {", " abstract int foo();", "", " static Builder builder() {", " return new AutoValue_Example_Baz.Builder();", " }", "", " @AutoValue.Builder", " abstract class Builder {", " abstract Builder foo(int x);", " abstract Baz build();", " }", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("@AutoValue.Builder cannot be applied to a non-static class") .inFile(javaFileObject) .onLineContaining("abstract class Builder"); } @Test public void autoValueBuilderOnEnum() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int foo();", "", " static Builder builder() {", " return null;", " }", "", " @AutoValue.Builder", " public enum Builder {}", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("can only apply to a class or an interface") .inFile(javaFileObject) .onLineContaining("public enum Builder"); } @Test public void autoValueBuilderDuplicate() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " @AutoValue.Builder", " public interface Builder1 {", " Baz build();", " }", "", " @AutoValue.Builder", " public interface Builder2 {", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("already has a Builder: foo.bar.Baz.Builder1") .inFile(javaFileObject) .onLineContaining("public interface Builder2"); } @Test public void autoValueBuilderMissingSetter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int blim();", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("with this signature: foo.bar.Baz.Builder blim(int)") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderMissingSetterUsingSetPrefix() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int blim();", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder setBlam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("with this signature: foo.bar.Baz.Builder setBlim(int)") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderWrongTypeSetter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int blim();", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(String x);", " Builder blam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Parameter type java.lang.String of setter method should be int " + "to match getter foo.bar.Baz.blim") .inFile(javaFileObject) .onLineContaining("Builder blim(String x)"); } @Test public void autoValueBuilderWrongTypeSetterWithCopyOf() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", " abstract ImmutableList blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(String x);", " Builder blam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Parameter type java.lang.String of setter method should be " + "com.google.common.collect.ImmutableList to match getter " + "foo.bar.Baz.blam, or it should be a type that can be passed to " + "ImmutableList.copyOf") .inFile(javaFileObject) .onLineContaining("Builder blam(String x)"); } @Test public void autoValueBuilderWrongTypeSetterWithCopyOfGenericallyWrong() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "import java.util.Collection;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", " abstract ImmutableList blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(String x);", " Builder blam(Collection x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Parameter type java.util.Collection of setter method should be " + "com.google.common.collect.ImmutableList to match getter " + "foo.bar.Baz.blam, or it should be a type that can be passed to " + "ImmutableList.copyOf to produce " + "com.google.common.collect.ImmutableList") .inFile(javaFileObject) .onLineContaining("Builder blam(Collection x)"); } @Test public void autoValueBuilderWrongTypeSetterWithGetPrefix() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int getBlim();", " abstract String getBlam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(String x);", " Builder blam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Parameter type java.lang.String of setter method should be int " + "to match getter foo.bar.Baz.getBlim") .inFile(javaFileObject) .onLineContaining("Builder blim(String x)"); } @Test public void autoValueBuilderNullableSetterForNonNullable() { JavaFileObject nullableFileObject = JavaFileObjects.forSourceLines( "foo.bar.Nullable", "package foo.bar;", "", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Target;", "", "@Target(ElementType.TYPE_USE)", "public @interface Nullable {}"); JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String notNull();", "", " @AutoValue.Builder", " public interface Builder {", " Builder setNotNull(@Nullable String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject, nullableFileObject); assertThat(compilation) .hadErrorContaining( "Parameter of setter method is @Nullable but property method" + " foo.bar.Baz.notNull() is not") .inFile(javaFileObject) .onLineContaining("setNotNull"); } // Check that we get a helpful error message if some of your properties look like getters but // others don't. @Test public void autoValueBuilderBeansConfusion() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Item", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Item {", " abstract String getTitle();", " abstract boolean hasThumbnail();", "", " @AutoValue.Builder", " public interface Builder {", " Builder setTitle(String title);", " Builder setHasThumbnail(boolean t);", " Item build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Method does not correspond to a property of foo.bar.Item") .inFile(javaFileObject) .onLineContaining("Builder setTitle(String title)"); assertThat(compilation) .hadNoteContaining("hasThumbnail") .inFile(javaFileObject) .onLineContaining("Builder setTitle(String title)"); } @Test public void autoValueBuilderExtraSetter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(int x);", " Builder blam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Method does not correspond to a property of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Builder blim(int x)"); } @Test public void autoValueBuilderSetPrefixAndNoSetPrefix() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int blim();", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(int x);", " Builder setBlam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("If any setter methods use the setFoo convention then all must") .inFile(javaFileObject) .onLineContaining("Builder blim(int x)"); } @Test public void autoValueBuilderWrongTypeGetter() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract T blim();", " abstract U blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blim(T x);", " Builder blam(U x);", " T blim();", " T blam();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method matches a property of foo.bar.Baz but has return type T instead of U") .inFile(javaFileObject) .onLineContaining("T blam()"); } @Test public void autoValueBuilderPropertyBuilderInvalidType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", "", " @AutoValue.Builder", " public interface Builder {", " StringBuilder blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method looks like a property builder, but it returns java.lang.StringBuilder which " + "does not have a non-static build() method") .inFile(javaFileObject) .onLineContaining("StringBuilder blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderNullable() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "", "@AutoValue", "public abstract class Baz {", " @interface Nullable {}", " abstract @Nullable ImmutableList strings();", "", " @AutoValue.Builder", " public interface Builder {", " ImmutableList.Builder stringsBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Property strings has a property builder so it cannot be @Nullable") .inFile(javaFileObject) .onLineContaining("@Nullable ImmutableList strings()"); } @Test public void autoValueBuilderPropertyBuilderNullableType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Target;", "", "@AutoValue", "public abstract class Baz {", " @Target(ElementType.TYPE_USE)", " @interface Nullable {}", " abstract @Nullable ImmutableList strings();", "", " @AutoValue.Builder", " public interface Builder {", " ImmutableList.Builder stringsBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Property strings has a property builder so it cannot be @Nullable") .inFile(javaFileObject) .onLineContaining("@Nullable ImmutableList strings()"); } @Test public void autoValueBuilderPropertyBuilderWrongCollectionType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract ImmutableList blim();", "", " @AutoValue.Builder", " public interface Builder {", " ImmutableSet.Builder blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder for blim has type com.google.common.collect.ImmutableSet.Builder " + "whose build() method returns com.google.common.collect.ImmutableSet " + "instead of com.google.common.collect.ImmutableList") .inFile(javaFileObject) .onLineContaining("ImmutableSet.Builder blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderWeirdBuilderType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract Integer blim();", "", " @AutoValue.Builder", " public interface Builder {", " int blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method looks like a property builder, but its return type is not a class or interface") .inFile(javaFileObject) .onLineContaining("int blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderWeirdBuiltType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract int blim();", "", " @AutoValue.Builder", " public interface Builder {", " Integer blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method looks like a property builder, but the type of property blim is not a class " + "or interface") .inFile(javaFileObject) .onLineContaining("Integer blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderHasNoBuild() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", "", " @AutoValue.Builder", " public interface Builder {", " StringBuilder blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method looks like a property builder, but it returns java.lang.StringBuilder which " + "does not have a non-static build() method") .inFile(javaFileObject) .onLineContaining("StringBuilder blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderHasStaticBuild() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", "", " public static class StringFactory {", " public static String build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " StringFactory blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method looks like a property builder, but it returns foo.bar.Baz.StringFactory which " + "does not have a non-static build() method") .inFile(javaFileObject) .onLineContaining("StringFactory blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderReturnsWrongType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "import java.util.List;", "", "@AutoValue", "public abstract class Baz {", " abstract List blim();", "", " public static class ListFactory {", " public List build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " ListFactory blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder for blim has type foo.bar.Baz.ListFactory whose build() method " + "returns java.util.List instead of java.util.List") .inFile(javaFileObject) .onLineContaining("ListFactory blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderCantConstruct() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", "", " public static class StringFactory {", " private StringFactory() {}", "", " public String build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " StringFactory blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method looks like a property builder, but its type foo.bar.Baz.StringFactory " + "does not have a public constructor and java.lang.String does not have a static " + "builder() or newBuilder() method that returns foo.bar.Baz.StringFactory") .inFile(javaFileObject) .onLineContaining("StringFactory blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderCantReconstruct() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", " abstract Builder toBuilder();", "", " public static class StringFactory {", " public String build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " StringFactory blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder method returns foo.bar.Baz.StringFactory but there is no way to make" + " that type from java.lang.String: java.lang.String does not have a non-static" + " toBuilder() method that returns foo.bar.Baz.StringFactory, and" + " foo.bar.Baz.StringFactory does not have a method addAll or putAll that accepts" + " an argument of type java.lang.String") .inFile(javaFileObject) .onLineContaining("StringFactory blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderWrongTypeAddAll() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "import java.util.Iterator;", "", "@AutoValue", "public abstract class Baz {", " abstract ImmutableSet strings();", " abstract Builder toBuilder();", "", " public static class ImmutableSetBuilder {", " public void addAll(Iterator elements) {}", "", " public ImmutableSet build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " ImmutableSetBuilder stringsBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder method returns foo.bar.Baz.ImmutableSetBuilder but" + " there is no way to make that type from" + " com.google.common.collect.ImmutableSet:" + " com.google.common.collect.ImmutableSet does not have a" + " non-static toBuilder() method that returns" + " foo.bar.Baz.ImmutableSetBuilder, and" + " foo.bar.Baz.ImmutableSetBuilder does not have a method" + " addAll or putAll that accepts an argument of type" + " com.google.common.collect.ImmutableSet") .inFile(javaFileObject) .onLineContaining("ImmutableSetBuilder stringsBuilder();"); } @Test public void autoValueBuilderPropertyBuilderCantSet() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract String blim();", "", " public static class StringFactory {", " public String build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " Builder setBlim(String s);", " StringFactory blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder method returns foo.bar.Baz.StringFactory but there is no way to make " + "that type from java.lang.String: java.lang.String does not have a non-static " + "toBuilder() method that returns foo.bar.Baz.StringFactory") .inFile(javaFileObject) .onLineContaining("StringFactory blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderWrongTypeToBuilder() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract Buh blim();", " abstract Builder toBuilder();", "", " public static class Buh {", " StringBuilder toBuilder() {", " return null;", " }", " }", "", " public static class BuhBuilder {", " public Buh build() {", " return null;", " }", " }", "", " @AutoValue.Builder", " public interface Builder {", " BuhBuilder blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder method returns foo.bar.Baz.BuhBuilder but there is no way to make " + "that type from foo.bar.Baz.Buh: foo.bar.Baz.Buh does not have a non-static " + "toBuilder() method that returns foo.bar.Baz.BuhBuilder") .inFile(javaFileObject) .onLineContaining("BuhBuilder blimBuilder()"); } @Test public void autoValueBuilderPropertyBuilderWrongElementType() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableSet;", "", "@AutoValue", "public abstract class Baz {", " abstract ImmutableSet blim();", "", " @AutoValue.Builder", " public interface Builder {", " ImmutableSet.Builder blimBuilder();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Property builder for blim has type com.google.common.collect.ImmutableSet.Builder " + "whose build() method returns com.google.common.collect.ImmutableSet " + "instead of com.google.common.collect.ImmutableSet") .inFile(javaFileObject) .onLineContaining("ImmutableSet.Builder blimBuilder()"); } @Test public void autoValueBuilderAlienMethod0() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(String x);", " Builder whut();", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method without arguments should be a build method returning foo.bar.Baz, or a getter" + " method with the same name and type as a getter method of foo.bar.Baz, or" + " fooBuilder() where foo() or getFoo() is a getter method of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Builder whut()"); } @Test public void autoValueBuilderAlienMethod1() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " void whut(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Method does not correspond to a property of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("void whut(String x)"); } @Test public void autoValueBuilderAlienMethod2() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(String x, String y);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Builder methods must have 0 or 1 parameters") .inFile(javaFileObject) .onLineContaining("Builder blam(String x, String y)"); } @Test public void autoValueBuilderMissingBuildMethod() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract T blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(T x);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Builder must have a single no-argument method returning foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderDuplicateBuildMethods() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(String x);", " Baz build();", " Baz create();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Baz build()"); assertThat(compilation) .hadErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("Baz create()"); } @Test public void autoValueBuilderWrongTypeBuildMethod() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(String x);", " String build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Method without arguments should be a build method returning foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("String build()"); } @Test public void autoValueBuilderTypeParametersDontMatch1() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(String x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Type parameters of foo.bar.Baz.Builder must have same names and " + "bounds as type parameters of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderTypeParametersDontMatch2() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract T blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(E x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Type parameters of foo.bar.Baz.Builder must have same names and " + "bounds as type parameters of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderTypeParametersDontMatch3() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz> {", " abstract T blam();", "", " @AutoValue.Builder", " public interface Builder {", " Builder blam(T x);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "Type parameters of foo.bar.Baz.Builder must have same names and " + "bounds as type parameters of foo.bar.Baz") .inFile(javaFileObject) .onLineContaining("public interface Builder"); } @Test public void autoValueBuilderToBuilderWrongTypeParameters() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "abstract class Baz, V> {", " abstract K key();", " abstract V value();", " abstract Builder toBuilder1();", "", " @AutoValue.Builder", " interface Builder, V> {", " Builder key(K key);", " Builder value(V value);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("Builder converter method should return foo.bar.Baz.Builder") .inFile(javaFileObject) .onLineContaining("abstract Builder toBuilder1()"); } @Test public void autoValueBuilderToBuilderDuplicate() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "abstract class Baz, V> {", " abstract K key();", " abstract V value();", " abstract Builder toBuilder1();", " abstract Builder toBuilder2();", "", " @AutoValue.Builder", " interface Builder, V> {", " Builder key(K key);", " Builder value(V value);", " Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("There can be at most one builder converter method") .inFile(javaFileObject) .onLineContaining("abstract Builder toBuilder1()"); } @Test public void getFooIsFoo() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract int getFoo();", " abstract boolean isFoo();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor()) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("More than one @AutoValue property called foo") .inFile(javaFileObject) .onLineContaining("getFoo"); assertThat(compilation) .hadErrorContaining("More than one @AutoValue property called foo") .inFile(javaFileObject) .onLineContaining("isFoo"); } @Retention(RetentionPolicy.SOURCE) public @interface Foo {} /* Processor that generates an empty class BarFoo every time it sees a class Bar annotated with * @Foo. */ public static class FooProcessor extends AbstractProcessor { @Override public Set getSupportedAnnotationTypes() { return ImmutableSet.of(Foo.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { Set elements = roundEnv.getElementsAnnotatedWith(Foo.class); for (TypeElement type : ElementFilter.typesIn(elements)) { try { generateFoo(type); } catch (IOException e) { throw new AssertionError(e); } } return false; } private void generateFoo(TypeElement type) throws IOException { String pkg = TypeSimplifier.packageNameOf(type); String className = type.getSimpleName().toString(); String generatedClassName = className + "Foo"; JavaFileObject source = processingEnv.getFiler().createSourceFile(pkg + "." + generatedClassName, type); PrintWriter writer = new PrintWriter(source.openWriter()); writer.println("package " + pkg + ";"); writer.println("public class " + generatedClassName + " {}"); writer.close(); } } @Test public void referencingGeneratedClass() { // Test that ensures that a type that does not exist can be the type of an @AutoValue property // as long as it later does come into existence. The BarFoo type referenced here does not exist // when the AutoValueProcessor runs on the first round, but the FooProcessor then generates it. // That generation provokes a further round of annotation processing and AutoValueProcessor // should succeed then. JavaFileObject bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract BarFoo barFoo();", "", " public static Baz create(BarFoo barFoo) {", " return new AutoValue_Baz(barFoo);", " }", "}"); JavaFileObject barFileObject = JavaFileObjects.forSourceLines( "foo.bar.Bar", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@" + Foo.class.getCanonicalName(), "public abstract class Bar {", " public abstract BarFoo barFoo();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(), new FooProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(bazFileObject, barFileObject); assertThat(compilation).succeededWithoutWarnings(); } @Test public void annotationReferencesUndefined() { // Test that we don't throw an exception if asked to compile @SuppressWarnings(UNDEFINED) // where UNDEFINED is an undefined symbol. JavaFileObject bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " @SuppressWarnings(UNDEFINED)", " public abstract int[] buh();", "}"); Compilation compilation1 = javac() .withOptions("-Xlint:-processing") .withProcessors(new AutoValueProcessor()) .compile(bazFileObject); assertThat(compilation1).hadErrorCount(1); assertThat(compilation1) .hadErrorContaining("UNDEFINED") .inFile(bazFileObject) .onLineContaining("UNDEFINED"); assertThat(compilation1).hadWarningCount(1); assertThat(compilation1) .hadWarningContaining("mutable") .inFile(bazFileObject) .onLineContaining("public abstract int[] buh()"); // Same test, except we do successfully suppress the warning despite the UNDEFINED. bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " @SuppressWarnings({UNDEFINED, \"mutable\"})", " public abstract int[] buh();", "}"); Compilation compilation2 = javac() .withOptions("-Xlint:-processing") .withProcessors(new AutoValueProcessor()) .compile(bazFileObject); assertThat(compilation2).hadErrorCount(1); assertThat(compilation2) .hadErrorContaining("UNDEFINED") .inFile(bazFileObject) .onLineContaining("UNDEFINED"); assertThat(compilation2).hadWarningCount(0); } @Test public void packagePrivateAnnotationFromOtherPackage() { JavaFileObject bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz extends otherpackage.Parent {", "}"); JavaFileObject parentFileObject = JavaFileObjects.forSourceLines( "otherpackage.Parent", "package otherpackage;", "", "public abstract class Parent {", " @PackageAnnotation", " public abstract String foo();", "", " @interface PackageAnnotation {}", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(bazFileObject, parentFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation).generatedSourceFile("foo.bar.AutoValue_Baz"); } @Test public void visibleProtectedAnnotationFromOtherPackage() { JavaFileObject bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz extends otherpackage.Parent {}"); JavaFileObject parentFileObject = JavaFileObjects.forSourceLines( "otherpackage.Parent", "package otherpackage;", "", "public abstract class Parent {", " @ProtectedAnnotation", " public abstract String foo();", "", " protected @interface ProtectedAnnotation {}", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(bazFileObject, parentFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .contentsAsUtf8String() .containsMatch("(?s:@Parent.ProtectedAnnotation\\s*@Override\\s*public String foo\\(\\))"); } @Test public void nonVisibleProtectedAnnotationFromOtherPackage() { JavaFileObject bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz extends otherpackage.Parent {", "}"); JavaFileObject parentFileObject = JavaFileObjects.forSourceLines( "otherpackage.Parent", "package otherpackage;", "", "import otherpackage.Annotations.ProtectedAnnotation;", "", "public abstract class Parent {", " @ProtectedAnnotation", " public abstract String foo();", "}"); JavaFileObject annotationsFileObject = JavaFileObjects.forSourceLines( "otherpackage.Annotations", "package otherpackage;", "", "public class Annotations {", " protected @interface ProtectedAnnotation {}", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(bazFileObject, parentFileObject, annotationsFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .contentsAsUtf8String() .doesNotContain("ProtectedAnnotation"); } @Test public void nonVisibleProtectedClassAnnotationFromOtherPackage() { JavaFileObject bazFileObject = JavaFileObjects.forSourceLines( "foo.bar.Outer", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "class Outer extends otherpackage.Parent {", " @AutoValue", " @AutoValue.CopyAnnotations", " @ProtectedAnnotation", " abstract static class Inner {", " abstract String foo();", " }", "}"); JavaFileObject parentFileObject = JavaFileObjects.forSourceLines( "otherpackage.Parent", "package otherpackage;", "", "public abstract class Parent {", " protected @interface ProtectedAnnotation {}", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(bazFileObject, parentFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Outer_Inner") .contentsAsUtf8String() .doesNotContain("ProtectedAnnotation"); } @Test public void builderWithVarArgsDoesNotImportJavaUtilArrays() { // Repro from https://github.com/google/auto/issues/373. JavaFileObject testFileObject = JavaFileObjects.forSourceLines( "foo.bar.Test", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "", "@AutoValue", "public abstract class Test {", " abstract ImmutableList foo();", "", " @AutoValue.Builder", " abstract static class Builder {", " abstract Builder foo(String... foos);", " abstract Test build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(testFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Test") .contentsAsUtf8String() .doesNotContain("java.util.Arrays"); } @Test public void staticBuilderMethodInBuilderClass() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "com.example.Foo", "package com.example;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Foo {", " public abstract String bar();", "", " @AutoValue.Builder", " public abstract static class Builder {", " public static Builder builder() {", " return new AutoValue_Foo.Builder();", " }", "", " public abstract Builder setBar(String s);", " public abstract Foo build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(javaFileObject); assertThat(compilation).succeeded(); assertThat(compilation) .hadWarningContaining("Static builder() method should be in the containing class") .inFile(javaFileObject) .onLineContaining("builder()"); } /** * Tests behaviour when the package containing an {@code @AutoValue} class also has classes with * the same name as classes in {@code java.lang}. If you call a class {@code Object} you are * asking for trouble, but you could innocently call a class {@code Compiler} without realizing * there is a {@code java.lang.Compiler}. * *

      The case where the class in question is mentioned in the {@code @AutoValue} class is the * easy one, because then our logic can easily see that there is a clash and will use * fully-qualified names. This is the case of the {@code Compiler} class below. The case where the * class is not mentioned is harder. We have to realize that we can't elide the package * name in {@code java.lang.Object} because there is also a {@code foo.bar.Object} in scope, and * in fact it takes precedence. */ @Test public void javaLangClash() { JavaFileObject object = JavaFileObjects.forSourceLines( "foo.bar.Object", // "package foo.bar;", "", "public class Object {}"); JavaFileObject string = JavaFileObjects.forSourceLines( "foo.bar.String", // "package foo.bar;", "", "public class String {}"); JavaFileObject integer = JavaFileObjects.forSourceLines( "foo.bar.Integer", // "package foo.bar;", "", "public class Integer {}"); JavaFileObject thread = JavaFileObjects.forSourceLines( "foo.bar.Thread", // "package foo.bar;", "", "public class Thread {}"); JavaFileObject test = JavaFileObjects.forSourceLines( "foo.bar.Test", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Test {", " public abstract java.lang.Integer integer();", " public abstract java.lang.Thread.State state();", " public static Builder builder() {", " return new AutoValue_Test.Builder();", " }", "", " @AutoValue.Builder", " public abstract static class Builder {", " public abstract Builder setInteger(java.lang.Integer x);", " public abstract Builder setState(java.lang.Thread.State x);", " public abstract Test build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor()) .withOptions("-Xlint:-processing", "-implicit:none") .compile(object, string, integer, thread, test); assertThat(compilation).succeededWithoutWarnings(); } private String sorted(String... imports) { return Arrays.stream(imports).sorted().collect(joining("\n")); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java000066400000000000000000001246301365703632600330570ustar00rootroot00000000000000/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; import com.google.auto.common.MoreTypes; import com.google.auto.value.extension.AutoValueExtension; import com.google.auto.value.extension.AutoValueExtension.BuilderContext; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.truth.Truth; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.Writer; import java.net.URL; import java.net.URLClassLoader; import java.util.Collections; import java.util.Map; import java.util.Optional; import java.util.ServiceConfigurationError; import java.util.Set; import java.util.function.Consumer; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import javax.annotation.processing.Filer; import javax.annotation.processing.SupportedOptions; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class ExtensionTest { @Test public void testExtensionCompilation() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", "}"); JavaFileObject expectedExtensionOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz", "package foo.bar;", "", "final class AutoValue_Baz extends $AutoValue_Baz {", " public AutoValue_Baz(String foo) {", " super(foo);", " }", " @Override public String foo() {", " return \"foo\";", " }", " public String dizzle() {\n", " return \"dizzle\";\n", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedExtensionOutput); } @Test public void testExtensionConsumesProperties() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", " abstract String dizzle();", "}"); JavaFileObject expectedExtensionOutput = JavaFileObjects.forSourceLines( "foo.bar.$AutoValue_Baz", "package foo.bar;", "", GeneratedImport.importGeneratedAnnotationType(), "", "@Generated(\"com.google.auto.value.processor.AutoValueProcessor\")", " abstract class $AutoValue_Baz extends Baz {", "", " private final String foo;", "", " $AutoValue_Baz(", " String foo) {", " if (foo == null) {", " throw new NullPointerException(\"Null foo\");", " }", " this.foo = foo;", " }", "", " @Override", " String foo() {", " return foo;", " }", "", " @Override", " public String toString() {", " return \"Baz{\"", " + \"foo=\" + foo", " + \"}\";", " }", "", " @Override", " public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Baz) {", " Baz that = (Baz) o;", " return this.foo.equals(that.foo());", " }", " return false;", " }", "", " @Override", " public int hashCode() {", " int h$ = 1;", " h$ *= 1000003;", " h$ ^= foo.hashCode();", " return h$;", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.$AutoValue_Baz") .hasSourceEquivalentTo(expectedExtensionOutput); } @Test public void testDoesntRaiseWarningForConsumedProperties() { JavaFileObject impl = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", " abstract String foo();", " abstract String dizzle();", "", " @AutoValue.Builder", " public abstract static class Builder {", " public abstract Builder foo(String s);", " public abstract Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(impl); assertThat(compilation).succeededWithoutWarnings(); } @Test public void testDoesntRaiseWarningForToBuilder() { JavaFileObject impl = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", " abstract String foo();", " abstract String dizzle();", " abstract Builder toBuilder();", "", " @AutoValue.Builder", " public abstract static class Builder {", " public abstract Builder foo(String s);", " public abstract Baz build();", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(impl); assertThat(compilation).succeededWithoutWarnings(); } @Test public void testCantConsumeTwice() { class ConsumeDizzle extends NonFinalExtension { @Override public Set consumeProperties(Context context) { return ImmutableSet.of("dizzle"); } } class AlsoConsumeDizzle extends ConsumeDizzle {} AutoValueExtension ext1 = new ConsumeDizzle(); AutoValueExtension ext2 = new AlsoConsumeDizzle(); Truth.assertThat(ext1).isNotEqualTo(ext2); JavaFileObject impl = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", " abstract String foo();", " abstract String dizzle();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(ext1, ext2))) .compile(impl); assertThat(compilation) .hadErrorContaining("wants to consume a method that was already consumed") .inFile(impl) .onLineContaining("String dizzle()"); } @Test public void testCantConsumeNonExistentProperty() { class ConsumeDizzle extends NonFinalExtension { @Override public Set consumeProperties(Context context) { return ImmutableSet.of("dizzle"); } } JavaFileObject impl = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", " abstract String foo();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new ConsumeDizzle()))) .compile(impl); assertThat(compilation) .hadErrorContaining("wants to consume a property that does not exist: dizzle") .inFile(impl) .onLineContaining("@AutoValue public abstract class Baz"); } @Test public void testCantConsumeConcreteMethod() { class ConsumeConcreteMethod extends NonFinalExtension { @Override public Set consumeMethods(Context context) { TypeElement autoValueClass = context.autoValueClass(); for (ExecutableElement method : ElementFilter.methodsIn(autoValueClass.getEnclosedElements())) { if (method.getSimpleName().contentEquals("frob")) { return ImmutableSet.of(method); } } throw new AssertionError("Could not find frob method"); } } JavaFileObject impl = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", " abstract String foo();", " void frob(int x) {}", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new ConsumeConcreteMethod()))) .compile(impl); assertThat(compilation) .hadErrorContainingMatch( "wants to consume a method that is not one of the abstract methods in this class" + ".*frob\\(int\\)") .inFile(impl) .onLineContaining("@AutoValue public abstract class Baz"); } @Test public void testCantConsumeNonExistentMethod() { class ConsumeBogusMethod extends NonFinalExtension { @Override public Set consumeMethods(Context context) { // Find Integer.intValue() and try to consume that. Elements elementUtils = context.processingEnvironment().getElementUtils(); TypeElement javaLangInteger = elementUtils.getTypeElement(Integer.class.getName()); for (ExecutableElement method : ElementFilter.methodsIn(javaLangInteger.getEnclosedElements())) { if (method.getSimpleName().contentEquals("intValue")) { return ImmutableSet.of(method); } } throw new AssertionError("Could not find Integer.intValue()"); } } JavaFileObject impl = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "import com.google.auto.value.AutoValue;", "@AutoValue public abstract class Baz {", " abstract String foo();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new ConsumeBogusMethod()))) .compile(impl); assertThat(compilation) .hadErrorContainingMatch( "wants to consume a method that is not one of the abstract methods in this class" + ".*intValue\\(\\)") .inFile(impl) .onLineContaining("@AutoValue public abstract class Baz"); } @Test public void testExtensionWithoutConsumedPropertiesFails() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", " abstract String dizzle();", " abstract Double[] bad();", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "An @AutoValue class cannot define an array-valued property unless " + "it is a primitive array") .inFile(javaFileObject) .onLineContaining("abstract Double[] bad()"); } @Test public void testConsumeMethodWithArguments() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", " abstract void writeToParcel(Object parcel, int flags);", "}"); Compilation compilation = javac() .withProcessors( new AutoValueProcessor(ImmutableList.of(new FakeWriteToParcelExtension()))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); } @Test public void testExtensionWithBuilderCompilation() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", " abstract String bar();", "", " @AutoValue.Builder public static abstract class Builder {", " public abstract Builder foo(String foo);", " public abstract Builder bar(String bar);", " public abstract Baz build();", " }", "}"); JavaFileObject expectedExtensionOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz", "package foo.bar;", "", "final class AutoValue_Baz extends $AutoValue_Baz {", " public AutoValue_Baz(String foo, String bar) {", " super(foo, bar);", " }", " @Override public String foo() {", " return \"foo\";", " }", " public String dizzle() {\n", " return \"dizzle\";\n", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedExtensionOutput); } @Test public void testLastExtensionGeneratesNoCode() { doTestNoCode(new FooExtension(), new NonFinalExtension(), new SideFileExtension()); } @Test public void testFirstExtensionGeneratesNoCode() { doTestNoCode(new SideFileExtension(), new FooExtension(), new NonFinalExtension()); } @Test public void testMiddleExtensionGeneratesNoCode() { doTestNoCode(new FooExtension(), new SideFileExtension(), new NonFinalExtension()); } @Test public void testLoneExtensionGeneratesNoCode() { doTestNoCode(new SideFileExtension()); } private void doTestNoCode(AutoValueExtension... extensions) { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public abstract String foo();", "", " public static Baz create(String foo) {", " return new AutoValue_Baz(foo);", " }", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.copyOf(extensions))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedFile(StandardLocation.SOURCE_OUTPUT, "foo.bar", "Side_Baz.java"); } @Test public void testTwoExtensionsBothWantToBeFinal() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", "}"); Compilation compilation = javac() .withProcessors( new AutoValueProcessor(ImmutableList.of(new FooExtension(), new FinalExtension()))) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining( "More than one extension wants to generate the final class: " + FooExtension.class.getName() + ", " + FinalExtension.class.getName()) .inFile(javaFileObject) .onLineContaining("public abstract class Baz"); } @Test public void testNonFinalThenFinal() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", "}"); FinalExtension finalExtension = new FinalExtension(); NonFinalExtension nonFinalExtension = new NonFinalExtension(); assertThat(finalExtension.generated).isFalse(); assertThat(nonFinalExtension.generated).isFalse(); Compilation compilation = javac() .withProcessors( new AutoValueProcessor(ImmutableList.of(finalExtension, nonFinalExtension))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(finalExtension.generated).isTrue(); assertThat(nonFinalExtension.generated).isTrue(); } @Test public void testFinalThenNonFinal() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", "}"); FinalExtension finalExtension = new FinalExtension(); NonFinalExtension nonFinalExtension = new NonFinalExtension(); assertThat(finalExtension.generated).isFalse(); assertThat(nonFinalExtension.generated).isFalse(); Compilation compilation = javac() .withProcessors( new AutoValueProcessor(ImmutableList.of(nonFinalExtension, finalExtension))) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(finalExtension.generated).isTrue(); assertThat(nonFinalExtension.generated).isTrue(); } @Test public void testUnconsumedMethod() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " abstract String foo();", " abstract void writeToParcel(Object parcel, int flags);", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension()))) .compile(javaFileObject); assertThat(compilation) .hadErrorContaining("writeToParcel"); assertThat(compilation) .hadWarningContaining( "Abstract method is neither a property getter nor a Builder converter, " + "and no extension consumed it") .inFile(javaFileObject) .onLineContaining("abstract void writeToParcel"); // The error here comes from the Java compiler rather than AutoValue, so we don't assume // much about what it looks like. On the other hand, the warning does come from AutoValue // so we know what to expect. } /** * Tests that the search for extensions doesn't completely blow AutoValue up if there is a corrupt * jar in the {@code processorpath}. If we're not careful, that can lead to a * ServiceConfigurationError. */ @Test public void testBadJarDoesntBlowUp() throws IOException { File badJar = File.createTempFile("bogus", ".jar"); try { doTestBadJarDoesntBlowUp(badJar); } finally { badJar.delete(); } } private void doTestBadJarDoesntBlowUp(File badJar) throws IOException { FileOutputStream fileOutputStream = new FileOutputStream(badJar); JarOutputStream jarOutputStream = new JarOutputStream(fileOutputStream); byte[] bogusLine = "bogus line\n".getBytes("UTF-8"); ZipEntry zipEntry = new ZipEntry("META-INF/services/" + AutoValueExtension.class.getName()); zipEntry.setSize(bogusLine.length); jarOutputStream.putNextEntry(zipEntry); jarOutputStream.write(bogusLine); jarOutputStream.close(); ClassLoader badJarLoader = new URLClassLoader(new URL[] {badJar.toURI().toURL()}); JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", "}"); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(badJarLoader)) .compile(javaFileObject); assertThat(compilation).succeeded(); assertThat(compilation).hadWarningContaining( "This may be due to a corrupt jar file in the compiler's classpath.\n " + ServiceConfigurationError.class.getName()); assertThat(compilation).generatedSourceFile("foo.bar.AutoValue_Baz"); } private static final String CUSTOM_OPTION = "customAnnotation.customOption"; /** * Tests that extensions providing their own (annotated) annotation types or options get picked * up. */ @Test public void extensionsWithAnnotatedOptions() { ExtensionWithAnnotatedOptions extension = new ExtensionWithAnnotatedOptions(); // Ensure default annotation support works assertThat(extension.getSupportedOptions()).contains(CUSTOM_OPTION); // Ensure it's carried over to the AutoValue processor assertThat(new AutoValueProcessor(ImmutableList.of(extension)).getSupportedOptions()) .contains(CUSTOM_OPTION); } /** * Tests that extensions providing their own implemented annotation types or options get picked * up. */ @Test public void extensionsWithImplementedOptions() { ExtensionWithImplementedOptions extension = new ExtensionWithImplementedOptions(); // Ensure it's carried over to the AutoValue processor assertThat(new AutoValueProcessor(ImmutableList.of(extension)).getSupportedOptions()) .contains(CUSTOM_OPTION); } @SupportedOptions(CUSTOM_OPTION) static class ExtensionWithAnnotatedOptions extends AutoValueExtension { @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { return null; } } static class ExtensionWithImplementedOptions extends AutoValueExtension { @Override public Set getSupportedOptions() { return ImmutableSet.of(CUSTOM_OPTION); } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { return null; } } private static class FooExtension extends AutoValueExtension { @Override public boolean applicable(Context context) { return true; } @Override public boolean mustBeFinal(Context context) { return true; } @Override public Set consumeProperties(Context context) { if (context.properties().containsKey("dizzle")) { return ImmutableSet.of("dizzle"); } else { return Collections.emptySet(); } } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { StringBuilder constructor = new StringBuilder().append(" public ").append(className).append("("); boolean first = true; for (Map.Entry el : context.properties().entrySet()) { if (first) { first = false; } else { constructor.append(", "); } constructor.append("String ").append(el.getKey()); } constructor.append(") {\n"); constructor.append(" super("); first = true; for (Map.Entry el : context.properties().entrySet()) { if (first) { first = false; } else { constructor.append(", "); } constructor.append(el.getKey()); } constructor.append(");\n"); constructor.append(" }\n"); return String.format( "package %s;\n" + "\n" + "%s class %s extends %s {\n" + constructor + " @Override public String foo() {\n" + " return \"foo\";\n" + " }\n" + " public String dizzle() {\n" + " return \"dizzle\";\n" + " }\n" + "}", context.packageName(), isFinal ? "final" : "abstract", className, classToExtend); } } // Extension that generates a class that just forwards to the parent constructor. // We will make subclasses that are respectively final and non-final. private abstract static class EmptyExtension extends AutoValueExtension { @Override public boolean applicable(Context context) { return true; } @Override public abstract boolean mustBeFinal(Context context); String extraText(Context context) { return ""; } boolean generated = false; @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { generated = true; ImmutableList.Builder typesAndNamesBuilder = ImmutableList.builder(); context.propertyTypes().forEach((name, type) -> typesAndNamesBuilder.add(type + " " + name)); String typesAndNames = Joiner.on(", ").join(typesAndNamesBuilder.build()); String template = "package {pkg};\n" + "\n" + "{finalOrAbstract} class {className} extends {classToExtend} {\n" + " {className}({propertyTypesAndNames}) {\n" + " super({propertyNames});\n" + " }\n" + " {extraText}\n" + "}\n"; return template .replace("{pkg}", context.packageName()) .replace("{finalOrAbstract}", isFinal ? "final" : "abstract") .replace("{className}", className) .replace("{classToExtend}", classToExtend) .replace("{propertyTypesAndNames}", typesAndNames) .replace("{propertyNames}", Joiner.on(", ").join(context.properties().keySet())) .replace("{extraText}", extraText(context)); } } private static class NonFinalExtension extends EmptyExtension { @Override public boolean mustBeFinal(Context context) { return false; } } private static class FinalExtension extends EmptyExtension { @Override public boolean mustBeFinal(Context context) { return true; } } private static class SideFileExtension extends AutoValueExtension { @Override public boolean applicable(Context context) { return true; } @Override public boolean mustBeFinal(Context context) { return false; } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { String sideClassName = "Side_" + context.autoValueClass().getSimpleName(); String sideClass = "" // + "package " + context.packageName() + ";\n" + "class " + sideClassName + " {}\n"; Filer filer = context.processingEnvironment().getFiler(); try { String sideClassFqName = context.packageName() + "." + sideClassName; JavaFileObject sourceFile = filer.createSourceFile(sideClassFqName, context.autoValueClass()); try (Writer sourceWriter = sourceFile.openWriter()) { sourceWriter.write(sideClass); } } catch (IOException e) { context .processingEnvironment() .getMessager() .printMessage(Diagnostic.Kind.ERROR, e.toString()); } return null; } } private static class FakeWriteToParcelExtension extends NonFinalExtension { private ExecutableElement writeToParcelMethod(Context context) { for (ExecutableElement method : context.abstractMethods()) { if (method.getSimpleName().contentEquals("writeToParcel")) { return method; } } throw new AssertionError("Did not see abstract method writeToParcel"); } @Override public Set consumeMethods(Context context) { return ImmutableSet.of(writeToParcelMethod(context)); } @Override String extraText(Context context) { // This is perhaps overgeneral. It is simply going to generate this: // @Override void writeToParcel(Object parcel, int flags) {} ExecutableElement methodToImplement = writeToParcelMethod(context); assertThat(methodToImplement.getReturnType().getKind()).isEqualTo(TypeKind.VOID); ImmutableList.Builder typesAndNamesBuilder = ImmutableList.builder(); for (VariableElement p : methodToImplement.getParameters()) { typesAndNamesBuilder.add(p.asType() + " " + p.getSimpleName()); } return "@Override void " + methodToImplement.getSimpleName() + "(" + Joiner.on(", ").join(typesAndNamesBuilder.build()) + ") {}"; } } @Test public void propertyTypes() { JavaFileObject parent = JavaFileObjects.forSourceLines( "foo.bar.Parent", "package foo.bar;", "", "import java.util.List;", "", "interface Parent {", " T thing();", " List list();", "}"); JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "abstract class Baz implements Parent {", "}"); ContextChecker checker = context -> { assertThat(context.builder()).isEmpty(); Map propertyTypes = context.propertyTypes(); assertThat(propertyTypes.keySet()).containsExactly("thing", "list"); TypeMirror thingType = propertyTypes.get("thing"); assertThat(thingType).isNotNull(); assertThat(thingType.getKind()).isEqualTo(TypeKind.DECLARED); assertThat(MoreTypes.asTypeElement(thingType).getQualifiedName().toString()) .isEqualTo("java.lang.String"); TypeMirror listType = propertyTypes.get("list"); assertThat(listType).isNotNull(); assertThat(listType.toString()).isEqualTo("java.util.List"); }; ContextCheckingExtension extension = new ContextCheckingExtension(checker); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(extension))) .compile(autoValueClass, parent); assertThat(compilation).succeededWithoutWarnings(); } @Test public void finalAutoValueClassName() { JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "abstract class Baz {", "}"); ContextChecker checker = context -> { assertThat(context.finalAutoValueClassName()).isEqualTo("foo.bar.AutoValue_Baz"); }; ContextCheckingExtension extension = new ContextCheckingExtension(checker); Compilation compilation = javac() .withProcessors( new AutoValueProcessor(ImmutableList.of(extension, new FinalExtension()))) .compile(autoValueClass); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation).generatedSourceFile("foo.bar.AutoValue_Baz"); // ContextCheckingExtension doesn't generate any code, so that name must be the class generated // by FinalExtension. } @Test public void builderContext() { JavaFileObject parent = JavaFileObjects.forSourceLines( "foo.bar.Parent", "package foo.bar;", "", "import com.google.common.collect.ImmutableList;", "", "interface Parent {", " T thing();", " ImmutableList list();", "}"); JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "", "@AutoValue", "abstract class Baz implements Parent {", " static Builder builder() {", " return new AutoValue_Baz.Builder();", " }", "", " abstract Builder toBuilder();", "", " @AutoValue.Builder", " abstract static class Builder {", " abstract Builder setThing(String x);", " abstract Builder setList(Iterable x);", " abstract Builder setList(ImmutableList x);", " abstract ImmutableList.Builder listBuilder();", " abstract Baz autoBuild();", " Baz build() {", " return autoBuild();", " }", " }", "}"); ContextChecker checker = context -> { assertThat(context.builder()).isPresent(); BuilderContext builderContext = context.builder().get(); assertThat(builderContext.builderType().getQualifiedName().toString()) .isEqualTo("foo.bar.Baz.Builder"); Set builderMethods = builderContext.builderMethods(); assertThat(builderMethods).hasSize(1); ExecutableElement builderMethod = Iterables.getOnlyElement(builderMethods); assertThat(builderMethod.getSimpleName().toString()).isEqualTo("builder"); Set toBuilderMethods = builderContext.toBuilderMethods(); assertThat(toBuilderMethods).hasSize(1); ExecutableElement toBuilderMethod = Iterables.getOnlyElement(toBuilderMethods); assertThat(toBuilderMethod.getSimpleName().toString()).isEqualTo("toBuilder"); Optional buildMethod = builderContext.buildMethod(); assertThat(buildMethod).isPresent(); assertThat(buildMethod.get().getSimpleName().toString()).isEqualTo("build"); assertThat(buildMethod.get().getParameters()).isEmpty(); assertThat(buildMethod.get().getReturnType().toString()).isEqualTo("foo.bar.Baz"); ExecutableElement autoBuildMethod = builderContext.autoBuildMethod(); assertThat(autoBuildMethod.getSimpleName().toString()).isEqualTo("autoBuild"); assertThat(autoBuildMethod.getModifiers()).contains(Modifier.ABSTRACT); assertThat(autoBuildMethod.getParameters()).isEmpty(); assertThat(autoBuildMethod.getReturnType().toString()).isEqualTo("foo.bar.Baz"); Map> setters = builderContext.setters(); assertThat(setters.keySet()).containsExactly("thing", "list"); Set thingSetters = setters.get("thing"); assertThat(thingSetters).hasSize(1); ExecutableElement thingSetter = Iterables.getOnlyElement(thingSetters); assertThat(thingSetter.getSimpleName().toString()).isEqualTo("setThing"); Set listSetters = setters.get("list"); assertThat(listSetters).hasSize(2); for (ExecutableElement listSetter : listSetters) { assertThat(listSetter.getSimpleName().toString()).isEqualTo("setList"); } Map propertyBuilders = builderContext.propertyBuilders(); assertThat(propertyBuilders.keySet()).containsExactly("list"); assertThat(propertyBuilders.get("list").getSimpleName().toString()) .isEqualTo("listBuilder"); }; ContextCheckingExtension extension = new ContextCheckingExtension(checker); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(extension))) .compile(autoValueClass, parent); assertThat(compilation).succeededWithoutWarnings(); } @Test public void oddBuilderContext() { JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "import com.google.common.collect.ImmutableList;", "", "@AutoValue", "abstract class Baz {", " abstract String string();", "", " @AutoValue.Builder", " abstract static class Builder {", " abstract Builder setString(String x);", " abstract Baz oddBuild();", " Baz build(int butNotReallyBecauseOfThisParameter) {", " return null;", " }", " }", "}"); ContextChecker checker = context -> { assertThat(context.builder()).isPresent(); BuilderContext builderContext = context.builder().get(); assertThat(builderContext.builderMethods()).isEmpty(); assertThat(builderContext.toBuilderMethods()).isEmpty(); assertThat(builderContext.buildMethod()).isEmpty(); assertThat(builderContext.autoBuildMethod().getSimpleName().toString()) .isEqualTo("oddBuild"); Map> setters = builderContext.setters(); assertThat(setters.keySet()).containsExactly("string"); Set thingSetters = setters.get("string"); assertThat(thingSetters).hasSize(1); ExecutableElement thingSetter = Iterables.getOnlyElement(thingSetters); assertThat(thingSetter.getSimpleName().toString()).isEqualTo("setString"); assertThat(builderContext.propertyBuilders()).isEmpty(); }; ContextCheckingExtension extension = new ContextCheckingExtension(checker); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(extension))) .compile(autoValueClass); assertThat(compilation).succeededWithoutWarnings(); } // https://github.com/google/auto/issues/809 @Test public void propertyErrorShouldNotCrash() { JavaFileObject autoValueClass = JavaFileObjects.forSourceLines( "test.Test", "package test;", "import com.google.auto.value.AutoValue;", "import java.util.List;", "", "@AutoValue", "public abstract class Test {", " abstract Integer property();", " abstract List listProperty();", "", " @AutoValue.Builder", " public interface Builder {", " Builder property(Integer property);", " Builder listProperty(List listProperty);", " Builder listProperty(Integer listPropertyValues);", " Test build();", " }", "}"); // We don't actually expect the extension to be invoked. Previously it was, and that led to a // NullPointerException when calling .setters() in the checker. ContextChecker checker = context -> { assertThat(context.builder()).isPresent(); assertThat(context.builder().get().setters()).isEmpty(); }; ContextCheckingExtension extension = new ContextCheckingExtension(checker); Compilation compilation = javac() .withProcessors(new AutoValueProcessor(ImmutableList.of(extension))) .compile(autoValueClass); assertThat(compilation) .hadErrorContaining("Parameter type java.lang.Integer of setter method") .inFile(autoValueClass) .onLineContaining("Builder listProperty(Integer listPropertyValues)"); } private interface ContextChecker extends Consumer {} private static class ContextCheckingExtension extends AutoValueExtension { private final Consumer checker; ContextCheckingExtension(Consumer checker) { this.checker = checker; } @Override public boolean applicable(Context context) { return true; } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { checker.accept(context); return null; } } } GeneratedDoesNotExistTest.java000066400000000000000000000217011365703632600352260ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; 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 com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.reflect.Reflection; import com.google.testing.compile.Compilation; import com.google.testing.compile.JavaFileObjects; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Collection; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** * Tests that {@link AutoValueProcessor} works even if run in a context where the {@code @Generated} * annotation does not exist. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(Parameterized.class) public class GeneratedDoesNotExistTest { @Parameters(name = "{0}") public static Collection data() { ImmutableList.Builder params = ImmutableList.builder(); if (SourceVersion.latestSupported().compareTo(SourceVersion.RELEASE_8) > 0) { // use default options when running on JDK > 8 // TODO(b/72513371): use --release 8 once compile-testing supports that params.add( new Object[] { ImmutableList.of(), "javax.annotation.processing.Generated", }); } params.add( new Object[] { ImmutableList.of("-source", "8", "-target", "8"), "javax.annotation.Generated", }); return params.build(); } private final ImmutableList javacOptions; private final String expectedAnnotation; public GeneratedDoesNotExistTest(ImmutableList javacOptions, String expectedAnnotation) { this.javacOptions = javacOptions; this.expectedAnnotation = expectedAnnotation; } // The classes here are basically just rigmarole to ensure that // Types.getTypeElement("javax.annotation.Generated") returns null, and to check that something // called that. We want a Processor that forwards everything to AutoValueProcessor, except that // the init(ProcessingEnvironment) method should forward a ProcessingEnvironment that filters // out the Generated class. So that ProcessingEnvironment forwards everything to the real // ProcessingEnvironment, except the ProcessingEnvironment.getElementUtils() method. That method // returns an Elements object that forwards everything to the real Elements except // getTypeElement("javax.annotation.Generated") and // getTypeElement("javax.annotation.processing.Generated"). private static final ImmutableSet GENERATED_ANNOTATIONS = ImmutableSet.of("javax.annotation.Generated", "javax.annotation.processing.Generated"); /** * InvocationHandler that forwards every method to an original object, except methods where there * is an implementation in this class with the same signature. So for example in the subclass * {@link ElementsHandler} there is a method {@link ElementsHandler#getTypeElement(CharSequence)}, * which means that a call of {@link Elements#getTypeElement(CharSequence)} on the proxy with this * invocation handler will end up calling that method, but a call of any of the other methods of * {@code Elements} will end up calling the method on the original object. */ private abstract static class OverridableInvocationHandler implements InvocationHandler { final T original; OverridableInvocationHandler(T original) { this.original = original; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { Method override = getClass().getMethod(method.getName(), method.getParameterTypes()); if (override.getDeclaringClass() == getClass()) { return override.invoke(this, args); } } catch (NoSuchMethodException ignored) { // OK: we don't have an override for this method, so just invoke the original method. } return method.invoke(original, args); } } private static T partialProxy(Class type, OverridableInvocationHandler handler) { return Reflection.newProxy(type, handler); } private static class ElementsHandler extends OverridableInvocationHandler { private final Set ignoredGenerated; ElementsHandler(Elements original, Set ignoredGenerated) { super(original); this.ignoredGenerated = ignoredGenerated; } public TypeElement getTypeElement(CharSequence name) { if (GENERATED_ANNOTATIONS.contains(name.toString())) { ignoredGenerated.add(name.toString()); return null; } else { return original.getTypeElement(name); } } } private static class ProcessingEnvironmentHandler extends OverridableInvocationHandler { private final Elements noGeneratedElements; ProcessingEnvironmentHandler(ProcessingEnvironment original, Set ignoredGenerated) { super(original); ElementsHandler elementsHandler = new ElementsHandler(original.getElementUtils(), ignoredGenerated); this.noGeneratedElements = partialProxy(Elements.class, elementsHandler); } public Elements getElementUtils() { return noGeneratedElements; } } private static class ProcessorHandler extends OverridableInvocationHandler { private final Set ignoredGenerated; ProcessorHandler(Processor original, Set ignoredGenerated) { super(original); this.ignoredGenerated = ignoredGenerated; } public void init(ProcessingEnvironment processingEnv) { ProcessingEnvironmentHandler processingEnvironmentHandler = new ProcessingEnvironmentHandler(processingEnv, ignoredGenerated); ProcessingEnvironment noGeneratedProcessingEnvironment = partialProxy(ProcessingEnvironment.class, processingEnvironmentHandler); original.init(noGeneratedProcessingEnvironment); } } @Test public void test() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( "foo.bar.Baz", "package foo.bar;", "", "import com.google.auto.value.AutoValue;", "", "@AutoValue", "public abstract class Baz {", " public static Baz create() {", " return new AutoValue_Baz();", " }", "}"); JavaFileObject expectedOutput = JavaFileObjects.forSourceLines( "foo.bar.AutoValue_Baz", "package foo.bar;", "", "final class AutoValue_Baz extends Baz {", " AutoValue_Baz() {", " }", "", " @Override public String toString() {", " return \"Baz{\"", " + \"}\";", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Baz) {", " return true;", " }", " return false;", " }", "", " @Override public int hashCode() {", " int h$ = 1;", " return h$;", " }", "}"); Set ignoredGenerated = ConcurrentHashMap.newKeySet(); Processor autoValueProcessor = new AutoValueProcessor(); ProcessorHandler handler = new ProcessorHandler(autoValueProcessor, ignoredGenerated); Processor noGeneratedProcessor = partialProxy(Processor.class, handler); Compilation compilation = javac() .withOptions(javacOptions) .withProcessors(noGeneratedProcessor) .compile(javaFileObject); assertThat(compilation).succeededWithoutWarnings(); assertThat(compilation) .generatedSourceFile("foo.bar.AutoValue_Baz") .hasSourceEquivalentTo(expectedOutput); assertThat(ignoredGenerated).containsExactly(expectedAnnotation); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/GeneratedImport.java000066400000000000000000000026571365703632600333400ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.value.processor; import javax.lang.model.SourceVersion; /** * Utility methods for compile-testing tests to know which {@code @Generated} annotation is * available. */ final class GeneratedImport { /** * Returns the qualified name of the {@code @Generated} annotation available during a compilation * task. */ static String generatedAnnotationType() { return SourceVersion.latestSupported().compareTo(SourceVersion.RELEASE_8) > 0 ? "javax.annotation.processing.Generated" : "javax.annotation.Generated"; } /** * Returns an {@code import} statement that imports the {@code @Generated} annotation {@linkplain * #generatedAnnotationType() available during a compilation task}. */ static String importGeneratedAnnotationType() { return "import " + generatedAnnotationType() + ";"; } } GuavaCollectionBuildersTest.java000066400000000000000000000110561365703632600355720ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableSet; import com.google.common.reflect.ClassPath; import com.google.common.truth.Expect; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Arrays; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Validates the assumptions AutoValue makes about Guava immutable collection builders. We expect * for each public class {@code com.google.common.collect.ImmutableFoo} that: * *

        *
      • it contains a public nested class {@code ImmutableFoo.Builder} with the same type * parameters; *
      • there is a public static method {@code ImmutableFoo.builder()} that returns {@code * ImmutableFoo.Builder}; *
      • there is a method {@code ImmutableFoo.Builder.build()} that returns {@code ImmutableFoo}; *
      • and there is a method in {@code ImmutableFoo.Builder} called either {@code addAll} or * {@code putAll} with a single parameter to which {@code ImmutableFoo} can be assigned. *
      * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class GuavaCollectionBuildersTest { private static final ImmutableSet NON_BUILDABLE_COLLECTIONS = ImmutableSet.of("ImmutableCollection"); @Rule public final Expect expect = Expect.create(); @Test public void testImmutableBuilders() throws Exception { ClassPath classPath = ClassPath.from(getClass().getClassLoader()); ImmutableSet classes = classPath.getAllClasses(); int checked = 0; for (ClassPath.ClassInfo classInfo : classes) { if (classInfo.getPackageName().equals("com.google.common.collect") && classInfo.getSimpleName().startsWith("Immutable") && !NON_BUILDABLE_COLLECTIONS.contains(classInfo.getSimpleName())) { Class c = Class.forName(classInfo.getName()); if (Modifier.isPublic(c.getModifiers())) { checked++; checkImmutableClass(c); } } } expect.that(checked).isGreaterThan(10); } private void checkImmutableClass(Class c) throws ClassNotFoundException, NoSuchMethodException { if (!Modifier.isPublic(c.getModifiers())) { return; } // We have a public static ImmutableFoo.builder() Method builderMethod = c.getMethod("builder"); assertThat(Modifier.isStatic(builderMethod.getModifiers())).isTrue(); // Its return type is Builder with the same type parameters. Type builderMethodReturn = builderMethod.getGenericReturnType(); expect.that(builderMethodReturn).isInstanceOf(ParameterizedType.class); ParameterizedType builderMethodParameterizedReturn = (ParameterizedType) builderMethodReturn; Class builderClass = Class.forName(c.getName() + "$Builder"); expect.that(builderMethod.getReturnType()).isEqualTo(builderClass); expect .withMessage(c.getName()) .that(Arrays.toString(builderMethodParameterizedReturn.getActualTypeArguments())) .isEqualTo(Arrays.toString(builderClass.getTypeParameters())); // The Builder has a public build() method that returns ImmutableFoo. Method buildMethod = builderClass.getMethod("build"); expect.that(buildMethod.getReturnType()).isEqualTo(c); // The Builder has either an addAll or a putAll public method with a parameter that // ImmutableFoo can be assigned to. boolean found = false; for (Method m : builderClass.getMethods()) { if ((m.getName().equals("addAll") || m.getName().equals("putAll")) && m.getParameterTypes().length == 1) { Class parameter = m.getParameterTypes()[0]; if (parameter.isAssignableFrom(c)) { found = true; break; } } } expect.withMessage(builderClass.getName() + " has addAll or putAll").that(found).isTrue(); } } IncrementalExtensionTest.java000066400000000000000000000112521365703632600351550ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2020 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Correspondence.transforming; import static com.google.common.truth.Truth.assertThat; import com.google.auto.value.extension.AutoValueExtension; import com.google.auto.value.extension.AutoValueExtension.IncrementalExtensionType; import com.google.auto.value.extension.memoized.processor.MemoizeExtension; import com.google.auto.value.extension.serializable.processor.SerializableAutoValueExtension; import com.google.common.collect.ImmutableList; import javax.annotation.processing.ProcessingEnvironment; import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests of Gradle incrementality in the presence of extensions. See * * incremental annotation processing in the Gradle user guide. */ @RunWith(JUnit4.class) public class IncrementalExtensionTest { @Test public void builtInExtensionsAreIsolating() { ImmutableList builtInExtensions = AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader()); // These are the current built-in extensions. We will update this test if we add more. // The (Object) cast is because otherwise the inferred type is Class, and we can't match // that with the class literals here. Even if we cast them to Class it will be a // different . assertThat(builtInExtensions) .comparingElementsUsing(transforming(e -> (Object) e.getClass(), "is class")) .containsExactly(MemoizeExtension.class, SerializableAutoValueExtension.class); AutoValueProcessor processor = new AutoValueProcessor(builtInExtensions); assertThat(processor.getSupportedOptions()) .contains(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption()); } @Test public void customExtensionsAreNotIsolatingByDefault() { AutoValueExtension nonIsolatingExtension = new NonIsolatingExtension(); assertThat(nonIsolatingExtension.incrementalType((ProcessingEnvironment) null)) .isEqualTo(IncrementalExtensionType.UNKNOWN); ImmutableList extensions = ImmutableList.builder() .addAll(AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader())) .add(nonIsolatingExtension) .build(); AutoValueProcessor processor = new AutoValueProcessor(extensions); assertThat(processor.getSupportedOptions()) .doesNotContain(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption()); } @Test public void customExtensionsCanBeIsolating() { AutoValueExtension isolatingExtension = new IsolatingExtension(); assertThat(isolatingExtension.incrementalType((ProcessingEnvironment) null)) .isEqualTo(IncrementalExtensionType.ISOLATING); ImmutableList extensions = ImmutableList.builder() .addAll(AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader())) .add(isolatingExtension) .build(); AutoValueProcessor processor = new AutoValueProcessor(extensions); assertThat(processor.getSupportedOptions()) .contains(IncrementalAnnotationProcessorType.ISOLATING.getProcessorOption()); } // Extensions are "UNKNOWN" by default. private static class NonIsolatingExtension extends AutoValueExtension { @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { return null; } } // Extensions are "ISOLATING" if they say they are. private static class IsolatingExtension extends AutoValueExtension { @Override public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) { return IncrementalExtensionType.ISOLATING; } @Override public String generateClass( Context context, String className, String classToExtend, boolean isFinal) { return null; } } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/JavaScannerTest.java000066400000000000000000000043161365703632600332740ustar00rootroot00000000000000/* * Copyright 2015 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class JavaScannerTest { private static final ImmutableList TOKENS = ImmutableList.of( " ", "\"hello \\\" world\\n\"", "'a'", " ", "'\\t'", " ", "`com.google.Foo`", " ", "\n ", "/* comment * comment \" whatever\n comment continued */", " ", "t", "h", "i", "n", "g", " ", "t", "h", "i", "n", "g", " ", "// line comment", "\n", "/*/ tricky comment */", "\n"); /** * Tests basic scanner functionality. The test concatenates the tokens in {@link #TOKENS} and then * retokenizes that string, checking that the same list of tokens is produced. */ @Test public void testScanner() { String input = Joiner.on("").join(TOKENS); ImmutableList.Builder tokensBuilder = ImmutableList.builder(); JavaScanner tokenizer = new JavaScanner(input); int end; for (int i = 0; i < input.length(); i = end) { end = tokenizer.tokenEnd(i); tokensBuilder.add(input.substring(i, end)); } assertThat(tokensBuilder.build()).containsExactlyElementsIn(TOKENS).inOrder(); } } PropertyAnnotationsTest.java000066400000000000000000000456631365703632600350760ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertAbout; import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.testing.compile.JavaFileObjects; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Target; import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; import javax.tools.JavaFileObject; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests to ensure annotations are kept on AutoValue generated classes * * @author jmcampanini */ @RunWith(JUnit4.class) public class PropertyAnnotationsTest { private static final String TEST_ANNOTATION = "@PropertyAnnotationsTest.TestAnnotation"; private static final String TEST_ARRAY_ANNOTATION = "@PropertyAnnotationsTest.TestArrayAnnotation"; public enum TestEnum { A, B; @Override public String toString() { // used to prove that the method we determine the value does not use the `toString()` method // of the enum return "not the same value"; } } public @interface TestAnnotation { byte testByte() default 1; short testShort() default 2; int testInt() default 3; long testLong() default 4L; float testFloat() default 5.6f; double testDouble() default 7.8d; char testChar() default 'a'; String testString() default "10"; boolean testBoolean() default false; Class testClass() default TestEnum.class; TestEnum testEnum() default TestEnum.A; OtherAnnotation testAnnotation() default @OtherAnnotation(foo = 23, bar = "baz"); } public @interface OtherAnnotation { int foo() default 123; String bar() default "bar"; } public @interface TestArrayAnnotation { byte[] testBytes() default {1, 2}; short[] testShorts() default {3, 4}; int[] testInts() default {5, 6}; long[] testLongs() default {7L, 8L}; float[] testFloats() default {9.1f, 2.3f}; double[] testDoubles() default {4.5d, 6.7d}; char[] testChars() default {'a', 'b'}; String[] testStrings() default {"cde", "fgh"}; boolean[] testBooleans() default {true, false}; Class[] testClasses() default {TestEnum.class, TestEnum.class}; TestEnum[] testEnums() default {TestEnum.A, TestEnum.B}; OtherAnnotation[] testAnnotations() default { @OtherAnnotation(foo = 999), @OtherAnnotation(bar = "baz") }; } @Inherited public @interface InheritedAnnotation {} private static class InputFileBuilder { Iterable imports = ImmutableList.of(); List annotations = new ArrayList<>(); List innerTypes = new ArrayList<>(); InputFileBuilder setImports(Iterable imports) { this.imports = imports; return this; } InputFileBuilder addAnnotations(String... annotations) { this.annotations.addAll(ImmutableList.copyOf(annotations)); return this; } InputFileBuilder addAnnotations(Iterable annotations) { this.annotations.addAll(ImmutableList.copyOf(annotations)); return this; } InputFileBuilder addInnerTypes(String... innerTypes) { this.innerTypes.addAll(ImmutableList.copyOf(innerTypes)); return this; } JavaFileObject build() { ImmutableList list = ImmutableList.builder() .add("package foo.bar;", "", "import com.google.auto.value.AutoValue;") .addAll(imports) .add("", "@AutoValue", "public abstract class Baz {") .addAll(annotations) .add( " public abstract int buh();", "", " public static Baz create(int buh) {", " return new AutoValue_Baz(buh);", " }") .addAll(innerTypes) .add("}") .build(); String[] lines = list.toArray(new String[list.size()]); return JavaFileObjects.forSourceLines("foo.bar.Baz", lines); } } private static class OutputFileBuilder { Iterable imports = ImmutableList.of(); List fieldAnnotations = new ArrayList<>(); List methodAnnotations = new ArrayList<>(); OutputFileBuilder setImports(Iterable imports) { this.imports = imports; return this; } OutputFileBuilder addFieldAnnotations(String... annotations) { this.fieldAnnotations.addAll(ImmutableList.copyOf(annotations)); return this; } OutputFileBuilder addMethodAnnotations(String... innerTypes) { this.methodAnnotations.addAll(ImmutableList.copyOf(innerTypes)); return this; } OutputFileBuilder addMethodAnnotations(Iterable innerTypes) { this.methodAnnotations.addAll(ImmutableList.copyOf(innerTypes)); return this; } JavaFileObject build() { String nullable = methodAnnotations.contains("@Nullable") ? "@Nullable " : ""; ImmutableSortedSet allImports = ImmutableSortedSet.naturalOrder() .add(GeneratedImport.importGeneratedAnnotationType()) .addAll(imports) .build(); ImmutableList list = ImmutableList.builder() .add("package foo.bar;", "") .addAll(allImports) .add( "", "@Generated(\"" + AutoValueProcessor.class.getName() + "\")", "final class AutoValue_Baz extends Baz {") .addAll(fieldAnnotations) .add( " private final int buh;", " AutoValue_Baz(" + nullable + "int buh) {", " this.buh = buh;", " }", "") .addAll(methodAnnotations) .add( " @Override public int buh() {", " return buh;", " }", "", " @Override public String toString() {", " return \"Baz{\"", " + \"buh=\" + buh", " + \"}\";", " }", "", " @Override public boolean equals(Object o) {", " if (o == this) {", " return true;", " }", " if (o instanceof Baz) {", " Baz that = (Baz) o;", " return this.buh == that.buh();", " }", " return false;", " }", "", " @Override public int hashCode() {", " int h$ = 1;", " h$ *= 1000003;", " h$ ^= buh;", " return h$;", " }", "}") .build(); String[] lines = list.toArray(new String[list.size()]); return JavaFileObjects.forSourceLines("foo.bar.AutoValue_Baz", lines); } } private ImmutableSet getImports(Class... classes) { ImmutableSet.Builder stringBuilder = ImmutableSortedSet.naturalOrder(); for (Class clazz : classes) { stringBuilder.add("import " + clazz.getName() + ";"); } return stringBuilder.build(); } private void assertGeneratedMatches( Iterable imports, Iterable annotations, Iterable expectedMethodAnnotations) { JavaFileObject javaFileObject = new InputFileBuilder().setImports(imports).addAnnotations(annotations).build(); JavaFileObject expectedOutput = new OutputFileBuilder() .setImports(imports) .addMethodAnnotations(expectedMethodAnnotations) .build(); assertAbout(javaSource()) .that(javaFileObject) .processedWith(new AutoValueProcessor()) .compilesWithoutError() .and() .generatesSources(expectedOutput); } @Test public void testSimpleAnnotation() { assertGeneratedMatches( ImmutableList.of(), ImmutableList.of("@Deprecated"), ImmutableList.of("@Deprecated")); } @Test public void testSingleStringValueAnnotation() { assertGeneratedMatches( ImmutableList.of(), ImmutableList.of("@SuppressWarnings(\"a\")"), ImmutableList.of("@SuppressWarnings(\"a\")")); } @Test public void testMultiStringValueAnnotation() { assertGeneratedMatches( ImmutableList.of(), ImmutableList.of("@SuppressWarnings({\"a\", \"b\"})"), ImmutableList.of("@SuppressWarnings({\"a\", \"b\"})")); } @Test public void testNumberValueAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of(TEST_ANNOTATION + "(testShort = 1, testInt = 2, testLong = 3L)"), ImmutableList.of(TEST_ANNOTATION + "(testShort = 1, testInt = 2, testLong = 3L)")); } @Test public void testByteValueAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of(TEST_ANNOTATION + "(testByte = 0)"), ImmutableList.of(TEST_ANNOTATION + "(testByte = 0)")); } @Test public void testDecimalValueAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of(TEST_ANNOTATION + "(testDouble = 1.2d, testFloat = 3.4f)"), ImmutableList.of(TEST_ANNOTATION + "(testDouble = 1.2d, testFloat = 3.4f)")); } @Test public void testOtherValuesAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ANNOTATION + "(testBoolean = true, testString = \"hallo\", testChar = 'a')"), ImmutableList.of( TEST_ANNOTATION + "(testBoolean = true, testString = \"hallo\", testChar = 'a')")); } @Test public void testClassAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of(TEST_ANNOTATION + "(testClass = String.class)"), ImmutableList.of(TEST_ANNOTATION + "(testClass = String.class)")); } @Test public void testEnumAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ANNOTATION + "(testEnum = " + PropertyAnnotationsTest.class.getName() + ".TestEnum.A)"), ImmutableList.of(TEST_ANNOTATION + "(testEnum = PropertyAnnotationsTest.TestEnum.A)")); } @Test public void testEmptyAnnotationAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ANNOTATION + "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation)"), ImmutableList.of( TEST_ANNOTATION + "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation)")); } @Test public void testValuedAnnotationAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ANNOTATION + "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation(foo=999))"), ImmutableList.of( TEST_ANNOTATION + "(testAnnotation = @PropertyAnnotationsTest.OtherAnnotation(foo=999))")); } @Test public void testNumberArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testShorts = {2, 3}, testInts = {4, 5}, testLongs = {6L, 7L})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testShorts = {2, 3}, testInts = {4, 5}, testLongs = {6L, 7L})")); } @Test public void testByteArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testBytes = {0, 1})"), ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testBytes = {0, 1})")); } @Test public void testDecimalArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testDoubles = {1.2d, 3.4d}, testFloats = {5.6f, 7.8f})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testDoubles = {1.2d, 3.4d}, testFloats = {5.6f, 7.8f})")); } @Test public void testOtherArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testBooleans = {false, false}," + " testStrings = {\"aaa\", \"bbb\"}, testChars={'x', 'y'})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testBooleans = {false, false}," + " testStrings = {\"aaa\", \"bbb\"}, testChars={'x', 'y'})")); } @Test public void testClassArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testClasses = {String.class, Long.class})"), ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testClasses = {String.class, Long.class})")); } @Test public void testImportedClassArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class, Nullable.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testClasses = {javax.annotation.Nullable.class, Long.class})"), ImmutableList.of(TEST_ARRAY_ANNOTATION + "(testClasses = {Nullable.class, Long.class})")); } @Test public void testEnumArrayAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A," + " PropertyAnnotationsTest.TestEnum.B})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A," + " PropertyAnnotationsTest.TestEnum.B})")); } @Test public void testArrayOfEmptyAnnotationAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testAnnotations = @PropertyAnnotationsTest.OtherAnnotation)")); assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation," + " @PropertyAnnotationsTest.OtherAnnotation})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation," + " @PropertyAnnotationsTest.OtherAnnotation})")); } @Test public void testArrayOfValuedAnnotationAnnotation() { assertGeneratedMatches( getImports(PropertyAnnotationsTest.class), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testAnnotations = {@PropertyAnnotationsTest.OtherAnnotation(foo = 999)})"), ImmutableList.of( TEST_ARRAY_ANNOTATION + "(testAnnotations = @PropertyAnnotationsTest.OtherAnnotation(foo = 999))")); } /** * Tests that when CopyAnnotations is present on a method, all non-inherited annotations (except * those appearing in CopyAnnotations.exclude) are copied to the method implementation in the * generated class. */ @Test public void testCopyingMethodAnnotations() { ImmutableSet imports = getImports(PropertyAnnotationsTest.class); JavaFileObject inputFile = new InputFileBuilder() .setImports(imports) .addAnnotations( "@AutoValue.CopyAnnotations(exclude=" + "{PropertyAnnotationsTest.TestAnnotation.class})", "@Deprecated", "@PropertyAnnotationsTest.TestAnnotation", "@PropertyAnnotationsTest.InheritedAnnotation") .build(); JavaFileObject outputFile = new OutputFileBuilder() .setImports(imports) .addMethodAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation") .addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation") .build(); assertAbout(javaSource()) .that(inputFile) .processedWith(new AutoValueProcessor()) .compilesWithoutError() .and() .generatesSources(outputFile); } /** * Tests that when CopyAnnotationsToGeneratedField is present on a method, all non-inherited * annotations (except those appearing in CopyAnnotationsToGeneratedField.exclude) are copied to * the method implementation in the generated class. */ @Test public void testCopyingMethodAnnotationsToGeneratedFields() { JavaFileObject inputFile = new InputFileBuilder() .setImports(getImports(PropertyAnnotationsTest.class, Target.class, ElementType.class)) .addAnnotations( "@AutoValue.CopyAnnotations(exclude={PropertyAnnotationsTest." + "TestAnnotation.class})", "@Deprecated", "@PropertyAnnotationsTest.TestAnnotation", "@PropertyAnnotationsTest.InheritedAnnotation", "@MethodsOnly") .addInnerTypes("@Target(ElementType.METHOD) @interface MethodsOnly {}") .build(); JavaFileObject outputFile = new OutputFileBuilder() .setImports(getImports(PropertyAnnotationsTest.class)) .addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation") .addMethodAnnotations( "@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation", "@Baz.MethodsOnly") .build(); assertAbout(javaSource()) .that(inputFile) .processedWith(new AutoValueProcessor()) .compilesWithoutError() .and() .generatesSources(outputFile); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java000066400000000000000000000041441365703632600337100ustar00rootroot00000000000000/* * Copyright 2018 Google LLC * * 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.google.auto.value.processor; import com.google.common.collect.ImmutableMap; import com.google.common.truth.Expect; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class PropertyNamesTest { @Rule public Expect expect = Expect.create(); private static final ImmutableMap NORMAL_CASES = ImmutableMap.builder() .put("Foo", "foo") .put("foo", "foo") .put("X", "x") .put("x", "x") .put("", "") .build(); @Test public void decapitalizeLikeJavaBeans() { NORMAL_CASES .forEach( (input, output) -> { expect.that(PropertyNames.decapitalizeLikeJavaBeans(input)).isEqualTo(output); }); expect.that(PropertyNames.decapitalizeLikeJavaBeans(null)).isNull(); expect.that(PropertyNames.decapitalizeLikeJavaBeans("HTMLPage")).isEqualTo("HTMLPage"); expect.that(PropertyNames.decapitalizeLikeJavaBeans("OAuth")).isEqualTo("OAuth"); } @Test public void decapitalizeNormally() { NORMAL_CASES .forEach( (input, output) -> { expect.that(PropertyNames.decapitalizeNormally(input)).isEqualTo(output); }); expect.that(PropertyNames.decapitalizeNormally(null)).isNull(); expect.that(PropertyNames.decapitalizeNormally("HTMLPage")).isEqualTo("hTMLPage"); expect.that(PropertyNames.decapitalizeNormally("OAuth")).isEqualTo("oAuth"); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java000066400000000000000000000063621365703632600333760ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Unit tests for {@link Reformatter}. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class ReformatterTest { @Test public void testSimple() { String input = "\n" + "package com.latin.declension; \n" + "\n" + "\n" + "public class Idem { \n" + " \n" + " Eadem idem ; \n" + "\n" + " Eundem eandem ( Idem eiusdem ) {\n" + "\n" + " eiusdem ( eiusdem ) ; \n" + "\n" + " eidem_eidem_eidem( ) ;\n" + "\n" + " }\n" + "\n" + "\n" + " Eodem ( Eadem eodem ) { }\n"; String output = "package com.latin.declension;\n" + "\n" + "public class Idem {\n" + "\n" + " Eadem idem;\n" + "\n" + " Eundem eandem (Idem eiusdem) {\n" + " eiusdem (eiusdem);\n" + " eidem_eidem_eidem();\n" + " }\n" + "\n" + " Eodem (Eadem eodem) { }\n"; assertEquals(output, Reformatter.fixup(input)); } @Test public void testSpecialSpaces() { String input = "\n" + "package com.example.whatever;\n" + "\n" + "public class SomeClass {\n" + " static final String STRING = \" hello world \\n\"; \n" + " static final String STRING_WITH_QUOTES = \" \\\"quote me now \\\" \" ;\n" + " static final int INT = /* not a string \" */ 23 ;\n" + " static final char QUOTE = '\"' ;\n" + " static final char QUOTE2 = '\\\"' ;\n" + "}\n"; String output = "package com.example.whatever;\n" + "\n" + "public class SomeClass {\n" + " static final String STRING = \" hello world \\n\";\n" + " static final String STRING_WITH_QUOTES = \" \\\"quote me now \\\" \";\n" + " static final int INT = /* not a string \" */ 23;\n" + " static final char QUOTE = '\"';\n" + " static final char QUOTE2 = '\\\"';\n" + "}\n"; assertEquals(output, Reformatter.fixup(input)); } @Test public void noTrailingNewline() { String input = "package com.example.whatever;\n\npublic class SomeClass {}"; String output = input + "\n"; assertEquals(output, Reformatter.fixup(input)); } } SimpleServiceLoaderTest.java000066400000000000000000000157601365703632600347300ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2019 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import static java.util.stream.Collectors.toList; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; import java.nio.charset.StandardCharsets; import java.util.Enumeration; import java.util.List; import java.util.ServiceConfigurationError; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public final class SimpleServiceLoaderTest { @Test public void loadOnce() throws Exception { ClassLoader loader = loaderForJarWithEntries( CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName()); ImmutableList providers = SimpleServiceLoader.load(CharSequence.class, loader); // The provider entry for java.lang.String should have caused us to call new String(), which // will produce this "" in the providers. assertThat(providers).contains(""); List> classes = providers.stream().map(Object::getClass).collect(toList()); assertThat(classes).containsExactly(String.class, StringBuilder.class).inOrder(); } @Test public void blankLinesAndComments() throws Exception { ClassLoader loader = loaderForJarWithEntries( CharSequence.class.getName(), "", "# this is a comment", " # this is also a comment", " java.lang.String # this is a comment after a class name"); ImmutableList providers = SimpleServiceLoader.load(CharSequence.class, loader); assertThat(providers).containsExactly(""); } @Test public void loadTwiceFromSameLoader() throws Exception { ClassLoader loader = loaderForJarWithEntries( CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName()); ImmutableList providers1 = SimpleServiceLoader.load(CharSequence.class, loader); ImmutableList providers2 = SimpleServiceLoader.load(CharSequence.class, loader); List> classes1 = providers1.stream().map(Object::getClass).collect(toList()); List> classes2 = providers2.stream().map(Object::getClass).collect(toList()); assertThat(classes2).containsExactlyElementsIn(classes1).inOrder(); } @Test public void loadTwiceFromDifferentLoaders() throws Exception { URL jarUrl = urlForJarWithEntries( CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName()); ClassLoader loader1 = new URLClassLoader(new URL[] {jarUrl}); ImmutableList providers1 = SimpleServiceLoader.load(CharSequence.class, loader1); // We should have called `new String()`, so the result should contain "". assertThat(providers1).contains(""); ClassLoader loader2 = new URLClassLoader(new URL[] {jarUrl}); ImmutableList providers2 = SimpleServiceLoader.load(CharSequence.class, loader2); List> classes1 = providers1.stream().map(Object::getClass).collect(toList()); List> classes2 = providers2.stream().map(Object::getClass).collect(toList()); assertThat(classes2).containsExactlyElementsIn(classes1).inOrder(); } @Test public void noProviders() throws Exception { ClassLoader loader = loaderForJarWithEntries(CharSequence.class.getName()); ImmutableList providers = SimpleServiceLoader.load(CharSequence.class, loader); assertThat(providers).isEmpty(); } @Test public void classNotFound() throws Exception { ClassLoader loader = loaderForJarWithEntries(CharSequence.class.getName(), "this.is.not.a.Class"); try { SimpleServiceLoader.load(CharSequence.class, loader); fail(); } catch (ServiceConfigurationError expected) { assertThat(expected).hasMessageThat().startsWith("Could not load "); } } @Test public void wrongTypeClass() throws Exception { ClassLoader loader = loaderForJarWithEntries(CharSequence.class.getName(), "java.lang.Thread"); try { SimpleServiceLoader.load(CharSequence.class, loader); fail(); } catch (ServiceConfigurationError expected) { assertThat(expected).hasMessageThat().startsWith("Class java.lang.Thread is not assignable"); } } @Test public void couldNotConstruct() throws Exception { ClassLoader loader = loaderForJarWithEntries("java.lang.System", "java.lang.System"); try { SimpleServiceLoader.load(System.class, loader); fail(); } catch (ServiceConfigurationError expected) { assertThat(expected).hasMessageThat().startsWith("Could not construct"); } } @Test public void brokenLoader() { ClassLoader loader = new URLClassLoader(new URL[0]) { @Override public Enumeration getResources(String name) throws IOException { throw new IOException("bang"); } }; try { SimpleServiceLoader.load(CharSequence.class, loader); fail(); } catch (ServiceConfigurationError expected) { assertThat(expected).hasMessageThat().startsWith("Could not look up"); assertThat(expected).hasCauseThat().hasMessageThat().isEqualTo("bang"); } } private static ClassLoader loaderForJarWithEntries(String service, String... lines) throws IOException { URL jarUrl = urlForJarWithEntries(service, lines); return new URLClassLoader(new URL[] {jarUrl}); } private static URL urlForJarWithEntries(String service, String... lines) throws IOException { File jar = File.createTempFile("SimpleServiceLoaderTest", "jar"); jar.deleteOnExit(); try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar))) { JarEntry jarEntry = new JarEntry("META-INF/services/" + service); out.putNextEntry(jarEntry); // It would be bad practice to use try-with-resources below, because closing the PrintWriter // would close the JarOutputStream. PrintWriter writer = new PrintWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)); for (String line : lines) { writer.println(line); } writer.flush(); } return jar.toURI().toURL(); } } SimplifyWithAnnotationsTest.java000066400000000000000000000200161365703632600356630ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2017 Google LLC * * 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.google.auto.value.processor; import static com.google.common.collect.MoreCollectors.onlyElement; 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 com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.truth.Expect; import com.google.testing.compile.JavaFileObjects; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; 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.ElementFilter; import javax.tools.JavaFileObject; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * This test verifies the method {@link TypeEncoder#encodeWithAnnotations(TypeMirror). * It takes a list of "type spellings", like {@code @Nullable String}, and compiles a class * with one field for each spelling. So there might be a field {@code @Nullable String x2;}. * Then it examines each compiled field to extract its {@code TypeMirror}, and uses the * {@code TypeSimplifier} method to reconvert that into a string. It should get back the same * type spelling in each case. * *

      I originally tried to write a less convoluted test using compile-testing. In my test, * each type to be tested was an actual type in the test class (the type of a field, or the * return type of a method). However, I found that if I examined these types by looking up a class * with {@link javax.lang.model.util.Elements#getTypeElement} and following through to the type * of interest, it never had any type annotations. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class SimplifyWithAnnotationsTest { @Rule public final Expect expect = Expect.create(); /** * The types that we will compile and then recreate. They are referenced in a context where {@code * Set} is unambiguous but not {@code List}, which allows us to test the placement of annotations * in unqualified types like {@code Set} and qualified types like {@code java.util.List}. */ private static final ImmutableList TYPE_SPELLINGS = ImmutableList.of( "Object", "Set", "String", "Nullable", "@Nullable String", "String[]", "@Nullable String[]", "String @Nullable []", "String @Nullable [] @Nullable []", "java.awt.List", "java.util.List", "Set<@Nullable String>", "@Nullable Set", "int", "@Nullable int", // whatever that might mean "@Nullable int[]", "int @Nullable []", "T", "@Nullable T", "Set<@Nullable T>", "Set", "Set", "Set", "java.util.@Nullable List<@Nullable T>", "java.util.@Nullable List>"); private static final JavaFileObject NULLABLE_FILE_OBJECT = JavaFileObjects.forSourceLines( "pkg.Nullable", "package pkg;", "", "import java.lang.annotation.ElementType;", "import java.lang.annotation.Target;", "", "@Target(ElementType.TYPE_USE)", "public @interface Nullable {}"); private static final JavaFileObject TEST_CLASS_FILE_OBJECT = JavaFileObjects.forSourceLines("pkg.TestClass", buildTestClass()); private static ImmutableList buildTestClass() { // Some older versions of javac don't handle type annotations at all well in annotation // processors. The `witness` method in the generated class is there to detect that, and // skip the test if it is the case. ImmutableList.Builder builder = ImmutableList.builder(); builder.add( "package pkg;", "", "import java.util.Set;", "", "public abstract class TestClass {", " abstract @Nullable T witness();"); int i = 0; for (String typeSpelling : TYPE_SPELLINGS) { builder.add(String.format(" %s x%d;\n", typeSpelling, i++)); } builder.add("}"); return builder.build(); } @Test public void testSimplifyWithAnnotations() { // The real test happens inside the .compile(...), by virtue of TestProcessor. assertThat( javac() .withOptions("-proc:only") .withProcessors(new TestProcessor()) .compile(NULLABLE_FILE_OBJECT, TEST_CLASS_FILE_OBJECT)) .succeededWithoutWarnings(); } @SupportedAnnotationTypes("*") private static class TestProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latest(); } @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { TypeElement testClass = processingEnv.getElementUtils().getTypeElement("pkg.TestClass"); testTypeSpellings(testClass); } return false; } void testTypeSpellings(TypeElement testClass) { ExecutableElement witness = ElementFilter.methodsIn(testClass.getEnclosedElements()) .stream() .filter(m -> m.getSimpleName().contentEquals("witness")) .collect(onlyElement()); if (witness.getReturnType().getAnnotationMirrors().isEmpty()) { System.err.println("SKIPPING TEST BECAUSE OF BUGGY COMPILER"); return; } ImmutableMap typeSpellingToType = typesFromTestClass(testClass); assertThat(typeSpellingToType).isNotEmpty(); StringBuilder text = new StringBuilder(); StringBuilder expected = new StringBuilder(); // Build up a fake source text with the encodings for the types in it, and decode it to // ensure the type spellings are what we expect. typeSpellingToType.forEach( (typeSpelling, type) -> { text.append("{").append(TypeEncoder.encodeWithAnnotations(type)).append("}"); expected.append("{").append(typeSpelling).append("}"); }); String decoded = TypeEncoder.decode(text.toString(), processingEnv, "pkg", null); assertThat(decoded).isEqualTo(expected.toString()); } private static ImmutableMap typesFromTestClass(TypeElement type) { // Reads the types of the fields from the compiled TestClass and uses them to produce // a map from type spellings to types. This method depends on type.getEnclosedElements() // returning the fields in source order, which it is specified to do. ImmutableMap.Builder typeSpellingToTypeBuilder = ImmutableMap.builder(); int i = 0; for (VariableElement field : fieldsIn(type.getEnclosedElements())) { String spelling = TYPE_SPELLINGS.get(i); typeSpellingToTypeBuilder.put(spelling, field.asType()); i++; } return typeSpellingToTypeBuilder.build(); } } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/TemplateVarsTest.java000066400000000000000000000231311365703632600335040ustar00rootroot00000000000000/* * Copyright 2014 Google LLC * * 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.google.auto.value.processor; import static com.google.common.base.StandardSystemProperty.JAVA_CLASS_PATH; import static com.google.common.base.StandardSystemProperty.PATH_SEPARATOR; import static com.google.common.truth.Truth.assertThat; import static java.util.logging.Level.WARNING; import static org.junit.Assert.fail; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.reflect.Reflection; import com.google.escapevelocity.Template; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.Callable; import java.util.logging.Logger; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for FieldReader. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class TemplateVarsTest { static class HappyVars extends TemplateVars { Integer integer; String string; List list; private static final String IGNORED_STATIC_FINAL = "hatstand"; @Override Template parsedTemplate() { return parsedTemplateForString("integer=$integer string=$string list=$list"); } } static Template parsedTemplateForString(String string) { try { Reader reader = new StringReader(string); return Template.parseFrom(reader); } catch (IOException e) { throw new AssertionError(e); } } @Test public void testHappy() { HappyVars happy = new HappyVars(); happy.integer = 23; happy.string = "wibble"; happy.list = ImmutableList.of(5, 17, 23); assertThat(HappyVars.IGNORED_STATIC_FINAL).isEqualTo("hatstand"); // avoids unused warning String expectedText = "integer=23 string=wibble list=[5, 17, 23]"; String actualText = happy.toText(); assertThat(actualText).isEqualTo(expectedText); } @Test public void testUnset() { HappyVars sad = new HappyVars(); sad.integer = 23; sad.list = ImmutableList.of(23); try { sad.toText(); fail("Did not get expected exception"); } catch (IllegalArgumentException expected) { } } static class SubHappyVars extends HappyVars { Character character; @Override Template parsedTemplate() { return parsedTemplateForString( "integer=$integer string=$string list=$list character=$character"); } } @Test public void testSubSub() { SubHappyVars vars = new SubHappyVars(); vars.integer = 23; vars.string = "wibble"; vars.list = ImmutableList.of(5, 17, 23); vars.character = 'ß'; String expectedText = "integer=23 string=wibble list=[5, 17, 23] character=ß"; String actualText = vars.toText(); assertThat(actualText).isEqualTo(expectedText); } static class Private extends TemplateVars { Integer integer; private String unusedString; @Override Template parsedTemplate() { throw new UnsupportedOperationException(); } } @Test public void testPrivate() { try { new Private(); fail("Did not get expected exception"); } catch (IllegalArgumentException expected) { } } static class Static extends TemplateVars { Integer integer; static String string; @Override Template parsedTemplate() { throw new UnsupportedOperationException(); } } @Test public void testStatic() { try { new Static(); fail("Did not get expected exception"); } catch (IllegalArgumentException expected) { } } static class Primitive extends TemplateVars { int integer; String string; @Override Template parsedTemplate() { throw new UnsupportedOperationException(); } } @Test public void testPrimitive() { try { new Primitive(); fail("Did not get expected exception"); } catch (IllegalArgumentException expected) { } } @Test public void testBrokenInputStream_IOException() throws Exception { doTestBrokenInputStream(new IOException("BrokenInputStream")); } @Test public void testBrokenInputStream_NullPointerException() throws Exception { doTestBrokenInputStream(new NullPointerException("BrokenInputStream")); } @Test public void testBrokenInputStream_IllegalStateException() throws Exception { doTestBrokenInputStream(new IllegalStateException("BrokenInputStream")); } // This is a complicated test that tries to simulates the failures that are worked around in // Template.parsedTemplateForResource. Those failures means that the InputStream returned by // ClassLoader.getResourceAsStream sometimes throws IOException or NullPointerException or // IllegalStateException while it is being read. To simulate that, we make a second ClassLoader // with the same configuration as the one that runs this test, and we override getResourceAsStream // so that it wraps the returned InputStream in a BrokenInputStream, which throws an exception // after a certain number of characters. We check that that exception was indeed seen, and that // we did indeed try to read the resource we're interested in, and that we succeeded in loading a // Template nevertheless. private void doTestBrokenInputStream(Exception exception) throws Exception { URLClassLoader shadowLoader = new ShadowLoader(getClass().getClassLoader(), exception); Runnable brokenInputStreamTest = (Runnable) shadowLoader .loadClass(BrokenInputStreamTest.class.getName()) .getConstructor() .newInstance(); brokenInputStreamTest.run(); } private static class ShadowLoader extends URLClassLoader implements Callable> { private static final Logger logger = Logger.getLogger(ShadowLoader.class.getName()); private final Exception exception; private final Set result = new TreeSet(); ShadowLoader(ClassLoader original, Exception exception) { super(getClassPathUrls(original), original.getParent()); this.exception = exception; } private static URL[] getClassPathUrls(ClassLoader original) { return original instanceof URLClassLoader ? ((URLClassLoader) original).getURLs() : parseJavaClassPath(); } /** * Returns the URLs in the class path specified by the {@code java.class.path} {@linkplain * System#getProperty system property}. */ // TODO(b/65488446): Use a new public API. private static URL[] parseJavaClassPath() { ImmutableList.Builder urls = ImmutableList.builder(); for (String entry : Splitter.on(PATH_SEPARATOR.value()).split(JAVA_CLASS_PATH.value())) { try { try { urls.add(new File(entry).toURI().toURL()); } catch (SecurityException e) { // File.toURI checks to see if the file is a directory urls.add(new URL("file", null, new File(entry).getAbsolutePath())); } } catch (MalformedURLException e) { logger.log(WARNING, "malformed classpath entry: " + entry, e); } } return urls.build().toArray(new URL[0]); } @Override public Set call() throws Exception { return result; } @Override public InputStream getResourceAsStream(String resource) { // Make sure this is actually the resource we are expecting. If we're using JaCoCo or the // like, we might end up reading some other resource, and we don't want to break that. if (resource.startsWith("com/google/auto")) { result.add(resource); return new BrokenInputStream(super.getResourceAsStream(resource)); } else { return super.getResourceAsStream(resource); } } private class BrokenInputStream extends InputStream { private final InputStream original; private int count = 0; BrokenInputStream(InputStream original) { this.original = original; } @Override public int read() throws IOException { if (++count > 10) { result.add("threw"); if (exception instanceof IOException) { throw (IOException) exception; } throw (RuntimeException) exception; } return original.read(); } } } public static class BrokenInputStreamTest implements Runnable { @Override public void run() { Template template = TemplateVars.parsedTemplateForResource("autovalue.vm"); assertThat(template).isNotNull(); String resourceName = Reflection.getPackageName(getClass()).replace('.', '/') + "/autovalue.vm"; @SuppressWarnings("unchecked") Callable> myLoader = (Callable>) getClass().getClassLoader(); try { Set result = myLoader.call(); assertThat(result).contains(resourceName); assertThat(result).contains("threw"); } catch (Exception e) { throw new AssertionError(e); } } } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java000066400000000000000000000446221365703632600333260ustar00rootroot00000000000000/* * Copyright 2017 Google LLC * * 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.google.auto.value.processor; 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 java.util.stream.Collectors.joining; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.testing.compile.Compilation; import com.google.testing.compile.CompilationRule; import com.google.testing.compile.JavaFileObjects; import java.util.List; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; 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.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link TypeEncoder}. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class TypeEncoderTest { @Rule public final CompilationRule compilationRule = new CompilationRule(); private Types typeUtils; private Elements elementUtils; @Before public void setUp() { typeUtils = compilationRule.getTypes(); elementUtils = compilationRule.getElements(); } /** * Assert that the fake program returned by fakeProgramForTypes has the given list of imports and * the given list of spellings. Here, "spellings" means the way each type is referenced in the * decoded program, for example {@code Timer} if {@code java.util.Timer} can be imported, or * {@code java.util.Timer} if not. * *

      We construct a fake program that references each of the given types in turn. * TypeEncoder.decode doesn't have any real notion of Java syntax, so our program just consists of * START and END markers around the {@code `import`} tag, followed by each type in braces, as * encoded by TypeEncoder.encode. Once decoded, the program should consist of the appropriate * imports (inside START...END) and each type in braces, spelled appropriately. * * @param fakePackage the package that TypeEncoder should consider the fake program to be in. * Classes in the same package don't usually need to be imported. */ private void assertTypeImportsAndSpellings( Set types, String fakePackage, List imports, List spellings) { String fakeProgram = "START\n`import`\nEND\n" + types.stream().map(TypeEncoder::encode).collect(joining("}\n{", "{", "}")); String decoded = TypeEncoder.decode( fakeProgram, elementUtils, typeUtils, fakePackage, baseWithoutContainedTypes()); String expected = "START\n" + imports.stream().map(s -> "import " + s + ";\n").collect(joining()) + "\nEND\n" + spellings.stream().collect(joining("}\n{", "{", "}")); assertThat(decoded).isEqualTo(expected); } private static class MultipleBounds & Comparable, V> {} @Test public void testImportsForNoTypes() { assertTypeImportsAndSpellings( typeMirrorSet(), "foo.bar", ImmutableList.of(), ImmutableList.of()); } @Test public void testImportsForImplicitlyImportedTypes() { Set types = typeMirrorSet( typeMirrorOf(java.lang.String.class), typeMirrorOf(javax.management.MBeanServer.class), // Same package, so no import. typeUtils.getPrimitiveType(TypeKind.INT), typeUtils.getPrimitiveType(TypeKind.BOOLEAN)); assertTypeImportsAndSpellings( types, "javax.management", ImmutableList.of(), ImmutableList.of("String", "MBeanServer", "int", "boolean")); } @Test public void testImportsForPlainTypes() { Set types = typeMirrorSet( typeUtils.getPrimitiveType(TypeKind.INT), typeMirrorOf(java.lang.String.class), typeMirrorOf(java.net.Proxy.class), typeMirrorOf(java.net.Proxy.Type.class), typeMirrorOf(java.util.regex.Pattern.class), typeMirrorOf(javax.management.MBeanServer.class)); assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of( "java.net.Proxy", "java.util.regex.Pattern", "javax.management.MBeanServer"), ImmutableList.of("int", "String", "Proxy", "Proxy.Type", "Pattern", "MBeanServer")); } @Test public void testImportsForComplicatedTypes() { TypeElement list = typeElementOf(java.util.List.class); TypeElement map = typeElementOf(java.util.Map.class); Set types = typeMirrorSet( typeUtils.getPrimitiveType(TypeKind.INT), typeMirrorOf(java.util.regex.Pattern.class), typeUtils.getDeclaredType( list, // List typeMirrorOf(java.util.Timer.class)), typeUtils.getDeclaredType( map, // Map typeUtils.getWildcardType(typeMirrorOf(java.util.Timer.class), null), typeUtils.getWildcardType(null, typeMirrorOf(java.math.BigInteger.class)))); // Timer is referenced twice but should obviously only be imported once. assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of( "java.math.BigInteger", "java.util.List", "java.util.Map", "java.util.Timer", "java.util.regex.Pattern"), ImmutableList.of( "int", "Pattern", "List", "Map")); } @Test public void testImportsForArrayTypes() { TypeElement list = typeElementOf(java.util.List.class); TypeElement set = typeElementOf(java.util.Set.class); Set types = typeMirrorSet( typeUtils.getArrayType(typeUtils.getPrimitiveType(TypeKind.INT)), typeUtils.getArrayType(typeMirrorOf(java.util.regex.Pattern.class)), typeUtils.getArrayType( // Set[] typeUtils.getDeclaredType( set, typeUtils.getArrayType(typeMirrorOf(java.util.regex.Matcher.class)))), typeUtils.getDeclaredType( list, // List typeUtils.getArrayType(typeMirrorOf(java.util.Timer.class)))); // Timer is referenced twice but should obviously only be imported once. assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of( "java.util.List", "java.util.Set", "java.util.Timer", "java.util.regex.Matcher", "java.util.regex.Pattern"), ImmutableList.of("int[]", "Pattern[]", "Set[]", "List")); } @Test public void testImportNestedType() { Set types = typeMirrorSet(typeMirrorOf(java.net.Proxy.Type.class)); assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of("java.net.Proxy"), ImmutableList.of("Proxy.Type")); } @Test public void testImportsForAmbiguousNames() { TypeMirror wildcard = typeUtils.getWildcardType(null, null); Set types = typeMirrorSet( typeUtils.getPrimitiveType(TypeKind.INT), typeMirrorOf(java.awt.List.class), typeMirrorOf(java.lang.String.class), typeUtils.getDeclaredType( // List typeElementOf(java.util.List.class), wildcard), typeUtils.getDeclaredType( // Map typeElementOf(java.util.Map.class), wildcard, wildcard)); assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of("java.util.Map"), ImmutableList.of("int", "java.awt.List", "String", "java.util.List", "Map")); } @Test public void testSimplifyJavaLangString() { Set types = typeMirrorSet(typeMirrorOf(java.lang.String.class)); assertTypeImportsAndSpellings(types, "foo.bar", ImmutableList.of(), ImmutableList.of("String")); } @Test public void testSimplifyJavaLangThreadState() { Set types = typeMirrorSet(typeMirrorOf(java.lang.Thread.State.class)); assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of(), ImmutableList.of("Thread.State")); } @Test public void testSimplifyJavaLangNamesake() { TypeMirror javaLangType = typeMirrorOf(java.lang.RuntimePermission.class); TypeMirror notJavaLangType = typeMirrorOf(com.google.auto.value.processor.testclasses.RuntimePermission.class); Set types = typeMirrorSet(javaLangType, notJavaLangType); assertTypeImportsAndSpellings( types, "foo.bar", ImmutableList.of(), ImmutableList.of(javaLangType.toString(), notJavaLangType.toString())); } @Test public void testSimplifyComplicatedTypes() { // This test constructs a set of types and feeds them to TypeEncoder. Then it verifies that // the resultant rewrites of those types are what we would expect. TypeElement list = typeElementOf(java.util.List.class); TypeElement map = typeElementOf(java.util.Map.class); TypeMirror string = typeMirrorOf(java.lang.String.class); TypeMirror integer = typeMirrorOf(java.lang.Integer.class); TypeMirror pattern = typeMirrorOf(java.util.regex.Pattern.class); TypeMirror timer = typeMirrorOf(java.util.Timer.class); TypeMirror bigInteger = typeMirrorOf(java.math.BigInteger.class); ImmutableMap typeMap = ImmutableMap.builder() .put(typeUtils.getPrimitiveType(TypeKind.INT), "int") .put(typeUtils.getArrayType(typeUtils.getPrimitiveType(TypeKind.BYTE)), "byte[]") .put(pattern, "Pattern") .put(typeUtils.getArrayType(pattern), "Pattern[]") .put(typeUtils.getArrayType(typeUtils.getArrayType(pattern)), "Pattern[][]") .put(typeUtils.getDeclaredType(list, typeUtils.getWildcardType(null, null)), "List") .put(typeUtils.getDeclaredType(list, timer), "List") .put(typeUtils.getDeclaredType(map, string, integer), "Map") .put( typeUtils.getDeclaredType( map, typeUtils.getWildcardType(timer, null), typeUtils.getWildcardType(null, bigInteger)), "Map") .build(); assertTypeImportsAndSpellings( typeMap.keySet(), "foo.bar", ImmutableList.of( "java.math.BigInteger", "java.util.List", "java.util.Map", "java.util.Timer", "java.util.regex.Pattern"), ImmutableList.copyOf(typeMap.values())); } @Test public void testSimplifyMultipleBounds() { TypeElement multipleBoundsElement = typeElementOf(MultipleBounds.class); TypeMirror multipleBoundsMirror = multipleBoundsElement.asType(); String text = "`import`\n"; text += "{" + TypeEncoder.encode(multipleBoundsMirror) + "}"; text += "{" + TypeEncoder.formalTypeParametersString(multipleBoundsElement) + "}"; String myPackage = getClass().getPackage().getName(); String decoded = TypeEncoder.decode(text, elementUtils, typeUtils, myPackage, baseWithoutContainedTypes()); String expected = "import java.util.List;\n\n" + "{TypeEncoderTest.MultipleBounds}" + "{ & Comparable, V>}"; assertThat(decoded).isEqualTo(expected); } @SuppressWarnings("ClassCanBeStatic") static class Outer { class InnerWithoutTypeParam {} class Middle { class InnerWithTypeParam {} } } @Test public void testOuterParameterizedInnerNot() { TypeElement outerElement = typeElementOf(Outer.class); DeclaredType doubleMirror = typeMirrorOf(Double.class); DeclaredType outerOfDoubleMirror = typeUtils.getDeclaredType(outerElement, doubleMirror); TypeElement innerWithoutTypeParamElement = typeElementOf(Outer.InnerWithoutTypeParam.class); DeclaredType parameterizedInnerWithoutTypeParam = typeUtils.getDeclaredType(outerOfDoubleMirror, innerWithoutTypeParamElement); String encoded = TypeEncoder.encode(parameterizedInnerWithoutTypeParam); String myPackage = getClass().getPackage().getName(); String decoded = TypeEncoder.decode( encoded, elementUtils, typeUtils, myPackage, baseWithoutContainedTypes()); String expected = "TypeEncoderTest.Outer.InnerWithoutTypeParam"; assertThat(decoded).isEqualTo(expected); } @Test public void testOuterParameterizedInnerAlso() { TypeElement outerElement = typeElementOf(Outer.class); DeclaredType doubleMirror = typeMirrorOf(Double.class); DeclaredType outerOfDoubleMirror = typeUtils.getDeclaredType(outerElement, doubleMirror); TypeElement middleElement = typeElementOf(Outer.Middle.class); DeclaredType stringMirror = typeMirrorOf(String.class); DeclaredType middleOfStringMirror = typeUtils.getDeclaredType(outerOfDoubleMirror, middleElement, stringMirror); TypeElement innerWithTypeParamElement = typeElementOf(Outer.Middle.InnerWithTypeParam.class); DeclaredType integerMirror = typeMirrorOf(Integer.class); DeclaredType parameterizedInnerWithTypeParam = typeUtils.getDeclaredType(middleOfStringMirror, innerWithTypeParamElement, integerMirror); String encoded = TypeEncoder.encode(parameterizedInnerWithTypeParam); String myPackage = getClass().getPackage().getName(); String decoded = TypeEncoder.decode( encoded, elementUtils, typeUtils, myPackage, baseWithoutContainedTypes()); String expected = "TypeEncoderTest.Outer.Middle.InnerWithTypeParam"; assertThat(decoded).isEqualTo(expected); } private static Set typeMirrorSet(TypeMirror... typeMirrors) { Set set = new TypeMirrorSet(); for (TypeMirror typeMirror : typeMirrors) { assertThat(set.add(typeMirror)).isTrue(); } return set; } private TypeElement typeElementOf(Class c) { return elementUtils.getTypeElement(c.getCanonicalName()); } private DeclaredType typeMirrorOf(Class c) { return MoreTypes.asDeclared(typeElementOf(c).asType()); } /** * Returns a "base type" for TypeSimplifier that does not contain any nested types. The point * being that every {@code TypeSimplifier} has a base type that the class being generated is going * to extend, and if that class has nested types they will be in scope, and therefore a possible * source of ambiguity. */ private TypeMirror baseWithoutContainedTypes() { return typeMirrorOf(Object.class); } // This test checks that we correctly throw MissingTypeException if there is an ErrorType anywhere // inside a type we are asked to simplify. There's no way to get an ErrorType from typeUtils or // elementUtils, so we need to fire up the compiler with an erroneous source file and use an // annotation processor to capture the resulting ErrorType. Then we can run tests within that // annotation processor, and propagate any failures out of this test. @Test public void testErrorTypes() { JavaFileObject source = JavaFileObjects.forSourceString( "ExtendsUndefinedType", "class ExtendsUndefinedType extends UndefinedParent {}"); Compilation compilation = javac().withProcessors(new ErrorTestProcessor()).compile(source); assertThat(compilation).failed(); assertThat(compilation).hadErrorContaining("UndefinedParent"); assertThat(compilation).hadErrorCount(1); } @SupportedAnnotationTypes("*") private static class ErrorTestProcessor extends AbstractProcessor { Types typeUtils; Elements elementUtils; @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { if (roundEnv.processingOver()) { typeUtils = processingEnv.getTypeUtils(); elementUtils = processingEnv.getElementUtils(); test(); } return false; } private void test() { TypeElement extendsUndefinedType = elementUtils.getTypeElement("ExtendsUndefinedType"); ErrorType errorType = (ErrorType) extendsUndefinedType.getSuperclass(); TypeElement list = elementUtils.getTypeElement("java.util.List"); TypeMirror listOfError = typeUtils.getDeclaredType(list, errorType); TypeMirror queryExtendsError = typeUtils.getWildcardType(errorType, null); TypeMirror listOfQueryExtendsError = typeUtils.getDeclaredType(list, queryExtendsError); TypeMirror querySuperError = typeUtils.getWildcardType(null, errorType); TypeMirror listOfQuerySuperError = typeUtils.getDeclaredType(list, querySuperError); TypeMirror arrayOfError = typeUtils.getArrayType(errorType); testErrorType(errorType); testErrorType(listOfError); testErrorType(listOfQueryExtendsError); testErrorType(listOfQuerySuperError); testErrorType(arrayOfError); } @SuppressWarnings("MissingFail") // error message gets converted into assertion failure private void testErrorType(TypeMirror typeWithError) { try { TypeEncoder.encode(typeWithError); processingEnv .getMessager() .printMessage(Diagnostic.Kind.ERROR, "Expected exception for type: " + typeWithError); } catch (MissingTypeException expected) { } } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } } } TypeSimplifierTest.java000066400000000000000000000255201365703632600337670ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/* * Copyright 2012 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import com.google.testing.compile.CompilationRule; import java.util.List; import java.util.Map; import java.util.Set; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; 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.TypeMirror; import javax.lang.model.type.TypeVariable; import javax.lang.model.util.ElementFilter; 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 org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link TypeSimplifier}. * * @author emcmanus@google.com (Éamonn McManus) */ @RunWith(JUnit4.class) public class TypeSimplifierTest { @Rule public final CompilationRule compilationRule = new CompilationRule(); private Types typeUtils; private Elements elementUtils; @Before public void setUp() { typeUtils = compilationRule.getTypes(); elementUtils = compilationRule.getElements(); } private static class Erasure { int intNo; boolean booleanNo; int[] intArrayNo; String stringNo; String[] stringArrayNo; @SuppressWarnings("rawtypes") List rawListNo; List listOfQueryNo; List listOfQueryExtendsObjectNo; Map mapQueryToQueryNo; List listOfStringYes; List listOfQueryExtendsStringYes; List listOfQuerySuperStringYes; List listOfTypeVarYes; List listOfQueryExtendsTypeVarYes; List listOfQuerySuperTypeVarYes; } private abstract static class Wildcards { abstract Map one(); abstract Map two(); } /** * This test shows why we need to have TypeMirrorSet. The mirror of java.lang.Object obtained from * {@link Elements#getTypeElement Elements.getTypeElement("java.lang.Object")} does not compare * equal to the mirror of the return type of Object.clone(), even though that is also * java.lang.Object and {@link Types#isSameType} considers them the same. * *

      There's no requirement that this test must pass and if it starts failing or doesn't work in * another test environment then we can delete it. The specification of {@link TypeMirror#equals} * explicitly says that it cannot be used for type equality, so even if this particular case stops * being a problem (which means this test would fail), we would need TypeMirrorSet for complete * correctness. */ @Test public void testQuirkyTypeMirrors() { TypeMirror objectMirror = objectMirror(); TypeMirror cloneReturnTypeMirror = cloneReturnTypeMirror(); assertThat(objectMirror).isNotEqualTo(cloneReturnTypeMirror); assertThat(typeUtils.isSameType(objectMirror, cloneReturnTypeMirror)).isTrue(); } @Test @SuppressWarnings("TypeEquals") // We want to test the equals method invocation on TypeMirror. public void testTypeMirrorSet() { // Test the TypeMirrorSet methods. Resist the temptation to rewrite these in terms of // Truth operations! For example, don't change assertThat(set.size()).isEqualTo(0) into // assertThat(set).isEmpty(), because then we wouldn't be testing size(). TypeMirror objectMirror = objectMirror(); TypeMirror otherObjectMirror = cloneReturnTypeMirror(); Set set = new TypeMirrorSet(); assertThat(set.size()).isEqualTo(0); assertThat(set.isEmpty()).isTrue(); boolean added = set.add(objectMirror); assertThat(added).isTrue(); assertThat(set.size()).isEqualTo(1); Set otherSet = typeMirrorSet(otherObjectMirror); assertThat(otherSet).isEqualTo(set); assertThat(set).isEqualTo(otherSet); assertThat(otherSet.hashCode()).isEqualTo(set.hashCode()); assertThat(set.add(otherObjectMirror)).isFalse(); assertThat(set.contains(otherObjectMirror)).isTrue(); assertThat(set.contains(null)).isFalse(); assertThat(set.contains((Object) "foo")).isFalse(); assertThat(set.remove(null)).isFalse(); assertThat(set.remove((Object) "foo")).isFalse(); TypeElement list = typeElementOf(java.util.List.class); TypeMirror listOfObjectMirror = typeUtils.getDeclaredType(list, objectMirror); TypeMirror listOfOtherObjectMirror = typeUtils.getDeclaredType(list, otherObjectMirror); assertThat(listOfObjectMirror.equals(listOfOtherObjectMirror)).isFalse(); assertThat(typeUtils.isSameType(listOfObjectMirror, listOfOtherObjectMirror)).isTrue(); added = set.add(listOfObjectMirror); assertThat(added).isTrue(); assertThat(set.size()).isEqualTo(2); assertThat(set.add(listOfOtherObjectMirror)).isFalse(); assertThat(set.contains(listOfOtherObjectMirror)).isTrue(); boolean removed = set.remove(listOfOtherObjectMirror); assertThat(removed).isTrue(); assertThat(set.contains(listOfObjectMirror)).isFalse(); set.removeAll(otherSet); assertThat(set.isEmpty()).isTrue(); } @Test public void testTypeMirrorSetWildcardCapture() { // TODO(emcmanus): this test should really be in MoreTypesTest. // This test checks the assumption made by MoreTypes that you can find the // upper bounds of a TypeVariable tv like this: // TypeParameterElement tpe = (TypeParameterElement) tv.asElement(); // List bounds = tpe.getBounds(); // There was some doubt as to whether this would be true in exotic cases involving // wildcard capture, but apparently it is. // The methods one and two here have identical signatures: // abstract Map name(); // Their return types should be considered equal by TypeMirrorSet. The capture of // each return type is different from the original return type, but the two captures // should compare equal to each other. We also add various other types like ? super U // to the set to ensure that the implied calls to the equals and hashCode visitors // don't cause a ClassCastException for TypeParameterElement. TypeElement wildcardsElement = typeElementOf(Wildcards.class); List methods = ElementFilter.methodsIn(wildcardsElement.getEnclosedElements()); assertThat(methods).hasSize(2); ExecutableElement one = methods.get(0); ExecutableElement two = methods.get(1); assertThat(one.getSimpleName().toString()).isEqualTo("one"); assertThat(two.getSimpleName().toString()).isEqualTo("two"); TypeMirrorSet typeMirrorSet = new TypeMirrorSet(); assertThat(typeMirrorSet.add(one.getReturnType())).isTrue(); assertThat(typeMirrorSet.add(two.getReturnType())).isFalse(); DeclaredType captureOne = (DeclaredType) typeUtils.capture(one.getReturnType()); assertThat(typeMirrorSet.add(captureOne)).isTrue(); DeclaredType captureTwo = (DeclaredType) typeUtils.capture(two.getReturnType()); assertThat(typeMirrorSet.add(captureTwo)).isFalse(); // Reminder: captureOne is Map TypeVariable extendsT = (TypeVariable) captureOne.getTypeArguments().get(0); assertThat(typeMirrorSet.add(extendsT)).isTrue(); assertThat(typeMirrorSet.add(extendsT.getLowerBound())).isTrue(); // NoType for (TypeMirror bound : ((TypeParameterElement) extendsT.asElement()).getBounds()) { assertThat(typeMirrorSet.add(bound)).isTrue(); } TypeVariable superU = (TypeVariable) captureOne.getTypeArguments().get(1); assertThat(typeMirrorSet.add(superU)).isTrue(); assertThat(typeMirrorSet.add(superU.getLowerBound())).isTrue(); } @Test public void testPackageNameOfString() { assertThat(TypeSimplifier.packageNameOf(typeElementOf(java.lang.String.class))) .isEqualTo("java.lang"); } @Test public void testPackageNameOfMapEntry() { assertThat(TypeSimplifier.packageNameOf(typeElementOf(java.util.Map.Entry.class))) .isEqualTo("java.util"); } // Test TypeSimplifier.isCastingUnchecked. We do this by examining the fields of the Erasure // class. A field whose name ends with Yes has a type where // isCastingUnchecked should return true, and one whose name ends with No has a type where // isCastingUnchecked should return false. @Test public void testIsCastingUnchecked() { TypeElement erasureClass = typeElementOf(Erasure.class); List fields = ElementFilter.fieldsIn(erasureClass.getEnclosedElements()); assertThat(fields).isNotEmpty(); for (VariableElement field : fields) { String fieldName = field.getSimpleName().toString(); boolean expectUnchecked; if (fieldName.endsWith("Yes")) { expectUnchecked = true; } else if (fieldName.endsWith("No")) { expectUnchecked = false; } else { throw new AssertionError("Fields in Erasure class must end with Yes or No: " + fieldName); } TypeMirror fieldType = field.asType(); boolean actualUnchecked = TypeSimplifier.isCastingUnchecked(fieldType); assertWithMessage("Unchecked-cast status for " + fieldType) .that(actualUnchecked) .isEqualTo(expectUnchecked); } } private static Set typeMirrorSet(TypeMirror... typeMirrors) { Set set = new TypeMirrorSet(); for (TypeMirror typeMirror : typeMirrors) { assertThat(set.add(typeMirror)).isTrue(); } return set; } private TypeMirror objectMirror() { return typeMirrorOf(Object.class); } private TypeMirror cloneReturnTypeMirror() { TypeElement object = typeElementOf(Object.class); ExecutableElement clone = null; for (Element element : object.getEnclosedElements()) { if (element.getSimpleName().contentEquals("clone")) { clone = (ExecutableElement) element; break; } } return clone.getReturnType(); } private TypeElement typeElementOf(Class c) { return elementUtils.getTypeElement(c.getCanonicalName()); } private TypeMirror typeMirrorOf(Class c) { return typeElementOf(c).asType(); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java000066400000000000000000000213341365703632600336520ustar00rootroot00000000000000/* * Copyright 2019 Google LLC * * 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.google.auto.value.processor; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableMap; import com.google.common.truth.Expect; import com.google.testing.compile.CompilationRule; import java.util.List; import java.util.Map; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @RunWith(JUnit4.class) public class TypeVariablesTest { @ClassRule public static final CompilationRule compilationRule = new CompilationRule(); @Rule public final Expect expect = Expect.create(); private static Elements elementUtils; private static Types typeUtils; @BeforeClass public static void setUpClass() { elementUtils = compilationRule.getElements(); typeUtils = compilationRule.getTypes(); } abstract static class Source1 { abstract String getFoo(); } abstract static class Target1 {} @Test public void noTypeParameters() { TypeElement source1 = elementUtils.getTypeElement(Source1.class.getCanonicalName()); TypeElement target1 = elementUtils.getTypeElement(Target1.class.getCanonicalName()); List sourceMethods = ElementFilter.methodsIn(source1.getEnclosedElements()); Map types = TypeVariables.rewriteReturnTypes(elementUtils, typeUtils, sourceMethods, source1, target1); assertThat(types).containsExactly(sourceMethods.get(0), sourceMethods.get(0).getReturnType()); } abstract static class Source2 { abstract List getFoo(); } abstract static class Target2 { abstract void setFoo(List list); } @Test public void simpleTypeParameter() { TypeElement source2 = elementUtils.getTypeElement(Source2.class.getCanonicalName()); TypeElement target2 = elementUtils.getTypeElement(Target2.class.getCanonicalName()); List sourceMethods = ElementFilter.methodsIn(source2.getEnclosedElements()); Map types = TypeVariables.rewriteReturnTypes(elementUtils, typeUtils, sourceMethods, source2, target2); List targetMethods = ElementFilter.methodsIn(target2.getEnclosedElements()); TypeMirror setFooParameter = targetMethods.get(0).getParameters().get(0).asType(); ExecutableElement getFoo = sourceMethods.get(0); TypeMirror originalGetFooReturn = getFoo.getReturnType(); TypeMirror rewrittenGetFooReturn = types.get(getFoo); assertThat(typeUtils.isAssignable(setFooParameter, originalGetFooReturn)).isFalse(); assertThat(typeUtils.isAssignable(setFooParameter, rewrittenGetFooReturn)).isTrue(); } abstract static class Source3, U> { abstract Map getFoo(); } abstract static class Target3, U> { abstract void setFoo(Map list); } @Test public void hairyTypeParameters() { TypeElement source3 = elementUtils.getTypeElement(Source3.class.getCanonicalName()); TypeElement target3 = elementUtils.getTypeElement(Target3.class.getCanonicalName()); List sourceMethods = ElementFilter.methodsIn(source3.getEnclosedElements()); Map types = TypeVariables.rewriteReturnTypes(elementUtils, typeUtils, sourceMethods, source3, target3); List targetMethods = ElementFilter.methodsIn(target3.getEnclosedElements()); TypeMirror setFooParameter = targetMethods.get(0).getParameters().get(0).asType(); ExecutableElement getFoo = sourceMethods.get(0); TypeMirror originalGetFooReturn = getFoo.getReturnType(); TypeMirror rewrittenGetFooReturn = types.get(getFoo); assertThat(typeUtils.isAssignable(setFooParameter, originalGetFooReturn)).isFalse(); assertThat(typeUtils.isAssignable(setFooParameter, rewrittenGetFooReturn)).isTrue(); } abstract static class Outer { abstract Map getFoo(); abstract List getBar(); abstract static class Inner { abstract void setFoo(Map foo); abstract void setBar(List bar); } } @Test public void nestedClasses() { TypeElement outer = elementUtils.getTypeElement(Outer.class.getCanonicalName()); TypeElement inner = elementUtils.getTypeElement(Outer.Inner.class.getCanonicalName()); List outerMethods = ElementFilter.methodsIn(outer.getEnclosedElements()); Map types = TypeVariables.rewriteReturnTypes(elementUtils, typeUtils, outerMethods, outer, inner); List innerMethods = ElementFilter.methodsIn(inner.getEnclosedElements()); ExecutableElement getFoo = methodNamed(outerMethods, "getFoo"); ExecutableElement getBar = methodNamed(outerMethods, "getBar"); ExecutableElement setFoo = methodNamed(innerMethods, "setFoo"); ExecutableElement setBar = methodNamed(innerMethods, "setBar"); TypeMirror setFooParameter = setFoo.getParameters().get(0).asType(); TypeMirror originalGetFooReturn = getFoo.getReturnType(); TypeMirror rewrittenGetFooReturn = types.get(getFoo); assertThat(typeUtils.isAssignable(setFooParameter, originalGetFooReturn)).isFalse(); assertThat(typeUtils.isAssignable(setFooParameter, rewrittenGetFooReturn)).isTrue(); TypeMirror setBarParameter = setBar.getParameters().get(0).asType(); TypeMirror originalGetBarReturn = getBar.getReturnType(); TypeMirror rewrittenGetBarReturn = types.get(getBar); assertThat(typeUtils.isAssignable(setBarParameter, originalGetBarReturn)).isFalse(); assertThat(typeUtils.isAssignable(setBarParameter, rewrittenGetBarReturn)).isTrue(); } @Test public void canAssignStaticMethodResult() { TypeElement immutableMap = elementUtils.getTypeElement(ImmutableMap.class.getCanonicalName()); TypeElement string = elementUtils.getTypeElement(String.class.getCanonicalName()); TypeElement integer = elementUtils.getTypeElement(Integer.class.getCanonicalName()); TypeElement number = elementUtils.getTypeElement(Number.class.getCanonicalName()); TypeMirror immutableMapStringNumber = typeUtils.getDeclaredType(immutableMap, string.asType(), number.asType()); TypeMirror immutableMapStringInteger = typeUtils.getDeclaredType(immutableMap, string.asType(), integer.asType()); TypeElement map = elementUtils.getTypeElement(Map.class.getCanonicalName()); TypeMirror erasedMap = typeUtils.erasure(map.asType()); // If the target type is ImmutableMap then we should be able to use // static ImmutableMap ImmutableMap.copyOf(Map) // with a parameter of type ImmutableMap. List immutableMapMethods = ElementFilter.methodsIn(immutableMap.getEnclosedElements()); ExecutableElement copyOf = methodNamed(immutableMapMethods, "copyOf", erasedMap); expect.that( TypeVariables.canAssignStaticMethodResult( copyOf, immutableMapStringInteger, immutableMapStringNumber, typeUtils)) .isTrue(); expect.that( TypeVariables.canAssignStaticMethodResult( copyOf, immutableMapStringNumber, immutableMapStringInteger, typeUtils)) .isFalse(); } private static ExecutableElement methodNamed(List methods, String name) { return methods.stream().filter(m -> m.getSimpleName().contentEquals(name)).findFirst().get(); } private static ExecutableElement methodNamed( List methods, String name, TypeMirror erasedParameterType) { return methods.stream() .filter(m -> m.getSimpleName().contentEquals(name)) .filter(m -> m.getParameters().size() == 1) .filter(m -> typeUtils.isSameType( erasedParameterType, typeUtils.erasure(m.getParameters().get(0).asType()))) .findFirst() .get(); } } auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/testclasses/000077500000000000000000000000001365703632600317275ustar00rootroot00000000000000RuntimePermission.java000066400000000000000000000015421365703632600362110ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/src/test/java/com/google/auto/value/processor/testclasses/* * Copyright 2016 Google LLC * * 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.google.auto.value.processor.testclasses; /** * This class just exists to test import behaviour when referencing a class which has the same name * as a class in java.lang. * * @author emcmanus@google.com (Éamonn McManus) */ public class RuntimePermission {} auto-auto-service-1.0-rc7/value/userguide/000077500000000000000000000000001365703632600205425ustar00rootroot00000000000000auto-auto-service-1.0-rc7/value/userguide/builders-howto.md000066400000000000000000000507471365703632600240500ustar00rootroot00000000000000# How do I... (Builder edition) This page answers common how-to questions that may come up when using AutoValue **with the builder option**. You should read and understand [AutoValue with builders](builders.md) first. If you are not using a builder, see [Introduction](index.md) and [How do I...](howto.md) instead. ## Contents How do I... * ... [use (or not use) `set` **prefixes**?](#beans) * ... [use different **names** besides `builder()`/`Builder`/`build()`?](#build_names) * ... [specify a **default** value for a property?](#default) * ... [initialize a builder to the same property values as an **existing** value instance](#to_builder) * ... [include `with-` methods on my value class for creating slightly **altered** instances?](#withers) * ... [**validate** property values?](#validate) * ... [**normalize** (modify) a property value at `build` time?](#normalize) * ... [expose **both** a builder and a factory method?](#both) * ... [handle `Optional` properties?](#optional) * ... [use a **collection**-valued property?](#collection) * ... [let my builder **accumulate** values for a collection-valued property (not require them all at once)?](#accumulate) * ... [accumulate values for a collection-valued property, without **"breaking the chain"**?](#add) * ... [offer **both** accumulation and set-at-once methods for the same collection-valued property?](#collection_both) * ... [access nested builders while building?](#nested_builders) ## ... use (or not use) `set` prefixes? Just as you can choose whether to use JavaBeans-style names for property getters (`getFoo()` or just `foo()`) in your value class, you have the same choice for setters in builders too (`setFoo(value)` or just `foo(value)`). As with getters, you must use these prefixes consistently or not at all. Using `get`/`is` prefixes for getters and using the `set` prefix for setters are independent choices. For example, it is fine to use the `set` prefixes on all your builder methods, but omit the `get`/`is` prefixes from all your accessors. Here is the `Animal` example using `get` prefixes but not `set` prefixes: ```java @AutoValue abstract class Animal { abstract String getName(); abstract int getNumberOfLegs(); static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder name(String value); abstract Builder numberOfLegs(int value); abstract Animal build(); } } ``` ## ... use different names besides `builder()`/`Builder`/`build()`? Use whichever names you like; AutoValue doesn't actually care. (We would gently recommend these names as conventional.) ## ... specify a default value for a property? What should happen when a caller does not supply a value for a property before calling `build()`? If the property in question is [nullable](howto.md#nullable), it will simply default to `null` as you would expect. And if it is [Optional](#optional) it will default to an empty `Optional` as you might also expect. But if it isn't either of those things (including if it is a primitive-valued property, which *can't* be null), then `build()` will throw an unchecked exception. But this presents a problem, since one of the main *advantages* of a builder in the first place is that callers can specify only the properties they care about! The solution is to provide a default value for such properties. Fortunately this is easy: just set it on the newly-constructed builder instance before returning it from the `builder()` method. Here is the `Animal` example with the default number of legs being 4: ```java @AutoValue abstract class Animal { abstract String name(); abstract int numberOfLegs(); static Builder builder() { return new AutoValue_Animal.Builder() .setNumberOfLegs(4); } @AutoValue.Builder abstract static class Builder { abstract Builder setName(String value); abstract Builder setNumberOfLegs(int value); abstract Animal build(); } } ``` Occasionally you may want to supply a default value, but only if the property is not set explicitly. This is covered in the section on [normalization](#normalize). ## ... initialize a builder to the same property values as an existing value instance Suppose your caller has an existing instance of your value class, and wants to change only one or two of its properties. Of course, it's immutable, but it would be convenient if they could easily get a `Builder` instance representing the same property values, which they could then modify and use to create a new value instance. To give them this ability, just add an abstract `toBuilder` method, returning your abstract builder type, to your value class. AutoValue will implement it. ```java public abstract Builder toBuilder(); ``` ## ... include `with-` methods on my value class for creating slightly altered instances? This is a somewhat common pattern among immutable classes. You can't have setters, but you can have methods that act similarly to setters by returning a new immutable instance that has one property changed. If you're already using the builder option, you can add these methods by hand: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public static Builder builder() { return new AutoValue_Animal.Builder(); } abstract Builder toBuilder(); public Animal withName(String name) { return toBuilder().setName(name).build(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); public abstract Animal build(); } } ``` Note that it's your free choice what to make public (`toBuilder`, `withName`, neither, or both). ## ... validate property values? Validating properties is a little less straightforward than it is in the [non-builder case](howto.md#validate). What you need to do is *split* your "build" method into two methods: * the non-visible, abstract method that AutoValue implements * and the visible, *concrete* method you provide, which calls the generated method and performs validation. We recommend naming these methods `autoBuild` and `build`, but any names will work. It ends up looking like this: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); abstract Animal autoBuild(); // not public public Animal build() { Animal animal = autoBuild(); Preconditions.checkState(animal.numberOfLegs() >= 0, "Negative legs"); return animal; } } } ``` ## ... normalize (modify) a property value at `build` time? Suppose you want to convert the animal's name to lower case. You'll need to add a *getter* to your builder, as shown: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); abstract String name(); // must match method name in Animal abstract Animal autoBuild(); // not public public Animal build() { setName(name().toLowerCase()); return autoBuild(); } } } ``` The getter in your builder must have the same signature as the abstract property accessor method in the value class. It will return the value that has been set on the `Builder`. If no value has been set for a non-[nullable](howto.md#nullable) property, `IllegalStateException` is thrown. Getters should generally only be used within the `Builder` as shown, so they are not public. As an alternative to returning the same type as the property accessor method, the builder getter can return an Optional wrapping of that type. This can be used if you want to supply a default, but only if the property has not been set. (The [usual way](#default) of supplying defaults means that the property always appears to have been set.) For example, suppose you wanted the default name of your Animal to be something like "4-legged creature", where 4 is the `numberOfLegs()` property. You might write this: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); abstract Optional name(); abstract int numberOfLegs(); abstract Animal autoBuild(); // not public public Animal build() { if (!name().isPresent()) { setName(numberOfLegs() + "-legged creature"); } return autoBuild(); } } } ``` Notice that this will throw `IllegalStateException` if the `numberOfLegs` property hasn't been set either. The Optional wrapping can be any of the Optional types mentioned in the [section](#optional) on `Optional` properties. If your property has type `int` it can be wrapped as either `Optional` or `OptionalInt`, and likewise for `long` and `double`. ## ... expose *both* a builder *and* a factory method? If you use the builder option, AutoValue will not generate a visible constructor for the generated concrete value class. If it's important to offer your caller the choice of a factory method as well as the builder, then your factory method implementation will have to invoke the builder itself. ## ... handle `Optional` properties? Properties of type `Optional` benefit from special treatment. If you have a property of type `Optional`, say, then it will default to an empty `Optional` without needing to [specify](#default) a default explicitly. And, instead of or as well as the normal `setFoo(Optional)` method, you can have `setFoo(String)`. Then `setFoo(s)` is equivalent to `setFoo(Optional.of(s))`. Here, `Optional` means either [`java.util.Optional`] from Java (Java 8 or later), or [`com.google.common.base.Optional`] from Guava. Java 8 also introduced related classes in `java.util` called [`OptionalInt`], [`OptionalLong`], and [`OptionalDouble`]. You can use those in the same way. For example a property of type `OptionalInt` will default to `OptionalInt.empty()` and you can set it with either `setFoo(OptionalInt)` or `setFoo(int)`. ```java @AutoValue public abstract class Animal { public abstract Optional name(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { // You can have either or both of these two methods: public abstract Builder setName(Optional value); public abstract Builder setName(String value); public abstract Animal build(); } } ``` [`java.util.Optional`]: https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html [`com.google.common.base.Optional`]: http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/base/Optional.html [`OptionalDouble`]: https://docs.oracle.com/javase/8/docs/api/java/util/OptionalDouble.html [`OptionalInt`]: https://docs.oracle.com/javase/8/docs/api/java/util/OptionalInt.html [`OptionalLong`]: https://docs.oracle.com/javase/8/docs/api/java/util/OptionalLong.html ## ... use a collection-valued property? Value objects should be immutable, so if a property of one is a collection then that collection should be immutable too. We recommend using Guava's [immutable collections] to make that explicit. AutoValue's builder support includes a few special arrangements to make this more convenient. In the examples here we use `ImmutableSet`, but the same principles apply to all of Guava's immutable collection types, like `ImmutableList`, `ImmutableMultimap`, and so on. We recommend using the immutable type (like `ImmutableSet`) as your actual property type. However, it can be a pain for callers to always have to construct `ImmutableSet` instances to pass into your builder. So AutoValue allows your builder method to accept an argument of any type that `ImmutableSet.copyOf` accepts. If our `Animal` acquires an `ImmutableSet` that is the countries it lives in, that can be set from a `Set` or a `Collection` or an `Iterable` or a `String[]` or any other compatible type. You can even offer multiple choices, as in this example: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public abstract ImmutableSet countries(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); public abstract Builder setCountries(Set value); public abstract Builder setCountries(String... value); public abstract Animal build(); } } ``` [immutable collections]: https://github.com/google/guava/wiki/ImmutableCollectionsExplained ### ... let my builder *accumulate* values for a collection-valued property (not require them all at once)? Instead of defining a setter for an immutable collection `foos`, you can define a method `foosBuilder()` that returns the associated builder type for that collection. In this example, we have an `ImmutableSet` which can be built using the `countriesBuilder()` method: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public abstract ImmutableSet countries(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); public abstract ImmutableSet.Builder countriesBuilder(); public abstract Animal build(); } } ``` The name of this method must be exactly the property name (`countries` here) followed by the string `Builder`. Even if the properties follow the `getCountries()` convention, the builder method must be `countriesBuilder()` and not `getCountriesBuilder()`. You may notice a small problem with this example: the caller can no longer create their instance in a single chained statement: ```java // This DOES NOT work! Animal dog = Animal.builder() .setName("dog") .setNumberOfLegs(4) .countriesBuilder() .add("Guam") .add("Laos") .build(); ``` Instead they are forced to hold the builder itself in a temporary variable: ```java // This DOES work... but we have to "break the chain"! Animal.Builder builder = Animal.builder() .setName("dog") .setNumberOfLegs(4); builder.countriesBuilder() .add("Guam") .add("Laos"); Animal dog = builder.build(); ``` One solution for this problem is just below. ### ... accumulate values for a collection-valued property, without "breaking the chain"? Another option is to keep `countriesBuilder()` itself non-public, and only use it to implement a public `addCountry` method: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract int numberOfLegs(); public abstract ImmutableSet countries(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String value); public abstract Builder setNumberOfLegs(int value); abstract ImmutableSet.Builder countriesBuilder(); public Builder addCountry(String value) { countriesBuilder().add(value); return this; } public abstract Animal build(); } } ``` Now the caller can do this: ```java // This DOES work! Animal dog = Animal.builder() .setName("dog") .setNumberOfLegs(4) .addCountry("Guam") .addCountry("Laos") // however many times needed .build(); ``` ### ... offer both accumulation and set-at-once methods for the same collection-valued property? Yes, you can provide both methods, letting your caller choose the style they prefer. The same caller can mix the two styles only in limited ways; once `foosBuilder` has been called, any subsequent call to `setFoos` will throw an unchecked exception. On the other hand, calling `setFoos` first is okay; a later call to `foosBuilder` will return a builder already populated with the previously-supplied elements. ## ... access nested builders while building? Often a property of an `@AutoValue` class is itself an immutable class, perhaps another `@AutoValue`. In such cases your builder can expose a builder for that nested class. This is very similar to exposing a builder for a collection property, as described [earlier](#accumulate). Suppose the `Animal` class has a property of type `Species`: ```java @AutoValue public abstract class Animal { public abstract String name(); public abstract Species species(); public static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setName(String name); public abstract Species.Builder speciesBuilder(); public abstract Animal build(); } } @AutoValue public abstract class Species { public abstract String genus(); public abstract String epithet(); public static Builder builder() { return new AutoValue_Species.Builder(); } @AutoValue.Builder public abstract static class Builder { public abstract Builder setGenus(String genus); public abstract Builder setEpithet(String epithet); public abstract Species build(); } } ``` Now you can access the builder of the nested `Species` while you are building the `Animal`: ```java Animal.Builder catBuilder = Animal.builder() .setName("cat"); catBuilder.speciesBuilder() .setGenus("Felis") .setEpithet("catus"); Animal cat = catBuilder.build(); ``` Although the nested class in the example (`Species`) is also an `@AutoValue` class, it does not have to be. For example, it could be a [protobuf]. The requirements are: * The nested class must have a way to make a new builder. This can be `new Species.Builder()`, or `Species.builder()`, or `Species.newBuilder()`. * There must be a way to build an instance from the builder: `Species.Builder` must have a method `Species build()`. * If there is a need to convert `Species` back into its builder, then `Species` must have a method `Species.Builder toBuilder()`. In the example, if `Animal` has an abstract [`toBuilder()`](#to_builder) method then `Species` must also have a `toBuilder()` method. That also applies if there is an abstract `setSpecies` method in addition to the `speciesBuilder` method. As an alternative to having a method `Species.Builder toBuilder()` in `Species`, `Species.Builder` can have a method called `addAll` or `putAll` that accepts an argument of type `Species`. This is how AutoValue handles `ImmutableSet` for example. `ImmutableSet` does not have a `toBuilder()` method, but `ImmutableSet.Builder` does have an `addAll` method that accepts an `ImmutableSet`. So given `ImmutableSet strings`, we can achieve the effect of `strings.toBuilder()` by doing: ``` ImmutableSet.Builder builder = ImmutableSet.builder(); builder.addAll(strings); ``` There are no requirements on the name of the builder class. Instead of `Species.Builder`, it could be `Species.Factory` or `SpeciesBuilder`. If `speciesBuilder()` is never called then the final `species()` property will be set as if by `speciesBuilder().build()`. In the example, that would result in an exception because the required properties of `Species` have not been set. [protobuf]: https://developers.google.com/protocol-buffers/docs/reference/java-generated#builders auto-auto-service-1.0-rc7/value/userguide/builders.md000066400000000000000000000076471365703632600227130ustar00rootroot00000000000000# AutoValue with Builders The [introduction](index.md) of this User Guide covers the basic usage of AutoValue using a static factory method as your public creation API. But in many circumstances (such as those laid out in *Effective Java, 2nd Edition* Item 2), you may prefer to let your callers use a *builder* instead. Fortunately, AutoValue can generate builder classes too! This page explains how. Note that we recommend reading and understanding the basic usage shown in the [introduction](index.md) first. ## How to use AutoValue with Builders As explained in the introduction, the AutoValue concept is that **you write an abstract value class, and AutoValue implements it**. Builder generation works in the exact same way: you also create an abstract builder class, nesting it inside your abstract value class, and AutoValue generates implementations for both. ### In `Animal.java` ```java import com.google.auto.value.AutoValue; @AutoValue abstract class Animal { abstract String name(); abstract int numberOfLegs(); static Builder builder() { return new AutoValue_Animal.Builder(); } @AutoValue.Builder abstract static class Builder { abstract Builder setName(String value); abstract Builder setNumberOfLegs(int value); abstract Animal build(); } } ``` Note that in real life, some classes and methods would presumably be public and have **Javadoc**. We're leaving these off in the User Guide only to keep the examples clean and short. ### Usage ```java public void testAnimal() { Animal dog = Animal.builder().setName("dog").setNumberOfLegs(4).build(); assertEquals("dog", dog.name()); assertEquals(4, dog.numberOfLegs()); // You probably don't need to write assertions like these; just illustrating. assertTrue( Animal.builder().setName("dog").setNumberOfLegs(4).build().equals(dog)); assertFalse( Animal.builder().setName("cat").setNumberOfLegs(4).build().equals(dog)); assertFalse( Animal.builder().setName("dog").setNumberOfLegs(2).build().equals(dog)); assertEquals("Animal{name=dog, numberOfLegs=4}", dog.toString()); } ``` ### What does AutoValue generate? For the `Animal` example shown above, here is [typical code AutoValue might generate](generated-builder-example.md). ## Warnings Be sure to put the static `builder()` method directly in your value class (e.g., `Animal`) and not the nested abstract `Builder` class. That ensures that the `Animal` class is always initialized before `Builder`. Otherwise you may be exposing yourself to initialization-order problems. ## How do I... * ... [use (or not use) `set` **prefixes**?](builders-howto.md#beans) * ... [use different **names** besides `builder()`/`Builder`/`build()`?](builders-howto.md#build_names) * ... [specify a **default** value for a property?](builders-howto.md#default) * ... [initialize a builder to the same property values as an **existing** value instance](builders-howto.md#to_builder) * ... [include `with-` methods on my value class for creating slightly **altered** instances?](builders-howto.md#withers) * ... [**validate** property values?](builders-howto.md#validate) * ... [**normalize** (modify) a property value at `build` time?](builders-howto.md#normalize) * ... [expose **both** a builder and a factory method?](builders-howto.md#both) * ... [use a **collection**-valued property?](builders-howto.md#collection) * ... [let my builder **accumulate** values for a collection-valued property (not require them all at once)?](builders-howto.md#accumulate) * ... [accumulate values for a collection-valued property, without **breaking the chain**?](builders-howto.md#add) * ... [offer **both** accumulation and set-at-once methods for the same collection-valued property?](builders-howto.md#collection_both) auto-auto-service-1.0-rc7/value/userguide/design-faq.md000066400000000000000000000001721365703632600231020ustar00rootroot00000000000000# Design FAQ TODO. This page will contain various explanations of *why* AutoValue's features were designed as they are. auto-auto-service-1.0-rc7/value/userguide/extensions.md000066400000000000000000000031441365703632600232650ustar00rootroot00000000000000# Extensions AutoValue can be extended to implement new features for classes annotated with `@AutoValue`. ## Using extensions Each extension is a class. If that class is on the `processorpath` when you compile your `@AutoValue` class, the extension can run. Some extensions are triggered by their own annotations, which you add to your class; others may be triggered in other ways. Consult the extension's documentation for usage instructions. ## Writing an extension To add a feature, write a class that extends [`AutoValueExtension`], and put that class on the `processorpath` along with `AutoValueProcessor`. `AutoValueExtension` uses the [`ServiceLoader`] mechanism, which means: * Your class must be public and have a public no-argument constructor. * Its fully-qualified name must appear in a file called `META-INF/services/com.google.auto.value.extension.AutoValueExtension` in a JAR that is on the compiler's `classpath` or `processorpath`. You can use [AutoService] to make implementing the `ServiceLoader` pattern easy. Without extensions, AutoValue generates a subclass of the `@AutoValue` class. Extensions can work by generating a chain of subclasses, each of which alters behavior by overriding or implementing new methods. ## TODO * How to distribute extensions. * List of known extensions. [AutoService]: https://github.com/google/auto/tree/master/service [`AutoValueExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java [`ServiceLoader`]: http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html auto-auto-service-1.0-rc7/value/userguide/generated-builder-example.md000066400000000000000000000041321365703632600260770ustar00rootroot00000000000000# Generated builder example For the code shown in the [builder documentation](builders.md), the following is typical code AutoValue might generate: ```java import javax.annotation.Generated; @Generated("com.google.auto.value.processor.AutoValueProcessor") final class AutoValue_Animal extends Animal { private final String name; private final int numberOfLegs; private AutoValue_Animal( String name, int numberOfLegs) { this.name = name; this.numberOfLegs = numberOfLegs; } @Override String name() { return name; } @Override int numberOfLegs() { return numberOfLegs; } @Override public String toString() { return "Animal{" + "name=" + name + ", " + "numberOfLegs=" + numberOfLegs + "}"; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof Animal) { Animal that = (Animal) o; return (this.name.equals(that.name())) && (this.numberOfLegs == that.numberOfLegs()); } return false; } @Override public int hashCode() { int h = 1; h *= 1000003; h ^= this.name.hashCode(); h *= 1000003; h ^= this.numberOfLegs; return h; } static final class Builder extends Animal.Builder { private String name; private Integer numberOfLegs; Builder() { } @Override Animal.Builder setName(String name) { if (name == null) { throw new NullPointerException("Null name"); } this.name = name; return this; } @Override Animal.Builder setNumberOfLegs(int numberOfLegs) { this.numberOfLegs = numberOfLegs; return this; } @Override Animal build() { String missing = ""; if (this.name == null) { missing += " name"; } if (this.numberOfLegs == null) { missing += " numberOfLegs"; } if (!missing.isEmpty()) { throw new IllegalStateException("Missing required properties:" + missing); } return new AutoValue_Animal( this.name, this.numberOfLegs); } } } ``` auto-auto-service-1.0-rc7/value/userguide/generated-example.md000066400000000000000000000023641365703632600244600ustar00rootroot00000000000000# Generated example For the code shown in the [introduction](index.md), the following is typical code AutoValue might generate: ```java import javax.annotation.Generated; @Generated("com.google.auto.value.processor.AutoValueProcessor") final class AutoValue_Animal extends Animal { private final String name; private final int numberOfLegs; AutoValue_Animal(String name, int numberOfLegs) { if (name == null) { throw new NullPointerException("Null name"); } this.name = name; this.numberOfLegs = numberOfLegs; } @Override String name() { return name; } @Override int numberOfLegs() { return numberOfLegs; } @Override public String toString() { return "Animal{" + "name=" + name + ", " + "numberOfLegs=" + numberOfLegs + "}"; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof Animal) { Animal that = (Animal) o; return this.name.equals(that.name()) && this.numberOfLegs == that.numberOfLegs(); } return false; } @Override public int hashCode() { int h = 1; h *= 1000003; h ^= this.name.hashCode(); h *= 1000003; h ^= this.numberOfLegs; return h; } } ``` auto-auto-service-1.0-rc7/value/userguide/howto.md000066400000000000000000000577651365703632600222500ustar00rootroot00000000000000# How do I... This page answers common how-to questions that may come up when using AutoValue. You should read and understand the [Introduction](index.md) first. Questions specific to usage of the **builder option** are documented separately; for this, start by reading [AutoValue with builders](builders.md). ## Contents How do I... * ... [also generate a **builder** for my value class?](#builder) * ... [use AutoValue with a **nested** class?](#nested) * ... [use (or not use) JavaBeans-style name **prefixes**?](#beans) * ... [use **nullable** properties?](#nullable) * ... [perform other **validation**?](#validate) * ... [use a property of a **mutable** type?](#mutable_property) * ... [use a **custom** implementation of `equals`, etc.?](#custom) * ... [have AutoValue implement a concrete or default method?](#concrete) * ... [have multiple **`create`** methods, or name it/them differently?](#create) * ... [**ignore** certain properties in `equals`, etc.?](#ignore) * ... [have AutoValue also implement abstract methods from my **supertypes**?](#supertypes) * ... [use AutoValue with a **generic** class?](#generic) * ... [make my class Java- or GWT\-**serializable**?](#serialize) * ... [use AutoValue to **implement** an **annotation** type?](#annotation) * ... [also include **setter** (mutator) methods?](#setters) * ... [also generate **`compareTo`**?](#compareTo) * ... [use a **primitive array** for a property value?](#primitive_array) * ... [use an **object array** for a property value?](#object_array) * ... [have one `@AutoValue` class **extend** another?](#inherit) * ... [keep my accessor methods **private**?](#private_accessors) * ... [expose a **constructor**, not factory method, as my public creation API?](#public_constructor) * ... [use AutoValue on an **interface**, not abstract class?](#interface) * ... [**memoize** ("cache") derived properties?](#memoize) * ... [memoize the result of `hashCode` or `toString`?](#memoize_hash_tostring) * ... [make a class where only one of its properties is ever set?](#oneof) * ... [copy annotations from a class/method to the implemented class/method/field?](#copy_annotations) ## ... also generate a builder for my value class? Please see [AutoValue with builders](builders.md). ## ... use AutoValue with a nested class? AutoValue composes the generated class name in the form `AutoValue_`*`Outer_Middle_Inner`*. As many of these segments will be used in the generated name as required. Only the simple class name will appear in `toString` output. ```java class Outer { static class Middle { @AutoValue abstract static class Inner { static Inner create(String foo) { return new AutoValue_Outer_Middle_Inner(foo); } ... ``` ## ... use (or not use) JavaBeans-style name prefixes? Some developers prefer to name their accessors with a `get-` or `is-` prefix, but would prefer that only the "bare" property name be used in `toString` and for the generated constructor's parameter names. AutoValue will do exactly this, but only if you are using these prefixes *consistently*. In that case, it infers your intended property name by first stripping the `get-` or `is-` prefix, then adjusting the case of what remains as specified by [Introspector.decapitalize](http://docs.oracle.com/javase/8/docs/api/java/beans/Introspector.html#decapitalize). Note that, in keeping with the JavaBeans specification, the `is-` prefix is only allowed on `boolean`-returning methods. `get-` is allowed on any type of accessor. ## ... use nullable properties? Ordinarily the generated constructor will reject any null values. If you want to accept null, simply apply any annotation named `@Nullable` to the appropriate accessor methods. This causes AutoValue to remove the null checks and generate null-friendly code for `equals`, `hashCode` and `toString`. Example: ```java @AutoValue public abstract class Foo { public static Foo create(@Nullable Bar bar) { return new AutoValue_Foo(bar); } @Nullable abstract Bar bar(); } ``` This example also shows annotating the corresponding `create` parameter with `@Nullable`. AutoValue does not actually require this annotation, only the one on the accessor, but we recommended it as useful documentation to your caller. Conversely, if `@Nullable` is only added to the parameter in `create` (or similarly the setter method of [AutoValue.Builder](builders)), but not the corresponding accessor method, it won't have any effect. ## ... perform other validation? Null checks are added automatically (as [above](#nullable)). For other types of precondition checks or pre-processing, just add them to your factory method: ```java static MyType create(String first, String second) { checkArgument(!first.isEmpty()); return new AutoValue_MyType(first, second.trim()); } ``` ## ... use a property of a mutable type? AutoValue classes are meant and expected to be immutable. But sometimes you would want to take a mutable type and use it as a property. In these cases: First, check if the mutable type has a corresponding immutable cousin. For example, the types `List` and `String[]` have the immutable counterpart `ImmutableList` in [Guava](http://github.com/google/guava). If so, use the immutable type for your property, and only accept the mutable type during construction: ```java @AutoValue public abstract class ListExample { public static ListExample create(String[] mutableNames) { return new AutoValue_ListExample(ImmutableList.copyOf(mutableNames)); } public abstract ImmutableList names(); } ``` Note: this is a perfectly sensible practice, not an ugly workaround! If there is no suitable immutable type to use, you'll need to proceed with caution. Your static factory method should pass a *clone* of the passed object to the generated constructor. Your accessor method should document a very loud warning never to mutate the object returned. ```java @AutoValue public abstract class MutableExample { public static MutableExample create(MutablePropertyType ouch) { // Replace `.clone` below with the right copying code for this type return new AutoValue_MutableExample(ouch.clone()); } /** * Returns the ouch associated with this object; do not mutate the * returned object. */ public abstract MutablePropertyType ouch(); } ``` Warning: this is an ugly workaround, not a perfectly sensible practice! Callers can trivially break the invariants of the immutable class by mutating the accessor's return value. An example where something can go wrong: AutoValue objects can be used as keys in Maps. ## ... use a custom implementation of `equals`, etc.? Simply write your custom implementation; AutoValue will notice this and will skip generating its own. Your hand-written logic will thus be inherited on the concrete implementation class. We call this *underriding* the method. Remember when doing this that you are losing AutoValue's protections. Be careful to follow the basic rules of hash codes: equal objects must have equal hash codes *always*, and equal hash codes should imply equal objects *almost always*. You should now test your class more thoroughly, ideally using [`EqualsTester`](http://static.javadoc.io/com.google.guava/guava-testlib/19.0/com/google/common/testing/EqualsTester.html) from [guava-testlib](http://github.com/google/guava). Best practice: mark your underriding methods `final` to make it clear to future readers that these methods aren't overridden by AutoValue. ## ... have AutoValue implement a concrete or default method? If a parent class defines a concrete (non-abstract) method that you would like AutoValue to implement, you can *redeclare* it as abstract. This applies to `Object` methods like `toString()`, but also to property methods that you would like to have AutoValue implement. It also applies to default methods in interfaces. ```java @AutoValue class PleaseOverrideExample extends SuperclassThatDefinesToString { ... // cause AutoValue to generate this even though the superclass has it @Override public abstract String toString(); } ``` ```java @AutoValue class PleaseReimplementDefaultMethod implements InterfaceWithDefaultMethod { ... // cause AutoValue to implement this even though the interface has a default // implementation @Override public abstract int numberOfLegs(); } ``` ## ... have multiple `create` methods, or name it/them differently? Just do it! AutoValue doesn't actually care. This [best practice item](practices.md#one_reference) may be relevant. ## ... ignore certain properties in `equals`, etc.? Suppose your value class has an extra field that shouldn't be included in `equals` or `hashCode` computations. If this is because it is a derived value based on other properties, see [How do I memoize derived properties?](#memoize). Otherwise, first make certain that you really want to do this. It is often, but not always, a mistake. Remember that libraries will treat two equal instances as absolutely *interchangeable* with each other. Whatever information is present in this extra field could essentially "disappear" when you aren't expecting it, for example when your value is stored and retrieved from certain collections. If you're sure, here is how to do it: ```java @AutoValue abstract class IgnoreExample { static IgnoreExample create(String normalProperty, String ignoredProperty) { IgnoreExample ie = new AutoValue_IgnoreExample(normalProperty); ie.ignoredProperty = ignoredProperty; return ie; } abstract String normalProperty(); private String ignoredProperty; // sadly, it can't be `final` final String ignoredProperty() { return ignoredProperty; } } ``` Note that this means the field is also ignored by `toString`; to AutoValue it simply doesn't exist. ## ... have AutoValue also implement abstract methods from my supertypes? AutoValue will recognize every abstract accessor method whether it is defined directly in your own hand-written class or in a supertype. These abstract methods can come from more than one place, for example from an interface and from the superclass. It may not then be obvious what order they are in, even though you need to know this order if you want to call the generated `AutoValue_Foo` constructor. You might find it clearer to use a [builder](builders.md) instead. But the order is deterministic: within a class or interface, methods are in the order they appear in the source code; methods in ancestors come before methods in descendants; methods in interfaces come before methods in classes; and in a class or interface that has more than one superinterface, the interfaces are in the order of their appearance in `implements` or `extends`. ## ... use AutoValue with a generic class? There's nothing to it: just add type parameters to your class and to your call to the generated constructor. ## ... make my class Java- or GWT\-serializable? Just add `implements Serializable` or the `@GwtCompatible(serializable = true)` annotation (respectively) to your hand-written class; it (as well as any `serialVersionUID`) will be duplicated on the generated class, and you'll be good to go. ## ... use AutoValue to implement an annotation type? Most users should never have the need to programmatically create "fake" annotation instances. But if you do, using `@AutoValue` in the usual way will fail because the `Annotation.hashCode` specification is incompatible with AutoValue's behavior. However, we've got you covered anyway! Suppose this annotation definition: ```java public @interface Named { String value(); } ``` All you need is this: ```java public class Names { @AutoAnnotation public static Named named(String value) { return new AutoAnnotation_Names_named(value); } } ``` For more details, see the [`AutoAnnotation` javadoc](http://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/AutoAnnotation.java#L24). ## ... also include setter (mutator) methods? You can't; AutoValue only generates immutable value classes. Note that giving value semantics to a mutable type is widely considered a questionable practice in the first place. Equal instances of a value class are treated as *interchangeable*, but they can't truly be interchangeable if one might be mutated and the other not. ## ... also generate `compareTo`? AutoValue intentionally does not provide this feature. It is better for you to roll your own comparison logic using the new methods added to [`Comparator`](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html) in Java 8, or [`ComparisonChain`](http://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/ComparisonChain.html) from [Guava](http://github.com/google/guava). Since these mechanisms are easy to use, require very little code, and give you the flexibility you need, there's really no way for AutoValue to improve on them! ## ... use a primitive array for a property value? Go right ahead! AutoValue will generate code that acts on the *values* stored the array, not the object identity of the array itself, which is (with virtual certainty) what you want. Heed the warnings given above about [mutable properties](#mutable_property). ## ... use an object array for a property value? This is not allowed. Object arrays are very badly-behaved and unlike primitive arrays, they can be replaced with a proper `List` implementation for very little added cost. If it's important to accept an object array at construction time, refer to the *first* example shown [here](#mutable_property). ## ... have one `@AutoValue` class extend another? This ability is intentionally not supported, because there is no way to do it correctly. See *Effective Java, 2nd Edition* Item 8: "Obey the general contract when overriding equals". ## ... keep my accessor methods private? We're sorry. This is one of the rare and unfortunate restrictions AutoValue's approach places on your API. Your accessor methods don't have to be *public*, but they must be at least package-visible. ## ... expose a constructor, not factory method, as my public creation API? We're sorry. This is one of the rare restrictions AutoValue's approach places on your API. However, note that static factory methods are recommended over public constructors by *Effective Java*, Item 1. ## ... use AutoValue on an interface, not abstract class? AutoValue classes can certainly implement an interface, however an interface may not be used in lieu of an abstract class. The only advantage of interfaces we're aware of is that you can omit `public abstract` from the methods. That's not much. On the other hand, you would lose the immutability guarantee, and you'd also invite more of the kind of bad behavior described in [this best-practices item](practices.md#simple). On balance, we don't think it's worth it. ## ... memoize ("cache") derived properties? Sometimes your class has properties that are derived from the ones that AutoValue implements. You'd typically implement them with a concrete method that uses the other properties: ```java @AutoValue abstract class Foo { abstract Bar barProperty(); String derivedProperty() { return someFunctionOf(barProperty()); } } ``` But what if `someFunctionOf(Bar)` is expensive? You'd like to calculate it only one time, then cache and reuse that value for all future calls. Normally, thread-safe lazy initialization involves a lot of tricky boilerplate. Instead, just write the derived-property accessor method as above, and annotate it with [`@Memoized`]. Then AutoValue will override that method to return a stored value after the first call: ```java @AutoValue abstract class Foo { abstract Bar barProperty(); @Memoized String derivedProperty() { return someFunctionOf(barProperty()); } } ``` Then your method will be called at most once, even if multiple threads attempt to access the property concurrently. The annotated method must have the usual form of an accessor method, and may not be `abstract`, `final`, or `private`. The stored value will not be used in the implementation of `equals`, `hashCode`, or `toString`. If a `@Memoized` method is also annotated with `@Nullable`, then `null` values will be stored; if not, then the overriding method throws `NullPointerException` when the annotated method returns `null`. [`@Memoized`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/memoized/Memoized.java ## ... memoize the result of `hashCode` or `toString`? You can also make your class remember and reuse the result of `hashCode`, `toString`, or both, like this: ```java @AutoValue abstract class Foo { abstract Bar barProperty(); @Memoized @Override public abstract int hashCode(); @Memoized @Override public abstract String toString(); } ``` ## ... make a class where only one of its properties is ever set? Often, the best way to do this is using inheritance. Although one `@AutoValue` class can't inherit from another, two `@AutoValue` classes can inherit from a common parent. ```java public abstract class StringOrInteger { public abstract String representation(); public static StringOrInteger ofString(String s) { return new AutoValue_StringOrInteger_StringValue(s); } public static StringOrInteger ofInteger(int i) { return new AutoValue_StringOrInteger_IntegerValue(i); } @AutoValue abstract class StringValue extends StringOrInteger { abstract String string(); @Override public String representation() { return '"' + string() + '"'; } } @AutoValue abstract class IntegerValue extends StringOrInteger { abstract int integer(); @Override public String representation() { return Integer.toString(integer()); } } } ``` So any `StringOrInteger` instance is actually either a `StringValue` or an `IntegerValue`. Clients only care about the `representation()` method, so they don't need to know which it is. But if clients of your class may want to take different actions depending on which property is set, there is an alternative to `@AutoValue` called `@AutoOneOf`. This effectively creates a [*tagged union*](https://en.wikipedia.org/wiki/Tagged_union). Here is `StringOrInteger` written using `@AutoOneOf`, with the `representation()` method moved to a separate client class: ```java @AutoOneOf(StringOrInteger.Kind.class) public abstract class StringOrInteger { public enum Kind {STRING, INTEGER} public abstract Kind getKind(); public abstract String string(); public abstract int integer(); public static StringOrInteger ofString(String s) { return AutoOneOf_StringOrInteger.string(s); } public static StringOrInteger ofInteger(int i) { return AutoOneOf_StringOrInteger.integer(i); } } public class Client { public String representation(StringOrInteger stringOrInteger) { switch (stringOrInteger.getKind()) { case STRING: return '"' + stringOrInteger.string() + '"'; case INTEGER: return Integer.toString(stringOrInteger.integer()); } throw new AssertionError(stringOrInteger.getKind()); } } ``` Switching on an enum like this can lead to more robust code than using `instanceof` checks, especially if a tool like [Error Prone](https://errorprone.info/bugpattern/MissingCasesInEnumSwitch) can alert you if you add a new variant without updating all your switches. (On the other hand, if nothing outside your class references `getKind()`, you should consider if a solution using inheritance might be better.) There must be an enum such as `Kind`, though it doesn't have to be called `Kind` and it doesn't have to be nested inside the `@AutoOneOf` class. There must be an abstract method returning the enum, though it doesn't have to be called `getKind()`. For every value of the enum, there must be an abstract method with the same name (ignoring case and underscores). An `@AutoOneOf` class called `Foo` will then get a generated class called `AutoOneOf_Foo` that has a static factory method for each property, with the same name. In the example, the `STRING` value in the enum corresponds to the `string()` property and to the `AutoOneOf_StringOrInteger.string` factory method. Properties in an `@AutoOneOf` class can be `void` to indicate that the corresponding variant has no data. In that case, the factory method for that variant has no parameters: ```java @AutoOneOf(Transform.Kind.class) public abstract class Transform { public enum Kind {NONE, CIRCLE_CROP, BLUR} public abstract Kind getKind(); abstract void none(); abstract void circleCrop(); public abstract BlurTransformParameters blur(); public static Transform ofNone() { return AutoOneOf_Transform.none(); } public static Transform ofCircleCrop() { return AutoOneOf_Transform.circleCrop(); } public static Transform ofBlur(BlurTransformParmeters params) { return AutoOneOf_Transform.blur(params); } } ``` Here, the `NONE` and `CIRCLE_CROP` variants have no associated data but are distinct from each other. The `BLUR` variant does have data. The `none()` and `circleCrop()` methods are package-private; they must exist to configure `@AutoOneOf`, but calling them is not very useful. (It does nothing if the instance is of the correct variant, or throws an exception otherwise.) The `AutoOneOf_Transform.none()` and `AutoOneOf_Transform.circleCrop()` methods return the same instance every time they are called. If one of the `void` variants means "none", consider using an `Optional` or a `@Nullable Transform` instead of that variant. Properties in an `@AutoOneOf` class cannot be null. Instead of a `StringOrInteger` with a `@Nullable String`, you probably want a `@Nullable StringOrInteger` or an `Optional`, or an empty variant as just described. ## ... copy annotations from a class/method to the implemented class/method/field? ### Copying to the generated class If you want to copy annotations from your `@AutoValue`-annotated class to the generated `AutoValue_...` implemention, annotate your class with [`@AutoValue.CopyAnnotations`]. For example, if `Example.java` is: ```java @AutoValue @AutoValue.CopyAnnotations @SuppressWarnings("Immutable") // justification ... abstract class Example { // details ... } ``` Then `@AutoValue` will generate `AutoValue_Example.java`: ```java @SuppressWarnings("Immutable") final class AutoValue_Example extends Example { // implementation ... } ``` ### Copying to the generated method For historical reasons, annotations on methods of an `@AutoValue`-annotated class are copied to the generated implementation class's methods. However, if you want to exclude some annotations from being copied, you can use [`@AutoValue.CopyAnnotations`]'s `exclude` method to stop this behavior. ### Copying to the generated field If you want to copy annotations from your `@AutoValue`-annotated class's methods to the generated fields in the `AutoValue_...` implementation, annotate your method with [`@AutoValue.CopyAnnotations`]. For example, if `Example.java` is: ```java @Immutable @AutoValue abstract class Example { @CopyAnnotations @SuppressWarnings("Immutable") // justification ... abstract Object getObject(); // other details ... } ``` Then `@AutoValue` will generate `AutoValue_Example.java`: ```java final class AutoValue_Example extends Example { @SuppressWarnings("Immutable") private final Object object; @SuppressWarnings("Immutable") @Override Object getObject() { return object; } // other details ... } ``` [`@AutoValue.CopyAnnotations`]: http://static.javadoc.io/com.google.auto.value/auto-value/1.6/com/google/auto/value/AutoValue.CopyAnnotations.html auto-auto-service-1.0-rc7/value/userguide/index.md000066400000000000000000000243351365703632600222020ustar00rootroot00000000000000# AutoValue *Generated immutable value classes for Java 7+*
      ***Éamonn McManus, Kevin Bourrillion***
      **Google, Inc.** > "AutoValue is a great tool for eliminating the drudgery of writing mundane > value classes in Java. It encapsulates much of the advice in Effective Java > Chapter 2, and frees you to concentrate on the more interesting aspects of > your program. The resulting program is likely to be shorter, clearer, and > freer of bugs. Two thumbs up." > > -- *Joshua Bloch, author, Effective Java* ## Background **Value classes** are extremely common in Java projects. These are classes for which you want to treat any two instances with suitably equal field values as interchangeable. That's right: we're talking about those classes where you wind up implementing `equals`, `hashCode` and `toString` in a bloated, repetitive, formulaic yet error-prone fashion. Writing these methods the first time is not too bad, with the aid of a few helper methods and IDE templates. But once written they continue to burden reviewers, editors and future readers. Their wide expanses of boilerplate sharply decrease the signal-to-noise ratio of your code... and they love to harbor hard-to-spot bugs. AutoValue provides an easier way to create immutable value classes, with a lot less code and less room for error, while **not restricting your freedom** to code almost any aspect of your class exactly the way you want it. This page will walk you through how to use AutoValue. Looking for a little more persuasion? Please see [Why AutoValue?](why.md). ## How to use AutoValue The AutoValue concept is extremely simple: **You write an abstract class, and AutoValue implements it.** That is all there is to it; there is literally *no* configuration. **Note:** Below, we will illustrate an AutoValue class *without* a generated builder class. If you're more interested in the builder support, continue reading at [AutoValue with Builders](builders.md) instead. ### In your value class Create your value class as an *abstract* class, with an abstract accessor method for each desired property, and bearing the `@AutoValue` annotation. ```java import com.google.auto.value.AutoValue; @AutoValue abstract class Animal { static Animal create(String name, int numberOfLegs) { return new AutoValue_Animal(name, numberOfLegs); } abstract String name(); abstract int numberOfLegs(); } ``` The constructor parameters correspond, in order, to the abstract accessor methods. **For a nested class**, see ["How do I use AutoValue with a nested class"](howto.md#nested). Note that in real life, some classes and methods would presumably be public and have Javadoc. We're leaving these off in the User Guide only to keep the examples short and simple. ### With Maven You will need `auto-value-annotations-${auto-value.version}.jar` in your compile-time classpath, and you will need `auto-value-${auto-value.version}.jar` in your annotation-processor classpath. For `auto-value-annotations`, you can write this in `pom.xml`: ```xml com.google.auto.value auto-value-annotations ${auto-value.version} ``` For `auto-value` (the annotation processor), you can write this: ```xml maven-compiler-plugin com.google.auto.value auto-value ${auto-value.version} ``` Alternatively, you can include the processor itself in your compile-time classpath. Doing so may pull unnecessary classes into your runtime classpath. ```xml com.google.auto.value auto-value ${auto-value.version} true ``` ### With Gradle Gradle users can declare the dependencies in their `build.gradle` script: ```groovy dependencies { // Use 'api' rather than 'compile' for Android or java-library projects. compile "com.google.auto.value:auto-value-annotations:${autoValueVersion}" annotationProcessor "com.google.auto.value:auto-value:${autoValueVersion}" } ``` Note: If you are using a version of Gradle prior to 4.6, you must apply an annotation processing plugin [as described in these instructions][tbroyer-apt]. [tbroyer-apt]: https://plugins.gradle.org/plugin/net.ltgt.apt ### Usage Your choice to use AutoValue is essentially *API-invisible*. This means that, to the consumer of your class, your class looks and functions like any other. The simple test below illustrates that behavior. Note that in real life, you would write tests that actually *do something interesting* with the object, instead of only checking field values going in and out. ```java public void testAnimal() { Animal dog = Animal.create("dog", 4); assertEquals("dog", dog.name()); assertEquals(4, dog.numberOfLegs()); // You probably don't need to write assertions like these; just illustrating. assertTrue(Animal.create("dog", 4).equals(dog)); assertFalse(Animal.create("cat", 4).equals(dog)); assertFalse(Animal.create("dog", 2).equals(dog)); assertEquals("Animal{name=dog, numberOfLegs=4}", dog.toString()); } ``` ### What's going on here? AutoValue runs inside `javac` as a standard annotation processor. It reads your abstract class and infers what the implementation class should look like. It generates source code, in your package, of a concrete implementation class which extends your abstract class, having: * package visibility (non-public) * one field for each of your abstract accessor methods * a constructor that sets these fields * a concrete implementation of each accessor method returning the associated field value * an `equals` implementation that compares these values in the usual way * an appropriate corresponding `hashCode` * a `toString` implementation returning a useful (but unspecified) string representation of the instance Your hand-written code, as shown above, delegates its factory method call to the generated constructor and voilà! For the `Animal` example shown above, here is [typical code AutoValue might generate](generated-example.md). Note that *consumers* of your value class *don't need to know any of this*. They just invoke your provided factory method and get a well-behaved instance back. ## Warnings Be careful that you don't accidentally pass parameters to the generated constructor in the wrong order. You must ensure that **your tests are sufficient** to catch any field ordering problem. In most cases this should be the natural outcome from testing whatever actual purpose this value class was created for! In other cases a very simple test like the one shown above is enough. Consider switching to use the [builder option](builders.md) to avoid this problem. We reserve the right to **change the `hashCode` implementation** at any time. Never persist the result of `hashCode` or use it for any other unintended purpose, and be careful never to depend on the order your values appear in unordered collections like `HashSet`. ## Why should I use AutoValue? See [Why AutoValue?](why.md). ## What Java versions does it work with? AutoValue requires that your compiler be at least Java 8. However, the code that it generates is compatible with Java 7. That means that you can use it with `-source 7 -target 7` or (for Java 9+) `--release 7`. ## How do I... How do I... * ... [also generate a **builder** for my value class?](howto.md#builder) * ... [use AutoValue with a **nested** class?](howto.md#nested) * ... [use (or not use) JavaBeans-style name **prefixes**?](howto.md#beans) * ... [use **nullable** properties?](howto.md#nullable) * ... [perform other **validation**?](howto.md#validate) * ... [use a property of a **mutable** type?](howto.md#mutable_property) * ... [use a **custom** implementation of `equals`, etc.?](howto.md#custom) * ... [have AutoValue implement a concrete or default method?](howto.md#concrete) * ... [have multiple **`create`** methods, or name it/them differently?](howto.md#create) * ... [**ignore** certain properties in `equals`, etc.?](howto.md#ignore) * ... [have AutoValue also implement abstract methods from my **supertypes**?](howto.md#supertypes) * ... [use AutoValue with a **generic** class?](howto.md#generic) * ... [make my class Java- or GWT\-**serializable**?](howto.md#serialize) * ... [use AutoValue to **implement** an **annotation** type?](howto.md#annotation) * ... [also include **setter** (mutator) methods?](howto.md#setters) * ... [also generate **`compareTo`**?](howto.md#compareTo) * ... [use a **primitive array** for a property value?](howto.md#primitive_array) * ... [use an **object array** for a property value?](howto.md#object_array) * ... [have one `@AutoValue` class **extend** another?](howto.md#inherit) * ... [keep my accessor methods **private**?](howto.md#private_accessors) * ... [expose a **constructor**, not factory method, as my public creation API?](howto.md#public_constructor) * ... [use AutoValue on an **interface**, not abstract class?](howto.md#interface) * ... [**memoize** ("cache") derived properties?](howto.md#memoize) * ... [memoize the result of `hashCode` or `toString`?](howto.md#memoize_hash_tostring) * ... [make a class where only one of its properties is ever set?](howto.md#oneof) * ... [copy annotations from a class/method to the implemented class/method/field?](howto.md#copy_annotations) ## More information See the links in the sidebar at the top left. auto-auto-service-1.0-rc7/value/userguide/performance.md000066400000000000000000000003371365703632600233700ustar00rootroot00000000000000# Performance notes TODO: write a real page * should perform like a hand-written class after HotSpot compiles it (generated accessors can be inlined) * what does proguard do with it * hash codes are not cached auto-auto-service-1.0-rc7/value/userguide/practices.md000066400000000000000000000057221365703632600230470ustar00rootroot00000000000000# Best practices ## "Equals means interchangeable" Don't use AutoValue to implement value semantics unless you really want value semantics. In particular, you should never care about the difference between two equal instances. ## Avoid mutable property types Avoid mutable types, including arrays, for your properties, especially if you make your accessor methods `public`. The generated accessors don't copy the field value on its way out, so you'd be exposing your internal state. Note that this doesn't mean your factory method can't *accept* mutable types as input parameters. Example: ```java @AutoValue public abstract class ListExample { abstract ImmutableList names(); public static ListExample create(List mutableNames) { return new AutoValue_ListExample(ImmutableList.copyOf(mutableNames)); } } ``` ## Keep behavior simple and dependency-free Your class can (and should) contain *simple* intrinsic behavior. But it shouldn't require complex dependencies and shouldn't access static state. You should essentially *never* need an alternative implementation of your hand-written abstract class, whether hand-written or generated by a mocking framework. If your behavior has enough complexity (or dependencies) that it actually needs to be mocked or faked, split it into a separate type that is *not* a value type. Otherwise it permits an instance with "real" behavior and one with "mock/fake" behavior to be `equals`, which does not make sense. ## One reference only Other code in the same package will be able to directly access the generated class, but *should not*. It's best if each generated class has one and only one reference from your source code: the call from your static factory method to the generated constructor. If you have multiple factory methods, have them all delegate to the same hand-written method, so there is still only one point of contact with the generated code. This way, you have only one place to insert precondition checks or other pre- or postprocessing. ## Mark all concrete methods `final` Consider that other developers will try to read and understand your value class while looking only at your hand-written class, not the actual (generated) implementation class. If you mark your concrete methods `final`, they won't have to wonder whether the generated subclass might be overriding them. This is especially helpful if you are *[underriding](howto.md#custom)* `equals`, `hashCode` or `toString`! ## Maybe add an explicit, inaccessible constructor There are a few small advantages to adding a package-private, parameterless constructor to your abstract class. It prevents unwanted subclasses, and prevents an undocumented public constructor showing up in your generated API documentation. Whether these benefits are worth the extra noise in the file is a matter of your judgment. auto-auto-service-1.0-rc7/value/userguide/trouble.md000066400000000000000000000005761365703632600225500ustar00rootroot00000000000000# Troubleshooting TODO ## `equals()` is not returning what I expect This is usually a sign that one of your field types is not implementing `equals` as you expect. A typical offending class is [`JSONObject`] (https://developer.android.com/reference/org/json/JSONObject.html), which doesn't override `Object.equals()` and thus compromises your class's `equals` behavior as well. auto-auto-service-1.0-rc7/value/userguide/why.md000066400000000000000000000012701365703632600216730ustar00rootroot00000000000000# Why use AutoValue? AutoValue is the only solution to the value class problem in Java having all of the following characteristics: * **API-invisible** (callers cannot become dependent on your choice to use it) * No runtime dependencies * Negligible cost to performance * Very few limitations on what your class can do * Extralinguistic "magic" kept to an absolute minimum (uses only standard Java platform technologies, in the manner they were intended) This [slide presentation] compares AutoValue to numerous alternatives and explains why we think it is better. [slide presentation]: https://docs.google.com/presentation/d/14u_h-lMn7f1rXE1nDiLX0azS3IkgjGl5uxp5jGJ75RE/edit