@@ -148,8 +148,8 @@ func MakePrefixMatcherOrDie(parts ...interface{}) *SetMatcher {
148
148
return result
149
149
}
150
150
151
- // PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of pattern path parts.
152
- // The pattern parts may any of:
151
+ // PrefixMatcher creates a SetMatcher that matches all field paths prefixed by the given list of matcher path parts.
152
+ // The matcher parts may any of:
153
153
//
154
154
// - PathElementMatcher - for wildcards, `MatchAnyPathElement()` can be used as well.
155
155
// - PathElement - for any path element
@@ -182,7 +182,7 @@ func PrefixMatcher(parts ...interface{}) (*SetMatcher, error) {
182
182
return nil , fmt .Errorf ("unexpected type %T" , t )
183
183
}
184
184
current = & SetMatcher {
185
- Members : []* SetMemberMatcher {{
185
+ members : []* SetMemberMatcher {{
186
186
Path : pattern ,
187
187
Child : current ,
188
188
}},
@@ -198,74 +198,81 @@ func MatchAnyPathElement() PathElementMatcher {
198
198
199
199
// MatchAnySet returns a SetMatcher that matches any set.
200
200
func MatchAnySet () * SetMatcher {
201
- return & SetMatcher {Wildcard : true }
201
+ return & SetMatcher {wildcard : true }
202
202
}
203
203
204
- // SetMatcher defines a pattern that matches fields in a Set.
204
+ // NewSetMatcher returns a new SetMatcher.
205
+ // Wildcard members take precedent over non-wildcard members;
206
+ // all non-wildcard members are ignored if there is a wildcard members.
207
+ func NewSetMatcher (wildcard bool , members ... * SetMemberMatcher ) * SetMatcher {
208
+ sort .Sort (sortedMemberMatcher (members ))
209
+ return & SetMatcher {wildcard : wildcard , members : members }
210
+ }
211
+
212
+ // SetMatcher defines a matcher that matches fields in a Set.
205
213
// SetMatcher is structured much like a Set but with wildcard support.
206
214
type SetMatcher struct {
207
- // Wildcard indicates that all members and children are included in the match.
208
- // If set, the Members field is ignored.
209
- Wildcard bool
210
- // Members provides patterns to match the members of a Set.
211
- Members []* SetMemberMatcher
215
+ // wildcard indicates that all members and children are included in the match.
216
+ // If set, the members field is ignored.
217
+ wildcard bool
218
+ // members provides patterns to match the members of a Set.
219
+ // Wildcard members are sorted before non-wildcards and take precedent over
220
+ // non-wildcard members.
221
+ members sortedMemberMatcher
222
+ }
223
+
224
+ type sortedMemberMatcher []* SetMemberMatcher
225
+
226
+ func (s sortedMemberMatcher ) Len () int { return len (s ) }
227
+ func (s sortedMemberMatcher ) Less (i , j int ) bool { return s [i ].Path .Less (s [j ].Path ) }
228
+ func (s sortedMemberMatcher ) Swap (i , j int ) { s [i ], s [j ] = s [j ], s [i ] }
229
+ func (s sortedMemberMatcher ) Find (p PathElementMatcher ) (location int , ok bool ) {
230
+ return sort .Find (len (s ), func (i int ) int {
231
+ return s [i ].Path .Compare (p )
232
+ })
212
233
}
213
234
214
- // Merge merges two SetMatchers into a single SetMatcher that matches the field paths of both SetMatchers.
215
- // During the merge, Members of s2 with the same PathElementMatcher as a member of s are merged into the member of s.
216
- // All other members of s2 are appended to the resulting member list in their original relative order.
217
- // When members are merged, the child SetMatchers are merged by calling this function recursively.
235
+ // Merge merges s and s2 and returns a SetMatcher that matches all field paths matched by either s or s2.
236
+ // During the merge, members of s and s2 with the same PathElementMatcher merged into a single member
237
+ // with the children of each merged by calling this function recursively.
218
238
func (s * SetMatcher ) Merge (s2 * SetMatcher ) * SetMatcher {
219
- if s .Wildcard || s2 .Wildcard {
220
- return & SetMatcher {Wildcard : true }
221
- }
222
-
223
- // TODO: Optimize. This is O(n^2). In practice, usually a single member is being inserted at a time,
224
- // but that's still O(n). Sorting would help a ton.
225
- var unionedMembers []* SetMemberMatcher
226
- for _ , m := range s .Members {
227
- for _ , m2 := range s2 .Members {
228
- if m .Path .PathElement .Equals (m2 .Path .PathElement ) && m .Path .Wildcard == m2 .Path .Wildcard {
229
- unionedMembers = append (unionedMembers , & SetMemberMatcher {
230
- Path : m .Path ,
231
- Child : m .Child .Merge (m2 .Child ),
232
- })
233
- } else {
234
- unionedMembers = append (unionedMembers , m )
235
- }
236
- }
237
- }
238
- for _ , m2 := range s2 .Members {
239
- for _ , existing := range unionedMembers {
240
- if ! m2 .Path .PathElement .Equals (existing .Path .PathElement ) {
241
- unionedMembers = append (unionedMembers , m2 )
239
+ if s .wildcard || s2 .wildcard {
240
+ return NewSetMatcher (true )
241
+ }
242
+ merged := make (sortedMemberMatcher , len (s .members ), len (s .members )+ len (s2 .members ))
243
+ copy (merged , s .members )
244
+ for _ , m := range s2 .members {
245
+ if i , ok := s .members .Find (m .Path ); ok {
246
+ // since merged is a shallow copy, do not modify elements in place
247
+ merged [i ] = & SetMemberMatcher {
248
+ Path : merged [i ].Path ,
249
+ Child : merged [i ].Child .Merge (m .Child ),
242
250
}
251
+ } else {
252
+ merged = append (merged , m )
243
253
}
244
254
}
245
-
246
- return & SetMatcher {
247
- Members : unionedMembers ,
248
- }
255
+ return NewSetMatcher (false , merged ... ) // sort happens here
249
256
}
250
257
251
- // SetMemberMatcher defines a pattern that matches the members of a Set.
258
+ // SetMemberMatcher defines a matcher that matches the members of a Set.
252
259
// SetMemberMatcher is structured much like the elements of a SetNodeMap, but
253
260
// with wildcard support.
254
261
type SetMemberMatcher struct {
255
- // Path provides a pattern to match members of a Set.
262
+ // Path provides a matcher to match members of a Set.
256
263
// If Path is a wildcard, all members of a Set are included in the match.
257
264
// Otherwise, if any Path is Equal to a member of a Set, that member is
258
265
// included in the match and the children of that member are matched
259
- // against the Child pattern .
266
+ // against the Child matcher .
260
267
Path PathElementMatcher
261
268
262
- // Child provides a pattern to use for the children of matched members of a Set.
269
+ // Child provides a matcher to use for the children of matched members of a Set.
263
270
Child * SetMatcher
264
271
}
265
272
266
- // PathElementMatcher defined a match pattern for a PathElement.
273
+ // PathElementMatcher defined a match matcher for a PathElement.
267
274
type PathElementMatcher struct {
268
- // Wildcard indicates that all PathElements are matched by this pattern .
275
+ // Wildcard indicates that all PathElements are matched by this matcher .
269
276
// If set, PathElement is ignored.
270
277
Wildcard bool
271
278
@@ -274,15 +281,37 @@ type PathElementMatcher struct {
274
281
PathElement
275
282
}
276
283
284
+ func (p PathElementMatcher ) Equals (p2 PathElementMatcher ) bool {
285
+ return p .Wildcard != p2 .Wildcard && p .PathElement .Equals (p2 .PathElement )
286
+ }
287
+
288
+ func (p PathElementMatcher ) Less (p2 PathElementMatcher ) bool {
289
+ if p .Wildcard && ! p2 .Wildcard {
290
+ return true
291
+ } else if p2 .Wildcard {
292
+ return false
293
+ }
294
+ return p .PathElement .Less (p2 .PathElement )
295
+ }
296
+
297
+ func (p PathElementMatcher ) Compare (p2 PathElementMatcher ) int {
298
+ if p .Wildcard && ! p2 .Wildcard {
299
+ return - 1
300
+ } else if p2 .Wildcard {
301
+ return 1
302
+ }
303
+ return p .PathElement .Compare (p2 .PathElement )
304
+ }
305
+
277
306
// FilterIncludeMatches returns a Set with only the field paths that match.
278
307
func (s * Set ) FilterIncludeMatches (pattern * SetMatcher ) * Set {
279
- if pattern .Wildcard {
308
+ if pattern .wildcard {
280
309
return s
281
310
}
282
311
283
312
members := PathElementSet {}
284
313
for _ , m := range s .Members .members {
285
- for _ , pm := range pattern .Members {
314
+ for _ , pm := range pattern .members {
286
315
if pm .Path .Wildcard || pm .Path .PathElement .Equals (m ) {
287
316
members .Insert (m )
288
317
break
@@ -635,15 +664,15 @@ func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.Ty
635
664
}
636
665
}
637
666
638
- // FilterIncludeMatches returns a SetNodeMap with only the field paths that match the pattern .
667
+ // FilterIncludeMatches returns a SetNodeMap with only the field paths that match the matcher .
639
668
func (s * SetNodeMap ) FilterIncludeMatches (pattern * SetMatcher ) * SetNodeMap {
640
- if pattern .Wildcard {
669
+ if pattern .wildcard {
641
670
return s
642
671
}
643
672
644
673
var out sortedSetNode
645
674
for _ , member := range s .members {
646
- for _ , c := range pattern .Members {
675
+ for _ , c := range pattern .members {
647
676
if c .Path .Wildcard || c .Path .PathElement .Equals (member .pathElement ) {
648
677
childSet := member .set .FilterIncludeMatches (c .Child )
649
678
if childSet .Size () > 0 {
@@ -694,7 +723,7 @@ func (s *SetNodeMap) Leaves() *SetNodeMap {
694
723
// NewExcludeSetFilter can be used to create a filter that removes
695
724
// specific field paths and all of their children.
696
725
// NewIncludeMatcherFilter can be used to create a filter that removes all fields except
697
- // the fields that match a field path pattern . PrefixMatcher and MakePrefixMatcherOrDie
726
+ // the fields that match a field path matcher . PrefixMatcher and MakePrefixMatcherOrDie
698
727
// can be used to define field path patterns.
699
728
type Filter interface {
700
729
// Filter returns a filtered copy of the set.
@@ -723,24 +752,25 @@ func (t excludeFilter) Filter(set *Set) *Set {
723
752
return set .RecursiveDifference (t .excludeSet )
724
753
}
725
754
726
- // NewIncludeMatcherFilter returns a filter that only includes field paths that match the pattern.
727
- // PrefixMatcher and MakePrefixMatcherOrDie can help create basic SetPatterns.
728
- func NewIncludeMatcherFilter (patterns ... * SetMatcher ) Filter {
729
- if len (patterns ) == 0 {
730
- return includeMatcherFilter {& SetMatcher {Wildcard : true }}
755
+ // NewIncludeMatcherFilter returns a filter that only includes field paths that match.
756
+ // If no matchers are provided, the filter includes all field paths.
757
+ // PrefixMatcher and MakePrefixMatcherOrDie can help create basic matcher.
758
+ func NewIncludeMatcherFilter (matchers ... * SetMatcher ) Filter {
759
+ if len (matchers ) == 0 {
760
+ return includeMatcherFilter {& SetMatcher {wildcard : true }}
731
761
}
732
- pattern := patterns [0 ]
733
- for i := 1 ; i < len (patterns ); i ++ {
734
- pattern = pattern .Merge (patterns [i ])
762
+ matcher := matchers [0 ]
763
+ for i := 1 ; i < len (matchers ); i ++ {
764
+ matcher = matcher .Merge (matchers [i ])
735
765
}
736
766
737
- return includeMatcherFilter {pattern }
767
+ return includeMatcherFilter {matcher }
738
768
}
739
769
740
770
type includeMatcherFilter struct {
741
- pattern * SetMatcher
771
+ matcher * SetMatcher
742
772
}
743
773
744
774
func (pf includeMatcherFilter ) Filter (set * Set ) * Set {
745
- return set .FilterIncludeMatches (pf .pattern )
775
+ return set .FilterIncludeMatches (pf .matcher )
746
776
}
0 commit comments