-
Notifications
You must be signed in to change notification settings - Fork 37
Expand file tree
/
Copy pathCelAttributePattern.java
More file actions
182 lines (167 loc) · 6.85 KB
/
Copy pathCelAttributePattern.java
File metadata and controls
182 lines (167 loc) · 6.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright 2022 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.runtime;
import static java.lang.Math.min;
import com.google.auto.value.AutoValue;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
/**
* A Pattern for matching against {@link CelAttribute}s.
*
* <p>CelAttributePatterns are structured the same as attributes, but permit some qualifiers be
* replaced with wildcards.
*/
@Immutable
@AutoValue
public abstract class CelAttributePattern {
/** Constructs a CelAttributePattern from a list of qualifiers. */
public static CelAttributePattern create(ImmutableList<CelAttribute.Qualifier> qualifiers) {
Preconditions.checkArgument(!qualifiers.isEmpty(), "qualifiers must be non-empty");
Preconditions.checkArgument(
CelAttribute.Qualifier.isLegalIdentifier(qualifiers.get(0)),
"'%s' is not a legal CEL identifier",
qualifiers.get(0));
return new AutoValue_CelAttributePattern(qualifiers);
}
/** Constructs a pattern from a single root identifier (single string qualifier). */
public static CelAttributePattern create(String rootIdentifier) {
Preconditions.checkArgument(
CelAttribute.Qualifier.isLegalIdentifier(rootIdentifier),
"'%s' is not a legal CEL identifier",
rootIdentifier);
return new AutoValue_CelAttributePattern(
ImmutableList.of(CelAttribute.Qualifier.ofString(rootIdentifier)));
}
/**
* Attempts to parse a dot qualified identifier into a CEL attribute.
*
* @throws IllegalArgumentException if qualifiedIdentifier isn't a legal qualified identifier.
* Note: this is intended for use with reference names in a checked CEL expression -- it does
* not check for some edge cases (e.g. reserved words).
*/
public static CelAttributePattern fromQualifiedIdentifier(String qualifiedIdentifier) {
ImmutableList.Builder<CelAttribute.Qualifier> qualifiers = ImmutableList.builder();
int start = 0;
int next;
while ((next = qualifiedIdentifier.indexOf('.', start)) != -1) {
qualifiers.add(CelAttribute.Qualifier.ofString(qualifiedIdentifier.substring(start, next)));
start = next + 1;
}
qualifiers.add(CelAttribute.Qualifier.ofString(qualifiedIdentifier.substring(start)));
return new AutoValue_CelAttributePattern(qualifiers.build());
}
/** The list of qualifiers representing the select paths this pattern matches. */
public abstract ImmutableList<CelAttribute.Qualifier> qualifiers();
/** Create a new attribute pattern that specifies a subfield of this pattern. */
public CelAttributePattern qualify(CelAttribute.Qualifier qualifier) {
return new AutoValue_CelAttributePattern(
ImmutableList.<CelAttribute.Qualifier>builderWithExpectedSize(qualifiers().size() + 1)
.addAll(qualifiers())
.add(qualifier)
.build());
}
@Override
public final String toString() {
Preconditions.checkState(
!qualifiers().isEmpty()
&& qualifiers().get(0).kind() == CelAttribute.Qualifier.Kind.AS_STRING,
"CelAttribute must have a root qualifier that is a legal identifier");
StringBuilder cname = new StringBuilder(qualifiers().get(0).asString());
for (CelAttribute.Qualifier qualifier : qualifiers().subList(1, qualifiers().size())) {
switch (qualifier.kind()) {
case WILD_CARD:
cname.append(".*");
break;
case AS_STRING:
if (CelAttribute.Qualifier.isLegalIdentifier(qualifier)) {
cname.append(".").append(qualifier.asString());
} else {
cname.append(qualifier.toIndexFormat());
}
break;
default:
cname.append(qualifier.toIndexFormat());
break;
}
}
return cname.toString();
}
/**
* Return whether this pattern matches the given attribute.
*
* <p>A pattern matches an attribute if the pattern contains the attribute (e.g. fully matches the
* attribute or one of its parents).
*/
public boolean isMatch(CelAttribute attribute) {
if (attribute.equals(CelAttribute.EMPTY)) {
return false;
}
int qualifierCount = min(qualifiers().size(), attribute.qualifiers().size());
int i = 0;
for (; i < qualifierCount; i++) {
CelAttribute.Qualifier pattern = qualifiers().get(i);
CelAttribute.Qualifier qualifier = attribute.qualifiers().get(i);
if (pattern.kind() != CelAttribute.Qualifier.Kind.WILD_CARD && !pattern.equals(qualifier)) {
return false;
}
}
// If the end of the loop is reached, either the pattern was exhausted (full match) or
// the attribute was exhausted (partial match / a sub field was matched).
return i >= qualifiers().size();
}
/**
* Return whether this pattern matches the given attribute or any of its descendants.
*
* <p>A partial match indicates that the attribute may contain some missing data, so the complete
* object is may be in an undefined state. For example:
*
* <ul>
* <li>pattern (this) object.field.list_field
* <li>attribute (arg) object.field
* </ul>
*
* is not a full match, but is a partial match.
*/
public boolean isPartialMatch(CelAttribute attribute) {
if (attribute.equals(CelAttribute.EMPTY)) {
return false;
}
int qualifierCount = min(qualifiers().size(), attribute.qualifiers().size());
for (int i = 0; i < qualifierCount; i++) {
CelAttribute.Qualifier pattern = qualifiers().get(i);
CelAttribute.Qualifier qualifier = attribute.qualifiers().get(i);
if (pattern.kind() != CelAttribute.Qualifier.Kind.WILD_CARD && !pattern.equals(qualifier)) {
return false;
}
}
// If the end of the loop is reached, either the pattern was exhausted (full match) or
// the attribute was exhausted (partial match / a sub field was matched).
return true;
}
/**
* Return an appropriate attribute for a pattern match.
*
* <p>For a partial match, return the attribute.
*
* <p>For a full match, return the (possibly parent attribute)
*/
public CelAttribute simplify(CelAttribute candidate) {
int patternLen = this.qualifiers().size();
if (patternLen < candidate.qualifiers().size()) {
return CelAttribute.create(candidate.qualifiers().subList(0, patternLen));
}
return candidate;
}
}