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