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 "io" 20*1fa6dee9SAndroid Build Coastguard Worker "math" 21*1fa6dee9SAndroid Build Coastguard Worker "sort" 22*1fa6dee9SAndroid Build Coastguard Worker) 23*1fa6dee9SAndroid Build Coastguard Worker 24*1fa6dee9SAndroid Build Coastguard Workerfunc AddStringToList(list *List, s string) (modified bool) { 25*1fa6dee9SAndroid Build Coastguard Worker for _, v := range list.Values { 26*1fa6dee9SAndroid Build Coastguard Worker if sv, ok := v.(*String); ok && sv.Value == s { 27*1fa6dee9SAndroid Build Coastguard Worker // string already exists 28*1fa6dee9SAndroid Build Coastguard Worker return false 29*1fa6dee9SAndroid Build Coastguard Worker } else if !ok { 30*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("expected string in list, got %s", v.Type())) 31*1fa6dee9SAndroid Build Coastguard Worker } 32*1fa6dee9SAndroid Build Coastguard Worker } 33*1fa6dee9SAndroid Build Coastguard Worker 34*1fa6dee9SAndroid Build Coastguard Worker list.Values = append(list.Values, &String{ 35*1fa6dee9SAndroid Build Coastguard Worker LiteralPos: list.RBracePos, 36*1fa6dee9SAndroid Build Coastguard Worker Value: s, 37*1fa6dee9SAndroid Build Coastguard Worker }) 38*1fa6dee9SAndroid Build Coastguard Worker 39*1fa6dee9SAndroid Build Coastguard Worker return true 40*1fa6dee9SAndroid Build Coastguard Worker} 41*1fa6dee9SAndroid Build Coastguard Worker 42*1fa6dee9SAndroid Build Coastguard Workerfunc RemoveStringFromList(list *List, s string) (modified bool) { 43*1fa6dee9SAndroid Build Coastguard Worker for i, v := range list.Values { 44*1fa6dee9SAndroid Build Coastguard Worker if sv, ok := v.(*String); ok && sv.Value == s { 45*1fa6dee9SAndroid Build Coastguard Worker list.Values = append(list.Values[:i], list.Values[i+1:]...) 46*1fa6dee9SAndroid Build Coastguard Worker return true 47*1fa6dee9SAndroid Build Coastguard Worker } else if !ok { 48*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("expected string in list, got %s", v.Type())) 49*1fa6dee9SAndroid Build Coastguard Worker } 50*1fa6dee9SAndroid Build Coastguard Worker } 51*1fa6dee9SAndroid Build Coastguard Worker 52*1fa6dee9SAndroid Build Coastguard Worker return false 53*1fa6dee9SAndroid Build Coastguard Worker} 54*1fa6dee9SAndroid Build Coastguard Worker 55*1fa6dee9SAndroid Build Coastguard Workerfunc ReplaceStringsInList(list *List, replacements map[string]string) (replaced bool) { 56*1fa6dee9SAndroid Build Coastguard Worker modified := false 57*1fa6dee9SAndroid Build Coastguard Worker for i, v := range list.Values { 58*1fa6dee9SAndroid Build Coastguard Worker if sv, ok := v.(*String); ok && replacements[sv.Value] != "" { 59*1fa6dee9SAndroid Build Coastguard Worker pos := list.Values[i].Pos() 60*1fa6dee9SAndroid Build Coastguard Worker list.Values[i] = &String{ 61*1fa6dee9SAndroid Build Coastguard Worker LiteralPos: pos, 62*1fa6dee9SAndroid Build Coastguard Worker Value: replacements[sv.Value], 63*1fa6dee9SAndroid Build Coastguard Worker } 64*1fa6dee9SAndroid Build Coastguard Worker modified = true 65*1fa6dee9SAndroid Build Coastguard Worker } else if !ok { 66*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("expected string in list, got %s", v.Type())) 67*1fa6dee9SAndroid Build Coastguard Worker } 68*1fa6dee9SAndroid Build Coastguard Worker } 69*1fa6dee9SAndroid Build Coastguard Worker return modified 70*1fa6dee9SAndroid Build Coastguard Worker} 71*1fa6dee9SAndroid Build Coastguard Worker 72*1fa6dee9SAndroid Build Coastguard Worker// A Patch represents a region of a text buffer to be replaced [Start, End) and its Replacement 73*1fa6dee9SAndroid Build Coastguard Workertype Patch struct { 74*1fa6dee9SAndroid Build Coastguard Worker Start, End int 75*1fa6dee9SAndroid Build Coastguard Worker Replacement string 76*1fa6dee9SAndroid Build Coastguard Worker} 77*1fa6dee9SAndroid Build Coastguard Worker 78*1fa6dee9SAndroid Build Coastguard Worker// A PatchList is a list of sorted, non-overlapping Patch objects 79*1fa6dee9SAndroid Build Coastguard Workertype PatchList []Patch 80*1fa6dee9SAndroid Build Coastguard Worker 81*1fa6dee9SAndroid Build Coastguard Workertype PatchOverlapError error 82*1fa6dee9SAndroid Build Coastguard Worker 83*1fa6dee9SAndroid Build Coastguard Worker// Add adds a Patch to a PatchList. It returns a PatchOverlapError if the patch cannot be added. 84*1fa6dee9SAndroid Build Coastguard Workerfunc (list *PatchList) Add(start, end int, replacement string) error { 85*1fa6dee9SAndroid Build Coastguard Worker patch := Patch{start, end, replacement} 86*1fa6dee9SAndroid Build Coastguard Worker if patch.Start > patch.End { 87*1fa6dee9SAndroid Build Coastguard Worker return fmt.Errorf("invalid patch, start %d is after end %d", patch.Start, patch.End) 88*1fa6dee9SAndroid Build Coastguard Worker } 89*1fa6dee9SAndroid Build Coastguard Worker for _, p := range *list { 90*1fa6dee9SAndroid Build Coastguard Worker if (patch.Start >= p.Start && patch.Start < p.End) || 91*1fa6dee9SAndroid Build Coastguard Worker (patch.End >= p.Start && patch.End < p.End) || 92*1fa6dee9SAndroid Build Coastguard Worker (p.Start >= patch.Start && p.Start < patch.End) || 93*1fa6dee9SAndroid Build Coastguard Worker (p.Start == patch.Start && p.End == patch.End) { 94*1fa6dee9SAndroid Build Coastguard Worker return PatchOverlapError(fmt.Errorf("new patch %d-%d overlaps with existing patch %d-%d", 95*1fa6dee9SAndroid Build Coastguard Worker patch.Start, patch.End, p.Start, p.End)) 96*1fa6dee9SAndroid Build Coastguard Worker } 97*1fa6dee9SAndroid Build Coastguard Worker } 98*1fa6dee9SAndroid Build Coastguard Worker *list = append(*list, patch) 99*1fa6dee9SAndroid Build Coastguard Worker list.sort() 100*1fa6dee9SAndroid Build Coastguard Worker return nil 101*1fa6dee9SAndroid Build Coastguard Worker} 102*1fa6dee9SAndroid Build Coastguard Worker 103*1fa6dee9SAndroid Build Coastguard Workerfunc (list *PatchList) sort() { 104*1fa6dee9SAndroid Build Coastguard Worker sort.SliceStable(*list, 105*1fa6dee9SAndroid Build Coastguard Worker func(i, j int) bool { 106*1fa6dee9SAndroid Build Coastguard Worker return (*list)[i].Start < (*list)[j].Start 107*1fa6dee9SAndroid Build Coastguard Worker }) 108*1fa6dee9SAndroid Build Coastguard Worker} 109*1fa6dee9SAndroid Build Coastguard Worker 110*1fa6dee9SAndroid Build Coastguard Worker// Apply applies all the Patch objects in PatchList to the data from an input ReaderAt to an output Writer. 111*1fa6dee9SAndroid Build Coastguard Workerfunc (list *PatchList) Apply(in io.ReaderAt, out io.Writer) error { 112*1fa6dee9SAndroid Build Coastguard Worker var offset int64 113*1fa6dee9SAndroid Build Coastguard Worker for _, patch := range *list { 114*1fa6dee9SAndroid Build Coastguard Worker toWrite := int64(patch.Start) - offset 115*1fa6dee9SAndroid Build Coastguard Worker written, err := io.Copy(out, io.NewSectionReader(in, offset, toWrite)) 116*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 117*1fa6dee9SAndroid Build Coastguard Worker return err 118*1fa6dee9SAndroid Build Coastguard Worker } 119*1fa6dee9SAndroid Build Coastguard Worker offset += toWrite 120*1fa6dee9SAndroid Build Coastguard Worker if written != toWrite { 121*1fa6dee9SAndroid Build Coastguard Worker return fmt.Errorf("unexpected EOF at %d", offset) 122*1fa6dee9SAndroid Build Coastguard Worker } 123*1fa6dee9SAndroid Build Coastguard Worker 124*1fa6dee9SAndroid Build Coastguard Worker _, err = io.WriteString(out, patch.Replacement) 125*1fa6dee9SAndroid Build Coastguard Worker if err != nil { 126*1fa6dee9SAndroid Build Coastguard Worker return err 127*1fa6dee9SAndroid Build Coastguard Worker } 128*1fa6dee9SAndroid Build Coastguard Worker 129*1fa6dee9SAndroid Build Coastguard Worker offset += int64(patch.End - patch.Start) 130*1fa6dee9SAndroid Build Coastguard Worker } 131*1fa6dee9SAndroid Build Coastguard Worker _, err := io.Copy(out, io.NewSectionReader(in, offset, math.MaxInt64-offset)) 132*1fa6dee9SAndroid Build Coastguard Worker return err 133*1fa6dee9SAndroid Build Coastguard Worker} 134