xref: /aosp_15_r20/build/blueprint/parser/sort.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Workerpackage parser
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
19*1fa6dee9SAndroid Build Coastguard Worker	"sort"
20*1fa6dee9SAndroid Build Coastguard Worker	"strconv"
21*1fa6dee9SAndroid Build Coastguard Worker	"strings"
22*1fa6dee9SAndroid Build Coastguard Worker	"text/scanner"
23*1fa6dee9SAndroid Build Coastguard Worker)
24*1fa6dee9SAndroid Build Coastguard Worker
25*1fa6dee9SAndroid Build Coastguard Worker// numericStringLess compares two strings, returning a lexicographical comparison unless the first
26*1fa6dee9SAndroid Build Coastguard Worker// difference occurs in a sequence of 1 or more numeric characters, in which case it returns the
27*1fa6dee9SAndroid Build Coastguard Worker// numerical comparison of the two numbers.
28*1fa6dee9SAndroid Build Coastguard Workerfunc numericStringLess(a, b string) bool {
29*1fa6dee9SAndroid Build Coastguard Worker	isNumeric := func(r rune) bool { return r >= '0' && r <= '9' }
30*1fa6dee9SAndroid Build Coastguard Worker	isNotNumeric := func(r rune) bool { return !isNumeric(r) }
31*1fa6dee9SAndroid Build Coastguard Worker
32*1fa6dee9SAndroid Build Coastguard Worker	minLength := len(a)
33*1fa6dee9SAndroid Build Coastguard Worker	if len(b) < minLength {
34*1fa6dee9SAndroid Build Coastguard Worker		minLength = len(b)
35*1fa6dee9SAndroid Build Coastguard Worker	}
36*1fa6dee9SAndroid Build Coastguard Worker
37*1fa6dee9SAndroid Build Coastguard Worker	byteIndex := 0
38*1fa6dee9SAndroid Build Coastguard Worker	numberStartIndex := -1
39*1fa6dee9SAndroid Build Coastguard Worker
40*1fa6dee9SAndroid Build Coastguard Worker	var aByte, bByte byte
41*1fa6dee9SAndroid Build Coastguard Worker
42*1fa6dee9SAndroid Build Coastguard Worker	// Start with a byte comparison to find where the strings differ.
43*1fa6dee9SAndroid Build Coastguard Worker	for ; byteIndex < minLength; byteIndex++ {
44*1fa6dee9SAndroid Build Coastguard Worker		aByte, bByte = a[byteIndex], b[byteIndex]
45*1fa6dee9SAndroid Build Coastguard Worker		if aByte != bByte {
46*1fa6dee9SAndroid Build Coastguard Worker			break
47*1fa6dee9SAndroid Build Coastguard Worker		}
48*1fa6dee9SAndroid Build Coastguard Worker		byteIsNumeric := isNumeric(rune(aByte))
49*1fa6dee9SAndroid Build Coastguard Worker		if numberStartIndex != -1 && !byteIsNumeric {
50*1fa6dee9SAndroid Build Coastguard Worker			numberStartIndex = -1
51*1fa6dee9SAndroid Build Coastguard Worker		} else if numberStartIndex == -1 && byteIsNumeric {
52*1fa6dee9SAndroid Build Coastguard Worker			numberStartIndex = byteIndex
53*1fa6dee9SAndroid Build Coastguard Worker		}
54*1fa6dee9SAndroid Build Coastguard Worker	}
55*1fa6dee9SAndroid Build Coastguard Worker
56*1fa6dee9SAndroid Build Coastguard Worker	// Handle the case where we reached the end of one or both strings without finding a difference.
57*1fa6dee9SAndroid Build Coastguard Worker	if byteIndex == minLength {
58*1fa6dee9SAndroid Build Coastguard Worker		if len(a) < len(b) {
59*1fa6dee9SAndroid Build Coastguard Worker			// Reached the end of a.  a is a prefix of b.
60*1fa6dee9SAndroid Build Coastguard Worker			return true
61*1fa6dee9SAndroid Build Coastguard Worker		} else {
62*1fa6dee9SAndroid Build Coastguard Worker			// Reached the end of b.  b is a prefix of a or b is equal to a.
63*1fa6dee9SAndroid Build Coastguard Worker			return false
64*1fa6dee9SAndroid Build Coastguard Worker		}
65*1fa6dee9SAndroid Build Coastguard Worker	}
66*1fa6dee9SAndroid Build Coastguard Worker
67*1fa6dee9SAndroid Build Coastguard Worker	aByteNumeric := isNumeric(rune(aByte))
68*1fa6dee9SAndroid Build Coastguard Worker	bByteNumeric := isNumeric(rune(bByte))
69*1fa6dee9SAndroid Build Coastguard Worker
70*1fa6dee9SAndroid Build Coastguard Worker	if (aByteNumeric || bByteNumeric) && !(aByteNumeric && bByteNumeric) && numberStartIndex != -1 {
71*1fa6dee9SAndroid Build Coastguard Worker		// Only one of aByte and bByte is a number, but the previous byte was a number.  That means
72*1fa6dee9SAndroid Build Coastguard Worker		// one is a longer number with the same prefix, which must be numerically larger.  If bByte
73*1fa6dee9SAndroid Build Coastguard Worker		// is a number then the number in b is numerically larger than the number in a.
74*1fa6dee9SAndroid Build Coastguard Worker		return bByteNumeric
75*1fa6dee9SAndroid Build Coastguard Worker	}
76*1fa6dee9SAndroid Build Coastguard Worker
77*1fa6dee9SAndroid Build Coastguard Worker	// If the bytes are both numbers do a numeric comparison.
78*1fa6dee9SAndroid Build Coastguard Worker	if aByteNumeric && bByteNumeric {
79*1fa6dee9SAndroid Build Coastguard Worker		// Extract the numbers from each string, starting from the first number after the last
80*1fa6dee9SAndroid Build Coastguard Worker		// non-number.  This won't be invalid utf8 because we are only looking for the bytes
81*1fa6dee9SAndroid Build Coastguard Worker		//'0'-'9', which can only occur as single-byte runes in utf8.
82*1fa6dee9SAndroid Build Coastguard Worker		if numberStartIndex == -1 {
83*1fa6dee9SAndroid Build Coastguard Worker			numberStartIndex = byteIndex
84*1fa6dee9SAndroid Build Coastguard Worker		}
85*1fa6dee9SAndroid Build Coastguard Worker		aNumberString := a[numberStartIndex:]
86*1fa6dee9SAndroid Build Coastguard Worker		bNumberString := b[numberStartIndex:]
87*1fa6dee9SAndroid Build Coastguard Worker
88*1fa6dee9SAndroid Build Coastguard Worker		// Find the first non-number in each, using the full length if there isn't one.
89*1fa6dee9SAndroid Build Coastguard Worker		endANumbers := strings.IndexFunc(aNumberString, isNotNumeric)
90*1fa6dee9SAndroid Build Coastguard Worker		endBNumbers := strings.IndexFunc(bNumberString, isNotNumeric)
91*1fa6dee9SAndroid Build Coastguard Worker		if endANumbers == -1 {
92*1fa6dee9SAndroid Build Coastguard Worker			endANumbers = len(aNumberString)
93*1fa6dee9SAndroid Build Coastguard Worker		}
94*1fa6dee9SAndroid Build Coastguard Worker		if endBNumbers == -1 {
95*1fa6dee9SAndroid Build Coastguard Worker			endBNumbers = len(bNumberString)
96*1fa6dee9SAndroid Build Coastguard Worker		}
97*1fa6dee9SAndroid Build Coastguard Worker
98*1fa6dee9SAndroid Build Coastguard Worker		// Convert each to an int.
99*1fa6dee9SAndroid Build Coastguard Worker		aNumber, err := strconv.Atoi(aNumberString[:endANumbers])
100*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
101*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("failed to convert %q from %q to number: %w",
102*1fa6dee9SAndroid Build Coastguard Worker				aNumberString[:endANumbers], a, err))
103*1fa6dee9SAndroid Build Coastguard Worker		}
104*1fa6dee9SAndroid Build Coastguard Worker		bNumber, err := strconv.Atoi(bNumberString[:endBNumbers])
105*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
106*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("failed to convert %q from %q to number: %w",
107*1fa6dee9SAndroid Build Coastguard Worker				bNumberString[:endBNumbers], b, err))
108*1fa6dee9SAndroid Build Coastguard Worker		}
109*1fa6dee9SAndroid Build Coastguard Worker		// Do a numeric comparison.
110*1fa6dee9SAndroid Build Coastguard Worker		return aNumber < bNumber
111*1fa6dee9SAndroid Build Coastguard Worker	}
112*1fa6dee9SAndroid Build Coastguard Worker
113*1fa6dee9SAndroid Build Coastguard Worker	// At least one is not a number, do a byte comparison.
114*1fa6dee9SAndroid Build Coastguard Worker	return aByte < bByte
115*1fa6dee9SAndroid Build Coastguard Worker}
116*1fa6dee9SAndroid Build Coastguard Worker
117*1fa6dee9SAndroid Build Coastguard Workerfunc SortLists(file *File) {
118*1fa6dee9SAndroid Build Coastguard Worker	for _, def := range file.Defs {
119*1fa6dee9SAndroid Build Coastguard Worker		if assignment, ok := def.(*Assignment); ok {
120*1fa6dee9SAndroid Build Coastguard Worker			sortListsInValue(assignment.Value, file)
121*1fa6dee9SAndroid Build Coastguard Worker		} else if module, ok := def.(*Module); ok {
122*1fa6dee9SAndroid Build Coastguard Worker			for _, prop := range module.Properties {
123*1fa6dee9SAndroid Build Coastguard Worker				sortListsInValue(prop.Value, file)
124*1fa6dee9SAndroid Build Coastguard Worker			}
125*1fa6dee9SAndroid Build Coastguard Worker		}
126*1fa6dee9SAndroid Build Coastguard Worker	}
127*1fa6dee9SAndroid Build Coastguard Worker	sort.Sort(commentsByOffset(file.Comments))
128*1fa6dee9SAndroid Build Coastguard Worker}
129*1fa6dee9SAndroid Build Coastguard Worker
130*1fa6dee9SAndroid Build Coastguard Workerfunc SortList(file *File, list *List) {
131*1fa6dee9SAndroid Build Coastguard Worker	if !isListOfPrimitives(list.Values) {
132*1fa6dee9SAndroid Build Coastguard Worker		return
133*1fa6dee9SAndroid Build Coastguard Worker	}
134*1fa6dee9SAndroid Build Coastguard Worker	for i := 0; i < len(list.Values); i++ {
135*1fa6dee9SAndroid Build Coastguard Worker		// Find a set of values on contiguous lines
136*1fa6dee9SAndroid Build Coastguard Worker		line := list.Values[i].Pos().Line
137*1fa6dee9SAndroid Build Coastguard Worker		var j int
138*1fa6dee9SAndroid Build Coastguard Worker		for j = i + 1; j < len(list.Values); j++ {
139*1fa6dee9SAndroid Build Coastguard Worker			if list.Values[j].Pos().Line > line+1 {
140*1fa6dee9SAndroid Build Coastguard Worker				break
141*1fa6dee9SAndroid Build Coastguard Worker			}
142*1fa6dee9SAndroid Build Coastguard Worker			line = list.Values[j].Pos().Line
143*1fa6dee9SAndroid Build Coastguard Worker		}
144*1fa6dee9SAndroid Build Coastguard Worker
145*1fa6dee9SAndroid Build Coastguard Worker		nextPos := list.End()
146*1fa6dee9SAndroid Build Coastguard Worker		if j < len(list.Values) {
147*1fa6dee9SAndroid Build Coastguard Worker			nextPos = list.Values[j].Pos()
148*1fa6dee9SAndroid Build Coastguard Worker		}
149*1fa6dee9SAndroid Build Coastguard Worker		sortSubList(list.Values[i:j], nextPos, file)
150*1fa6dee9SAndroid Build Coastguard Worker		i = j - 1
151*1fa6dee9SAndroid Build Coastguard Worker	}
152*1fa6dee9SAndroid Build Coastguard Worker}
153*1fa6dee9SAndroid Build Coastguard Worker
154*1fa6dee9SAndroid Build Coastguard Workerfunc ListIsSorted(list *List) bool {
155*1fa6dee9SAndroid Build Coastguard Worker	for i := 0; i < len(list.Values); i++ {
156*1fa6dee9SAndroid Build Coastguard Worker		// Find a set of values on contiguous lines
157*1fa6dee9SAndroid Build Coastguard Worker		line := list.Values[i].Pos().Line
158*1fa6dee9SAndroid Build Coastguard Worker		var j int
159*1fa6dee9SAndroid Build Coastguard Worker		for j = i + 1; j < len(list.Values); j++ {
160*1fa6dee9SAndroid Build Coastguard Worker			if list.Values[j].Pos().Line > line+1 {
161*1fa6dee9SAndroid Build Coastguard Worker				break
162*1fa6dee9SAndroid Build Coastguard Worker			}
163*1fa6dee9SAndroid Build Coastguard Worker			line = list.Values[j].Pos().Line
164*1fa6dee9SAndroid Build Coastguard Worker		}
165*1fa6dee9SAndroid Build Coastguard Worker
166*1fa6dee9SAndroid Build Coastguard Worker		if !subListIsSorted(list.Values[i:j]) {
167*1fa6dee9SAndroid Build Coastguard Worker			return false
168*1fa6dee9SAndroid Build Coastguard Worker		}
169*1fa6dee9SAndroid Build Coastguard Worker		i = j - 1
170*1fa6dee9SAndroid Build Coastguard Worker	}
171*1fa6dee9SAndroid Build Coastguard Worker
172*1fa6dee9SAndroid Build Coastguard Worker	return true
173*1fa6dee9SAndroid Build Coastguard Worker}
174*1fa6dee9SAndroid Build Coastguard Worker
175*1fa6dee9SAndroid Build Coastguard Workerfunc sortListsInValue(value Expression, file *File) {
176*1fa6dee9SAndroid Build Coastguard Worker	switch v := value.(type) {
177*1fa6dee9SAndroid Build Coastguard Worker	case *Variable:
178*1fa6dee9SAndroid Build Coastguard Worker		// Nothing
179*1fa6dee9SAndroid Build Coastguard Worker	case *Operator:
180*1fa6dee9SAndroid Build Coastguard Worker		sortListsInValue(v.Args[0], file)
181*1fa6dee9SAndroid Build Coastguard Worker		sortListsInValue(v.Args[1], file)
182*1fa6dee9SAndroid Build Coastguard Worker	case *Map:
183*1fa6dee9SAndroid Build Coastguard Worker		for _, p := range v.Properties {
184*1fa6dee9SAndroid Build Coastguard Worker			sortListsInValue(p.Value, file)
185*1fa6dee9SAndroid Build Coastguard Worker		}
186*1fa6dee9SAndroid Build Coastguard Worker	case *List:
187*1fa6dee9SAndroid Build Coastguard Worker		SortList(file, v)
188*1fa6dee9SAndroid Build Coastguard Worker	}
189*1fa6dee9SAndroid Build Coastguard Worker}
190*1fa6dee9SAndroid Build Coastguard Worker
191*1fa6dee9SAndroid Build Coastguard Workerfunc sortSubList(values []Expression, nextPos scanner.Position, file *File) {
192*1fa6dee9SAndroid Build Coastguard Worker	if !isListOfPrimitives(values) {
193*1fa6dee9SAndroid Build Coastguard Worker		return
194*1fa6dee9SAndroid Build Coastguard Worker	}
195*1fa6dee9SAndroid Build Coastguard Worker	l := make([]elem, len(values))
196*1fa6dee9SAndroid Build Coastguard Worker	for i, v := range values {
197*1fa6dee9SAndroid Build Coastguard Worker		s, ok := v.(*String)
198*1fa6dee9SAndroid Build Coastguard Worker		if !ok {
199*1fa6dee9SAndroid Build Coastguard Worker			panic("list contains non-string element")
200*1fa6dee9SAndroid Build Coastguard Worker		}
201*1fa6dee9SAndroid Build Coastguard Worker		n := nextPos
202*1fa6dee9SAndroid Build Coastguard Worker		if i < len(values)-1 {
203*1fa6dee9SAndroid Build Coastguard Worker			n = values[i+1].Pos()
204*1fa6dee9SAndroid Build Coastguard Worker		}
205*1fa6dee9SAndroid Build Coastguard Worker		l[i] = elem{s.Value, i, v.Pos(), n}
206*1fa6dee9SAndroid Build Coastguard Worker	}
207*1fa6dee9SAndroid Build Coastguard Worker
208*1fa6dee9SAndroid Build Coastguard Worker	sort.SliceStable(l, func(i, j int) bool {
209*1fa6dee9SAndroid Build Coastguard Worker		return numericStringLess(l[i].s, l[j].s)
210*1fa6dee9SAndroid Build Coastguard Worker	})
211*1fa6dee9SAndroid Build Coastguard Worker
212*1fa6dee9SAndroid Build Coastguard Worker	copyValues := append([]Expression{}, values...)
213*1fa6dee9SAndroid Build Coastguard Worker	copyComments := make([]*CommentGroup, len(file.Comments))
214*1fa6dee9SAndroid Build Coastguard Worker	for i := range file.Comments {
215*1fa6dee9SAndroid Build Coastguard Worker		cg := *file.Comments[i]
216*1fa6dee9SAndroid Build Coastguard Worker		cg.Comments = make([]*Comment, len(cg.Comments))
217*1fa6dee9SAndroid Build Coastguard Worker		for j := range file.Comments[i].Comments {
218*1fa6dee9SAndroid Build Coastguard Worker			c := *file.Comments[i].Comments[j]
219*1fa6dee9SAndroid Build Coastguard Worker			cg.Comments[j] = &c
220*1fa6dee9SAndroid Build Coastguard Worker		}
221*1fa6dee9SAndroid Build Coastguard Worker		copyComments[i] = &cg
222*1fa6dee9SAndroid Build Coastguard Worker	}
223*1fa6dee9SAndroid Build Coastguard Worker
224*1fa6dee9SAndroid Build Coastguard Worker	curPos := values[0].Pos()
225*1fa6dee9SAndroid Build Coastguard Worker	for i, e := range l {
226*1fa6dee9SAndroid Build Coastguard Worker		values[i] = copyValues[e.i]
227*1fa6dee9SAndroid Build Coastguard Worker		values[i].(*String).LiteralPos = curPos
228*1fa6dee9SAndroid Build Coastguard Worker		for j, c := range copyComments {
229*1fa6dee9SAndroid Build Coastguard Worker			if c.Pos().Offset > e.pos.Offset && c.Pos().Offset < e.nextPos.Offset {
230*1fa6dee9SAndroid Build Coastguard Worker				file.Comments[j].Comments[0].Slash.Line = curPos.Line + c.Pos().Line - e.pos.Line
231*1fa6dee9SAndroid Build Coastguard Worker				curPos.Line += c.Pos().Line - e.pos.Line
232*1fa6dee9SAndroid Build Coastguard Worker				file.Comments[j].Comments[0].Slash.Offset += values[i].Pos().Offset - e.pos.Offset
233*1fa6dee9SAndroid Build Coastguard Worker			}
234*1fa6dee9SAndroid Build Coastguard Worker		}
235*1fa6dee9SAndroid Build Coastguard Worker
236*1fa6dee9SAndroid Build Coastguard Worker		curPos.Offset += e.nextPos.Offset - e.pos.Offset
237*1fa6dee9SAndroid Build Coastguard Worker		curPos.Line++
238*1fa6dee9SAndroid Build Coastguard Worker	}
239*1fa6dee9SAndroid Build Coastguard Worker}
240*1fa6dee9SAndroid Build Coastguard Worker
241*1fa6dee9SAndroid Build Coastguard Workerfunc subListIsSorted(values []Expression) bool {
242*1fa6dee9SAndroid Build Coastguard Worker	if !isListOfPrimitives(values) {
243*1fa6dee9SAndroid Build Coastguard Worker		return true
244*1fa6dee9SAndroid Build Coastguard Worker	}
245*1fa6dee9SAndroid Build Coastguard Worker	prev := ""
246*1fa6dee9SAndroid Build Coastguard Worker	for _, v := range values {
247*1fa6dee9SAndroid Build Coastguard Worker		s, ok := v.(*String)
248*1fa6dee9SAndroid Build Coastguard Worker		if !ok {
249*1fa6dee9SAndroid Build Coastguard Worker			panic("list contains non-string element")
250*1fa6dee9SAndroid Build Coastguard Worker		}
251*1fa6dee9SAndroid Build Coastguard Worker		if prev != "" && numericStringLess(s.Value, prev) {
252*1fa6dee9SAndroid Build Coastguard Worker			return false
253*1fa6dee9SAndroid Build Coastguard Worker		}
254*1fa6dee9SAndroid Build Coastguard Worker		prev = s.Value
255*1fa6dee9SAndroid Build Coastguard Worker	}
256*1fa6dee9SAndroid Build Coastguard Worker
257*1fa6dee9SAndroid Build Coastguard Worker	return true
258*1fa6dee9SAndroid Build Coastguard Worker}
259*1fa6dee9SAndroid Build Coastguard Worker
260*1fa6dee9SAndroid Build Coastguard Workertype elem struct {
261*1fa6dee9SAndroid Build Coastguard Worker	s       string
262*1fa6dee9SAndroid Build Coastguard Worker	i       int
263*1fa6dee9SAndroid Build Coastguard Worker	pos     scanner.Position
264*1fa6dee9SAndroid Build Coastguard Worker	nextPos scanner.Position
265*1fa6dee9SAndroid Build Coastguard Worker}
266*1fa6dee9SAndroid Build Coastguard Worker
267*1fa6dee9SAndroid Build Coastguard Workertype commentsByOffset []*CommentGroup
268*1fa6dee9SAndroid Build Coastguard Worker
269*1fa6dee9SAndroid Build Coastguard Workerfunc (l commentsByOffset) Len() int {
270*1fa6dee9SAndroid Build Coastguard Worker	return len(l)
271*1fa6dee9SAndroid Build Coastguard Worker}
272*1fa6dee9SAndroid Build Coastguard Worker
273*1fa6dee9SAndroid Build Coastguard Workerfunc (l commentsByOffset) Less(i, j int) bool {
274*1fa6dee9SAndroid Build Coastguard Worker	return l[i].Pos().Offset < l[j].Pos().Offset
275*1fa6dee9SAndroid Build Coastguard Worker}
276*1fa6dee9SAndroid Build Coastguard Worker
277*1fa6dee9SAndroid Build Coastguard Workerfunc (l commentsByOffset) Swap(i, j int) {
278*1fa6dee9SAndroid Build Coastguard Worker	l[i], l[j] = l[j], l[i]
279*1fa6dee9SAndroid Build Coastguard Worker}
280*1fa6dee9SAndroid Build Coastguard Worker
281*1fa6dee9SAndroid Build Coastguard Workerfunc isListOfPrimitives(values []Expression) bool {
282*1fa6dee9SAndroid Build Coastguard Worker	if len(values) == 0 {
283*1fa6dee9SAndroid Build Coastguard Worker		return true
284*1fa6dee9SAndroid Build Coastguard Worker	}
285*1fa6dee9SAndroid Build Coastguard Worker	switch values[0].(type) {
286*1fa6dee9SAndroid Build Coastguard Worker	case *Bool, *String, *Int64:
287*1fa6dee9SAndroid Build Coastguard Worker		return true
288*1fa6dee9SAndroid Build Coastguard Worker	default:
289*1fa6dee9SAndroid Build Coastguard Worker		return false
290*1fa6dee9SAndroid Build Coastguard Worker	}
291*1fa6dee9SAndroid Build Coastguard Worker}
292