Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions common/src/main/java/dev/cel/common/values/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -317,14 +317,18 @@ java_library(
srcs = [
"ProtoLiteCelValueConverter.java",
"ProtoMessageLiteValue.java",
"RawProtoMessageLiteValue.java",
],
tags = [
],
deps = [
":base_proto_cel_value_converter",
":optimized_selectable",
":select_field",
":values",
"//:auto_value",
"//common/annotations",
"//common/exceptions:attribute_not_found",
"//common/internal:cel_lite_descriptor_pool",
"//common/internal:well_known_proto",
"//common/types",
Expand All @@ -333,6 +337,7 @@ java_library(
"//protobuf:cel_lite_descriptor",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
"@maven_android//:com_google_protobuf_protobuf_javalite",
],
)
Expand All @@ -342,14 +347,18 @@ cel_android_library(
srcs = [
"ProtoLiteCelValueConverter.java",
"ProtoMessageLiteValue.java",
"RawProtoMessageLiteValue.java",
],
tags = [
],
deps = [
":base_proto_cel_value_converter_android",
":optimized_selectable_android",
":select_field_android",
":values_android",
"//:auto_value",
"//common/annotations",
"//common/exceptions:attribute_not_found",
"//common/internal:cel_lite_descriptor_pool_android",
"//common/internal:well_known_proto_android",
"//common/types:type_providers_android",
Expand All @@ -358,6 +367,7 @@ cel_android_library(
"//protobuf:cel_lite_descriptor",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
"@maven_android//:com_google_guava_guava",
"@maven_android//:com_google_protobuf_protobuf_javalite",
],
Expand Down Expand Up @@ -428,3 +438,85 @@ cel_android_library(
"@maven//:com_google_errorprone_error_prone_annotations",
],
)

java_library(
name = "select_field",
srcs = ["SelectField.java"],
tags = [
],
deps = [
"//:auto_value",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:com_google_guava_guava",
"@maven//:org_jspecify_jspecify",
],
)

cel_android_library(
name = "select_field_android",
srcs = ["SelectField.java"],
tags = [
],
deps = [
"//:auto_value",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
"@maven//:org_jspecify_jspecify",
"@maven_android//:com_google_guava_guava",
],
)

java_library(
name = "optimized_selectable",
srcs = ["OptimizedSelectable.java"],
tags = [
],
deps = [
":select_field",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
],
)

cel_android_library(
name = "optimized_selectable_android",
srcs = ["OptimizedSelectable.java"],
tags = [
],
deps = [
":select_field_android",
"//common/annotations",
"@maven//:com_google_errorprone_error_prone_annotations",
],
)

java_library(
name = "optimized_select_traversal",
srcs = ["OptimizedSelectTraversal.java"],
tags = [
],
deps = [
":optimized_selectable",
":select_field",
":values",
"//common/annotations",
"//common/exceptions:attribute_not_found",
"@maven//:com_google_guava_guava",
],
)

cel_android_library(
name = "optimized_select_traversal_android",
srcs = ["OptimizedSelectTraversal.java"],
tags = [
],
deps = [
":optimized_selectable_android",
":select_field_android",
":values_android",
"//common/annotations",
"//common/exceptions:attribute_not_found",
"@maven_android//:com_google_guava_guava",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.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.common.values;

import com.google.common.collect.ImmutableList;
import dev.cel.common.annotations.Internal;
import dev.cel.common.exceptions.CelAttributeNotFoundException;
import java.util.Map;
import java.util.Optional;

/**
* Walks a sequence of {@link SelectField} selections, dispatching each field over {@link
* OptimizedSelectable}, {@link SelectableValue}, or {@link Map}.
*
* <p>CEL Library Internals. Do Not Use.
*/
@Internal
public final class OptimizedSelectTraversal {

/**
* Qualifies {@code target} through every field in {@code fields} and returns the terminal value.
*
* @param celValueConverter Converter for unadapted entries encountered in a root {@link Map}.
*/
public static Object qualify(
Object target, ImmutableList<SelectField> fields, CelValueConverter celValueConverter) {
Object current = target;
for (int i = 0; i < fields.size(); i++) {
current = qualifyField(current, fields.get(i), celValueConverter);
}
return current;
}

/**
* Presence tests the terminal field of {@code fields}, navigating through all preceding fields.
*
* <p>Absence of any intermediate field short-circuits to {@code false}.
*/
public static boolean hasField(
Object target, ImmutableList<SelectField> fields, CelValueConverter celValueConverter) {
if (fields.isEmpty()) {
return false;
}
Object current = target;
int terminalIndex = fields.size() - 1;
for (int i = 0; i < terminalIndex; i++) {
Optional<Object> next = navigateField(current, fields.get(i), celValueConverter);
if (!next.isPresent()) {
return false;
}
current = next.get();
}
return hasTerminalField(current, fields.get(terminalIndex));
}

// SelectableValue is only ever instantiated with String keys in the select path.
@SuppressWarnings("unchecked")
private static Object qualifyField(
Object target, SelectField field, CelValueConverter celValueConverter) {
if (target instanceof ErrorValue) {
return target;
}
if (target instanceof OptimizedSelectable) {
return ((OptimizedSelectable) target).selectByFieldNumber(field);
}
if (target instanceof SelectableValue) {
SelectableValue<String> selectable = (SelectableValue<String>) target;
if (field.defaultValue() != null) {
return selectable
.find(field.fieldName())
.map(Object.class::cast)
.orElse(field.defaultValue());
}
return selectable.select(field.fieldName());
}
if (target instanceof Map) {
return getMapEntry((Map<?, ?>) target, field.fieldName(), celValueConverter);
}
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
}

// SelectableValue is only ever instantiated with String keys in the select path.
@SuppressWarnings("unchecked")
private static Optional<Object> navigateField(
Object target, SelectField field, CelValueConverter celValueConverter) {
if (target instanceof ErrorValue) {
return Optional.of(target);
}
if (target instanceof OptimizedSelectable) {
return ((OptimizedSelectable) target).findByFieldNumber(field);
}
if (target instanceof SelectableValue) {
return ((SelectableValue<String>) target).find(field.fieldName()).map(Object.class::cast);
}
if (target instanceof Map) {
return findMapEntry((Map<?, ?>) target, field.fieldName(), celValueConverter);
}
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
}

// SelectableValue is only ever instantiated with String keys in the select path.
@SuppressWarnings("unchecked")
private static boolean hasTerminalField(Object target, SelectField field) {
if (target instanceof ErrorValue) {
return false;
}
if (target instanceof OptimizedSelectable) {
return ((OptimizedSelectable) target).hasFieldByNumber(field);
}
if (target instanceof SelectableValue) {
return ((SelectableValue<String>) target).find(field.fieldName()).isPresent();
}
if (target instanceof Map) {
return ((Map<?, ?>) target).containsKey(field.fieldName());
}
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
}

private static Object getMapEntry(
Map<?, ?> map, String key, CelValueConverter celValueConverter) {
return findMapEntry(map, key, celValueConverter)
.orElseThrow(() -> CelAttributeNotFoundException.forMissingMapKey(key));
}

private static Optional<Object> findMapEntry(
Map<?, ?> map, String key, CelValueConverter celValueConverter) {
Object mapValue = map.get(key);
if (mapValue != null) {
return Optional.of(toStepTarget(mapValue, celValueConverter));
}
if (!map.containsKey(key)) {
return Optional.empty();
}
throw CelAttributeNotFoundException.of(
String.format("Map value cannot be null for key: %s", key));
}

static Object toStepTarget(Object value, CelValueConverter celValueConverter) {
if (value instanceof Map) {
return value;
}
return celValueConverter.toRuntimeValue(value);
}

private OptimizedSelectTraversal() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.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.common.values;

import com.google.errorprone.annotations.Immutable;
import dev.cel.common.annotations.Internal;
import java.util.Optional;

/**
* Resolves an optimized field selection within a selection chain rewritten by the select optimizer.
*
* <p>Implementations resolve individual field selections against themselves by protobuf field
* number. Walking the chain across multiple fields and heterogeneous values belongs to {@link
* OptimizedSelectTraversal}.
*
* <p>CEL Library Internals. Do Not Use.
*/
@Internal
@Immutable
public interface OptimizedSelectable {

/** Selects {@code field}, falling back to its default value or an empty submessage if absent. */
Object selectByFieldNumber(SelectField field);

/** Returns whether {@code field} is present. */
boolean hasFieldByNumber(SelectField field);

/**
* Returns the value of the field at {@code field} (a scalar or submessage) for an intermediate
* step of a presence test, or empty if absent.
*/
Optional<Object> findByFieldNumber(SelectField field);
}
Loading
Loading