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
82 changes: 82 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 @@ -434,3 +434,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,116 @@
// 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.Optional;

/**
* Walks a sequence of {@link SelectField} selections, dispatching each field over {@link
* OptimizedSelectable} or {@link SelectableValue}.
*
* <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.
*/
public static Object qualify(Object target, ImmutableList<SelectField> fields) {
Object current = target;
for (int i = 0; i < fields.size(); i++) {
current = qualifyField(current, fields.get(i));
}
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) {
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));
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) {
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());
}
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) {
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);
}
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();
}
throw CelAttributeNotFoundException.forFieldResolution(field.fieldName());
}

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);
}
124 changes: 124 additions & 0 deletions common/src/main/java/dev/cel/common/values/SelectField.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// 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 static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.auto.value.AutoValue;
import com.google.errorprone.annotations.Immutable;
import dev.cel.common.annotations.Internal;
import org.jspecify.annotations.Nullable;

/**
* Represents a single field selection in an optimized selection chain.
*
* <p>CEL Library Internals. Do Not Use.
*/
@Internal
@AutoValue
@AutoValue.CopyAnnotations
@Immutable
@SuppressWarnings("Immutable") // Default value is an immutable CEL literal or null
public abstract class SelectField {

private static final int MAX_FIELD_NUMBER = 536870911;

/** CEL-specific type code used to encode CEL maps on the wire. */
public static final int CEL_MAP_TYPE_CODE = -1;

/** Sentinel for a presence-test qualifier, whose 2-tuple carries no type code. */
public static final int NO_TYPE_CODE = 0;

/** Protobuf field type code for {@code TYPE_MESSAGE} ({@code FieldDescriptorProto.Type}). */
public static final int MESSAGE_TYPE_CODE = 11;

// Mirrors FieldDescriptorProto.Type. Not validated against a protobuf enum because the :values
// target is deliberately protobuf-free; keep in sync with CelLiteDescriptor.FieldLiteDescriptor.
private static final int MIN_PROTO_TYPE_CODE = 1; // TYPE_DOUBLE
private static final int MAX_PROTO_TYPE_CODE = 18; // TYPE_SINT64
private static final int GROUP_PROTO_TYPE_CODE = 10; // Unsupported by CEL.

/** Protobuf field number of this hop. */
public abstract int fieldNumber();

/** Protobuf field name or map key of this hop. */
public abstract String fieldName();

/**
* Protobuf wire type code (1..18, except 10), {@link #CEL_MAP_TYPE_CODE}, or {@link
* #NO_TYPE_CODE}.
*/
public abstract int typeCode();

/**
* Default value for this hop, or null if unspecified. When non-null, this must be an immutable
* CEL literal value.
*/
public abstract @Nullable Object defaultValue();

/**
* Creates a presence-test qualifier hop.
*
* @param fieldNumber Protobuf field number. Takes {@code long} for compatibility with CEL's int64
* constant representations.
* @param fieldName Protobuf field name.
*/
public static SelectField create(long fieldNumber, String fieldName) {
checkArgument(
fieldNumber >= 1 && fieldNumber <= MAX_FIELD_NUMBER,
"Field number out of protobuf range: %s",
fieldNumber);
checkNotNull(fieldName);
return new AutoValue_SelectField(
(int) fieldNumber, fieldName, NO_TYPE_CODE, /* defaultValue= */ null);
}

/**
* Creates a fully-specified field selection hop with type code and optional default value.
*
* @param fieldNumber Protobuf field number. Takes {@code long} for compatibility with CEL's int64
* constant representations.
* @param fieldName Protobuf field name.
* @param typeCode Protobuf wire type code or {@link #CEL_MAP_TYPE_CODE}. Takes {@code long} for
* compatibility with CEL's int64 constant representations.
* @param defaultValue Default value for the field, or null if unspecified.
*/
public static SelectField create(
long fieldNumber, String fieldName, long typeCode, @Nullable Object defaultValue) {
checkArgument(
fieldNumber >= 1 && fieldNumber <= MAX_FIELD_NUMBER,
"Field number out of protobuf range: %s",
fieldNumber);
checkNotNull(fieldName);
checkArgument(isSupportedTypeCode(typeCode), "Invalid protobuf type code: %s", typeCode);
return new AutoValue_SelectField((int) fieldNumber, fieldName, (int) typeCode, defaultValue);
}

/**
* Returns whether {@code typeCode} is a protobuf field type code CEL supports, or the {@link
* #CEL_MAP_TYPE_CODE} sentinel.
*/
public static boolean isSupportedTypeCode(long typeCode) {
if (typeCode == CEL_MAP_TYPE_CODE) {
return true;
}
return typeCode >= MIN_PROTO_TYPE_CODE
&& typeCode <= MAX_PROTO_TYPE_CODE
&& typeCode != GROUP_PROTO_TYPE_CODE;
}

SelectField() {}
}
3 changes: 3 additions & 0 deletions common/src/test/java/dev/cel/common/values/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@ java_library(
"//common/values:cel_value_provider",
"//common/values:combined_cel_value_converter",
"//common/values:combined_cel_value_provider",
"//common/values:optimized_select_traversal",
"//common/values:optimized_selectable",
"//common/values:proto_message_lite_value",
"//common/values:proto_message_lite_value_provider",
"//common/values:proto_message_value",
"//common/values:proto_message_value_provider",
"//common/values:select_field",
"//protobuf:cel_lite_descriptor",
"//testing/protos:test_all_types_cel_java_proto3",
"@cel_spec//proto/cel/expr/conformance/proto2:test_all_types_java_proto",
Expand Down
Loading
Loading