/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.inferred.freebuilder.processor; import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import org.inferred.freebuilder.processor.util.Excerpt; import org.inferred.freebuilder.processor.util.ParameterizedType; import org.inferred.freebuilder.processor.util.QualifiedName; import org.inferred.freebuilder.processor.util.SourceBuilder; import javax.annotation.Nullable; import javax.lang.model.type.TypeMirror; /** * Metadata about a @{@link org.inferred.freebuilder.FreeBuilder FreeBuilder} type. */ public abstract class Metadata { /** Standard Java methods that may be underridden. */ public enum StandardMethod { TO_STRING, HASH_CODE, EQUALS } /** How compulsory the underride is. */ public enum UnderrideLevel { /** There is no underride. */ ABSENT, /** The underride can be overridden (viz. to respect Partials). */ OVERRIDEABLE, /** The underride is declared final. */ FINAL; } public static class Visibility extends Excerpt { public static final Visibility PUBLIC = new Visibility(0, "PUBLIC", "public "); public static final Visibility PROTECTED = new Visibility(1, "PROTECTED", "protected "); public static final Visibility PACKAGE = new Visibility(2, "PACKAGE", ""); public static final Visibility PRIVATE = new Visibility(3, "PRIVATE", "private "); private final int order; private final String name; private final String excerpt; Visibility(int order, String name, String excerpt) { this.order = order; this.name = name; this.excerpt = excerpt; } public static Visibility mostVisible(Visibility a, Visibility b) { return (a.order < b.order) ? a : b; } @Override public void addTo(SourceBuilder code) { code.add(excerpt); } @Override public String toString() { return name; } @Override protected void addFields(FieldReceiver fields) { fields.add("excerpt", excerpt); } } /** Returns the type itself. */ public abstract ParameterizedType getType(); /** Returns true if the type is an interface. */ public abstract boolean isInterfaceType(); abstract Optional<ParameterizedType> getOptionalBuilder(); /** * Returns true if there is a user-visible Builder subclass defined. */ public boolean hasBuilder() { return getOptionalBuilder().isPresent(); } /** * Returns the builder type that users will see. * * @throws IllegalStateException if {@link #hasBuilder} returns false. */ public ParameterizedType getBuilder() { return getOptionalBuilder().get(); } /** Returns the builder factory mechanism the user has exposed, if any. */ public abstract Optional<BuilderFactory> getBuilderFactory(); /** Returns the builder class that should be generated. */ public abstract ParameterizedType getGeneratedBuilder(); /** Returns the value class that should be generated. */ public abstract ParameterizedType getValueType(); /** Returns the partial value class that should be generated. */ public abstract ParameterizedType getPartialType(); /** * Returns a set of nested types that will be visible in the generated class, either because they * will be generated, or because they are present in a superclass. */ public abstract ImmutableSet<QualifiedName> getVisibleNestedTypes(); /** Returns the Property enum that may be generated. */ public abstract ParameterizedType getPropertyEnum(); /** Returns metadata about the properties of the type. */ public abstract ImmutableList<Property> getProperties(); public UnderrideLevel standardMethodUnderride(StandardMethod standardMethod) { UnderrideLevel underrideLevel = getStandardMethodUnderrides().get(standardMethod); return (underrideLevel == null) ? UnderrideLevel.ABSENT : underrideLevel; } public abstract ImmutableMap<StandardMethod, UnderrideLevel> getStandardMethodUnderrides(); /** Returns whether the builder type should be serializable. */ public abstract boolean isBuilderSerializable(); /** Returns whether the value type has a toBuilder method that needs to be generated. */ public abstract boolean getHasToBuilderMethod(); /** Returns a list of annotations that should be applied to the generated builder class. */ public abstract ImmutableList<Excerpt> getGeneratedBuilderAnnotations(); /** Returns a list of annotations that should be applied to the generated value class. */ public abstract ImmutableList<Excerpt> getValueTypeAnnotations(); /** Returns the visibility of the generated value class. */ public abstract Visibility getValueTypeVisibility(); /** Returns a list of nested classes that should be added to the generated builder class. */ public abstract ImmutableList<Function<Metadata, Excerpt>> getNestedClasses(); public Builder toBuilder() { return new Builder().mergeFrom(this); } /** Metadata about a property of a {@link Metadata}. */ public abstract static class Property { /** Returns the type of the property. */ public abstract TypeMirror getType(); /** Returns the boxed form of {@link #getType()}, or null if type is not primitive. */ @Nullable public abstract TypeMirror getBoxedType(); /** Returns the name of the property, e.g. myProperty. */ public abstract String getName(); /** Returns the capitalized name of the property, e.g. MyProperty. */ public abstract String getCapitalizedName(); /** Returns the name of the property in all-caps with underscores, e.g. MY_PROPERTY. */ public abstract String getAllCapsName(); /** Returns true if getters start with "get"; setters should follow suit with "set". */ public abstract boolean isUsingBeanConvention(); /** Returns the name of the getter for the property, e.g. getMyProperty, or isSomethingTrue. */ public abstract String getGetterName(); /** * Returns the code generator to use for this property, or null if no generator has been picked * (i.e. when passed to {@link PropertyCodeGenerator.Factory#create}. */ @Nullable public abstract PropertyCodeGenerator getCodeGenerator(); /** * Returns true if a cast to this property type is guaranteed to be fully checked at runtime. * This is true for any type that is non-generic, raw, or parameterized with unbounded * wildcards, such as {@code Integer}, {@code List} or {@code Map<?, ?>}. */ public abstract boolean isFullyCheckedCast(); /** * Returns a list of annotations that should be applied to the accessor methods of this * property; that is, the getter method, and a single setter method that will accept the result * of the getter method as its argument. For a list, for example, that would be getX() and * addAllX(). */ public abstract ImmutableList<Excerpt> getAccessorAnnotations(); public Builder toBuilder() { return new Builder().mergeFrom(this); } /** Builder for {@link Property}. */ public static class Builder extends Metadata_Property_Builder {} } public static final Function<Property, PropertyCodeGenerator> GET_CODE_GENERATOR = new Function<Property, PropertyCodeGenerator>() { @Override public PropertyCodeGenerator apply(Property input) { return input.getCodeGenerator(); } }; /** Builder for {@link Metadata}. */ public static class Builder extends Metadata_Builder { public Builder() { super.setValueTypeVisibility(Visibility.PRIVATE); super.setHasToBuilderMethod(false); } /** * Sets the value to be returned by {@link Metadata#getValueTypeVisibility()} to the most * visible of the current value and {@code visibility}. Will not decrease visibility. * * @return this {@code Builder} object * @throws NullPointerException if {@code visibility} is null */ @Override public Builder setValueTypeVisibility(Visibility visibility) { return super.setValueTypeVisibility( Visibility.mostVisible(getValueTypeVisibility(), visibility)); } /** Sets the builder class that users will see, if any. */ public Builder setBuilder(Optional<ParameterizedType> builder) { return setOptionalBuilder(builder); } /** Sets the builder class that users will see. */ public Builder setBuilder(ParameterizedType builder) { return setOptionalBuilder(builder); } /** * Returns a newly-built {@link Metadata} based on the content of the {@code Builder}. */ @Override public Metadata build() { Metadata metadata = super.build(); QualifiedName generatedBuilder = metadata.getGeneratedBuilder().getQualifiedName(); checkState(metadata.getValueType().getQualifiedName().getEnclosingType() .equals(generatedBuilder), "%s not a nested class of %s", metadata.getValueType(), generatedBuilder); checkState(metadata.getPartialType().getQualifiedName().getEnclosingType() .equals(generatedBuilder), "%s not a nested class of %s", metadata.getPartialType(), generatedBuilder); checkState(metadata.getPropertyEnum().getQualifiedName().getEnclosingType() .equals(generatedBuilder), "%s not a nested class of %s", metadata.getPropertyEnum(), generatedBuilder); return metadata; } } }