JsonNameUtil.java
/*
* Copyright 2022 Proto Utils Authors
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package net.morimekta.proto.utils;
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;
import com.google.protobuf.ProtocolMessageEnum;
import net.morimekta.collect.SetBuilder;
import net.morimekta.collect.UnmodifiableSet;
import net.morimekta.collect.util.Pair;
import net.morimekta.proto.ProtoEnum;
import net.morimekta.proto.ProtoMessage;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import static net.morimekta.collect.UnmodifiableMap.toMap;
import static net.morimekta.collect.UnmodifiableSet.setOf;
import static net.morimekta.collect.util.SetOperations.product;
import static net.morimekta.proto.ProtoEnum.getEnumDescriptor;
/**
* Utilities for handling JSON names.
*/
public final class JsonNameUtil {
/**
* @param descriptor A proto descriptor.
* @param <E> The proto enum value type.
* @return A map from JSON names to enum value.
*/
@SuppressWarnings("unchecked,rawtypes")
public static <E extends Enum<E> & ProtocolMessageEnum>
Map<String, E> getJsonEnumMap(ProtoEnum<E> descriptor) {
return (Map<String, E>) (Map) enumNameMaps.computeIfAbsent(
descriptor,
d -> descriptor
.allValues()
.stream()
.flatMap(e -> product(getJsonEnumNames(e), setOf(e)).stream())
.collect(toMap(Pair::getFirst, Pair::getSecond)));
}
/**
* @param type A proto type class.
* @param <E> The proto enum value type.
* @return A map from JSON names to enum value.
*/
public static <E extends Enum<E> & ProtocolMessageEnum>
Map<String, E> getJsonEnumMap(Class<E> type) {
return getJsonEnumMap(getEnumDescriptor(type));
}
/**
* @param type A message type class.
* @param <M> The message type.
* @return A map of JSON field names to field for.
*/
public static <M extends Message>
Map<String, Descriptors.FieldDescriptor> getJsonFieldMap(Class<M> type) {
return getJsonFieldMap(ProtoMessage.getMessageDescriptor(type));
}
/**
* @param descriptor A message descriptor.
* @return A map of JSON field names to field for.
*/
public static Map<String, Descriptors.FieldDescriptor> getJsonFieldMap(Descriptors.Descriptor descriptor) {
return fieldNameMaps.computeIfAbsent(
descriptor,
d -> descriptor.getFields()
.stream()
.flatMap(f -> product(getJsonFieldNames(f), setOf(f)).stream())
.collect(toMap(p -> p.first, p -> p.second)));
}
/**
* @param value An enum value.
* @return The default JSON name for the value.
*/
public static String getJsonEnumName(ProtocolMessageEnum value) {
return value.getValueDescriptor().getName();
}
/**
* @param field An field descriptor.
* @return The default JSON name for the field.
*/
public static String getJsonFieldName(Descriptors.FieldDescriptor field) {
if (field.isExtension()) {
return field.getFullName();
} else {
return field.getName();
}
}
/**
* @param value A proto enum value.
* @return A set of possible JSON names for the enum value.
*/
public static Set<String> getJsonEnumNames(ProtocolMessageEnum value) {
SetBuilder<String> names = UnmodifiableSet.newBuilder();
names.add(value.getValueDescriptor().getName());
if (value.getDescriptorForType().getOptions().getAllowAlias()) {
for (Descriptors.EnumValueDescriptor desc : value.getDescriptorForType().getValues()) {
if (desc.getNumber() == value.getNumber()) {
names.add(desc.getName());
}
}
}
return names.build();
}
/**
* @param field An field descriptor.
* @return The set of possible JSON names for the field.
*/
public static Set<String> getJsonFieldNames(Descriptors.FieldDescriptor field) {
SetBuilder<String> names = UnmodifiableSet.newBuilder();
names.add(field.getName());
names.add(field.getJsonName());
return names.build();
}
/**
* A set of field names for the type on serialized unwrapped any types.
*/
public static final Set<String> ANY_TYPE_FIELDS = setOf("@type", "__type");
/**
* Pattern used to detect numeric field ID string.
*/
public static final Pattern NUMERIC_FIELD_ID = Pattern.compile("^[1-9][0-9]{0,8}$");
// ---- Private ----
private static final Map<ProtoEnum<?>, Map<String, Enum<?>>> enumNameMaps = new ConcurrentHashMap<>();
private static final Map<Descriptors.Descriptor, Map<String, Descriptors.FieldDescriptor>> fieldNameMaps = new ConcurrentHashMap<>();
private JsonNameUtil() {
}
}