
org.immutables.value.internal.$processor$.$OkJsons Maven / Gradle / Ivy
/*
Copyright 2015-2018 Immutables Authors and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES 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.immutables.value.processor;
import com.google.common.base.CaseFormat;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.EnumSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import org.immutables.generator.Generator;
import org.immutables.value.Value;
import org.immutables.value.processor.meta.OkNamedMirror;
import org.immutables.value.processor.meta.Proto.AbstractDeclaring;
import org.immutables.value.processor.meta.Proto.DeclaringType;
import org.immutables.value.processor.meta.Proto.Protoclass;
import org.immutables.value.processor.meta.ValueAttribute;
import org.immutables.value.processor.meta.ValueType;
//@Generator.Template
abstract class OkJsons extends ValuesTemplate {
@Generator.Typedef
OkTypeAdapterTypes Adapted;
@Generator.Typedef
AttributesByFirstLetter Mm;
@Value.Immutable
abstract static class OkTypeAdapterTypes {
abstract AbstractDeclaring definedBy();
abstract String packageGenerated();
abstract List types();
@Value.Derived
EnumAllDefinitions enums() {
return new EnumAllDefinitions(types(), definedBy());
}
final Function decider = new Function() {
private final IdentityHashMap cache = Maps.newIdentityHashMap();
@Override
public AdapterDecider apply(ValueAttribute input) {
AdapterDecider d = cache.get(input);
if (d == null) {
d = new AdapterDecider(input, enums());
cache.put(input, d);
}
return d;
}
};
final Predicate requireAdapters = new Predicate() {
@Override
public boolean apply(@Nullable ValueType input) {
for (AdapterDecider d : Iterables.transform(input.allMarshalingAttributes(), decider)) {
if (d.useAdapter()) {
return true;
}
}
return false;
}
};
}
@Generator.Typedef
AdapterDecider Decider;
static class AdapterDecider {
final ValueAttribute attribute;
private final EnumAllDefinitions enums;
private final AdaptedUse adapterUse;
private final LocalEnumUse enumUse;
AdapterDecider(ValueAttribute attribute, EnumAllDefinitions enums) {
this.attribute = attribute;
this.enums = enums;
this.adapterUse = inferAdapter();
this.enumUse = inferEnum();
}
private enum AdaptedUse {
NONE, FULL, FIRST, SECOND, BOTH;
}
private enum LocalEnumUse {
NONE, FULL, FIRST, SECOND, BOTH;
}
private AdaptedUse inferAdapter() {
if (!attribute.getJsonQualiferAnnotations().isEmpty()) {
return AdaptedUse.FULL;
}
if (attribute.isOptionalType() || attribute.isCollectionType() || attribute.isArrayType()) {
if (attribute.isRequiresMarshalingAdapter()
&& !enums.definitionsByQualifiedName.containsKey(attribute.firstTypeParameter())) {
return AdaptedUse.FIRST;
}
return AdaptedUse.NONE;
}
if (attribute.isMapType()) {
if (attribute.isRequiresMarshalingAdapter()
&& !enums.definitionsByQualifiedName.containsKey(attribute.firstTypeParameter())) {
return AdaptedUse.FIRST;
}
if (attribute.isRequiresMarshalingSecondaryAdapter()
&& !enums.definitionsByQualifiedName.containsKey(attribute.secondTypeParameter())) {
return AdaptedUse.SECOND;
}
return AdaptedUse.NONE;
}
if (attribute.isRequiresMarshalingAdapter()
&& !enums.definitionsByQualifiedName.containsKey(attribute.getType())) {
return AdaptedUse.FULL;
}
return AdaptedUse.NONE;
}
private LocalEnumUse inferEnum() {
if (adapterUse == AdaptedUse.FULL) {
return LocalEnumUse.NONE;
}
if (adapterUse != AdaptedUse.FIRST && adapterUse != AdaptedUse.BOTH) {
if (attribute.isOptionalType() || attribute.isCollectionType() || attribute.isArrayType()) {
if (attribute.isRequiresMarshalingAdapter()
&& enums.definitionsByQualifiedName.containsKey(attribute.firstTypeParameter())) {
return LocalEnumUse.FIRST;
}
return LocalEnumUse.NONE;
}
}
if (adapterUse != AdaptedUse.BOTH) {
if (attribute.isMapType()) {
if (attribute.isRequiresMarshalingAdapter()
&& enums.definitionsByQualifiedName.containsKey(attribute.firstTypeParameter())) {
if (adapterUse != AdaptedUse.SECOND) {
if (attribute.isRequiresMarshalingSecondaryAdapter()
&& enums.definitionsByQualifiedName.containsKey(attribute.secondTypeParameter())) {
return LocalEnumUse.BOTH;
}
}
return LocalEnumUse.FIRST;
}
if (adapterUse != AdaptedUse.SECOND) {
if (attribute.isRequiresMarshalingSecondaryAdapter()
&& enums.definitionsByQualifiedName.containsKey(attribute.secondTypeParameter())) {
return LocalEnumUse.SECOND;
}
}
return LocalEnumUse.NONE;
}
}
if (attribute.isRequiresMarshalingAdapter()
&& !enums.definitionsByQualifiedName.containsKey(attribute.getType())) {
return LocalEnumUse.FULL;
}
return LocalEnumUse.NONE;
}
public boolean isSimple() {
return adapterUse == AdaptedUse.FULL
|| enumUse == LocalEnumUse.FULL
|| attribute.typeKind().isRegular();
}
public boolean isComplex() {
return !isSimple();
}
boolean useEnum() {
return enumUse != LocalEnumUse.NONE;
}
@Nullable
EnumDefinition enumFull() {
if (enumUse == LocalEnumUse.FULL) {
return enums.apply(attribute.getType());
}
return null;
}
@Nullable
EnumDefinition enumFirst() {
if (enumUse == LocalEnumUse.FIRST || enumUse == LocalEnumUse.BOTH) {
return enums.apply(attribute.firstTypeParameter());
}
return null;
}
@Nullable
EnumDefinition enumSecond() {
if (enumUse == LocalEnumUse.SECOND || enumUse == LocalEnumUse.BOTH) {
return enums.apply(attribute.secondTypeParameter());
}
return null;
}
boolean useAdapter() {
return adapterUse != AdaptedUse.NONE;
}
@Nullable
AdapterSpecifier adapterFull() {
if (adapterUse == AdaptedUse.FULL) {
if (!attribute.getJsonQualiferAnnotations().isEmpty()) {
return new AdapterSpecifier(attribute.getType(), true);
}
return AdapterSpecifier.from(attribute.getType());
}
return null;
}
@Nullable
AdapterSpecifier adapterFirst() {
if (adapterUse == AdaptedUse.FIRST || adapterUse == AdaptedUse.BOTH) {
return AdapterSpecifier.from(attribute.firstTypeParameter());
}
return null;
}
@Nullable
AdapterSpecifier adapterSecond() {
if (adapterUse == AdaptedUse.SECOND || adapterUse == AdaptedUse.BOTH) {
return AdapterSpecifier.from(attribute.secondTypeParameter());
}
return null;
}
static class AdapterSpecifier {
final String type;
final boolean reflect;
AdapterSpecifier(String type, boolean reflect) {
this.type = type;
this.reflect = reflect;
}
static AdapterSpecifier from(String type) {
return new AdapterSpecifier(type, type.indexOf('<') > 0);
}
}
}
OkTypeAdapterTypes adapted;
final Function setCurrent = new Function() {
@Override
public Void apply(OkTypeAdapterTypes input) {
adapted = input;
return null;
}
};
Iterable typeAdapters() {
Multimap byDeclaring = HashMultimap.create();
for (ValueType value : values.values()) {
Protoclass protoclass = value.constitution.protoclass();
if (protoclass.kind().isValue()) {
Optional typeAdaptersProvider = protoclass.okTypeAdaptersProvider();
if (typeAdaptersProvider.isPresent()) {
byDeclaring.put(typeAdaptersProvider.get(), value);
} else if (protoclass.okJsonTypeAdapters().isPresent()
&& protoclass.declaringType().isPresent()) {
DeclaringType topLevel = protoclass.declaringType().get().associatedTopLevel();
byDeclaring.put(topLevel, value);
}
}
}
ImmutableList.Builder builder = ImmutableList.builder();
for (Entry> entry : byDeclaring.asMap().entrySet()) {
String pack = Iterables.get(entry.getValue(), 0).$$package();
builder.add(ImmutableOkTypeAdapterTypes.builder()
.definedBy(entry.getKey())
.packageGenerated(pack)
.addAllTypes(entry.getValue())
.build());
}
return builder.build();
}
static class AttributesByFirstLetter {
final Multimap byFirst;
final Map> asMap;
AttributesByFirstLetter(Iterable attributes) {
ImmutableMultimap.Builder builder = ImmutableMultimap.builder();
for (ValueAttribute attribute : attributes) {
String name = attribute.getMarshaledName();
char firstChar = name.charAt(0);
builder.put(firstChar, attribute);
}
byFirst = builder.build();
asMap = byFirst.asMap();
}
Collection values() {
return byFirst.values();
}
boolean useFlatIfElse() {
return byFirst.keySet().size() < 4
|| byFirst.values().size() < 4;
}
}
final Function, AttributesByFirstLetter> byFirstCharacter =
new Function, AttributesByFirstLetter>() {
@Override
public AttributesByFirstLetter apply(Iterable attributes) {
return new AttributesByFirstLetter(attributes);
}
};
@Generator.Typedef
EnumAllDefinitions EnumAllDefinitions;
@Generator.Typedef
EnumDefinition EnumDefinition;
@Generator.Typedef
EnumConstant EnumConstant;
static class EnumAllDefinitions implements Function {
final Map definitionsByQualifiedName = Maps.newHashMap();
final Set takenSimpleNames = Sets.newHashSet();
EnumAllDefinitions(List types, AbstractDeclaring declaring) {
for (ValueType t : types) {
for (ValueAttribute v : t.getMarshaledAttributes()) {
addEnumDefintions(declaring, v, ConvertionDirection.TO_STRING);
}
for (ValueAttribute v : t.getUnmarshaledAttributes()) {
addEnumDefintions(declaring, v, ConvertionDirection.FROM_STRING);
}
}
}
private void addEnumDefintions(
AbstractDeclaring declaring,
ValueAttribute v,
ConvertionDirection direction) {
if (!v.getJsonQualiferAnnotations().isEmpty()) {
// don't handle qualified enums as they should be
// resolved using adapters etc
return;
}
for (TypeElement e : v.getEnumElements()) {
String qualified = e.getQualifiedName().toString();
if (inDefinitionCoveredByAdapter(declaring, qualified)) {
@Nullable EnumDefinition definition = definitionsByQualifiedName.get(qualified);
if (definition == null) {
String simple = unambigousSimpleName(takenSimpleNames, e);
definition = new EnumDefinition(e, qualified, simple);
definitionsByQualifiedName.put(qualified, definition);
}
definition.conversions.add(direction);
}
}
}
private boolean inDefinitionCoveredByAdapter(AbstractDeclaring declaring, String qualified) {
return qualified.startsWith(declaring.asPrefix());
}
private String unambigousSimpleName(Set simpleNames, TypeElement e) {
String base = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, e.getSimpleName().toString());
String simple = base;
for (int c = 0; !simpleNames.add(simple); c++) {
simple = base + c;
}
return simple;
}
Collection all() {
return definitionsByQualifiedName.values();
}
@Override
public EnumDefinition apply(String input) {
return definitionsByQualifiedName.get(input);
}
}
enum ConvertionDirection {
TO_STRING, FROM_STRING
}
static class EnumConstant {
final String name;
final String json;
EnumConstant(String name, String json) {
this.name = name;
this.json = json;
}
}
static class EnumDefinition {
final String qualified;
final String simple;
final Set conversions = EnumSet.noneOf(ConvertionDirection.class);
final Multimap byFirstLetter = HashMultimap.create();
EnumDefinition(TypeElement element, String qualified, String simple) {
this.qualified = qualified;
this.simple = simple;
for (Element e : element.getEnclosedElements()) {
if (e.getKind() == ElementKind.ENUM_CONSTANT) {
Optional nameAnnotation = OkNamedMirror.find(e);
String name = e.getSimpleName().toString();
String jsonName = name;
if (nameAnnotation.isPresent()) {
String s = nameAnnotation.get().name();
// just ignore annotation with empty name
if (!s.isEmpty()) {
jsonName = s;
}
}
byFirstLetter.put(
jsonName.charAt(0),
new EnumConstant(name, jsonName));
}
}
}
boolean useFlatIfElse() {
return byFirstLetter.keySet().size() < 4
|| byFirstLetter.values().size() < 4;
}
Collection constants() {
return byFirstLetter.values();
}
boolean useToString() {
return conversions.contains(ConvertionDirection.TO_STRING);
}
boolean useFromString() {
return conversions.contains(ConvertionDirection.FROM_STRING);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy