diff --git a/WORKSPACE b/WORKSPACE
index 15fdf8f20..1c1e03c59 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -242,11 +242,11 @@ http_archive(
)
# cel-spec api/expr canonical protos
-CEL_SPEC_VERSION = "0.20.0"
+CEL_SPEC_VERSION = "0.22.1"
http_archive(
name = "cel_spec",
- sha256 = "9f4acb83116f68af8a6b6acf700561a22a1bd8a9ad2f49bf642b7f9b8f285043",
+ sha256 = "1f1ad32bce5d31cf82e9c8f40685b1902de3ab07c78403601e7a43c3fb4de9a6",
strip_prefix = "cel-spec-" + CEL_SPEC_VERSION,
urls = [
"https://github.com/google/cel-spec/archive/" +
diff --git a/common/internal/BUILD.bazel b/common/internal/BUILD.bazel
index 63bff51d9..d93121150 100644
--- a/common/internal/BUILD.bazel
+++ b/common/internal/BUILD.bazel
@@ -95,6 +95,10 @@ java_library(
cel_android_library(
name = "internal_android",
- visibility = ["//:android_allow_list"],
exports = ["//common/src/main/java/dev/cel/common/internal:internal_android"],
)
+
+java_library(
+ name = "proto_java_qualified_names",
+ exports = ["//common/src/main/java/dev/cel/common/internal:proto_java_qualified_names"],
+)
diff --git a/common/src/main/java/dev/cel/common/internal/BUILD.bazel b/common/src/main/java/dev/cel/common/internal/BUILD.bazel
index bca6ec303..6aa3c4de8 100644
--- a/common/src/main/java/dev/cel/common/internal/BUILD.bazel
+++ b/common/src/main/java/dev/cel/common/internal/BUILD.bazel
@@ -309,6 +309,7 @@ java_library(
tags = [
],
deps = [
+ "//common/annotations",
"@maven//:com_google_guava_guava",
"@maven//:com_google_protobuf_protobuf_java",
],
diff --git a/common/src/main/java/dev/cel/common/internal/ProtoJavaQualifiedNames.java b/common/src/main/java/dev/cel/common/internal/ProtoJavaQualifiedNames.java
index 9c2ba049e..a16abb8fc 100644
--- a/common/src/main/java/dev/cel/common/internal/ProtoJavaQualifiedNames.java
+++ b/common/src/main/java/dev/cel/common/internal/ProtoJavaQualifiedNames.java
@@ -24,10 +24,16 @@
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.GenericDescriptor;
import com.google.protobuf.Descriptors.ServiceDescriptor;
+import dev.cel.common.annotations.Internal;
import java.util.ArrayDeque;
-/** Helper class for constructing a fully qualified Java class name from a protobuf descriptor. */
-final class ProtoJavaQualifiedNames {
+/**
+ * Helper class for constructing a fully qualified Java class name from a protobuf descriptor. * *
+ *
+ *
CEL Library Internals. Do Not Use.
+ */
+@Internal
+public final class ProtoJavaQualifiedNames {
// Controls how many times we should recursively inspect a nested message for building fully
// qualified java class name before aborting.
private static final int SAFE_RECURSE_LIMIT = 50;
@@ -45,6 +51,10 @@ public static String getFullyQualifiedJavaClassName(Descriptor descriptor) {
return getFullyQualifiedJavaClassNameImpl(descriptor);
}
+ public static String getFullyQualifiedJavaClassName(EnumDescriptor descriptor) {
+ return getFullyQualifiedJavaClassNameImpl(descriptor);
+ }
+
private static String getFullyQualifiedJavaClassNameImpl(GenericDescriptor descriptor) {
StringBuilder fullClassName = new StringBuilder();
diff --git a/java_lite_proto_cel_library.bzl b/java_lite_proto_cel_library.bzl
new file mode 100644
index 000000000..2f7f6a876
--- /dev/null
+++ b/java_lite_proto_cel_library.bzl
@@ -0,0 +1,99 @@
+# Copyright 2025 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Starlark rule for generating descriptors that is compatible with Protolite Messages."""
+
+load("@rules_java//java:defs.bzl", "java_library")
+load("@rules_proto//proto:defs.bzl", "proto_descriptor_set")
+load("//publish:cel_version.bzl", "CEL_VERSION")
+
+def java_lite_proto_cel_library(
+ name,
+ java_descriptor_class_prefix,
+ deps,
+ debug = False):
+ """Generates a CelLiteDescriptor
+
+ Args:
+ name: name of this target.
+ java_descriptor_class_prefix: Prefix name for the generated descriptor java class (ex: 'TestAllTypes' generates 'TestAllTypesCelLiteDescriptor.java').
+ deps: Name of the proto_library target. Only a single proto_library is supported at this time.
+ debug: (optional) If true, prints additional information during codegen for debugging purposes.
+ """
+ if not name:
+ fail("You must provide a name.")
+
+ if not java_descriptor_class_prefix:
+ fail("You must provide a descriptor_class_prefix.")
+
+ if not deps:
+ fail("You must provide a proto_library dependency.")
+
+ if len(deps) > 1:
+ fail("You must provide only one proto_library dependency.")
+
+ _generate_cel_lite_descriptor_class(
+ name,
+ java_descriptor_class_prefix + "CelLiteDescriptor",
+ deps[0],
+ debug,
+ )
+
+ descriptor_codegen_deps = [
+ "//protobuf:cel_lite_descriptor",
+ ]
+
+ java_library(
+ name = name,
+ srcs = [":" + name + "_cel_lite_descriptor"],
+ deps = deps + descriptor_codegen_deps,
+ )
+
+def _generate_cel_lite_descriptor_class(
+ name,
+ descriptor_class_name,
+ proto_src,
+ debug):
+ outfile = "%s.java" % descriptor_class_name
+
+ transitive_descriptor_set_name = "%s_transitive_descriptor_set" % name
+ proto_descriptor_set(
+ name = transitive_descriptor_set_name,
+ deps = [proto_src],
+ )
+
+ direct_descriptor_set_name = proto_src
+
+ debug_flag = "--debug" if debug else ""
+
+ cmd = (
+ "$(location //protobuf:cel_lite_descriptor_generator) " +
+ "--descriptor $(location %s) " % direct_descriptor_set_name +
+ "--transitive_descriptor_set $(location %s) " % transitive_descriptor_set_name +
+ "--descriptor_class_name %s " % descriptor_class_name +
+ "--out $(location %s) " % outfile +
+ "--version %s " % CEL_VERSION +
+ debug_flag
+ )
+
+ native.genrule(
+ name = name + "_cel_lite_descriptor",
+ srcs = [
+ transitive_descriptor_set_name,
+ direct_descriptor_set_name,
+ ],
+ cmd = cmd,
+ outs = [outfile],
+ tools = ["//protobuf:cel_lite_descriptor_generator"],
+ )
diff --git a/protobuf/BUILD.bazel b/protobuf/BUILD.bazel
new file mode 100644
index 000000000..8674d578b
--- /dev/null
+++ b/protobuf/BUILD.bazel
@@ -0,0 +1,17 @@
+load("@rules_java//java:defs.bzl", "java_library")
+
+package(
+ default_applicable_licenses = ["//:license"],
+ default_visibility = ["//:internal"], # TODO: Expose when ready
+)
+
+java_library(
+ name = "cel_lite_descriptor",
+ exports = ["//protobuf/src/main/java/dev/cel/protobuf:cel_lite_descriptor"],
+)
+
+alias(
+ name = "cel_lite_descriptor_generator",
+ actual = "//protobuf/src/main/java/dev/cel/protobuf:cel_lite_descriptor_generator",
+ visibility = ["//:internal"],
+)
diff --git a/protobuf/src/main/java/dev/cel/protobuf/BUILD.bazel b/protobuf/src/main/java/dev/cel/protobuf/BUILD.bazel
new file mode 100644
index 000000000..9df0bf2fe
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/BUILD.bazel
@@ -0,0 +1,77 @@
+load("@rules_java//java:defs.bzl", "java_binary", "java_library")
+
+package(
+ default_applicable_licenses = ["//:license"],
+ default_visibility = ["//protobuf:__pkg__"],
+)
+
+filegroup(
+ name = "cel_lite_descriptor_template_file",
+ srcs = ["templates/cel_lite_descriptor_template.txt"],
+ visibility = ["//visibility:private"],
+)
+
+java_binary(
+ name = "cel_lite_descriptor_generator",
+ srcs = ["CelLiteDescriptorGenerator.java"],
+ main_class = "dev.cel.protobuf.CelLiteDescriptorGenerator",
+ deps = [
+ ":debug_printer",
+ ":java_file_generator",
+ ":proto_descriptor_collector",
+ "//common:cel_descriptors",
+ "//common/internal:proto_java_qualified_names",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ "@maven//:info_picocli_picocli",
+ ],
+)
+
+java_library(
+ name = "proto_descriptor_collector",
+ srcs = ["ProtoDescriptorCollector.java"],
+ deps = [
+ ":cel_lite_descriptor",
+ ":debug_printer",
+ "//common:cel_descriptors",
+ "//common/internal:proto_java_qualified_names",
+ "//common/internal:well_known_proto",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
+
+java_library(
+ name = "java_file_generator",
+ srcs = ["JavaFileGenerator.java"],
+ resources = [
+ ":cel_lite_descriptor_template_file",
+ ],
+ deps = [
+ ":cel_lite_descriptor",
+ "//:auto_value",
+ "@maven//:com_google_guava_guava",
+ "@maven//:org_freemarker_freemarker",
+ ],
+)
+
+java_library(
+ name = "debug_printer",
+ srcs = ["DebugPrinter.java"],
+ deps = [
+ "@maven//:info_picocli_picocli",
+ ],
+)
+
+java_library(
+ name = "cel_lite_descriptor",
+ srcs = ["CelLiteDescriptor.java"],
+ tags = [
+ ],
+ deps = [
+ "//common/annotations",
+ "@maven//:com_google_errorprone_error_prone_annotations",
+ "@maven//:com_google_guava_guava",
+ "@maven//:com_google_protobuf_protobuf_java",
+ ],
+)
diff --git a/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java b/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java
new file mode 100644
index 000000000..16fe66f7b
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptor.java
@@ -0,0 +1,410 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.protobuf;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.Math.ceil;
+
+import com.google.errorprone.annotations.Immutable;
+import com.google.protobuf.ByteString;
+import dev.cel.common.annotations.Internal;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for code generated CEL lite descriptors to extend from.
+ *
+ *
CEL Library Internals. Do Not Use.
+ */
+@Internal
+@Immutable
+public abstract class CelLiteDescriptor {
+ @SuppressWarnings("Immutable") // Copied to unmodifiable map
+ private final Map protoFqnToDescriptors;
+
+ @SuppressWarnings("Immutable") // Copied to unmodifiable map
+ private final Map protoJavaClassNameToDescriptors;
+
+ public Map getProtoTypeNamesToDescriptors() {
+ return protoFqnToDescriptors;
+ }
+
+ public Map getProtoJavaClassNameToDescriptors() {
+ return protoJavaClassNameToDescriptors;
+ }
+
+ /**
+ * Contains a collection of classes which describe protobuf messagelite types.
+ *
+ * CEL Library Internals. Do Not Use.
+ */
+ @Internal
+ @Immutable
+ public static final class MessageLiteDescriptor {
+ private final String fullyQualifiedProtoTypeName;
+ private final String fullyQualifiedProtoJavaClassName;
+
+ @SuppressWarnings("Immutable") // Copied to unmodifiable map
+ private final Map fieldInfoMap;
+
+ public String getFullyQualifiedProtoTypeName() {
+ return fullyQualifiedProtoTypeName;
+ }
+
+ public String getFullyQualifiedProtoJavaClassName() {
+ return fullyQualifiedProtoJavaClassName;
+ }
+
+ public Map getFieldInfoMap() {
+ return fieldInfoMap;
+ }
+
+ public MessageLiteDescriptor(
+ String fullyQualifiedProtoTypeName,
+ String fullyQualifiedProtoJavaClassName,
+ Map fieldInfoMap) {
+ this.fullyQualifiedProtoTypeName = checkNotNull(fullyQualifiedProtoTypeName);
+ this.fullyQualifiedProtoJavaClassName = checkNotNull(fullyQualifiedProtoJavaClassName);
+ // This is a cheap operation. View over the existing map with mutators disabled.
+ this.fieldInfoMap = checkNotNull(Collections.unmodifiableMap(fieldInfoMap));
+ }
+ }
+
+ /**
+ * Describes a field of a protobuf messagelite type.
+ *
+ * CEL Library Internals. Do Not Use.
+ */
+ @Internal
+ @Immutable
+ public static final class FieldDescriptor {
+ private final JavaType javaType;
+ private final String fieldJavaClassName;
+ private final String fieldProtoTypeName;
+ private final String fullyQualifiedProtoFieldName;
+ private final String methodSuffixName;
+ private final Type protoFieldType;
+ private final CelFieldValueType celFieldValueType;
+ private final boolean hasHasser;
+
+ /**
+ * Enumeration of the CEL field value type. This is analogous to the following from field
+ * descriptors:
+ *
+ *
+ * - LIST: Repeated Field
+ *
- MAP: Map Field
+ *
- SCALAR: Neither of above (scalars, messages)
+ *
+ */
+ public enum CelFieldValueType {
+ SCALAR,
+ LIST,
+ MAP
+ }
+
+ /**
+ * Enumeration of the java type.
+ *
+ * This is exactly the same as com.google.protobuf.Descriptors#JavaType
+ */
+ public enum JavaType {
+ INT,
+ LONG,
+ FLOAT,
+ DOUBLE,
+ BOOLEAN,
+ STRING,
+ BYTE_STRING,
+ ENUM,
+ MESSAGE
+ }
+
+ /**
+ * Enumeration of the protobuf type.
+ *
+ *
This is exactly the same as com.google.protobuf.Descriptors#Type
+ */
+ public enum Type {
+ DOUBLE,
+ FLOAT,
+ INT64,
+ UINT64,
+ INT32,
+ FIXED64,
+ FIXED32,
+ BOOL,
+ STRING,
+ GROUP,
+ MESSAGE,
+ BYTES,
+ UINT32,
+ ENUM,
+ SFIXED32,
+ SFIXED64,
+ SINT32,
+ SINT64
+ }
+
+ // Lazily-loaded field
+ @SuppressWarnings("Immutable")
+ private volatile Class> fieldJavaClass;
+
+ /**
+ * Returns the {@link Class} object for this field. In case of protobuf messages, the class
+ * object is lazily loaded then memoized.
+ */
+ public Class> getFieldJavaClass() {
+ if (fieldJavaClass == null) {
+ synchronized (this) {
+ if (fieldJavaClass == null) {
+ fieldJavaClass = loadNonPrimitiveFieldTypeClass();
+ }
+ }
+ }
+ return fieldJavaClass;
+ }
+
+ /**
+ * Gets the field's java type.
+ *
+ *
This is exactly the same as com.google.protobuf.Descriptors#JavaType
+ */
+ public JavaType getJavaType() {
+ return javaType;
+ }
+
+ /**
+ * Returns the method suffix name as part of getters or setters of the field in the protobuf
+ * message's builder. (Ex: for a field named single_string, "SingleString" is returned).
+ */
+ public String getMethodSuffixName() {
+ return methodSuffixName;
+ }
+
+ /**
+ * Returns the setter name for the field used in protobuf message's builder (Ex:
+ * setSingleString).
+ */
+ public String getSetterName() {
+ String prefix = "";
+ switch (celFieldValueType) {
+ case SCALAR:
+ prefix = "set";
+ break;
+ case LIST:
+ prefix = "addAll";
+ break;
+ case MAP:
+ prefix = "putAll";
+ break;
+ }
+ return prefix + getMethodSuffixName();
+ }
+
+ /**
+ * Returns the getter name for the field used in protobuf message's builder (Ex:
+ * getSingleString).
+ */
+ public String getGetterName() {
+ String suffix = "";
+ switch (celFieldValueType) {
+ case SCALAR:
+ break;
+ case LIST:
+ suffix = "List";
+ break;
+ case MAP:
+ suffix = "Map";
+ break;
+ }
+ return "get" + getMethodSuffixName() + suffix;
+ }
+
+ /**
+ * Returns the hasser name for the field (Ex: hasSingleString).
+ *
+ * @throws IllegalArgumentException If the message does not have a hasser.
+ */
+ public String getHasserName() {
+ if (!getHasHasser()) {
+ throw new IllegalArgumentException("This message does not have a hasser.");
+ }
+ return "has" + getMethodSuffixName();
+ }
+
+ /**
+ * Returns the fully qualified java class name for the underlying field. (Ex:
+ * com.google.protobuf.StringValue). Returns an empty string for primitives .
+ */
+ public String getFieldJavaClassName() {
+ return fieldJavaClassName;
+ }
+
+ public CelFieldValueType getCelFieldValueType() {
+ return celFieldValueType;
+ }
+
+ /**
+ * Gets the field's protobuf type.
+ *
+ *
This is exactly the same as com.google.protobuf.Descriptors#Type
+ */
+ public Type getProtoFieldType() {
+ return protoFieldType;
+ }
+
+ public boolean getHasHasser() {
+ return hasHasser && celFieldValueType.equals(CelFieldValueType.SCALAR);
+ }
+
+ /**
+ * Gets the fully qualified protobuf message field name, including its package name (ex:
+ * cel.expr.conformance.proto3.TestAllTypes.single_string)
+ */
+ public String getFullyQualifiedProtoFieldName() {
+ return fullyQualifiedProtoFieldName;
+ }
+
+ /**
+ * Gets the fully qualified protobuf type name for the field, including its package name (ex:
+ * cel.expr.conformance.proto3.TestAllTypes.SingleStringWrapper). Returns an empty string for
+ * primitives.
+ */
+ public String getFieldProtoTypeName() {
+ return fieldProtoTypeName;
+ }
+
+ /**
+ * Must be public, used for codegen only. Do not use.
+ *
+ * @param fullyQualifiedProtoTypeName Fully qualified protobuf type name including the namespace
+ * (ex: cel.expr.conformance.proto3.TestAllTypes)
+ * @param javaTypeName Canonical Java type name (ex: Long, Double, Float, Message... see
+ * Descriptors#JavaType)
+ * @param methodSuffixName Suffix used to decorate the getters/setters (eg: "foo" in "setFoo"
+ * and "getFoo")
+ * @param celFieldValueType Describes whether the field is a scalar, list or a map with respect
+ * to CEL.
+ * @param protoFieldType Protobuf Field Type (ex: INT32, SINT32, GROUP, MESSAGE... see
+ * Descriptors#Type)
+ * @param hasHasser True if the message has a presence test method (ex: wrappers).
+ * @param fieldJavaClassName Fully qualified Java class name for the field, including its
+ * package name. Empty if the field is a primitive.
+ * @param fieldProtoTypeName Fully qualified protobuf type name for the field. Empty if the
+ * field is a primitive.
+ */
+ @Internal
+ public FieldDescriptor(
+ String fullyQualifiedProtoTypeName,
+ String javaTypeName,
+ String methodSuffixName,
+ String celFieldValueType, // LIST, MAP, SCALAR
+ String protoFieldType, // INT32, SINT32, GROUP, MESSAGE... (See Descriptors#Type)
+ String hasHasser, //
+ String fieldJavaClassName,
+ String fieldProtoTypeName) {
+ this.fullyQualifiedProtoFieldName = checkNotNull(fullyQualifiedProtoTypeName);
+ this.javaType = JavaType.valueOf(javaTypeName);
+ this.methodSuffixName = checkNotNull(methodSuffixName);
+ this.fieldJavaClassName = checkNotNull(fieldJavaClassName);
+ this.celFieldValueType = CelFieldValueType.valueOf(checkNotNull(celFieldValueType));
+ this.protoFieldType = Type.valueOf(protoFieldType);
+ this.hasHasser = Boolean.parseBoolean(hasHasser);
+ this.fieldProtoTypeName = checkNotNull(fieldProtoTypeName);
+ this.fieldJavaClass = getPrimitiveFieldTypeClass();
+ }
+
+ @SuppressWarnings("ReturnMissingNullable") // Avoid taking a dependency on jspecify.nullable.
+ private Class> getPrimitiveFieldTypeClass() {
+ switch (celFieldValueType) {
+ case LIST:
+ return Iterable.class;
+ case MAP:
+ return Map.class;
+ case SCALAR:
+ return getScalarFieldTypeClass();
+ }
+
+ throw new IllegalStateException("Unexpected celFieldValueType: " + celFieldValueType);
+ }
+
+ @SuppressWarnings("ReturnMissingNullable") // Avoid taking a dependency on jspecify.nullable.
+ private Class> getScalarFieldTypeClass() {
+ switch (javaType) {
+ case INT:
+ return int.class;
+ case LONG:
+ return long.class;
+ case FLOAT:
+ return float.class;
+ case DOUBLE:
+ return double.class;
+ case BOOLEAN:
+ return boolean.class;
+ case STRING:
+ return String.class;
+ case BYTE_STRING:
+ return ByteString.class;
+ default:
+ // Non-primitives must be lazily loaded during instantiation of the runtime environment,
+ // where the generated messages are linked into the binary via java_lite_proto_library.
+ return null;
+ }
+ }
+
+ private Class> loadNonPrimitiveFieldTypeClass() {
+ if (!javaType.equals(JavaType.ENUM) && !javaType.equals(JavaType.MESSAGE)) {
+ throw new IllegalArgumentException("Unexpected java type name for " + javaType);
+ }
+
+ try {
+ return Class.forName(fieldJavaClassName);
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError(String.format("Could not find class %s", fieldJavaClassName), e);
+ }
+ }
+ }
+
+ protected CelLiteDescriptor(List messageInfoList) {
+ Map protoFqnMap =
+ new HashMap<>(getMapInitialCapacity(messageInfoList.size()));
+ Map protoJavaClassNameMap =
+ new HashMap<>(getMapInitialCapacity(messageInfoList.size()));
+ for (MessageLiteDescriptor msgInfo : messageInfoList) {
+ protoFqnMap.put(msgInfo.getFullyQualifiedProtoTypeName(), msgInfo);
+ protoJavaClassNameMap.put(msgInfo.getFullyQualifiedProtoJavaClassName(), msgInfo);
+ }
+
+ this.protoFqnToDescriptors = Collections.unmodifiableMap(protoFqnMap);
+ this.protoJavaClassNameToDescriptors = Collections.unmodifiableMap(protoJavaClassNameMap);
+ }
+
+ /**
+ * Returns a capacity that is sufficient to keep the map from being resized as long as it grows no
+ * larger than expectedSize and the load factor is ≥ its default (0.75).
+ */
+ private static int getMapInitialCapacity(int expectedSize) {
+ if (expectedSize < 3) {
+ return expectedSize + 1;
+ }
+
+ // See https://github.com/openjdk/jdk/commit/3e393047e12147a81e2899784b943923fc34da8e. 0.75 is
+ // used as a load factor.
+ return (int) ceil(expectedSize / 0.75);
+ }
+}
diff --git a/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptorGenerator.java b/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptorGenerator.java
new file mode 100644
index 000000000..479c69c63
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/CelLiteDescriptorGenerator.java
@@ -0,0 +1,156 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.protobuf;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.io.Files;
+import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
+import com.google.protobuf.DescriptorProtos.FileDescriptorSet;
+import com.google.protobuf.Descriptors.FileDescriptor;
+import com.google.protobuf.ExtensionRegistry;
+import dev.cel.common.CelDescriptorUtil;
+import dev.cel.common.internal.ProtoJavaQualifiedNames;
+import dev.cel.protobuf.JavaFileGenerator.JavaFileGeneratorOption;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import picocli.CommandLine;
+import picocli.CommandLine.Model.OptionSpec;
+import picocli.CommandLine.Option;
+
+final class CelLiteDescriptorGenerator implements Callable {
+
+ @Option(
+ names = {"--out"},
+ description = "Outpath for the CelLiteDescriptor")
+ private String outPath = "";
+
+ @Option(
+ names = {"--descriptor"},
+ description =
+ "Path to the descriptor (from proto_library) that the CelLiteDescriptor is to be"
+ + " generated from")
+ private String targetDescriptorPath = "";
+
+ @Option(
+ names = {"--transitive_descriptor_set"},
+ description = "Path to the transitive set of descriptors")
+ private String transitiveDescriptorSetPath = "";
+
+ @Option(
+ names = {"--descriptor_class_name"},
+ description = "Class name for the CelLiteDescriptor")
+ private String descriptorClassName = "";
+
+ @Option(
+ names = {"--version"},
+ description = "CEL-Java version")
+ private String version = "";
+
+ @Option(
+ names = {"--debug"},
+ description = "Prints debug output")
+ private boolean debug = false;
+
+ private DebugPrinter debugPrinter;
+
+ @Override
+ public Integer call() throws Exception {
+ String targetDescriptorProtoPath = extractProtoPath(targetDescriptorPath);
+ debugPrinter.print("Target descriptor proto path: " + targetDescriptorProtoPath);
+
+ FileDescriptor targetFileDescriptor = null;
+ ImmutableSet transitiveFileDescriptors =
+ CelDescriptorUtil.getFileDescriptorsFromFileDescriptorSet(
+ load(transitiveDescriptorSetPath));
+ for (FileDescriptor fd : transitiveFileDescriptors) {
+ if (fd.getFullName().equals(targetDescriptorProtoPath)) {
+ debugPrinter.print("Transitive Descriptor Path: " + fd.getFullName());
+ targetFileDescriptor = fd;
+ break;
+ }
+ }
+
+ if (targetFileDescriptor == null) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Target descriptor %s not found from transitive set of descriptors!",
+ targetDescriptorProtoPath));
+ }
+
+ codegenCelLiteDescriptor(targetFileDescriptor);
+
+ return 0;
+ }
+
+ private void codegenCelLiteDescriptor(FileDescriptor targetFileDescriptor) throws Exception {
+ String javaPackageName = ProtoJavaQualifiedNames.getJavaPackageName(targetFileDescriptor);
+ ProtoDescriptorCollector descriptorCollector =
+ ProtoDescriptorCollector.newInstance(debugPrinter);
+
+ JavaFileGenerator.createFile(
+ outPath,
+ JavaFileGeneratorOption.newBuilder()
+ .setVersion(version)
+ .setDescriptorClassName(descriptorClassName)
+ .setPackageName(javaPackageName)
+ .setMessageInfoList(descriptorCollector.collectMessageInfo(targetFileDescriptor))
+ .build());
+ }
+
+ private String extractProtoPath(String descriptorPath) {
+ FileDescriptorSet fds = load(descriptorPath);
+ FileDescriptorProto fileDescriptorProto = Iterables.getOnlyElement(fds.getFileList());
+ return fileDescriptorProto.getName();
+ }
+
+ private FileDescriptorSet load(String descriptorSetPath) {
+ try {
+ byte[] descriptorBytes = Files.toByteArray(new File(descriptorSetPath));
+ // TODO: Implement ProtoExtensions
+ return FileDescriptorSet.parseFrom(descriptorBytes, ExtensionRegistry.getEmptyRegistry());
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "Failed to load FileDescriptorSet from path: " + descriptorSetPath, e);
+ }
+ }
+
+ private void printAllFlags(CommandLine cmd) {
+ debugPrinter.print("Flag values:");
+ debugPrinter.print("-------------------------------------------------------------");
+ for (OptionSpec option : cmd.getCommandSpec().options()) {
+ debugPrinter.print(option.longestName() + ": " + option.getValue());
+ }
+ debugPrinter.print("-------------------------------------------------------------");
+ }
+
+ private void initializeDebugPrinter() {
+ this.debugPrinter = DebugPrinter.newInstance(debug);
+ }
+
+ public static void main(String[] args) {
+ CelLiteDescriptorGenerator celLiteDescriptorGenerator = new CelLiteDescriptorGenerator();
+ CommandLine cmd = new CommandLine(celLiteDescriptorGenerator);
+ cmd.parseArgs(args);
+ celLiteDescriptorGenerator.initializeDebugPrinter();
+ celLiteDescriptorGenerator.printAllFlags(cmd);
+
+ int exitCode = cmd.execute(args);
+ System.exit(exitCode);
+ }
+
+ CelLiteDescriptorGenerator() {}
+}
diff --git a/protobuf/src/main/java/dev/cel/protobuf/DebugPrinter.java b/protobuf/src/main/java/dev/cel/protobuf/DebugPrinter.java
new file mode 100644
index 000000000..34a09ce98
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/DebugPrinter.java
@@ -0,0 +1,36 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.protobuf;
+
+import picocli.CommandLine.Help.Ansi;
+
+final class DebugPrinter {
+
+ private final boolean debug;
+
+ static DebugPrinter newInstance(boolean debug) {
+ return new DebugPrinter(debug);
+ }
+
+ void print(String message) {
+ if (debug) {
+ System.out.println(Ansi.ON.string("@|cyan [CelLiteDescriptorGenerator] |@" + message));
+ }
+ }
+
+ private DebugPrinter(boolean debug) {
+ this.debug = debug;
+ }
+}
diff --git a/protobuf/src/main/java/dev/cel/protobuf/JavaFileGenerator.java b/protobuf/src/main/java/dev/cel/protobuf/JavaFileGenerator.java
new file mode 100644
index 000000000..ff6966a69
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/JavaFileGenerator.java
@@ -0,0 +1,96 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.protobuf;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+// CEL-Internal-5
+import dev.cel.protobuf.CelLiteDescriptor.MessageLiteDescriptor;
+import freemarker.template.Configuration;
+import freemarker.template.DefaultObjectWrapperBuilder;
+import freemarker.template.Template;
+import freemarker.template.TemplateException;
+import freemarker.template.Version;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+final class JavaFileGenerator {
+
+ private static final String HELPER_CLASS_TEMPLATE_FILE = "cel_lite_descriptor_template.txt";
+
+ public static void createFile(String filePath, JavaFileGeneratorOption option)
+ throws IOException, TemplateException {
+ Version version = Configuration.VERSION_2_3_32;
+ Configuration cfg = new Configuration(version);
+ cfg.setClassForTemplateLoading(JavaFileGenerator.class, "templates/");
+ cfg.setDefaultEncoding("UTF-8");
+ cfg.setBooleanFormat("c");
+ cfg.setAPIBuiltinEnabled(true);
+ DefaultObjectWrapperBuilder wrapperBuilder = new DefaultObjectWrapperBuilder(version);
+ wrapperBuilder.setExposeFields(true);
+ cfg.setObjectWrapper(wrapperBuilder.build());
+
+ Template template = cfg.getTemplate(HELPER_CLASS_TEMPLATE_FILE);
+ Writer out = new StringWriter();
+
+ template.process(option.getTemplateMap(), out);
+
+ Files.asCharSink(new File(filePath), UTF_8).write(out.toString());
+ }
+
+ @AutoValue
+ abstract static class JavaFileGeneratorOption {
+ abstract String packageName();
+
+ abstract String descriptorClassName();
+
+ abstract String version();
+
+ abstract ImmutableList messageInfoList();
+
+ ImmutableMap getTemplateMap() {
+ return ImmutableMap.of(
+ "package_name", packageName(),
+ "descriptor_class_name", descriptorClassName(),
+ "version", version(),
+ "message_info_list", messageInfoList());
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract Builder setPackageName(String packageName);
+
+ abstract Builder setDescriptorClassName(String className);
+
+ abstract Builder setVersion(String version);
+
+ abstract Builder setMessageInfoList(ImmutableList messageInfo);
+
+ abstract JavaFileGeneratorOption build();
+ }
+
+ static Builder newBuilder() {
+ return new AutoValue_JavaFileGenerator_JavaFileGeneratorOption.Builder();
+ }
+ }
+
+ private JavaFileGenerator() {}
+}
diff --git a/protobuf/src/main/java/dev/cel/protobuf/ProtoDescriptorCollector.java b/protobuf/src/main/java/dev/cel/protobuf/ProtoDescriptorCollector.java
new file mode 100644
index 000000000..6ffa414e3
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/ProtoDescriptorCollector.java
@@ -0,0 +1,129 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.protobuf;
+
+import static com.google.common.collect.ImmutableSet.toImmutableSet;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.protobuf.Descriptors;
+import com.google.protobuf.Descriptors.Descriptor;
+import com.google.protobuf.Descriptors.FileDescriptor;
+import dev.cel.common.CelDescriptorUtil;
+import dev.cel.common.CelDescriptors;
+import dev.cel.common.internal.ProtoJavaQualifiedNames;
+import dev.cel.common.internal.WellKnownProto;
+import dev.cel.protobuf.CelLiteDescriptor.FieldDescriptor;
+import dev.cel.protobuf.CelLiteDescriptor.FieldDescriptor.CelFieldValueType;
+import dev.cel.protobuf.CelLiteDescriptor.MessageLiteDescriptor;
+
+/**
+ * ProtoDescriptorCollector inspects a {@link FileDescriptor} to collect message information into
+ * {@link MessageLiteDescriptor}.
+ */
+final class ProtoDescriptorCollector {
+
+ private final DebugPrinter debugPrinter;
+
+ ImmutableList collectMessageInfo(FileDescriptor targetFileDescriptor) {
+ ImmutableList.Builder messageInfoListBuilder = ImmutableList.builder();
+ CelDescriptors celDescriptors =
+ CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
+ ImmutableList.of(targetFileDescriptor), /* resolveTypeDependencies= */ false);
+ ImmutableSet messageTypes =
+ celDescriptors.messageTypeDescriptors().stream()
+ .filter(d -> WellKnownProto.getByTypeName(d.getFullName()) == null)
+ .collect(toImmutableSet());
+
+ for (Descriptor descriptor : messageTypes) {
+ ImmutableMap.Builder fieldMap = ImmutableMap.builder();
+ for (Descriptors.FieldDescriptor fieldDescriptor : descriptor.getFields()) {
+ String methodSuffixName =
+ CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, fieldDescriptor.getName());
+
+ String javaType = fieldDescriptor.getJavaType().toString();
+ String embeddedFieldJavaClassName = "";
+ String embeddedFieldProtoTypeName = "";
+ switch (javaType) {
+ case "ENUM":
+ embeddedFieldJavaClassName =
+ ProtoJavaQualifiedNames.getFullyQualifiedJavaClassName(
+ fieldDescriptor.getEnumType());
+ embeddedFieldProtoTypeName = fieldDescriptor.getEnumType().getFullName();
+ break;
+ case "MESSAGE":
+ embeddedFieldJavaClassName =
+ ProtoJavaQualifiedNames.getFullyQualifiedJavaClassName(
+ fieldDescriptor.getMessageType());
+ embeddedFieldProtoTypeName = fieldDescriptor.getMessageType().getFullName();
+ break;
+ default:
+ break;
+ }
+
+ CelFieldValueType fieldValueType;
+ if (fieldDescriptor.isMapField()) {
+ fieldValueType = CelFieldValueType.MAP;
+ } else if (fieldDescriptor.isRepeated()) {
+ fieldValueType = CelFieldValueType.LIST;
+ } else {
+ fieldValueType = CelFieldValueType.SCALAR;
+ }
+
+ fieldMap.put(
+ fieldDescriptor.getName(),
+ new FieldDescriptor(
+ /* fullyQualifiedProtoTypeName= */ fieldDescriptor.getFullName(),
+ /* javaTypeName= */ javaType,
+ /* methodSuffixName= */ methodSuffixName,
+ /* celFieldValueType= */ fieldValueType.toString(),
+ /* protoFieldType= */ fieldDescriptor.getType().toString(),
+ /* hasHasser= */ String.valueOf(fieldDescriptor.hasPresence()),
+ /* fieldJavaClassName= */ embeddedFieldJavaClassName,
+ /* fieldProtoTypeName= */ embeddedFieldProtoTypeName));
+
+ debugPrinter.print(
+ String.format(
+ "Method suffix name in %s, for field %s: %s",
+ descriptor.getFullName(), fieldDescriptor.getFullName(), methodSuffixName));
+ debugPrinter.print(String.format("FieldType: %s", fieldValueType));
+ if (!embeddedFieldJavaClassName.isEmpty()) {
+ debugPrinter.print(
+ String.format(
+ "Java class name for field %s: %s",
+ fieldDescriptor.getName(), embeddedFieldJavaClassName));
+ }
+ }
+
+ messageInfoListBuilder.add(
+ new MessageLiteDescriptor(
+ descriptor.getFullName(),
+ ProtoJavaQualifiedNames.getFullyQualifiedJavaClassName(descriptor),
+ fieldMap.buildOrThrow()));
+ }
+
+ return messageInfoListBuilder.build();
+ }
+
+ static ProtoDescriptorCollector newInstance(DebugPrinter debugPrinter) {
+ return new ProtoDescriptorCollector(debugPrinter);
+ }
+
+ private ProtoDescriptorCollector(DebugPrinter debugPrinter) {
+ this.debugPrinter = debugPrinter;
+ }
+}
diff --git a/protobuf/src/main/java/dev/cel/protobuf/templates/cel_lite_descriptor_template.txt b/protobuf/src/main/java/dev/cel/protobuf/templates/cel_lite_descriptor_template.txt
new file mode 100644
index 000000000..9c65878d1
--- /dev/null
+++ b/protobuf/src/main/java/dev/cel/protobuf/templates/cel_lite_descriptor_template.txt
@@ -0,0 +1,70 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Generated by CEL-Java library. DO NOT EDIT!
+ * Version: ${version}
+ */
+
+package ${package_name};
+
+import dev.cel.protobuf.CelLiteDescriptor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public final class ${descriptor_class_name} extends CelLiteDescriptor {
+
+ private static final ${descriptor_class_name} DESCRIPTOR = new ${descriptor_class_name}();
+
+ public static ${descriptor_class_name} getDescriptor() {
+ return DESCRIPTOR;
+ }
+
+ private static List newDescriptors() {
+ List descriptors = new ArrayList<>(${message_info_list?size});
+ Map fieldDescriptors;
+ <#list message_info_list as message_info>
+
+ fieldDescriptors = new HashMap<>(${message_info.fieldInfoMap?size});
+ <#list message_info.fieldInfoMap as key, value>
+ fieldDescriptors.put("${key}", new FieldDescriptor(
+ "${value.fullyQualifiedProtoFieldName}",
+ "${value.javaType}",
+ "${value.methodSuffixName}",
+ "${value.celFieldValueType}",
+ "${value.protoFieldType}",
+ "${value.hasHasser}",
+ "${value.fieldJavaClassName}",
+ "${value.fieldProtoTypeName}"
+ ));
+ #list>
+
+ descriptors.add(
+ new MessageLiteDescriptor(
+ "${message_info.fullyQualifiedProtoTypeName}",
+ "${message_info.fullyQualifiedProtoJavaClassName}",
+ Collections.unmodifiableMap(fieldDescriptors))
+ );
+ #list>
+
+ return Collections.unmodifiableList(descriptors);
+ }
+
+ private ${descriptor_class_name}() {
+ super(newDescriptors());
+ }
+}
\ No newline at end of file
diff --git a/protobuf/src/test/java/dev/cel/protobuf/BUILD.bazel b/protobuf/src/test/java/dev/cel/protobuf/BUILD.bazel
new file mode 100644
index 000000000..9d6fe3699
--- /dev/null
+++ b/protobuf/src/test/java/dev/cel/protobuf/BUILD.bazel
@@ -0,0 +1,36 @@
+load("@rules_java//java:defs.bzl", "java_library")
+load("//:java_lite_proto_cel_library.bzl", "java_lite_proto_cel_library")
+load("//:testing.bzl", "junit4_test_suites")
+
+package(default_applicable_licenses = ["//:license"])
+
+java_library(
+ name = "tests",
+ testonly = 1,
+ srcs = glob(["*.java"]),
+ deps = [
+ ":test_all_types_proto3_java_lite_cel_proto",
+ "//:java_truth",
+ "//protobuf:cel_lite_descriptor",
+ "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_java_proto_lite",
+ "@maven//:com_google_testparameterinjector_test_parameter_injector",
+ "@maven//:junit_junit",
+ ],
+)
+
+java_lite_proto_cel_library(
+ name = "test_all_types_proto3_java_lite_cel_proto",
+ java_descriptor_class_prefix = "TestAllTypes",
+ deps = [
+ "@cel_spec//proto/cel/expr/conformance/proto3:test_all_types_proto",
+ ],
+)
+
+junit4_test_suites(
+ name = "test_suites",
+ sizes = [
+ "small",
+ ],
+ src_dir = "src/test/java",
+ deps = [":tests"],
+)
diff --git a/protobuf/src/test/java/dev/cel/protobuf/CelLiteDescriptorTest.java b/protobuf/src/test/java/dev/cel/protobuf/CelLiteDescriptorTest.java
new file mode 100644
index 000000000..69f0b573e
--- /dev/null
+++ b/protobuf/src/test/java/dev/cel/protobuf/CelLiteDescriptorTest.java
@@ -0,0 +1,307 @@
+// Copyright 2025 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.cel.protobuf;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import com.google.testing.junit.testparameterinjector.TestParameterInjector;
+import dev.cel.expr.conformance.proto3.TestAllTypes;
+import dev.cel.expr.conformance.proto3.TestAllTypesCelLiteDescriptor;
+import dev.cel.protobuf.CelLiteDescriptor.FieldDescriptor;
+import dev.cel.protobuf.CelLiteDescriptor.FieldDescriptor.CelFieldValueType;
+import dev.cel.protobuf.CelLiteDescriptor.FieldDescriptor.JavaType;
+import dev.cel.protobuf.CelLiteDescriptor.MessageLiteDescriptor;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(TestParameterInjector.class)
+public class CelLiteDescriptorTest {
+
+ private static final TestAllTypesCelLiteDescriptor TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR =
+ TestAllTypesCelLiteDescriptor.getDescriptor();
+
+ @Test
+ public void getProtoTypeNamesToDescriptors_containsAllMessages() {
+ Map protoNamesToDescriptors =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR.getProtoTypeNamesToDescriptors();
+
+ assertThat(protoNamesToDescriptors).hasSize(3);
+ assertThat(protoNamesToDescriptors).containsKey("cel.expr.conformance.proto3.TestAllTypes");
+ assertThat(protoNamesToDescriptors)
+ .containsKey("cel.expr.conformance.proto3.TestAllTypes.NestedMessage");
+ assertThat(protoNamesToDescriptors)
+ .containsKey("cel.expr.conformance.proto3.NestedTestAllTypes");
+ }
+
+ @Test
+ public void getDescriptors_fromProtoTypeAndJavaClassNames_referenceEquals() {
+ Map protoNamesToDescriptors =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR.getProtoTypeNamesToDescriptors();
+ Map javaClassNamesToDescriptors =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR.getProtoJavaClassNameToDescriptors();
+
+ assertThat(protoNamesToDescriptors.get("cel.expr.conformance.proto3.TestAllTypes"))
+ .isSameInstanceAs(
+ javaClassNamesToDescriptors.get("dev.cel.expr.conformance.proto3.TestAllTypes"));
+ assertThat(
+ protoNamesToDescriptors.get("cel.expr.conformance.proto3.TestAllTypes.NestedMessage"))
+ .isSameInstanceAs(
+ javaClassNamesToDescriptors.get(
+ "dev.cel.expr.conformance.proto3.TestAllTypes$NestedMessage"));
+ assertThat(protoNamesToDescriptors.get("cel.expr.conformance.proto3.NestedTestAllTypes"))
+ .isSameInstanceAs(
+ javaClassNamesToDescriptors.get("dev.cel.expr.conformance.proto3.NestedTestAllTypes"));
+ }
+
+ @Test
+ public void testAllTypesMessageLiteDescriptor_fullyQualifiedNames() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+
+ assertThat(testAllTypesDescriptor.getFullyQualifiedProtoTypeName())
+ .isEqualTo("cel.expr.conformance.proto3.TestAllTypes");
+ assertThat(testAllTypesDescriptor.getFullyQualifiedProtoJavaClassName())
+ .isEqualTo("dev.cel.expr.conformance.proto3.TestAllTypes");
+ }
+
+ @Test
+ public void testAllTypesMessageLiteDescriptor_fieldInfoMap_containsAllEntries() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+
+ assertThat(testAllTypesDescriptor.getFieldInfoMap()).hasSize(243);
+ }
+
+ @Test
+ public void fieldDescriptor_scalarField() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor = testAllTypesDescriptor.getFieldInfoMap().get("single_string");
+
+ assertThat(fieldDescriptor.getCelFieldValueType()).isEqualTo(CelFieldValueType.SCALAR);
+ assertThat(fieldDescriptor.getJavaType()).isEqualTo(JavaType.STRING);
+ assertThat(fieldDescriptor.getProtoFieldType()).isEqualTo(FieldDescriptor.Type.STRING);
+ }
+
+ @Test
+ public void fieldDescriptor_primitiveField_fullyQualifiedNames() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor = testAllTypesDescriptor.getFieldInfoMap().get("single_string");
+
+ assertThat(fieldDescriptor.getFullyQualifiedProtoFieldName())
+ .isEqualTo("cel.expr.conformance.proto3.TestAllTypes.single_string");
+ assertThat(fieldDescriptor.getFieldProtoTypeName()).isEmpty();
+ }
+
+ @Test
+ public void fieldDescriptor_primitiveField_getFieldJavaClass() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor = testAllTypesDescriptor.getFieldInfoMap().get("single_string");
+
+ assertThat(fieldDescriptor.getFieldJavaClass()).isEqualTo(String.class);
+ assertThat(fieldDescriptor.getFieldJavaClassName()).isEmpty();
+ }
+
+ @Test
+ public void fieldDescriptor_scalarField_builderMethods() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor = testAllTypesDescriptor.getFieldInfoMap().get("single_string");
+
+ assertThat(fieldDescriptor.getHasHasser()).isFalse();
+ assertThat(fieldDescriptor.getGetterName()).isEqualTo("getSingleString");
+ assertThat(fieldDescriptor.getSetterName()).isEqualTo("setSingleString");
+ }
+
+ @Test
+ public void fieldDescriptor_getHasserName_throwsIfNotWrapper() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor = testAllTypesDescriptor.getFieldInfoMap().get("single_string");
+
+ assertThrows(IllegalArgumentException.class, fieldDescriptor::getHasserName);
+ }
+
+ @Test
+ public void fieldDescriptor_getHasserName_success() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("single_string_wrapper");
+
+ assertThat(fieldDescriptor.getHasHasser()).isTrue();
+ assertThat(fieldDescriptor.getHasserName()).isEqualTo("hasSingleStringWrapper");
+ }
+
+ @Test
+ public void fieldDescriptor_mapField() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("map_bool_string");
+
+ assertThat(fieldDescriptor.getCelFieldValueType()).isEqualTo(CelFieldValueType.MAP);
+ assertThat(fieldDescriptor.getJavaType()).isEqualTo(JavaType.MESSAGE);
+ assertThat(fieldDescriptor.getProtoFieldType()).isEqualTo(FieldDescriptor.Type.MESSAGE);
+ }
+
+ @Test
+ public void fieldDescriptor_mapField_builderMethods() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("map_bool_string");
+
+ assertThat(fieldDescriptor.getHasHasser()).isFalse();
+ assertThat(fieldDescriptor.getGetterName()).isEqualTo("getMapBoolStringMap");
+ assertThat(fieldDescriptor.getSetterName()).isEqualTo("putAllMapBoolString");
+ }
+
+ @Test
+ public void fieldDescriptor_mapField_getFieldJavaClass() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("map_bool_string");
+
+ assertThat(fieldDescriptor.getFieldJavaClass()).isEqualTo(Map.class);
+ assertThat(fieldDescriptor.getFieldJavaClassName())
+ .isEqualTo("dev.cel.expr.conformance.proto3.TestAllTypes$MapBoolStringEntry");
+ }
+
+ @Test
+ public void fieldDescriptor_repeatedField() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("repeated_int64");
+
+ assertThat(fieldDescriptor.getCelFieldValueType()).isEqualTo(CelFieldValueType.LIST);
+ assertThat(fieldDescriptor.getJavaType()).isEqualTo(JavaType.LONG);
+ assertThat(fieldDescriptor.getProtoFieldType()).isEqualTo(FieldDescriptor.Type.INT64);
+ }
+
+ @Test
+ public void fieldDescriptor_repeatedField_builderMethods() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("repeated_int64");
+
+ assertThat(fieldDescriptor.getHasHasser()).isFalse();
+ assertThat(fieldDescriptor.getGetterName()).isEqualTo("getRepeatedInt64List");
+ assertThat(fieldDescriptor.getSetterName()).isEqualTo("addAllRepeatedInt64");
+ }
+
+ @Test
+ public void fieldDescriptor_repeatedField_primitives_getFieldJavaClass() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("repeated_int64");
+
+ assertThat(fieldDescriptor.getFieldJavaClass()).isEqualTo(Iterable.class);
+ assertThat(fieldDescriptor.getFieldJavaClassName()).isEmpty();
+ }
+
+ @Test
+ public void fieldDescriptor_repeatedField_wrappers_getFieldJavaClass() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("repeated_double_wrapper");
+
+ assertThat(fieldDescriptor.getFieldJavaClass()).isEqualTo(Iterable.class);
+ assertThat(fieldDescriptor.getFieldJavaClassName())
+ .isEqualTo("com.google.protobuf.DoubleValue");
+ }
+
+ @Test
+ public void fieldDescriptor_nestedMessage() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("standalone_message");
+
+ assertThat(fieldDescriptor.getCelFieldValueType()).isEqualTo(CelFieldValueType.SCALAR);
+ assertThat(fieldDescriptor.getJavaType()).isEqualTo(JavaType.MESSAGE);
+ assertThat(fieldDescriptor.getProtoFieldType()).isEqualTo(FieldDescriptor.Type.MESSAGE);
+ }
+
+ @Test
+ public void fieldDescriptor_nestedMessage_getFieldJavaClass() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("standalone_message");
+
+ assertThat(fieldDescriptor.getFieldJavaClass()).isEqualTo(TestAllTypes.NestedMessage.class);
+ assertThat(fieldDescriptor.getFieldJavaClassName())
+ .isEqualTo("dev.cel.expr.conformance.proto3.TestAllTypes$NestedMessage");
+ }
+
+ @Test
+ public void fieldDescriptor_nestedMessage_fullyQualifiedNames() {
+ MessageLiteDescriptor testAllTypesDescriptor =
+ TEST_ALL_TYPES_CEL_LITE_DESCRIPTOR
+ .getProtoTypeNamesToDescriptors()
+ .get("cel.expr.conformance.proto3.TestAllTypes");
+ FieldDescriptor fieldDescriptor =
+ testAllTypesDescriptor.getFieldInfoMap().get("standalone_message");
+
+ assertThat(fieldDescriptor.getFullyQualifiedProtoFieldName())
+ .isEqualTo("cel.expr.conformance.proto3.TestAllTypes.standalone_message");
+ assertThat(fieldDescriptor.getFieldProtoTypeName())
+ .isEqualTo("cel.expr.conformance.proto3.TestAllTypes.NestedMessage");
+ }
+}